@jeffjassky/telemetry 0.4.0 → 0.6.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
@@ -61,7 +61,13 @@ var newCounters = () => ({
61
61
  deduped: 0,
62
62
  truncated: 0,
63
63
  rollupSkippedBy: {},
64
- undeclaredAttrs: {}
64
+ undeclaredAttrs: {},
65
+ subjectsLinked: 0,
66
+ subjectLinkMisses: 0,
67
+ subjectLinkErrors: 0,
68
+ subjectLinkTimeouts: 0,
69
+ subjectLinkUndeclared: 0,
70
+ subjectLinkCapped: 0
65
71
  });
66
72
  var COUNTER_MAP_MAX = 1e3;
67
73
  var COUNTER_OVERFLOW_KEY = "(other)|(other)";
@@ -545,10 +551,11 @@ var truncate = (d, b) => {
545
551
  if (b === "day") return day;
546
552
  return new Date(day.getTime() - (day.getUTCDay() + 6) % 7 * 864e5);
547
553
  };
548
- async function recordRollup(RollupModel, doc, name, spec, counters) {
554
+ async function recordRollup(RollupModel, doc, name, spec, counters, opts) {
555
+ const dry = opts?.dryRun === true;
549
556
  if (spec.actors && doc.actor) {
550
557
  const actorType = String(doc.actor).split(":")[0];
551
- if (!spec.actors.includes(actorType)) return;
558
+ if (!spec.actors.includes(actorType)) return 0;
552
559
  }
553
560
  const as = spec.as ?? name;
554
561
  const at = doc.occurredAt;
@@ -560,9 +567,11 @@ async function recordRollup(RollupModel, doc, name, spec, counters) {
560
567
  let v = resolveDim(src, doc);
561
568
  if (v == null || v === "") {
562
569
  if (spec.dimDefault === void 0) {
563
- counters.rollupSkipped++;
564
- bumpCounterMap(counters.rollupSkippedBy, `${as}|${label(src)}`);
565
- return;
570
+ if (!dry) {
571
+ counters.rollupSkipped++;
572
+ bumpCounterMap(counters.rollupSkippedBy, `${as}|${label(src)}`);
573
+ }
574
+ return 0;
566
575
  }
567
576
  v = spec.dimDefault;
568
577
  }
@@ -572,48 +581,48 @@ async function recordRollup(RollupModel, doc, name, spec, counters) {
572
581
  const refs = fansOut ? (doc.subjectKeys ?? []).filter(
573
582
  (r) => !spec.subjects || spec.subjects.includes(r.split(":")[0])
574
583
  ) : [null];
575
- if (!refs.length) return;
584
+ if (!refs.length) return 0;
576
585
  const firstCapture = Object.fromEntries(
577
586
  (spec.capture ?? []).map((src) => [label(src), resolveDim(src, doc)]).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
578
587
  );
579
588
  const expiresAt = spec.retentionDays != null ? new Date(at.getTime() + spec.retentionDays * 864e5) : void 0;
580
- await RollupModel.bulkWrite(
581
- refs.map((ref) => {
582
- const dims = spec.by.map((src) => src === "subject" ? ref : fixed.get(src));
583
- const isNewFirst = {
584
- $or: [{ $eq: [{ $type: "$firstAt" }, "missing"] }, { $lt: [at, "$firstAt"] }]
585
- };
586
- const sums = Object.fromEntries(
587
- (spec.sum ?? []).map((k) => [k, doc.metrics?.get(k)]).filter(([, v]) => typeof v === "number").map(([k, v]) => [`sums.${k}`, { $add: [{ $ifNull: [`$sums.${k}`, 0] }, v] }])
588
- );
589
- return {
590
- updateOne: {
591
- filter: { _id: `${doc.tenantId}|${as}|${dims.join("|")}|${bucketKey}` },
592
- update: [
593
- {
594
- $set: {
595
- tenantId: doc.tenantId,
596
- as,
597
- dims,
598
- ...ref ? { subjectType: ref.split(":")[0] } : {},
599
- ...bucketAt ? { bucketAt } : {},
600
- ...expiresAt ? { expiresAt } : {},
601
- // aggregation $min/$max ignore missing, so correct on insert too
602
- firstAt: { $min: ["$firstAt", at] },
603
- lastAt: { $max: ["$lastAt", at] },
604
- count: { $add: [{ $ifNull: ["$count", 0] }, 1] },
605
- ...sums,
606
- firstTraceId: { $cond: [isNewFirst, doc.traceId ?? null, "$firstTraceId"] },
607
- firstCapture: { $cond: [isNewFirst, { $literal: firstCapture }, "$firstCapture"] }
608
- }
589
+ const ops = refs.map((ref) => {
590
+ const dims = spec.by.map((src) => src === "subject" ? ref : fixed.get(src));
591
+ const isNewFirst = {
592
+ $or: [{ $eq: [{ $type: "$firstAt" }, "missing"] }, { $lt: [at, "$firstAt"] }]
593
+ };
594
+ const sums = Object.fromEntries(
595
+ (spec.sum ?? []).map((k) => [k, doc.metrics?.get(k)]).filter(([, v]) => typeof v === "number").map(([k, v]) => [`sums.${k}`, { $add: [{ $ifNull: [`$sums.${k}`, 0] }, v] }])
596
+ );
597
+ return {
598
+ updateOne: {
599
+ filter: { _id: `${doc.tenantId}|${as}|${dims.join("|")}|${bucketKey}` },
600
+ update: [
601
+ {
602
+ $set: {
603
+ tenantId: doc.tenantId,
604
+ as,
605
+ dims,
606
+ ...ref ? { subjectType: ref.split(":")[0] } : {},
607
+ ...bucketAt ? { bucketAt } : {},
608
+ ...expiresAt ? { expiresAt } : {},
609
+ // aggregation $min/$max ignore missing, so correct on insert too
610
+ firstAt: { $min: ["$firstAt", at] },
611
+ lastAt: { $max: ["$lastAt", at] },
612
+ count: { $add: [{ $ifNull: ["$count", 0] }, 1] },
613
+ ...sums,
614
+ firstTraceId: { $cond: [isNewFirst, doc.traceId ?? null, "$firstTraceId"] },
615
+ firstCapture: { $cond: [isNewFirst, { $literal: firstCapture }, "$firstCapture"] }
609
616
  }
610
- ],
611
- upsert: true
612
- }
613
- };
614
- }),
615
- { ordered: false }
616
- );
617
+ }
618
+ ],
619
+ upsert: true
620
+ }
621
+ };
622
+ });
623
+ if (dry) return ops.length;
624
+ await RollupModel.bulkWrite(ops, { ordered: false });
625
+ return ops.length;
617
626
  }
618
627
  function buildCheckpointModel(connection, modelName, collection) {
619
628
  const existing = connection.models?.[modelName];
@@ -663,6 +672,110 @@ function noteUndeclaredAttrs(counters, name, spec, attrs) {
663
672
  bumpCounterMap(counters.undeclaredAttrs, `${name}|${key}`);
664
673
  }
665
674
  }
675
+ var SUBJECT_MAX = 8;
676
+ var SUBJECT_LINK_TIMEOUT_MS = 50;
677
+ var LINK_TIMEOUT = /* @__PURE__ */ Symbol("telemetry.subjectLink.timeout");
678
+ function createSubjectLinking(opts) {
679
+ const { linker, counters, logger } = opts;
680
+ if (!linker) return null;
681
+ const timeoutMs = opts.timeoutMs ?? SUBJECT_LINK_TIMEOUT_MS;
682
+ const warned = /* @__PURE__ */ new Set();
683
+ const warnOnce = (key, msg) => {
684
+ if (warned.has(key) || warned.size >= COUNTER_MAP_MAX) return;
685
+ warned.add(key);
686
+ logger.warn(msg);
687
+ };
688
+ return async function linkSubjects(name, spec, tenantId, declared) {
689
+ const have = Array.isArray(declared) ? declared : [];
690
+ const seen = /* @__PURE__ */ new Set();
691
+ const view = [];
692
+ for (const s of have) {
693
+ const ref = wellFormed(s);
694
+ if (!ref) continue;
695
+ seen.add(`${ref.type}:${ref.id}`);
696
+ view.push(ref);
697
+ }
698
+ let out;
699
+ let timer;
700
+ try {
701
+ out = await Promise.race([
702
+ // the async wrapper turns a SYNCHRONOUS throw into a rejection, so a
703
+ // linker that dies on its first line lands in the same catch as one
704
+ // whose promise rejects
705
+ (async () => linker.link(view, { name, tenantId }))(),
706
+ new Promise((_, reject) => {
707
+ timer = setTimeout(() => reject(LINK_TIMEOUT), timeoutMs);
708
+ })
709
+ ]);
710
+ } catch (e) {
711
+ if (e === LINK_TIMEOUT) {
712
+ counters.subjectLinkTimeouts++;
713
+ warnOnce(
714
+ "timeout",
715
+ `[telemetry] subjectLinker.link() exceeded ${timeoutMs}ms \u2014 records are being written UNLINKED rather than waiting. The hook is expected to answer from a cache; a resolver that queries per record cannot keep up with ingest. Warned once \u2014 the count is counters.subjectLinkTimeouts.`
716
+ );
717
+ } else {
718
+ counters.subjectLinkErrors++;
719
+ warnOnce(
720
+ "threw",
721
+ `[telemetry] subjectLinker.link() threw \u2014 records are being written unlinked: ${e}. Warned once \u2014 the count is counters.subjectLinkErrors.`
722
+ );
723
+ }
724
+ return null;
725
+ } finally {
726
+ clearTimeout(timer);
727
+ }
728
+ if (!Array.isArray(out)) {
729
+ counters.subjectLinkErrors++;
730
+ warnOnce(
731
+ "shape",
732
+ `[telemetry] subjectLinker.link() resolved to ${typeof out}, not an array \u2014 records are being written unlinked. Return [] when nothing links. Warned once \u2014 the count is counters.subjectLinkErrors.`
733
+ );
734
+ return null;
735
+ }
736
+ if (!out.length) {
737
+ counters.subjectLinkMisses++;
738
+ return null;
739
+ }
740
+ let room = Math.max(0, SUBJECT_MAX - have.length);
741
+ let capped2 = 0;
742
+ const add = [];
743
+ for (const s of out) {
744
+ const ref = wellFormed(s);
745
+ if (!ref) {
746
+ counters.subjectLinkErrors++;
747
+ warnOnce(
748
+ "entry",
749
+ "[telemetry] subjectLinker returned an entry that is not { type, id } \u2014 dropped. Warned once \u2014 the count is counters.subjectLinkErrors."
750
+ );
751
+ continue;
752
+ }
753
+ const key = `${ref.type}:${ref.id}`;
754
+ if (seen.has(key)) continue;
755
+ if (!spec.subjects.includes(ref.type)) {
756
+ counters.subjectLinkUndeclared++;
757
+ }
758
+ if (room <= 0) {
759
+ capped2++;
760
+ continue;
761
+ }
762
+ seen.add(key);
763
+ room--;
764
+ add.push(ref);
765
+ }
766
+ counters.subjectLinkCapped += capped2;
767
+ if (!add.length) return null;
768
+ counters.subjectsLinked += add.length;
769
+ return [...have, ...add];
770
+ };
771
+ }
772
+ function wellFormed(s) {
773
+ if (!s || typeof s !== "object") return null;
774
+ const { type, id, role } = s;
775
+ if (typeof type !== "string" || !type) return null;
776
+ if (typeof id !== "string" || !id) return null;
777
+ return typeof role === "string" && role ? { type, id, role } : { type, id };
778
+ }
666
779
  function createEmitter(ctx) {
667
780
  const { registry, byKind, RollupModel, rejects, counters } = ctx;
668
781
  const burstBuckets = /* @__PURE__ */ new Map();
@@ -699,11 +812,15 @@ function createEmitter(ctx) {
699
812
  const durable = kind === TelemetryKind.Usage || (doc.durable ?? spec.durable ?? false);
700
813
  const Model = byKind[kind];
701
814
  const { forceKeep: _drop, durable: _durable, ...rest } = doc;
815
+ const linked = ctx.linkSubjects ? await ctx.linkSubjects(name, spec, doc.tenantId, doc.subjects) : null;
702
816
  const safe = (o) => new Map(Object.entries(o ?? {}).map(([k, v]) => [k.replace(/\./g, "_"), v]));
703
817
  const payload = {
704
818
  ...rest,
705
819
  _id: id,
706
820
  name,
821
+ // computed like everything below it, and absent when nothing linked, so a
822
+ // host with no linker hands the model the exact object 0.4.0 did
823
+ ...linked ? { subjects: linked } : {},
707
824
  sampleRate: forced ? 1 : baseRate,
708
825
  forced,
709
826
  attrs: safe(doc.attrs),
@@ -869,6 +986,109 @@ function createForget(ctx) {
869
986
  };
870
987
  }
871
988
 
989
+ // src/server/relink.ts
990
+ var RELINK_BATCH_SIZE = 500;
991
+ var refKey = (s) => `${s.type}:${s.id}`;
992
+ function createRelink(ctx) {
993
+ const { registry, TelemetryModel, RollupModel, counters, logger, linkSubjects } = ctx;
994
+ return async function relink(opts = {}) {
995
+ const dryRun = opts.dryRun !== false;
996
+ const batchSize = opts.batchSize ?? RELINK_BATCH_SIZE;
997
+ const result = {
998
+ examined: 0,
999
+ linked: 0,
1000
+ subjects: 0,
1001
+ rollups: 0,
1002
+ misses: 0,
1003
+ errors: 0,
1004
+ skipped: 0
1005
+ };
1006
+ if (opts.names) {
1007
+ const unknown = opts.names.filter((n) => !registry[n]);
1008
+ if (unknown.length) {
1009
+ throw new Error(
1010
+ `telemetry: relink() was given names this registry does not declare: ${unknown.join(", ")}. A name with no spec has no rollup families to replay, so relinking it could only desynchronize its rows from its aggregates. Omit \`names\` to sweep everything.`
1011
+ );
1012
+ }
1013
+ }
1014
+ if (!linkSubjects) {
1015
+ logger.warn(
1016
+ "[telemetry] relink() has no subjectLinker to ask \u2014 nothing was read and nothing was written. Configure createTelemetry({ subjectLinker }) first; relink() backfills what that hook would have done, it does not replace it."
1017
+ );
1018
+ result.skipped = 1;
1019
+ return result;
1020
+ }
1021
+ if (opts.limit != null && opts.limit <= 0) return result;
1022
+ const filter = {};
1023
+ if (opts.names) filter.name = { $in: opts.names };
1024
+ if (opts.since) filter.occurredAt = { $gte: opts.since };
1025
+ const query = TelemetryModel.find(filter).hint({ _id: 1 }).batchSize(batchSize);
1026
+ if (opts.limit != null) query.limit(opts.limit);
1027
+ const progress = () => {
1028
+ if (!opts.onProgress) return;
1029
+ try {
1030
+ opts.onProgress({ ...result });
1031
+ } catch (e) {
1032
+ logger.warn(`[telemetry] relink() onProgress threw \u2014 ignored, the backfill continues: ${e}`);
1033
+ }
1034
+ };
1035
+ const cursor = query.cursor();
1036
+ let sinceProgress = 0;
1037
+ try {
1038
+ for await (const doc of cursor) {
1039
+ result.examined++;
1040
+ sinceProgress++;
1041
+ if (sinceProgress >= batchSize) {
1042
+ sinceProgress = 0;
1043
+ progress();
1044
+ }
1045
+ const spec = registry[doc.name];
1046
+ if (!spec) {
1047
+ result.skipped++;
1048
+ continue;
1049
+ }
1050
+ const declared = (doc.subjects ?? []).map(
1051
+ (s) => s.role ? { type: s.type, id: s.id, role: s.role } : { type: s.type, id: s.id }
1052
+ );
1053
+ const m0 = counters.subjectLinkMisses;
1054
+ const e0 = counters.subjectLinkErrors + counters.subjectLinkTimeouts;
1055
+ const merged = await linkSubjects(doc.name, spec, doc.tenantId, declared);
1056
+ result.misses += counters.subjectLinkMisses - m0;
1057
+ if (counters.subjectLinkErrors + counters.subjectLinkTimeouts > e0) result.errors++;
1058
+ if (!merged) continue;
1059
+ const had = new Set(declared.map(refKey));
1060
+ const mergedKeys = [];
1061
+ const newKeys = [];
1062
+ for (const s of merged) {
1063
+ const key = refKey(s);
1064
+ if (mergedKeys.includes(key)) continue;
1065
+ mergedKeys.push(key);
1066
+ if (!had.has(key)) newKeys.push(key);
1067
+ }
1068
+ if (!newKeys.length) continue;
1069
+ result.linked++;
1070
+ result.subjects += newKeys.length;
1071
+ if (!dryRun) {
1072
+ await TelemetryModel.updateOne(
1073
+ { _id: doc._id },
1074
+ { $set: { subjects: merged, subjectKeys: mergedKeys } }
1075
+ );
1076
+ }
1077
+ doc.subjectKeys = newKeys;
1078
+ for (const r of spec.rollups ?? []) {
1079
+ if (!r.by.includes("subject")) continue;
1080
+ result.rollups += await recordRollup(RollupModel, doc, doc.name, r, counters, { dryRun });
1081
+ }
1082
+ }
1083
+ } finally {
1084
+ await cursor.close().catch(() => {
1085
+ });
1086
+ }
1087
+ if (sinceProgress) progress();
1088
+ return result;
1089
+ };
1090
+ }
1091
+
872
1092
  // src/server/indexes.ts
873
1093
  var INDEX_BUDGET = 24;
874
1094
  function createSyncIndexes(ctx) {
@@ -1212,6 +1432,8 @@ function createIngest(opts) {
1212
1432
  const occurredAt = Number.isFinite(occurredRaw) ? new Date(occurredRaw - clockSkewMs) : receivedAt;
1213
1433
  const safeMap = (o) => o && typeof o === "object" ? new Map(Object.entries(o).map(([k, v]) => [k.replace(/\./g, "_"), v])) : /* @__PURE__ */ new Map();
1214
1434
  noteUndeclaredAttrs(t.counters, name, spec, rec.attrs);
1435
+ const subjects = mergeSubjects(rec.subjects);
1436
+ const linked = t.linkSubjects ? await t.linkSubjects(name, spec, tenantId, subjects) : null;
1215
1437
  const Model = t.models.byKind[spec.kind];
1216
1438
  const d = new Model({
1217
1439
  // facts the wire may not assert: tenant, service, env, origin, plane
@@ -1222,7 +1444,7 @@ function createIngest(opts) {
1222
1444
  tenantId,
1223
1445
  occurredAt,
1224
1446
  severity: typeof rec.severity === "string" ? rec.severity : void 0,
1225
- subjects: mergeSubjects(rec.subjects),
1447
+ subjects: linked ?? subjects,
1226
1448
  actor: ctx.actor ?? (typeof rec.actor === "string" ? rec.actor : batchActor),
1227
1449
  onBehalfOf: typeof rec.onBehalfOf === "string" ? rec.onBehalfOf : void 0,
1228
1450
  service: key.service,
@@ -3625,7 +3847,22 @@ function createTelemetry(config) {
3625
3847
  inFlight.add(p);
3626
3848
  void p.finally(() => inFlight.delete(p));
3627
3849
  };
3628
- const emit = createEmitter({ registry, byKind, RollupModel, rejects, counters, logger, track });
3850
+ const linkSubjects = createSubjectLinking({
3851
+ linker: config.subjectLinker,
3852
+ timeoutMs: config.subjectLinkTimeoutMs,
3853
+ counters,
3854
+ logger
3855
+ });
3856
+ const emit = createEmitter({
3857
+ registry,
3858
+ byKind,
3859
+ RollupModel,
3860
+ rejects,
3861
+ counters,
3862
+ logger,
3863
+ track,
3864
+ linkSubjects
3865
+ });
3629
3866
  const forget = createForget({
3630
3867
  TelemetryModel,
3631
3868
  RollupModel,
@@ -3643,6 +3880,14 @@ function createTelemetry(config) {
3643
3880
  },
3644
3881
  globalSubjectRefs: () => config.globalSubjectRefs === true
3645
3882
  });
3883
+ const relink = createRelink({
3884
+ registry,
3885
+ TelemetryModel,
3886
+ RollupModel,
3887
+ counters,
3888
+ logger,
3889
+ linkSubjects
3890
+ });
3646
3891
  const syncIndexes = createSyncIndexes({
3647
3892
  registry,
3648
3893
  TelemetryModel,
@@ -3654,6 +3899,21 @@ function createTelemetry(config) {
3654
3899
  emit,
3655
3900
  /** erasure: delete sole-party rows, redact shared ones, rekey rollups, drop aliases */
3656
3901
  forget,
3902
+ /**
3903
+ * Backfill: re-ask the `subjectLinker` about records ALREADY on disk, and
3904
+ * replay the rollups the new subjects reach.
3905
+ *
3906
+ * Linking happens at write time, so configuring it fixes the future and
3907
+ * nothing else — a lifetime `by:['subject']` family is keyed on the subject
3908
+ * the record was written with, and a read-time join cannot reach back into
3909
+ * it. This is how a host catches up the backlog it adopted the hook with.
3910
+ *
3911
+ * DRY RUN BY DEFAULT: it rewrites historical aggregates, so the short call
3912
+ * reports and the writing call says `{ dryRun: false }`. Idempotent by
3913
+ * construction — a row that already carries the linked subject yields
3914
+ * nothing new, so a second run is a no-op.
3915
+ */
3916
+ relink,
3657
3917
  /**
3658
3918
  * Tenant scope is not optional — force every read through here. The five
3659
3919
  * dashboard query primitives (records/series/distribution/rollups/journey)
@@ -3688,6 +3948,17 @@ function createTelemetry(config) {
3688
3948
  counters,
3689
3949
  /** the registry, exposed for the router factories — hosts should import their own */
3690
3950
  registry,
3951
+ /**
3952
+ * Write-time subject linking, exposed for the router factories. `null` when
3953
+ * no `subjectLinker` is configured.
3954
+ *
3955
+ * The wire path does not go through emit() — createIngest() builds its
3956
+ * record itself, because at-least-once delivery inverts the plane order
3957
+ * (insert first, THEN aggregate). So it reaches the linker the same way it
3958
+ * reaches the registry and the models: off the instance, running the one
3959
+ * implementation, rather than growing a second copy of the rules.
3960
+ */
3961
+ linkSubjects,
3691
3962
  logger,
3692
3963
  /** mint an ingest key; the full key string is returned once, never again */
3693
3964
  createKey: (input) => createKey(KeyModel, input),
@@ -3704,6 +3975,6 @@ function createTelemetry(config) {
3704
3975
  };
3705
3976
  }
3706
3977
 
3707
- export { BODY_MAX_CHARS, COUNTER_MAP_MAX, COUNTER_OVERFLOW_KEY, DEFAULT_LIMITS, Env, INDEX_BUDGET, KeyKind, LogLevel, MAX_SUGGESTIONS, Origin, PLATFORM_SCOPE, RETENTION_DAYS, SAMPLE_RATE, SCHEMA_VERSION, TelemetryKind, TenantMode, boundedMeta, createDashboard, createIngest, createKey, createQueries, createTelemetry, createValues, defaultSpaDir, defineRegistry, deriveCatalog, deriveSuggestions, deriveViews, executeReport, findFamily, foldRollups, hashSecret, intervalForRange, isPlatformScope, median, newId, normalizeQuery, parseKeyString, parseReportQuery, plain, projectRegistry, rangeOf, reportToQuery, requireMilestoneFamily, resolveDim, resolveReport, summarizeStages, traceKeep, truncate, validateRegistry, verifySecret };
3978
+ export { BODY_MAX_CHARS, COUNTER_MAP_MAX, COUNTER_OVERFLOW_KEY, DEFAULT_LIMITS, Env, INDEX_BUDGET, KeyKind, LogLevel, MAX_SUGGESTIONS, Origin, PLATFORM_SCOPE, RELINK_BATCH_SIZE, RETENTION_DAYS, SAMPLE_RATE, SCHEMA_VERSION, SUBJECT_LINK_TIMEOUT_MS, SUBJECT_MAX, TelemetryKind, TenantMode, boundedMeta, createDashboard, createIngest, createKey, createQueries, createTelemetry, createValues, defaultSpaDir, defineRegistry, deriveCatalog, deriveSuggestions, deriveViews, executeReport, findFamily, foldRollups, hashSecret, intervalForRange, isPlatformScope, median, newId, normalizeQuery, parseKeyString, parseReportQuery, plain, projectRegistry, rangeOf, reportToQuery, requireMilestoneFamily, resolveDim, resolveReport, summarizeStages, traceKeep, truncate, validateRegistry, verifySecret };
3708
3979
  //# sourceMappingURL=index.js.map
3709
3980
  //# sourceMappingURL=index.js.map