@rawdash/core 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -50,13 +50,66 @@ var BaseConnector = class {
50
50
  credentials;
51
51
  settings;
52
52
  creds;
53
+ rawCredInput;
53
54
  constructor(settings, creds) {
54
55
  this.settings = settings;
56
+ this.rawCredInput = creds;
55
57
  this.creds = creds ? resolveSecretRefs(
56
58
  creds,
57
59
  new EnvSecretsResolver()
58
60
  ) : {};
59
61
  }
62
+ serializeConfig() {
63
+ const config = {
64
+ ...this.settings
65
+ };
66
+ if (this.rawCredInput) {
67
+ for (const [key, value] of Object.entries(
68
+ this.rawCredInput
69
+ )) {
70
+ if (value !== void 0) {
71
+ config[key] = value;
72
+ }
73
+ }
74
+ }
75
+ return config;
76
+ }
77
+ sleep(ms, signal) {
78
+ if (signal?.aborted) {
79
+ return Promise.reject(signal.reason ?? new Error("Aborted"));
80
+ }
81
+ return new Promise((resolve, reject) => {
82
+ const onAbort = () => {
83
+ clearTimeout(timer);
84
+ reject(signal.reason ?? new Error("Aborted"));
85
+ };
86
+ const timer = setTimeout(() => {
87
+ signal?.removeEventListener("abort", onAbort);
88
+ resolve();
89
+ }, ms);
90
+ signal?.addEventListener("abort", onAbort, { once: true });
91
+ });
92
+ }
93
+ async withRetry(fn, options) {
94
+ const {
95
+ maxAttempts = 10,
96
+ initialDelayMs = 1e3,
97
+ maxDelayMs = 1e4,
98
+ signal
99
+ } = options ?? {};
100
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
101
+ signal?.throwIfAborted();
102
+ const result = await fn(signal);
103
+ if (result.status === "done") {
104
+ return result.value;
105
+ }
106
+ if (attempt < maxAttempts - 1) {
107
+ const delay = Math.min(initialDelayMs * 2 ** attempt, maxDelayMs);
108
+ await this.sleep(delay, signal);
109
+ }
110
+ }
111
+ return null;
112
+ }
60
113
  };
61
114
  function defineConnector() {
62
115
  return function(def) {
@@ -80,32 +133,105 @@ function defineConnector() {
80
133
 
81
134
  // src/widget-schemas.ts
82
135
  import { z } from "zod";
83
- var widgetSchemas = {
84
- stat: z.object({
85
- title: z.string().meta({ label: "Title", description: "Widget title." }),
86
- metric: z.string().meta({ label: "Metric", description: "Metric reference." }),
87
- window: z.string().optional().meta({ label: "Window", description: "Time window, e.g. '7d'." }),
88
- compare: z.enum(["none", "previous-period"]).default("none").meta({ label: "Compare", description: "Comparison mode." })
136
+ var shapeSchema = z.enum([
137
+ "event",
138
+ "entity",
139
+ "metric",
140
+ "edge",
141
+ "distribution"
142
+ ]);
143
+ var aggFnSchema = z.enum([
144
+ "count",
145
+ "sum",
146
+ "avg",
147
+ "min",
148
+ "max",
149
+ "latest",
150
+ "first"
151
+ ]);
152
+ var filterOperatorSchema = z.enum([
153
+ "eq",
154
+ "neq",
155
+ "gt",
156
+ "gte",
157
+ "lt",
158
+ "lte",
159
+ "contains"
160
+ ]);
161
+ var filterConditionSchema = z.object({
162
+ field: z.string(),
163
+ op: filterOperatorSchema,
164
+ value: z.union([z.string(), z.number(), z.boolean()])
165
+ });
166
+ var filterClauseSchema = z.union([
167
+ filterConditionSchema,
168
+ z.object({ or: z.array(filterConditionSchema) })
169
+ ]);
170
+ var groupBySchema = z.object({
171
+ field: z.string(),
172
+ granularity: z.enum(["hour", "day", "week", "month"])
173
+ });
174
+ var resolvedMetricSchema = z.object({
175
+ connectorId: z.string(),
176
+ shape: shapeSchema,
177
+ name: z.string().optional(),
178
+ entityType: z.string().optional(),
179
+ field: z.string(),
180
+ fn: z.string(),
181
+ window: z.string().optional(),
182
+ filter: z.array(filterClauseSchema).optional(),
183
+ groupBy: groupBySchema.optional()
184
+ });
185
+ var titleField = z.string().meta({ label: "Title", description: "Widget title." });
186
+ var statWidgetSchema = z.object({
187
+ kind: z.literal("stat"),
188
+ title: titleField,
189
+ metric: resolvedMetricSchema.meta({
190
+ label: "Metric",
191
+ description: "Resolved metric definition."
89
192
  }),
90
- status: z.object({
91
- title: z.string().meta({ label: "Title", description: "Widget title." }),
92
- source: z.string().meta({
93
- label: "Source",
94
- description: "Connector or data source reference."
95
- })
193
+ window: z.string().optional().meta({ label: "Window", description: "Time window, e.g. '7d'." }),
194
+ compare: z.enum(["none", "previous-period"]).default("none").meta({ label: "Compare", description: "Comparison mode." })
195
+ });
196
+ var statusWidgetSchema = z.object({
197
+ kind: z.literal("status"),
198
+ title: titleField,
199
+ source: z.string().meta({
200
+ label: "Source",
201
+ description: "Connector or data source reference."
202
+ })
203
+ });
204
+ var timeseriesWidgetSchema = z.object({
205
+ kind: z.literal("timeseries"),
206
+ title: titleField,
207
+ metric: resolvedMetricSchema.meta({
208
+ label: "Metric",
209
+ description: "Resolved metric definition."
96
210
  }),
97
- timeseries: z.object({
98
- title: z.string().meta({ label: "Title", description: "Widget title." }),
99
- metric: z.string().meta({ label: "Metric", description: "Metric reference." }),
100
- window: z.string().meta({ label: "Window", description: "Time window, e.g. '30d'." }),
101
- granularity: z.enum(["hour", "day", "week"]).default("day").meta({ label: "Granularity", description: "Time bucket size." })
211
+ window: z.string().meta({ label: "Window", description: "Time window, e.g. '30d'." }),
212
+ granularity: z.enum(["hour", "day", "week"]).default("day").meta({ label: "Granularity", description: "Time bucket size." })
213
+ });
214
+ var distributionWidgetSchema = z.object({
215
+ kind: z.literal("distribution"),
216
+ title: titleField,
217
+ metric: resolvedMetricSchema.meta({
218
+ label: "Metric",
219
+ description: "Resolved metric definition."
102
220
  }),
103
- distribution: z.object({
104
- title: z.string().meta({ label: "Title", description: "Widget title." }),
105
- metric: z.string().meta({ label: "Metric", description: "Metric reference." }),
106
- window: z.string().meta({ label: "Window", description: "Time window, e.g. '7d'." })
107
- })
221
+ window: z.string().meta({ label: "Window", description: "Time window, e.g. '7d'." })
222
+ });
223
+ var widgetSchemas = {
224
+ stat: statWidgetSchema,
225
+ status: statusWidgetSchema,
226
+ timeseries: timeseriesWidgetSchema,
227
+ distribution: distributionWidgetSchema
108
228
  };
229
+ var widgetSchema = z.discriminatedUnion("kind", [
230
+ statWidgetSchema,
231
+ statusWidgetSchema,
232
+ timeseriesWidgetSchema,
233
+ distributionWidgetSchema
234
+ ]);
109
235
  function getWidgetSchema(kind) {
110
236
  return widgetSchemas[kind];
111
237
  }
@@ -120,17 +246,7 @@ function defineDashboard(options) {
120
246
  );
121
247
  }
122
248
  const schema = getWidgetSchema(widget.kind);
123
- const parseInput = { ...widget };
124
- if (widget.kind !== "status") {
125
- const m = widget.metric;
126
- if (typeof m !== "object" || m === null) {
127
- throw new Error(
128
- `Widget "${key}" (kind "${widget.kind}"): metric is required`
129
- );
130
- }
131
- parseInput.metric = "placeholder";
132
- }
133
- const result = schema.safeParse(parseInput);
249
+ const result = schema.safeParse(widget);
134
250
  if (!result.success) {
135
251
  throw new Error(
136
252
  `Widget "${key}" (kind "${widget.kind}"): ${result.error.issues.map((i) => i.message).join("; ")}`
@@ -283,20 +399,543 @@ function defineConfigFields(schema) {
283
399
  }
284
400
  return schema;
285
401
  }
402
+
403
+ // src/compute.ts
404
+ function matchesCondition(record, cond) {
405
+ const val = record[cond.field];
406
+ switch (cond.op) {
407
+ case "eq":
408
+ return val === cond.value;
409
+ case "neq":
410
+ return val !== cond.value;
411
+ case "gt":
412
+ if (typeof val !== "number" || typeof cond.value !== "number") {
413
+ return false;
414
+ }
415
+ return val > cond.value;
416
+ case "gte":
417
+ if (typeof val !== "number" || typeof cond.value !== "number") {
418
+ return false;
419
+ }
420
+ return val >= cond.value;
421
+ case "lt":
422
+ if (typeof val !== "number" || typeof cond.value !== "number") {
423
+ return false;
424
+ }
425
+ return val < cond.value;
426
+ case "lte":
427
+ if (typeof val !== "number" || typeof cond.value !== "number") {
428
+ return false;
429
+ }
430
+ return val <= cond.value;
431
+ case "contains":
432
+ return String(val).includes(String(cond.value));
433
+ default:
434
+ return false;
435
+ }
436
+ }
437
+ function applyFilter(record, filter) {
438
+ if (!filter) {
439
+ return true;
440
+ }
441
+ for (const clause of filter) {
442
+ if ("or" in clause) {
443
+ if (!clause.or.some((cond) => matchesCondition(record, cond))) {
444
+ return false;
445
+ }
446
+ } else {
447
+ if (!matchesCondition(record, clause)) {
448
+ return false;
449
+ }
450
+ }
451
+ }
452
+ return true;
453
+ }
454
+ var WINDOW_MS = {
455
+ h: 36e5,
456
+ d: 864e5,
457
+ w: 6048e5,
458
+ m: 2592e6
459
+ };
460
+ function parseWindowMs(window) {
461
+ const match = /^(\d+)(h|d|w|m)$/.exec(window);
462
+ if (!match) {
463
+ return null;
464
+ }
465
+ const unitMs = WINDOW_MS[match[2]];
466
+ if (unitMs === void 0) {
467
+ return null;
468
+ }
469
+ return parseInt(match[1]) * unitMs;
470
+ }
471
+ function truncateToGranularity(ts, granularity) {
472
+ const d = new Date(ts);
473
+ switch (granularity) {
474
+ case "hour":
475
+ d.setUTCMinutes(0, 0, 0);
476
+ return d.toISOString();
477
+ case "day":
478
+ d.setUTCHours(0, 0, 0, 0);
479
+ return d.toISOString().slice(0, 10);
480
+ case "week": {
481
+ d.setUTCDate(d.getUTCDate() - d.getUTCDay());
482
+ d.setUTCHours(0, 0, 0, 0);
483
+ return d.toISOString().slice(0, 10);
484
+ }
485
+ case "month":
486
+ d.setUTCDate(1);
487
+ d.setUTCHours(0, 0, 0, 0);
488
+ return d.toISOString().slice(0, 7);
489
+ default:
490
+ return d.toISOString().slice(0, 10);
491
+ }
492
+ }
493
+ function computeAgg(records, field, fn) {
494
+ if (fn === "count") {
495
+ return records.length;
496
+ }
497
+ if (fn === "latest") {
498
+ return records.at(-1)?.[field] ?? null;
499
+ }
500
+ if (fn === "first") {
501
+ return records[0]?.[field] ?? null;
502
+ }
503
+ const values = records.map((r) => r[field]).filter((v) => v !== void 0 && v !== null);
504
+ const nonNumeric = values.find((v) => typeof v !== "number");
505
+ if (nonNumeric !== void 0) {
506
+ throw new Error(
507
+ `computeAgg: fn "${fn}" requires numeric values for field "${field}", got ${typeof nonNumeric} (${String(nonNumeric)})`
508
+ );
509
+ }
510
+ const numbers = values;
511
+ if (fn === "sum") {
512
+ return numbers.reduce((a, b) => a + b, 0);
513
+ }
514
+ if (fn === "avg") {
515
+ return numbers.length > 0 ? numbers.reduce((a, b) => a + b, 0) / numbers.length : null;
516
+ }
517
+ if (fn === "min") {
518
+ return numbers.length > 0 ? numbers.reduce((a, b) => a < b ? a : b) : null;
519
+ }
520
+ if (fn === "max") {
521
+ return numbers.length > 0 ? numbers.reduce((a, b) => a > b ? a : b) : null;
522
+ }
523
+ return null;
524
+ }
525
+ function sortByTs(records, tsField) {
526
+ return [...records].sort((a, b) => {
527
+ return a[tsField] - b[tsField];
528
+ });
529
+ }
530
+ function computeGroupBy(records, metric, tsField) {
531
+ const { field, granularity } = metric.groupBy;
532
+ const groups = /* @__PURE__ */ new Map();
533
+ for (const record of records) {
534
+ const ts = record[field];
535
+ if (ts === void 0 || typeof ts !== "number") {
536
+ continue;
537
+ }
538
+ const key = truncateToGranularity(ts, granularity);
539
+ if (!groups.has(key)) {
540
+ groups.set(key, []);
541
+ }
542
+ groups.get(key).push(record);
543
+ }
544
+ return [...groups.entries()].map(([key, groupRecords]) => ({
545
+ date: key,
546
+ value: computeAgg(
547
+ sortByTs(groupRecords, tsField),
548
+ metric.field,
549
+ metric.fn
550
+ )
551
+ })).sort((a, b) => a.date < b.date ? -1 : 1);
552
+ }
553
+ function getTimestampField(shape) {
554
+ switch (shape) {
555
+ case "event":
556
+ return "start_ts";
557
+ case "metric":
558
+ case "distribution":
559
+ return "ts";
560
+ case "entity":
561
+ case "edge":
562
+ return "updated_at";
563
+ default:
564
+ return "start_ts";
565
+ }
566
+ }
567
+ async function computeMetric(storage, metric) {
568
+ const tsField = getTimestampField(metric.shape);
569
+ const windowMs = metric.window ? parseWindowMs(metric.window) : null;
570
+ const windowStart = windowMs !== null ? Date.now() - windowMs : void 0;
571
+ let records;
572
+ switch (metric.shape) {
573
+ case "event": {
574
+ const events = await storage.queryEvents({
575
+ name: metric.name,
576
+ start: windowStart
577
+ });
578
+ records = events.map((e) => ({
579
+ ...e.attributes,
580
+ name: e.name,
581
+ start_ts: e.start_ts,
582
+ end_ts: e.end_ts
583
+ }));
584
+ break;
585
+ }
586
+ case "entity": {
587
+ const type = metric.entityType ?? metric.name ?? "";
588
+ const entities = await storage.queryEntities({ type });
589
+ records = entities.map((e) => ({
590
+ ...e.attributes,
591
+ type: e.type,
592
+ id: e.id,
593
+ updated_at: e.updated_at
594
+ }));
595
+ if (windowStart !== void 0) {
596
+ records = records.filter((r) => r[tsField] >= windowStart);
597
+ }
598
+ break;
599
+ }
600
+ case "metric": {
601
+ const metrics = await storage.queryMetrics({
602
+ name: metric.name,
603
+ start: windowStart
604
+ });
605
+ records = metrics.map((m) => ({
606
+ ...m.attributes,
607
+ name: m.name,
608
+ ts: m.ts,
609
+ value: m.value
610
+ }));
611
+ break;
612
+ }
613
+ case "edge": {
614
+ const edges = await storage.traverse({ kind: metric.name });
615
+ records = edges.map((e) => ({
616
+ ...e.attributes,
617
+ from_type: e.from_type,
618
+ from_id: e.from_id,
619
+ kind: e.kind,
620
+ to_type: e.to_type,
621
+ to_id: e.to_id,
622
+ updated_at: e.updated_at
623
+ }));
624
+ if (windowStart !== void 0) {
625
+ records = records.filter((r) => r[tsField] >= windowStart);
626
+ }
627
+ break;
628
+ }
629
+ case "distribution": {
630
+ const distributions = await storage.queryDistributions({
631
+ name: metric.name,
632
+ start: windowStart
633
+ });
634
+ records = distributions.map((d) => ({
635
+ ...d.attributes,
636
+ name: d.name,
637
+ ts: d.ts,
638
+ kind: d.kind,
639
+ data: d.data
640
+ }));
641
+ break;
642
+ }
643
+ default:
644
+ return null;
645
+ }
646
+ const filtered = records.filter((r) => applyFilter(r, metric.filter));
647
+ const sorted = sortByTs(filtered, tsField);
648
+ if (metric.groupBy) {
649
+ return computeGroupBy(sorted, metric, tsField);
650
+ }
651
+ return computeAgg(sorted, metric.field, metric.fn);
652
+ }
653
+
654
+ // src/resolve-widget.ts
655
+ async function resolveWidget(id, widget, connectors, storage) {
656
+ if (widget.kind === "status") {
657
+ return {
658
+ id,
659
+ widgetId: id,
660
+ connectorId: widget.source,
661
+ data: null,
662
+ cachedAt: null
663
+ };
664
+ }
665
+ const { connectorId } = widget.metric;
666
+ const connectorEntry = connectors.find((e) => e.connector.id === connectorId);
667
+ if (!connectorEntry) {
668
+ return void 0;
669
+ }
670
+ const handle = storage.getStorageHandle(connectorId);
671
+ const data = await computeMetric(handle, widget.metric);
672
+ return {
673
+ id,
674
+ widgetId: id,
675
+ connectorId,
676
+ data,
677
+ cachedAt: (await storage.getSyncState()).lastSyncAt
678
+ };
679
+ }
680
+
681
+ // src/in-memory-storage.ts
682
+ var InMemoryStorage = class {
683
+ eventStore = /* @__PURE__ */ new Map();
684
+ entityStore = /* @__PURE__ */ new Map();
685
+ metricStore = /* @__PURE__ */ new Map();
686
+ edgeStore = /* @__PURE__ */ new Map();
687
+ distributionStore = /* @__PURE__ */ new Map();
688
+ syncState = {
689
+ status: "idle",
690
+ lastSyncAt: null,
691
+ lastError: null
692
+ };
693
+ getStorageHandle(connectorId) {
694
+ const getEntityMap = () => {
695
+ if (!this.entityStore.has(connectorId)) {
696
+ this.entityStore.set(connectorId, /* @__PURE__ */ new Map());
697
+ }
698
+ return this.entityStore.get(connectorId);
699
+ };
700
+ const upsertEntities = (es) => {
701
+ const byType = getEntityMap();
702
+ for (const e of es) {
703
+ if (!byType.has(e.type)) {
704
+ byType.set(e.type, /* @__PURE__ */ new Map());
705
+ }
706
+ byType.get(e.type).set(e.id, e);
707
+ }
708
+ };
709
+ const upsertEdges = (es) => {
710
+ const existing = this.edgeStore.get(connectorId) ?? [];
711
+ const index = /* @__PURE__ */ new Map();
712
+ for (let i = 0; i < existing.length; i++) {
713
+ const e = existing[i];
714
+ index.set(
715
+ `${e.from_type}:${e.from_id}:${e.kind}:${e.to_type}:${e.to_id}`,
716
+ i
717
+ );
718
+ }
719
+ for (const e of es) {
720
+ const key = `${e.from_type}:${e.from_id}:${e.kind}:${e.to_type}:${e.to_id}`;
721
+ const idx = index.get(key);
722
+ if (idx !== void 0) {
723
+ existing[idx] = e;
724
+ } else {
725
+ index.set(key, existing.length);
726
+ existing.push(e);
727
+ }
728
+ }
729
+ this.edgeStore.set(connectorId, existing);
730
+ };
731
+ return {
732
+ event: async (e) => {
733
+ if (!this.eventStore.has(connectorId)) {
734
+ this.eventStore.set(connectorId, []);
735
+ }
736
+ this.eventStore.get(connectorId).push(e);
737
+ },
738
+ entity: async (e) => {
739
+ upsertEntities([e]);
740
+ },
741
+ metric: async (m) => {
742
+ if (!this.metricStore.has(connectorId)) {
743
+ this.metricStore.set(connectorId, []);
744
+ }
745
+ this.metricStore.get(connectorId).push(m);
746
+ },
747
+ edge: async (e) => {
748
+ upsertEdges([e]);
749
+ },
750
+ distribution: async (d) => {
751
+ if (!this.distributionStore.has(connectorId)) {
752
+ this.distributionStore.set(connectorId, []);
753
+ }
754
+ this.distributionStore.get(connectorId).push(d);
755
+ },
756
+ events: async (es, scope) => {
757
+ const names = new Set(scope?.names ?? es.map((e) => e.name));
758
+ const kept = (this.eventStore.get(connectorId) ?? []).filter(
759
+ (e) => !names.has(e.name)
760
+ );
761
+ this.eventStore.set(connectorId, [...kept, ...es]);
762
+ },
763
+ entities: async (es, scope) => {
764
+ const byType = getEntityMap();
765
+ const types = new Set(scope?.types ?? es.map((e) => e.type));
766
+ for (const type of types) {
767
+ byType.set(type, /* @__PURE__ */ new Map());
768
+ }
769
+ upsertEntities(es);
770
+ },
771
+ metrics: async (ms, scope) => {
772
+ const names = new Set(scope?.names ?? ms.map((m) => m.name));
773
+ const kept = (this.metricStore.get(connectorId) ?? []).filter(
774
+ (m) => !names.has(m.name)
775
+ );
776
+ this.metricStore.set(connectorId, [...kept, ...ms]);
777
+ },
778
+ edges: async (es, scope) => {
779
+ const kinds = new Set(scope?.kinds ?? es.map((e) => e.kind));
780
+ const kept = (this.edgeStore.get(connectorId) ?? []).filter(
781
+ (e) => !kinds.has(e.kind)
782
+ );
783
+ this.edgeStore.set(connectorId, kept);
784
+ upsertEdges(es);
785
+ },
786
+ distributions: async (ds, scope) => {
787
+ const names = new Set(scope?.names ?? ds.map((d) => d.name));
788
+ const kept = (this.distributionStore.get(connectorId) ?? []).filter(
789
+ (d) => !names.has(d.name)
790
+ );
791
+ this.distributionStore.set(connectorId, [...kept, ...ds]);
792
+ },
793
+ queryEvents: async (q) => {
794
+ let results = this.eventStore.get(connectorId) ?? [];
795
+ if (q.name !== void 0) {
796
+ results = results.filter((e) => e.name === q.name);
797
+ }
798
+ if (q.start !== void 0) {
799
+ results = results.filter((e) => e.start_ts >= q.start);
800
+ }
801
+ if (q.end !== void 0) {
802
+ results = results.filter((e) => e.start_ts <= q.end);
803
+ }
804
+ return results;
805
+ },
806
+ getEntity: async (type, id) => {
807
+ return getEntityMap().get(type)?.get(id) ?? null;
808
+ },
809
+ queryEntities: async (q) => {
810
+ const byType = getEntityMap().get(q.type);
811
+ if (!byType) {
812
+ return [];
813
+ }
814
+ return Array.from(byType.values());
815
+ },
816
+ queryMetrics: async (q) => {
817
+ let results = this.metricStore.get(connectorId) ?? [];
818
+ if (q.name !== void 0) {
819
+ results = results.filter((m) => m.name === q.name);
820
+ }
821
+ if (q.start !== void 0) {
822
+ results = results.filter((m) => m.ts >= q.start);
823
+ }
824
+ if (q.end !== void 0) {
825
+ results = results.filter((m) => m.ts <= q.end);
826
+ }
827
+ return results;
828
+ },
829
+ traverse: async (q) => {
830
+ let results = this.edgeStore.get(connectorId) ?? [];
831
+ if (q.fromType !== void 0) {
832
+ results = results.filter((e) => e.from_type === q.fromType);
833
+ }
834
+ if (q.fromId !== void 0) {
835
+ results = results.filter((e) => e.from_id === q.fromId);
836
+ }
837
+ if (q.kind !== void 0) {
838
+ results = results.filter((e) => e.kind === q.kind);
839
+ }
840
+ if (q.toType !== void 0) {
841
+ results = results.filter((e) => e.to_type === q.toType);
842
+ }
843
+ if (q.toId !== void 0) {
844
+ results = results.filter((e) => e.to_id === q.toId);
845
+ }
846
+ return results;
847
+ },
848
+ queryDistributions: async (q) => {
849
+ let results = this.distributionStore.get(connectorId) ?? [];
850
+ if (q.name !== void 0) {
851
+ results = results.filter((d) => d.name === q.name);
852
+ }
853
+ if (q.start !== void 0) {
854
+ results = results.filter((d) => d.ts >= q.start);
855
+ }
856
+ if (q.end !== void 0) {
857
+ results = results.filter((d) => d.ts <= q.end);
858
+ }
859
+ return results;
860
+ },
861
+ deleteOlderThan: async (shape, tsUnixMs) => {
862
+ if (shape === "events") {
863
+ const before = this.eventStore.get(connectorId) ?? [];
864
+ const after = before.filter((e) => e.start_ts >= tsUnixMs);
865
+ this.eventStore.set(connectorId, after);
866
+ return { rowsDeleted: before.length - after.length };
867
+ } else if (shape === "metrics") {
868
+ const before = this.metricStore.get(connectorId) ?? [];
869
+ const after = before.filter((m) => m.ts >= tsUnixMs);
870
+ this.metricStore.set(connectorId, after);
871
+ return { rowsDeleted: before.length - after.length };
872
+ } else if (shape === "distributions") {
873
+ const before = this.distributionStore.get(connectorId) ?? [];
874
+ const after = before.filter((d) => d.ts >= tsUnixMs);
875
+ this.distributionStore.set(connectorId, after);
876
+ return { rowsDeleted: before.length - after.length };
877
+ } else {
878
+ throw new Error(
879
+ `Unsupported shape for deleteOlderThan: ${String(shape)}`
880
+ );
881
+ }
882
+ }
883
+ };
884
+ }
885
+ async getSyncState() {
886
+ return { ...this.syncState };
887
+ }
888
+ async setSyncing() {
889
+ if (this.syncState.status === "syncing") {
890
+ return false;
891
+ }
892
+ this.syncState = { ...this.syncState, status: "syncing" };
893
+ return true;
894
+ }
895
+ async setSyncSuccess() {
896
+ this.syncState = {
897
+ status: "idle",
898
+ lastSyncAt: (/* @__PURE__ */ new Date()).toISOString(),
899
+ lastError: null
900
+ };
901
+ }
902
+ async setSyncError(error) {
903
+ this.syncState = {
904
+ status: "error",
905
+ lastSyncAt: this.syncState.lastSyncAt,
906
+ lastError: error
907
+ };
908
+ }
909
+ };
286
910
  export {
287
911
  BaseConnector,
288
912
  EnvSecretsResolver,
913
+ InMemoryStorage,
914
+ aggFnSchema,
915
+ computeMetric,
289
916
  computeRetention,
290
917
  defineConfig,
291
918
  defineConfigFields,
292
919
  defineConnector,
293
920
  defineDashboard,
294
921
  defineMetric,
922
+ distributionWidgetSchema,
923
+ filterClauseSchema,
924
+ filterConditionSchema,
925
+ filterOperatorSchema,
295
926
  getWidgetSchema,
927
+ groupBySchema,
296
928
  isSecretRef,
297
929
  resolveSecretRefs,
930
+ resolveWidget,
931
+ resolvedMetricSchema,
298
932
  secret,
299
933
  selectForDeletion,
934
+ shapeSchema,
935
+ statWidgetSchema,
936
+ statusWidgetSchema,
937
+ timeseriesWidgetSchema,
938
+ widgetSchema,
300
939
  widgetSchemas
301
940
  };
302
941
  //# sourceMappingURL=index.js.map