theokit 0.12.0 → 0.13.0

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 (131) hide show
  1. package/dist/{actions-virtual-module-SQDY3V5X.js → actions-virtual-module-3CDQTWOC.js} +6 -6
  2. package/dist/{actions-virtual-module-PNPRCEOS.js → actions-virtual-module-EIPXX4ZB.js} +3 -3
  3. package/dist/adapters/web-shim.d.ts +67 -0
  4. package/dist/adapters/ws-shim.d.ts +55 -0
  5. package/dist/agent-events-DosDXkSV.d.ts +94 -0
  6. package/dist/agents-typed-client-SAWAAH7K.js +142 -0
  7. package/dist/agents-typed-client-SAWAAH7K.js.map +1 -0
  8. package/dist/agents-typed-client-UTEQUA63.js +143 -0
  9. package/dist/agents-typed-client-UTEQUA63.js.map +1 -0
  10. package/dist/{app-typed-client-5GYEOYP3.js → app-typed-client-7PBFWZUE.js} +3 -3
  11. package/dist/{app-typed-client-QG7BVZYW.js → app-typed-client-CSOK7NPC.js} +6 -6
  12. package/dist/audit-log-BQWM5YLG.d.ts +60 -0
  13. package/dist/body-parser-web-FV5HWCY3.js +71 -0
  14. package/dist/body-parser-web-FV5HWCY3.js.map +1 -0
  15. package/dist/boot/index.d.ts +39 -0
  16. package/dist/{build-QFRLSEZ4.js → build-HXND27XG.js} +11 -11
  17. package/dist/{chunk-223EFY5X.js → chunk-2J7XU3PW.js} +68 -27
  18. package/dist/chunk-2J7XU3PW.js.map +1 -0
  19. package/dist/{chunk-RESN62GB.js → chunk-2KZQPDYR.js} +5 -48
  20. package/dist/chunk-2KZQPDYR.js.map +1 -0
  21. package/dist/chunk-3S3BNW5K.js +445 -0
  22. package/dist/chunk-3S3BNW5K.js.map +1 -0
  23. package/dist/{chunk-6FYD34NX.js → chunk-BQDGES7C.js} +28 -28
  24. package/dist/{chunk-6FYD34NX.js.map → chunk-BQDGES7C.js.map} +1 -1
  25. package/dist/chunk-EXP56GFQ.js +52 -0
  26. package/dist/chunk-EXP56GFQ.js.map +1 -0
  27. package/dist/chunk-F4YUPDJ2.js +115 -0
  28. package/dist/chunk-F4YUPDJ2.js.map +1 -0
  29. package/dist/{chunk-NAZ4E2GT.js → chunk-KXA37ONC.js} +2 -2
  30. package/dist/chunk-NHJMZCAS.js +32 -0
  31. package/dist/chunk-NHJMZCAS.js.map +1 -0
  32. package/dist/{chunk-43D6XNDR.js → chunk-O62MW4MT.js} +91 -18
  33. package/dist/chunk-O62MW4MT.js.map +1 -0
  34. package/dist/chunk-RSVN727G.js +1 -0
  35. package/dist/{chunk-7CBRKNQA.js → chunk-RYTZYFSD.js} +198 -6
  36. package/dist/chunk-RYTZYFSD.js.map +1 -0
  37. package/dist/chunk-UNLA45FY.js +235 -0
  38. package/dist/chunk-UNLA45FY.js.map +1 -0
  39. package/dist/{chunk-GFMQJHXX.js → chunk-WR4F4EEZ.js} +1082 -1074
  40. package/dist/chunk-WR4F4EEZ.js.map +1 -0
  41. package/dist/{chunk-AD74EAK3.js → chunk-ZSTZXR2D.js} +1 -30
  42. package/dist/chunk-ZSTZXR2D.js.map +1 -0
  43. package/dist/cli/index.js +5 -5
  44. package/dist/client/index.d.ts +418 -0
  45. package/dist/client/index.js +84 -3
  46. package/dist/client/index.js.map +1 -1
  47. package/dist/csrf-BBrEZSBW.d.ts +107 -0
  48. package/dist/csrf-readiness-store-CjIoub3U.d.ts +43 -0
  49. package/dist/define-websocket-CdK94O-D.d.ts +64 -0
  50. package/dist/{dev-GBXOTXUP.js → dev-OWW4XVIH.js} +10 -10
  51. package/dist/{dev-emit-FEFEDLZF.js → dev-emit-5MDSBP5D.js} +3 -3
  52. package/dist/{dev-emit-O4EGOSNV.js → dev-emit-QH2YGZXN.js} +2 -2
  53. package/dist/devtools/entry.d.ts +5 -0
  54. package/dist/error-envelope-BsNzzAV5.d.ts +62 -0
  55. package/dist/health-route-C0hk64_U.d.ts +57 -0
  56. package/dist/index-B40qUSrQ.d.ts +575 -0
  57. package/dist/index.d.ts +361 -0
  58. package/dist/index.js +6 -4
  59. package/dist/index.js.map +1 -1
  60. package/dist/internal-api-4YTJDITC.js +83 -0
  61. package/dist/internal-api-EFKZWIYZ.js +66 -0
  62. package/dist/internal-api-EFKZWIYZ.js.map +1 -0
  63. package/dist/job-backend-CgC8Xf33.d.ts +68 -0
  64. package/dist/match-CfbEFRG4.d.ts +26 -0
  65. package/dist/{openapi-VR6AFBLJ.js → openapi-FHY6HC6I.js} +7 -7
  66. package/dist/plugin-runner-BGBkzgi0.d.ts +95 -0
  67. package/dist/plugin-types-DNJGxr4Z.d.ts +79 -0
  68. package/dist/rate-limit-BdNDZ3vt.d.ts +58 -0
  69. package/dist/rate-limit-store-BEJnhWdw.d.ts +72 -0
  70. package/dist/react-query/index.d.ts +33 -0
  71. package/dist/{registry-Q2TZQLUH.js → registry-34LL7NF4.js} +1 -1
  72. package/dist/{routes-LRYOIIAI.js → routes-EW7TP7NJ.js} +2 -2
  73. package/dist/schema-BpH6ivDY.d.ts +74 -0
  74. package/dist/server/agent/index.d.ts +229 -0
  75. package/dist/server/agent/index.js +2 -1
  76. package/dist/server/auth/index.d.ts +419 -0
  77. package/dist/server/cost/index.d.ts +177 -0
  78. package/dist/server/cron/index.d.ts +208 -0
  79. package/dist/server/define/index.d.ts +313 -0
  80. package/dist/server/define/index.js +4 -2
  81. package/dist/server/http/index.d.ts +11 -0
  82. package/dist/server/index.d.ts +848 -0
  83. package/dist/server/index.js +9 -294
  84. package/dist/server/index.js.map +1 -1
  85. package/dist/server/jobs/index.d.ts +348 -0
  86. package/dist/server/observability/index.d.ts +324 -0
  87. package/dist/server/plugins/index.d.ts +17 -0
  88. package/dist/server/rate-limit/index.d.ts +105 -0
  89. package/dist/server/realtime/index.d.ts +15 -0
  90. package/dist/server/scan/index.d.ts +126 -0
  91. package/dist/server/scan/index.js +1 -1
  92. package/dist/server/security/index.d.ts +193 -0
  93. package/dist/server/storage/index.d.ts +22 -0
  94. package/dist/server/webhook/index.d.ts +148 -0
  95. package/dist/{start-3ZHAXSJE.js → start-KIQ5TTLR.js} +76 -13
  96. package/dist/start-KIQ5TTLR.js.map +1 -0
  97. package/dist/storage-manager-C4jsO0Tp.d.ts +89 -0
  98. package/dist/storage-types-DsDTCPbp.d.ts +96 -0
  99. package/dist/vite-plugin/index.d.ts +115 -0
  100. package/dist/vite-plugin/index.js +6 -4
  101. package/dist/{vite-plugin-WO72VLYR.js → vite-plugin-RK66K26Z.js} +7 -7
  102. package/dist/vite-plugin-RK66K26Z.js.map +1 -0
  103. package/package.json +4 -4
  104. package/dist/chunk-223EFY5X.js.map +0 -1
  105. package/dist/chunk-3LVRAGAZ.js +0 -73
  106. package/dist/chunk-3LVRAGAZ.js.map +0 -1
  107. package/dist/chunk-43D6XNDR.js.map +0 -1
  108. package/dist/chunk-7CBRKNQA.js.map +0 -1
  109. package/dist/chunk-AD74EAK3.js.map +0 -1
  110. package/dist/chunk-GFMQJHXX.js.map +0 -1
  111. package/dist/chunk-PBEH6NXR.js +0 -44
  112. package/dist/chunk-PBEH6NXR.js.map +0 -1
  113. package/dist/chunk-PIVX3DYW.js +0 -142
  114. package/dist/chunk-PIVX3DYW.js.map +0 -1
  115. package/dist/chunk-PPPR5DGR.js +0 -1
  116. package/dist/chunk-RESN62GB.js.map +0 -1
  117. package/dist/start-3ZHAXSJE.js.map +0 -1
  118. /package/dist/{actions-virtual-module-SQDY3V5X.js.map → actions-virtual-module-3CDQTWOC.js.map} +0 -0
  119. /package/dist/{actions-virtual-module-PNPRCEOS.js.map → actions-virtual-module-EIPXX4ZB.js.map} +0 -0
  120. /package/dist/{app-typed-client-5GYEOYP3.js.map → app-typed-client-7PBFWZUE.js.map} +0 -0
  121. /package/dist/{app-typed-client-QG7BVZYW.js.map → app-typed-client-CSOK7NPC.js.map} +0 -0
  122. /package/dist/{build-QFRLSEZ4.js.map → build-HXND27XG.js.map} +0 -0
  123. /package/dist/{chunk-NAZ4E2GT.js.map → chunk-KXA37ONC.js.map} +0 -0
  124. /package/dist/{chunk-PPPR5DGR.js.map → chunk-RSVN727G.js.map} +0 -0
  125. /package/dist/{dev-GBXOTXUP.js.map → dev-OWW4XVIH.js.map} +0 -0
  126. /package/dist/{dev-emit-FEFEDLZF.js.map → dev-emit-5MDSBP5D.js.map} +0 -0
  127. /package/dist/{dev-emit-O4EGOSNV.js.map → dev-emit-QH2YGZXN.js.map} +0 -0
  128. /package/dist/{vite-plugin-WO72VLYR.js.map → internal-api-4YTJDITC.js.map} +0 -0
  129. /package/dist/{openapi-VR6AFBLJ.js.map → openapi-FHY6HC6I.js.map} +0 -0
  130. /package/dist/{registry-Q2TZQLUH.js.map → registry-34LL7NF4.js.map} +0 -0
  131. /package/dist/{routes-LRYOIIAI.js.map → routes-EW7TP7NJ.js.map} +0 -0
@@ -0,0 +1,229 @@
1
+ import { a as AgentErrorEvent, A as AgentEvent } from '../../agent-events-DosDXkSV.js';
2
+ export { b as AgentMessageEvent, f as AgentRunErrorCode, c as AgentThinkingEvent, d as AgentToolCallEvent, e as AgentToolResultEvent } from '../../agent-events-DosDXkSV.js';
3
+
4
+ /**
5
+ * Item #4 — `streamAgentRun`
6
+ *
7
+ * Adapter that consumes the `@theokit/sdk` `Run.stream()` async generator
8
+ * (SDKMessage variants) and yields TheoKit `AgentEvent`s suitable for SSE
9
+ * via `defineAgentEndpoint`. One line at the consumer side:
10
+ *
11
+ * ```ts
12
+ * const run = await agent.send(message)
13
+ * yield* streamAgentRun(run)
14
+ * ```
15
+ *
16
+ * Mapping table (SDK → AgentEvent):
17
+ *
18
+ * | SDK message | AgentEvent yielded |
19
+ * |---|---|
20
+ * | assistant.content[].type==='text' | { type: 'message', content } |
21
+ * | assistant.content[].type==='tool_use'| (none — covered by tool_call below) |
22
+ * | tool_call(status='running') | { type: 'tool_call', name, args, id } |
23
+ * | tool_call(status='completed') | { type: 'tool_result', name, data, id }|
24
+ * | tool_call(status='error') | { type: 'error', message, id } |
25
+ * | run.wait() status==='error' | { type: 'error', message } |
26
+ * | run.wait() status==='cancelled' | (none — cancel ≠ error) |
27
+ * | system / user / thinking / status / | (none — internal SDK telemetry) |
28
+ * | task / request / object_delta | |
29
+ *
30
+ * Abort semantics: when the consumer calls `generator.return()` on the
31
+ * outer `defineAgentEndpoint` generator, `streamAgentRun` exits the
32
+ * `for await` loop and does NOT call `run.wait()`. Cleanup of in-flight
33
+ * SDK resources is the SDK consumer's responsibility (`run.cancel()`).
34
+ */
35
+ /**
36
+ * Local mirror of the SDK's `Run` interface — only the surfaces we consume.
37
+ * The message contract is minimal (just `{ type: string }`) so the SDK's
38
+ * `SDKMessage` discriminated union IS structurally assignable without any
39
+ * cast at the consumer site (covariant — TS accepts the SDK's typed message
40
+ * as the wider `{ type: string }`). Property access inside `streamAgentRun`
41
+ * narrows via runtime type guards.
42
+ *
43
+ * `import type` from `@theokit/sdk` would couple TheoKit to the SDK at type-
44
+ * resolution time even for consumers who never use the agent surface.
45
+ */
46
+ interface AgentRunLike {
47
+ stream: () => AsyncIterable<{
48
+ type: string;
49
+ }>;
50
+ wait: () => Promise<AgentRunResult>;
51
+ }
52
+ /**
53
+ * Re-exported as a convenience for test fixtures. Production code typically
54
+ * passes an SDK `Run` directly (structural match via `{ type: string }`).
55
+ */
56
+ type AgentRunStreamMessage = {
57
+ type: 'assistant';
58
+ message: {
59
+ role: 'assistant';
60
+ content: {
61
+ type: string;
62
+ text?: string;
63
+ }[];
64
+ };
65
+ } | {
66
+ type: 'tool_call';
67
+ name: string;
68
+ status: 'running' | 'completed' | 'error';
69
+ args?: unknown;
70
+ result?: unknown;
71
+ call_id: string;
72
+ } | {
73
+ type: string;
74
+ [k: string]: unknown;
75
+ };
76
+ /** Subset of the SDK `RunResult` we consume. */
77
+ interface AgentRunResult {
78
+ status: 'finished' | 'error' | 'cancelled';
79
+ error?: {
80
+ message: string;
81
+ code?: string;
82
+ cause?: unknown;
83
+ };
84
+ }
85
+ /**
86
+ * Map an SDK error to the AgentErrorEvent shape. Pure function — easy to test.
87
+ *
88
+ * Backward compat (D4): non-AgentRunError throws yield only `message` field
89
+ * (legacy shape); discriminated fields stay `undefined`.
90
+ *
91
+ * Return type is the specific `AgentErrorEvent` (not the union) for ergonomic
92
+ * call-site access — `errorToEvent(err).code` works without narrowing.
93
+ */
94
+ declare function errorToEvent(err: unknown, id?: string): AgentErrorEvent;
95
+ declare function streamAgentRun(run: AgentRunLike): AsyncGenerator<AgentEvent, void, unknown>;
96
+
97
+ /**
98
+ * Item #5 — `createConversationHistory`
99
+ *
100
+ * Orchestrator that resolves a stable `agentId` from (explicit → session →
101
+ * cookie → fresh UUID), then returns an `@theokit/sdk` `Agent` via
102
+ * `Agent.getOrCreate(agentId, options)`. Conversation turns auto-persist in
103
+ * `<cwd>/.theokit/agents/<agentId>/messages.jsonl` — SDK owns the storage
104
+ * (ADR D1). `MemorySettings` (facts recall layer) is opt-in passthrough via
105
+ * `options.memory` (ADR D2).
106
+ *
107
+ * Security: agentId from cookie/explicit is attacker-controlled. The SDK
108
+ * uses it as a filesystem path component AND we serialize it into Set-Cookie.
109
+ * EC-1 enforces a strict regex `^[a-zA-Z0-9_-]{1,128}$` at every entry point;
110
+ * invalid values fall through (treated as "missing") rather than throw.
111
+ */
112
+ /**
113
+ * Minimum surface of an SDK Agent that consumers care about post-creation.
114
+ * Structural match — any object with `send` + `dispose` of compatible
115
+ * shape works. `send` returns a `SdkRunLike` (a Run-shaped object) that
116
+ * `streamAgentRun` can consume. Permissive `unknown` for now; consumers
117
+ * who want stricter types can cast to the SDK's own types.
118
+ *
119
+ * **T5.2 (architecture-cleanup) — DP-7 decision: KEEP (Opt B).** The 5 duck-typed
120
+ * mirrors below were flagged as "over-engineered" by the 2026-05-27 architecture
121
+ * review. Decision: KEEP them because `@theokit/sdk` is `devDependency` (not
122
+ * required at runtime for consumers that don't use agent features). Removing
123
+ * the mirrors would force a hard runtime dep on the SDK, breaking consumers
124
+ * who build TheoKit apps without the agent layer.
125
+ *
126
+ * @kept Defensive duck-type. Do NOT replace with direct SDK type imports unless
127
+ * `@theokit/sdk` is promoted from devDependency to dependency. If promoting,
128
+ * ALSO drop the mirrors (Opt A from the architecture-cleanup plan T5.2).
129
+ */
130
+ interface SdkRunLike {
131
+ stream: () => AsyncIterable<{
132
+ type: string;
133
+ }>;
134
+ wait: () => Promise<{
135
+ status: 'finished' | 'error' | 'cancelled';
136
+ error?: {
137
+ message: string;
138
+ };
139
+ }>;
140
+ }
141
+ interface SdkAgent {
142
+ send: (message: string, options?: unknown) => Promise<SdkRunLike>;
143
+ dispose: () => Promise<void>;
144
+ }
145
+ /**
146
+ * Phase 2 — structural duck-type of `@theokit/sdk`'s `ConversationStorageAdapter`.
147
+ *
148
+ * D2 (decoupling): we mirror the SDK's shape locally rather than hard-import
149
+ * the SDK type. This lets consumers pass any object matching the structural
150
+ * contract (own implementation, SDK's `FileSystemConversationStorage`,
151
+ * `InMemoryConversationStorage`, or a Postgres/Redis recipe).
152
+ *
153
+ * EC-5 (SHOULD TEST — sync drift detection): the SDK type MUST be assignable
154
+ * to this interface AND vice-versa. A contract test asserts both directions.
155
+ *
156
+ * `unknown` for the message payload avoids coupling to the SDK's `SDKMessage`
157
+ * shape. Real consumers cast at the call site if they need stricter types.
158
+ */
159
+ interface ConversationStorageLike {
160
+ getMessages(conversationId: string): Promise<readonly unknown[]>;
161
+ appendMessage(conversationId: string, message: unknown): Promise<void>;
162
+ deleteConversation(conversationId: string): Promise<void>;
163
+ listConversationIds?(opts?: {
164
+ limit?: number;
165
+ }): Promise<readonly string[] | undefined>;
166
+ dispose?(): Promise<void>;
167
+ }
168
+ /**
169
+ * Minimum surface of `AgentOptions` accepted by `Agent.getOrCreate`. Forward-
170
+ * compatible: callers pass whatever the SDK supports (memory, tools, etc.).
171
+ *
172
+ * Phase 2 adds typed `conversationStorage` slot. The index signature still
173
+ * passes everything else opaquely.
174
+ */
175
+ interface SdkAgentOptions {
176
+ apiKey?: string;
177
+ model?: {
178
+ id: string;
179
+ };
180
+ tools?: readonly unknown[];
181
+ memory?: Record<string, unknown>;
182
+ /**
183
+ * Phase 2 (Production-Readiness #1) — pluggable conversation persistence.
184
+ * Default (when omitted): SDK falls back to `FileSystemConversationStorage`.
185
+ * Required for serverless / multi-host deploys.
186
+ */
187
+ conversationStorage?: ConversationStorageLike;
188
+ [key: string]: unknown;
189
+ }
190
+ interface ConversationHistoryArgs {
191
+ /** Request — for reading the conversation cookie. */
192
+ request: Request | {
193
+ headers?: {
194
+ cookie?: string;
195
+ } | Headers;
196
+ };
197
+ /**
198
+ * Response-like surface that accepts a `Set-Cookie` header. The primitive
199
+ * appends a Set-Cookie line when issuing a new conversation id. If absent,
200
+ * the primitive still reads the existing cookie but cannot issue a new
201
+ * one — useful for read-only contexts.
202
+ */
203
+ response?: {
204
+ headers: Headers;
205
+ };
206
+ /** Explicit override — wins over session/cookie/uuid (ADR D3 step 1). */
207
+ agentId?: string;
208
+ /** Auth session containing a `conversationId` field — ADR D3 step 2. */
209
+ session?: {
210
+ conversationId?: string;
211
+ } | null;
212
+ /** SDK AgentOptions forwarded to Agent.getOrCreate. apiKey + model required. */
213
+ options: SdkAgentOptions;
214
+ /** Cookie name override. Default: 'theo_conversation'. */
215
+ cookieName?: string;
216
+ /** Cookie max-age in seconds. Default + min: 30 days. Non-positive coerced to default (EC-4). */
217
+ cookieMaxAge?: number;
218
+ }
219
+ interface ConversationHistoryResult {
220
+ /** The SDK Agent, ready to receive `agent.send(message)`. */
221
+ agent: SdkAgent;
222
+ /** The resolved conversation id (useful for logging / debugging). */
223
+ conversationId: string;
224
+ /** True when the id was newly generated (no prior cookie / session). */
225
+ isNew: boolean;
226
+ }
227
+ declare function createConversationHistory(args: ConversationHistoryArgs): Promise<ConversationHistoryResult>;
228
+
229
+ export { AgentErrorEvent, AgentEvent, type AgentRunLike, type AgentRunResult, type AgentRunStreamMessage, type ConversationHistoryArgs, type ConversationHistoryResult, type ConversationStorageLike, type SdkAgent, type SdkAgentOptions, type SdkRunLike, createConversationHistory, errorToEvent, streamAgentRun };
@@ -4,7 +4,8 @@ import {
4
4
  createConversationHistory,
5
5
  errorToEvent,
6
6
  streamAgentRun
7
- } from "../../chunk-RESN62GB.js";
7
+ } from "../../chunk-2KZQPDYR.js";
8
+ import "../../chunk-EXP56GFQ.js";
8
9
  import "../../chunk-DGUM43GV.js";
9
10
  export {
10
11
  __resetSdkForTests,
@@ -0,0 +1,419 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { R as RateLimitStore } from '../../rate-limit-store-BEJnhWdw.js';
3
+
4
+ /**
5
+ * T4.1 — Per-request CSP nonce generation.
6
+ *
7
+ * Returns a 22–24 character base64-encoded string carrying 16 bytes of
8
+ * cryptographic entropy. Used by the SSR pipeline to nonce the inline
9
+ * hydration data script so the default CSP can drop `'unsafe-inline'`
10
+ * from `script-src` in 0.3.0.
11
+ *
12
+ * Runtime portability: prefers Web Crypto (`globalThis.crypto`) because
13
+ * it is available on every target runtime (Node 19+, Bun, Deno, Vercel
14
+ * Edge, Cloudflare Workers). Falls back to `node:crypto` for older Node
15
+ * builds where `globalThis.crypto` is absent.
16
+ *
17
+ * NOT a security primitive in the cryptographic sense — the nonce is a
18
+ * one-shot, single-request value that defeats trivial XSS injection by
19
+ * making the attacker guess a 128-bit string per request. It is NOT
20
+ * suitable for signing or session tokens.
21
+ */
22
+ declare function generateNonce(): string;
23
+
24
+ declare class AuthRequiredError extends Error {
25
+ code: "AUTH_REQUIRED";
26
+ status: number;
27
+ constructor(message?: string);
28
+ }
29
+ declare function requireAuth<T>(session: T | null | undefined): asserts session is T;
30
+
31
+ interface SessionConfig {
32
+ /**
33
+ * Session secret. Either a single string (legacy) or an array of strings
34
+ * where index 0 is the newest. The array form enables dual-key rotation
35
+ * (T3.1 / ADR D5): encrypt always uses `secrets[0]`, decrypt walks the
36
+ * array, transparent re-encrypt on legacy hits (T3.2).
37
+ *
38
+ * EC-1: array length capped at 5 — enforced via throw at construction.
39
+ */
40
+ secret: string | string[];
41
+ cookieName?: string;
42
+ maxAge?: number;
43
+ }
44
+ interface SessionMeta {
45
+ /** Index of the secret that decrypted the session. 0 = newest; > 0 = legacy. */
46
+ secretIndex: number;
47
+ /** True when the decrypt used a legacy secret and the cookie should be re-encrypted. */
48
+ needsReencrypt: boolean;
49
+ }
50
+ interface SessionManager<TSession> {
51
+ getSession(req: IncomingMessage): Promise<TSession | null>;
52
+ /**
53
+ * T3.2 — Variant of `getSession` that surfaces decrypt metadata.
54
+ * Used by `api-middleware.ts` to wire transparent re-encrypt BEFORE
55
+ * the handler runs (so streaming SSR routes don't miss the Set-Cookie
56
+ * window — EC-4).
57
+ */
58
+ getSessionWithMeta(req: IncomingMessage): Promise<{
59
+ data: TSession | null;
60
+ meta: SessionMeta;
61
+ }>;
62
+ createSession(res: ServerResponse, data: TSession): Promise<void>;
63
+ destroySession(res: ServerResponse): void;
64
+ /**
65
+ * T3.3 — OWASP A07:2021 session fixation mitigation. Re-encrypts the
66
+ * current session with a fresh IV and refreshed expiry. No-op when no
67
+ * session is present.
68
+ */
69
+ rotateSession(req: IncomingMessage, res: ServerResponse): Promise<TSession | null>;
70
+ }
71
+ declare function createSessionManager<TSession>(config: SessionConfig): SessionManager<TSession>;
72
+ /**
73
+ * T5a.2 Phase D slice 3/3 — Web-Standards session manager.
74
+ *
75
+ * Mirror of `SessionManager<TSession>` for the Web `Request` + `Headers`
76
+ * shape. Same `SessionConfig`, same encryption (`encrypt`/`decrypt` from
77
+ * `./crypto.js`), same dual-key rotation logic (CR-002 constant-time
78
+ * decrypt fallback walk + transparent re-encrypt), same OWASP A07
79
+ * `rotateSession` invariant.
80
+ *
81
+ * **Signature differences vs `SessionManager`:**
82
+ * - Read methods take `Request` (instead of `IncomingMessage`).
83
+ * - Write methods take a `Headers` instance to mutate (instead of
84
+ * `ServerResponse`) — caller passes the headers they're building
85
+ * for their `Response` constructor.
86
+ *
87
+ * Uses `getCookieFromRequest` / `appendCookieToHeaders` /
88
+ * `appendDeleteCookieToHeaders` from Phase B slice 6/6.
89
+ */
90
+ interface SessionManagerWeb<TSession> {
91
+ getSession(request: Request): Promise<TSession | null>;
92
+ getSessionWithMeta(request: Request): Promise<{
93
+ data: TSession | null;
94
+ meta: SessionMeta;
95
+ }>;
96
+ createSession(target: Headers, data: TSession): Promise<void>;
97
+ destroySession(target: Headers): void;
98
+ rotateSession(request: Request, target: Headers): Promise<TSession | null>;
99
+ }
100
+ declare function createSessionManagerWeb<TSession>(config: SessionConfig): SessionManagerWeb<TSession>;
101
+ /**
102
+ * T5a.2 Phase D slice 3/3 — Web-Standards transparent re-encrypt helper.
103
+ *
104
+ * Mirror of `rotateIfNeeded(sm, req, res)` for the Web Request + Headers
105
+ * shape. Reads session via `getSessionWithMeta`; if the cookie was
106
+ * decrypted with a legacy secret (index > 0), re-issues the cookie with
107
+ * the newest secret via `createSession(target, data)`.
108
+ *
109
+ * **EC-4 honest framing:** unlike the IncomingMessage path where the
110
+ * timing constraint is "before `res.writeHead` fires", the Web Request
111
+ * path's constraint is "before the `Response` object is constructed and
112
+ * returned to the runtime". Caller MUST invoke `rotateIfNeededWeb`
113
+ * BEFORE building the final `Response(body, { headers: target })` so the
114
+ * re-encrypted Set-Cookie lands on the wire.
115
+ */
116
+ declare function rotateIfNeededWeb<TSession>(sm: SessionManagerWeb<TSession>, request: Request, target: Headers): Promise<TSession | null>;
117
+ /**
118
+ * T3.2 — Transparent re-encrypt helper (EC-4 timing-safe wrapper).
119
+ *
120
+ * Call this in your `createContext` (before any rendering / streaming
121
+ * starts). It:
122
+ * 1. Reads the session via `getSessionWithMeta`.
123
+ * 2. If the cookie was decrypted with a legacy secret (index > 0), it
124
+ * immediately re-issues the cookie with `secrets[0]` so the next
125
+ * request lands on the newest key.
126
+ * 3. Returns the session data (or null) for use downstream.
127
+ *
128
+ * EC-4: this MUST run before `renderToPipeableStream` / `res.writeHead`
129
+ * fires. Calling it inside an SSR component or handler body that has
130
+ * already streamed bytes makes the re-encrypt a silent no-op (Set-Cookie
131
+ * is locked once headers commit), trapping users on the legacy secret
132
+ * forever once it's removed from the array.
133
+ */
134
+ declare function rotateIfNeeded<TSession>(sm: SessionManager<TSession>, req: IncomingMessage, res: ServerResponse): Promise<TSession | null>;
135
+ declare function assertProductionSecret(secret: string | string[]): void;
136
+
137
+ declare function encrypt<T>(data: T, secret: string): Promise<string>;
138
+ declare function decrypt<T>(token: string, secret: string): Promise<T | null>;
139
+ /**
140
+ * Reset the derived-key cache. Test helper — never call from production code.
141
+ * Used by Vitest to guarantee isolation between tests that use distinct
142
+ * secrets and want to confirm the derivation path runs.
143
+ */
144
+ declare function _resetKeyCacheForTests(): void;
145
+
146
+ /**
147
+ * T6.2 — RFC 6238 TOTP primitive.
148
+ *
149
+ * Reference: https://datatracker.ietf.org/doc/html/rfc6238
150
+ * HMAC-based one-time password (HOTP) base: https://datatracker.ietf.org/doc/html/rfc4226
151
+ *
152
+ * Pure-function, dependency-free implementation via Web Crypto. Secrets
153
+ * may be passed as Uint8Array (preferred) or base32 string.
154
+ *
155
+ * SECURITY NOTES (documented per EC-13):
156
+ * - TOTP secrets are equivalent to passwords. Encrypt at rest using a
157
+ * separate KMS key from the session secret. If your DB leaks, ALL
158
+ * 2FA codes are compromised — rotate by forcing all users to re-enroll.
159
+ * - Use `verifyTotp` constant-time (we use crypto.timingSafeEqual internally).
160
+ */
161
+ /** Supported HMAC algorithms per RFC 6238 §1.2. Default SHA-1. */
162
+ type TotpAlgorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
163
+ interface TotpOptions {
164
+ /** Shared secret — bytes (preferred) OR base32 string. */
165
+ secret: Uint8Array | string;
166
+ /** Time step in seconds. Default 30 (RFC 6238 §5.2 recommended). */
167
+ step?: number;
168
+ /** Code length: 6, 7, or 8 digits. Default 6. */
169
+ digits?: 6 | 7 | 8;
170
+ /** HMAC algorithm. Default SHA-1 (RFC default). */
171
+ algorithm?: TotpAlgorithm;
172
+ /** Time in ms since epoch. Default Date.now(). */
173
+ time?: number;
174
+ }
175
+ interface VerifyTotpOptions extends TotpOptions {
176
+ /**
177
+ * Drift tolerance in number of steps on each side. Default 1
178
+ * (RFC 6238 §5.2 recommends ±1 step = 90s total window).
179
+ */
180
+ window?: number;
181
+ }
182
+ /** Generate a TOTP code for a given time. */
183
+ declare function generateTotp(opts: TotpOptions): Promise<string>;
184
+ /**
185
+ * Verify a TOTP code against the current window ± `window` steps.
186
+ *
187
+ * Returns `false` for malformed tokens (non-digit, wrong length) without
188
+ * throwing — defensive against accidental user-input passthrough.
189
+ */
190
+ declare function verifyTotp(token: string, opts: VerifyTotpOptions): Promise<boolean>;
191
+ /** Generate a cryptographically random TOTP secret. Default 20 bytes (RFC minimum). */
192
+ declare function generateTotpSecret(bytes?: number): Uint8Array;
193
+ /**
194
+ * Build an `otpauth://` URI for QR code enrollment. Format follows the
195
+ * de facto Google Authenticator spec adopted by every major TOTP app.
196
+ * otpauth://totp/<issuer>:<account>?secret=<base32>&issuer=<issuer>&algorithm=<alg>&digits=<n>&period=<sec>
197
+ */
198
+ interface TotpUriOptions {
199
+ secret: Uint8Array;
200
+ issuer: string;
201
+ account: string;
202
+ algorithm?: TotpAlgorithm;
203
+ digits?: 6 | 7 | 8;
204
+ step?: number;
205
+ }
206
+ declare function totpUri(opts: TotpUriOptions): string;
207
+
208
+ /**
209
+ * T6.3 — Backup codes for 2FA recovery.
210
+ *
211
+ * generateBackupCodes returns N plaintexts (display once to user) + hashes
212
+ * (store in DB). verifyBackupCode constant-time-walks all hashes and
213
+ * returns the matchedHash so caller can delete the used code from storage
214
+ * — replay protection lives at the caller (we can't see their database).
215
+ *
216
+ * SECURITY NOTES:
217
+ * - Codes have ~40 bits entropy (8 chars × 5 bits per char from a
218
+ * 32-char alphabet excluding I/L/O/0/1). Single-use → argon2id
219
+ * overhead is unnecessary; SHA-256 of the normalized code suffices.
220
+ * - The caller MUST atomically delete the matched hash on successful
221
+ * verify. Without that, the code is replayable. We surface
222
+ * matchedHash explicitly so this step is conspicuous.
223
+ * - Pair this with throttleLoginAttempts (T6.1) on the verify endpoint
224
+ * to prevent online brute-force against the 40-bit space.
225
+ */
226
+ interface BackupCodeOptions {
227
+ /** How many codes to generate. Default 10. */
228
+ count?: number;
229
+ /** Length in chars (excluding separator). Default 8. */
230
+ length?: number;
231
+ /** Separator inserted at the midpoint. Default '-'; pass null to omit. */
232
+ separator?: '-' | null;
233
+ /** Custom alphabet. Default excludes ambiguous chars (I, L, O, 0, 1). */
234
+ alphabet?: string;
235
+ }
236
+ interface BackupCode {
237
+ plaintext: string;
238
+ hash: string;
239
+ }
240
+ /**
241
+ * Generate cryptographically random backup codes + their SHA-256 hashes.
242
+ *
243
+ * Plaintext is uppercase alphanumeric (default alphabet excludes ambiguous
244
+ * chars). Separator inserted at the midpoint (default `-`).
245
+ */
246
+ declare function generateBackupCodes(opts?: BackupCodeOptions): Promise<BackupCode[]>;
247
+ /**
248
+ * Verify a code against a list of stored hashes. Walks all hashes
249
+ * constant-time (no early exit). Returns `valid:true` + matchedHash on
250
+ * hit; `valid:false` otherwise.
251
+ *
252
+ * `matchedHash` is the EXACT string the caller stored, ready to be passed
253
+ * to a `DELETE FROM backup_codes WHERE hash = ?` query.
254
+ */
255
+ declare function verifyBackupCode(code: string, hashes: readonly string[]): Promise<{
256
+ valid: boolean;
257
+ matchedHash?: string;
258
+ }>;
259
+
260
+ /**
261
+ * T6.1 — Login throttling primitive.
262
+ *
263
+ * Per-credential failure counter backed by any `RateLimitStore`. After
264
+ * `maxAttempts` failures within `windowMs`, the identifier is locked
265
+ * for `lockoutMs`. Successful login resets the counter.
266
+ *
267
+ * Usage in a login handler:
268
+ *
269
+ * const state = await checkThrottle({ store, identifier: hash(email) })
270
+ * if (!state.allowed) return 429
271
+ * const valid = await verifyPassword(creds)
272
+ * await recordAttempt({ store, identifier: hash(email) }, valid)
273
+ * if (!valid) return 401
274
+ *
275
+ * SECURITY:
276
+ * - NEVER pass raw email/username as `identifier`. Hash or normalize
277
+ * first (e.g. SHA-256 of the lowercased email). This prevents PII
278
+ * from landing in the rate-limit store or audit logs.
279
+ * - Combine with the per-route IP rate limit (T2.2) for layered defense:
280
+ * IP-level limits stop scripted floods; credential-level limits stop
281
+ * distributed brute force.
282
+ */
283
+ interface ThrottleOptions {
284
+ store: RateLimitStore;
285
+ /**
286
+ * Stable identifier per credential. Hash raw emails/usernames before
287
+ * passing in — never leak PII to the rate-limit key space.
288
+ */
289
+ identifier: string;
290
+ /** Failures before lockout. Default 5. */
291
+ maxAttempts?: number;
292
+ /** Sliding window for counting failures. Default 15 minutes. */
293
+ windowMs?: number;
294
+ /** Lockout duration after maxAttempts hit. Default 1 hour. */
295
+ lockoutMs?: number;
296
+ }
297
+ interface ThrottleState {
298
+ allowed: boolean;
299
+ remainingAttempts: number;
300
+ /** Set when `allowed === false`. Absolute timestamp when the lockout expires. */
301
+ lockedUntil?: Date;
302
+ }
303
+ /**
304
+ * Read the current throttle state for an identifier WITHOUT modifying
305
+ * the counter. Call before validating credentials so locked attempts
306
+ * are rejected fast.
307
+ */
308
+ declare function checkThrottle(opts: ThrottleOptions): Promise<ThrottleState>;
309
+ /**
310
+ * Record an attempt outcome. On success → reset the counter. On failure
311
+ * → increment within the sliding window. Returns the new throttle state.
312
+ *
313
+ * Storage model: the store entry's TTL is `lockoutMs` (which doubles as
314
+ * the failure-accumulation window). When `lockoutMs` is short — common
315
+ * in tests or for soft lockouts — the entry auto-expires and the next
316
+ * attempt starts fresh.
317
+ */
318
+ declare function recordAttempt(opts: ThrottleOptions, success: boolean): Promise<ThrottleState>;
319
+
320
+ /**
321
+ * T7.3 — RFC 7636 PKCE primitive.
322
+ *
323
+ * Reference: https://datatracker.ietf.org/doc/html/rfc7636
324
+ *
325
+ * Pure-function, dependency-free implementation using Web Crypto.
326
+ *
327
+ * Usage in an OAuth authorization-code flow:
328
+ *
329
+ * const { codeVerifier, codeChallenge, codeChallengeMethod } = await generatePkceChallenge()
330
+ * // Store codeVerifier in the user's session
331
+ * // Send codeChallenge + codeChallengeMethod in the /authorize redirect
332
+ * // ... user authenticates with provider ...
333
+ * // On /callback, send codeVerifier with the code → token exchange
334
+ */
335
+ interface PkceChallenge {
336
+ codeVerifier: string;
337
+ codeChallenge: string;
338
+ codeChallengeMethod: 'S256';
339
+ }
340
+ /**
341
+ * Compute the code_challenge from a code_verifier per RFC 7636 §4.2.
342
+ * Exported separately to enable RFC 7636 Appendix B vector testing.
343
+ */
344
+ declare function pkceChallengeFromVerifier(verifier: string): Promise<string>;
345
+ /**
346
+ * Generate a fresh PKCE challenge pair.
347
+ *
348
+ * Verifier: 32 cryptographically random bytes → 43-char base64url string.
349
+ * RFC 7636 §4.1 allows 43–128 chars; we ship the minimum-safe default.
350
+ *
351
+ * Method: 'S256' only — no 'plain' fallback. RFC 7636 §7.2 strongly
352
+ * discourages plain and modern providers reject it.
353
+ */
354
+ declare function generatePkceChallenge(): Promise<PkceChallenge>;
355
+
356
+ /**
357
+ * T7.4 — OAuth `state` parameter helpers (RFC 6749 §10.12).
358
+ *
359
+ * `state` is a cryptographically random anti-CSRF token. The client
360
+ * stores it (in session/cookie/db) before redirecting to the
361
+ * authorization endpoint and verifies it on the callback. Without it,
362
+ * an attacker can authorize on the user's account using a captured code.
363
+ */
364
+ /**
365
+ * Generate a cryptographically random state token. Default 32 bytes
366
+ * (~43 base64url chars). Increase for higher entropy if your provider
367
+ * supports it.
368
+ */
369
+ declare function generateOAuthState(opts?: {
370
+ bytes?: number;
371
+ }): string;
372
+ /**
373
+ * Verify a state token. Constant-time compare. Empty inputs always fail
374
+ * (defensive — never accept empty as "they match").
375
+ *
376
+ * CR-012 fix: `constantTimeEquals` does not early-exit on length mismatch,
377
+ * so the comparison time is independent of how the input was forged.
378
+ */
379
+ declare function verifyOAuthState(provided: string, stored: string): boolean;
380
+
381
+ /**
382
+ * T7.4 — OpenID Connect Discovery 1.0 metadata fetcher.
383
+ *
384
+ * Reference: https://openid.net/specs/openid-connect-discovery-1_0.html
385
+ *
386
+ * Caches metadata in module scope. Cache key is the exact issuer string
387
+ * (trailing-slash sensitive per RFC 8414 §3). Failures are NOT cached —
388
+ * subsequent calls retry the fetch.
389
+ *
390
+ * EC-7 — HTTPS enforced. RFC 8414 §3 requires HTTPS for OIDC issuers.
391
+ * Localhost (loopback) is allowed for development.
392
+ */
393
+ interface OidcMetadata {
394
+ issuer: string;
395
+ authorization_endpoint: string;
396
+ token_endpoint: string;
397
+ jwks_uri?: string;
398
+ userinfo_endpoint?: string;
399
+ end_session_endpoint?: string;
400
+ scopes_supported?: string[];
401
+ response_types_supported?: string[];
402
+ grant_types_supported?: string[];
403
+ id_token_signing_alg_values_supported?: string[];
404
+ [key: string]: unknown;
405
+ }
406
+ /** Clear the discovery cache. Used by tests. */
407
+ declare function clearOidcCache(): void;
408
+ /**
409
+ * Fetch (or return cached) OIDC provider metadata.
410
+ *
411
+ * Throws when:
412
+ * - Issuer URL is malformed
413
+ * - Issuer is HTTP and not a loopback host (EC-7)
414
+ * - HTTP response is non-OK
415
+ * - Metadata lacks `authorization_endpoint` (sanity check)
416
+ */
417
+ declare function discoverOidcProvider(issuer: string | URL): Promise<OidcMetadata>;
418
+
419
+ export { AuthRequiredError, type BackupCode, type BackupCodeOptions, type OidcMetadata, type PkceChallenge, type SessionConfig, type SessionManager, type SessionManagerWeb, type SessionMeta, type ThrottleOptions, type ThrottleState, type TotpAlgorithm, type TotpOptions, type TotpUriOptions, type VerifyTotpOptions, _resetKeyCacheForTests, assertProductionSecret, checkThrottle, clearOidcCache, createSessionManager, createSessionManagerWeb, decrypt, discoverOidcProvider, encrypt, generateBackupCodes, generateNonce, generateOAuthState, generatePkceChallenge, generateTotp, generateTotpSecret, pkceChallengeFromVerifier, recordAttempt, requireAuth, rotateIfNeeded, rotateIfNeededWeb, totpUri, verifyBackupCode, verifyOAuthState, verifyTotp };