@apifuse/provider-sdk 2.2.0-beta.62 → 2.2.0-beta.64

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 (261) hide show
  1. package/AUTHORING.md +367 -9
  2. package/CHANGELOG.md +8 -0
  3. package/LICENSE +21 -0
  4. package/README.md +1 -0
  5. package/bin/apifuse-check.ts +84 -2
  6. package/bin/apifuse-dev.ts +11 -31
  7. package/bin/apifuse-migrate-deployment.ts +200 -0
  8. package/bin/apifuse-pack-check.ts +11 -4
  9. package/bin/apifuse-pack-types.ts +187 -4
  10. package/bin/apifuse-record.ts +44 -32
  11. package/bin/apifuse-submit-check.ts +9 -5
  12. package/bin/apifuse-test.ts +3 -1
  13. package/dist/cli/commands.d.ts +1 -1
  14. package/dist/cli/commands.js +11 -0
  15. package/dist/cli/deployment-intent.d.ts +178 -0
  16. package/dist/cli/deployment-intent.js +277 -0
  17. package/dist/cli/migrate-deployment-intent.d.ts +132 -0
  18. package/dist/cli/migrate-deployment-intent.js +1552 -0
  19. package/dist/cli/migrate-provider-shape.d.ts +5 -1
  20. package/dist/cli/migrate-provider-shape.js +4 -2
  21. package/dist/cli/resolver-runtime.d.ts +21 -0
  22. package/dist/cli/resolver-runtime.js +31 -0
  23. package/dist/define.d.ts +3 -0
  24. package/dist/define.js +5 -0
  25. package/dist/dev.d.ts +3 -1
  26. package/dist/dev.js +6 -1
  27. package/dist/engine.d.ts +35 -1
  28. package/dist/engine.js +21 -2
  29. package/dist/error-resolution.d.ts +1 -0
  30. package/dist/error-resolution.js +54 -0
  31. package/dist/errors.d.ts +39 -1
  32. package/dist/errors.js +50 -0
  33. package/dist/handle-meta.d.ts +36 -1
  34. package/dist/handle-meta.js +16 -0
  35. package/dist/handle.d.ts +23 -5
  36. package/dist/handle.js +54 -8
  37. package/dist/health-scenario.d.ts +38 -0
  38. package/dist/i18n/error-messages.d.ts +86 -0
  39. package/dist/i18n/error-messages.js +192 -0
  40. package/dist/i18n/index.d.ts +1 -0
  41. package/dist/i18n/index.js +1 -0
  42. package/dist/i18n/keys.d.ts +12 -0
  43. package/dist/i18n/keys.js +76 -5
  44. package/dist/index.d.ts +11 -3
  45. package/dist/index.js +9 -1
  46. package/dist/lint.d.ts +15 -0
  47. package/dist/lint.js +469 -82
  48. package/dist/provider.d.ts +6 -6
  49. package/dist/provider.js +3 -3
  50. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  51. package/dist/runtime/browser-telemetry-binding.js +83 -0
  52. package/dist/runtime/browser-telemetry.d.ts +76 -0
  53. package/dist/runtime/browser-telemetry.js +165 -0
  54. package/dist/runtime/browser.d.ts +3 -0
  55. package/dist/runtime/browser.js +208 -47
  56. package/dist/runtime/cache-telemetry.d.ts +71 -0
  57. package/dist/runtime/cache-telemetry.js +177 -0
  58. package/dist/runtime/cache.js +67 -7
  59. package/dist/runtime/capability-response.d.ts +3 -0
  60. package/dist/runtime/capability-response.js +56 -0
  61. package/dist/runtime/capability-telemetry.d.ts +46 -0
  62. package/dist/runtime/capability-telemetry.js +68 -0
  63. package/dist/runtime/diagnostic-env.d.ts +3 -0
  64. package/dist/runtime/diagnostic-env.js +10 -1
  65. package/dist/runtime/engine-mode.d.ts +74 -0
  66. package/dist/runtime/engine-mode.js +152 -0
  67. package/dist/runtime/handle.js +9 -1
  68. package/dist/runtime/http-telemetry-guard.d.ts +10 -0
  69. package/dist/runtime/http-telemetry-guard.js +62 -10
  70. package/dist/runtime/http.js +141 -35
  71. package/dist/runtime/instrumentation.js +92 -13
  72. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  73. package/dist/runtime/ocr-telemetry.js +192 -0
  74. package/dist/runtime/ocr.js +35 -8
  75. package/dist/runtime/request-telemetry.d.ts +2 -0
  76. package/dist/runtime/request-telemetry.js +2 -1
  77. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  78. package/dist/runtime/resolver-runtime-options.js +33 -0
  79. package/dist/runtime/resolver-shared.d.ts +3 -2
  80. package/dist/runtime/resolver-shared.js +4 -4
  81. package/dist/runtime/resolver-telemetry.d.ts +1 -0
  82. package/dist/runtime/resolver-telemetry.js +1 -1
  83. package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
  84. package/dist/runtime/resolver-vendors/browser.js +46 -0
  85. package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
  86. package/dist/runtime/resolver-vendors/hosts.js +35 -0
  87. package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
  88. package/dist/runtime/resolver-vendors/types.d.ts +5 -4
  89. package/dist/runtime/resolver.d.ts +4 -1
  90. package/dist/runtime/resolver.js +46 -13
  91. package/dist/runtime/state-telemetry.d.ts +79 -0
  92. package/dist/runtime/state-telemetry.js +194 -0
  93. package/dist/runtime/state.d.ts +3 -0
  94. package/dist/runtime/state.js +26 -7
  95. package/dist/runtime/stealth-cookies.d.ts +5 -1
  96. package/dist/runtime/stealth-cookies.js +21 -1
  97. package/dist/runtime/stealth-owned-headers.d.ts +26 -0
  98. package/dist/runtime/stealth-owned-headers.js +37 -0
  99. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  100. package/dist/runtime/stealth-telemetry.js +179 -0
  101. package/dist/runtime/stealth.d.ts +4 -1
  102. package/dist/runtime/stealth.js +228 -85
  103. package/dist/runtime/stt-telemetry.d.ts +77 -0
  104. package/dist/runtime/stt-telemetry.js +176 -0
  105. package/dist/runtime/stt.js +15 -6
  106. package/dist/runtime-boundary-lint.d.ts +132 -0
  107. package/dist/runtime-boundary-lint.js +586 -0
  108. package/dist/server/error-stack-frames.d.ts +35 -0
  109. package/dist/server/error-stack-frames.js +219 -0
  110. package/dist/server/index.d.ts +7 -0
  111. package/dist/server/index.js +4 -0
  112. package/dist/server/sdk-version.d.ts +3 -0
  113. package/dist/server/sdk-version.js +19 -0
  114. package/dist/server/self-test.js +1 -18
  115. package/dist/server/sensitive-values.d.ts +1 -1
  116. package/dist/server/sensitive-values.js +9 -0
  117. package/dist/server/serve-implementation.d.ts +51 -2
  118. package/dist/server/serve-implementation.js +337 -90
  119. package/dist/server/types.d.ts +1 -0
  120. package/dist/server/types.js +7 -0
  121. package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
  122. package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
  123. package/dist/stateful/stateful-provider-runtime-context.js +1 -0
  124. package/dist/types.d.ts +123 -1
  125. package/dist/typescript-module.d.ts +39 -0
  126. package/dist/typescript-module.js +72 -0
  127. package/package.json +6 -1
  128. package/src/cli/commands.ts +14 -0
  129. package/src/cli/deployment-intent.ts +364 -0
  130. package/src/cli/migrate-deployment-intent.ts +1908 -0
  131. package/src/cli/migrate-provider-shape.ts +10 -4
  132. package/src/cli/resolver-runtime.ts +72 -0
  133. package/src/define.ts +13 -0
  134. package/src/dev.ts +9 -2
  135. package/src/engine.ts +38 -2
  136. package/src/error-resolution.ts +58 -0
  137. package/src/errors.ts +77 -1
  138. package/src/handle-meta.ts +51 -1
  139. package/src/handle.ts +92 -10
  140. package/src/health-scenario.ts +38 -0
  141. package/src/i18n/error-messages.ts +255 -0
  142. package/src/i18n/index.ts +1 -0
  143. package/src/i18n/keys.ts +80 -6
  144. package/src/index.ts +86 -0
  145. package/src/lint.ts +604 -91
  146. package/src/provider.ts +12 -0
  147. package/src/runtime/browser-telemetry-binding.ts +92 -0
  148. package/src/runtime/browser-telemetry.ts +289 -0
  149. package/src/runtime/browser.ts +249 -65
  150. package/src/runtime/cache-telemetry.ts +247 -0
  151. package/src/runtime/cache.ts +68 -17
  152. package/src/runtime/capability-response.ts +53 -0
  153. package/src/runtime/capability-telemetry.ts +101 -0
  154. package/src/runtime/diagnostic-env.ts +11 -1
  155. package/src/runtime/engine-mode.ts +233 -0
  156. package/src/runtime/handle.ts +9 -1
  157. package/src/runtime/http-telemetry-guard.ts +66 -13
  158. package/src/runtime/http.ts +168 -37
  159. package/src/runtime/instrumentation.ts +105 -16
  160. package/src/runtime/ocr-telemetry.ts +308 -0
  161. package/src/runtime/ocr.ts +114 -74
  162. package/src/runtime/request-telemetry.ts +2 -1
  163. package/src/runtime/resolver-runtime-options.ts +50 -0
  164. package/src/runtime/resolver-shared.ts +9 -4
  165. package/src/runtime/resolver-telemetry.ts +1 -1
  166. package/src/runtime/resolver-vendors/browser.ts +65 -7
  167. package/src/runtime/resolver-vendors/hosts.ts +43 -0
  168. package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
  169. package/src/runtime/resolver-vendors/types.ts +5 -4
  170. package/src/runtime/resolver.ts +64 -21
  171. package/src/runtime/state-telemetry.ts +298 -0
  172. package/src/runtime/state.ts +46 -45
  173. package/src/runtime/stealth-cookies.ts +24 -1
  174. package/src/runtime/stealth-owned-headers.ts +41 -0
  175. package/src/runtime/stealth-telemetry.ts +304 -0
  176. package/src/runtime/stealth.ts +355 -150
  177. package/src/runtime/stt-telemetry.ts +282 -0
  178. package/src/runtime/stt.ts +93 -71
  179. package/src/runtime-boundary-lint.ts +703 -0
  180. package/src/server/error-stack-frames.ts +214 -0
  181. package/src/server/index.ts +53 -0
  182. package/src/server/sdk-version.ts +22 -0
  183. package/src/server/self-test.ts +1 -21
  184. package/src/server/sensitive-values.ts +14 -0
  185. package/src/server/serve-implementation.ts +569 -108
  186. package/src/server/types.ts +7 -0
  187. package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
  188. package/src/stateful/stateful-provider-runtime-context.ts +4 -0
  189. package/src/types.ts +128 -1
  190. package/src/typescript-module.ts +100 -0
  191. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
  192. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
  193. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
  194. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
  195. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
  196. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
  197. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
  198. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
  199. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
  200. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
  201. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
  202. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
  203. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
  204. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
  205. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
  206. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
  207. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
  208. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
  209. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
  210. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
  211. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
  212. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
  213. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
  214. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
  215. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
  216. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
  217. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
  218. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
  219. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
  220. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
  221. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
  222. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
  246. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
  247. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
  248. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
  249. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
  250. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
  251. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
  252. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
  253. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
  254. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
  255. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
  256. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
  257. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
  258. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
  259. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
  260. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
  261. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +0 -12
@@ -0,0 +1,194 @@
1
+ import { createTelemetryObserver } from "./http-telemetry-guard.js";
2
+ import { closedEnum, } from "./request-telemetry.js";
3
+ import { getStateViolation, isStateRedisNoop } from "./state.js";
4
+ const MAX_SAMPLES = 24;
5
+ const MAX_TEXT = 300;
6
+ const STATE_TELEMETRY_INSTRUMENTED = Symbol.for("@apifuse/provider-sdk/state-telemetry-instrumented");
7
+ function integer(value) {
8
+ return Number.isFinite(value)
9
+ ? Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.floor(value)))
10
+ : Number.MAX_SAFE_INTEGER;
11
+ }
12
+ function add(a, b) {
13
+ return Math.min(Number.MAX_SAFE_INTEGER, integer(a) + integer(b));
14
+ }
15
+ function keyText(value, redact) {
16
+ if (value === undefined)
17
+ return undefined;
18
+ let result;
19
+ try {
20
+ result = redact?.(value) ?? value;
21
+ }
22
+ catch {
23
+ result = "[REDACTION_FAILED]";
24
+ }
25
+ const length = Math.min(result.length, MAX_TEXT);
26
+ const units = new Uint16Array(length);
27
+ for (let index = 0; index < length; index += 1)
28
+ units[index] = result.charCodeAt(index);
29
+ return String.fromCharCode(...units);
30
+ }
31
+ export class StateTelemetryCollector {
32
+ key = "state";
33
+ #redact;
34
+ #telemetryFailed = false;
35
+ #ops = { list: 0, get: 0, set: 0, patch: 0, delete: 0, cas: 0, increment: 0 };
36
+ #conflicts = 0;
37
+ #violations = { quota: 0, ttl: 0, size: 0, unsupported: 0, error: 0 };
38
+ #backend = "unsupported";
39
+ #redisFallbacks = 0;
40
+ #configured = "not_configured";
41
+ #redisRoundTrips = 0;
42
+ #ms = 0;
43
+ #samples = [];
44
+ #dropped = 0;
45
+ constructor(options = {}) {
46
+ this.#redact = options.redact;
47
+ }
48
+ markTelemetryFailed() {
49
+ this.#telemetryFailed = true;
50
+ }
51
+ setRedisConfigured(configured) {
52
+ if (configured && this.#configured === "not_configured")
53
+ this.#configured = "configured";
54
+ }
55
+ record(event) {
56
+ if (!["list", "get", "set", "patch", "delete", "cas", "increment"].includes(event.op) ||
57
+ !["redis", "memory", "unsupported"].includes(event.backend) ||
58
+ (event.violation !== undefined &&
59
+ !["quota", "ttl", "size", "unsupported", "error"].includes(event.violation))) {
60
+ this.#telemetryFailed = true;
61
+ return;
62
+ }
63
+ this.#ops[event.op] = add(this.#ops[event.op], 1);
64
+ this.#backend = event.backend;
65
+ if (event.backend === "redis" && this.#configured === "not_configured")
66
+ this.#configured = "configured";
67
+ if (event.backend === "redis" && event.ok && event.redisTouched !== false)
68
+ this.#redisRoundTrips = add(this.#redisRoundTrips, 1);
69
+ if (event.backend === "redis" && !event.ok)
70
+ this.#configured = "degraded";
71
+ this.#ms = add(this.#ms, event.ms);
72
+ if (event.conflict)
73
+ this.#conflicts = add(this.#conflicts, 1);
74
+ if (event.violation)
75
+ this.#violations[event.violation] = add(this.#violations[event.violation], 1);
76
+ if (this.#samples.length < MAX_SAMPLES)
77
+ this.#samples.push({
78
+ key: keyText(event.key, this.#redact) ?? "",
79
+ op: event.op,
80
+ ms: integer(event.ms),
81
+ backend: event.backend,
82
+ });
83
+ else
84
+ this.#dropped = add(this.#dropped, 1);
85
+ }
86
+ toLogPayload(_spans) {
87
+ if (Object.values(this.#ops).every((value) => value === 0) && !this.#telemetryFailed)
88
+ return undefined;
89
+ return {
90
+ ...(this.#telemetryFailed ? { telemetryFailed: true } : {}),
91
+ ops: { ...this.#ops },
92
+ casConflicts: this.#conflicts,
93
+ violations: { ...this.#violations },
94
+ backend: closedEnum(this.#backend),
95
+ redisFallbacks: this.#redisFallbacks,
96
+ redisMode: closedEnum(this.#configured),
97
+ redisRoundTrips: this.#redisRoundTrips,
98
+ ms: this.#ms,
99
+ samples: this.#samples.map((sample) => ({ ...sample })),
100
+ dropped: this.#dropped,
101
+ };
102
+ }
103
+ toHeaderPayload(log) {
104
+ return {
105
+ ops: log.ops,
106
+ casConflicts: log.casConflicts,
107
+ violations: log.violations,
108
+ backend: log.backend,
109
+ redisFallbacks: log.redisFallbacks,
110
+ redisMode: log.redisMode,
111
+ redisRoundTrips: log.redisRoundTrips,
112
+ ms: log.ms,
113
+ dropped: log.dropped,
114
+ };
115
+ }
116
+ }
117
+ function backendOf(state) {
118
+ try {
119
+ const name = state.constructor?.name;
120
+ return name?.includes("Redis") ? "redis" : name?.includes("Memory") ? "memory" : "unsupported";
121
+ }
122
+ catch {
123
+ return "unsupported";
124
+ }
125
+ }
126
+ function violationOf(error) {
127
+ return getStateViolation(error) ?? "error";
128
+ }
129
+ function wrapNamespace(namespace, sink, backend) {
130
+ // Compare-and-set records one event per attempt; a three-attempt loop therefore
131
+ // reports cas: 3 and casConflicts: 2 when the first two attempts conflict.
132
+ const observe = createTelemetryObserver(sink);
133
+ return new Proxy(namespace, {
134
+ get(target, property, receiver) {
135
+ const value = Reflect.get(target, property, receiver);
136
+ if (typeof value !== "function" || property === "constructor")
137
+ return value;
138
+ const op = property === "compareAndSet" ? "cas" : String(property);
139
+ if (!(op in { list: 1, get: 1, set: 1, patch: 1, delete: 1, cas: 1, increment: 1 }))
140
+ return value;
141
+ return async (...args) => {
142
+ const started = Date.now();
143
+ const key = typeof args[0] === "string" ? args[0] : undefined;
144
+ try {
145
+ const result = await Reflect.apply(value, target, args);
146
+ observe(() => sink.record({
147
+ op,
148
+ key,
149
+ ms: Date.now() - started,
150
+ backend,
151
+ ok: true,
152
+ redisTouched: !isStateRedisNoop(result),
153
+ conflict: op === "cas" &&
154
+ !!result &&
155
+ typeof result === "object" &&
156
+ "ok" in result &&
157
+ result.ok === false,
158
+ }));
159
+ return result;
160
+ }
161
+ catch (error) {
162
+ observe(() => sink.record({
163
+ op,
164
+ key,
165
+ ms: Date.now() - started,
166
+ backend,
167
+ ok: false,
168
+ violation: violationOf(error),
169
+ }));
170
+ throw error;
171
+ }
172
+ };
173
+ },
174
+ });
175
+ }
176
+ export function instrumentProviderRuntimeState(state, sink, redact) {
177
+ if (state[STATE_TELEMETRY_INSTRUMENTED])
178
+ return state;
179
+ const backend = backendOf(state);
180
+ const observe = createTelemetryObserver(sink);
181
+ observe(() => sink.setRedisConfigured?.(backend === "redis"));
182
+ return new Proxy(state, {
183
+ get(target, property, receiver) {
184
+ if (property === STATE_TELEMETRY_INSTRUMENTED)
185
+ return true;
186
+ const value = Reflect.get(target, property, receiver);
187
+ if (property === "forConnection" && typeof value === "function")
188
+ return (id) => instrumentProviderRuntimeState(value.call(target, id), sink, redact);
189
+ if (property === "namespace" && typeof value === "function")
190
+ return (name, options) => wrapNamespace(value.call(target, name, options), sink, backend);
191
+ return value;
192
+ },
193
+ });
194
+ }
@@ -10,6 +10,9 @@ type RedisProviderRuntimeStateOptions = {
10
10
  export declare class UnsupportedProviderStateError extends ProviderError {
11
11
  constructor(message?: string);
12
12
  }
13
+ export declare function isStateRedisNoop(result: unknown): boolean;
14
+ type StateViolationKind = "quota" | "ttl" | "size" | "unsupported";
15
+ export declare function getStateViolation(error: unknown): StateViolationKind | undefined;
13
16
  export declare function createRedisProviderRuntimeState(options: RedisProviderRuntimeStateOptions): ProviderRuntimeState;
14
17
  export declare function createProviderRuntimeStateFromEnv(options?: {
15
18
  readonly providerId?: string;
@@ -247,17 +247,17 @@ class RedisProviderStateNamespace {
247
247
  enforceValueSize(value) {
248
248
  const bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
249
249
  if (bytes > this.options.maxValueBytes) {
250
- throw new UnsupportedProviderStateError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`);
250
+ throw stateViolationError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`, "size");
251
251
  }
252
252
  }
253
253
  quotaExceeded() {
254
- return new UnsupportedProviderStateError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`);
254
+ return stateViolationError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`, "quota");
255
255
  }
256
256
  writeTiming(ttl) {
257
257
  const ttlMs = parseStateDurationMs(ttl ?? this.options.defaultTtl);
258
258
  const maxTtlMs = parseStateDurationMs(this.options.maxTtl);
259
259
  if (ttlMs > maxTtlMs) {
260
- throw new UnsupportedProviderStateError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`);
260
+ throw stateViolationError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`, "ttl");
261
261
  }
262
262
  const expiresAtMs = Date.now() + ttlMs;
263
263
  return {
@@ -269,7 +269,7 @@ class RedisProviderStateNamespace {
269
269
  async list(options) {
270
270
  const requestedLimit = Math.max(0, options?.limit ?? this.options.maxEntries);
271
271
  if (requestedLimit === 0)
272
- return [];
272
+ return recordStateRedisNoop([]);
273
273
  const keys = await this.indexedKeys(this.options.maxEntries);
274
274
  if (keys.length === 0)
275
275
  return [];
@@ -376,8 +376,27 @@ export class UnsupportedProviderStateError extends ProviderError {
376
376
  constructor(message = "Provider runtime state is not available in this runtime") {
377
377
  super(message, { code: "PROVIDER_STATE_UNSUPPORTED" });
378
378
  this.name = "UnsupportedProviderStateError";
379
+ stateViolationKinds.set(this, "unsupported");
379
380
  }
380
381
  }
382
+ // Keep the exact no-command result out of band, without inspecting caller options twice.
383
+ const stateRedisNoopResults = new WeakSet();
384
+ function recordStateRedisNoop(result) {
385
+ stateRedisNoopResults.add(result);
386
+ return result;
387
+ }
388
+ export function isStateRedisNoop(result) {
389
+ return result !== null && typeof result === "object" && stateRedisNoopResults.has(result);
390
+ }
391
+ const stateViolationKinds = new WeakMap();
392
+ function stateViolationError(message, kind) {
393
+ const error = new UnsupportedProviderStateError(message);
394
+ stateViolationKinds.set(error, kind);
395
+ return error;
396
+ }
397
+ export function getStateViolation(error) {
398
+ return error !== null && typeof error === "object" ? stateViolationKinds.get(error) : undefined;
399
+ }
381
400
  class UnsupportedProviderStateNamespace {
382
401
  async list(_options) {
383
402
  throw new UnsupportedProviderStateError();
@@ -421,7 +440,7 @@ class MemoryProviderStateNamespace {
421
440
  enforceValueSize(value) {
422
441
  const bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
423
442
  if (bytes > this.options.maxValueBytes) {
424
- throw new UnsupportedProviderStateError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`);
443
+ throw stateViolationError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`, "size");
425
444
  }
426
445
  }
427
446
  enforceWritePolicy(key, value, ttl) {
@@ -429,10 +448,10 @@ class MemoryProviderStateNamespace {
429
448
  const ttlMs = parseStateDurationMs(ttl ?? this.options.defaultTtl);
430
449
  const maxTtlMs = parseStateDurationMs(this.options.maxTtl);
431
450
  if (ttlMs > maxTtlMs) {
432
- throw new UnsupportedProviderStateError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`);
451
+ throw stateViolationError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`, "ttl");
433
452
  }
434
453
  if (!this.values.has(key) && this.values.size >= this.options.maxEntries) {
435
- throw new UnsupportedProviderStateError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`);
454
+ throw stateViolationError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`, "quota");
436
455
  }
437
456
  }
438
457
  pruneExpired(nowMs = this.now()) {
@@ -1,8 +1,11 @@
1
1
  import type { CookieJar, StealthCookieStore, StealthCookieStoreV1, StealthSessionCookies } from "../types.js";
2
+ /** Extract cookie values from Set-Cookie/header strings for request-scoped registration. */
3
+ export declare function cookieValuesFromHeaders(headers: string | readonly string[]): string[];
2
4
  export declare class StealthCookieJar implements CookieJar, StealthSessionCookies {
3
5
  private cookies;
4
6
  private readonly defaultUrl;
5
- constructor(cookieStrings: readonly string[], defaultUrl?: string);
7
+ private readonly register;
8
+ constructor(cookieStrings: readonly string[], defaultUrl?: string, register?: (value: string) => void);
6
9
  setFromCookieStrings(cookieStrings: readonly string[], url?: string): void;
7
10
  get(name: string, url?: string): string | undefined;
8
11
  getAll(url?: string): Record<string, string>;
@@ -15,6 +18,7 @@ export declare class StealthCookieJar implements CookieJar, StealthSessionCookie
15
18
  deserialize(state: StealthCookieStore): void;
16
19
  clear(): void;
17
20
  find(predicate: (cookie: string) => boolean, url?: string): string | undefined;
21
+ names(url?: string): string[];
18
22
  private normalizeUrl;
19
23
  private getUniqueCookies;
20
24
  }
@@ -4,15 +4,27 @@ function isRecord(value) {
4
4
  return typeof value === "object" && value !== null;
5
5
  }
6
6
  const LEGACY_COOKIE_ORIGIN = "https://legacy-cookie.invalid/";
7
+ /** Extract cookie values from Set-Cookie/header strings for request-scoped registration. */
8
+ export function cookieValuesFromHeaders(headers) {
9
+ const values = [];
10
+ for (const header of typeof headers === "string" ? [headers] : headers) {
11
+ const cookie = Cookie.parse(header);
12
+ if (cookie?.value)
13
+ values.push(cookie.value);
14
+ }
15
+ return values;
16
+ }
7
17
  export class StealthCookieJar {
8
18
  cookies;
9
19
  defaultUrl;
10
- constructor(cookieStrings, defaultUrl = LEGACY_COOKIE_ORIGIN) {
20
+ register;
21
+ constructor(cookieStrings, defaultUrl = LEGACY_COOKIE_ORIGIN, register) {
11
22
  this.cookies = new ToughCookieJar(undefined, {
12
23
  allowSecureOnLocal: false,
13
24
  rejectPublicSuffixes: true,
14
25
  });
15
26
  this.defaultUrl = this.normalizeUrl(defaultUrl) ?? LEGACY_COOKIE_ORIGIN;
27
+ this.register = register;
16
28
  this.setFromCookieStrings(cookieStrings);
17
29
  }
18
30
  setFromCookieStrings(cookieStrings, url = this.defaultUrl) {
@@ -20,6 +32,8 @@ export class StealthCookieJar {
20
32
  if (!cookieUrl)
21
33
  return;
22
34
  for (const cookieString of cookieStrings) {
35
+ for (const value of cookieValuesFromHeaders(cookieString))
36
+ this.register?.(value);
23
37
  this.cookies.setCookieSync(cookieString, cookieUrl, { ignoreError: true });
24
38
  }
25
39
  }
@@ -54,6 +68,7 @@ export class StealthCookieJar {
54
68
  for (const [name, value] of Object.entries(cookies)) {
55
69
  if (!name)
56
70
  continue;
71
+ this.register?.(value);
57
72
  this.cookies.setCookieSync(new Cookie({ key: name, path: "/", value }), this.defaultUrl, {
58
73
  ignoreError: true,
59
74
  });
@@ -88,6 +103,9 @@ export class StealthCookieJar {
88
103
  }
89
104
  return undefined;
90
105
  }
106
+ names(url) {
107
+ return this.getUniqueCookies(url ?? this.defaultUrl).map((cookie) => cookie.key);
108
+ }
91
109
  normalizeUrl(url) {
92
110
  try {
93
111
  return new URL(url).toString();
@@ -102,6 +120,8 @@ export class StealthCookieJar {
102
120
  return [];
103
121
  const names = new Set();
104
122
  return this.cookies.getCookiesSync(cookieUrl).filter((cookie) => {
123
+ if (cookie.value)
124
+ this.register?.(cookie.value);
105
125
  if (names.has(cookie.key))
106
126
  return false;
107
127
  names.add(cookie.key);
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Header names the stealth transport owns.
3
+ *
4
+ * `ctx.stealth` composes these from the Chrome emulation and the request class,
5
+ * so a caller value is rejected at request time with `SDKError` code
6
+ * `STEALTH_HEADER_OVERRIDE_UNSUPPORTED` (`assertCallerHeadersSupported` in
7
+ * stealth.ts). The `browser-version-literal` lint reports a subset of these
8
+ * names — the `sec-fetch-*` and `sec-ch-ua*` families — in files that use
9
+ * `ctx.stealth` (`isReportedOwnedHeaderName` in lint.ts). Both read this
10
+ * module, so the lint cannot claim a rejection the runtime does not perform;
11
+ * it reports deliberately less than the runtime rejects (host, connection,
12
+ * accept-encoding and a non-literal user-agent stay silent — see that
13
+ * predicate for why), so a green `apifuse check` is not proof that no owned
14
+ * header is set.
15
+ *
16
+ * Leaf module by design: no imports, so the lazily loaded CLI lint path does
17
+ * not pull the stealth runtime in.
18
+ */
19
+ export declare const SDK_OWNED_EXACT_CHROME_HEADERS: ReadonlySet<string>;
20
+ export declare const SDK_OWNED_CHROME_HEADER_PREFIX = "sec-fetch-";
21
+ /**
22
+ * True when `ctx.stealth` rejects a caller value for `name` (case-insensitive):
23
+ * an exact member of {@link SDK_OWNED_EXACT_CHROME_HEADERS} or any
24
+ * `sec-fetch-*` name.
25
+ */
26
+ export declare function isStealthOwnedHeaderName(name: string): boolean;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Header names the stealth transport owns.
3
+ *
4
+ * `ctx.stealth` composes these from the Chrome emulation and the request class,
5
+ * so a caller value is rejected at request time with `SDKError` code
6
+ * `STEALTH_HEADER_OVERRIDE_UNSUPPORTED` (`assertCallerHeadersSupported` in
7
+ * stealth.ts). The `browser-version-literal` lint reports a subset of these
8
+ * names — the `sec-fetch-*` and `sec-ch-ua*` families — in files that use
9
+ * `ctx.stealth` (`isReportedOwnedHeaderName` in lint.ts). Both read this
10
+ * module, so the lint cannot claim a rejection the runtime does not perform;
11
+ * it reports deliberately less than the runtime rejects (host, connection,
12
+ * accept-encoding and a non-literal user-agent stay silent — see that
13
+ * predicate for why), so a green `apifuse check` is not proof that no owned
14
+ * header is set.
15
+ *
16
+ * Leaf module by design: no imports, so the lazily loaded CLI lint path does
17
+ * not pull the stealth runtime in.
18
+ */
19
+ export const SDK_OWNED_EXACT_CHROME_HEADERS = new Set([
20
+ "host",
21
+ "connection",
22
+ "user-agent",
23
+ "sec-ch-ua",
24
+ "sec-ch-ua-mobile",
25
+ "sec-ch-ua-platform",
26
+ "accept-encoding",
27
+ ]);
28
+ export const SDK_OWNED_CHROME_HEADER_PREFIX = "sec-fetch-";
29
+ /**
30
+ * True when `ctx.stealth` rejects a caller value for `name` (case-insensitive):
31
+ * an exact member of {@link SDK_OWNED_EXACT_CHROME_HEADERS} or any
32
+ * `sec-fetch-*` name.
33
+ */
34
+ export function isStealthOwnedHeaderName(name) {
35
+ const lower = name.toLowerCase();
36
+ return (SDK_OWNED_EXACT_CHROME_HEADERS.has(lower) || lower.startsWith(SDK_OWNED_CHROME_HEADER_PREFIX));
37
+ }
@@ -0,0 +1,92 @@
1
+ import type { StealthBrowser, StealthChallengeClassification, StealthOS, StealthProfileDescriptor } from "../types.js";
2
+ import { type ClosedEnum, type TelemetryContributor } from "./request-telemetry.js";
3
+ export type StealthTelemetryErrorCode = "transport_network_error" | "transport_timeout" | "transport_cancelled" | "upstream_http_error" | "response_too_large" | "proxy_connect_failed" | "PROXY_POOL_STALE" | "PROXY_EDGE_AUTH_REJECTED" | "PROXY_AUTH_IP_DENIED" | "PROXY_EDGE_TLS_REJECTED" | "PROXY_REQUIRED" | "other";
4
+ export type StealthTelemetryRequestClass = "navigation" | "script_navigation" | "xhr" | "post";
5
+ export type StealthTelemetryAttemptKind = "request" | "resolver" | "proxy_diagnostic";
6
+ export type StealthTelemetrySbsdOutcome = StealthChallengeClassification["outcome"] | "detected" | "refetch_clear";
7
+ export type StealthTelemetryDiagnostics = {
8
+ readonly name?: string;
9
+ readonly message?: string;
10
+ readonly code?: string;
11
+ };
12
+ export type StealthTelemetryAttemptEvent = {
13
+ readonly ms: number;
14
+ readonly status?: number;
15
+ readonly errorCode?: StealthTelemetryErrorCode;
16
+ readonly diagnostics?: StealthTelemetryDiagnostics;
17
+ readonly profileId: StealthProfileDescriptor;
18
+ readonly proxyUsed: boolean;
19
+ readonly requestClass: StealthTelemetryRequestClass;
20
+ readonly kind?: StealthTelemetryAttemptKind;
21
+ };
22
+ export type StealthTelemetrySbsdEvent = {
23
+ readonly detected: boolean;
24
+ readonly outcome?: StealthTelemetrySbsdOutcome;
25
+ };
26
+ export interface StealthTelemetrySink {
27
+ recordAttempt(event: StealthTelemetryAttemptEvent): void;
28
+ recordPoolRefresh(): void;
29
+ recordRedirectHop(): void;
30
+ recordSbsd(event?: StealthTelemetrySbsdEvent | StealthTelemetrySbsdOutcome): void;
31
+ recordSafeRefetch(): void;
32
+ }
33
+ export type StealthTelemetryAttemptSample = {
34
+ n: number;
35
+ ms: number;
36
+ status?: number;
37
+ e?: StealthTelemetryErrorCode;
38
+ kind?: StealthTelemetryAttemptKind;
39
+ diagnostics?: StealthTelemetryDiagnostics;
40
+ };
41
+ export type StealthTelemetryLogPayload = {
42
+ attempts: number;
43
+ poolRefreshes: number;
44
+ redirectHops: number;
45
+ profileId?: StealthProfileDescriptor;
46
+ proxyUsed: boolean;
47
+ requestClass?: StealthTelemetryRequestClass;
48
+ sbsdDetected?: boolean;
49
+ sbsdOutcome?: StealthTelemetrySbsdOutcome;
50
+ safeRefetch: number;
51
+ lastStatus?: number;
52
+ ms: number;
53
+ attemptSamples?: StealthTelemetryAttemptSample[];
54
+ attemptSamplesDropped?: number;
55
+ };
56
+ export type StealthTelemetryHeaderPayload = {
57
+ attempts: number;
58
+ poolRefreshes: number;
59
+ redirectHops: number;
60
+ profileId?: {
61
+ browser: ClosedEnum<StealthBrowser>;
62
+ os: ClosedEnum<StealthOS>;
63
+ };
64
+ proxyUsed: boolean;
65
+ requestClass?: ClosedEnum<StealthTelemetryRequestClass>;
66
+ sbsdDetected?: boolean;
67
+ sbsdOutcome?: ClosedEnum<StealthTelemetrySbsdOutcome>;
68
+ safeRefetch: number;
69
+ lastStatus?: number;
70
+ ms: number;
71
+ attemptSamples?: {
72
+ n: number;
73
+ ms: number;
74
+ status?: number;
75
+ e?: ClosedEnum<StealthTelemetryErrorCode>;
76
+ kind?: ClosedEnum<StealthTelemetryAttemptKind>;
77
+ }[];
78
+ };
79
+ export declare class StealthTelemetryCollector implements StealthTelemetrySink, TelemetryContributor<StealthTelemetryLogPayload, StealthTelemetryHeaderPayload> {
80
+ #private;
81
+ readonly key: "stealth";
82
+ constructor(options?: {
83
+ redact?: (text: string) => string;
84
+ });
85
+ recordAttempt(event: StealthTelemetryAttemptEvent): void;
86
+ recordPoolRefresh(): void;
87
+ recordRedirectHop(): void;
88
+ recordSbsd(event?: StealthTelemetrySbsdEvent | StealthTelemetrySbsdOutcome): void;
89
+ recordSafeRefetch(): void;
90
+ toLogPayload(): StealthTelemetryLogPayload | undefined;
91
+ toHeaderPayload(log: StealthTelemetryLogPayload): StealthTelemetryHeaderPayload;
92
+ }