@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.21

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.
Files changed (291) hide show
  1. package/AUTHORING.md +487 -0
  2. package/CHANGELOG.md +90 -0
  3. package/README.md +49 -3
  4. package/SUBMISSION.md +1 -1
  5. package/bin/apifuse-check.ts +44 -59
  6. package/bin/apifuse-create.ts +1 -1
  7. package/bin/apifuse-dev.ts +27 -52
  8. package/bin/apifuse-pack-check.ts +14 -0
  9. package/bin/apifuse-pack-smoke.ts +36 -81
  10. package/bin/apifuse-pack-types.ts +305 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +659 -111
  13. package/bin/apifuse-submit-check.ts +546 -48
  14. package/bin/apifuse-sync-assets.ts +117 -0
  15. package/bin/apifuse.ts +1 -1
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/bin/submit-check-xml-semantics.ts +204 -0
  18. package/bin/submit-check-xml.ts +134 -0
  19. package/dist/auth-turn/index.d.ts +3 -3
  20. package/dist/auth.d.ts +2 -2
  21. package/dist/auth.js +9 -18
  22. package/dist/ceremonies/index.d.ts +9 -1
  23. package/dist/ceremonies/index.js +65 -18
  24. package/dist/cli/commands.d.ts +1 -1
  25. package/dist/cli/commands.js +8 -0
  26. package/dist/cli/create.d.ts +3 -0
  27. package/dist/cli/create.js +34 -35
  28. package/dist/cli/prompt-assets.d.ts +80 -0
  29. package/dist/cli/prompt-assets.js +743 -0
  30. package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
  31. package/dist/config/loader.d.ts +176 -8
  32. package/dist/config/loader.js +424 -95
  33. package/dist/contract-serialization.d.ts +2 -2
  34. package/dist/contract-serialization.js +3 -6
  35. package/dist/contract-types.d.ts +2 -2
  36. package/dist/contract.d.ts +3 -3
  37. package/dist/contract.js +4 -6
  38. package/dist/define.d.ts +9 -1
  39. package/dist/define.js +304 -119
  40. package/dist/dev.d.ts +1 -1
  41. package/dist/dev.js +1 -1
  42. package/dist/error-resolution.d.ts +2 -0
  43. package/dist/error-resolution.js +90 -0
  44. package/dist/errors.d.ts +22 -1
  45. package/dist/errors.js +88 -0
  46. package/dist/fixture-sanitization.d.ts +26 -0
  47. package/dist/fixture-sanitization.js +216 -0
  48. package/dist/i18n/catalog.d.ts +2 -2
  49. package/dist/i18n/catalog.js +4 -10
  50. package/dist/i18n/index.d.ts +2 -2
  51. package/dist/i18n/index.js +2 -2
  52. package/dist/i18n/keys.d.ts +2 -2
  53. package/dist/index.d.ts +46 -42
  54. package/dist/index.js +41 -37
  55. package/dist/lint.d.ts +1 -1
  56. package/dist/lint.js +8 -15
  57. package/dist/native-address.d.ts +43 -0
  58. package/dist/native-address.js +281 -0
  59. package/dist/native-egress-policy.d.ts +31 -0
  60. package/dist/native-egress-policy.js +288 -0
  61. package/dist/observability.d.ts +5 -2
  62. package/dist/observability.js +48 -1
  63. package/dist/provider.d.ts +12 -11
  64. package/dist/provider.js +10 -9
  65. package/dist/public-schema-field-lint.d.ts +1 -1
  66. package/dist/recipes/gov-api.js +1 -1
  67. package/dist/runtime/auth-flow.d.ts +2 -1
  68. package/dist/runtime/auth-flow.js +4 -3
  69. package/dist/runtime/browser.d.ts +1 -1
  70. package/dist/runtime/browser.js +15 -29
  71. package/dist/runtime/cache.d.ts +1 -1
  72. package/dist/runtime/cache.js +4 -8
  73. package/dist/runtime/choice.d.ts +1 -1
  74. package/dist/runtime/choice.js +31 -35
  75. package/dist/runtime/credential.d.ts +1 -1
  76. package/dist/runtime/credential.js +1 -1
  77. package/dist/runtime/env.d.ts +1 -1
  78. package/dist/runtime/executor.d.ts +1 -1
  79. package/dist/runtime/executor.js +31 -4
  80. package/dist/runtime/http.d.ts +2 -2
  81. package/dist/runtime/http.js +387 -47
  82. package/dist/runtime/insights.d.ts +1 -1
  83. package/dist/runtime/insights.js +6 -13
  84. package/dist/runtime/instrumentation.d.ts +2 -2
  85. package/dist/runtime/instrumentation.js +345 -22
  86. package/dist/runtime/keyring.js +1 -1
  87. package/dist/runtime/namespace.js +1 -1
  88. package/dist/runtime/native-network.d.ts +127 -0
  89. package/dist/runtime/native-network.js +1298 -0
  90. package/dist/runtime/otlp.d.ts +1 -1
  91. package/dist/runtime/perf.d.ts +1 -1
  92. package/dist/runtime/provider.d.ts +1 -1
  93. package/dist/runtime/provider.js +1 -2
  94. package/dist/runtime/proxy-errors.d.ts +1 -1
  95. package/dist/runtime/proxy-errors.js +9 -7
  96. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  97. package/dist/runtime/proxy-nodemaven.js +146 -0
  98. package/dist/runtime/proxy-retry-policy.d.ts +2 -2
  99. package/dist/runtime/proxy-retry-policy.js +2 -2
  100. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  101. package/dist/runtime/proxy-telemetry.js +55 -52
  102. package/dist/runtime/redirects.d.ts +29 -0
  103. package/dist/runtime/redirects.js +36 -0
  104. package/dist/runtime/redis.d.ts +1 -1
  105. package/dist/runtime/redis.js +2 -4
  106. package/dist/runtime/request-options.d.ts +68 -1
  107. package/dist/runtime/request-options.js +548 -0
  108. package/dist/runtime/secrets.d.ts +27 -0
  109. package/dist/runtime/secrets.js +51 -0
  110. package/dist/runtime/state.d.ts +2 -2
  111. package/dist/runtime/state.js +238 -26
  112. package/dist/runtime/stealth.d.ts +5 -3
  113. package/dist/runtime/stealth.js +423 -143
  114. package/dist/runtime/stt.d.ts +1 -1
  115. package/dist/runtime/stt.js +11 -15
  116. package/dist/runtime/trace.d.ts +2 -2
  117. package/dist/runtime/trace.js +2 -4
  118. package/dist/runtime/waterfall.d.ts +1 -1
  119. package/dist/schema.d.ts +1 -1
  120. package/dist/schema.js +7 -15
  121. package/dist/serve.d.ts +1 -1
  122. package/dist/serve.js +1 -1
  123. package/dist/server/index.d.ts +7 -7
  124. package/dist/server/index.js +6 -6
  125. package/dist/server/self-test-input-tokens.d.ts +2 -1
  126. package/dist/server/self-test-input-tokens.js +18 -14
  127. package/dist/server/self-test-redaction.d.ts +1 -1
  128. package/dist/server/self-test-redaction.js +1 -1
  129. package/dist/server/self-test.d.ts +104 -3
  130. package/dist/server/self-test.js +673 -115
  131. package/dist/server/serve.d.ts +116 -4
  132. package/dist/server/serve.js +799 -128
  133. package/dist/server/types.d.ts +34 -9
  134. package/dist/server/types.js +8 -1
  135. package/dist/stateful/errors.d.ts +14 -0
  136. package/dist/stateful/errors.js +14 -0
  137. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  138. package/dist/stateful/http-provider-event-emitter.js +237 -0
  139. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  140. package/dist/stateful/http-session-owner-registry.js +210 -0
  141. package/dist/stateful/index.d.ts +18 -0
  142. package/dist/stateful/index.js +18 -0
  143. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  144. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  145. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  146. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  147. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  148. package/dist/stateful/provider-event-pipeline.js +1 -0
  149. package/dist/stateful/provider-events.d.ts +101 -0
  150. package/dist/stateful/provider-events.js +289 -0
  151. package/dist/stateful/session-key.d.ts +15 -0
  152. package/dist/stateful/session-key.js +86 -0
  153. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  154. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  155. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  156. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  157. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  158. package/dist/stateful/stateful-provider-adapter.js +287 -0
  159. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  160. package/dist/stateful/stateful-provider-observability.js +161 -0
  161. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  162. package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
  163. package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
  164. package/dist/stateful/stateful-provider-runtime-context.js +60 -0
  165. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  166. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  167. package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
  168. package/dist/stateful/stateful-provider-session-routing.js +353 -0
  169. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  170. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  171. package/dist/stateful-signing.d.ts +18 -0
  172. package/dist/stateful-signing.js +27 -0
  173. package/dist/stealth/profiles.d.ts +1 -1
  174. package/dist/stealth/profiles.js +5 -14
  175. package/dist/stream-evidence.d.ts +74 -0
  176. package/dist/stream-evidence.js +785 -0
  177. package/dist/stream.d.ts +1 -1
  178. package/dist/testing/index.d.ts +2 -2
  179. package/dist/testing/index.js +2 -2
  180. package/dist/testing/run.d.ts +32 -2
  181. package/dist/testing/run.js +478 -28
  182. package/dist/types.d.ts +342 -14
  183. package/dist/types.js +1 -0
  184. package/dist/user-input.d.ts +30 -0
  185. package/dist/user-input.js +66 -0
  186. package/package.json +16 -5
  187. package/src/auth-turn/index.ts +1 -1
  188. package/src/auth.ts +28 -86
  189. package/src/ceremonies/index.ts +103 -78
  190. package/src/cli/commands.ts +10 -0
  191. package/src/cli/create.ts +42 -35
  192. package/src/cli/prompt-assets.ts +865 -0
  193. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  194. package/src/config/loader.ts +652 -204
  195. package/src/contract-serialization.ts +5 -11
  196. package/src/contract-types.ts +2 -2
  197. package/src/contract.ts +12 -28
  198. package/src/define.ts +499 -498
  199. package/src/dev.ts +4 -9
  200. package/src/error-resolution.ts +91 -0
  201. package/src/errors.ts +110 -5
  202. package/src/fixture-sanitization.ts +247 -0
  203. package/src/i18n/catalog.ts +10 -32
  204. package/src/i18n/index.ts +2 -2
  205. package/src/i18n/keys.ts +5 -11
  206. package/src/index.ts +112 -42
  207. package/src/lint.ts +88 -152
  208. package/src/native-address.ts +340 -0
  209. package/src/native-egress-policy.ts +358 -0
  210. package/src/observability.ts +51 -1
  211. package/src/provider.ts +66 -11
  212. package/src/public-schema-field-lint.ts +7 -33
  213. package/src/recipes/gov-api.ts +2 -5
  214. package/src/runtime/auth-flow.ts +7 -7
  215. package/src/runtime/browser.ts +64 -187
  216. package/src/runtime/cache.ts +20 -67
  217. package/src/runtime/choice.ts +79 -132
  218. package/src/runtime/credential.ts +2 -2
  219. package/src/runtime/env.ts +1 -1
  220. package/src/runtime/executor.ts +43 -20
  221. package/src/runtime/http.ts +494 -57
  222. package/src/runtime/insights.ts +15 -53
  223. package/src/runtime/instrumentation.ts +495 -66
  224. package/src/runtime/keyring.ts +7 -19
  225. package/src/runtime/namespace.ts +2 -7
  226. package/src/runtime/native-network.ts +1686 -0
  227. package/src/runtime/otlp.ts +12 -23
  228. package/src/runtime/perf.ts +1 -1
  229. package/src/runtime/provider.ts +4 -9
  230. package/src/runtime/proxy-errors.ts +29 -42
  231. package/src/runtime/proxy-nodemaven.ts +221 -0
  232. package/src/runtime/proxy-retry-policy.ts +3 -3
  233. package/src/runtime/proxy-telemetry.ts +79 -77
  234. package/src/runtime/redirects.ts +66 -0
  235. package/src/runtime/redis.ts +4 -12
  236. package/src/runtime/request-options.ts +679 -9
  237. package/src/runtime/secrets.ts +64 -0
  238. package/src/runtime/state.ts +353 -133
  239. package/src/runtime/stealth.ts +505 -154
  240. package/src/runtime/stt.ts +38 -94
  241. package/src/runtime/trace.ts +14 -44
  242. package/src/runtime/waterfall.ts +5 -18
  243. package/src/schema.ts +23 -84
  244. package/src/serve.ts +1 -1
  245. package/src/server/index.ts +29 -7
  246. package/src/server/self-test-input-tokens.ts +29 -14
  247. package/src/server/self-test-redaction.ts +2 -2
  248. package/src/server/self-test.ts +857 -132
  249. package/src/server/serve.ts +1151 -328
  250. package/src/server/types.ts +12 -13
  251. package/src/stateful/README.md +146 -0
  252. package/src/stateful/errors.ts +23 -0
  253. package/src/stateful/http-provider-event-emitter.ts +314 -0
  254. package/src/stateful/http-session-owner-registry.ts +306 -0
  255. package/src/stateful/index.ts +18 -0
  256. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  257. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  258. package/src/stateful/provider-event-pipeline.ts +61 -0
  259. package/src/stateful/provider-events.ts +462 -0
  260. package/src/stateful/session-key.ts +111 -0
  261. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  262. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  263. package/src/stateful/stateful-provider-adapter.ts +562 -0
  264. package/src/stateful/stateful-provider-observability.ts +261 -0
  265. package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
  266. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  267. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  268. package/src/stateful/stateful-provider-session-routing.ts +555 -0
  269. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  270. package/src/stateful-signing.ts +46 -0
  271. package/src/stealth/profiles.ts +10 -26
  272. package/src/stream-evidence.ts +988 -0
  273. package/src/stream.ts +8 -19
  274. package/src/testing/index.ts +10 -2
  275. package/src/testing/run.ts +653 -74
  276. package/src/types.ts +408 -28
  277. package/src/user-input.ts +118 -0
  278. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  279. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  280. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  281. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  282. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  283. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  284. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  285. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  286. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  287. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  288. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  289. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  290. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  291. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
@@ -1,11 +1,13 @@
1
- import { createTraceContext, getTraceRecorder, } from "./trace";
2
- const BROWSER_PAGE_METHODS = new Set([
3
- "goto",
4
- "fill",
5
- "click",
6
- "type",
7
- "waitForSelector",
8
- ]);
1
+ import { readableBytes, readableLines, readableTextChunks } from "../stream.js";
2
+ import { parseHttpRequestInvocation, isSensitiveKey, redactSensitiveError, redactSensitiveText, redactUrlQueryParams, requestOptionsFromHttpInvocation, serializeRequestUrl, } from "./request-options.js";
3
+ import { createTraceContext, getTraceRecorder, } from "./trace.js";
4
+ const BROWSER_PAGE_METHODS = new Set(["goto", "fill", "click", "type", "waitForSelector"]);
5
+ const DIAGNOSTIC_BASE_URL = "http://apifuse-instrumentation.invalid";
6
+ function isThenable(value) {
7
+ return ((typeof value === "object" || typeof value === "function") &&
8
+ value !== null &&
9
+ typeof value.then === "function");
10
+ }
9
11
  function getErrorStatus(error) {
10
12
  if (typeof error === "object" &&
11
13
  error !== null &&
@@ -52,8 +54,173 @@ function getUrl(namespace, args, result) {
52
54
  }
53
55
  return undefined;
54
56
  }
57
+ function requestOptionsForInvocation(namespace, methodName, args) {
58
+ if (namespace === "http") {
59
+ const invocation = parseHttpRequestInvocation(methodName, [...args]);
60
+ return invocation ? requestOptionsFromHttpInvocation(invocation) : undefined;
61
+ }
62
+ if (namespace !== "stealth" || methodName !== "fetch" || typeof args[0] !== "string") {
63
+ return undefined;
64
+ }
65
+ const options = args[1];
66
+ return options !== null && typeof options === "object" && !Array.isArray(options)
67
+ ? options
68
+ : undefined;
69
+ }
70
+ function fallbackSensitiveValues(options) {
71
+ const sensitiveParams = options?.sensitiveParams;
72
+ if (!sensitiveParams || typeof sensitiveParams !== "object")
73
+ return [];
74
+ return Object.values(sensitiveParams).map(String);
75
+ }
76
+ function stripDiagnosticBase(url) {
77
+ return url.startsWith(DIAGNOSTIC_BASE_URL) ? url.slice(DIAGNOSTIC_BASE_URL.length) || "/" : url;
78
+ }
79
+ function serializeDiagnosticUrl(url, options) {
80
+ try {
81
+ return {
82
+ degraded: false,
83
+ serializedUrl: serializeRequestUrl(url, options?.params, options?.sensitiveParams),
84
+ };
85
+ }
86
+ catch {
87
+ try {
88
+ const absoluteUrl = new URL(url, DIAGNOSTIC_BASE_URL).toString();
89
+ const serialized = serializeRequestUrl(absoluteUrl, options?.params, options?.sensitiveParams);
90
+ return {
91
+ degraded: false,
92
+ serializedUrl: {
93
+ requestUrl: stripDiagnosticBase(serialized.requestUrl),
94
+ redactedUrl: stripDiagnosticBase(serialized.redactedUrl),
95
+ sensitiveValues: serialized.sensitiveValues,
96
+ },
97
+ };
98
+ }
99
+ catch {
100
+ const sensitiveParamNames = Object.keys(options?.sensitiveParams ?? {});
101
+ const structural = redactUrlQueryParams(url, sensitiveParamNames);
102
+ return {
103
+ degraded: true,
104
+ serializedUrl: {
105
+ requestUrl: url,
106
+ redactedUrl: structural.redactedUrl,
107
+ sensitiveValues: [
108
+ ...new Set([...fallbackSensitiveValues(options), ...structural.sensitiveValues]),
109
+ ],
110
+ },
111
+ };
112
+ }
113
+ }
114
+ }
115
+ function snapshotRequestDiagnostics(namespace, methodName, args) {
116
+ const options = requestOptionsForInvocation(namespace, methodName, args);
117
+ const url = typeof args[0] === "string" ? args[0] : undefined;
118
+ const hasSensitiveParams = Boolean(options?.sensitiveParams && Object.keys(options.sensitiveParams).length > 0);
119
+ const sensitiveParamNames = Object.keys(options?.sensitiveParams ?? {});
120
+ const diagnosticUrl = hasSensitiveParams && url ? serializeDiagnosticUrl(url, options) : undefined;
121
+ const traceUrl = hasSensitiveParams && url
122
+ ? serializeDiagnosticUrl(url, { sensitiveParams: options?.sensitiveParams }).serializedUrl
123
+ .redactedUrl
124
+ : undefined;
125
+ return {
126
+ ...(diagnosticUrl?.degraded ? { diagnosticUrlDegraded: true } : {}),
127
+ ...(hasSensitiveParams ? { requestId: crypto.randomUUID() } : {}),
128
+ serializedUrl: diagnosticUrl?.serializedUrl,
129
+ sensitiveParamNames,
130
+ sensitiveValues: diagnosticUrl?.serializedUrl.sensitiveValues ?? fallbackSensitiveValues(options),
131
+ traceUrl,
132
+ };
133
+ }
134
+ function sanitizeRequestError(error, diagnostics) {
135
+ return redactSensitiveError(error, diagnostics.sensitiveValues, diagnostics.serializedUrl?.requestUrl, diagnostics.serializedUrl?.redactedUrl);
136
+ }
137
+ function isHttpStreamResponse(value) {
138
+ return (typeof value === "object" &&
139
+ value !== null &&
140
+ "body" in value &&
141
+ value.body instanceof ReadableStream);
142
+ }
143
+ function instrumentHttpStreamConsumption(value, recorder, args, diagnostics) {
144
+ const source = value.body;
145
+ let reader;
146
+ const body = new ReadableStream({
147
+ async pull(controller) {
148
+ try {
149
+ reader ??= source.getReader();
150
+ const chunk = await reader.read();
151
+ if (chunk.done) {
152
+ controller.close();
153
+ return;
154
+ }
155
+ controller.enqueue(chunk.value);
156
+ }
157
+ catch (error) {
158
+ const sanitizedError = sanitizeRequestError(error, diagnostics);
159
+ try {
160
+ await recorder.runSpan("http.stream.consume", () => {
161
+ throw sanitizedError;
162
+ }, {
163
+ onError: (spanError) => buildSpanAttributes("http", "stream", args, undefined, spanError, diagnostics),
164
+ });
165
+ }
166
+ catch (recordedError) {
167
+ controller.error(recordedError);
168
+ }
169
+ }
170
+ },
171
+ async cancel(reason) {
172
+ try {
173
+ await (reader ? reader.cancel(reason) : source.cancel(reason));
174
+ }
175
+ catch (error) {
176
+ throw sanitizeRequestError(error, diagnostics);
177
+ }
178
+ },
179
+ }, { highWaterMark: 0 });
180
+ const instrumented = {
181
+ ...value,
182
+ body,
183
+ bytes: () => readableBytes(body),
184
+ textChunks: () => readableTextChunks(body),
185
+ lines: () => readableLines(body),
186
+ };
187
+ try {
188
+ Object.assign(value, instrumented);
189
+ return value;
190
+ }
191
+ catch {
192
+ return instrumented;
193
+ }
194
+ }
195
+ function isAsyncIterable(value) {
196
+ return (typeof value === "object" &&
197
+ value !== null &&
198
+ Symbol.asyncIterator in value &&
199
+ typeof value[Symbol.asyncIterator] === "function");
200
+ }
201
+ function instrumentHttpSseConsumption(value, recorder, args, diagnostics) {
202
+ const source = value;
203
+ return {
204
+ async *[Symbol.asyncIterator]() {
205
+ try {
206
+ for await (const event of source)
207
+ yield event;
208
+ }
209
+ catch (error) {
210
+ const sanitizedError = sanitizeRequestError(error, diagnostics);
211
+ return await recorder.runSpan("http.sse.consume", () => {
212
+ throw sanitizedError;
213
+ }, {
214
+ onError: (spanError) => buildSpanAttributes("http", "sse", args, undefined, spanError, diagnostics),
215
+ });
216
+ }
217
+ },
218
+ };
219
+ }
55
220
  function getMethod(namespace, methodName, args) {
56
221
  if (namespace === "http") {
222
+ // Preserve the original instrumentation contract: generic entry points are
223
+ // reported as REQUEST/STREAM/SSE, independent of transport options.
57
224
  return methodName.toUpperCase();
58
225
  }
59
226
  if (namespace === "stealth") {
@@ -65,22 +232,27 @@ function getMethod(namespace, methodName, args) {
65
232
  }
66
233
  return undefined;
67
234
  }
68
- function buildSpanAttributes(namespace, methodName, args, result, error) {
235
+ function buildSpanAttributes(namespace, methodName, args, result, error, diagnostics = { sensitiveParamNames: [], sensitiveValues: [] }) {
69
236
  const attributes = {};
70
- const url = getUrl(namespace, args, result);
237
+ const rawUrl = getUrl(namespace, args, result);
238
+ const structuralUrl = diagnostics.traceUrl ?? rawUrl;
239
+ const url = structuralUrl
240
+ ? redactSensitiveText(structuralUrl, diagnostics.sensitiveValues, diagnostics.serializedUrl?.requestUrl, diagnostics.serializedUrl?.redactedUrl)
241
+ : undefined;
71
242
  const method = getMethod(namespace, methodName, args);
72
- const status = error
73
- ? getErrorStatus(error)
74
- : getResponseStatus(namespace, result);
243
+ const status = error ? getErrorStatus(error) : getResponseStatus(namespace, result);
75
244
  const duration = error ? undefined : getResponseDuration(result);
76
245
  if (url) {
77
246
  attributes.url = url;
78
247
  }
248
+ if (diagnostics.requestId)
249
+ attributes.request_id = diagnostics.requestId;
250
+ if (diagnostics.diagnosticUrlDegraded)
251
+ attributes.redaction_degraded = true;
79
252
  if (method) {
80
253
  attributes.method = method;
81
254
  }
82
- if (status !== undefined &&
83
- (namespace === "http" || namespace === "stealth")) {
255
+ if (status !== undefined && (namespace === "http" || namespace === "stealth")) {
84
256
  attributes.status = status;
85
257
  }
86
258
  if (duration !== undefined) {
@@ -95,6 +267,51 @@ function buildSpanAttributes(namespace, methodName, args, result, error) {
95
267
  }
96
268
  return attributes;
97
269
  }
270
+ function isStealthRedirectRunResult(value) {
271
+ return (typeof value === "object" &&
272
+ value !== null &&
273
+ "reason" in value &&
274
+ typeof value.reason === "string" &&
275
+ "hops" in value &&
276
+ Array.isArray(value.hops) &&
277
+ "final" in value &&
278
+ typeof value.final === "object" &&
279
+ value.final !== null);
280
+ }
281
+ function buildStealthRedirectAttributes(args, result, error, diagnostics) {
282
+ const attributes = buildSpanAttributes("stealth", "fetch", args, undefined, error, diagnostics);
283
+ if (!isStealthRedirectRunResult(result))
284
+ return attributes;
285
+ attributes.redirect_reason = result.reason;
286
+ attributes.redirect_hop_count = result.hops.length;
287
+ attributes.status = result.final.status;
288
+ if (result.hops.length > 0) {
289
+ const sensitiveValues = new Set(diagnostics.sensitiveValues);
290
+ const path = result.hops
291
+ .map((hop) => {
292
+ const hopUrl = hop.nextUrl ?? hop.url;
293
+ const responseSensitiveParamNames = [...queryParamNames(hopUrl)].filter(isSensitiveKey);
294
+ const structural = redactUrlQueryParams(hopUrl, [
295
+ ...new Set([...diagnostics.sensitiveParamNames, ...responseSensitiveParamNames]),
296
+ ]);
297
+ for (const value of structural.sensitiveValues)
298
+ sensitiveValues.add(value);
299
+ const safeUrl = redactSensitiveText(structural.redactedUrl, [...sensitiveValues]);
300
+ return `${hop.method} ${hop.status} ${safeUrl}`;
301
+ })
302
+ .join(" -> ");
303
+ attributes.redirect_path = redactSensitiveText(path, [...sensitiveValues], diagnostics.serializedUrl?.requestUrl, diagnostics.serializedUrl?.redactedUrl);
304
+ }
305
+ return attributes;
306
+ }
307
+ function queryParamNames(url) {
308
+ const queryStart = url.indexOf("?");
309
+ if (queryStart === -1)
310
+ return new Set();
311
+ const fragmentStart = url.indexOf("#", queryStart);
312
+ const query = url.slice(queryStart + 1, fragmentStart === -1 ? undefined : fragmentStart);
313
+ return new Set(new URLSearchParams(query).keys());
314
+ }
98
315
  function getBrowserPageAttributes(methodName, args, elapsedMs, error) {
99
316
  const attributes = {};
100
317
  if (methodName === "goto") {
@@ -155,7 +372,49 @@ function wrapPage(page, trace) {
155
372
  },
156
373
  });
157
374
  }
158
- function wrapNamespace(namespace, target, trace) {
375
+ function wrapStealthRedirects(redirects, trace) {
376
+ const recorder = getTraceRecorder(trace);
377
+ if (!recorder)
378
+ return redirects;
379
+ return {
380
+ run(...args) {
381
+ const diagnosticArgs = [args[0].url, args[0]];
382
+ const diagnostics = snapshotRequestDiagnostics("stealth", "fetch", diagnosticArgs);
383
+ let result;
384
+ try {
385
+ result = redirects.run(...args);
386
+ }
387
+ catch (error) {
388
+ const sanitizedError = sanitizeRequestError(error, diagnostics);
389
+ recorder
390
+ .runSpan("stealth.redirects.run", () => {
391
+ throw sanitizedError;
392
+ }, {
393
+ onError: (spanError) => buildStealthRedirectAttributes(diagnosticArgs, undefined, spanError, diagnostics),
394
+ })
395
+ .catch(() => undefined);
396
+ throw sanitizedError;
397
+ }
398
+ const sanitizedResult = Promise.resolve(result).catch((error) => {
399
+ throw sanitizeRequestError(error, diagnostics);
400
+ });
401
+ return recorder.runSpan("stealth.redirects.run", () => sanitizedResult, {
402
+ onSuccess: (spanResult) => buildStealthRedirectAttributes(diagnosticArgs, spanResult, undefined, diagnostics),
403
+ onError: (error) => buildStealthRedirectAttributes(diagnosticArgs, undefined, error, diagnostics),
404
+ });
405
+ },
406
+ };
407
+ }
408
+ function wrapStealthSession(session, trace) {
409
+ const wrappedSession = wrapNamespace("stealth", session, trace);
410
+ const redirects = wrapStealthRedirects(session.redirects, trace);
411
+ return new Proxy(wrappedSession, {
412
+ get(target, property, receiver) {
413
+ return property === "redirects" ? redirects : Reflect.get(target, property, receiver);
414
+ },
415
+ });
416
+ }
417
+ function wrapNamespace(namespace, target, trace, shouldInstrument) {
159
418
  const recorder = getTraceRecorder(trace);
160
419
  if (!recorder) {
161
420
  return target;
@@ -192,8 +451,7 @@ function wrapNamespace(namespace, target, trace) {
192
451
  if (namespaceTarget &&
193
452
  typeof namespaceTarget === "object" &&
194
453
  "engine" in namespaceTarget &&
195
- typeof namespaceTarget.engine ===
196
- "string") {
454
+ typeof namespaceTarget.engine === "string") {
197
455
  attributes.engine = namespaceTarget.engine;
198
456
  }
199
457
  return attributes;
@@ -224,10 +482,75 @@ function wrapNamespace(namespace, target, trace) {
224
482
  wrappedMethods.set(property, wrapped);
225
483
  return wrapped;
226
484
  }
227
- const wrapped = (...args) => recorder.runSpan(`${namespace}.${methodName}`, () => Reflect.apply(value, namespaceTarget, args), {
228
- onSuccess: (result) => buildSpanAttributes(namespace, methodName, args, result),
229
- onError: (error) => buildSpanAttributes(namespace, methodName, args, undefined, error),
230
- });
485
+ const wrapped = (...args) => {
486
+ if (shouldInstrument && !shouldInstrument(methodName, args)) {
487
+ return Reflect.apply(value, namespaceTarget, args);
488
+ }
489
+ const requestDiagnostics = snapshotRequestDiagnostics(namespace, methodName, args);
490
+ // Invoke first and decide by the RETURN VALUE. `runSpan` always
491
+ // returns a Promise, so unconditionally span-wrapping every member
492
+ // silently rewrote synchronous contracts: `ctx.state.namespace()`
493
+ // (a sync factory) came back as a Promise, and every method call on
494
+ // it threw a raw TypeError — surfaced in production as catchtable's
495
+ // deterministic CONFIRM_STATE_UNAVAILABLE on 2026-07-27 (and the
496
+ // 2026-07-22 reserve internal_error loop before it). Sync members
497
+ // keep their sync contract; only genuinely async operations are
498
+ // recorded as spans.
499
+ let result;
500
+ try {
501
+ result = Reflect.apply(value, namespaceTarget, args);
502
+ }
503
+ catch (error) {
504
+ const sanitizedError = sanitizeRequestError(error, requestDiagnostics);
505
+ // A promise-returning implementation may still throw
506
+ // SYNCHRONOUSLY during pre-flight validation. Preserve the
507
+ // synchronous throw contract, but keep recording the failure
508
+ // span (the pre-fidelity wrapper captured these).
509
+ recorder
510
+ .runSpan(`${namespace}.${methodName}`, () => {
511
+ throw sanitizedError;
512
+ }, {
513
+ onError: (spanError) => buildSpanAttributes(namespace, methodName, args, undefined, spanError, requestDiagnostics),
514
+ })
515
+ .catch(() => undefined);
516
+ throw sanitizedError;
517
+ }
518
+ if (!isThenable(result)) {
519
+ // The state namespace factory returns the object whose METHODS
520
+ // are the operations worth tracing — instrument that object so
521
+ // `state.get`/`state.compareAndSet`/… spans exist (they never
522
+ // could before: the factory's return value was destroyed).
523
+ if (namespace === "state" &&
524
+ methodName === "namespace" &&
525
+ typeof result === "object" &&
526
+ result !== null) {
527
+ return wrapNamespace(namespace, result, trace);
528
+ }
529
+ if (namespace === "stealth" &&
530
+ methodName === "createSession" &&
531
+ typeof result === "object" &&
532
+ result !== null) {
533
+ return wrapStealthSession(result, trace);
534
+ }
535
+ return result;
536
+ }
537
+ const sanitizedResult = Promise.resolve(result).catch((error) => {
538
+ throw sanitizeRequestError(error, requestDiagnostics);
539
+ });
540
+ const tracedResult = recorder.runSpan(`${namespace}.${methodName}`, () => sanitizedResult, {
541
+ onSuccess: (spanResult) => buildSpanAttributes(namespace, methodName, args, spanResult, undefined, requestDiagnostics),
542
+ onError: (error) => buildSpanAttributes(namespace, methodName, args, undefined, error, requestDiagnostics),
543
+ });
544
+ return namespace === "http" && methodName === "stream"
545
+ ? tracedResult.then((spanResult) => isHttpStreamResponse(spanResult)
546
+ ? instrumentHttpStreamConsumption(spanResult, recorder, args, requestDiagnostics)
547
+ : spanResult)
548
+ : namespace === "http" && methodName === "sse"
549
+ ? tracedResult.then((spanResult) => isAsyncIterable(spanResult)
550
+ ? instrumentHttpSseConsumption(spanResult, recorder, args, requestDiagnostics)
551
+ : spanResult)
552
+ : tracedResult;
553
+ };
231
554
  wrappedMethods.set(property, wrapped);
232
555
  return wrapped;
233
556
  },
@@ -1,4 +1,4 @@
1
- import { ConfigurationError, decodeMasterKey } from "./key-derivation";
1
+ import { ConfigurationError, decodeMasterKey } from "./key-derivation.js";
2
2
  const DEFAULT_KEY_PREFIX = "APIFUSE__KEYRING__MASTER_KEY_V";
3
3
  const DEFAULT_ACCEPT_LIST_VAR = "APIFUSE__KEYRING__MASTER_KEY_ACCEPT_LIST";
4
4
  const DEFAULT_WRITER_VERSION_VAR = "APIFUSE__KEYRING__MASTER_KEY_WRITER_VERSION";
@@ -1,5 +1,5 @@
1
1
  import { createHmac } from "node:crypto";
2
- import { deriveSubkey } from "./key-derivation";
2
+ import { deriveSubkey } from "./key-derivation.js";
3
3
  const HMAC_HEX_LENGTH = 16;
4
4
  /**
5
5
  * @internal Trusted loaders only; not re-exported to provider-importable paths.
@@ -0,0 +1,127 @@
1
+ import { Socket } from "node:net";
2
+ import { type TLSSocket } from "node:tls";
3
+ import { type ProxyProtocol } from "../config/loader.js";
4
+ import { TransportError } from "../errors.js";
5
+ import { type DynamicEgressRuleSnapshot } from "../native-egress-policy.js";
6
+ import type { NativeNetworkClient, NativeNetworkConnectInput, NativeNetworkConnection, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProxyEgressInfo, ProviderProxyPolicy, ProviderProxyProvider, EnvContext } from "../types.js";
7
+ export type NativeNetworkErrorCode = "native_connection_aborted" | "native_connection_closed" | "native_connection_failed" | "native_connection_idle_timeout" | "native_connection_timeout" | "native_egress_authorization_failed" | "native_egress_grant_expired" | "native_egress_grant_invalid" | "native_egress_grant_limit_exceeded" | "native_egress_input_invalid" | "native_egress_not_declared" | "native_egress_policy_invalid" | "native_dynamic_egress_unsupported" | "native_proxy_expired" | "native_proxy_invalid";
8
+ export declare class NativeNetworkError extends TransportError {
9
+ constructor(message: string, code: NativeNetworkErrorCode, cause?: Error);
10
+ get code(): NativeNetworkErrorCode;
11
+ }
12
+ export declare class NativeProxyExpiredError extends NativeNetworkError {
13
+ readonly expiresAt: string;
14
+ constructor(expiresAt: string);
15
+ }
16
+ /** Raised before transport setup when a native destination is not authorized. */
17
+ export declare class NativeEgressNotDeclaredError extends NativeNetworkError {
18
+ readonly port: number;
19
+ readonly tls: "required" | "disabled";
20
+ readonly host: string;
21
+ constructor(host: string, port: number, tls: "required" | "disabled");
22
+ }
23
+ /**
24
+ * Raised when the destination was authorized by a grant whose TTL elapsed and
25
+ * its expiry remains in the client's bounded recent-expiry evidence window.
26
+ */
27
+ export declare class NativeEgressGrantExpiredError extends NativeNetworkError {
28
+ readonly port: number;
29
+ readonly tls: "required" | "disabled";
30
+ readonly expiresAt: string;
31
+ readonly host: string;
32
+ constructor(host: string, port: number, tls: "required" | "disabled", expiresAt: string);
33
+ }
34
+ /** Raised when an established connection exceeds its opt-in read-idle window. */
35
+ export declare class NativeIdleTimeoutError extends NativeNetworkError {
36
+ constructor();
37
+ }
38
+ export type NativeGatewayProxy = NativeProxyEgressInfo & {
39
+ readonly url: string;
40
+ };
41
+ export type NativeGatewayProxySynthesisInput = {
42
+ readonly vendor: ProviderProxyProvider;
43
+ readonly policy: ProviderProxyPolicy;
44
+ readonly affinityKey?: string;
45
+ readonly now: number;
46
+ readonly protocol: ProxyProtocol;
47
+ readonly credentials: VendorCredentialResolver;
48
+ };
49
+ export type VendorCredentialLookup = {
50
+ readonly kind: "present";
51
+ readonly values: Readonly<Record<string, string>>;
52
+ } | {
53
+ readonly kind: "absent";
54
+ readonly missing: readonly string[];
55
+ };
56
+ export type VendorCredentialResolver = (vendor: ProviderProxyProvider) => VendorCredentialLookup;
57
+ export type NativeGatewayProxySkipReason = {
58
+ readonly kind: "credentials_absent";
59
+ readonly missing: readonly string[];
60
+ } | {
61
+ readonly kind: "protocol_unsupported";
62
+ readonly protocol: string;
63
+ } | {
64
+ readonly kind: "allocation_failed";
65
+ readonly cause: Error;
66
+ } | {
67
+ readonly kind: "credential_lookup_failed";
68
+ readonly cause: Error;
69
+ };
70
+ export type NativeGatewayProxySynthesisResult = NativeGatewayProxy | {
71
+ readonly kind: "skipped";
72
+ readonly reason: NativeGatewayProxySkipReason;
73
+ } | undefined;
74
+ /** A vendor adapter in the ordered native gateway resolution chain. */
75
+ export type NativeGatewayProxySynthesizer = (input: NativeGatewayProxySynthesisInput) => NativeGatewayProxySynthesisResult | Promise<NativeGatewayProxySynthesisResult>;
76
+ export type NativeGatewayProxyResolutionInput = {
77
+ readonly policy: ProviderProxyPolicy;
78
+ readonly affinityKey?: string;
79
+ readonly now?: number;
80
+ readonly protocol?: ProxyProtocol;
81
+ readonly credentials?: VendorCredentialResolver;
82
+ readonly gatewaySynthesizers?: readonly NativeGatewayProxySynthesizer[];
83
+ };
84
+ export type NativeNetworkClientOptions = {
85
+ readonly proxyPolicy?: ProviderProxyPolicy;
86
+ readonly affinityKey?: string;
87
+ /** Stable credential/account identity; hashed before vendor synthesis. */
88
+ readonly credentialIdentity?: string;
89
+ /** Vendor credential lookup; defaults to the process EnvContext. */
90
+ readonly credentials?: VendorCredentialResolver;
91
+ /** Explicit CONNECT/SOCKS5 override; vendors otherwise choose their default. */
92
+ readonly proxyProtocol?: ProxyProtocol;
93
+ /** Vendor adapters in priority order within each policy vendor slot. */
94
+ readonly gatewaySynthesizers?: readonly NativeGatewayProxySynthesizer[];
95
+ /** Warning-level lifecycle diagnostic sink. */
96
+ readonly warn?: (message: string) => void;
97
+ /**
98
+ * Provider-declared native egress. Undefined preserves legacy unrestricted
99
+ * behavior; any provided declaration, including an empty object, is enforced.
100
+ */
101
+ readonly egress?: NonNullable<NativeProviderConfig["network"]>;
102
+ /** Additional deployment authorization layered on top of SDK enforcement. */
103
+ readonly grantTcpEgress?: (input: NativeNetworkDynamicGrantOptions) => NativeNetworkEgressGrant;
104
+ };
105
+ /** Build a resolver over the SDK's existing injectable environment context. */
106
+ export declare function createEnvVendorCredentialResolver(env?: EnvContext): VendorCredentialResolver;
107
+ /** Domain-separated, process-independent affinity derived from credential identity. */
108
+ export declare function deriveNativeCredentialAffinityKey(credentialIdentity: string): string;
109
+ /** Resolve the first configured native gateway, including allocation vendors. */
110
+ export declare function resolveNativeGatewayProxy(input: NativeGatewayProxyResolutionInput): Promise<NativeGatewayProxy | undefined>;
111
+ export declare function createNativeNetworkConnection(socket: Socket | TLSSocket, proxy: NativeGatewayProxy | undefined, options: NativeNetworkClientOptions, idleTimeoutMs?: number): NativeNetworkConnection;
112
+ type NativeConnectTls = "required" | "disabled";
113
+ export declare const NATIVE_EGRESS_EXPIRED_EVIDENCE_LIMIT = 256;
114
+ /** Internal validator-independent source selector matcher. */
115
+ export declare function matchesSourceHost(rule: DynamicEgressRuleSnapshot, host: string): boolean;
116
+ /** Internal canonical snapshot shared by production and SDK transport test doubles. */
117
+ export declare function snapshotNativeConnectInput(input: NativeNetworkConnectInput): NativeNetworkConnectInput;
118
+ /** Internal canonical snapshot shared by production and SDK transport test doubles. */
119
+ export declare function snapshotNativeGrantInput(input: NativeNetworkDynamicGrantOptions): NativeNetworkDynamicGrantOptions;
120
+ /** Internal authorization seam shared by production and SDK transport test doubles. */
121
+ export declare function createNativeEgressAuthorization(options: NativeNetworkClientOptions): {
122
+ assertConnect(input: NativeNetworkConnectInput, tls: NativeConnectTls): void;
123
+ grant(input: NativeNetworkDynamicGrantOptions): NativeNetworkEgressGrant;
124
+ };
125
+ /** Create the SDK byte-stream runtime with provider-declared egress enforcement. */
126
+ export declare function createNativeNetworkClient(options?: NativeNetworkClientOptions): NativeNetworkClient;
127
+ export {};