@apifuse/provider-sdk 2.2.0-beta.1 → 2.2.0-beta.11

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 +106 -0
  2. package/CHANGELOG.md +41 -0
  3. package/README.md +5 -1
  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 +22 -81
  10. package/bin/apifuse-pack-types.ts +266 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +53 -70
  13. package/bin/apifuse-submit-check.ts +510 -45
  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 +1 -1
  23. package/dist/ceremonies/index.js +8 -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 +149 -8
  32. package/dist/config/loader.js +378 -89
  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 +8 -1
  39. package/dist/define.js +119 -122
  40. package/dist/dev.d.ts +1 -1
  41. package/dist/dev.js +1 -1
  42. package/dist/errors.d.ts +4 -1
  43. package/dist/errors.js +48 -0
  44. package/dist/i18n/catalog.d.ts +2 -2
  45. package/dist/i18n/catalog.js +4 -10
  46. package/dist/i18n/index.d.ts +2 -2
  47. package/dist/i18n/index.js +2 -2
  48. package/dist/i18n/keys.d.ts +2 -2
  49. package/dist/index.d.ts +44 -42
  50. package/dist/index.js +39 -37
  51. package/dist/lint.js +8 -15
  52. package/dist/provider.d.ts +11 -11
  53. package/dist/provider.js +9 -9
  54. package/dist/public-schema-field-lint.d.ts +1 -1
  55. package/dist/recipes/gov-api.js +1 -1
  56. package/dist/runtime/auth-flow.d.ts +1 -1
  57. package/dist/runtime/auth-flow.js +3 -3
  58. package/dist/runtime/browser.d.ts +1 -1
  59. package/dist/runtime/browser.js +15 -29
  60. package/dist/runtime/cache.d.ts +1 -1
  61. package/dist/runtime/cache.js +4 -8
  62. package/dist/runtime/choice.d.ts +1 -1
  63. package/dist/runtime/choice.js +31 -35
  64. package/dist/runtime/credential.d.ts +1 -1
  65. package/dist/runtime/credential.js +1 -1
  66. package/dist/runtime/env.d.ts +1 -1
  67. package/dist/runtime/executor.d.ts +1 -1
  68. package/dist/runtime/executor.js +15 -3
  69. package/dist/runtime/http.d.ts +2 -2
  70. package/dist/runtime/http.js +144 -348
  71. package/dist/runtime/insights.d.ts +1 -1
  72. package/dist/runtime/insights.js +6 -13
  73. package/dist/runtime/instrumentation.d.ts +2 -2
  74. package/dist/runtime/instrumentation.js +56 -19
  75. package/dist/runtime/keyring.js +1 -1
  76. package/dist/runtime/namespace.js +1 -1
  77. package/dist/runtime/otlp.d.ts +1 -1
  78. package/dist/runtime/perf.d.ts +1 -1
  79. package/dist/runtime/provider.d.ts +1 -1
  80. package/dist/runtime/provider.js +1 -2
  81. package/dist/runtime/proxy-errors.d.ts +1 -1
  82. package/dist/runtime/proxy-errors.js +9 -7
  83. package/dist/runtime/proxy-nodemaven.d.ts +35 -0
  84. package/dist/runtime/proxy-nodemaven.js +128 -0
  85. package/dist/runtime/proxy-retry-policy.d.ts +40 -0
  86. package/dist/runtime/proxy-retry-policy.js +326 -0
  87. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  88. package/dist/runtime/proxy-telemetry.js +55 -52
  89. package/dist/runtime/redis.d.ts +1 -1
  90. package/dist/runtime/redis.js +2 -4
  91. package/dist/runtime/request-options.d.ts +1 -1
  92. package/dist/runtime/secrets.d.ts +27 -0
  93. package/dist/runtime/secrets.js +51 -0
  94. package/dist/runtime/state.d.ts +2 -2
  95. package/dist/runtime/state.js +15 -4
  96. package/dist/runtime/stealth.d.ts +2 -2
  97. package/dist/runtime/stealth.js +108 -212
  98. package/dist/runtime/stt.d.ts +1 -1
  99. package/dist/runtime/stt.js +11 -15
  100. package/dist/runtime/trace.d.ts +2 -2
  101. package/dist/runtime/trace.js +2 -4
  102. package/dist/runtime/waterfall.d.ts +1 -1
  103. package/dist/schema.d.ts +1 -1
  104. package/dist/schema.js +7 -15
  105. package/dist/serve.d.ts +1 -1
  106. package/dist/serve.js +1 -1
  107. package/dist/server/index.d.ts +7 -7
  108. package/dist/server/index.js +6 -6
  109. package/dist/server/self-test-redaction.d.ts +1 -1
  110. package/dist/server/self-test-redaction.js +1 -1
  111. package/dist/server/self-test.d.ts +104 -3
  112. package/dist/server/self-test.js +673 -115
  113. package/dist/server/serve.d.ts +105 -4
  114. package/dist/server/serve.js +588 -80
  115. package/dist/server/types.d.ts +10 -9
  116. package/dist/server/types.js +3 -7
  117. package/dist/stateful/errors.d.ts +14 -0
  118. package/dist/stateful/errors.js +14 -0
  119. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  120. package/dist/stateful/http-provider-event-emitter.js +237 -0
  121. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  122. package/dist/stateful/http-session-owner-registry.js +210 -0
  123. package/dist/stateful/index.d.ts +18 -0
  124. package/dist/stateful/index.js +18 -0
  125. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  126. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  127. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  128. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  129. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  130. package/dist/stateful/provider-event-pipeline.js +1 -0
  131. package/dist/stateful/provider-events.d.ts +101 -0
  132. package/dist/stateful/provider-events.js +289 -0
  133. package/dist/stateful/session-key.d.ts +15 -0
  134. package/dist/stateful/session-key.js +86 -0
  135. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  136. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  137. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  138. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  139. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  140. package/dist/stateful/stateful-provider-adapter.js +287 -0
  141. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  142. package/dist/stateful/stateful-provider-observability.js +161 -0
  143. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  144. package/dist/stateful/stateful-provider-owner-forwarder.js +207 -0
  145. package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
  146. package/dist/stateful/stateful-provider-runtime-context.js +60 -0
  147. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  148. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  149. package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
  150. package/dist/stateful/stateful-provider-session-routing.js +353 -0
  151. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  152. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  153. package/dist/stateful-signing.d.ts +18 -0
  154. package/dist/stateful-signing.js +27 -0
  155. package/dist/stealth/profiles.d.ts +1 -1
  156. package/dist/stealth/profiles.js +5 -14
  157. package/dist/stream.d.ts +1 -1
  158. package/dist/testing/index.d.ts +2 -2
  159. package/dist/testing/index.js +2 -2
  160. package/dist/testing/run.d.ts +1 -1
  161. package/dist/testing/run.js +7 -14
  162. package/dist/types.d.ts +87 -1
  163. package/dist/user-input.d.ts +30 -0
  164. package/dist/user-input.js +66 -0
  165. package/package.json +14 -5
  166. package/src/auth-turn/index.ts +1 -1
  167. package/src/auth.ts +28 -86
  168. package/src/ceremonies/index.ts +24 -78
  169. package/src/cli/commands.ts +10 -0
  170. package/src/cli/create.ts +42 -35
  171. package/src/cli/prompt-assets.ts +865 -0
  172. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  173. package/src/config/loader.ts +543 -208
  174. package/src/contract-serialization.ts +5 -11
  175. package/src/contract-types.ts +2 -2
  176. package/src/contract.ts +12 -28
  177. package/src/define.ts +247 -505
  178. package/src/dev.ts +4 -9
  179. package/src/errors.ts +58 -5
  180. package/src/i18n/catalog.ts +10 -32
  181. package/src/i18n/index.ts +2 -2
  182. package/src/i18n/keys.ts +5 -11
  183. package/src/index.ts +49 -42
  184. package/src/lint.ts +89 -159
  185. package/src/provider.ts +15 -11
  186. package/src/public-schema-field-lint.ts +7 -33
  187. package/src/recipes/gov-api.ts +2 -5
  188. package/src/runtime/auth-flow.ts +5 -7
  189. package/src/runtime/browser.ts +64 -187
  190. package/src/runtime/cache.ts +20 -67
  191. package/src/runtime/choice.ts +79 -132
  192. package/src/runtime/credential.ts +2 -2
  193. package/src/runtime/env.ts +1 -1
  194. package/src/runtime/executor.ts +23 -20
  195. package/src/runtime/http.ts +195 -559
  196. package/src/runtime/insights.ts +15 -53
  197. package/src/runtime/instrumentation.ts +78 -65
  198. package/src/runtime/keyring.ts +7 -19
  199. package/src/runtime/namespace.ts +2 -7
  200. package/src/runtime/otlp.ts +12 -23
  201. package/src/runtime/perf.ts +1 -1
  202. package/src/runtime/provider.ts +4 -9
  203. package/src/runtime/proxy-errors.ts +29 -42
  204. package/src/runtime/proxy-nodemaven.ts +178 -0
  205. package/src/runtime/proxy-retry-policy.ts +469 -0
  206. package/src/runtime/proxy-telemetry.ts +79 -77
  207. package/src/runtime/redis.ts +4 -12
  208. package/src/runtime/request-options.ts +4 -13
  209. package/src/runtime/secrets.ts +64 -0
  210. package/src/runtime/state.ts +41 -110
  211. package/src/runtime/stealth.ts +162 -377
  212. package/src/runtime/stt.ts +38 -94
  213. package/src/runtime/trace.ts +14 -44
  214. package/src/runtime/waterfall.ts +5 -18
  215. package/src/schema.ts +23 -84
  216. package/src/serve.ts +1 -1
  217. package/src/server/index.ts +24 -7
  218. package/src/server/self-test-redaction.ts +2 -2
  219. package/src/server/self-test.ts +857 -132
  220. package/src/server/serve.ts +859 -266
  221. package/src/server/types.ts +7 -19
  222. package/src/stateful/README.md +146 -0
  223. package/src/stateful/errors.ts +23 -0
  224. package/src/stateful/http-provider-event-emitter.ts +314 -0
  225. package/src/stateful/http-session-owner-registry.ts +306 -0
  226. package/src/stateful/index.ts +18 -0
  227. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  228. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  229. package/src/stateful/provider-event-pipeline.ts +61 -0
  230. package/src/stateful/provider-events.ts +462 -0
  231. package/src/stateful/session-key.ts +111 -0
  232. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  233. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  234. package/src/stateful/stateful-provider-adapter.ts +562 -0
  235. package/src/stateful/stateful-provider-observability.ts +261 -0
  236. package/src/stateful/stateful-provider-owner-forwarder.ts +279 -0
  237. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  238. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  239. package/src/stateful/stateful-provider-session-routing.ts +555 -0
  240. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  241. package/src/stateful-signing.ts +46 -0
  242. package/src/stealth/profiles.ts +10 -26
  243. package/src/stream.ts +8 -19
  244. package/src/testing/index.ts +2 -2
  245. package/src/testing/run.ts +19 -67
  246. package/src/types.ts +88 -1
  247. package/src/user-input.ts +118 -0
  248. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  249. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  250. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  251. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  252. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  253. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  254. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  255. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  256. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  257. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  258. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  259. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  260. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  261. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
@@ -0,0 +1,86 @@
1
+ const PREFIX = "stateful:v1";
2
+ const REQUIRED_DIMENSIONS = ["providerId", "serviceAccountId", "connectionId"];
3
+ const REQUIRED_DIMENSION_SET = new Set(REQUIRED_DIMENSIONS);
4
+ export function buildSessionKey(parts) {
5
+ validateRequiredPart(parts.providerId, "providerId");
6
+ validateRequiredPart(parts.serviceAccountId, "serviceAccountId");
7
+ validateRequiredPart(parts.connectionId, "connectionId");
8
+ const dimensions = Object.entries(parts.dimensions ?? {}).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
9
+ for (const [name, value] of dimensions) {
10
+ validateDimension(name, value);
11
+ }
12
+ const segments = [
13
+ ["providerId", parts.providerId],
14
+ ["serviceAccountId", parts.serviceAccountId],
15
+ ["connectionId", parts.connectionId],
16
+ ...dimensions,
17
+ ];
18
+ return `${PREFIX}/${segments
19
+ .map(([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`)
20
+ .join("/")}`;
21
+ }
22
+ export function parseSessionKey(key) {
23
+ const prefix = `${PREFIX}/`;
24
+ if (!key.startsWith(prefix)) {
25
+ throw new Error(`Invalid session key: expected canonical prefix "${PREFIX}".`);
26
+ }
27
+ const parsed = new Map();
28
+ for (const segment of key.slice(prefix.length).split("/")) {
29
+ const separator = segment.indexOf("=");
30
+ if (separator <= 0) {
31
+ throw new Error(`Invalid session key segment "${segment}"; expected name=value.`);
32
+ }
33
+ const name = decodeSegment(segment.slice(0, separator), "dimension name");
34
+ const value = decodeSegment(segment.slice(separator + 1), `dimension "${name}"`);
35
+ if (parsed.has(name)) {
36
+ throw new Error(`Invalid session key: duplicate dimension "${name}".`);
37
+ }
38
+ if (name.trim().length === 0) {
39
+ throw new Error("Invalid session key: dimension name must not be empty.");
40
+ }
41
+ if (value.trim().length === 0) {
42
+ throw new Error(`Invalid session key: dimension "${name}" must not be empty.`);
43
+ }
44
+ parsed.set(name, value);
45
+ }
46
+ for (const required of REQUIRED_DIMENSIONS) {
47
+ if (!parsed.has(required)) {
48
+ throw new Error(`Invalid session key: missing required dimension "${required}".`);
49
+ }
50
+ }
51
+ const dimensions = {};
52
+ for (const [name, value] of parsed) {
53
+ if (!REQUIRED_DIMENSION_SET.has(name))
54
+ dimensions[name] = value;
55
+ }
56
+ return {
57
+ providerId: parsed.get("providerId"),
58
+ serviceAccountId: parsed.get("serviceAccountId"),
59
+ connectionId: parsed.get("connectionId"),
60
+ dimensions,
61
+ };
62
+ }
63
+ function validateRequiredPart(value, name) {
64
+ if (typeof value !== "string" || value.trim().length === 0) {
65
+ throw new Error(`Cannot build session key: required dimension "${name}" is missing or empty.`);
66
+ }
67
+ }
68
+ function validateDimension(name, value) {
69
+ if (name.trim().length === 0) {
70
+ throw new Error("Cannot build session key: extra dimension name must not be empty.");
71
+ }
72
+ if (REQUIRED_DIMENSION_SET.has(name)) {
73
+ throw new Error(`Cannot build session key: extra dimension "${name}" duplicates a required dimension.`);
74
+ }
75
+ if (typeof value !== "string" || value.trim().length === 0) {
76
+ throw new Error(`Cannot build session key: extra dimension "${name}" is missing or empty.`);
77
+ }
78
+ }
79
+ function decodeSegment(value, label) {
80
+ try {
81
+ return decodeURIComponent(value);
82
+ }
83
+ catch {
84
+ throw new Error(`Invalid session key: ${label} is not valid URI encoding.`);
85
+ }
86
+ }
@@ -0,0 +1,5 @@
1
+ import type { StatefulProviderSessionContext } from "./stateful-provider-adapter.js";
2
+ import type { StatefulOperationRequest } from "./stateful-provider-session-routing.js";
3
+ import type { ManagedSession, SessionOwnerRecord } from "./stateful-provider-session-runtime.js";
4
+ export declare function makeStatefulProviderSessionContext(providerId: string, owner: SessionOwnerRecord, request: StatefulOperationRequest, signal: AbortSignal): StatefulProviderSessionContext;
5
+ export declare function makeStatefulProviderCloseContext<TSession>(providerId: string, session: ManagedSession<TSession>): StatefulProviderSessionContext;
@@ -0,0 +1,42 @@
1
+ export function makeStatefulProviderSessionContext(providerId, owner, request, signal) {
2
+ const ctx = {
3
+ sessionKey: owner.sessionKey,
4
+ providerId,
5
+ connectionId: request.connectionId,
6
+ serviceAccountId: request.serviceAccountId,
7
+ ownerPodId: owner.ownerPodId,
8
+ ownerEndpoint: owner.ownerEndpoint,
9
+ ownerStatus: owner.status,
10
+ generation: owner.generation,
11
+ signal,
12
+ requestId: request.requestId,
13
+ operationId: request.operationId,
14
+ ...(request.runtimeContext !== undefined ? { runtimeContext: request.runtimeContext } : {}),
15
+ };
16
+ return withOptionalRequestMetadata(ctx, request);
17
+ }
18
+ export function makeStatefulProviderCloseContext(providerId, session) {
19
+ const identity = session.identity;
20
+ return {
21
+ sessionKey: session.sessionKey,
22
+ providerId,
23
+ ...(identity
24
+ ? {
25
+ connectionId: identity.connectionId,
26
+ serviceAccountId: identity.serviceAccountId,
27
+ ownerPodId: identity.ownerPodId,
28
+ ownerEndpoint: identity.ownerEndpoint,
29
+ ownerStatus: identity.ownerStatus,
30
+ }
31
+ : {}),
32
+ generation: session.generation,
33
+ signal: new AbortController().signal,
34
+ };
35
+ }
36
+ function withOptionalRequestMetadata(ctx, request) {
37
+ return {
38
+ ...ctx,
39
+ ...(request.idempotencyKey ? { idempotencyKey: request.idempotencyKey } : {}),
40
+ ...(request.deadlineAt ? { deadlineAt: request.deadlineAt } : {}),
41
+ };
42
+ }
@@ -0,0 +1,15 @@
1
+ import type { StatefulProviderMetricEmitter } from "./stateful-provider-observability.js";
2
+ import type { StatefulOperationRequest } from "./stateful-provider-session-routing.js";
3
+ import type { SessionOwnerRecord } from "./stateful-provider-session-runtime.js";
4
+ export declare function observeStatefulSessionOperationDuration(input: {
5
+ readonly metricEmitter: StatefulProviderMetricEmitter;
6
+ readonly request: StatefulOperationRequest;
7
+ readonly owner: SessionOwnerRecord;
8
+ readonly durationMs: number;
9
+ }): void;
10
+ export declare function emitStatefulSessionInvalidatedMetric(input: {
11
+ readonly metricEmitter: StatefulProviderMetricEmitter;
12
+ readonly request: StatefulOperationRequest;
13
+ readonly owner: SessionOwnerRecord;
14
+ readonly reason: string;
15
+ }): void;
@@ -0,0 +1,21 @@
1
+ export function observeStatefulSessionOperationDuration(input) {
2
+ input.metricEmitter.observe("apifuse_stateful_provider_session_operation_duration_ms", statefulSessionMetricLabels(input.request, input.owner), Math.max(0, input.durationMs));
3
+ }
4
+ export function emitStatefulSessionInvalidatedMetric(input) {
5
+ input.metricEmitter.increment("apifuse_stateful_provider_session_invalidations_total", statefulSessionMetricLabels(input.request, input.owner, {
6
+ redactedErrorCode: input.reason,
7
+ }));
8
+ }
9
+ function statefulSessionMetricLabels(request, owner, extra = {}) {
10
+ return {
11
+ requestId: request.requestId,
12
+ providerId: request.providerId,
13
+ connectionId: request.connectionId,
14
+ serviceAccountId: request.serviceAccountId,
15
+ operationId: request.operationId,
16
+ sessionKey: request.sessionKey,
17
+ ownerPodId: owner.ownerPodId,
18
+ generation: owner.generation,
19
+ ...extra,
20
+ };
21
+ }
@@ -0,0 +1,98 @@
1
+ import type { ProviderEventPublishOptions } from "./provider-event-pipeline.js";
2
+ import { type StatefulProviderMetricEmitter } from "./stateful-provider-observability.js";
3
+ import type { StatefulOperationExecutor, StatefulOperationRequest, StatefulOperationResult, StatefulOwnershipValidator } from "./stateful-provider-session-routing.js";
4
+ import type { SessionOwnerRecord, SessionPoolPolicy } from "./stateful-provider-session-runtime.js";
5
+ export type StatefulProviderConcurrencyPolicy = {
6
+ readonly mode: "serialize";
7
+ } | {
8
+ readonly mode: "parallel";
9
+ readonly maxInFlight?: number;
10
+ };
11
+ export type StatefulProviderReconnectPolicy = "resume" | "recreate" | "unsupported";
12
+ export type StatefulProviderAdapterPolicy = {
13
+ readonly concurrency: StatefulProviderConcurrencyPolicy;
14
+ readonly reconnect: StatefulProviderReconnectPolicy;
15
+ };
16
+ export type StatefulSessionHealth = {
17
+ readonly status: "ready" | "connecting" | "reconnecting" | "degraded" | "closed";
18
+ readonly reason?: string;
19
+ readonly lastError?: string;
20
+ };
21
+ export declare class StatefulSessionInvalidatedError extends Error {
22
+ readonly name = "StatefulSessionInvalidatedError";
23
+ readonly reason: string;
24
+ readonly retryable: boolean;
25
+ constructor(message: string, options: {
26
+ readonly reason: string;
27
+ readonly retryable?: boolean;
28
+ });
29
+ }
30
+ export interface StatefulProviderSessionContext {
31
+ readonly sessionKey: string;
32
+ readonly providerId: string;
33
+ /** Present for operation-created sessions; optional for direct pool users. */
34
+ readonly connectionId?: string;
35
+ /** Present for operation-created sessions; optional for direct pool users. */
36
+ readonly serviceAccountId?: string;
37
+ readonly ownerPodId?: string;
38
+ readonly ownerEndpoint?: string;
39
+ readonly ownerStatus?: string;
40
+ readonly generation: number;
41
+ readonly signal: AbortSignal;
42
+ readonly requestId?: string;
43
+ readonly operationId?: string;
44
+ readonly idempotencyKey?: string;
45
+ readonly deadlineAt?: string;
46
+ readonly runtimeContext?: unknown;
47
+ }
48
+ export type StatefulProviderEventPublish<TEvent = unknown> = (event: TEvent) => void | Promise<void>;
49
+ export type StatefulProviderEventDisposer = () => void | Promise<void>;
50
+ export interface StatefulProviderEventPublisher<TEvent = unknown> {
51
+ publish(event: TEvent, options: ProviderEventPublishOptions): unknown;
52
+ }
53
+ export type StatefulWriteReconciliationResult<TSharedState = unknown> = {
54
+ readonly result: StatefulOperationResult;
55
+ readonly sharedState?: TSharedState;
56
+ };
57
+ export interface StatefulProviderAdapter<TSession, TSharedState = unknown, TEvent = unknown> {
58
+ readonly providerId: string;
59
+ readonly policy: StatefulProviderAdapterPolicy;
60
+ /** Optional until durable cursor/checkpoint state exists for a provider. */
61
+ restore?(ctx: StatefulProviderSessionContext): Promise<TSharedState | undefined>;
62
+ connect(ctx: StatefulProviderSessionContext, sharedState: TSharedState | undefined): Promise<TSession>;
63
+ invoke(ctx: StatefulProviderSessionContext, session: TSession, request: StatefulOperationRequest): Promise<StatefulOperationResult>;
64
+ /** Requires a matching session-manager checkpointStore. */
65
+ snapshot?(ctx: StatefulProviderSessionContext, session: TSession): Promise<TSharedState | undefined>;
66
+ /** Caller-driven through StatefulProviderSessionManager.health(). */
67
+ health?(ctx: StatefulProviderSessionContext, session: TSession): Promise<StatefulSessionHealth>;
68
+ close(ctx: StatefulProviderSessionContext, session: TSession, reason: string): Promise<void>;
69
+ /** Caller-driven through StatefulProviderSessionManager.reconcileWrite(). */
70
+ reconcileWrite?(ctx: StatefulProviderSessionContext, ledgerEntry: unknown, sharedState: TSharedState | undefined): Promise<StatefulWriteReconciliationResult<TSharedState>>;
71
+ /** Registers a connection-owned event source. Its disposer runs before checkpoint and close. */
72
+ subscribe?(ctx: StatefulProviderSessionContext, session: TSession, publish: StatefulProviderEventPublish<TEvent>): StatefulProviderEventDisposer | Promise<StatefulProviderEventDisposer>;
73
+ }
74
+ export type StatefulProviderSessionManagerOptions<TSession, TSharedState, TEvent = unknown> = {
75
+ readonly adapter: StatefulProviderAdapter<TSession, TSharedState, TEvent>;
76
+ readonly poolPolicy: SessionPoolPolicy;
77
+ readonly checkpointStore?: (ctx: StatefulProviderSessionContext, state: TSharedState) => Promise<void>;
78
+ readonly eventPublisher?: StatefulProviderEventPublisher<TEvent>;
79
+ readonly clock?: () => Date;
80
+ readonly metricEmitter?: StatefulProviderMetricEmitter;
81
+ };
82
+ export declare class StatefulProviderSessionManager<TSession, TSharedState = unknown, TEvent = unknown> implements StatefulOperationExecutor<TSession> {
83
+ #private;
84
+ constructor(options: StatefulProviderSessionManagerOptions<TSession, TSharedState, TEvent>);
85
+ invoke(owner: SessionOwnerRecord, request: StatefulOperationRequest, signal: AbortSignal, validateOwnership?: StatefulOwnershipValidator): Promise<StatefulOperationResult>;
86
+ executeLocal(request: StatefulOperationRequest, owner: SessionOwnerRecord, signal: AbortSignal, validateOwnership?: StatefulOwnershipValidator): Promise<StatefulOperationResult>;
87
+ health(owner: SessionOwnerRecord, request: StatefulOperationRequest, signal: AbortSignal, validateOwnership?: StatefulOwnershipValidator): Promise<StatefulSessionHealth>;
88
+ reconcileWrite(owner: SessionOwnerRecord, request: StatefulOperationRequest, ledgerEntry: unknown, signal: AbortSignal, validateOwnership?: StatefulOwnershipValidator): Promise<StatefulWriteReconciliationResult<TSharedState>>;
89
+ closeAll(reason: string): Promise<void>;
90
+ private getOrCreate;
91
+ private connect;
92
+ private validatePooledOwnership;
93
+ private refreshEventOwnerFence;
94
+ private closeManagedSession;
95
+ private observeDuration;
96
+ private runLimited;
97
+ private schedule;
98
+ }
@@ -0,0 +1,287 @@
1
+ import { makeStatefulProviderCloseContext, makeStatefulProviderSessionContext, } from "./stateful-provider-adapter-context.js";
2
+ import { emitStatefulSessionInvalidatedMetric, observeStatefulSessionOperationDuration, } from "./stateful-provider-adapter-metrics.js";
3
+ import { NOOP_STATEFUL_PROVIDER_METRIC_EMITTER, } from "./stateful-provider-observability.js";
4
+ import { PodLocalSessionPool } from "./stateful-provider-session-runtime.js";
5
+ export class StatefulSessionInvalidatedError extends Error {
6
+ name = "StatefulSessionInvalidatedError";
7
+ reason;
8
+ retryable;
9
+ constructor(message, options) {
10
+ super(message);
11
+ this.reason = options.reason;
12
+ this.retryable = options.retryable ?? false;
13
+ }
14
+ }
15
+ const STATEFUL_SESSION_ESTABLISHMENT_FAILURE = Symbol.for("@apifuse/provider-sdk/stateful/session-establishment-failure@1");
16
+ function markStatefulSessionEstablishmentFailure(error) {
17
+ if ((typeof error === "object" && error !== null) || typeof error === "function") {
18
+ try {
19
+ Object.defineProperty(error, STATEFUL_SESSION_ESTABLISHMENT_FAILURE, {
20
+ value: true,
21
+ enumerable: false,
22
+ });
23
+ return error;
24
+ }
25
+ catch { }
26
+ }
27
+ const wrapped = new Error("Stateful session establishment failed.", { cause: error });
28
+ Object.defineProperty(wrapped, STATEFUL_SESSION_ESTABLISHMENT_FAILURE, { value: true });
29
+ return wrapped;
30
+ }
31
+ export class StatefulProviderSessionManager {
32
+ #adapter;
33
+ #pool;
34
+ #checkpointStore;
35
+ #eventPublisher;
36
+ #clock;
37
+ #metricEmitter;
38
+ #eventDisposers = new Map();
39
+ #eventOwnerFences = new Map();
40
+ #parallelLimiters = new Map();
41
+ constructor(options) {
42
+ validateAdapterConfiguration(options);
43
+ this.#adapter = options.adapter;
44
+ this.#checkpointStore = options.checkpointStore;
45
+ this.#eventPublisher = options.eventPublisher;
46
+ this.#clock = options.clock ?? (() => new Date());
47
+ this.#metricEmitter = options.metricEmitter ?? NOOP_STATEFUL_PROVIDER_METRIC_EMITTER;
48
+ this.#pool = new PodLocalSessionPool(options.poolPolicy, (session, reason) => this.closeManagedSession(session, reason));
49
+ }
50
+ async invoke(owner, request, signal, validateOwnership) {
51
+ const startedAt = this.#clock().getTime();
52
+ const run = async () => {
53
+ const managedSession = await this.getOrCreate(owner, request, signal, validateOwnership);
54
+ const validatedOwner = await this.validatePooledOwnership(owner, request.sessionKey, signal, validateOwnership);
55
+ const ctx = makeStatefulProviderSessionContext(this.#adapter.providerId, validatedOwner, request, signal);
56
+ try {
57
+ const result = await this.#adapter.invoke(ctx, managedSession.value, request);
58
+ this.observeDuration(request, owner, startedAt);
59
+ return result;
60
+ }
61
+ catch (error) {
62
+ if (!(error instanceof StatefulSessionInvalidatedError))
63
+ throw error;
64
+ emitStatefulSessionInvalidatedMetric({
65
+ metricEmitter: this.#metricEmitter,
66
+ request,
67
+ owner,
68
+ reason: error.reason,
69
+ });
70
+ await this.#pool.invalidate(owner.sessionKey, error.reason);
71
+ if (!error.retryable || this.#adapter.policy.reconnect === "unsupported")
72
+ throw error;
73
+ const reconnected = await this.getOrCreate(owner, request, signal, validateOwnership);
74
+ const revalidatedOwner = await this.validatePooledOwnership(owner, request.sessionKey, signal, validateOwnership);
75
+ const reconnectedCtx = makeStatefulProviderSessionContext(this.#adapter.providerId, revalidatedOwner, request, signal);
76
+ const result = await this.#adapter.invoke(reconnectedCtx, reconnected.value, request);
77
+ this.observeDuration(request, owner, startedAt);
78
+ return result;
79
+ }
80
+ };
81
+ return this.schedule(owner.sessionKey, run);
82
+ }
83
+ executeLocal(request, owner, signal, validateOwnership) {
84
+ return this.invoke(owner, request, signal, validateOwnership);
85
+ }
86
+ async health(owner, request, signal, validateOwnership) {
87
+ if (!this.#adapter.health) {
88
+ throw new Error(`Stateful provider adapter "${this.#adapter.providerId}" has no health hook.`);
89
+ }
90
+ return this.schedule(owner.sessionKey, async () => {
91
+ const session = await this.getOrCreate(owner, request, signal, validateOwnership);
92
+ const validatedOwner = await this.validatePooledOwnership(owner, request.sessionKey, signal, validateOwnership);
93
+ return this.#adapter.health?.(makeStatefulProviderSessionContext(this.#adapter.providerId, validatedOwner, request, signal), session.value);
94
+ });
95
+ }
96
+ async reconcileWrite(owner, request, ledgerEntry, signal, validateOwnership) {
97
+ if (!this.#adapter.reconcileWrite) {
98
+ throw new Error(`Stateful provider adapter "${this.#adapter.providerId}" does not support write reconciliation.`);
99
+ }
100
+ return this.schedule(owner.sessionKey, async () => {
101
+ const restoreCtx = makeStatefulProviderSessionContext(this.#adapter.providerId, owner, request, signal);
102
+ const sharedState = this.#adapter.restore
103
+ ? await this.#adapter.restore(restoreCtx)
104
+ : undefined;
105
+ const validatedOwner = validateOwnership ? await validateOwnership(owner, signal) : owner;
106
+ const ctx = makeStatefulProviderSessionContext(this.#adapter.providerId, validatedOwner, request, signal);
107
+ return this.#adapter.reconcileWrite?.(ctx, ledgerEntry, sharedState);
108
+ });
109
+ }
110
+ closeAll(reason) {
111
+ return this.#pool.closeAll(reason);
112
+ }
113
+ async getOrCreate(owner, request, signal, validateOwnership) {
114
+ return this.#pool.getOrCreate(owner.sessionKey, owner.generation, () => this.connect(owner, request, signal, validateOwnership), this.#clock(), managedSessionIdentity(owner, request));
115
+ }
116
+ async connect(owner, request, signal, validateOwnership) {
117
+ const ctx = makeStatefulProviderSessionContext(this.#adapter.providerId, owner, request, signal);
118
+ let session;
119
+ let connected = false;
120
+ try {
121
+ const sharedState = this.#adapter.restore ? await this.#adapter.restore(ctx) : undefined;
122
+ session = await this.#adapter.connect(ctx, sharedState);
123
+ connected = true;
124
+ const validatedOwner = validateOwnership ? await validateOwnership(owner, signal) : owner;
125
+ const validatedCtx = makeStatefulProviderSessionContext(this.#adapter.providerId, validatedOwner, request, signal);
126
+ this.refreshEventOwnerFence(validatedOwner, request.sessionKey);
127
+ if (!this.#adapter.subscribe || !this.#eventPublisher)
128
+ return session;
129
+ const identityKey = sessionIdentityKey(owner.sessionKey, owner.generation);
130
+ const disposer = await this.#adapter.subscribe(validatedCtx, session, (event) => {
131
+ const ownerFence = this.#eventOwnerFences.get(identityKey);
132
+ if (!ownerFence)
133
+ return;
134
+ this.#eventPublisher?.publish(event, { ownerFence });
135
+ });
136
+ if (typeof disposer !== "function") {
137
+ throw new Error(`Stateful provider adapter "${this.#adapter.providerId}" subscribe hook must return a disposer.`);
138
+ }
139
+ this.#eventDisposers.set(identityKey, disposer);
140
+ return session;
141
+ }
142
+ catch (error) {
143
+ this.#eventOwnerFences.delete(sessionIdentityKey(owner.sessionKey, owner.generation));
144
+ let establishmentError = error;
145
+ if (connected) {
146
+ try {
147
+ await this.#adapter.close(ctx, session, "session-establishment-failed");
148
+ }
149
+ catch (closeError) {
150
+ establishmentError = new AggregateError([error, closeError], "Stateful session establishment and cleanup both failed.");
151
+ }
152
+ }
153
+ throw markStatefulSessionEstablishmentFailure(establishmentError);
154
+ }
155
+ }
156
+ async validatePooledOwnership(owner, sessionKey, signal, validateOwnership) {
157
+ try {
158
+ const validatedOwner = validateOwnership ? await validateOwnership(owner, signal) : owner;
159
+ this.refreshEventOwnerFence(validatedOwner, sessionKey);
160
+ return validatedOwner;
161
+ }
162
+ catch (error) {
163
+ await this.#pool
164
+ .invalidate(owner.sessionKey, "ownership-validation-failed")
165
+ .catch(() => undefined);
166
+ throw markStatefulSessionEstablishmentFailure(error);
167
+ }
168
+ }
169
+ refreshEventOwnerFence(owner, sessionKey) {
170
+ this.#eventOwnerFences.set(sessionIdentityKey(owner.sessionKey, owner.generation), {
171
+ sessionKey,
172
+ generation: owner.generation,
173
+ ownerPodId: owner.ownerPodId,
174
+ ownerEndpoint: owner.ownerEndpoint,
175
+ });
176
+ }
177
+ async closeManagedSession(session, reason) {
178
+ const ctx = makeStatefulProviderCloseContext(this.#adapter.providerId, session);
179
+ const errors = [];
180
+ const disposerKey = sessionIdentityKey(session.sessionKey, session.generation);
181
+ const disposer = this.#eventDisposers.get(disposerKey);
182
+ this.#eventDisposers.delete(disposerKey);
183
+ this.#eventOwnerFences.delete(disposerKey);
184
+ if (disposer) {
185
+ try {
186
+ await disposer();
187
+ }
188
+ catch (error) {
189
+ errors.push(error);
190
+ }
191
+ }
192
+ if (this.#adapter.snapshot && this.#checkpointStore) {
193
+ try {
194
+ const state = await this.#adapter.snapshot(ctx, session.value);
195
+ if (state !== undefined)
196
+ await this.#checkpointStore(ctx, state);
197
+ }
198
+ catch (error) {
199
+ errors.push(error);
200
+ }
201
+ }
202
+ try {
203
+ await this.#adapter.close(ctx, session.value, reason);
204
+ }
205
+ catch (error) {
206
+ errors.push(error);
207
+ }
208
+ if (errors.length === 1)
209
+ throw errors[0];
210
+ if (errors.length > 1) {
211
+ throw new AggregateError(errors, "Stateful session cleanup failed in multiple hooks.");
212
+ }
213
+ }
214
+ observeDuration(request, owner, startedAt) {
215
+ observeStatefulSessionOperationDuration({
216
+ metricEmitter: this.#metricEmitter,
217
+ request,
218
+ owner,
219
+ durationMs: this.#clock().getTime() - startedAt,
220
+ });
221
+ }
222
+ async runLimited(sessionKey, maxInFlight, run) {
223
+ const limiter = this.#parallelLimiters.get(sessionKey) ?? { active: 0, waiting: [] };
224
+ this.#parallelLimiters.set(sessionKey, limiter);
225
+ if (limiter.active >= maxInFlight) {
226
+ await new Promise((resolve) => limiter.waiting.push(resolve));
227
+ }
228
+ limiter.active += 1;
229
+ try {
230
+ return await run();
231
+ }
232
+ finally {
233
+ limiter.active -= 1;
234
+ limiter.waiting.shift()?.();
235
+ if (limiter.active === 0 && limiter.waiting.length === 0) {
236
+ this.#parallelLimiters.delete(sessionKey);
237
+ }
238
+ }
239
+ }
240
+ schedule(sessionKey, run) {
241
+ const concurrency = this.#adapter.policy.concurrency;
242
+ if (concurrency.mode === "serialize")
243
+ return this.#pool.runExclusive(sessionKey, run);
244
+ if (concurrency.maxInFlight === undefined)
245
+ return run();
246
+ return this.runLimited(sessionKey, concurrency.maxInFlight, run);
247
+ }
248
+ }
249
+ function validateAdapterConfiguration(options) {
250
+ const adapter = options.adapter;
251
+ if (!adapter.policy?.concurrency || !adapter.policy.reconnect) {
252
+ throw new Error(`Stateful provider adapter "${adapter.providerId}" requires concurrency and reconnect policies.`);
253
+ }
254
+ const concurrency = adapter.policy.concurrency;
255
+ if (concurrency.mode !== "serialize" && concurrency.mode !== "parallel") {
256
+ throw new Error('Stateful concurrency policy mode must be "serialize" or "parallel".');
257
+ }
258
+ if (concurrency.mode === "serialize" && "maxInFlight" in concurrency) {
259
+ throw new Error("Stateful serialize concurrency must not declare maxInFlight.");
260
+ }
261
+ if (concurrency.mode === "parallel" &&
262
+ concurrency.maxInFlight !== undefined &&
263
+ (!Number.isInteger(concurrency.maxInFlight) || concurrency.maxInFlight <= 0)) {
264
+ throw new Error("Stateful parallel concurrency maxInFlight must be a positive integer.");
265
+ }
266
+ if (!["resume", "recreate", "unsupported"].includes(adapter.policy.reconnect)) {
267
+ throw new Error('Stateful reconnect policy must be "resume", "recreate", or "unsupported".');
268
+ }
269
+ if (Boolean(adapter.snapshot) !== Boolean(options.checkpointStore)) {
270
+ throw new Error(`Stateful provider adapter "${adapter.providerId}" snapshot and session-manager checkpointStore must be configured together.`);
271
+ }
272
+ if (Boolean(adapter.subscribe) !== Boolean(options.eventPublisher)) {
273
+ throw new Error(`Stateful provider adapter "${adapter.providerId}" subscribe and session-manager eventPublisher must be configured together.`);
274
+ }
275
+ }
276
+ function managedSessionIdentity(owner, request) {
277
+ return {
278
+ connectionId: request.connectionId,
279
+ serviceAccountId: request.serviceAccountId,
280
+ ownerPodId: owner.ownerPodId,
281
+ ownerEndpoint: owner.ownerEndpoint,
282
+ ownerStatus: owner.status,
283
+ };
284
+ }
285
+ function sessionIdentityKey(sessionKey, generation) {
286
+ return `${sessionKey}\u001f${generation}`;
287
+ }
@@ -0,0 +1,62 @@
1
+ export declare const STATEFUL_PROVIDER_METRIC_NAMES: readonly ["apifuse_stateful_provider_session_owner_acquire_total", "apifuse_stateful_provider_session_owner_renew_total", "apifuse_stateful_provider_session_owner_release_total", "apifuse_stateful_provider_session_owner_conflict_total", "apifuse_stateful_provider_session_owner_fencing_stale_total", "apifuse_stateful_provider_active_sessions", "apifuse_stateful_provider_lru_evictions_total", "apifuse_stateful_provider_session_invalidations_total", "apifuse_stateful_provider_session_operation_queue_depth", "apifuse_stateful_provider_session_operation_queue_wait_ms", "apifuse_stateful_provider_session_operation_duration_ms", "apifuse_stateful_provider_routing_forward_total", "apifuse_stateful_provider_routing_local_total", "apifuse_stateful_provider_routing_reacquire_total", "apifuse_stateful_provider_routing_deadline_expired_total", "apifuse_stateful_provider_event_append_total", "apifuse_stateful_provider_event_dedupe_total", "apifuse_stateful_provider_event_drop_total", "apifuse_stateful_provider_event_lag_ms", "apifuse_stateful_provider_webhook_enqueue_total", "apifuse_stateful_provider_webhook_fanout_recovered_total", "apifuse_stateful_provider_webhook_delivered_total", "apifuse_stateful_provider_webhook_retry_total", "apifuse_stateful_provider_webhook_dead_letter_total", "apifuse_stateful_provider_webhook_canceled_total", "apifuse_stateful_provider_webhook_replay_total", "apifuse_stateful_provider_webhook_latency_ms", "apifuse_stateful_provider_write_ledger_ambiguous_total", "apifuse_stateful_provider_write_ledger_reconciled_total", "apifuse_stateful_provider_write_ledger_failed_total"];
2
+ export type StatefulProviderMetricName = (typeof STATEFUL_PROVIDER_METRIC_NAMES)[number];
3
+ export declare const STATEFUL_PROVIDER_LOG_FIELDS: readonly ["requestId", "eventId", "deliveryId", "providerId", "connectionId", "serviceAccountId", "operationId", "sessionKeyHash", "ownerPodId", "generation", "leaseExpiresAt", "capacityClass", "queueDepth", "eventType", "subjectKind", "subjectId", "retryCount", "ledgerState", "healthStatus", "redactedErrorCode"];
4
+ export type StatefulProviderLogField = (typeof STATEFUL_PROVIDER_LOG_FIELDS)[number];
5
+ export declare const STATEFUL_PROVIDER_AUTOSCALING_SIGNALS: readonly ["active_sessions", "session_queue_depth", "session_queue_wait_ms", "reconnect_rate", "provider_event_lag_ms", "webhook_outbox_depth", "webhook_due_retry_depth", "memory_pressure", "file_descriptor_pressure", "socket_pressure"];
6
+ export type StatefulProviderAutoscalingSignal = (typeof STATEFUL_PROVIDER_AUTOSCALING_SIGNALS)[number];
7
+ export declare const STATEFUL_PROVIDER_HEALTH_DIMENSIONS: readonly ["registry_connectivity", "owner_lease_freshness", "pool_capacity", "upstream_auth_validity", "push_loop_lag", "event_append_lag", "webhook_backlog"];
8
+ export type StatefulProviderHealthDimension = (typeof STATEFUL_PROVIDER_HEALTH_DIMENSIONS)[number];
9
+ export declare const STATEFUL_PROVIDER_ROLLOUT_STATES: readonly ["disabled", "tenant_allowlist", "connection_allowlist", "shadow", "canary", "production", "rollback"];
10
+ export type StatefulProviderRolloutState = (typeof STATEFUL_PROVIDER_ROLLOUT_STATES)[number];
11
+ export interface StatefulProviderMetricLabelInput {
12
+ readonly requestId?: string;
13
+ readonly eventId?: string;
14
+ readonly deliveryId?: string;
15
+ readonly providerId?: string;
16
+ readonly connectionId?: string;
17
+ readonly serviceAccountId?: string;
18
+ readonly operationId?: string;
19
+ readonly sessionKey?: string;
20
+ readonly sessionKeyHash?: string;
21
+ readonly ownerPodId?: string;
22
+ readonly generation?: number | string;
23
+ readonly leaseExpiresAt?: string;
24
+ readonly capacityClass?: string;
25
+ readonly queueDepth?: number | string;
26
+ readonly eventType?: string;
27
+ readonly subjectKind?: string;
28
+ readonly subjectId?: string;
29
+ readonly retryCount?: number | string;
30
+ readonly ledgerState?: string;
31
+ readonly healthStatus?: string;
32
+ readonly redactedErrorCode?: string;
33
+ }
34
+ export type StatefulProviderMetricLabels = Partial<Record<StatefulProviderLogField, string>>;
35
+ export interface StatefulProviderMetricEmitter {
36
+ increment(name: StatefulProviderMetricName, labels?: StatefulProviderMetricLabelInput, value?: number): void;
37
+ gauge(name: StatefulProviderMetricName, labels: StatefulProviderMetricLabelInput, value: number): void;
38
+ observe(name: StatefulProviderMetricName, labels: StatefulProviderMetricLabelInput, value: number): void;
39
+ }
40
+ export type StatefulProviderRecordedMetric = {
41
+ readonly kind: "increment" | "gauge" | "observe";
42
+ readonly name: StatefulProviderMetricName;
43
+ readonly labels: StatefulProviderMetricLabels;
44
+ readonly value: number;
45
+ };
46
+ export declare const NOOP_STATEFUL_PROVIDER_METRIC_EMITTER: {
47
+ increment(): void;
48
+ gauge(): void;
49
+ observe(): void;
50
+ };
51
+ export declare class RecordingStatefulProviderMetricEmitter implements StatefulProviderMetricEmitter {
52
+ readonly metrics: StatefulProviderRecordedMetric[];
53
+ increment(name: StatefulProviderMetricName, labels?: StatefulProviderMetricLabelInput, value?: number): void;
54
+ gauge(name: StatefulProviderMetricName, labels: StatefulProviderMetricLabelInput, value: number): void;
55
+ observe(name: StatefulProviderMetricName, labels: StatefulProviderMetricLabelInput, value: number): void;
56
+ }
57
+ export declare function isStatefulProviderMetricName(value: unknown): value is StatefulProviderMetricName;
58
+ export declare function isStatefulProviderLogField(value: unknown): value is StatefulProviderLogField;
59
+ export declare function isStatefulProviderAutoscalingSignal(value: unknown): value is StatefulProviderAutoscalingSignal;
60
+ export declare function sanitizeSessionKeyForLog(sessionKey: string): string;
61
+ export declare function statefulProviderMetricLabels(input: StatefulProviderMetricLabelInput): StatefulProviderMetricLabels;
62
+ export declare function assertNoRawSessionKeyInLabels(labels: Record<string, string | number | boolean | null | undefined>, rawSessionKey: string): void;