@apifuse/provider-sdk 2.2.0-beta.47 → 2.2.0-beta.49
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 +91 -36
- package/CHANGELOG.md +8 -0
- package/README.md +11 -9
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-dev.ts +24 -13
- package/bin/apifuse-migrate-operation-declaration.ts +55 -0
- package/bin/apifuse-pack-smoke.ts +1 -1
- package/bin/apifuse-pack-types.ts +2 -1
- package/bin/apifuse-record.ts +30 -16
- package/bin/apifuse-submit-check.ts +20 -35
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +11 -0
- package/dist/cli/migrate-operation-declaration.d.ts +59 -0
- package/dist/cli/migrate-operation-declaration.js +1178 -0
- package/dist/cli/templates/provider/README.md.tpl +3 -3
- package/dist/cli/templates/provider/operations/ping.ts.tpl +2 -0
- package/dist/config/loader.d.ts +2 -0
- package/dist/config/loader.js +18 -7
- package/dist/contract-types.d.ts +11 -5
- package/dist/contract.js +21 -10
- package/dist/define.d.ts +25 -22
- package/dist/define.js +49 -75
- package/dist/dev.d.ts +3 -0
- package/dist/dev.js +1 -1
- package/dist/engine.d.ts +78 -0
- package/dist/engine.js +133 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/lint.d.ts +7 -15
- package/dist/lint.js +45 -70
- package/dist/provider.d.ts +3 -1
- package/dist/provider.js +1 -0
- package/dist/runtime/chrome149-header-order.d.ts +58 -0
- package/dist/runtime/chrome149-header-order.js +289 -0
- package/dist/runtime/env.js +12 -0
- package/dist/runtime/executor.d.ts +2 -1
- package/dist/runtime/executor.js +3 -36
- package/dist/runtime/insights.js +2 -2
- package/dist/runtime/otlp.d.ts +71 -2
- package/dist/runtime/otlp.js +397 -16
- package/dist/runtime/resolver-public.d.ts +1 -1
- package/dist/runtime/resolver-public.js +1 -1
- package/dist/runtime/resolver-vendors/capsolver.js +9 -3
- package/dist/runtime/resolver-vendors/twocaptcha.js +1 -0
- package/dist/runtime/resolver.d.ts +12 -0
- package/dist/runtime/resolver.js +45 -11
- package/dist/runtime/stealth.d.ts +13 -4
- package/dist/runtime/stealth.js +362 -85
- package/dist/runtime/trace-config.js +2 -1
- package/dist/runtime/trace.d.ts +5 -0
- package/dist/runtime/trace.js +43 -10
- package/dist/server/self-test.d.ts +1 -3
- package/dist/server/self-test.js +2 -12
- package/dist/server/serve-implementation.d.ts +6 -1
- package/dist/server/serve-implementation.js +55 -40
- package/dist/server/trace-output.d.ts +3 -1
- package/dist/server/trace-output.js +61 -2
- package/dist/stealth/profiles.d.ts +9 -8
- package/dist/stealth/profiles.js +123 -286
- package/dist/types.d.ts +116 -108
- package/package.json +2 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +10 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +8 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +5 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +16 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +31 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +21 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +14 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +15 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +4 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +12 -0
- package/src/cli/commands.ts +13 -0
- package/src/cli/migrate-operation-declaration.ts +1654 -0
- package/src/cli/templates/provider/README.md.tpl +3 -3
- package/src/cli/templates/provider/operations/ping.ts.tpl +2 -0
- package/src/config/loader.ts +31 -6
- package/src/contract-types.ts +11 -5
- package/src/contract.ts +21 -10
- package/src/define.ts +107 -119
- package/src/dev.ts +4 -1
- package/src/engine.ts +279 -0
- package/src/index.ts +13 -5
- package/src/lint.ts +58 -92
- package/src/provider.ts +25 -3
- package/src/runtime/chrome149-header-order.ts +330 -0
- package/src/runtime/env.ts +13 -0
- package/src/runtime/executor.ts +7 -40
- package/src/runtime/insights.ts +2 -2
- package/src/runtime/otlp.ts +467 -21
- package/src/runtime/resolver-public.ts +3 -0
- package/src/runtime/resolver-vendors/capsolver.ts +12 -4
- package/src/runtime/resolver-vendors/twocaptcha.ts +1 -0
- package/src/runtime/resolver.ts +68 -19
- package/src/runtime/stealth.ts +435 -103
- package/src/runtime/trace-config.ts +3 -2
- package/src/runtime/trace.ts +57 -17
- package/src/server/self-test.ts +2 -9
- package/src/server/serve-implementation.ts +89 -72
- package/src/server/trace-output.ts +99 -2
- package/src/stealth/profiles.ts +169 -327
- package/src/types.ts +114 -137
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
const U64_MASK = (1n << 64n) - 1n;
|
|
2
|
+
const RAPID_SEED = 0xbdd89aa982704029n;
|
|
3
|
+
const RAPID_SECRET = [0x2d358dccaa6c78a5n, 0x8bb84b93962eacc9n, 0x4b33a62ed433d4a3n];
|
|
4
|
+
function latin1CaseFold(character) {
|
|
5
|
+
if (character >= 0x41 && character <= 0x5a)
|
|
6
|
+
return character + 0x20;
|
|
7
|
+
if (character === 0xb5)
|
|
8
|
+
return 0x03bc;
|
|
9
|
+
if ((character >= 0xc0 && character <= 0xd6) || (character >= 0xd8 && character <= 0xde))
|
|
10
|
+
return character + 0x20;
|
|
11
|
+
return character;
|
|
12
|
+
}
|
|
13
|
+
function multiply128(a, b) {
|
|
14
|
+
const product = (a & U64_MASK) * (b & U64_MASK);
|
|
15
|
+
return [product & U64_MASK, (product >> 64n) & U64_MASK];
|
|
16
|
+
}
|
|
17
|
+
function rapidMix(a, b) {
|
|
18
|
+
const [low, high] = multiply128(a, b);
|
|
19
|
+
return low ^ high;
|
|
20
|
+
}
|
|
21
|
+
class PlainByteHashReader {
|
|
22
|
+
bytes;
|
|
23
|
+
compressionFactor = 1;
|
|
24
|
+
expansionFactor = 1;
|
|
25
|
+
constructor(bytes) {
|
|
26
|
+
this.bytes = bytes;
|
|
27
|
+
}
|
|
28
|
+
read64(offset) {
|
|
29
|
+
let result = 0n;
|
|
30
|
+
for (let index = 0; index < 8; index += 1) {
|
|
31
|
+
result |= BigInt(this.bytes[offset + index] ?? 0) << BigInt(index * 8);
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
read32(offset) {
|
|
36
|
+
let result = 0n;
|
|
37
|
+
for (let index = 0; index < 4; index += 1) {
|
|
38
|
+
result |= BigInt(this.bytes[offset + index] ?? 0) << BigInt(index * 8);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
readSmall(offset, length) {
|
|
43
|
+
return ((BigInt(this.bytes[offset] ?? 0) << 56n) |
|
|
44
|
+
(BigInt(this.bytes[offset + (length >> 1)] ?? 0) << 32n) |
|
|
45
|
+
BigInt(this.bytes[offset + length - 1] ?? 0));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class CaseFoldingLatin1HashReader {
|
|
49
|
+
input;
|
|
50
|
+
compressionFactor = 1;
|
|
51
|
+
expansionFactor = 2;
|
|
52
|
+
constructor(input) {
|
|
53
|
+
this.input = input;
|
|
54
|
+
}
|
|
55
|
+
fold(offset) {
|
|
56
|
+
return BigInt(latin1CaseFold(this.input.charCodeAt(offset)));
|
|
57
|
+
}
|
|
58
|
+
read64(offset) {
|
|
59
|
+
return (this.fold(offset) |
|
|
60
|
+
(this.fold(offset + 1) << 16n) |
|
|
61
|
+
(this.fold(offset + 2) << 32n) |
|
|
62
|
+
(this.fold(offset + 3) << 48n));
|
|
63
|
+
}
|
|
64
|
+
read32(offset) {
|
|
65
|
+
return this.fold(offset) | (this.fold(offset + 1) << 16n);
|
|
66
|
+
}
|
|
67
|
+
readSmall(offset, length) {
|
|
68
|
+
if (length !== 2) {
|
|
69
|
+
throw new Error(`CaseFoldingHashReader<LChar> received length ${length}; expected 2.`);
|
|
70
|
+
}
|
|
71
|
+
return this.fold(offset);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function rapidHash(reader, producedLength) {
|
|
75
|
+
const x = reader.compressionFactor;
|
|
76
|
+
const y = reader.expansionFactor;
|
|
77
|
+
if (producedLength % y !== 0)
|
|
78
|
+
throw new Error("Invalid rapidhash reader length.");
|
|
79
|
+
let seed = RAPID_SEED;
|
|
80
|
+
seed ^= rapidMix(seed ^ RAPID_SECRET[0], RAPID_SECRET[1]) ^ BigInt(producedLength);
|
|
81
|
+
seed &= U64_MASK;
|
|
82
|
+
let offset = 0;
|
|
83
|
+
let a;
|
|
84
|
+
let b;
|
|
85
|
+
if (producedLength <= 16) {
|
|
86
|
+
if (producedLength >= 4) {
|
|
87
|
+
const last = offset + Math.trunc(((producedLength - 4) * x) / y);
|
|
88
|
+
a = (reader.read32(offset) << 32n) | reader.read32(last);
|
|
89
|
+
const delta = Math.trunc((((producedLength & 24) >> (producedLength >> 3)) * x) / y);
|
|
90
|
+
b = (reader.read32(offset + delta) << 32n) | reader.read32(last - delta);
|
|
91
|
+
}
|
|
92
|
+
else if (producedLength > 0) {
|
|
93
|
+
a = reader.readSmall(offset, producedLength);
|
|
94
|
+
b = 0n;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
a = 0n;
|
|
98
|
+
b = 0n;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
let remaining = producedLength;
|
|
103
|
+
if (remaining > 48) {
|
|
104
|
+
let see1 = seed;
|
|
105
|
+
let see2 = seed;
|
|
106
|
+
do {
|
|
107
|
+
seed = rapidMix(reader.read64(offset) ^ RAPID_SECRET[0], reader.read64(offset + Math.trunc((8 * x) / y)) ^ seed);
|
|
108
|
+
see1 = rapidMix(reader.read64(offset + Math.trunc((16 * x) / y)) ^ RAPID_SECRET[1], reader.read64(offset + Math.trunc((24 * x) / y)) ^ see1);
|
|
109
|
+
see2 = rapidMix(reader.read64(offset + Math.trunc((32 * x) / y)) ^ RAPID_SECRET[2], reader.read64(offset + Math.trunc((40 * x) / y)) ^ see2);
|
|
110
|
+
offset += Math.trunc((48 * x) / y);
|
|
111
|
+
remaining -= 48;
|
|
112
|
+
} while (remaining >= 48);
|
|
113
|
+
seed ^= see1 ^ see2;
|
|
114
|
+
}
|
|
115
|
+
if (remaining > 16) {
|
|
116
|
+
seed = rapidMix(reader.read64(offset) ^ RAPID_SECRET[2], reader.read64(offset + Math.trunc((8 * x) / y)) ^ seed ^ RAPID_SECRET[1]);
|
|
117
|
+
if (remaining > 32) {
|
|
118
|
+
seed = rapidMix(reader.read64(offset + Math.trunc((16 * x) / y)) ^ RAPID_SECRET[2], reader.read64(offset + Math.trunc((24 * x) / y)) ^ seed);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
a = reader.read64(offset + Math.trunc(((remaining - 16) * x) / y));
|
|
122
|
+
b = reader.read64(offset + Math.trunc(((remaining - 8) * x) / y));
|
|
123
|
+
}
|
|
124
|
+
a ^= RAPID_SECRET[1];
|
|
125
|
+
b ^= seed;
|
|
126
|
+
[a, b] = multiply128(a, b);
|
|
127
|
+
return rapidMix(a ^ RAPID_SECRET[0] ^ BigInt(producedLength), b ^ RAPID_SECRET[1]);
|
|
128
|
+
}
|
|
129
|
+
function maskTopEightBits(hash) {
|
|
130
|
+
const masked = Number(hash & 0xffffffn);
|
|
131
|
+
return masked === 0 ? 0x800000 : masked;
|
|
132
|
+
}
|
|
133
|
+
/** @internal Chromium 149 DeprecatedCaseFoldingHash for an LChar string. */
|
|
134
|
+
export function chrome149CaseFoldingHash(name) {
|
|
135
|
+
const reader = new CaseFoldingLatin1HashReader(name);
|
|
136
|
+
return maskTopEightBits(rapidHash(reader, name.length * reader.expansionFactor));
|
|
137
|
+
}
|
|
138
|
+
/** @internal Numeric rapidhash fixture from Chromium's string_hasher_test.cc. */
|
|
139
|
+
export function chrome149RapidhashFixture() {
|
|
140
|
+
const fixture = [0x41, 0x95, 0xff, 0x50, 0x01];
|
|
141
|
+
const full = rapidHash(new PlainByteHashReader(fixture), fixture.length);
|
|
142
|
+
return { full, masked: maskTopEightBits(full) };
|
|
143
|
+
}
|
|
144
|
+
function asciiCaseKey(name) {
|
|
145
|
+
let result = "";
|
|
146
|
+
for (let index = 0; index < name.length; index += 1) {
|
|
147
|
+
const character = name.charCodeAt(index);
|
|
148
|
+
result += String.fromCharCode(character >= 0x41 && character <= 0x5a ? character + 0x20 : character);
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
class WtfHeaderHashMap {
|
|
153
|
+
table = [];
|
|
154
|
+
keyCount = 0;
|
|
155
|
+
insertWithoutExpansion(key) {
|
|
156
|
+
const mask = this.table.length - 1;
|
|
157
|
+
let index = chrome149CaseFoldingHash(key) & mask;
|
|
158
|
+
let probeCount = 0;
|
|
159
|
+
const keyCaseFolded = asciiCaseKey(key);
|
|
160
|
+
for (;;) {
|
|
161
|
+
const stored = this.table[index];
|
|
162
|
+
if (stored === undefined) {
|
|
163
|
+
this.table[index] = key;
|
|
164
|
+
this.keyCount += 1;
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
if (asciiCaseKey(stored) === keyCaseFolded)
|
|
168
|
+
return false;
|
|
169
|
+
probeCount += 1;
|
|
170
|
+
index = (index + probeCount) & mask;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
rehash(newSize) {
|
|
174
|
+
const oldTable = this.table;
|
|
175
|
+
this.table = Array(newSize);
|
|
176
|
+
this.keyCount = 0;
|
|
177
|
+
for (const key of oldTable) {
|
|
178
|
+
if (key !== undefined)
|
|
179
|
+
this.insertWithoutExpansion(key);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
insert(key) {
|
|
183
|
+
if (this.table.length === 0)
|
|
184
|
+
this.table = Array(8);
|
|
185
|
+
if (!this.insertWithoutExpansion(key))
|
|
186
|
+
return;
|
|
187
|
+
if (this.keyCount * 2 >= this.table.length)
|
|
188
|
+
this.rehash(this.table.length * 2);
|
|
189
|
+
}
|
|
190
|
+
iterationOrder() {
|
|
191
|
+
return this.table.filter((key) => key !== undefined);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* HTTPHeaderMap insertion sequence of the fixed headers in a real Chrome
|
|
196
|
+
* 149.0.7827.155 page fetch/XHR (insertion order, not bucket order). Caller
|
|
197
|
+
* headers precede all of these (core/fetch/fetch_manager.cc:1128-1130).
|
|
198
|
+
*
|
|
199
|
+
* platform/loader/fetch/resource_request_utils.cc:146 UpgradeResourceRequestForLoader
|
|
200
|
+
* :166 context.UpgradeResourceRequestForLoader
|
|
201
|
+
* -> core/loader/frame_fetch_context.cc:1000 AddClientHintsIfNecessary
|
|
202
|
+
* Set sec-ch-ua @737, sec-ch-ua-mobile @748, sec-ch-ua-platform @761
|
|
203
|
+
* -> :1001 AddReducedAcceptLanguageIfNecessary is a no-op
|
|
204
|
+
* (kReduceAcceptLanguage[HTTP] are FEATURE_DISABLED_BY_DEFAULT,
|
|
205
|
+
* services/network/public/cpp/features.cc:204,214)
|
|
206
|
+
* :214 context.PrepareRequest
|
|
207
|
+
* -> core/loader/frame_fetch_context.cc:419 SetHTTPUserAgent
|
|
208
|
+
* -> :455 probe::PrepareRequest, which only touches the map when a
|
|
209
|
+
* DevTools session has set an Accept-Language override:
|
|
210
|
+
* InspectorEmulationAgent::PrepareRequest (inspector_emulation_agent.cc
|
|
211
|
+
* :626-636, Emulation.setUserAgentOverride acceptLanguage; skips a key
|
|
212
|
+
* the page already set) or InspectorNetworkAgent::PrepareRequest
|
|
213
|
+
* (inspector_network_agent.cc:1524-1547, Network.setExtraHTTPHeaders;
|
|
214
|
+
* overwrites)
|
|
215
|
+
*
|
|
216
|
+
* Accept-Language is therefore NOT a map key in real Chrome. //net appends it
|
|
217
|
+
* in URLRequestHttpJob::AddExtraHeaders (net/url_request/url_request_http_job.cc
|
|
218
|
+
* :784-797) after Accept-Encoding, and only when the request does not already
|
|
219
|
+
* carry one (SetHeaderIfMissing), which is why a caller-supplied Accept-Language
|
|
220
|
+
* stays at its map bucket and is never emitted twice. al-placement-capture.json
|
|
221
|
+
* (B: no locale, C: --accept-lang=ja) confirms both the four-key map and the
|
|
222
|
+
* downstream slot.
|
|
223
|
+
*
|
|
224
|
+
* Every earlier corpus (placement-rule-sweep, expansion-sweep, holdout-capture,
|
|
225
|
+
* m1-capture) was captured through Playwright `newContext({ locale })`, which
|
|
226
|
+
* sets that override (Emulation.setUserAgentOverride acceptLanguage) and so
|
|
227
|
+
* inserts Accept-Language into the map as a fifth fixed key. Those fixtures still
|
|
228
|
+
* validate the hash and table mechanics;
|
|
229
|
+
* the tests interpret them through that harness insertion list. No production
|
|
230
|
+
* path uses it.
|
|
231
|
+
*/
|
|
232
|
+
export const CHROME149_FIXED_MAP_INSERTION = [
|
|
233
|
+
"sec-ch-ua",
|
|
234
|
+
"sec-ch-ua-mobile",
|
|
235
|
+
"sec-ch-ua-platform",
|
|
236
|
+
"User-Agent",
|
|
237
|
+
];
|
|
238
|
+
/**
|
|
239
|
+
* Appended after PopulateResourceRequest has iterated the map: Fetch Metadata
|
|
240
|
+
* and Referer by the network service, then //net's Accept-Encoding and
|
|
241
|
+
* Accept-Language, then the HTTP/2 Priority header (net/spdy/spdy_http_utils.cc).
|
|
242
|
+
*/
|
|
243
|
+
const DOWNSTREAM_SUFFIX = [
|
|
244
|
+
"sec-fetch-site",
|
|
245
|
+
"sec-fetch-mode",
|
|
246
|
+
"sec-fetch-dest",
|
|
247
|
+
"referer",
|
|
248
|
+
];
|
|
249
|
+
/**
|
|
250
|
+
* @internal Header-name order model parameterised by the fixed map insertion
|
|
251
|
+
* sequence. Production always passes {@link CHROME149_FIXED_MAP_INSERTION};
|
|
252
|
+
* tests pass the DevTools-harness sequence to validate corpora captured through
|
|
253
|
+
* Playwright's `locale` option.
|
|
254
|
+
*/
|
|
255
|
+
export function chrome149HeaderOrderForMapInsertion(callerNames, fixedMapInsertion) {
|
|
256
|
+
const unique = new Map();
|
|
257
|
+
for (const name of callerNames) {
|
|
258
|
+
const key = asciiCaseKey(name);
|
|
259
|
+
if (!unique.has(key))
|
|
260
|
+
unique.set(key, name);
|
|
261
|
+
}
|
|
262
|
+
const headerMap = new WtfHeaderHashMap();
|
|
263
|
+
for (const key of [...unique.keys()].sort())
|
|
264
|
+
headerMap.insert(unique.get(key));
|
|
265
|
+
for (const name of fixedMapInsertion)
|
|
266
|
+
headerMap.insert(name);
|
|
267
|
+
const order = headerMap.iterationOrder().map((name) => name.toLowerCase());
|
|
268
|
+
// SetAcceptHeader adds */* only when the caller Accept was absent.
|
|
269
|
+
if (!order.includes("accept"))
|
|
270
|
+
order.push("accept");
|
|
271
|
+
order.push(...DOWNSTREAM_SUFFIX);
|
|
272
|
+
// URLRequestHttpJob::AddExtraHeaders: Accept-Encoding, then Accept-Language,
|
|
273
|
+
// each only if the request does not already carry it.
|
|
274
|
+
if (!order.includes("accept-encoding"))
|
|
275
|
+
order.push("accept-encoding");
|
|
276
|
+
if (!order.includes("accept-language"))
|
|
277
|
+
order.push("accept-language");
|
|
278
|
+
// CreateSpdyHeadersFromHttpRequest appends the default Priority only if absent.
|
|
279
|
+
if (!order.includes("priority"))
|
|
280
|
+
order.push("priority");
|
|
281
|
+
return order;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* @internal Predict real Chrome 149's non-pseudo HTTP/2 fetch/XHR header-name
|
|
285
|
+
* order for the given caller header names (no DevTools session attached).
|
|
286
|
+
*/
|
|
287
|
+
export function chrome149HeaderOrder(callerNames) {
|
|
288
|
+
return chrome149HeaderOrderForMapInsertion(callerNames, CHROME149_FIXED_MAP_INSERTION);
|
|
289
|
+
}
|
package/dist/runtime/env.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
import { isEngineOwnedEnvName } from "../engine.js";
|
|
2
|
+
/**
|
|
3
|
+
* A mixed-case spelling of an engine-owned name is an alias: on Windows it resolves to the
|
|
4
|
+
* engine's variable while evading an exact-name allowlist. Only the exact (upper-case) name,
|
|
5
|
+
* which engine-internal contexts allowlist explicitly, may read those values.
|
|
6
|
+
*/
|
|
7
|
+
function isEngineOwnedEnvAlias(key) {
|
|
8
|
+
return isEngineOwnedEnvName(key) && key !== key.toUpperCase();
|
|
9
|
+
}
|
|
1
10
|
export function createEnvContext(allowedKeys) {
|
|
2
11
|
return {
|
|
3
12
|
get(key) {
|
|
4
13
|
if (allowedKeys && !allowedKeys.includes(key)) {
|
|
5
14
|
return undefined;
|
|
6
15
|
}
|
|
16
|
+
if (isEngineOwnedEnvAlias(key)) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
7
19
|
return process.env[key];
|
|
8
20
|
},
|
|
9
21
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProviderDefinition } from "../types.js";
|
|
1
|
+
import type { EnvContext, ProviderDefinition } from "../types.js";
|
|
2
2
|
export declare function isStreamingOperation(provider: ProviderDefinition, operationId: string): boolean;
|
|
3
3
|
/**
|
|
4
4
|
* Execute a provider operation by calling its handler.
|
|
@@ -13,4 +13,5 @@ export declare function isStreamingOperation(provider: ProviderDefinition, opera
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function executeOperation<const TProvider extends ProviderDefinition, const TOperationId extends keyof TProvider["operations"] & string>(provider: TProvider, operationId: TOperationId, ctx: NoInfer<Parameters<TProvider["operations"][TOperationId]["handler"]>[0]>, input: unknown, _options?: {
|
|
15
15
|
skipAuth?: boolean;
|
|
16
|
+
env?: EnvContext;
|
|
16
17
|
}): Promise<unknown>;
|
package/dist/runtime/executor.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isSessionExpiredError, isValidationError, ProviderError, SessionExpiredError, ValidationError, } from "../errors.js";
|
|
1
|
+
import { isValidationError, ProviderError, ValidationError, } from "../errors.js";
|
|
3
2
|
import { z } from "zod";
|
|
4
3
|
import { parseSchema } from "../schema.js";
|
|
5
4
|
import { assertRequiredSecretsPresent } from "./secrets.js";
|
|
@@ -7,12 +6,6 @@ export function isStreamingOperation(provider, operationId) {
|
|
|
7
6
|
const kind = provider.operations[operationId]?.transport?.kind ?? "json";
|
|
8
7
|
return kind !== "json";
|
|
9
8
|
}
|
|
10
|
-
function preservedSessionExpiredOptions(error) {
|
|
11
|
-
const observability = safeProviderErrorObservability(error);
|
|
12
|
-
return observability
|
|
13
|
-
? { observability, retryable: true }
|
|
14
|
-
: { retryable: true };
|
|
15
|
-
}
|
|
16
9
|
/**
|
|
17
10
|
* Execute a provider operation by calling its handler.
|
|
18
11
|
*
|
|
@@ -38,37 +31,11 @@ export async function executeOperation(provider, operationId, ctx, input, _optio
|
|
|
38
31
|
// record) fails with the same structured MISSING_SECRET error instead of a
|
|
39
32
|
// handler-specific crash. Providers must not re-check presence locally.
|
|
40
33
|
if (provider.secrets?.some((secret) => secret.required === true)) {
|
|
41
|
-
assertRequiredSecretsPresent(provider, "env" in ctx ? ctx.env : { get: () => undefined });
|
|
34
|
+
assertRequiredSecretsPresent(provider, _options?.env ?? ("env" in ctx ? ctx.env : { get: () => undefined }));
|
|
42
35
|
}
|
|
43
36
|
const validatedInput = await parseSchema(operation.input, input, `operations.${operationId}.input`);
|
|
44
37
|
const execute = () => ctx.trace.span(`handler:${operationId}`, () => Promise.resolve(operation.handler(ctx, validatedInput)));
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
result = await execute();
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
// Session expiry is renewed by Credential Service via the /auth/refresh
|
|
51
|
-
// route, NOT in-process here: this executor cannot mutate ctx.credential,
|
|
52
|
-
// so an in-process retry would just repeat the call with the same stale
|
|
53
|
-
// credential (and risk repeating partial side-effects). Instead we surface
|
|
54
|
-
// the expiry so Credential Service refreshes and re-drives the operation
|
|
55
|
-
// with a fresh credential. `retryOnAuthRefresh` declares that this
|
|
56
|
-
// operation is safe to re-drive after refresh, which we signal by marking
|
|
57
|
-
// the surfaced error retryable; non-idempotent operations (the default)
|
|
58
|
-
// stay non-retryable so they are not auto-re-driven. See design.md §4.3 D3.
|
|
59
|
-
// Use the branded guard, not `instanceof`: a handler loaded through a
|
|
60
|
-
// duplicate/published SDK module can throw a correctly branded
|
|
61
|
-
// SessionExpiredError whose constructor identity differs from this
|
|
62
|
-
// executor's, which `instanceof` would miss — dropping the retryable
|
|
63
|
-
// upgrade and stranding an operation that opted into auth refresh.
|
|
64
|
-
if (isSessionExpiredError(error) && operation.retryOnAuthRefresh) {
|
|
65
|
-
// Preserve provider-authored safe metadata while forcing the retry signal.
|
|
66
|
-
// `cause` intentionally remains dropped, matching the pre-existing
|
|
67
|
-
// reconstruction semantics.
|
|
68
|
-
throw new SessionExpiredError(error.message, preservedSessionExpiredOptions(error));
|
|
69
|
-
}
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
38
|
+
const result = await execute();
|
|
72
39
|
if (isStreamingOperation(provider, operationId)) {
|
|
73
40
|
return result;
|
|
74
41
|
}
|
package/dist/runtime/insights.js
CHANGED
|
@@ -4,7 +4,7 @@ const SLOW_TRANSFORM_MS = 10;
|
|
|
4
4
|
const DNS_WARN_MS = 5;
|
|
5
5
|
const BROWSER_IDLE_MS = 5_000;
|
|
6
6
|
const REFRESH_WARN_RATE = 0.1;
|
|
7
|
-
const TLS_REUSE_FIX = `const session = ctx.stealth.createSession({
|
|
7
|
+
const TLS_REUSE_FIX = `const session = ctx.stealth.createSession({ stealth: { browser: 'chrome', os: 'macos' } });
|
|
8
8
|
const resp = await session.fetch(url, opts);`;
|
|
9
9
|
const TRANSFORM_FIX = `transformResponse: (raw) => {
|
|
10
10
|
return raw.items.map(({ id, name, price }) => ({ id, name, price }));
|
|
@@ -13,7 +13,7 @@ const LARGE_RESPONSE_FIX = `const resp = await ctx.http.get('/items', {
|
|
|
13
13
|
params: { limit: 50, page: 1 },
|
|
14
14
|
});`;
|
|
15
15
|
const DNS_FIX = `// Enable DNS caching or reuse a long-lived session per host.
|
|
16
|
-
const session = ctx.stealth.createSession({
|
|
16
|
+
const session = ctx.stealth.createSession({ stealth: { browser: 'chrome', os: 'macos' } });
|
|
17
17
|
await session.fetch(url, opts);`;
|
|
18
18
|
const PROXY_FIX = `// Re-check whether this operation really needs a proxy.
|
|
19
19
|
await ctx.stealth.fetch(url, { ...opts, proxy: undefined });`;
|
package/dist/runtime/otlp.d.ts
CHANGED
|
@@ -4,7 +4,54 @@ export interface OTLPExportOptions {
|
|
|
4
4
|
headers?: Record<string, string>;
|
|
5
5
|
timeout?: number;
|
|
6
6
|
}
|
|
7
|
-
export declare
|
|
7
|
+
export declare const OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
|
|
8
|
+
export declare const OTEL_EXPORTER_OTLP_ENDPOINT = "OTEL_EXPORTER_OTLP_ENDPOINT";
|
|
9
|
+
export declare const OTEL_EXPORTER_OTLP_TRACES_HEADERS = "OTEL_EXPORTER_OTLP_TRACES_HEADERS";
|
|
10
|
+
export declare const OTEL_EXPORTER_OTLP_HEADERS = "OTEL_EXPORTER_OTLP_HEADERS";
|
|
11
|
+
export declare const OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME";
|
|
12
|
+
export declare const OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES";
|
|
13
|
+
type EnvLike = Record<string, string | undefined>;
|
|
14
|
+
/**
|
|
15
|
+
* `invalid` carries a fixed-vocabulary `reason` and the name of the offending
|
|
16
|
+
* source so callers can warn without ever echoing the configured value.
|
|
17
|
+
*/
|
|
18
|
+
export type OTLPExportResolution = {
|
|
19
|
+
status: "resolved";
|
|
20
|
+
options: OTLPExportOptions;
|
|
21
|
+
} | {
|
|
22
|
+
status: "unconfigured";
|
|
23
|
+
} | {
|
|
24
|
+
status: "invalid";
|
|
25
|
+
source: string;
|
|
26
|
+
reason: string;
|
|
27
|
+
};
|
|
28
|
+
export type OTLPResourceResolution = {
|
|
29
|
+
attributes: Record<string, string>;
|
|
30
|
+
/** Environment variables whose whole value was discarded because it could not be parsed. */
|
|
31
|
+
discarded: string[];
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Resolves the OTLP/HTTP export target from explicit configuration and the
|
|
35
|
+
* standard OpenTelemetry environment contract. Endpoint precedence is explicit
|
|
36
|
+
* config, then OTEL_EXPORTER_OTLP_TRACES_ENDPOINT verbatim, then
|
|
37
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT with `/v1/traces` appended. The winning candidate
|
|
38
|
+
* must be an absolute http(s) URL; an invalid one fails closed instead of
|
|
39
|
+
* falling through to a lower-precedence destination. Header values are never
|
|
40
|
+
* surfaced in the result beyond the request options themselves.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveOTLPExportOptions(explicit: {
|
|
43
|
+
endpoint?: string;
|
|
44
|
+
headers?: Record<string, string>;
|
|
45
|
+
timeout?: number;
|
|
46
|
+
}, env?: EnvLike): OTLPExportResolution;
|
|
47
|
+
/**
|
|
48
|
+
* Merges OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME under the caller's
|
|
49
|
+
* explicit resource attributes: explicit wins per key, OTEL_SERVICE_NAME wins
|
|
50
|
+
* over a `service.name` inside OTEL_RESOURCE_ATTRIBUTES. An unparseable
|
|
51
|
+
* OTEL_RESOURCE_ATTRIBUTES value is discarded as a unit and reported in `discarded`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveOTLPResourceAttributes(explicit: Record<string, string>, env?: EnvLike): OTLPResourceResolution;
|
|
54
|
+
export declare function spansToOTLP(spans: TraceSpan[], resourceAttributes?: Record<string, string>, traceId?: string): {
|
|
8
55
|
resourceSpans: Array<{
|
|
9
56
|
resource: {
|
|
10
57
|
attributes: Array<{
|
|
@@ -36,4 +83,26 @@ export declare function spansToOTLP(spans: TraceSpan[], resourceAttributes?: Rec
|
|
|
36
83
|
}>;
|
|
37
84
|
}>;
|
|
38
85
|
};
|
|
39
|
-
|
|
86
|
+
type ExportTransport = typeof fetch;
|
|
87
|
+
/** Process-wide bounds so a collector outage can never turn into unbounded sockets, memory, or log volume. */
|
|
88
|
+
export declare const OTLP_EXPORT_LIMITS: {
|
|
89
|
+
readonly maxInFlight: 4;
|
|
90
|
+
readonly maxQueued: 64;
|
|
91
|
+
readonly maxAttempts: 3;
|
|
92
|
+
readonly retryBaseDelayMs: 200;
|
|
93
|
+
readonly retryMaxDelayMs: 2000;
|
|
94
|
+
readonly warningCooldownMs: 10000;
|
|
95
|
+
};
|
|
96
|
+
/** Exponential backoff between attempts: base * 2^(attempt-1), capped at the maximum delay. */
|
|
97
|
+
export declare function retryDelayMs(attempt: number): number;
|
|
98
|
+
/**
|
|
99
|
+
* Queues one export batch behind the process-wide concurrency and queue bounds.
|
|
100
|
+
* Resolves once the batch has been delivered or dropped; it never rejects, so
|
|
101
|
+
* callers can fire and forget.
|
|
102
|
+
*/
|
|
103
|
+
export declare function exportSpansOTLP(spans: TraceSpan[], options: OTLPExportOptions, resourceAttributes?: Record<string, string>, traceId?: string): Promise<void>;
|
|
104
|
+
/** Test seam (not re-exported from any package entry point): substitute the engine transport. */
|
|
105
|
+
export declare function swapOTLPTransportForTests(next?: ExportTransport): void;
|
|
106
|
+
/** Test seam: drop queued batches, clear the warning throttle, and restore the engine transport. */
|
|
107
|
+
export declare function resetOTLPExportForTests(): void;
|
|
108
|
+
export {};
|