@rawdash/core 0.15.0 → 0.17.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 +1 -1
- package/dist/index.d.ts +183 -28
- package/dist/index.js +431 -60
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -291,8 +291,79 @@ function computeDelay(attempt, initialDelayMs, maxDelayMs) {
|
|
|
291
291
|
const jitter = base * 0.25 * Math.random();
|
|
292
292
|
return Math.min(base + jitter, maxDelayMs);
|
|
293
293
|
}
|
|
294
|
+
var MAX_VALUE_LEN = 120;
|
|
295
|
+
function truncate(s, max = MAX_VALUE_LEN) {
|
|
296
|
+
if (s.length <= max) {
|
|
297
|
+
return s;
|
|
298
|
+
}
|
|
299
|
+
return `${s.slice(0, max - 1)}\u2026`;
|
|
300
|
+
}
|
|
301
|
+
function formatValue(value) {
|
|
302
|
+
if (value === null) {
|
|
303
|
+
return "null";
|
|
304
|
+
}
|
|
305
|
+
if (value === void 0) {
|
|
306
|
+
return "";
|
|
307
|
+
}
|
|
308
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
309
|
+
return String(value);
|
|
310
|
+
}
|
|
311
|
+
if (typeof value === "string") {
|
|
312
|
+
const t = truncate(value);
|
|
313
|
+
if (/[\s"=]/.test(t)) {
|
|
314
|
+
return JSON.stringify(t);
|
|
315
|
+
}
|
|
316
|
+
return t;
|
|
317
|
+
}
|
|
318
|
+
if (typeof value === "bigint") {
|
|
319
|
+
return value.toString();
|
|
320
|
+
}
|
|
321
|
+
let json;
|
|
322
|
+
try {
|
|
323
|
+
json = JSON.stringify(value);
|
|
324
|
+
} catch {
|
|
325
|
+
json = void 0;
|
|
326
|
+
}
|
|
327
|
+
return truncate(json ?? String(value));
|
|
328
|
+
}
|
|
329
|
+
function formatLogFields(fields) {
|
|
330
|
+
if (!fields) {
|
|
331
|
+
return "";
|
|
332
|
+
}
|
|
333
|
+
const parts = [];
|
|
334
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
335
|
+
if (v === void 0) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
parts.push(`${k}=${formatValue(v)}`);
|
|
339
|
+
}
|
|
340
|
+
return parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
341
|
+
}
|
|
342
|
+
function formatLogLine(scope, event, fields) {
|
|
343
|
+
return `[${scope}] ${event}${formatLogFields(fields)}`;
|
|
344
|
+
}
|
|
345
|
+
function createDefaultConnectorLogger(opts) {
|
|
346
|
+
return {
|
|
347
|
+
info(event, fields) {
|
|
348
|
+
console.info(formatLogLine(opts.scope, event, fields));
|
|
349
|
+
},
|
|
350
|
+
warn(event, fields) {
|
|
351
|
+
console.warn(formatLogLine(opts.scope, event, fields));
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
var NOOP_LOGGER = {
|
|
356
|
+
info() {
|
|
357
|
+
},
|
|
358
|
+
warn() {
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
function noopConnectorLogger() {
|
|
362
|
+
return NOOP_LOGGER;
|
|
363
|
+
}
|
|
294
364
|
|
|
295
365
|
// src/secrets.ts
|
|
366
|
+
import { z } from "zod";
|
|
296
367
|
function secret(name) {
|
|
297
368
|
if (!/^[A-Z][A-Z0-9_]*$/.test(name)) {
|
|
298
369
|
throw new Error(
|
|
@@ -304,10 +375,31 @@ function secret(name) {
|
|
|
304
375
|
function isSecret(value) {
|
|
305
376
|
return typeof value === "object" && value !== null && "$secret" in value && typeof value.$secret === "string";
|
|
306
377
|
}
|
|
378
|
+
var secretRefSchema = z.strictObject({
|
|
379
|
+
$secret: z.string()
|
|
380
|
+
});
|
|
381
|
+
function withSecretRef(schema) {
|
|
382
|
+
return z.union([schema, secretRefSchema]);
|
|
383
|
+
}
|
|
307
384
|
var EnvSecretsResolver = class {
|
|
308
385
|
resolve(name) {
|
|
309
386
|
const env = globalThis.process?.env;
|
|
310
|
-
|
|
387
|
+
const raw = env?.[name];
|
|
388
|
+
if (raw === void 0) {
|
|
389
|
+
return void 0;
|
|
390
|
+
}
|
|
391
|
+
if (raw.length === 0) {
|
|
392
|
+
return raw;
|
|
393
|
+
}
|
|
394
|
+
const first = raw.charCodeAt(0);
|
|
395
|
+
if (first !== 123 && first !== 91) {
|
|
396
|
+
return raw;
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
return JSON.parse(raw);
|
|
400
|
+
} catch {
|
|
401
|
+
return raw;
|
|
402
|
+
}
|
|
311
403
|
}
|
|
312
404
|
};
|
|
313
405
|
function extractSecretNames(value) {
|
|
@@ -364,6 +456,7 @@ var BaseConnector = class {
|
|
|
364
456
|
creds;
|
|
365
457
|
rawCredInput;
|
|
366
458
|
ctx;
|
|
459
|
+
cachedLogger;
|
|
367
460
|
constructor(settings, creds, ctx) {
|
|
368
461
|
this.settings = settings;
|
|
369
462
|
this.rawCredInput = creds;
|
|
@@ -373,6 +466,12 @@ var BaseConnector = class {
|
|
|
373
466
|
this.ctx.secretsResolver ?? new EnvSecretsResolver()
|
|
374
467
|
) : {};
|
|
375
468
|
}
|
|
469
|
+
get logger() {
|
|
470
|
+
if (!this.cachedLogger) {
|
|
471
|
+
this.cachedLogger = this.ctx.logger ?? createDefaultConnectorLogger({ scope: this.id });
|
|
472
|
+
}
|
|
473
|
+
return this.cachedLogger;
|
|
474
|
+
}
|
|
376
475
|
request(req, opts) {
|
|
377
476
|
return request(req, {
|
|
378
477
|
resource: opts.resource,
|
|
@@ -405,6 +504,13 @@ var BaseConnector = class {
|
|
|
405
504
|
{ resource: opts.resource, requestId: opts.requestId }
|
|
406
505
|
);
|
|
407
506
|
}
|
|
507
|
+
isResourceEnabled(resource) {
|
|
508
|
+
const enabled = this.settings?.resources;
|
|
509
|
+
if (!enabled || enabled.length === 0) {
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
return enabled.includes(resource);
|
|
513
|
+
}
|
|
408
514
|
serializeConfig() {
|
|
409
515
|
const config = {
|
|
410
516
|
...this.settings
|
|
@@ -478,8 +584,44 @@ function defineConnector() {
|
|
|
478
584
|
}
|
|
479
585
|
|
|
480
586
|
// src/paginate-chunked.ts
|
|
587
|
+
function selectActivePhases(resourceToPhase, order, enabled) {
|
|
588
|
+
if (!enabled || enabled.length === 0) {
|
|
589
|
+
return [...order];
|
|
590
|
+
}
|
|
591
|
+
const want = /* @__PURE__ */ new Set();
|
|
592
|
+
for (const r of enabled) {
|
|
593
|
+
want.add(resourceToPhase(r));
|
|
594
|
+
}
|
|
595
|
+
return order.filter((p) => want.has(p));
|
|
596
|
+
}
|
|
597
|
+
function makeChunkedCursorGuard(phases) {
|
|
598
|
+
const phaseSet = new Set(phases);
|
|
599
|
+
return (value) => {
|
|
600
|
+
if (typeof value !== "object" || value === null) {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
const v = value;
|
|
604
|
+
if (typeof v.phase !== "string" || !phaseSet.has(v.phase)) {
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
if (v.page !== null && typeof v.page !== "string") {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
return true;
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
function truncateCursor(page) {
|
|
614
|
+
if (page === null || page === void 0) {
|
|
615
|
+
return void 0;
|
|
616
|
+
}
|
|
617
|
+
const s = typeof page === "string" ? page : JSON.stringify(page);
|
|
618
|
+
if (s.length <= 80) {
|
|
619
|
+
return s;
|
|
620
|
+
}
|
|
621
|
+
return `${s.slice(0, 79)}\u2026`;
|
|
622
|
+
}
|
|
481
623
|
async function paginateChunked(opts) {
|
|
482
|
-
const { phases, cursor, signal, fetchPage, writeBatch } = opts;
|
|
624
|
+
const { phases, cursor, signal, fetchPage, writeBatch, logger } = opts;
|
|
483
625
|
if (phases.length === 0) {
|
|
484
626
|
return { done: true };
|
|
485
627
|
}
|
|
@@ -489,6 +631,9 @@ async function paginateChunked(opts) {
|
|
|
489
631
|
for (let i = startIdx; i < phases.length; i++) {
|
|
490
632
|
const phase = phases[i];
|
|
491
633
|
let page = i === startIdx && hasKnownResumePhase ? cursor.page : null;
|
|
634
|
+
let pageCount = 0;
|
|
635
|
+
let itemCount = 0;
|
|
636
|
+
const phaseStart = Date.now();
|
|
492
637
|
while (true) {
|
|
493
638
|
if (signal?.aborted) {
|
|
494
639
|
return {
|
|
@@ -496,6 +641,7 @@ async function paginateChunked(opts) {
|
|
|
496
641
|
cursor: { phase, page }
|
|
497
642
|
};
|
|
498
643
|
}
|
|
644
|
+
pageCount += 1;
|
|
499
645
|
let items;
|
|
500
646
|
let next;
|
|
501
647
|
try {
|
|
@@ -507,12 +653,26 @@ async function paginateChunked(opts) {
|
|
|
507
653
|
cursor: { phase, page }
|
|
508
654
|
};
|
|
509
655
|
}
|
|
656
|
+
logger?.warn("fetch page failed", {
|
|
657
|
+
resource: phase,
|
|
658
|
+
page: pageCount,
|
|
659
|
+
cursor: truncateCursor(page),
|
|
660
|
+
error: err instanceof Error ? err.message : String(err)
|
|
661
|
+
});
|
|
510
662
|
return {
|
|
511
663
|
done: false,
|
|
512
664
|
cursor: { phase, page },
|
|
513
665
|
transientError: err
|
|
514
666
|
};
|
|
515
667
|
}
|
|
668
|
+
itemCount += items.length;
|
|
669
|
+
logger?.info("fetched page", {
|
|
670
|
+
resource: phase,
|
|
671
|
+
page: pageCount,
|
|
672
|
+
items: items.length,
|
|
673
|
+
cursor: truncateCursor(page),
|
|
674
|
+
next: truncateCursor(next)
|
|
675
|
+
});
|
|
516
676
|
try {
|
|
517
677
|
await writeBatch(phase, items, page);
|
|
518
678
|
} catch (err) {
|
|
@@ -522,6 +682,12 @@ async function paginateChunked(opts) {
|
|
|
522
682
|
cursor: { phase, page }
|
|
523
683
|
};
|
|
524
684
|
}
|
|
685
|
+
logger?.warn("write batch failed", {
|
|
686
|
+
resource: phase,
|
|
687
|
+
page: pageCount,
|
|
688
|
+
cursor: truncateCursor(page),
|
|
689
|
+
error: err instanceof Error ? err.message : String(err)
|
|
690
|
+
});
|
|
525
691
|
return {
|
|
526
692
|
done: false,
|
|
527
693
|
cursor: { phase, page },
|
|
@@ -533,20 +699,26 @@ async function paginateChunked(opts) {
|
|
|
533
699
|
}
|
|
534
700
|
page = next;
|
|
535
701
|
}
|
|
702
|
+
logger?.info("resource done", {
|
|
703
|
+
resource: phase,
|
|
704
|
+
pages: pageCount,
|
|
705
|
+
items: itemCount,
|
|
706
|
+
duration_ms: Date.now() - phaseStart
|
|
707
|
+
});
|
|
536
708
|
}
|
|
537
709
|
return { done: true };
|
|
538
710
|
}
|
|
539
711
|
|
|
540
712
|
// src/widget-schemas.ts
|
|
541
|
-
import { z } from "zod";
|
|
542
|
-
var shapeSchema =
|
|
713
|
+
import { z as z2 } from "zod";
|
|
714
|
+
var shapeSchema = z2.enum([
|
|
543
715
|
"event",
|
|
544
716
|
"entity",
|
|
545
717
|
"metric",
|
|
546
718
|
"edge",
|
|
547
719
|
"distribution"
|
|
548
720
|
]);
|
|
549
|
-
var aggFnSchema =
|
|
721
|
+
var aggFnSchema = z2.enum([
|
|
550
722
|
"count",
|
|
551
723
|
"sum",
|
|
552
724
|
"avg",
|
|
@@ -555,7 +727,7 @@ var aggFnSchema = z.enum([
|
|
|
555
727
|
"latest",
|
|
556
728
|
"first"
|
|
557
729
|
]);
|
|
558
|
-
var filterOperatorSchema =
|
|
730
|
+
var filterOperatorSchema = z2.enum([
|
|
559
731
|
"eq",
|
|
560
732
|
"neq",
|
|
561
733
|
"gt",
|
|
@@ -564,70 +736,70 @@ var filterOperatorSchema = z.enum([
|
|
|
564
736
|
"lte",
|
|
565
737
|
"contains"
|
|
566
738
|
]);
|
|
567
|
-
var filterConditionSchema =
|
|
568
|
-
field:
|
|
739
|
+
var filterConditionSchema = z2.object({
|
|
740
|
+
field: z2.string(),
|
|
569
741
|
op: filterOperatorSchema,
|
|
570
|
-
value:
|
|
742
|
+
value: z2.union([z2.string(), z2.number(), z2.boolean()])
|
|
571
743
|
});
|
|
572
|
-
var filterClauseSchema =
|
|
744
|
+
var filterClauseSchema = z2.union([
|
|
573
745
|
filterConditionSchema,
|
|
574
|
-
|
|
746
|
+
z2.object({ or: z2.array(filterConditionSchema) })
|
|
575
747
|
]);
|
|
576
|
-
var groupBySchema =
|
|
577
|
-
field:
|
|
578
|
-
granularity:
|
|
748
|
+
var groupBySchema = z2.object({
|
|
749
|
+
field: z2.string(),
|
|
750
|
+
granularity: z2.enum(["hour", "day", "week", "month"])
|
|
579
751
|
});
|
|
580
|
-
var computedMetricSchema =
|
|
581
|
-
connectorId:
|
|
752
|
+
var computedMetricSchema = z2.object({
|
|
753
|
+
connectorId: z2.string(),
|
|
582
754
|
shape: shapeSchema,
|
|
583
|
-
name:
|
|
584
|
-
entityType:
|
|
585
|
-
field:
|
|
755
|
+
name: z2.string().optional(),
|
|
756
|
+
entityType: z2.string().optional(),
|
|
757
|
+
field: z2.string().optional(),
|
|
586
758
|
fn: aggFnSchema,
|
|
587
|
-
window:
|
|
588
|
-
filter:
|
|
759
|
+
window: z2.string().optional(),
|
|
760
|
+
filter: z2.array(filterClauseSchema).optional(),
|
|
589
761
|
groupBy: groupBySchema.optional()
|
|
590
762
|
}).refine((m) => m.fn === "count" || m.field !== void 0, {
|
|
591
763
|
message: 'field is required unless fn is "count"',
|
|
592
764
|
path: ["field"]
|
|
593
765
|
});
|
|
594
|
-
var titleField =
|
|
595
|
-
var statWidgetSchema =
|
|
596
|
-
kind:
|
|
766
|
+
var titleField = z2.string().meta({ label: "Title", description: "Widget title." });
|
|
767
|
+
var statWidgetSchema = z2.object({
|
|
768
|
+
kind: z2.literal("stat"),
|
|
597
769
|
title: titleField,
|
|
598
770
|
metric: computedMetricSchema.meta({
|
|
599
771
|
label: "Metric",
|
|
600
772
|
description: "Computed metric definition."
|
|
601
773
|
}),
|
|
602
|
-
window:
|
|
603
|
-
compare:
|
|
774
|
+
window: z2.string().optional().meta({ label: "Window", description: "Time window, e.g. '7d'." }),
|
|
775
|
+
compare: z2.enum(["none", "previous-period"]).default("none").meta({ label: "Compare", description: "Comparison mode." })
|
|
604
776
|
});
|
|
605
|
-
var statusWidgetSchema =
|
|
606
|
-
kind:
|
|
777
|
+
var statusWidgetSchema = z2.object({
|
|
778
|
+
kind: z2.literal("status"),
|
|
607
779
|
title: titleField,
|
|
608
|
-
source:
|
|
780
|
+
source: z2.string().meta({
|
|
609
781
|
label: "Source",
|
|
610
782
|
description: "Connector or data source reference."
|
|
611
783
|
})
|
|
612
784
|
});
|
|
613
|
-
var timeseriesWidgetSchema =
|
|
614
|
-
kind:
|
|
785
|
+
var timeseriesWidgetSchema = z2.object({
|
|
786
|
+
kind: z2.literal("timeseries"),
|
|
615
787
|
title: titleField,
|
|
616
788
|
metric: computedMetricSchema.meta({
|
|
617
789
|
label: "Metric",
|
|
618
790
|
description: "Computed metric definition."
|
|
619
791
|
}),
|
|
620
|
-
window:
|
|
621
|
-
granularity:
|
|
792
|
+
window: z2.string().meta({ label: "Window", description: "Time window, e.g. '30d'." }),
|
|
793
|
+
granularity: z2.enum(["hour", "day", "week"]).default("day").meta({ label: "Granularity", description: "Time bucket size." })
|
|
622
794
|
});
|
|
623
|
-
var distributionWidgetSchema =
|
|
624
|
-
kind:
|
|
795
|
+
var distributionWidgetSchema = z2.object({
|
|
796
|
+
kind: z2.literal("distribution"),
|
|
625
797
|
title: titleField,
|
|
626
798
|
metric: computedMetricSchema.meta({
|
|
627
799
|
label: "Metric",
|
|
628
800
|
description: "Computed metric definition."
|
|
629
801
|
}),
|
|
630
|
-
window:
|
|
802
|
+
window: z2.string().meta({ label: "Window", description: "Time window, e.g. '7d'." })
|
|
631
803
|
});
|
|
632
804
|
var widgetSchemas = {
|
|
633
805
|
stat: statWidgetSchema,
|
|
@@ -635,7 +807,7 @@ var widgetSchemas = {
|
|
|
635
807
|
timeseries: timeseriesWidgetSchema,
|
|
636
808
|
distribution: distributionWidgetSchema
|
|
637
809
|
};
|
|
638
|
-
var widgetSchema =
|
|
810
|
+
var widgetSchema = z2.discriminatedUnion("kind", [
|
|
639
811
|
statWidgetSchema,
|
|
640
812
|
statusWidgetSchema,
|
|
641
813
|
timeseriesWidgetSchema,
|
|
@@ -837,9 +1009,9 @@ async function computeRetention(handle, config, nowMs = Date.now()) {
|
|
|
837
1009
|
}
|
|
838
1010
|
|
|
839
1011
|
// src/config-fields.ts
|
|
840
|
-
import { z as
|
|
1012
|
+
import { z as z3 } from "zod";
|
|
841
1013
|
function defineConfigFields(schema) {
|
|
842
|
-
if (!(schema instanceof
|
|
1014
|
+
if (!(schema instanceof z3.ZodObject)) {
|
|
843
1015
|
throw new Error(
|
|
844
1016
|
`configFields must be a Zod object schema (z.object({...})). Received: ${Object.prototype.toString.call(schema)}`
|
|
845
1017
|
);
|
|
@@ -847,6 +1019,88 @@ function defineConfigFields(schema) {
|
|
|
847
1019
|
return schema;
|
|
848
1020
|
}
|
|
849
1021
|
|
|
1022
|
+
// src/connector-doc.ts
|
|
1023
|
+
import { z as z4 } from "zod";
|
|
1024
|
+
var connectorCategorySchema = z4.enum([
|
|
1025
|
+
"engineering",
|
|
1026
|
+
"product",
|
|
1027
|
+
"analytics",
|
|
1028
|
+
"marketing",
|
|
1029
|
+
"sales",
|
|
1030
|
+
"support",
|
|
1031
|
+
"finance",
|
|
1032
|
+
"infrastructure",
|
|
1033
|
+
"security"
|
|
1034
|
+
]);
|
|
1035
|
+
var connectorDocSchema = z4.object({
|
|
1036
|
+
displayName: z4.string().min(1),
|
|
1037
|
+
category: connectorCategorySchema,
|
|
1038
|
+
tagline: z4.string().min(1),
|
|
1039
|
+
// Brand accent color (hex) for the connector's docs/landing card. The icon
|
|
1040
|
+
// itself is a committed `icon.svg` co-located in the connector package.
|
|
1041
|
+
brandColor: z4.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
|
|
1042
|
+
vendor: z4.object({
|
|
1043
|
+
name: z4.string().min(1),
|
|
1044
|
+
apiDocs: z4.url().optional(),
|
|
1045
|
+
website: z4.url().optional()
|
|
1046
|
+
}),
|
|
1047
|
+
auth: z4.object({
|
|
1048
|
+
summary: z4.string().min(1),
|
|
1049
|
+
setup: z4.array(z4.string().min(1))
|
|
1050
|
+
}),
|
|
1051
|
+
// Upstream API rate-limit / quota notes worth surfacing (e.g. "GA4 Data API:
|
|
1052
|
+
// 200k tokens/day per property"). Free text, rendered in docs.
|
|
1053
|
+
rateLimit: z4.string().min(1).optional(),
|
|
1054
|
+
// Operational caveats / out-of-scope notes (API ceilings, sampling, Cloud-only,
|
|
1055
|
+
// data revision windows, etc.).
|
|
1056
|
+
limitations: z4.array(z4.string().min(1)).optional()
|
|
1057
|
+
});
|
|
1058
|
+
function defineConnectorDoc(doc) {
|
|
1059
|
+
return connectorDocSchema.parse(doc);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// src/resource.ts
|
|
1063
|
+
var SHAPES = /* @__PURE__ */ new Set([
|
|
1064
|
+
"entity",
|
|
1065
|
+
"event",
|
|
1066
|
+
"metric",
|
|
1067
|
+
"edge",
|
|
1068
|
+
"distribution"
|
|
1069
|
+
]);
|
|
1070
|
+
function defineResources(defs) {
|
|
1071
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
1072
|
+
if (!name) {
|
|
1073
|
+
throw new Error("Resource name must be a non-empty string.");
|
|
1074
|
+
}
|
|
1075
|
+
if (!SHAPES.has(def.shape)) {
|
|
1076
|
+
throw new Error(
|
|
1077
|
+
`Resource "${name}" has invalid shape "${def.shape}". Expected one of: ${[...SHAPES].join(", ")}.`
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
if (!def.description || def.description.trim().length === 0) {
|
|
1081
|
+
throw new Error(`Resource "${name}" must have a non-empty description.`);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
return defs;
|
|
1085
|
+
}
|
|
1086
|
+
function schemasFromResources(defs) {
|
|
1087
|
+
const out = {};
|
|
1088
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
1089
|
+
if (!def.responses) {
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
for (const [tag, schema] of Object.entries(def.responses)) {
|
|
1093
|
+
if (out[tag]) {
|
|
1094
|
+
throw new Error(
|
|
1095
|
+
`Duplicate response schema tag "${tag}" (declared again on resource "${name}").`
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
out[tag] = schema;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return out;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
850
1104
|
// src/compute.ts
|
|
851
1105
|
function matchesCondition(record, cond) {
|
|
852
1106
|
const val = record[cond.field];
|
|
@@ -1101,6 +1355,77 @@ async function computeMetric(storage, metric) {
|
|
|
1101
1355
|
return computeAgg(sorted, metric.field, metric.fn);
|
|
1102
1356
|
}
|
|
1103
1357
|
|
|
1358
|
+
// src/backfill-window.ts
|
|
1359
|
+
var WINDOW_UNIT_MS = {
|
|
1360
|
+
h: 36e5,
|
|
1361
|
+
d: 864e5,
|
|
1362
|
+
w: 6048e5,
|
|
1363
|
+
m: 2592e6
|
|
1364
|
+
};
|
|
1365
|
+
function parseWindowMs2(window) {
|
|
1366
|
+
const match = /^(\d+)(h|d|w|m)$/.exec(window);
|
|
1367
|
+
if (!match) {
|
|
1368
|
+
return void 0;
|
|
1369
|
+
}
|
|
1370
|
+
const unitMs = WINDOW_UNIT_MS[match[2]];
|
|
1371
|
+
if (unitMs === void 0) {
|
|
1372
|
+
return void 0;
|
|
1373
|
+
}
|
|
1374
|
+
return parseInt(match[1]) * unitMs;
|
|
1375
|
+
}
|
|
1376
|
+
function widgetWindow(widget) {
|
|
1377
|
+
switch (widget.kind) {
|
|
1378
|
+
case "stat":
|
|
1379
|
+
return widget.window ?? widget.metric.window;
|
|
1380
|
+
case "timeseries":
|
|
1381
|
+
case "distribution":
|
|
1382
|
+
return widget.window;
|
|
1383
|
+
case "status":
|
|
1384
|
+
return void 0;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
function widgetReference(widget) {
|
|
1388
|
+
if (widget.kind === "status") {
|
|
1389
|
+
return { connectorName: widget.source, resourceName: void 0 };
|
|
1390
|
+
}
|
|
1391
|
+
return {
|
|
1392
|
+
connectorName: widget.metric.connectorId,
|
|
1393
|
+
resourceName: widget.metric.name ?? widget.metric.entityType
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
function mergeWindow(existing, next) {
|
|
1397
|
+
if (next === void 0) {
|
|
1398
|
+
return existing;
|
|
1399
|
+
}
|
|
1400
|
+
if (existing === void 0) {
|
|
1401
|
+
return next;
|
|
1402
|
+
}
|
|
1403
|
+
return Math.max(existing, next);
|
|
1404
|
+
}
|
|
1405
|
+
function computeConnectorBackfill(config) {
|
|
1406
|
+
const result = /* @__PURE__ */ new Map();
|
|
1407
|
+
for (const dashboard of Object.values(config.dashboards)) {
|
|
1408
|
+
for (const widget of Object.values(dashboard.widgets)) {
|
|
1409
|
+
const { connectorName, resourceName } = widgetReference(widget);
|
|
1410
|
+
const windowStr = widgetWindow(widget);
|
|
1411
|
+
const windowMs = windowStr ? parseWindowMs2(windowStr) : void 0;
|
|
1412
|
+
let resources = result.get(connectorName);
|
|
1413
|
+
if (!resources) {
|
|
1414
|
+
resources = /* @__PURE__ */ new Map();
|
|
1415
|
+
result.set(connectorName, resources);
|
|
1416
|
+
}
|
|
1417
|
+
if (resourceName === void 0) {
|
|
1418
|
+
continue;
|
|
1419
|
+
}
|
|
1420
|
+
const existing = resources.get(resourceName);
|
|
1421
|
+
resources.set(resourceName, {
|
|
1422
|
+
requiredWindowMs: mergeWindow(existing?.requiredWindowMs, windowMs)
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return result;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1104
1429
|
// src/resolve-widget.ts
|
|
1105
1430
|
var FAILING_CONNECTOR_STATUSES = /* @__PURE__ */ new Set(["error", "auth_failed", "paused"]);
|
|
1106
1431
|
function deriveSyncStateFromHealth(health) {
|
|
@@ -1124,14 +1449,17 @@ function buildMetaFromHealth(health) {
|
|
|
1124
1449
|
}
|
|
1125
1450
|
return meta;
|
|
1126
1451
|
}
|
|
1127
|
-
async function resolveWidget(widgetId, widget, connectors, storage) {
|
|
1452
|
+
async function resolveWidget(dashboardId, widgetId, widget, connectors, storage) {
|
|
1128
1453
|
const connectorId = widget.kind === "status" ? widget.source : widget.metric.connectorId;
|
|
1129
1454
|
if (connectors !== void 0 && !connectors.includes(connectorId)) {
|
|
1130
1455
|
return void 0;
|
|
1131
1456
|
}
|
|
1132
1457
|
const handle = storage.getStorageHandle(connectorId);
|
|
1133
1458
|
const health = await handle.getHealth?.() ?? null;
|
|
1134
|
-
|
|
1459
|
+
let data = null;
|
|
1460
|
+
if (widget.kind !== "status") {
|
|
1461
|
+
data = await computeMetric(handle, widget.metric);
|
|
1462
|
+
}
|
|
1135
1463
|
let syncState;
|
|
1136
1464
|
let meta;
|
|
1137
1465
|
if (health) {
|
|
@@ -1148,12 +1476,40 @@ async function resolveWidget(widgetId, widget, connectors, storage) {
|
|
|
1148
1476
|
data,
|
|
1149
1477
|
cachedAt: health?.lastSyncAt ?? null,
|
|
1150
1478
|
syncState,
|
|
1479
|
+
syncIntervalSeconds: health?.syncIntervalSeconds,
|
|
1151
1480
|
meta
|
|
1152
1481
|
};
|
|
1153
1482
|
}
|
|
1154
1483
|
|
|
1484
|
+
// src/widget-etag.ts
|
|
1485
|
+
function stableStringify(value) {
|
|
1486
|
+
if (value === null || typeof value !== "object") {
|
|
1487
|
+
return JSON.stringify(value) ?? "null";
|
|
1488
|
+
}
|
|
1489
|
+
if (Array.isArray(value)) {
|
|
1490
|
+
return "[" + value.map(stableStringify).join(",") + "]";
|
|
1491
|
+
}
|
|
1492
|
+
const keys = Object.keys(value).sort();
|
|
1493
|
+
const parts = keys.map(
|
|
1494
|
+
(k) => JSON.stringify(k) + ":" + stableStringify(value[k])
|
|
1495
|
+
);
|
|
1496
|
+
return "{" + parts.join(",") + "}";
|
|
1497
|
+
}
|
|
1498
|
+
function hashWidgetConfig(widget) {
|
|
1499
|
+
const s = stableStringify(widget);
|
|
1500
|
+
let h = 2166136261;
|
|
1501
|
+
for (let i = 0; i < s.length; i++) {
|
|
1502
|
+
h ^= s.charCodeAt(i);
|
|
1503
|
+
h = Math.imul(h, 16777619);
|
|
1504
|
+
}
|
|
1505
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
1506
|
+
}
|
|
1507
|
+
function computeWidgetEtag(lastSyncAt, widget) {
|
|
1508
|
+
return `"${lastSyncAt ?? "null"}-${hashWidgetConfig(widget)}"`;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1155
1511
|
// src/registry.ts
|
|
1156
|
-
function instantiateConnector(entry, registry, secretsResolver) {
|
|
1512
|
+
function instantiateConnector(entry, registry, secretsResolver, logger) {
|
|
1157
1513
|
const Cls = registry[entry.connectorId];
|
|
1158
1514
|
if (!Cls) {
|
|
1159
1515
|
throw new Error(
|
|
@@ -1171,7 +1527,8 @@ function instantiateConnector(entry, registry, secretsResolver) {
|
|
|
1171
1527
|
}
|
|
1172
1528
|
}
|
|
1173
1529
|
return new Cls(settings, credSchema ? creds : void 0, {
|
|
1174
|
-
secretsResolver
|
|
1530
|
+
secretsResolver,
|
|
1531
|
+
logger
|
|
1175
1532
|
});
|
|
1176
1533
|
}
|
|
1177
1534
|
|
|
@@ -1555,24 +1912,24 @@ var InMemoryStorage = class {
|
|
|
1555
1912
|
};
|
|
1556
1913
|
|
|
1557
1914
|
// src/wire-config.ts
|
|
1558
|
-
import { z as
|
|
1559
|
-
var wireConnectorSchema =
|
|
1560
|
-
name:
|
|
1561
|
-
connectorId:
|
|
1562
|
-
displayName:
|
|
1563
|
-
config:
|
|
1564
|
-
syncIntervalSeconds:
|
|
1565
|
-
enabled:
|
|
1915
|
+
import { z as z5 } from "zod";
|
|
1916
|
+
var wireConnectorSchema = z5.object({
|
|
1917
|
+
name: z5.string(),
|
|
1918
|
+
connectorId: z5.string(),
|
|
1919
|
+
displayName: z5.string().optional(),
|
|
1920
|
+
config: z5.record(z5.string(), z5.unknown()),
|
|
1921
|
+
syncIntervalSeconds: z5.number().optional(),
|
|
1922
|
+
enabled: z5.boolean().optional()
|
|
1566
1923
|
});
|
|
1567
|
-
var wireDashboardSchema =
|
|
1568
|
-
id:
|
|
1569
|
-
name:
|
|
1570
|
-
slug:
|
|
1571
|
-
config:
|
|
1924
|
+
var wireDashboardSchema = z5.object({
|
|
1925
|
+
id: z5.string().optional(),
|
|
1926
|
+
name: z5.string(),
|
|
1927
|
+
slug: z5.string(),
|
|
1928
|
+
config: z5.record(z5.string(), z5.unknown())
|
|
1572
1929
|
});
|
|
1573
|
-
var wireConfigSchema =
|
|
1574
|
-
connectors:
|
|
1575
|
-
dashboards:
|
|
1930
|
+
var wireConfigSchema = z5.object({
|
|
1931
|
+
connectors: z5.array(wireConnectorSchema).optional(),
|
|
1932
|
+
dashboards: z5.array(wireDashboardSchema).optional()
|
|
1576
1933
|
});
|
|
1577
1934
|
function toWireConfig(config) {
|
|
1578
1935
|
return {
|
|
@@ -1598,14 +1955,21 @@ export {
|
|
|
1598
1955
|
EnvSecretsResolver,
|
|
1599
1956
|
InMemoryStorage,
|
|
1600
1957
|
aggFnSchema,
|
|
1958
|
+
computeConnectorBackfill,
|
|
1601
1959
|
computeMetric,
|
|
1602
1960
|
computeRetention,
|
|
1961
|
+
computeWidgetEtag,
|
|
1603
1962
|
computedMetricSchema,
|
|
1963
|
+
connectorCategorySchema,
|
|
1964
|
+
connectorDocSchema,
|
|
1965
|
+
createDefaultConnectorLogger,
|
|
1604
1966
|
defineConfig,
|
|
1605
1967
|
defineConfigFields,
|
|
1606
1968
|
defineConnector,
|
|
1969
|
+
defineConnectorDoc,
|
|
1607
1970
|
defineDashboard,
|
|
1608
1971
|
defineMetric,
|
|
1972
|
+
defineResources,
|
|
1609
1973
|
distributionWidgetSchema,
|
|
1610
1974
|
extractSecretNames,
|
|
1611
1975
|
filterClauseSchema,
|
|
@@ -1613,13 +1977,19 @@ export {
|
|
|
1613
1977
|
filterOperatorSchema,
|
|
1614
1978
|
getWidgetSchema,
|
|
1615
1979
|
groupBySchema,
|
|
1980
|
+
hashWidgetConfig,
|
|
1616
1981
|
instantiateConnector,
|
|
1617
1982
|
isSecret,
|
|
1618
1983
|
isSyncActive,
|
|
1984
|
+
makeChunkedCursorGuard,
|
|
1985
|
+
noopConnectorLogger,
|
|
1619
1986
|
paginateChunked,
|
|
1620
1987
|
resolveSecrets,
|
|
1621
1988
|
resolveWidget,
|
|
1989
|
+
schemasFromResources,
|
|
1622
1990
|
secret,
|
|
1991
|
+
secretRefSchema,
|
|
1992
|
+
selectActivePhases,
|
|
1623
1993
|
selectForDeletion,
|
|
1624
1994
|
shapeSchema,
|
|
1625
1995
|
statWidgetSchema,
|
|
@@ -1631,6 +2001,7 @@ export {
|
|
|
1631
2001
|
wireConfigSchema,
|
|
1632
2002
|
wireConnectorSchema,
|
|
1633
2003
|
wireDashboardSchema,
|
|
1634
|
-
withAbortSignal
|
|
2004
|
+
withAbortSignal,
|
|
2005
|
+
withSecretRef
|
|
1635
2006
|
};
|
|
1636
2007
|
//# sourceMappingURL=index.js.map
|