@littlebearapps/platform-admin-sdk 1.2.0 → 1.4.1

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.
@@ -357,7 +357,8 @@ export async function checkAndTripCircuitBreakers(env: Env): Promise<boolean> {
357
357
 
358
358
  // Fetch settings and D1 writes in parallel
359
359
  const [settings, writes24h] = await Promise.all([getPlatformSettings(env), getD1WriteCount(env)]);
360
- const d1WriteLimit = settings.d1WriteLimit;
360
+ // Defense-in-depth: floor critical limits to prevent stale/poisoned cache values
361
+ const d1WriteLimit = Math.max(settings.d1WriteLimit, 1000);
361
362
 
362
363
  // Check D1 write limit (global)
363
364
  const d1Status = determineCircuitBreakerStatus(writes24h, d1WriteLimit);
@@ -869,8 +870,9 @@ const MONTHLY_METRIC_TO_COLUMN: Record<string, string> = {
869
870
  /** Allowlist for safe column interpolation in SQL. */
870
871
  const ALLOWED_MONTHLY_COLUMNS = new Set(Object.values(MONTHLY_METRIC_TO_COLUMN));
871
872
 
872
- // TODO: Add your project IDs here (must match project_registry in D1)
873
- const MONTHLY_PROJECTS = ['all', 'platform'] as const;
873
+ // TODO: Add your project slugs from project_registry (e.g., 'my-app', 'my-api')
874
+ // 'all' is the account-level aggregate and should always be included.
875
+ const MONTHLY_PROJECTS = ['all'] as const;
874
876
 
875
877
  /**
876
878
  * Check monthly budget usage against limits.
@@ -910,6 +912,10 @@ export async function checkMonthlyBudgets(env: Env): Promise<number> {
910
912
  return 0;
911
913
  }
912
914
 
915
+ // Pre-calculate month start to enable index usage on (project, snapshot_date)
916
+ const now = new Date();
917
+ const monthStart = `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, '0')}-01`;
918
+
913
919
  // For each project, get monthly totals from daily_usage_rollups
914
920
  for (const project of MONTHLY_PROJECTS) {
915
921
  // Get the monthly sum for this project
@@ -929,9 +935,9 @@ export async function checkMonthlyBudgets(env: Env): Promise<number> {
929
935
  SUM(vectorize_queries) as vectorize_queries,
930
936
  SUM(vectorize_inserts) as vectorize_inserts
931
937
  FROM daily_usage_rollups
932
- WHERE project = ? AND snapshot_date >= date('now', 'start of month')
938
+ WHERE project = ? AND snapshot_date >= ?
933
939
  LIMIT 1
934
- `).bind(project).first<Record<string, number | null>>();
940
+ `).bind(project, monthStart).first<Record<string, number | null>>();
935
941
 
936
942
  if (!monthlyTotals) continue;
937
943
 
@@ -388,8 +388,9 @@ const MONITORED_METRICS = [
388
388
  * Projects monitored for per-project anomaly detection.
389
389
  * Includes 'all' (aggregate) plus individual projects.
390
390
  */
391
- // TODO: Add your project IDs here (must match project_registry in D1)
392
- const MONITORED_PROJECTS = ['all', 'platform'] as const;
391
+ // TODO: Add your project slugs from project_registry (e.g., 'my-app', 'my-api')
392
+ // 'all' is the account-level aggregate and should always be included.
393
+ const MONITORED_PROJECTS = ['all'] as const;
393
394
 
394
395
  /**
395
396
  * Run anomaly detection for key metrics across all monitored projects.
@@ -467,6 +468,13 @@ export async function calculateHourlyRollingStats(
467
468
  }
468
469
 
469
470
  try {
471
+ // Pre-calculate time bounds to enable index usage on snapshot_hour
472
+ const now = new Date();
473
+ const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
474
+ const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
475
+ const sevenDaysAgoStr = sevenDaysAgo.toISOString().replace(/\.\d{3}Z$/, 'Z');
476
+ const oneHourAgoStr = oneHourAgo.toISOString().replace(/\.\d{3}Z$/, 'Z');
477
+
470
478
  const result = await env.PLATFORM_DB.prepare(
471
479
  `
472
480
  SELECT
@@ -476,11 +484,11 @@ export async function calculateHourlyRollingStats(
476
484
  AVG(${metric}) as avg_value
477
485
  FROM hourly_usage_snapshots
478
486
  WHERE project = ?
479
- AND snapshot_hour >= datetime('now', '-7 days')
480
- AND snapshot_hour < datetime('now', '-1 hour')
487
+ AND snapshot_hour >= ?
488
+ AND snapshot_hour < ?
481
489
  `
482
490
  )
483
- .bind(project)
491
+ .bind(project, sevenDaysAgoStr, oneHourAgoStr)
484
492
  .first<{
485
493
  sample_count: number;
486
494
  sum_value: number;
@@ -522,19 +530,26 @@ export async function detectHourlyD1WriteAnomalies(env: Env): Promise<number> {
522
530
  const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:anomaly');
523
531
 
524
532
  try {
533
+ // Pre-calculate time bounds to enable index usage on snapshot_hour
534
+ const now = new Date();
535
+ const twoHoursAgo = new Date(now.getTime() - 2 * 60 * 60 * 1000);
536
+ const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
537
+ const twoHoursAgoStr = twoHoursAgo.toISOString().replace(/\.\d{3}Z$/, 'Z');
538
+ const oneHourAgoStr = oneHourAgo.toISOString().replace(/\.\d{3}Z$/, 'Z');
539
+
525
540
  // Get the last completed hour's value
526
541
  const lastHourResult = await env.PLATFORM_DB.prepare(
527
542
  `
528
543
  SELECT ${metric} as value
529
544
  FROM hourly_usage_snapshots
530
545
  WHERE project = ?
531
- AND snapshot_hour >= datetime('now', '-2 hours')
532
- AND snapshot_hour < datetime('now', '-1 hour')
546
+ AND snapshot_hour >= ?
547
+ AND snapshot_hour < ?
533
548
  ORDER BY snapshot_hour DESC
534
549
  LIMIT 1
535
550
  `
536
551
  )
537
- .bind(project)
552
+ .bind(project, twoHoursAgoStr, oneHourAgoStr)
538
553
  .first<{ value: number }>();
539
554
 
540
555
  if (!lastHourResult || lastHourResult.value === 0) {
@@ -127,11 +127,14 @@ export async function runDailyRollup(env: Env, date: string): Promise<number> {
127
127
  const log = createLoggerFromEnv(env, 'platform-usage', 'platform:usage:scheduled');
128
128
  log.info(`Running daily rollup for ${date}`, { tag: 'SCHEDULED' });
129
129
 
130
- // Calculate the previous day's date
130
+ // Calculate previous and next day dates for range queries (index-friendly)
131
131
  const targetDate = new Date(date + 'T00:00:00Z');
132
132
  const prevDate = new Date(targetDate);
133
133
  prevDate.setUTCDate(prevDate.getUTCDate() - 1);
134
134
  const prevDateStr = prevDate.toISOString().split('T')[0];
135
+ const nextDate = new Date(targetDate);
136
+ nextDate.setUTCDate(nextDate.getUTCDate() + 1);
137
+ const nextDateStr = nextDate.toISOString().split('T')[0];
135
138
 
136
139
  // Check if this is the first day of the month
137
140
  const isFirstDayOfMonth = targetDate.getUTCDate() === 1;
@@ -269,11 +272,11 @@ export async function runDailyRollup(env: Env, date: string): Promise<number> {
269
272
  MAX(total_cost_usd) as total_cost_usd,
270
273
  COUNT(*) as samples_count
271
274
  FROM hourly_usage_snapshots
272
- WHERE DATE(snapshot_hour) = ?
275
+ WHERE snapshot_hour >= ? AND snapshot_hour < ?
273
276
  GROUP BY project
274
277
  `
275
278
  )
276
- .bind(date)
279
+ .bind(date + 'T00:00:00Z', nextDateStr + 'T00:00:00Z')
277
280
  .all<HourlyMaxRow>();
278
281
 
279
282
  if (!todayResult.results || todayResult.results.length === 0) {
@@ -324,11 +327,11 @@ export async function runDailyRollup(env: Env, date: string): Promise<number> {
324
327
  MAX(COALESCE(workflows_cost_usd, 0)) as workflows_cost_usd,
325
328
  MAX(total_cost_usd) as total_cost_usd
326
329
  FROM hourly_usage_snapshots
327
- WHERE DATE(snapshot_hour) = ?
330
+ WHERE snapshot_hour >= ? AND snapshot_hour < ?
328
331
  GROUP BY project
329
332
  `
330
333
  )
331
- .bind(prevDateStr)
334
+ .bind(prevDateStr + 'T00:00:00Z', date + 'T00:00:00Z')
332
335
  .all<PrevDayMaxRow>();
333
336
 
334
337
  if (prevResult.results) {