ai-inference-stepper 1.0.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 (160) hide show
  1. package/.env.example +169 -0
  2. package/.eslintrc.cjs +23 -0
  3. package/.github/workflows/ci.yml +51 -0
  4. package/.github/workflows/keep-alive.yml +22 -0
  5. package/.github/workflows/publish.yml +34 -0
  6. package/ARCHITECTURE.md +594 -0
  7. package/Dockerfile +16 -0
  8. package/LICENSE +28 -0
  9. package/README.md +261 -0
  10. package/dist/alerts/discord.d.ts +19 -0
  11. package/dist/alerts/discord.d.ts.map +1 -0
  12. package/dist/alerts/discord.js +70 -0
  13. package/dist/alerts/discord.js.map +1 -0
  14. package/dist/cache/redisCache.d.ts +45 -0
  15. package/dist/cache/redisCache.d.ts.map +1 -0
  16. package/dist/cache/redisCache.js +171 -0
  17. package/dist/cache/redisCache.js.map +1 -0
  18. package/dist/cli.d.ts +3 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +8 -0
  21. package/dist/cli.js.map +1 -0
  22. package/dist/config.d.ts +6 -0
  23. package/dist/config.d.ts.map +1 -0
  24. package/dist/config.js +251 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/fallback/templateFallback.d.ts +7 -0
  27. package/dist/fallback/templateFallback.d.ts.map +1 -0
  28. package/dist/fallback/templateFallback.js +29 -0
  29. package/dist/fallback/templateFallback.js.map +1 -0
  30. package/dist/index.d.ts +121 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +198 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/logging.d.ts +10 -0
  35. package/dist/logging.d.ts.map +1 -0
  36. package/dist/logging.js +44 -0
  37. package/dist/logging.js.map +1 -0
  38. package/dist/metrics/metrics.d.ts +22 -0
  39. package/dist/metrics/metrics.d.ts.map +1 -0
  40. package/dist/metrics/metrics.js +78 -0
  41. package/dist/metrics/metrics.js.map +1 -0
  42. package/dist/providers/factory.d.ts +11 -0
  43. package/dist/providers/factory.d.ts.map +1 -0
  44. package/dist/providers/factory.js +52 -0
  45. package/dist/providers/factory.js.map +1 -0
  46. package/dist/providers/hfSpace.adapter.d.ts +21 -0
  47. package/dist/providers/hfSpace.adapter.d.ts.map +1 -0
  48. package/dist/providers/hfSpace.adapter.js +110 -0
  49. package/dist/providers/hfSpace.adapter.js.map +1 -0
  50. package/dist/providers/httpTemplate.adapter.d.ts +42 -0
  51. package/dist/providers/httpTemplate.adapter.d.ts.map +1 -0
  52. package/dist/providers/httpTemplate.adapter.js +98 -0
  53. package/dist/providers/httpTemplate.adapter.js.map +1 -0
  54. package/dist/providers/promptBuilder.d.ts +34 -0
  55. package/dist/providers/promptBuilder.d.ts.map +1 -0
  56. package/dist/providers/promptBuilder.js +315 -0
  57. package/dist/providers/promptBuilder.js.map +1 -0
  58. package/dist/providers/provider.interface.d.ts +45 -0
  59. package/dist/providers/provider.interface.d.ts.map +1 -0
  60. package/dist/providers/provider.interface.js +47 -0
  61. package/dist/providers/provider.interface.js.map +1 -0
  62. package/dist/providers/specs.d.ts +18 -0
  63. package/dist/providers/specs.d.ts.map +1 -0
  64. package/dist/providers/specs.js +326 -0
  65. package/dist/providers/specs.js.map +1 -0
  66. package/dist/providers/unified.adapter.d.ts +37 -0
  67. package/dist/providers/unified.adapter.d.ts.map +1 -0
  68. package/dist/providers/unified.adapter.js +141 -0
  69. package/dist/providers/unified.adapter.js.map +1 -0
  70. package/dist/queue/producer.d.ts +30 -0
  71. package/dist/queue/producer.d.ts.map +1 -0
  72. package/dist/queue/producer.js +87 -0
  73. package/dist/queue/producer.js.map +1 -0
  74. package/dist/queue/worker.d.ts +9 -0
  75. package/dist/queue/worker.d.ts.map +1 -0
  76. package/dist/queue/worker.js +137 -0
  77. package/dist/queue/worker.js.map +1 -0
  78. package/dist/server/app.d.ts +4 -0
  79. package/dist/server/app.d.ts.map +1 -0
  80. package/dist/server/app.js +394 -0
  81. package/dist/server/app.js.map +1 -0
  82. package/dist/server/start.d.ts +16 -0
  83. package/dist/server/start.d.ts.map +1 -0
  84. package/dist/server/start.js +45 -0
  85. package/dist/server/start.js.map +1 -0
  86. package/dist/stepper/orchestrator.d.ts +22 -0
  87. package/dist/stepper/orchestrator.d.ts.map +1 -0
  88. package/dist/stepper/orchestrator.js +333 -0
  89. package/dist/stepper/orchestrator.js.map +1 -0
  90. package/dist/types.d.ts +216 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +14 -0
  93. package/dist/types.js.map +1 -0
  94. package/dist/utils/redaction.d.ts +9 -0
  95. package/dist/utils/redaction.d.ts.map +1 -0
  96. package/dist/utils/redaction.js +41 -0
  97. package/dist/utils/redaction.js.map +1 -0
  98. package/dist/utils/safeRequest.d.ts +38 -0
  99. package/dist/utils/safeRequest.d.ts.map +1 -0
  100. package/dist/utils/safeRequest.js +104 -0
  101. package/dist/utils/safeRequest.js.map +1 -0
  102. package/dist/validation/report.schema.d.ts +48 -0
  103. package/dist/validation/report.schema.d.ts.map +1 -0
  104. package/dist/validation/report.schema.js +72 -0
  105. package/dist/validation/report.schema.js.map +1 -0
  106. package/dist/webhooks/delivery.d.ts +31 -0
  107. package/dist/webhooks/delivery.d.ts.map +1 -0
  108. package/dist/webhooks/delivery.js +102 -0
  109. package/dist/webhooks/delivery.js.map +1 -0
  110. package/docs/assets/architecture.png +0 -0
  111. package/package.json +75 -0
  112. package/render.yaml +25 -0
  113. package/src/alerts/README.md +25 -0
  114. package/src/alerts/discord.ts +86 -0
  115. package/src/cache/How redis caching works in package stepper.md +971 -0
  116. package/src/cache/README.md +51 -0
  117. package/src/cache/redisCache.ts +194 -0
  118. package/src/ci/deploy.sh +36 -0
  119. package/src/cli.ts +9 -0
  120. package/src/config.ts +265 -0
  121. package/src/fallback/templateFallback.ts +32 -0
  122. package/src/index.ts +246 -0
  123. package/src/logging.ts +46 -0
  124. package/src/metrics/README.md +24 -0
  125. package/src/metrics/metrics.ts +84 -0
  126. package/src/providers/How the providers interact.md +121 -0
  127. package/src/providers/README.md +121 -0
  128. package/src/providers/factory.ts +57 -0
  129. package/src/providers/hfSpace.adapter.ts +119 -0
  130. package/src/providers/httpTemplate.adapter.ts +138 -0
  131. package/src/providers/promptBuilder.ts +330 -0
  132. package/src/providers/provider.interface.ts +73 -0
  133. package/src/providers/specs.ts +366 -0
  134. package/src/providers/unified.adapter.ts +172 -0
  135. package/src/queue/How queue works in package stepper.md +149 -0
  136. package/src/queue/README.md +41 -0
  137. package/src/queue/producer.ts +108 -0
  138. package/src/queue/worker.ts +170 -0
  139. package/src/server/app.ts +451 -0
  140. package/src/server/start.ts +68 -0
  141. package/src/stepper/Dockerfile +48 -0
  142. package/src/stepper/How orchestrator works in package stepper.md +746 -0
  143. package/src/stepper/README.md +43 -0
  144. package/src/stepper/orchestrator.ts +437 -0
  145. package/src/types.ts +238 -0
  146. package/src/utils/redaction.ts +50 -0
  147. package/src/utils/safeRequest.ts +140 -0
  148. package/src/validation/README.md +25 -0
  149. package/src/validation/report.schema.ts +96 -0
  150. package/src/webhooks/delivery.ts +162 -0
  151. package/tests/integration/full-flow.test.ts +192 -0
  152. package/tests/unit/alerts/discord.test.ts +119 -0
  153. package/tests/unit/cache.test.ts +87 -0
  154. package/tests/unit/orchestrator-fallback.test.ts +92 -0
  155. package/tests/unit/orchestrator.test.ts +105 -0
  156. package/tests/unit/providers/factory.test.ts +161 -0
  157. package/tests/unit/providers/unified.adapter.test.ts +206 -0
  158. package/tests/unit/utils/redaction.test.ts +140 -0
  159. package/tests/unit/utils/safeRequest.test.ts +164 -0
  160. package/tsconfig.json +26 -0
package/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # Stepper: Production-Grade AI Inference Orchestrator
2
+
3
+ [![CI Status](https://github.com/samuel-adedigba/ai-inference-stepper/actions/workflows/ci.yml/badge.svg)](https://github.com/samuel-adedigba/ai-inference-stepper/actions)
4
+ [![License: Custom](https://img.shields.io/badge/License-Attribution%20Required-blue.svg)](#license)
5
+ [![DeepMind Tech](https://img.shields.io/badge/Built%20With-TypeScript%20%26%20Node.js-informational)](https://www.typescriptlang.org/)
6
+
7
+ **Stepper** is a resilient, multi-provider AI inference engine designed for high-load production environments. It handles provider fallbacks, intelligent caching, job queuing, and circuit breaking out of the box.
8
+
9
+ - Back to root: [../../README.md](../../README.md)
10
+ - CommitDiary packages: [../api/README.md](../api/README.md) • [../web-dashboard/README.md](../web-dashboard/README.md) • [../extension/README.md](../extension/README.md) • [../core/README.md](../core/README.md)
11
+
12
+ ## ✅ Standalone Setup (Local)
13
+
14
+ Stepper is open source and can run independently or inside this monorepo.
15
+
16
+ ### Prerequisites
17
+
18
+ - Node.js 18+
19
+ - pnpm
20
+ - Redis (required)
21
+
22
+ ### Install
23
+
24
+ ```bash
25
+ cd packages/stepper
26
+ pnpm install
27
+ ```
28
+
29
+ ### Configure
30
+
31
+ ```bash
32
+ cp .env.example .env
33
+ ```
34
+
35
+ Add at least one provider key and Redis config in .env.
36
+
37
+ ### Run
38
+
39
+ ```bash
40
+ docker run -d -p 6379:6379 redis:alpine
41
+ pnpm dev
42
+ ```
43
+
44
+ ### ✅ Why This Setup Works
45
+
46
+ - Redis backs cache and queue state for resilient processing
47
+ - Provider adapters allow fallback across multiple AI vendors
48
+ - Callbacks let CommitDiary save reports and notify users reliably
49
+
50
+ ---
51
+
52
+ ## 🏗️ Architecture First
53
+
54
+ Understanding how Stepper handles your requests is key to using its full power.
55
+
56
+ ![Stepper Architecture](./docs/assets/architecture.png)
57
+
58
+ ### The Core Flow
59
+
60
+ 1. **Request Capture**: Received via HTTP or internal Library Call.
61
+ 2. **Smart Caching**: Checks Redis. Supports **Stale-While-Revalidate** (returns stale data while refreshing in background).
62
+ 3. **Job Queueing**: If not cached, the request is enqueued via **BullMQ** to prevent overloading providers.
63
+ 4. **Resilient Orchestration**:
64
+ - **Priority Fallback**: Tries Gemini → Cohere → HF Space in sequence.
65
+ - **Circuit Breakers**: Stops calling failing providers to allow them to recover.
66
+ - **Rate Limiting**: Per-provider bottlenecking to respect API quotas.
67
+ 5. **Finalize**: Result is cached, and `onSuccess` callbacks are triggered.
68
+
69
+ > [!TIP]
70
+ > For a deep dive into the system design, see [ARCHITECTURE.md](./ARCHITECTURE.md).
71
+
72
+ ---
73
+
74
+ ## 🔗 CommitDiary Integration Flow
75
+
76
+ ```mermaid
77
+ flowchart LR
78
+ A[API Server] --> B[Stepper enqueueReport]
79
+ B --> C[Queue + Providers]
80
+ C --> D[Callback to API]
81
+ D --> E[Report saved + webhooks]
82
+ ```
83
+
84
+ ### How CommitDiary Uses Stepper
85
+
86
+ - API calls Stepper to generate commit reports
87
+ - Stepper returns a jobId or cached result
88
+ - Stepper posts back to API callbacks for delivery and persistence
89
+
90
+ See API docs for endpoints and callbacks: [../api/README.md](../api/README.md)
91
+
92
+ ---
93
+
94
+ ## 🧩 Component Deep Dives
95
+
96
+ Stepper is modular. Explore each subsystem's technical documentation:
97
+
98
+ | Component | Purpose | Technical Details |
99
+ | :---------------- | :------------------------------- | :------------------------------------------ |
100
+ | **⚡ Cache** | Intelligent Redis strategies | [Cache Guide](./src/cache/README.md) |
101
+ | **🤖 Providers** | Adapter logic for different LLMs | [Provider Specs](./src/providers/README.md) |
102
+ | **📥 Queue** | Background processing & retries | [Queue System](./src/queue/README.md) |
103
+ | **📊 Metrics** | Prometheus & Observability | [Metrics Docs](./src/metrics/README.md) |
104
+ | **🛡️ Alerts** | Discord & error notifications | [Alerts System](./src/alerts/README.md) |
105
+ | **✅ Validation** | Zod-based strict output parsing | [Validation](./src/validation/README.md) |
106
+
107
+ ### 🌟 Provider-Specific Optimizations
108
+
109
+ #### Google Gemini (Gemini 3 Models)
110
+
111
+ Stepper includes specialized optimizations for Google's Gemini 3 models based on [official Google prompting strategies](https://ai.google.dev/gemini-api/docs/prompting-strategies).
112
+
113
+ **Why Gemini is Different:**
114
+ - **XML-Structured Prompts**: Uses `<role>`, `<instructions>`, `<context>`, `<task>` tags for better model understanding
115
+ - **Query Parameter Authentication**: API key passed in URL (`?key=YOUR_KEY`) instead of headers
116
+ - **Locked Temperature**: Must use `temperature: 1.0` (Google requirement for optimal Gemini 3 performance)
117
+ - **Increased Token Limit**: 4096 tokens for detailed analysis
118
+
119
+ **Conditional Implementation:**
120
+ ```typescript
121
+ if (provider === 'gemini') {
122
+ // Use XML-structured prompt
123
+ prompt = buildGeminiPrompt(input);
124
+ // Append API key to URL
125
+ endpoint = `${endpoint}?key=${apiKey}`;
126
+ }
127
+ ```
128
+
129
+ This pattern allows each provider to have unique optimizations while maintaining clean code separation. See [Provider Documentation](./src/providers/README.md#-provider-specific-implementations) for details.
130
+
131
+ ---
132
+
133
+ ## ⚡ Quick Start (3 Minutes)
134
+
135
+ ### 1. Install Dependencies
136
+
137
+ ```bash
138
+ pnpm install
139
+ ```
140
+
141
+ ### 2. Configure Environment
142
+
143
+ Copy the example and add your API keys:
144
+
145
+ ```bash
146
+ cp .env.example .env
147
+ ```
148
+
149
+ ### 3. Spin Up Redis & Stepper
150
+
151
+ ```bash
152
+ # Start Redis (Required)
153
+ docker run -d -p 6379:6379 redis:alpine
154
+
155
+ # Start in Dev Mode
156
+ pnpm dev
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 🧭 Monorepo Notes
162
+
163
+ - API expects Stepper at STEPPER_URL (default http://localhost:3005)
164
+ - If running inside the monorepo, keep API and Stepper dev servers up
165
+ - See root setup guide: [../../README.md](../../README.md)
166
+
167
+ ---
168
+
169
+ ## 🛠️ Usage Modes
170
+
171
+ ### Mode A: As a Library (Direct Integration)
172
+
173
+ Best for monorepos or when you want to avoid network overhead.
174
+
175
+ ```typescript
176
+ import { enqueueReport, registerCallbacks, initStepper } from "ai-inference-stepper";
177
+
178
+ // Optional: programmatic config overrides (no env file required)
179
+ initStepper({
180
+ config: {
181
+ redis: { url: "redis://localhost:6379" },
182
+ },
183
+ providers: [
184
+ { name: "gemini", enabled: true, apiKey: process.env.GEMINI_API_KEY, baseUrl: "https://generativelanguage.googleapis.com/v1", modelName: "gemini-pro", concurrency: 2, rateLimitRPM: 5 }
185
+ ]
186
+ });
187
+
188
+ // 1. Setup notification logic
189
+ registerCallbacks({
190
+ onSuccess: (id, provider, data) => console.log(`✅ Success via ${provider}`),
191
+ onFailure: (id, errors) => console.error("❌ Failed:", errors),
192
+ });
193
+
194
+ // 2. Trigger a request (returns immediately if queued or cached)
195
+ const result = await enqueueReport({
196
+ commitSha: "abc123",
197
+ message: "Fix bug",
198
+ // ...other input
199
+ });
200
+ ```
201
+
202
+ ### Mode B: As an HTTP Service
203
+
204
+ Best for microservices or remote deployments (Render/Railway).
205
+
206
+ ```bash
207
+ # Send a report generation request
208
+ curl -X POST http://localhost:3001/v1/reports \
209
+ -H "Content-Type: application/json" \
210
+ -d '{ "message": "Refactor API", "files": ["src/app.ts"] }'
211
+
212
+ #### CLI (npm)
213
+
214
+ ```bash
215
+ # One-off run
216
+ npx ai-inference-stepper
217
+
218
+ # Or install and run
219
+ npm i -g ai-inference-stepper
220
+ ai-inference-stepper
221
+ ```
222
+
223
+ #### Environment Setup (Service Mode)
224
+
225
+ Stepper reads config from environment variables. Use .env for local runs:
226
+
227
+ ```bash
228
+ cp .env.example .env
229
+ ```
230
+
231
+ If you install Stepper as a library, you can either:
232
+ - Provide env variables in your host app process (recommended for deployments), or
233
+ - Call `initStepper({ config, providers })` programmatically to override defaults.
234
+
235
+ # Response gives you a JobID to poll
236
+ # { "status": "queued", "jobId": "...", "statusUrl": "..." }
237
+ ```
238
+
239
+ ---
240
+
241
+ ## 🤝 Contributing & Community
242
+
243
+ We love contributors! Whether it's a bug report or a new provider adapter:
244
+
245
+ - **Issues**: Found a bug? [Raise an issue](https://github.com/samuel-adedigba/ai-inference-stepper/issues).
246
+ - **Pull Requests**: Have a fix? [Open a PR](https://github.com/samuel-adedigba/ai-inference-stepper/pulls).
247
+
248
+ If contributing inside the CommitDiary monorepo, start at [../../README.md](../../README.md) for the full workflow.
249
+
250
+ ---
251
+
252
+ ## 📜 License
253
+
254
+ **Custom Attribution License**
255
+
256
+ You are free to use, modify, and distribute this software for personal or commercial projects, provided that:
257
+
258
+ 1. **Credit is given**: You must attribute the original work to **Samuel Adedigba (@samuel-adedigba)**.
259
+ 2. **Pull Requests**: Contributions and improvements are encouraged back to this core repository.
260
+
261
+ _For full details, see the [LICENSE](./LICENSE) file (MIT-based with attribution)._
@@ -0,0 +1,19 @@
1
+ export interface DiscordAlert {
2
+ title: string;
3
+ message: string;
4
+ severity: 'info' | 'warning' | 'critical';
5
+ metadata?: Record<string, unknown>;
6
+ }
7
+ /**
8
+ * Send alert to Discord webhook
9
+ */
10
+ export declare function sendDiscordAlert(alert: DiscordAlert): Promise<void>;
11
+ /**
12
+ * Send provider failure alert
13
+ */
14
+ export declare function alertProviderFailure(provider: string, errorCount: number, error?: unknown): Promise<void>;
15
+ /**
16
+ * Send circuit breaker alert
17
+ */
18
+ export declare function alertCircuitOpen(provider: string, lastError?: unknown): Promise<void>;
19
+ //# sourceMappingURL=discord.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord.d.ts","sourceRoot":"","sources":["../../src/alerts/discord.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAwCzE;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/G;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAU3F"}
@@ -0,0 +1,70 @@
1
+ // `packages/stepper/src/alerts/discord.ts
2
+ import { logger } from '../logging.js';
3
+ const DISCORD_WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL;
4
+ /**
5
+ * Send alert to Discord webhook
6
+ */
7
+ export async function sendDiscordAlert(alert) {
8
+ if (!DISCORD_WEBHOOK_URL) {
9
+ logger.debug('Discord webhook URL not configured, skipping alert');
10
+ return;
11
+ }
12
+ const emoji = alert.severity === 'critical' ? '🚨' : alert.severity === 'warning' ? '⚠️' : 'ℹ️';
13
+ const content = `${emoji} **${alert.title}**\n${alert.message}`;
14
+ const embed = alert.metadata
15
+ ? {
16
+ embeds: [
17
+ {
18
+ title: alert.title,
19
+ description: alert.message,
20
+ color: alert.severity === 'critical' ? 0xff0000 : alert.severity === 'warning' ? 0xffa500 : 0x00ff00,
21
+ fields: Object.entries(alert.metadata).map(([key, value]) => ({
22
+ name: key,
23
+ value: String(value),
24
+ inline: true,
25
+ })),
26
+ timestamp: new Date().toISOString(),
27
+ },
28
+ ],
29
+ }
30
+ : { content };
31
+ try {
32
+ const response = await fetch(DISCORD_WEBHOOK_URL, {
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json' },
35
+ body: JSON.stringify(embed),
36
+ });
37
+ if (!response.ok) {
38
+ logger.warn({ status: response.status }, 'Discord webhook request failed');
39
+ }
40
+ }
41
+ catch (error) {
42
+ logger.error({ error }, 'Failed to send Discord alert');
43
+ }
44
+ }
45
+ /**
46
+ * Send provider failure alert
47
+ */
48
+ export async function alertProviderFailure(provider, errorCount, error) {
49
+ const errorDetail = error instanceof Error ? error.message : String(error || 'Unknown error');
50
+ await sendDiscordAlert({
51
+ title: 'AI Provider Failure',
52
+ message: `Provider **${provider}** has failed ${errorCount} times\n\n**Error Details:**\n\`${errorDetail}\``,
53
+ severity: errorCount >= 5 ? 'critical' : 'warning',
54
+ metadata: { provider, errorCount, error: errorDetail, timestamp: new Date().toISOString() },
55
+ });
56
+ }
57
+ /**
58
+ * Send circuit breaker alert
59
+ */
60
+ export async function alertCircuitOpen(provider, lastError) {
61
+ const errorDetail = lastError ? (lastError instanceof Error ? lastError.message : String(lastError)) : undefined;
62
+ const message = `Circuit breaker for provider **${provider}** is now OPEN${errorDetail ? `\n\n**Last Error:**\n\`${errorDetail}\`` : ''}`;
63
+ await sendDiscordAlert({
64
+ title: 'Circuit Breaker Opened',
65
+ message,
66
+ severity: 'critical',
67
+ metadata: { provider, lastError: errorDetail, timestamp: new Date().toISOString() },
68
+ });
69
+ }
70
+ //# sourceMappingURL=discord.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord.js","sourceRoot":"","sources":["../../src/alerts/discord.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import Redis from 'ioredis';
2
+ import { CacheEntry, ReportOutput, ProviderAttemptMeta } from '../types.js';
3
+ /**
4
+ * Get or create Redis client
5
+ */
6
+ export declare function getRedisClient(): Redis;
7
+ /**
8
+ * Build cache key for a report
9
+ */
10
+ export declare function buildCacheKey(userId: string, commitSha: string, templateHash?: string): string;
11
+ /**
12
+ * Get cache entry: Looks up a report in the cache using its key. Like asking: "Do we already have a copy of this report?"
13
+ */
14
+ export declare function getReportCache(key: string): Promise<CacheEntry | null>;
15
+ /**
16
+ * Set dehydrated cache entry (job enqueued)
17
+ */
18
+ export declare function setDehydrated(key: string, jobId: string): Promise<void>;
19
+ /**
20
+ * Set hydrated cache entry (report generated)
21
+ * Stores a completed report in the cache. This is the "meal is ready!" moment.
22
+ */
23
+ export declare function setHydrated(key: string, result: ReportOutput, providersAttempted: ProviderAttemptMeta[], fallback?: boolean, ttl?: number): Promise<void>;
24
+ /**
25
+ * Mark cache entry as failed: Records that report generation failed completely. All AI providers were tried and none worked.
26
+ */
27
+ export declare function markFailed(key: string, errorMessage: string, providersAttempted: ProviderAttemptMeta[]): Promise<void>;
28
+ /**
29
+ * Check if hydrated entry is fresh
30
+ */
31
+ export declare function isHydratedFresh(entry: CacheEntry): boolean;
32
+ /**
33
+ * Check if entry is stale but usable for stale-while-revalidate: Checks if a report is old but still usable while a new one is being generated in the background.
34
+ */
35
+ export declare function isStaleButUsable(entry: CacheEntry): boolean;
36
+ /**
37
+ * Delete cache entry: Removes the record from Redis immediately.
38
+ * Call this once the backend has successfully saved the report to its database.
39
+ */
40
+ export declare function deleteCacheEntry(key: string): Promise<void>;
41
+ /**
42
+ * Close Redis connection (for graceful shutdown)
43
+ */
44
+ export declare function closeRedis(): Promise<void>;
45
+ //# sourceMappingURL=redisCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisCache.d.ts","sourceRoot":"","sources":["../../src/cache/redisCache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAO5E;;GAEG;AACH,wBAAgB,cAAc,IAAI,KAAK,CAwBtC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAkB,GAAG,MAAM,CAEzG;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAa5E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB7E;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC7B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,EACpB,kBAAkB,EAAE,mBAAmB,EAAE,EACzC,QAAQ,GAAE,OAAe,EACzB,GAAG,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5H;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAQ1D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAK3D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASjE;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAMhD"}
@@ -0,0 +1,171 @@
1
+ // packages/stepper/src/cache/redisCache.ts
2
+ import Redis from 'ioredis';
3
+ import { config } from '../config.js';
4
+ import { logger } from '../logging.js';
5
+ import { sendDiscordAlert } from '../alerts/discord.js';
6
+ let redisClient = null;
7
+ /**
8
+ * Get or create Redis client
9
+ */
10
+ export function getRedisClient() {
11
+ if (!redisClient) {
12
+ redisClient = new Redis(config.redis.url, {
13
+ maxRetriesPerRequest: null, // Required by BullMQ for blocking operations
14
+ enableReadyCheck: true,
15
+ lazyConnect: false,
16
+ });
17
+ redisClient.on('error', (err) => {
18
+ logger.error({ err }, 'Redis client error');
19
+ void sendDiscordAlert({
20
+ title: 'Redis Connection Error',
21
+ message: `Redis client encountered an error: ${err.message}`,
22
+ severity: 'critical',
23
+ metadata: { error: err.message, timestamp: new Date().toISOString() }
24
+ });
25
+ });
26
+ redisClient.on('connect', () => {
27
+ logger.info('Redis client connected');
28
+ });
29
+ }
30
+ return redisClient;
31
+ }
32
+ /**
33
+ * Build cache key for a report
34
+ */
35
+ export function buildCacheKey(userId, commitSha, templateHash = 'default') {
36
+ return `${config.redis.keyPrefix}report:${userId}:${commitSha}:${templateHash}`;
37
+ }
38
+ /**
39
+ * Get cache entry: Looks up a report in the cache using its key. Like asking: "Do we already have a copy of this report?"
40
+ */
41
+ export async function getReportCache(key) {
42
+ const redis = getRedisClient();
43
+ try {
44
+ const data = await redis.get(key);
45
+ if (!data)
46
+ return null;
47
+ const entry = JSON.parse(data);
48
+ return entry;
49
+ }
50
+ catch (error) {
51
+ logger.error({ error, key }, 'Failed to get cache entry');
52
+ return null;
53
+ }
54
+ }
55
+ /**
56
+ * Set dehydrated cache entry (job enqueued)
57
+ */
58
+ export async function setDehydrated(key, jobId) {
59
+ const redis = getRedisClient();
60
+ const entry = {
61
+ status: 'dehydrated', //Mark it as "in progress"
62
+ jobId,
63
+ timestamps: {
64
+ created: new Date().toISOString(),
65
+ updated: new Date().toISOString(),
66
+ },
67
+ };
68
+ try {
69
+ await redis.setex(key, config.cache.ttlSeconds, JSON.stringify(entry)); //Store in Redis with expiration time Default is 604,800 seconds = 7 days
70
+ logger.debug({ key, jobId }, 'Created dehydrated cache entry');
71
+ }
72
+ catch (error) {
73
+ logger.error({ error, key }, 'Failed to set dehydrated cache');
74
+ throw error;
75
+ }
76
+ }
77
+ /**
78
+ * Set hydrated cache entry (report generated)
79
+ * Stores a completed report in the cache. This is the "meal is ready!" moment.
80
+ */
81
+ export async function setHydrated(key, result, providersAttempted, fallback = false, ttl // How long to keep it 604800 (7 days in seconds)
82
+ ) {
83
+ const redis = getRedisClient();
84
+ const entry = {
85
+ status: 'hydrated', // Report is complete
86
+ result,
87
+ providersAttempted,
88
+ fallback,
89
+ timestamps: {
90
+ created: new Date().toISOString(),
91
+ updated: new Date().toISOString(),
92
+ },
93
+ ttl: ttl || config.cache.ttlSeconds,
94
+ };
95
+ try {
96
+ await redis.setex(key, ttl || config.cache.ttlSeconds, JSON.stringify(entry));
97
+ logger.debug({ key, fallback }, 'Stored hydrated cache entry');
98
+ }
99
+ catch (error) {
100
+ logger.error({ error, key }, 'Failed to set hydrated cache');
101
+ throw error;
102
+ }
103
+ }
104
+ /**
105
+ * Mark cache entry as failed: Records that report generation failed completely. All AI providers were tried and none worked.
106
+ */
107
+ export async function markFailed(key, errorMessage, providersAttempted) {
108
+ const redis = getRedisClient();
109
+ const entry = {
110
+ status: 'failed',
111
+ error: errorMessage,
112
+ providersAttempted,
113
+ timestamps: {
114
+ created: new Date().toISOString(),
115
+ updated: new Date().toISOString(),
116
+ },
117
+ };
118
+ try {
119
+ await redis.setex(key, 3600, JSON.stringify(entry)); // Keep failed for 1 hour
120
+ logger.debug({ key }, 'Marked cache entry as failed');
121
+ }
122
+ catch (error) {
123
+ logger.error({ error, key }, 'Failed to mark cache as failed');
124
+ }
125
+ }
126
+ /**
127
+ * Check if hydrated entry is fresh
128
+ */
129
+ export function isHydratedFresh(entry) {
130
+ if (entry.status !== 'hydrated')
131
+ return false; //Is this a complete report?
132
+ const updatedAt = new Date(entry.timestamps.updated).getTime();
133
+ const now = Date.now();
134
+ const ageSeconds = (now - updatedAt) / 1000; //How old is this report?
135
+ return ageSeconds < config.cache.staleThresholdSeconds; //Is it younger than 24 hours?
136
+ }
137
+ /**
138
+ * Check if entry is stale but usable for stale-while-revalidate: Checks if a report is old but still usable while a new one is being generated in the background.
139
+ */
140
+ export function isStaleButUsable(entry) {
141
+ if (entry.status !== 'hydrated')
142
+ return false;
143
+ if (!config.cache.enableStaleWhileRevalidate)
144
+ return false;
145
+ return !isHydratedFresh(entry);
146
+ }
147
+ /**
148
+ * Delete cache entry: Removes the record from Redis immediately.
149
+ * Call this once the backend has successfully saved the report to its database.
150
+ */
151
+ export async function deleteCacheEntry(key) {
152
+ const redis = getRedisClient();
153
+ try {
154
+ await redis.del(key);
155
+ logger.debug({ key }, 'Deleted cache entry after successful delivery');
156
+ }
157
+ catch (error) {
158
+ logger.error({ error, key }, 'Failed to delete cache entry');
159
+ }
160
+ }
161
+ /**
162
+ * Close Redis connection (for graceful shutdown)
163
+ */
164
+ export async function closeRedis() {
165
+ if (redisClient) {
166
+ await redisClient.quit();
167
+ redisClient = null;
168
+ logger.info('Redis client disconnected');
169
+ }
170
+ }
171
+ //# sourceMappingURL=redisCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisCache.js","sourceRoot":"","sources":["../../src/cache/redisCache.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAE3C,OAAO,KAAK,MAAM,SAAS,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,IAAI,WAAW,GAAiB,IAAI,CAAC;AAErC;;GAEG;AACH,MAAM,UAAU,cAAc;IAC1B,IAAI,CAAC,WAAW,EAAE,CAAC;QACf,WAAW,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;YACtC,oBAAoB,EAAE,IAAI,EAAE,6CAA6C;YACzE,gBAAgB,EAAE,IAAI;YACtB,WAAW,EAAE,KAAK;SACrB,CAAC,CAAC;QAEH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAC5B,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,oBAAoB,CAAC,CAAC;YAC5C,KAAK,gBAAgB,CAAC;gBAClB,KAAK,EAAE,wBAAwB;gBAC/B,OAAO,EAAE,sCAAsC,GAAG,CAAC,OAAO,EAAE;gBAC5D,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE;aACxE,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YAC3B,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO,WAAW,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,SAAiB,EAAE,eAAuB,SAAS;IAC7F,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,UAAU,MAAM,IAAI,SAAS,IAAI,YAAY,EAAE,CAAC;AACpF,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC5C,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAE/B,IAAI,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QAEvB,MAAM,KAAK,GAAe,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,KAAK,CAAC;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,2BAA2B,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW,EAAE,KAAa;IAC1D,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAE/B,MAAM,KAAK,GAAe;QACtB,MAAM,EAAE,YAAY,EAAE,0BAA0B;QAChD,KAAK;QACL,UAAU,EAAE;YACR,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACjC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC;KACJ,CAAC;IAEF,IAAI,CAAC;QACD,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,yEAAyE;QACjJ,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,gCAAgC,CAAC,CAAC;IACnE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,gCAAgC,CAAC,CAAC;QAC/D,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC7B,GAAW,EACX,MAAoB,EACpB,kBAAyC,EACzC,WAAoB,KAAK,EACzB,GAAY,CAAC,iDAAiD;;IAE9D,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAE/B,MAAM,KAAK,GAAe;QACtB,MAAM,EAAE,UAAU,EAAE,qBAAqB;QACzC,MAAM;QACN,kBAAkB;QAClB,QAAQ;QACR,UAAU,EAAE;YACR,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACjC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC;QACD,GAAG,EAAE,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU;KACtC,CAAC;IAEF,IAAI,CAAC;QACD,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9E,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,6BAA6B,CAAC,CAAC;IACnE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,8BAA8B,CAAC,CAAC;QAC7D,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,YAAoB,EAAE,kBAAyC;IACzG,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAE/B,MAAM,KAAK,GAAe;QACtB,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,YAAY;QACnB,kBAAkB;QAClB,UAAU,EAAE;YACR,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACjC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC;KACJ,CAAC;IAEF,IAAI,CAAC;QACD,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,yBAAyB;QAC9E,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,8BAA8B,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,gCAAgC,CAAC,CAAC;IACnE,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAiB;IAC7C,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC,CAAC,4BAA4B;IAE3E,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,yBAAyB;IAEtE,OAAO,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAE,8BAA8B;AAC3F,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B;QAAE,OAAO,KAAK,CAAC;IAE3D,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAC9C,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAE/B,IAAI,CAAC;QACD,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrB,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,+CAA+C,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,8BAA8B,CAAC,CAAC;IACjE,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC5B,IAAI,WAAW,EAAE,CAAC;QACd,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;QACzB,WAAW,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAC7C,CAAC;AACL,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { startServer } from './server/start.js';
3
+ import { logger } from './logging.js';
4
+ startServer().catch((error) => {
5
+ logger.error({ error }, 'Failed to start Stepper server');
6
+ process.exit(1);
7
+ });
8
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC5B,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,gCAAgC,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { StepperConfig } from './types.js';
2
+ export declare function loadConfig(): StepperConfig;
3
+ export declare function createConfig(overrides?: Partial<StepperConfig>): StepperConfig;
4
+ export declare let config: StepperConfig;
5
+ export declare function applyConfigOverrides(overrides?: Partial<StepperConfig>): StepperConfig;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkB,MAAM,YAAY,CAAC;AAyD3D,wBAAgB,UAAU,IAAI,aAAa,CAoI1C;AA2DD,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAM9E;AAED,eAAO,IAAI,MAAM,eAAe,CAAC;AAEjC,wBAAgB,oBAAoB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAMtF"}