@littlebearapps/create-platform 1.0.0 → 1.1.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.
- package/README.md +98 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +36 -6
- package/dist/prompts.d.ts +14 -2
- package/dist/prompts.js +29 -7
- package/dist/templates.js +78 -0
- package/package.json +3 -2
- package/templates/full/workers/lib/pattern-discovery/ai-prompt.ts +644 -0
- package/templates/full/workers/lib/pattern-discovery/clustering.ts +278 -0
- package/templates/full/workers/lib/pattern-discovery/shadow-evaluation.ts +603 -0
- package/templates/full/workers/lib/pattern-discovery/storage.ts +806 -0
- package/templates/full/workers/lib/pattern-discovery/types.ts +159 -0
- package/templates/full/workers/lib/pattern-discovery/validation.ts +278 -0
- package/templates/full/workers/pattern-discovery.ts +661 -0
- package/templates/full/workers/platform-alert-router.ts +1809 -0
- package/templates/full/workers/platform-notifications.ts +424 -0
- package/templates/full/workers/platform-search.ts +480 -0
- package/templates/full/workers/platform-settings.ts +436 -0
- package/templates/shared/workers/lib/analytics-engine.ts +357 -0
- package/templates/shared/workers/lib/billing.ts +293 -0
- package/templates/shared/workers/lib/circuit-breaker-middleware.ts +25 -0
- package/templates/shared/workers/lib/control.ts +292 -0
- package/templates/shared/workers/lib/economics.ts +368 -0
- package/templates/shared/workers/lib/metrics.ts +103 -0
- package/templates/shared/workers/lib/platform-settings.ts +407 -0
- package/templates/shared/workers/lib/shared/allowances.ts +333 -0
- package/templates/shared/workers/lib/shared/cloudflare.ts +1362 -0
- package/templates/shared/workers/lib/shared/types.ts +58 -0
- package/templates/shared/workers/lib/telemetry-sampling.ts +360 -0
- package/templates/shared/workers/lib/usage/collectors/example.ts +96 -0
- package/templates/shared/workers/lib/usage/collectors/index.ts +128 -0
- package/templates/shared/workers/lib/usage/handlers/audit.ts +306 -0
- package/templates/shared/workers/lib/usage/handlers/backfill.ts +845 -0
- package/templates/shared/workers/lib/usage/handlers/behavioral.ts +429 -0
- package/templates/shared/workers/lib/usage/handlers/data-queries.ts +507 -0
- package/templates/shared/workers/lib/usage/handlers/dlq-admin.ts +364 -0
- package/templates/shared/workers/lib/usage/handlers/health-trends.ts +222 -0
- package/templates/shared/workers/lib/usage/handlers/index.ts +35 -0
- package/templates/shared/workers/lib/usage/handlers/usage-admin.ts +421 -0
- package/templates/shared/workers/lib/usage/handlers/usage-features.ts +1262 -0
- package/templates/shared/workers/lib/usage/handlers/usage-metrics.ts +2420 -0
- package/templates/shared/workers/lib/usage/handlers/usage-settings.ts +610 -0
- package/templates/shared/workers/lib/usage/queue/budget-enforcement.ts +1032 -0
- package/templates/shared/workers/lib/usage/queue/cost-budget-enforcement.ts +128 -0
- package/templates/shared/workers/lib/usage/queue/cost-calculator.ts +77 -0
- package/templates/shared/workers/lib/usage/queue/dlq-handler.ts +161 -0
- package/templates/shared/workers/lib/usage/queue/index.ts +19 -0
- package/templates/shared/workers/lib/usage/queue/telemetry-processor.ts +790 -0
- package/templates/shared/workers/lib/usage/scheduled/anomaly-detection.ts +732 -0
- package/templates/shared/workers/lib/usage/scheduled/data-collection.ts +956 -0
- package/templates/shared/workers/lib/usage/scheduled/error-digest.ts +343 -0
- package/templates/shared/workers/lib/usage/scheduled/index.ts +18 -0
- package/templates/shared/workers/lib/usage/scheduled/rollups.ts +1561 -0
- package/templates/shared/workers/lib/usage/shared/constants.ts +362 -0
- package/templates/shared/workers/lib/usage/shared/index.ts +14 -0
- package/templates/shared/workers/lib/usage/shared/types.ts +1066 -0
- package/templates/shared/workers/lib/usage/shared/utils.ts +795 -0
- package/templates/shared/workers/platform-usage.ts +1915 -0
- package/templates/standard/workers/error-collector.ts +2670 -0
- package/templates/standard/workers/lib/error-collector/capture.ts +213 -0
- package/templates/standard/workers/lib/error-collector/digest.ts +448 -0
- package/templates/standard/workers/lib/error-collector/email-health-alerts.ts +262 -0
- package/templates/standard/workers/lib/error-collector/fingerprint.ts +258 -0
- package/templates/standard/workers/lib/error-collector/gap-alerts.ts +293 -0
- package/templates/standard/workers/lib/error-collector/github.ts +329 -0
- package/templates/standard/workers/lib/error-collector/types.ts +262 -0
- package/templates/standard/workers/lib/sentinel/gap-detection.ts +734 -0
- package/templates/standard/workers/lib/shared/slack-alerts.ts +585 -0
- package/templates/standard/workers/platform-sentinel.ts +1744 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Digest Module
|
|
3
|
+
*
|
|
4
|
+
* Handles error alerting, tracking, and digest generation.
|
|
5
|
+
* Extracted from platform-usage.ts as part of scheduled task modularisation.
|
|
6
|
+
*
|
|
7
|
+
* Functions:
|
|
8
|
+
* - checkAndAlertErrors: Check telemetry for P0 conditions
|
|
9
|
+
* - storeErrorEvent: Store error events to D1 for aggregation
|
|
10
|
+
* - getErrorRateStats: Get error rate statistics over sliding window
|
|
11
|
+
* - sendErrorAlert: Send alerts via alert-router or Slack fallback
|
|
12
|
+
* - sendHourlyErrorDigest: Generate and send P1 hourly digest
|
|
13
|
+
* - sendDailyErrorSummary: Generate and send P2 daily summary
|
|
14
|
+
* - cleanupOldErrorEvents: Remove old error events (7-day retention)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { Env, ErrorAlertPayload, TelemetryMessage } from '../shared';
|
|
18
|
+
import { ERROR_RATE_THRESHOLDS } from '../shared';
|
|
19
|
+
import { createLoggerFromEnv } from '@littlebearapps/platform-sdk';
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// ERROR CHECKING AND ALERTING
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if telemetry message contains errors that warrant alerting.
|
|
27
|
+
* Detects P0 conditions: circuit breaker trips, high error rates.
|
|
28
|
+
*/
|
|
29
|
+
export async function checkAndAlertErrors(telemetry: TelemetryMessage, env: Env): Promise<void> {
|
|
30
|
+
// Skip if no errors reported
|
|
31
|
+
if (!telemetry.error_count || telemetry.error_count === 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// P0 Condition 1: Circuit breaker error
|
|
36
|
+
if (telemetry.error_category === 'CIRCUIT_BREAKER') {
|
|
37
|
+
await sendErrorAlert(env, {
|
|
38
|
+
type: 'p0_immediate',
|
|
39
|
+
feature_key: telemetry.feature_key,
|
|
40
|
+
project: telemetry.project,
|
|
41
|
+
category: telemetry.category,
|
|
42
|
+
feature: telemetry.feature,
|
|
43
|
+
correlation_id: telemetry.correlation_id,
|
|
44
|
+
error_category: telemetry.error_category,
|
|
45
|
+
error_code: telemetry.error_codes?.[0],
|
|
46
|
+
window_minutes: ERROR_RATE_THRESHOLDS.windowMinutes,
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Store error event in D1 for aggregation
|
|
52
|
+
await storeErrorEvent(telemetry, env);
|
|
53
|
+
|
|
54
|
+
// Check error rate over window for P0/P1 conditions
|
|
55
|
+
const errorStats = await getErrorRateStats(telemetry.feature_key, env);
|
|
56
|
+
|
|
57
|
+
if (errorStats.totalRequests >= ERROR_RATE_THRESHOLDS.minRequests) {
|
|
58
|
+
const errorRate = (errorStats.errorCount / errorStats.totalRequests) * 100;
|
|
59
|
+
|
|
60
|
+
if (errorRate >= ERROR_RATE_THRESHOLDS.p0) {
|
|
61
|
+
// P0: High error rate (>50%)
|
|
62
|
+
await sendErrorAlert(env, {
|
|
63
|
+
type: 'p0_immediate',
|
|
64
|
+
feature_key: telemetry.feature_key,
|
|
65
|
+
project: telemetry.project,
|
|
66
|
+
category: telemetry.category,
|
|
67
|
+
feature: telemetry.feature,
|
|
68
|
+
correlation_id: telemetry.correlation_id,
|
|
69
|
+
error_category: telemetry.error_category,
|
|
70
|
+
error_code: telemetry.error_codes?.[0],
|
|
71
|
+
error_rate: errorRate,
|
|
72
|
+
window_minutes: ERROR_RATE_THRESHOLDS.windowMinutes,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// =============================================================================
|
|
79
|
+
// ERROR EVENT STORAGE
|
|
80
|
+
// =============================================================================
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Store error event in D1 for aggregation and historical analysis.
|
|
84
|
+
*/
|
|
85
|
+
export async function storeErrorEvent(telemetry: TelemetryMessage, env: Env): Promise<void> {
|
|
86
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-tracking');
|
|
87
|
+
try {
|
|
88
|
+
await env.PLATFORM_DB.prepare(
|
|
89
|
+
`INSERT INTO feature_error_events (
|
|
90
|
+
id, feature_key, error_category, error_code, error_message,
|
|
91
|
+
correlation_id, worker, priority, created_at
|
|
92
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
93
|
+
)
|
|
94
|
+
.bind(
|
|
95
|
+
crypto.randomUUID(),
|
|
96
|
+
telemetry.feature_key,
|
|
97
|
+
telemetry.error_category || 'INTERNAL',
|
|
98
|
+
telemetry.error_codes?.[0] || null,
|
|
99
|
+
null, // No message in telemetry (truncated for space)
|
|
100
|
+
telemetry.correlation_id || null,
|
|
101
|
+
null, // Worker name not in telemetry
|
|
102
|
+
'P2', // Default priority, upgraded by alert detection
|
|
103
|
+
Math.floor(Date.now() / 1000)
|
|
104
|
+
)
|
|
105
|
+
.run();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
log.error('Failed to store error event', error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// ERROR RATE STATISTICS
|
|
113
|
+
// =============================================================================
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get error rate statistics for a feature over the sliding window.
|
|
117
|
+
*/
|
|
118
|
+
export async function getErrorRateStats(
|
|
119
|
+
featureKey: string,
|
|
120
|
+
env: Env
|
|
121
|
+
): Promise<{ errorCount: number; totalRequests: number }> {
|
|
122
|
+
try {
|
|
123
|
+
const windowStart = Math.floor(Date.now() / 1000) - ERROR_RATE_THRESHOLDS.windowMinutes * 60;
|
|
124
|
+
|
|
125
|
+
const result = await env.PLATFORM_DB.prepare(
|
|
126
|
+
`SELECT
|
|
127
|
+
COUNT(*) as error_count,
|
|
128
|
+
(SELECT COUNT(*) FROM feature_error_events
|
|
129
|
+
WHERE feature_key = ?1 AND created_at >= ?2) as total_events
|
|
130
|
+
FROM feature_error_events
|
|
131
|
+
WHERE feature_key = ?1 AND created_at >= ?2`
|
|
132
|
+
)
|
|
133
|
+
.bind(featureKey, windowStart)
|
|
134
|
+
.first<{ error_count: number; total_events: number }>();
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
errorCount: result?.error_count ?? 0,
|
|
138
|
+
totalRequests: result?.total_events ?? 0,
|
|
139
|
+
};
|
|
140
|
+
} catch (error) {
|
|
141
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-tracking');
|
|
142
|
+
log.error('Failed to get error rate', error);
|
|
143
|
+
return { errorCount: 0, totalRequests: 0 };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// =============================================================================
|
|
148
|
+
// ALERT SENDING
|
|
149
|
+
// =============================================================================
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Send error alert to alert-router.
|
|
153
|
+
* Uses service binding if available, falls back to direct Slack.
|
|
154
|
+
*/
|
|
155
|
+
export async function sendErrorAlert(env: Env, payload: ErrorAlertPayload): Promise<void> {
|
|
156
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-alerting');
|
|
157
|
+
try {
|
|
158
|
+
if (env.ALERT_ROUTER) {
|
|
159
|
+
// Use service binding to call alert-router
|
|
160
|
+
const response = await env.ALERT_ROUTER.fetch('https://alert-router/errors', {
|
|
161
|
+
method: 'POST',
|
|
162
|
+
headers: { 'Content-Type': 'application/json' },
|
|
163
|
+
body: JSON.stringify(payload),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
log.error(`alert-router returned ${response.status}`);
|
|
168
|
+
} else {
|
|
169
|
+
log.info('Alert sent', { type: payload.type, featureKey: payload.feature_key });
|
|
170
|
+
}
|
|
171
|
+
} else if (env.SLACK_WEBHOOK_URL) {
|
|
172
|
+
// Fallback: send directly to Slack (basic format)
|
|
173
|
+
const emoji = payload.type === 'p0_immediate' ? '🚨' : '⚠️';
|
|
174
|
+
await fetch(env.SLACK_WEBHOOK_URL, {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
headers: { 'Content-Type': 'application/json' },
|
|
177
|
+
body: JSON.stringify({
|
|
178
|
+
text: `${emoji} [${payload.type.toUpperCase()}] Error in ${payload.feature_key}: ${payload.error_category}`,
|
|
179
|
+
}),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
log.error('Failed to send alert', error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// DIGEST GENERATION
|
|
189
|
+
// =============================================================================
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Generate and send hourly P1 error digest.
|
|
193
|
+
* Only sends if error threshold met (>20% error rate or >100 errors).
|
|
194
|
+
*/
|
|
195
|
+
export async function sendHourlyErrorDigest(env: Env): Promise<void> {
|
|
196
|
+
try {
|
|
197
|
+
const hourAgo = Math.floor(Date.now() / 1000) - 3600;
|
|
198
|
+
|
|
199
|
+
// Get top errors from the last hour
|
|
200
|
+
const errors = await env.PLATFORM_DB.prepare(
|
|
201
|
+
`SELECT
|
|
202
|
+
feature_key,
|
|
203
|
+
error_category,
|
|
204
|
+
COUNT(*) as error_count
|
|
205
|
+
FROM feature_error_events
|
|
206
|
+
WHERE created_at >= ?
|
|
207
|
+
GROUP BY feature_key, error_category
|
|
208
|
+
ORDER BY error_count DESC
|
|
209
|
+
LIMIT 10`
|
|
210
|
+
)
|
|
211
|
+
.bind(hourAgo)
|
|
212
|
+
.all<{ feature_key: string; error_category: string; error_count: number }>();
|
|
213
|
+
|
|
214
|
+
if (!errors.results || errors.results.length === 0) {
|
|
215
|
+
return; // No errors in the last hour
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const totalErrors = errors.results.reduce((sum, e) => sum + e.error_count, 0);
|
|
219
|
+
const distinctTypes = new Set(errors.results.map((e) => e.error_category)).size;
|
|
220
|
+
|
|
221
|
+
// Only send P1 digest if threshold met
|
|
222
|
+
if (totalErrors < 100) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const now = new Date();
|
|
227
|
+
const hourAgoDate = new Date(now.getTime() - 3600000);
|
|
228
|
+
|
|
229
|
+
const payload: ErrorAlertPayload = {
|
|
230
|
+
type: 'p1_digest',
|
|
231
|
+
feature_key: 'platform:aggregate:hourly',
|
|
232
|
+
project: 'platform',
|
|
233
|
+
category: 'aggregate',
|
|
234
|
+
feature: 'hourly',
|
|
235
|
+
total_errors: totalErrors,
|
|
236
|
+
distinct_types: distinctTypes,
|
|
237
|
+
top_errors: errors.results.map((e) => ({
|
|
238
|
+
feature_key: e.feature_key,
|
|
239
|
+
error_category: e.error_category,
|
|
240
|
+
count: e.error_count,
|
|
241
|
+
})),
|
|
242
|
+
period_start: hourAgoDate.toISOString(),
|
|
243
|
+
period_end: now.toISOString(),
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
await sendErrorAlert(env, payload);
|
|
247
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-digest');
|
|
248
|
+
log.info('P1 hourly digest sent', { totalErrors });
|
|
249
|
+
} catch (error) {
|
|
250
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-digest');
|
|
251
|
+
log.error('Failed to generate hourly digest', error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Generate and send daily P2 error summary.
|
|
257
|
+
* Runs at midnight UTC (09:00 AEDT).
|
|
258
|
+
*/
|
|
259
|
+
export async function sendDailyErrorSummary(env: Env): Promise<void> {
|
|
260
|
+
try {
|
|
261
|
+
const dayAgo = Math.floor(Date.now() / 1000) - 86400;
|
|
262
|
+
|
|
263
|
+
// Get error summary for the last 24 hours
|
|
264
|
+
const errors = await env.PLATFORM_DB.prepare(
|
|
265
|
+
`SELECT
|
|
266
|
+
feature_key,
|
|
267
|
+
error_category,
|
|
268
|
+
COUNT(*) as error_count
|
|
269
|
+
FROM feature_error_events
|
|
270
|
+
WHERE created_at >= ?
|
|
271
|
+
GROUP BY feature_key, error_category
|
|
272
|
+
ORDER BY error_count DESC
|
|
273
|
+
LIMIT 20`
|
|
274
|
+
)
|
|
275
|
+
.bind(dayAgo)
|
|
276
|
+
.all<{ feature_key: string; error_category: string; error_count: number }>();
|
|
277
|
+
|
|
278
|
+
if (!errors.results || errors.results.length === 0) {
|
|
279
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-digest');
|
|
280
|
+
log.info('No errors in the last 24 hours, skipping daily summary');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const totalErrors = errors.results.reduce((sum, e) => sum + e.error_count, 0);
|
|
285
|
+
const distinctFeatures = new Set(errors.results.map((e) => e.feature_key)).size;
|
|
286
|
+
|
|
287
|
+
const now = new Date();
|
|
288
|
+
const dayAgoDate = new Date(now.getTime() - 86400000);
|
|
289
|
+
|
|
290
|
+
const payload: ErrorAlertPayload = {
|
|
291
|
+
type: 'p2_summary',
|
|
292
|
+
feature_key: 'platform:aggregate:daily',
|
|
293
|
+
project: 'platform',
|
|
294
|
+
category: 'aggregate',
|
|
295
|
+
feature: 'daily',
|
|
296
|
+
total_errors: totalErrors,
|
|
297
|
+
distinct_types: distinctFeatures,
|
|
298
|
+
top_errors: errors.results.map((e) => ({
|
|
299
|
+
feature_key: e.feature_key,
|
|
300
|
+
error_category: e.error_category,
|
|
301
|
+
count: e.error_count,
|
|
302
|
+
})),
|
|
303
|
+
period_start: dayAgoDate.toISOString(),
|
|
304
|
+
period_end: now.toISOString(),
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
await sendErrorAlert(env, payload);
|
|
308
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-digest');
|
|
309
|
+
log.info('P2 daily summary sent', { totalErrors, distinctFeatures });
|
|
310
|
+
} catch (error) {
|
|
311
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-digest');
|
|
312
|
+
log.error('Failed to generate daily summary', error);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// =============================================================================
|
|
317
|
+
// CLEANUP
|
|
318
|
+
// =============================================================================
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Clean up old error events (7-day retention).
|
|
322
|
+
*/
|
|
323
|
+
export async function cleanupOldErrorEvents(env: Env): Promise<number> {
|
|
324
|
+
const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:error-cleanup');
|
|
325
|
+
try {
|
|
326
|
+
const weekAgo = Math.floor(Date.now() / 1000) - 7 * 86400;
|
|
327
|
+
|
|
328
|
+
const result = await env.PLATFORM_DB.prepare(
|
|
329
|
+
`DELETE FROM feature_error_events WHERE created_at < ?`
|
|
330
|
+
)
|
|
331
|
+
.bind(weekAgo)
|
|
332
|
+
.run();
|
|
333
|
+
|
|
334
|
+
const deleted = result.meta?.changes ?? 0;
|
|
335
|
+
if (deleted > 0) {
|
|
336
|
+
log.info('Deleted old error events', { deleted });
|
|
337
|
+
}
|
|
338
|
+
return deleted;
|
|
339
|
+
} catch (error) {
|
|
340
|
+
log.error('Failed to cleanup old error events', error);
|
|
341
|
+
return 0;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduled Module Exports
|
|
3
|
+
*
|
|
4
|
+
* Barrel export for all scheduled task modules.
|
|
5
|
+
* These handle cron-triggered data collection, rollups, and monitoring.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Data collection functions (hourly snapshots, third-party APIs)
|
|
9
|
+
export * from './data-collection';
|
|
10
|
+
|
|
11
|
+
// Rollup functions (daily, monthly aggregation)
|
|
12
|
+
export * from './rollups';
|
|
13
|
+
|
|
14
|
+
// Anomaly detection and alerting
|
|
15
|
+
export * from './anomaly-detection';
|
|
16
|
+
|
|
17
|
+
// Error digest and alerting
|
|
18
|
+
export * from './error-digest';
|