sermon-js 0.1.0 → 0.3.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 (52) hide show
  1. package/README.md +2 -1
  2. package/dist/api/index.d.cts +4 -4
  3. package/dist/api/index.d.ts +4 -4
  4. package/dist/{chunk-PARE6EUZ.cjs → chunk-NZTKXXW2.cjs} +7 -3
  5. package/dist/chunk-NZTKXXW2.cjs.map +1 -0
  6. package/dist/{chunk-2M4UBCDM.js → chunk-R7BDAQOM.js} +7 -3
  7. package/dist/chunk-R7BDAQOM.js.map +1 -0
  8. package/dist/{config-BEbP-uLf.d.cts → config-gro29syt.d.cts} +1 -1
  9. package/dist/{config-w1tm1bdu.d.ts → config-iY18j9IA.d.ts} +1 -1
  10. package/dist/{generic-lmrGxMDC.d.cts → generic-BUiaKpNp.d.cts} +2 -2
  11. package/dist/{generic-BJmlgtUM.d.ts → generic-CV2O4aNH.d.ts} +2 -2
  12. package/dist/index.cjs +6 -6
  13. package/dist/index.d.cts +8 -8
  14. package/dist/index.d.ts +8 -8
  15. package/dist/index.js +2 -2
  16. package/dist/presets/index.cjs +552 -47
  17. package/dist/presets/index.cjs.map +1 -1
  18. package/dist/presets/index.d.cts +261 -13
  19. package/dist/presets/index.d.ts +261 -13
  20. package/dist/presets/index.js +542 -48
  21. package/dist/presets/index.js.map +1 -1
  22. package/dist/react/heroui/index.cjs +27 -18
  23. package/dist/react/heroui/index.cjs.map +1 -1
  24. package/dist/react/heroui/index.d.cts +7 -2
  25. package/dist/react/heroui/index.d.ts +7 -2
  26. package/dist/react/heroui/index.js +13 -4
  27. package/dist/react/heroui/index.js.map +1 -1
  28. package/dist/react/index.d.cts +2 -2
  29. package/dist/react/index.d.ts +2 -2
  30. package/dist/{status-DsWzgb9Q.d.cts → status-BExBb08T.d.cts} +3 -3
  31. package/dist/{status-zd04ZQ98.d.ts → status-DmRhwvVC.d.ts} +3 -3
  32. package/dist/{store-_GuzSP0z.d.ts → store-BfKP7CjW.d.ts} +1 -1
  33. package/dist/{store-DybhDphS.d.cts → store-CAIB7Esp.d.cts} +1 -1
  34. package/dist/stores/mongodb.d.cts +2 -2
  35. package/dist/stores/mongodb.d.ts +2 -2
  36. package/dist/stores/redis.d.cts +2 -2
  37. package/dist/stores/redis.d.ts +2 -2
  38. package/dist/stores/sql.d.cts +2 -2
  39. package/dist/stores/sql.d.ts +2 -2
  40. package/dist/stores/upstash.d.cts +2 -2
  41. package/dist/stores/upstash.d.ts +2 -2
  42. package/dist/testing/index.cjs +3 -3
  43. package/dist/testing/index.d.cts +3 -3
  44. package/dist/testing/index.d.ts +3 -3
  45. package/dist/testing/index.js +1 -1
  46. package/dist/{tree-BXbXfjF1.d.cts → tree-J3riUyI9.d.cts} +9 -0
  47. package/dist/{tree-BXbXfjF1.d.ts → tree-J3riUyI9.d.ts} +9 -0
  48. package/dist/{types-CctvUAs2.d.cts → types-BVa6vIKk.d.cts} +9 -0
  49. package/dist/{types-CctvUAs2.d.ts → types-BVa6vIKk.d.ts} +9 -0
  50. package/package.json +1 -1
  51. package/dist/chunk-2M4UBCDM.js.map +0 -1
  52. package/dist/chunk-PARE6EUZ.cjs.map +0 -1
@@ -17,9 +17,9 @@ function withKeys(machines) {
17
17
  const seen = /* @__PURE__ */ new Map();
18
18
  return [...machines].sort((a, b) => (a.region ?? "").localeCompare(b.region ?? "") || a.id.localeCompare(b.id)).map((machine) => {
19
19
  const region = machine.region ?? "unknown";
20
- const count = (seen.get(region) ?? 0) + 1;
21
- seen.set(region, count);
22
- return { machine, region, key: count === 1 ? region : `${region}#${count}` };
20
+ const count2 = (seen.get(region) ?? 0) + 1;
21
+ seen.set(region, count2);
22
+ return { machine, region, key: count2 === 1 ? region : `${region}#${count2}` };
23
23
  });
24
24
  }
25
25
  function median(values) {
@@ -221,8 +221,8 @@ var instance = definePreset()({
221
221
  }),
222
222
  check: async ({ options, secrets, http, now }) => {
223
223
  const account = parseServiceAccount(secrets.serviceAccount);
224
- const project = options.project ?? account?.project_id;
225
- if (!account || !project) {
224
+ const project2 = options.project ?? account?.project_id;
225
+ if (!account || !project2) {
226
226
  return { status: "down", probed: false, error: "The service account secret is not a service-account JSON key" };
227
227
  }
228
228
  let token;
@@ -237,7 +237,7 @@ var instance = definePreset()({
237
237
  return { status: "down", probed: false, error: error instanceof Error ? error.message : String(error) };
238
238
  }
239
239
  const info = await http(
240
- `https://compute.googleapis.com/compute/v1/projects/${project}/zones/${options.zone}/instances/${options.instance}`,
240
+ `https://compute.googleapis.com/compute/v1/projects/${project2}/zones/${options.zone}/instances/${options.instance}`,
241
241
  { headers: { Authorization: `Bearer ${token}` } }
242
242
  );
243
243
  const state = info.json?.status;
@@ -260,6 +260,68 @@ var instance = definePreset()({
260
260
  }
261
261
  });
262
262
  var gcp = { instance };
263
+
264
+ // src/presets/providers/shared.ts
265
+ var bearer = (token) => ({ Authorization: `Bearer ${token}` });
266
+ var round = (value, digits = 1) => Math.round(value * 10 ** digits) / 10 ** digits;
267
+ function base64(text) {
268
+ const bytes = new TextEncoder().encode(text);
269
+ let binary = "";
270
+ for (const byte of bytes) binary += String.fromCharCode(byte);
271
+ return btoa(binary);
272
+ }
273
+ async function call(http, url, init, refine) {
274
+ const response = await http(url, init);
275
+ if (!response.ok || !refine) return response.outcome;
276
+ return { ...response.outcome, ...normalizeReturn(refine(response)) };
277
+ }
278
+ function quota({
279
+ label,
280
+ used,
281
+ limit,
282
+ unit,
283
+ resetsAt,
284
+ warnAbovePct,
285
+ warning
286
+ }) {
287
+ const pct = limit > 0 ? used / limit * 100 : 0;
288
+ const over = pct >= warnAbovePct;
289
+ return {
290
+ status: over ? "degraded" : "up",
291
+ error: over ? warning : void 0,
292
+ usage: [{ label, used, limit, unit, resetsAt }]
293
+ };
294
+ }
295
+ function summarize(instances, noun) {
296
+ const down = instances.filter((instance2) => instance2.status === "down");
297
+ const degraded = instances.filter((instance2) => instance2.status === "degraded");
298
+ const awake = instances.filter((instance2) => instance2.status === "up" || instance2.status === "degraded");
299
+ const keysOf = (items) => items.map((instance2) => instance2.key).join(", ");
300
+ const latencies = instances.map((instance2) => instance2.latencyMs).filter((l) => l !== void 0);
301
+ const latencyMs = latencies.length ? Math.max(...latencies) : void 0;
302
+ if (instances.length > 0 && down.length === instances.length) {
303
+ return { status: "down", latencyMs, error: `All ${instances.length} ${noun}(s) failing` };
304
+ }
305
+ if (down.length > 0) {
306
+ return { status: "degraded", latencyMs, error: `${down.length}/${instances.length} ${noun}(s) failing: ${keysOf(down)}` };
307
+ }
308
+ if (degraded.length > 0) return { status: "degraded", latencyMs, error: `Degraded: ${keysOf(degraded)}` };
309
+ return { status: awake.length || instances.length === 0 ? "up" : "sleeping", latencyMs };
310
+ }
311
+ function keyAccepted(res) {
312
+ if (res.status === 400 || res.status === 422) return { status: "up", latencyMs: res.latencyMs, httpStatus: res.status };
313
+ if (res.status === 402) {
314
+ return { status: "degraded", latencyMs: res.latencyMs, httpStatus: 402, error: "Out of credits, or payment required" };
315
+ }
316
+ return res.outcome;
317
+ }
318
+ var emptyPost = (headers) => ({
319
+ method: "POST",
320
+ headers: { "content-type": "application/json", ...headers },
321
+ body: "{}"
322
+ });
323
+
324
+ // src/presets/aws.ts
263
325
  async function signedCall({
264
326
  http,
265
327
  secrets,
@@ -319,7 +381,170 @@ var s3Bucket = definePreset()({
319
381
  return response.outcome;
320
382
  }
321
383
  });
322
- var aws = { credentials, s3Bucket };
384
+ function pingBody(model) {
385
+ if (/(^|\.)cohere\.embed/.test(model)) return { texts: ["ping"], input_type: "search_query" };
386
+ return { anthropic_version: "bedrock-2023-05-31", max_tokens: 1, messages: [{ role: "user", content: "ping" }] };
387
+ }
388
+ var bedrockPreset = definePreset()({
389
+ kind: "aws.bedrock",
390
+ requiredFields: ["region"],
391
+ requiredSecrets: "all",
392
+ defaults: { slowMs: 5e3, timeoutMs: 2e4 },
393
+ derive: () => ({
394
+ name: "Amazon Bedrock",
395
+ identity: "aws.bedrock",
396
+ description: "Invokes each monitored model with one token"
397
+ }),
398
+ check: async ({ options, secrets, http }) => {
399
+ const signer = secrets.accessKeyId && secrets.secretAccessKey ? new AwsClient({
400
+ accessKeyId: secrets.accessKeyId,
401
+ secretAccessKey: secrets.secretAccessKey,
402
+ service: "bedrock",
403
+ region: options.region
404
+ }) : void 0;
405
+ const invoke = async (model) => {
406
+ const url = `https://bedrock-runtime.${options.region}.amazonaws.com/model/${encodeURIComponent(model)}/invoke`;
407
+ const init = {
408
+ method: "POST",
409
+ headers: { "content-type": "application/json", accept: "application/json" },
410
+ body: JSON.stringify(pingBody(model))
411
+ };
412
+ if (!signer) return http(url, { ...init, headers: { ...init.headers, ...bearer(secrets.bearerToken ?? "") } });
413
+ const signed = await signer.sign(url, init);
414
+ return http(signed.url, { ...init, headers: Object.fromEntries(signed.headers.entries()) });
415
+ };
416
+ const instances = await Promise.all(
417
+ [...new Set(options.models)].map(async (model) => {
418
+ const res = await invoke(model);
419
+ return {
420
+ key: model,
421
+ status: res.outcome.status,
422
+ latencyMs: res.status === void 0 ? void 0 : res.latencyMs,
423
+ httpStatus: res.status,
424
+ error: res.outcome.error
425
+ };
426
+ })
427
+ );
428
+ return { ...summarize(instances, "model"), instances, instanceLabels: { key: "Model" } };
429
+ }
430
+ });
431
+ function bedrock(options) {
432
+ const { bearerToken, accessKeyId, secretAccessKey } = options.secrets;
433
+ return bedrockPreset({ ...options, secrets: bearerToken ? { bearerToken } : { accessKeyId, secretAccessKey } });
434
+ }
435
+ var aws = { credentials, s3Bucket, bedrock };
436
+
437
+ // src/presets/neon.ts
438
+ var API = "https://console.neon.tech/api/v2";
439
+ var GB = 1024 ** 3;
440
+ function withKeys2(endpoints, branches) {
441
+ const seen = /* @__PURE__ */ new Map();
442
+ return endpoints.map((endpoint) => {
443
+ const name = branches.get(endpoint.branch_id)?.name ?? endpoint.branch_id;
444
+ const count2 = (seen.get(name) ?? 0) + 1;
445
+ seen.set(name, count2);
446
+ return { endpoint, key: count2 === 1 ? name : `${name}#${count2}` };
447
+ });
448
+ }
449
+ var project = definePreset()({
450
+ kind: "neon.project",
451
+ requiredFields: ["projectId"],
452
+ requiredSecrets: ["apiKey"],
453
+ defaults: { slowMs: 1e3 },
454
+ derive: (options) => ({
455
+ name: "Neon",
456
+ identity: `neon.project:${options.projectId ?? ""}`,
457
+ description: "Compute states from the Neon API, then `select 1` on an active compute only"
458
+ }),
459
+ check: async ({ options, secrets, http, measure }) => {
460
+ const base = `${API}/projects/${encodeURIComponent(options.projectId)}`;
461
+ const headers = { ...bearer(secrets.apiKey), accept: "application/json" };
462
+ const [list, info, branchList] = await Promise.all([
463
+ http(`${base}/endpoints`, { headers }),
464
+ http(base, { headers }),
465
+ http(`${base}/branches`, { headers })
466
+ ]);
467
+ if (list.status === 404) {
468
+ return { status: "down", probed: false, httpStatus: 404, error: `Project "${options.projectId}" not found on Neon` };
469
+ }
470
+ const endpoints = list.json?.endpoints;
471
+ if (!list.ok || !Array.isArray(endpoints)) {
472
+ return {
473
+ status: "degraded",
474
+ probed: false,
475
+ httpStatus: list.status,
476
+ error: list.status ? `Neon API HTTP ${list.status}` : `Neon API unreachable: ${list.outcome.error ?? "no response"}`
477
+ };
478
+ }
479
+ if (endpoints.length === 0) return { status: "down", probed: false, error: "No compute: nothing can connect" };
480
+ const branches = branchList.json?.branches ?? [];
481
+ const details = info.json?.project;
482
+ const byId = new Map(branches.map((branch) => [branch.id, branch]));
483
+ const defaultBranch = branches.find((branch) => branch.default)?.id;
484
+ const keyed = withKeys2(endpoints, byId);
485
+ const target = keyed.find(({ endpoint }) => endpoint.id === options.endpoint) ?? (options.endpoint ? void 0 : keyed.find(({ endpoint }) => endpoint.type === "read_write" && endpoint.branch_id === defaultBranch) ?? keyed.find(({ endpoint }) => endpoint.type === "read_write"));
486
+ const ping = options.query && target?.endpoint.current_state === "active" && !target.endpoint.disabled ? await measure(async () => void await options.query("select 1")) : void 0;
487
+ const instances = keyed.map(({ endpoint, key }) => {
488
+ const row = { key, region: endpoint.region_id, id: endpoint.id };
489
+ if (endpoint.disabled) return { ...row, state: "disabled", status: "sleeping" };
490
+ if (endpoint.current_state === "idle") return { ...row, state: "idle", status: "sleeping" };
491
+ if (endpoint.current_state !== "active") return { ...row, state: endpoint.current_state, status: "degraded" };
492
+ if (ping && endpoint.id === target?.endpoint.id) {
493
+ return { ...row, state: "active", status: ping.status, latencyMs: ping.latencyMs, error: ping.error };
494
+ }
495
+ return { ...row, state: "active", status: "up" };
496
+ });
497
+ const usage = [];
498
+ const warnings = [];
499
+ const bar = (entry, warnAbovePct = 90) => {
500
+ usage.push(entry);
501
+ if (entry.limit > 0 && entry.used / entry.limit * 100 >= warnAbovePct) warnings.push(`${entry.label} close to the plan limit`);
502
+ };
503
+ const compute = options.track?.compute;
504
+ if (compute && typeof details?.compute_time_seconds === "number") {
505
+ bar(
506
+ {
507
+ label: "Compute this period",
508
+ used: round(details.compute_time_seconds / 3600),
509
+ limit: compute.maxHours,
510
+ unit: "CU-hours",
511
+ resetsAt: details.consumption_period_end ? Date.parse(details.consumption_period_end) || void 0 : void 0
512
+ },
513
+ compute.warnAbovePct
514
+ );
515
+ }
516
+ const storage = options.track?.storage;
517
+ if (storage && branches.length > 0) {
518
+ const bytes = branches.reduce((sum, branch) => sum + (branch.logical_size ?? 0), 0);
519
+ bar({ label: "Storage", used: round(bytes / GB, 2), limit: storage.maxGb, unit: "GB" }, storage.warnAbovePct);
520
+ }
521
+ const main = instances.find((instance2) => instance2.id === target?.endpoint.id) ?? instances[0];
522
+ const active = instances.filter((instance2) => instance2.state === "active").length;
523
+ let status = main.status;
524
+ let error = main.error;
525
+ if (status === "degraded" && !error) error = `Compute is ${main.state}`;
526
+ if ((status === "up" || status === "sleeping") && warnings.length) {
527
+ status = "degraded";
528
+ error = warnings.join("; ");
529
+ }
530
+ return {
531
+ status,
532
+ probed: !!ping,
533
+ latencyMs: ping?.latencyMs ?? list.latencyMs,
534
+ error,
535
+ metrics: {
536
+ ...details?.pg_version ? { postgres: details.pg_version } : {},
537
+ ...details?.region_id ? { region: details.region_id } : {},
538
+ ...branches.length ? { branches: branches.length } : {},
539
+ computes: `${active}/${instances.length} active`
540
+ },
541
+ usage: usage.length ? usage : void 0,
542
+ instances,
543
+ instanceLabels: { key: "Compute", detail: "Endpoint" }
544
+ };
545
+ }
546
+ });
547
+ var neon = { project };
323
548
 
324
549
  // src/presets/databases.ts
325
550
  var mongoPing = definePreset()({
@@ -346,38 +571,16 @@ var redisPing = definePreset()({
346
571
  })
347
572
  });
348
573
  var redis = { ping: redisPing };
349
-
350
- // src/presets/providers/shared.ts
351
- var bearer = (token) => ({ Authorization: `Bearer ${token}` });
352
- var round = (value, digits = 1) => Math.round(value * 10 ** digits) / 10 ** digits;
353
- function base64(text) {
354
- const bytes = new TextEncoder().encode(text);
355
- let binary = "";
356
- for (const byte of bytes) binary += String.fromCharCode(byte);
357
- return btoa(binary);
358
- }
359
- async function call(http, url, init, refine) {
360
- const response = await http(url, init);
361
- if (!response.ok || !refine) return response.outcome;
362
- return { ...response.outcome, ...normalizeReturn(refine(response)) };
363
- }
364
- function quota({
365
- label,
366
- used,
367
- limit,
368
- unit,
369
- resetsAt,
370
- warnAbovePct,
371
- warning
372
- }) {
373
- const pct = limit > 0 ? used / limit * 100 : 0;
374
- const over = pct >= warnAbovePct;
375
- return {
376
- status: over ? "degraded" : "up",
377
- error: over ? warning : void 0,
378
- usage: [{ label, used, limit, unit, resetsAt }]
379
- };
380
- }
574
+ var postgresPing = definePreset()({
575
+ kind: "postgres.ping",
576
+ requiredFields: ["query"],
577
+ defaults: { slowMs: 1e3 },
578
+ derive: () => ({ name: "PostgreSQL", description: "Runs `select 1`" }),
579
+ check: ({ options, measure }) => measure(async () => {
580
+ await options.query("select 1");
581
+ })
582
+ });
583
+ var postgres = { ping: postgresPing };
381
584
 
382
585
  // src/presets/providers/ai.ts
383
586
  var deepgram = definePreset()({
@@ -442,13 +645,33 @@ var openai = definePreset()({
442
645
  }
443
646
  )
444
647
  });
648
+ var API2 = "https://api.anthropic.com/v1/models";
445
649
  var anthropic = definePreset()({
446
650
  kind: "anthropic",
447
651
  requiredSecrets: ["apiKey"],
448
- derive: () => ({ name: "Anthropic", identity: "anthropic", description: "Lists models" }),
449
- check: ({ secrets, http }) => call(http, "https://api.anthropic.com/v1/models?limit=1", {
450
- headers: { "x-api-key": secrets.apiKey, "anthropic-version": "2023-06-01" }
451
- })
652
+ derive: (options) => ({
653
+ name: "Anthropic",
654
+ identity: "anthropic",
655
+ description: options.track?.models?.length ? "Reads each monitored model" : "Lists models"
656
+ }),
657
+ check: async ({ options, secrets, http }) => {
658
+ const headers = { "x-api-key": secrets.apiKey, "anthropic-version": "2023-06-01" };
659
+ const models = [...new Set(options.track?.models ?? [])];
660
+ if (models.length === 0) return (await http(`${API2}?limit=1`, { headers })).outcome;
661
+ const responses = await Promise.all(
662
+ models.map(async (model) => ({ model, res: await http(`${API2}/${encodeURIComponent(model)}`, { headers }) }))
663
+ );
664
+ const rejected = responses.find(({ res }) => res.status === 401 || res.status === 403);
665
+ if (rejected) return rejected.res.outcome;
666
+ const instances = responses.map(({ model, res }) => {
667
+ const base = { key: model, latencyMs: res.status === void 0 ? void 0 : res.latencyMs, httpStatus: res.status };
668
+ if (res.ok) return { ...base, status: "up" };
669
+ if (res.status === 404) return { ...base, status: "down", error: "Model not found: retired, or not available to this key" };
670
+ if (res.status === 529) return { ...base, status: "degraded", error: "Overloaded" };
671
+ return { ...base, status: res.outcome.status, error: res.outcome.error };
672
+ });
673
+ return { ...summarize(instances, "model"), instances, instanceLabels: { key: "Model" } };
674
+ }
452
675
  });
453
676
  var gemini = definePreset()({
454
677
  kind: "gemini",
@@ -464,6 +687,38 @@ var cambai = definePreset()({
464
687
  derive: () => ({ name: "CAMB.AI", identity: "cambai", description: "Lists source languages" }),
465
688
  check: ({ secrets, http }) => call(http, "https://client.camb.ai/apis/source-languages", { headers: { "x-api-key": secrets.apiKey } })
466
689
  });
690
+ var elevenlabs = definePreset()({
691
+ kind: "elevenlabs",
692
+ requiredSecrets: ["apiKey"],
693
+ derive: () => ({ name: "ElevenLabs", identity: "elevenlabs", description: "Reads the subscription" }),
694
+ check: async ({ options, secrets, http }) => {
695
+ const res = await http("https://api.elevenlabs.io/v1/user/subscription", {
696
+ headers: { "xi-api-key": secrets.apiKey }
697
+ });
698
+ const body = res.json ?? {};
699
+ if (res.status === 401 && body.detail?.status === "missing_permissions") {
700
+ return { status: "up", latencyMs: res.latencyMs, httpStatus: 401, metrics: { key: "restricted" } };
701
+ }
702
+ if (!res.ok) return res.outcome;
703
+ const metrics = body.tier ? { tier: body.tier } : void 0;
704
+ const { character_count: used, character_limit: limit } = body;
705
+ if (typeof used !== "number" || !limit) return { ...res.outcome, metrics };
706
+ return {
707
+ ...res.outcome,
708
+ ...quota({
709
+ label: "Credits",
710
+ used,
711
+ limit,
712
+ unit: "credits",
713
+ // ElevenLabs gives the reset as epoch seconds
714
+ resetsAt: body.next_character_count_reset_unix ? body.next_character_count_reset_unix * 1e3 : void 0,
715
+ warnAbovePct: 100 - (options.track?.quota?.warnBelowPct ?? 10),
716
+ warning: "Credits almost exhausted"
717
+ }),
718
+ metrics
719
+ };
720
+ }
721
+ });
467
722
 
468
723
  // src/presets/providers/translation.ts
469
724
  var deepl = definePreset()({
@@ -695,6 +950,64 @@ var statuspage = definePreset()({
695
950
  return { status, error: status === "up" ? void 0 : description };
696
951
  })
697
952
  });
953
+ var resend = definePreset()({
954
+ kind: "resend",
955
+ requiredSecrets: ["apiKey"],
956
+ derive: () => ({ name: "Resend", identity: "resend", description: "Lists domains" }),
957
+ check: async ({ secrets, http }) => {
958
+ const res = await http("https://api.resend.com/domains", { headers: bearer(secrets.apiKey) });
959
+ const body = res.json ?? {};
960
+ if (res.status === 401 && body.name === "restricted_api_key") {
961
+ return { status: "up", latencyMs: res.latencyMs, httpStatus: 401, metrics: { key: "sending only" } };
962
+ }
963
+ if (!res.ok || !Array.isArray(body.data)) return res.outcome;
964
+ const verified = body.data.filter((domain) => domain.status === "verified").length;
965
+ const unverified = body.data.length > 0 && verified === 0;
966
+ return {
967
+ ...res.outcome,
968
+ status: unverified ? "degraded" : "up",
969
+ error: unverified ? "No verified domain" : void 0,
970
+ metrics: { domains: body.data.length, verified }
971
+ };
972
+ }
973
+ });
974
+ var meilisearch = definePreset()({
975
+ kind: "meilisearch",
976
+ requiredFields: ["host"],
977
+ requiredSecrets: ["apiKey"],
978
+ derive: (options) => ({
979
+ name: "Meilisearch",
980
+ identity: options.index ? `meilisearch:${options.index}` : "meilisearch",
981
+ description: options.index ? `Reads the health and the ${options.index} index` : "Reads the health and lists indexes"
982
+ }),
983
+ check: async ({ options, secrets, http, combine }) => {
984
+ const host = options.host.replace(/\/+$/, "");
985
+ const headers = bearer(secrets.apiKey);
986
+ const index = options.index ? `/indexes/${encodeURIComponent(options.index)}` : void 0;
987
+ const health = call(
988
+ http,
989
+ `${host}/health`,
990
+ void 0,
991
+ (res2) => res2.json?.status === "available" ? void 0 : { status: "down", error: "Not available" }
992
+ );
993
+ const access = http(`${host}${index ?? "/indexes?limit=1"}`, { headers }).then((res2) => {
994
+ if (res2.status !== 404 || !options.index) return res2.outcome;
995
+ return {
996
+ status: "degraded",
997
+ latencyMs: res2.latencyMs,
998
+ httpStatus: 404,
999
+ error: `Index ${options.index} not found`
1000
+ };
1001
+ });
1002
+ const stats = http(`${host}${index ?? ""}/stats`, { headers });
1003
+ const outcome = combine(await Promise.all([health, access]));
1004
+ const res = await stats;
1005
+ if (!res.ok) return outcome;
1006
+ const body = res.json ?? {};
1007
+ const documents = body.numberOfDocuments ?? Object.values(body.indexes ?? {}).reduce((sum, entry) => sum + (entry.numberOfDocuments ?? 0), 0);
1008
+ return { ...outcome, metrics: { ...outcome.metrics, documents, ...body.isIndexing ? { indexing: "yes" } : {} } };
1009
+ }
1010
+ });
698
1011
 
699
1012
  // src/presets/providers/pusher.ts
700
1013
  var MAX_CHANNELS = 500;
@@ -778,12 +1091,12 @@ var pusher = definePreset()({
778
1091
  let countingDisabled = false;
779
1092
  const counts = await mapLimit(queried, CONCURRENCY, async (name) => {
780
1093
  const res = await get(`/channels/${encodeURIComponent(name)}`, { info: "subscription_count" });
781
- const count = res.json?.subscription_count;
1094
+ const count2 = res.json?.subscription_count;
782
1095
  if (!res.ok) failed += 1;
783
- else if (typeof count !== "number") countingDisabled = true;
784
- return typeof count === "number" ? count : 0;
1096
+ else if (typeof count2 !== "number") countingDisabled = true;
1097
+ return typeof count2 === "number" ? count2 : 0;
785
1098
  });
786
- const connections = counts.reduce((sum, count) => sum + count, 0);
1099
+ const connections = counts.reduce((sum, count2) => sum + count2, 0);
787
1100
  const signedInUsers = queried.reduce(
788
1101
  (sum, name, index) => name.startsWith("#server-to-user-") ? sum + (counts[index] ?? 0) : sum,
789
1102
  0
@@ -826,6 +1139,187 @@ var pusher = definePreset()({
826
1139
  }
827
1140
  });
828
1141
 
829
- export { anthropic, attendee, aws, axiom, azureTranslator, cambai, clearGoogleTokens, cloudflareStream, crashReason, deepgram, deepl, fly, gcp, gemini, github, gladia, googleServiceAccountToken, googleTranslate, mailersend, mongodb, mux, openai, parseServiceAccount, pusher, redis, revolut, signGoogleAssertion, signPusherRequest, speechmatics, statuspage, upstashRedis };
1142
+ // src/presets/providers/vercel.ts
1143
+ var aiGateway = definePreset()({
1144
+ kind: "vercel.aiGateway",
1145
+ requiredSecrets: ["apiKey"],
1146
+ derive: (options) => ({
1147
+ name: "Vercel AI Gateway",
1148
+ identity: "vercel.aiGateway",
1149
+ description: options.track?.credits === false ? "Lists models" : "Reads the credit balance"
1150
+ }),
1151
+ check: ({ options, secrets, http }) => {
1152
+ const headers = bearer(secrets.apiKey);
1153
+ if (options.track?.credits === false) return call(http, "https://ai-gateway.vercel.sh/v1/models", { headers });
1154
+ const warnBelow = options.track?.credits?.warnBelow ?? 5;
1155
+ return call(http, "https://ai-gateway.vercel.sh/v1/credits", { headers }, (res) => {
1156
+ const body = res.json ?? {};
1157
+ const balance = Number(body.balance);
1158
+ if (body.balance === void 0 || !Number.isFinite(balance)) return void 0;
1159
+ const used = Number(body.total_used);
1160
+ const low = balance < warnBelow;
1161
+ return {
1162
+ status: low ? "degraded" : "up",
1163
+ error: low ? "Low credit balance" : void 0,
1164
+ metrics: {
1165
+ balance: `${round(balance, 2)} USD`,
1166
+ ...body.total_used !== void 0 && Number.isFinite(used) ? { used: `${round(used, 2)} USD` } : {}
1167
+ }
1168
+ };
1169
+ });
1170
+ }
1171
+ });
1172
+ var blob = definePreset()({
1173
+ kind: "vercel.blob",
1174
+ requiredSecrets: ["token"],
1175
+ derive: () => ({ name: "Vercel Blob", identity: "vercel.blob", description: "Lists one blob of the store" }),
1176
+ check: ({ secrets, http }) => call(http, "https://vercel.com/api/blob?limit=1", {
1177
+ headers: { ...bearer(secrets.token), "x-api-version": "12" }
1178
+ })
1179
+ });
1180
+ var vercel = { aiGateway, blob };
1181
+
1182
+ // src/presets/providers/stripe.ts
1183
+ var API3 = "https://api.stripe.com/v1";
1184
+ var DAY_S = 24 * 60 * 60;
1185
+ var ZERO_DECIMAL = /* @__PURE__ */ new Set(["bif", "clp", "djf", "gnf", "jpy", "kmf", "krw", "mga", "pyg", "rwf", "ugx", "vnd", "vuv", "xaf", "xof", "xpf"]);
1186
+ function money(amounts) {
1187
+ return (amounts ?? []).slice(0, 3).map(({ amount, currency }) => {
1188
+ const value = ZERO_DECIMAL.has(currency) ? String(amount) : (amount / 100).toFixed(2);
1189
+ return `${value} ${currency.toUpperCase()}`;
1190
+ }).join(", ");
1191
+ }
1192
+ function endpointKey(url) {
1193
+ try {
1194
+ const { host, pathname } = new URL(url);
1195
+ return `${host}${pathname}`;
1196
+ } catch {
1197
+ return "endpoint";
1198
+ }
1199
+ }
1200
+ var count = (list) => `${list.data?.length ?? 0}${list.has_more ? "+" : ""}`;
1201
+ var stripe = definePreset()({
1202
+ kind: "stripe",
1203
+ requiredSecrets: ["apiKey"],
1204
+ derive: () => ({ name: "Stripe", identity: "stripe", description: "Reads the balance, account and webhook endpoints" }),
1205
+ check: async ({ options, secrets, http, now }) => {
1206
+ const { account = true, webhooks = true, disputes = true } = options.track ?? {};
1207
+ const get = (path) => http(`${API3}${path}`, { headers: bearer(secrets.apiKey) });
1208
+ const skip = Promise.resolve(void 0);
1209
+ const [balance, accountRes, endpoints, undelivered, disputeList] = await Promise.all([
1210
+ get("/balance"),
1211
+ account ? get("/account") : skip,
1212
+ webhooks ? get("/webhook_endpoints?limit=100") : skip,
1213
+ webhooks ? get(`/events?delivery_success=false&limit=100&created[gte]=${Math.floor(now / 1e3) - DAY_S}`) : skip,
1214
+ disputes ? get("/disputes?limit=100") : skip
1215
+ ]);
1216
+ if (!balance.ok && balance.status !== 403) return balance.outcome;
1217
+ const metrics = {};
1218
+ const warnings = [];
1219
+ const funds = balance.ok ? balance.json : void 0;
1220
+ if (funds) {
1221
+ metrics.mode = funds.livemode ? "live" : "test";
1222
+ if (funds.available?.length) metrics.available = money(funds.available);
1223
+ if (funds.pending?.length) metrics.pending = money(funds.pending);
1224
+ } else {
1225
+ metrics.key = "restricted";
1226
+ }
1227
+ if (accountRes?.ok && funds?.livemode) {
1228
+ const body = accountRes.json;
1229
+ if (body.charges_enabled === false) warnings.push("Charges are disabled");
1230
+ if (body.payouts_enabled === false) warnings.push("Payouts are disabled");
1231
+ const overdue = body.requirements?.past_due?.length ?? 0;
1232
+ if (overdue > 0) warnings.push(`${overdue} account requirement(s) past due`);
1233
+ }
1234
+ let instances;
1235
+ if (endpoints?.ok) {
1236
+ const list = endpoints.json.data ?? [];
1237
+ const seen = /* @__PURE__ */ new Set();
1238
+ instances = list.map((endpoint) => {
1239
+ let key = endpointKey(endpoint.url);
1240
+ for (let n = 2; seen.has(key); n++) key = `${endpointKey(endpoint.url)}#${n}`;
1241
+ seen.add(key);
1242
+ const enabled = endpoint.status === "enabled";
1243
+ return { key, id: endpoint.id, state: endpoint.status, status: enabled ? "up" : "down", error: enabled ? void 0 : "Endpoint disabled" };
1244
+ });
1245
+ metrics.webhookEndpoints = list.length;
1246
+ const disabled = instances.filter((instance2) => instance2.status === "down").length;
1247
+ if (disabled > 0) warnings.push(`${disabled} webhook endpoint(s) disabled`);
1248
+ }
1249
+ if (undelivered?.ok) {
1250
+ const list = undelivered.json;
1251
+ metrics.undelivered24h = count(list);
1252
+ if (list.data?.length) warnings.push(`${count(list)} event(s) not delivered in the last 24 h`);
1253
+ }
1254
+ if (disputeList?.ok) {
1255
+ const open = (disputeList.json.data ?? []).filter(
1256
+ (dispute) => dispute.status.endsWith("needs_response")
1257
+ );
1258
+ metrics.openDisputes = open.length;
1259
+ }
1260
+ return {
1261
+ status: warnings.length ? "degraded" : "up",
1262
+ latencyMs: balance.latencyMs,
1263
+ httpStatus: balance.status,
1264
+ error: warnings.join("; ") || void 0,
1265
+ metrics,
1266
+ instances: instances?.length ? instances : void 0,
1267
+ instanceLabels: { key: "Webhook endpoint", detail: "ID" }
1268
+ };
1269
+ }
1270
+ });
1271
+
1272
+ // src/presets/providers/search.ts
1273
+ var exa = definePreset()({
1274
+ kind: "exa",
1275
+ requiredSecrets: ["apiKey"],
1276
+ derive: () => ({ name: "Exa", identity: "exa", description: "Validates the API key (no search is run)" }),
1277
+ check: async ({ secrets, http }) => keyAccepted(await http("https://api.exa.ai/search", emptyPost({ "x-api-key": secrets.apiKey })))
1278
+ });
1279
+ var nimble = definePreset()({
1280
+ kind: "nimble",
1281
+ requiredSecrets: ["apiKey"],
1282
+ derive: () => ({ name: "Nimble", identity: "nimble", description: "Validates the API key (no search is run)" }),
1283
+ check: async ({ secrets, http }) => keyAccepted(await http("https://sdk.nimbleway.com/v1/search", emptyPost(bearer(secrets.apiKey))))
1284
+ });
1285
+ var tavily = definePreset()({
1286
+ kind: "tavily",
1287
+ requiredSecrets: ["apiKey"],
1288
+ derive: () => ({ name: "Tavily", identity: "tavily", description: "Reads the credits used this period" }),
1289
+ check: async ({ options, secrets, http }) => {
1290
+ const res = await http("https://api.tavily.com/usage", { headers: bearer(secrets.apiKey) });
1291
+ if (!res.ok) return res.outcome;
1292
+ const { key, account } = res.json ?? {};
1293
+ const metrics = account?.current_plan ? { plan: account.current_plan } : void 0;
1294
+ const [used, limit] = key?.limit ? [key.usage, key.limit] : [account?.plan_usage, account?.plan_limit];
1295
+ if (typeof used !== "number" || !limit) return { ...res.outcome, metrics };
1296
+ return {
1297
+ ...res.outcome,
1298
+ ...quota({
1299
+ label: "Credits",
1300
+ used,
1301
+ limit,
1302
+ unit: "credits",
1303
+ warnAbovePct: options.track?.quota?.warnAbovePct ?? 90,
1304
+ warning: "Credits almost exhausted"
1305
+ }),
1306
+ metrics
1307
+ };
1308
+ }
1309
+ });
1310
+ var emelia = definePreset()({
1311
+ kind: "emelia",
1312
+ requiredSecrets: ["apiKey"],
1313
+ derive: () => ({ name: "Emelia", identity: "emelia", description: "Validates the API key (no lookup is run)" }),
1314
+ check: async ({ options, secrets, http }) => keyAccepted(
1315
+ await http(
1316
+ `${(options.baseUrl ?? "https://api.emelia.io").replace(/\/+$/, "")}/tools/find/email`,
1317
+ // Emelia takes the bare key, without a scheme
1318
+ emptyPost({ Authorization: secrets.apiKey })
1319
+ )
1320
+ )
1321
+ });
1322
+
1323
+ export { anthropic, attendee, aws, axiom, azureTranslator, cambai, clearGoogleTokens, cloudflareStream, crashReason, deepgram, deepl, elevenlabs, emelia, exa, fly, gcp, gemini, github, gladia, googleServiceAccountToken, googleTranslate, mailersend, meilisearch, mongodb, mux, neon, nimble, openai, parseServiceAccount, postgres, pusher, redis, resend, revolut, signGoogleAssertion, signPusherRequest, speechmatics, statuspage, stripe, tavily, upstashRedis, vercel };
830
1324
  //# sourceMappingURL=index.js.map
831
1325
  //# sourceMappingURL=index.js.map