@powerhousedao/analytics-engine-core 6.2.3-dev.7 → 6.2.3-dev.70

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.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { AnalyticsDimension, AnalyticsGranularity, AnalyticsPath, AnalyticsPathSegment, AnalyticsPeriod, AnalyticsQuery, AnalyticsSerializerTypes, AnalyticsSeries, AnalyticsSeriesInput, AnalyticsSeriesQuery, AnalyticsUpdateCallback, CompoundAnalyticsQuery, IAnalyticsStore, MultiCurrencyConversion } from "@powerhousedao/shared/analytics";
2
2
  import { DateTime } from "luxon";
3
-
4
3
  //#region src/AnalyticsProfiler.d.ts
5
4
  interface IAnalyticsProfiler {
6
5
  get prefix(): string;
@@ -10,7 +9,7 @@ interface IAnalyticsProfiler {
10
9
  record: <T>(metric: string, fn: () => Promise<T>) => Promise<T>;
11
10
  recordSync: <T>(metric: string, fn: () => T) => T;
12
11
  }
13
- declare class AnalyticsProfiler implements IAnalyticsProfiler {
12
+ export declare class AnalyticsProfiler implements IAnalyticsProfiler {
14
13
  private readonly _ns;
15
14
  private readonly _logger;
16
15
  private readonly _stack;
@@ -24,7 +23,7 @@ declare class AnalyticsProfiler implements IAnalyticsProfiler {
24
23
  recordSync<T>(metric: string, fn: () => T): T;
25
24
  updatePrefix(): void;
26
25
  }
27
- declare class PassthroughAnalyticsProfiler implements IAnalyticsProfiler {
26
+ export declare class PassthroughAnalyticsProfiler implements IAnalyticsProfiler {
28
27
  get prefix(): string;
29
28
  push(_system: string): void;
30
29
  pop(): void;
@@ -52,7 +51,7 @@ type GroupedPeriodResult = {
52
51
  type GroupedPeriodResults = Array<GroupedPeriodResult>;
53
52
  //#endregion
54
53
  //#region src/AnalyticsQueryEngine.d.ts
55
- declare class AnalyticsQueryEngine {
54
+ export declare class AnalyticsQueryEngine {
56
55
  private readonly _analyticsStore;
57
56
  private readonly _profiler;
58
57
  constructor(_analyticsStore: IAnalyticsStore, profiler?: IAnalyticsProfiler);
@@ -69,14 +68,14 @@ declare class AnalyticsQueryEngine {
69
68
  }
70
69
  //#endregion
71
70
  //#region src/AnalyticsSubscriptionManager.d.ts
72
- declare class NotificationError extends Error {
71
+ export declare class NotificationError extends Error {
73
72
  readonly innerErrors: Error[];
74
73
  constructor(errors: Error[]);
75
74
  }
76
75
  /**
77
76
  * Manages subscriptions for analytics paths.
78
77
  */
79
- declare class AnalyticsSubscriptionManager {
78
+ export declare class AnalyticsSubscriptionManager {
80
79
  private _subscriptions;
81
80
  /**
82
81
  * Subscribe to updates for an analytics path. A subscribed function will be
@@ -123,8 +122,9 @@ interface ISqlExecutor {
123
122
  }
124
123
  //#endregion
125
124
  //#region src/util.d.ts
126
- declare const defaultQueryLogger: (tag: string) => SqlQueryLogger;
127
- declare const defaultResultsLogger: (tag: string) => SqlResultsLogger;
125
+ export declare const defaultQueryLogger: (tag: string) => SqlQueryLogger;
126
+ export declare const defaultResultsLogger: (tag: string) => SqlResultsLogger;
128
127
  //#endregion
129
- export { type AnalyticsDimension, AnalyticsGranularity, AnalyticsPath, AnalyticsPathSegment, AnalyticsPeriod, AnalyticsProfiler, type AnalyticsQuery, AnalyticsQueryEngine, AnalyticsSerializerTypes, type AnalyticsSeries, type AnalyticsSeriesInput, type AnalyticsSeriesQuery, AnalyticsSubscriptionManager, type AnalyticsUpdateCallback, type GroupedPeriodResult, type GroupedPeriodResults, type IAnalyticsProfiler, type IAnalyticsStore, ISqlExecutor, NotificationError, PassthroughAnalyticsProfiler, SqlQueryLogger, SqlResultsLogger, defaultQueryLogger, defaultResultsLogger };
128
+ export { type AnalyticsDimension, AnalyticsGranularity, AnalyticsPath, AnalyticsPathSegment, AnalyticsPeriod, type AnalyticsQuery, AnalyticsSerializerTypes, type AnalyticsSeries, type AnalyticsSeriesInput, type AnalyticsSeriesQuery, type AnalyticsUpdateCallback, type GroupedPeriodResult, type GroupedPeriodResults, type IAnalyticsProfiler, type IAnalyticsStore, type ISqlExecutor, type SqlQueryLogger, type SqlResultsLogger };
129
+ export {};
130
130
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/AnalyticsProfiler.ts","../src/AnalyticsDiscretizer.ts","../src/AnalyticsQueryEngine.ts","../src/AnalyticsSubscriptionManager.ts","../src/types.ts","../src/util.ts"],"mappings":";;;;UAAiB,kBAAA;EAAA,IACX,MAAA;EAEJ,IAAA,GAAO,MAAA;EACP,GAAA;EACA,YAAA,GAAe,MAAA;EAEf,MAAA,MAAY,MAAA,UAAgB,EAAA,QAAU,OAAA,CAAQ,CAAA,MAAO,OAAA,CAAQ,CAAA;EAC7D,UAAA,MAAgB,MAAA,UAAgB,EAAA,QAAU,CAAA,KAAM,CAAA;AAAA;AAAA,cAGrC,iBAAA,YAA6B,kBAAA;EAAA,iBAKrB,GAAA;EAAA,iBACA,OAAA;EAAA,iBALF,MAAA;EAAA,QACT,OAAA;cAGW,GAAA,UACA,OAAA,GAAU,UAAA,UAAoB,EAAA;EAAA,IAK7C,MAAA,CAAA;EAIJ,IAAA,CAAK,MAAA;EAML,GAAA,CAAA;EAMA,YAAA,CAAa,MAAA;EAMP,MAAA,GAAA,CAAU,MAAA,UAAgB,EAAA,QAAU,OAAA,CAAQ,CAAA,IAAK,OAAA,CAAQ,CAAA;EAW/D,UAAA,GAAA,CAAc,MAAA,UAAgB,EAAA,QAAU,CAAA,GAAI,CAAA;EAW5C,YAAA,CAAA;AAAA;AAAA,cASW,4BAAA,YAAwC,kBAAA;EAAA,IAC/C,MAAA,CAAA;EAIJ,IAAA,CAAK,OAAA;EAIL,GAAA,CAAA;EAIA,YAAA,CAAa,MAAA;EAIP,MAAA,GAAA,CAAU,MAAA,UAAgB,EAAA,QAAU,OAAA,CAAQ,CAAA,IAAE,OAAA,CAAA,CAAA;EAIpD,UAAA,GAAA,CAAc,MAAA,UAAgB,EAAA,QAAU,CAAA,GAAC,CAAA;AAAA;;;KC9E/B,mBAAA;EACV,MAAA;EACA,KAAA,EAAO,QAAA;EACP,GAAA,EAAK,QAAA;EACL,IAAA,EAAM,KAAA;IACJ,UAAA,EAAY,MAAA,SAAe,kBAAA;IAC3B,MAAA;IACA,IAAA;IACA,KAAA;IDlB+C;;;ICuB/C,GAAA;EAAA;AAAA;AAAA,KAIQ,oBAAA,GAAuB,KAAA,CAAM,mBAAA;;;cCd5B,oBAAA;EAAA,iBAIQ,eAAA;EAAA,iBAHF,SAAA;cAGE,eAAA,EAAiB,eAAA,EAClC,QAAA,GAAW,kBAAA;EAKA,eAAA,CACX,KAAA,EAAO,sBAAA,GACN,OAAA,CAAQ,oBAAA;EA0DE,OAAA,CAAQ,KAAA,EAAO,cAAA,GAAiB,OAAA,CAAQ,oBAAA;EAsBxC,oBAAA,CACX,KAAA,EAAO,cAAA,EACP,GAAA,EAAK,uBAAA,GACJ,OAAA,CAAQ,oBAAA;EAAA,QAwCH,oBAAA;EAAA,QAgBA,oBAAA;EAAA,QAoDA,qBAAA;EAAA,QAiBM,mBAAA;EAAA,QAcN,UAAA;EAAA,QAUA,oBAAA;EAmBK,aAAA,CAAA,GAAiB,OAAA;AAAA;;;cCzRnB,iBAAA,SAA0B,KAAA;EAAA,SACrB,WAAA,EAAa,KAAA;cAEjB,MAAA,EAAQ,KAAA;AAAA;;;;cAWT,4BAAA;EAAA,QACH,cAAA;EHVkC;;;;;;;;;;;;;;EG0BnC,eAAA,CACL,IAAA,EAAM,aAAA,EACN,QAAA,EAAU,uBAAA;EH7BgB;;;;;EGyDrB,iBAAA,CAAkB,KAAA,EAAO,aAAA;EHxDU;;;EAAA,QG0HlC,aAAA;EH1HyC;AAGnD;;EAHmD,QGoIzC,0BAAA;EHhG0C;;;;;EAAA,QG+I1C,eAAA;AAAA;;;KC3LE,cAAA,IAAkB,KAAA,UAAe,KAAA;AAAA,KAEjC,gBAAA,IAAoB,KAAA,UAAe,OAAA;AAAA,UAE9B,YAAA;EACf,OAAA,CAAQ,MAAA,UAAgB,MAAA,eAAqB,OAAA;AAAA;;;cCHlC,kBAAA,GACV,GAAA,aAAc,cAAA;AAAA,cAKJ,oBAAA,GACV,GAAA,aAAc,gBAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/AnalyticsProfiler.ts","../src/AnalyticsDiscretizer.ts","../src/AnalyticsQueryEngine.ts","../src/AnalyticsSubscriptionManager.ts","../src/types.ts","../src/util.ts"],"mappings":";;;UAAiB;MACX;EAEJ,OAAO;EACP;EACA,eAAe;EAEf,SAAS,GAAG,gBAAgB,UAAU,QAAQ,OAAO,QAAQ;EAC7D,aAAa,GAAG,gBAAgB,UAAU,MAAM;;qBAGrC,6BAA6B;mBAKrB;mBACA;mBALF;UACT;EAER,YACmB,aACA,UAAU,oBAAoB;MAK7C;EAIJ,KAAK;EAML;EAMA,aAAa;EAMP,OAAO,GAAG,gBAAgB,UAAU,QAAQ,KAAK,QAAQ;EAW/D,WAAW,GAAG,gBAAgB,UAAU,IAAI;EAW5C;;qBASW,wCAAwC;MAC/C;EAIJ,KAAK;EAIL;EAIA,aAAa;EAIP,OAAO,GAAG,gBAAgB,UAAU,QAAQ,KAAE,QAAA;EAIpD,WAAW,GAAG,gBAAgB,UAAU,IAAC;;;;KC9E/B;EACV;EACA,OAAO;EACP,KAAK;EACL,MAAM;IACJ,YAAY,eAAe;IAC3B;IACA;IACA;;;;IAKA;;;KAIQ,uBAAuB,MAAM;;;qBCd5B;mBAIQ;mBAHF;EAEjB,YACmB,iBAAiB,iBAClC,WAAW;EAKA,gBACX,OAAO,yBACN,QAAQ;EA0DE,QAAQ,OAAO,iBAAiB,QAAQ;EAsBxC,qBACX,OAAO,gBACP,KAAK,0BACJ,QAAQ;UAwCH;UAgBA;UAoDA;UAiBM;UAcN;UAUA;EAmBK,iBAAiB;;;;qBCzRnB,0BAA0B;WACrB,aAAa;EAE7B,YAAY,QAAQ;;;;;qBAWT;UACH;;;;;;;;;;;;;;;EAgBD,gBACL,MAAM,eACN,UAAU;;;;;;EA4BL,kBAAkB,OAAO;;;;UAkExB;;;;UAUA;;;;;;UA+CA;;;;KC3LE,kBAAkB,eAAe;KAEjC,oBAAoB,eAAe;UAE9B;EACf,QAAQ,gBAAgB,qBAAqB;;;;qBCHlC,qBAAkB,gBACd;qBAKJ,uBAAoB,gBAChB"}
package/dist/index.js CHANGED
@@ -2,6 +2,8 @@ import { AnalyticsGranularity, AnalyticsPath, AnalyticsPathSegment, AnalyticsPer
2
2
  import { DateTime, Interval } from "luxon";
3
3
  //#region src/AnalyticsProfiler.ts
4
4
  var AnalyticsProfiler = class {
5
+ _ns;
6
+ _logger;
5
7
  _stack = [];
6
8
  _prefix = "";
7
9
  constructor(_ns, _logger) {
@@ -110,7 +112,7 @@ const _createFactoryFn = (range) => {
110
112
  case AnalyticsGranularity.Hourly: result = _nextHourlyPeriod(current, range.end);
111
113
  }
112
114
  if (result === null) current = null;
113
- else current = result.end.plus({ milliseconds: 1 });
115
+ else current = result.end;
114
116
  return result;
115
117
  };
116
118
  };
@@ -128,13 +130,14 @@ const _nextAnnualPeriod = (nextStart, seriesEnd) => {
128
130
  return {
129
131
  period: "annual",
130
132
  start: nextStart,
131
- end: DateTime.utc(inputUtc.year, inputUtc.month, inputUtc.day).plus({ years: 1 })
133
+ end: DateTime.utc(inputUtc.year + 1, 1, 1)
132
134
  };
133
135
  };
134
136
  const _nextSemiAnnualPeriod = (nextStart, seriesEnd) => {
135
137
  if (seriesEnd <= nextStart) return null;
136
- const midYear = DateTime.utc(nextStart.year, 7, 1);
137
- const endYear = DateTime.utc(nextStart.year, 12, 31, 23, 59, 59, 999);
138
+ const inputUtc = nextStart.toUTC();
139
+ const midYear = DateTime.utc(inputUtc.year, 7, 1);
140
+ const endYear = DateTime.utc(inputUtc.year + 1, 1, 1);
138
141
  let endDate;
139
142
  if (midYear > nextStart) endDate = midYear;
140
143
  else endDate = endYear;
@@ -153,7 +156,7 @@ const _nextQuarterlyPeriod = (nextStart, seriesEnd) => {
153
156
  if (startMonth < 3) endDate = DateTime.utc(nextStartUtc.year, 4, 1);
154
157
  else if (startMonth < 6) endDate = DateTime.utc(nextStartUtc.year, 7, 1);
155
158
  else if (startMonth < 9) endDate = DateTime.utc(nextStartUtc.year, 10, 1);
156
- else endDate = DateTime.utc(nextStartUtc.year, 12, 31, 23, 59, 59, 999);
159
+ else endDate = DateTime.utc(nextStartUtc.year + 1, 1, 1);
157
160
  if (endDate > seriesEnd) endDate = seriesEnd;
158
161
  return {
159
162
  period: "quarterly",
@@ -203,7 +206,7 @@ const _nextDailyPeriod = (nextStart, seriesEnd) => {
203
206
  };
204
207
  const _nextHourlyPeriod = (nextStart, seriesEnd) => {
205
208
  if (seriesEnd <= nextStart) return null;
206
- let endDate = nextStart.toUTC().plus({ hours: 1 });
209
+ let endDate = nextStart.toUTC().plus({ hours: 1 }).startOf("hour");
207
210
  if (endDate > seriesEnd) {
208
211
  if (nextStart.hour !== seriesEnd.hour) endDate = seriesEnd.toUTC();
209
212
  }
@@ -261,29 +264,30 @@ var AnalyticsDiscretizer = class AnalyticsDiscretizer {
261
264
  return Object.values(result);
262
265
  }
263
266
  static _getPeriodString(p) {
267
+ const startUtc = p.start.toUTC();
264
268
  switch (p.period) {
265
- case "annual": return p.start.year.toString();
266
- case "semiAnnual": return `${p.start.year}/${p.start.month < 7 ? "H1" : "H2"}`;
267
- case "quarterly": return `${p.start.year}/Q${getQuarter(p.start)}`;
269
+ case "annual": return startUtc.year.toString();
270
+ case "semiAnnual": return `${startUtc.year}/${startUtc.month < 7 ? "H1" : "H2"}`;
271
+ case "quarterly": return `${startUtc.year}/Q${getQuarter(startUtc)}`;
268
272
  case "monthly":
269
- const month = p.start.toUTC().month;
273
+ const month = startUtc.month;
270
274
  const formattedMonth = month < 10 ? `0${month}` : `${month}`;
271
- return `${p.start.year}/${formattedMonth}`;
272
- case "weekly": return `${p.start.weekYear}/W${p.start.weekNumber}`;
275
+ return `${startUtc.year}/${formattedMonth}`;
276
+ case "weekly": return `${startUtc.weekYear}/W${startUtc.weekNumber}`;
273
277
  case "daily":
274
- const monthD = p.start.month;
275
- const day = p.start.day;
278
+ const monthD = startUtc.month;
279
+ const day = startUtc.day;
276
280
  const formattedMonthD = monthD < 10 ? `0${monthD}` : `${monthD}`;
277
281
  const formattedDay = day < 10 ? `0${day}` : `${day}`;
278
- return `${p.start.year}/${formattedMonthD}/${formattedDay}`;
282
+ return `${startUtc.year}/${formattedMonthD}/${formattedDay}`;
279
283
  case "hourly":
280
- const monthH = p.start.month;
281
- const dayH = p.start.day;
282
- const hourH = p.start.hour;
284
+ const monthH = startUtc.month;
285
+ const dayH = startUtc.day;
286
+ const hourH = startUtc.hour;
283
287
  const formattedMonthH = monthH < 10 ? `0${monthH}` : `${monthH}`;
284
288
  const formattedDayH = dayH < 10 ? `0${dayH}` : `${dayH}`;
285
289
  const formattedHourH = hourH < 10 ? `0${hourH}` : `${hourH}`;
286
- return `${p.start.year}/${formattedMonthH}/${formattedDayH}/${formattedHourH}`;
290
+ return `${startUtc.year}/${formattedMonthH}/${formattedDayH}/${formattedHourH}`;
287
291
  default: return p.period;
288
292
  }
289
293
  }
@@ -324,6 +328,7 @@ var AnalyticsDiscretizer = class AnalyticsDiscretizer {
324
328
  }
325
329
  static _discretizeSeries(series, periods) {
326
330
  const result = {};
331
+ if (periods.length === 0) return result;
327
332
  for (const s of series) {
328
333
  let oldSum = this._getValue(s, periods[0].start);
329
334
  for (const p of periods) {
@@ -349,13 +354,13 @@ var AnalyticsDiscretizer = class AnalyticsDiscretizer {
349
354
  }
350
355
  }
351
356
  static _getSingleValue(series, when) {
352
- return when >= series.start ? series.value : 0;
357
+ return when > series.start ? series.value : 0;
353
358
  }
354
359
  static _getVestValue(series, when) {
355
360
  const now = when;
356
361
  const start = series.start;
357
362
  const end = series.end;
358
- const cliff = series.params?.cliff ? DateTime.fromISO(series.params.cliff) : null;
363
+ const cliff = series.params?.cliff ? DateTime.fromISO(series.params.cliff, { zone: "utc" }) : null;
359
364
  if (now < start || cliff && now < cliff) return 0;
360
365
  else if (end && now >= end) return series.value;
361
366
  const a = Interval.fromDateTimes(start, now);
@@ -405,6 +410,7 @@ var AnalyticsDiscretizer = class AnalyticsDiscretizer {
405
410
  //#endregion
406
411
  //#region src/AnalyticsQueryEngine.ts
407
412
  var AnalyticsQueryEngine = class {
413
+ _analyticsStore;
408
414
  _profiler;
409
415
  constructor(_analyticsStore, profiler) {
410
416
  this._analyticsStore = _analyticsStore;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/AnalyticsProfiler.ts","../src/AnalyticsTimeSlicer.ts","../src/AnalyticsDiscretizer.ts","../src/AnalyticsQueryEngine.ts","../src/AnalyticsSubscriptionManager.ts","../src/util.ts"],"sourcesContent":["export interface IAnalyticsProfiler {\n get prefix(): string;\n\n push: (system: string) => void;\n pop: () => void;\n popAndReturn: (result: any) => any;\n\n record: <T>(metric: string, fn: () => Promise<T>) => Promise<T>;\n recordSync: <T>(metric: string, fn: () => T) => T;\n}\n\nexport class AnalyticsProfiler implements IAnalyticsProfiler {\n private readonly _stack: string[] = [];\n private _prefix: string = \"\";\n\n constructor(\n private readonly _ns: string,\n private readonly _logger: (metricName: string, ms: number) => void,\n ) {\n this._prefix = _ns;\n }\n\n get prefix(): string {\n return this._prefix;\n }\n\n push(system: string): void {\n this._stack.push(system);\n\n this.updatePrefix();\n }\n\n pop(): void {\n if (this._stack.pop()) {\n this.updatePrefix();\n }\n }\n\n popAndReturn(result: any): any {\n this.pop();\n\n return result;\n }\n\n async record<T>(metric: string, fn: () => Promise<T>): Promise<T> {\n const start = performance.now();\n const fullMetric = `${this.prefix}.${metric}`;\n\n try {\n return await fn();\n } finally {\n this._logger(fullMetric, performance.now() - start);\n }\n }\n\n recordSync<T>(metric: string, fn: () => T): T {\n const start = performance.now();\n const fullMetric = `${this.prefix}.${metric}`;\n\n try {\n return fn();\n } finally {\n this._logger(fullMetric, performance.now() - start);\n }\n }\n\n updatePrefix(): void {\n if (this._stack.length > 0) {\n this._prefix = `${this._ns}.${this._stack.join(\".\")}`;\n } else {\n this._prefix = this._ns;\n }\n }\n}\n\nexport class PassthroughAnalyticsProfiler implements IAnalyticsProfiler {\n get prefix(): string {\n return \"\";\n }\n\n push(_system: string) {\n //\n }\n\n pop() {\n //\n }\n\n popAndReturn(result: any) {\n return result;\n }\n\n async record<T>(metric: string, fn: () => Promise<T>) {\n return await fn();\n }\n\n recordSync<T>(metric: string, fn: () => T) {\n return fn();\n }\n}\n","import { DateTime } from \"luxon\";\nimport { AnalyticsGranularity } from \"./AnalyticsQuery.js\";\n\nexport type AnalyticsRange = {\n start: DateTime;\n end: DateTime;\n granularity: AnalyticsGranularity;\n};\n\nexport type AnalyticsPeriod = {\n period: string;\n start: DateTime;\n end: DateTime;\n};\n\ninterface AnalyticsPeriodSeries {\n start: DateTime;\n end: DateTime;\n granularity: AnalyticsGranularity;\n next(): AnalyticsPeriod | null;\n}\n\nexport const getPeriodSeriesArray = (\n range: AnalyticsRange,\n): AnalyticsPeriod[] => {\n const result: AnalyticsPeriod[] = [];\n const series = getPeriodSeries(range);\n\n let next = series.next();\n while (next) {\n result.push(next);\n next = series.next();\n }\n\n return result;\n};\n\nexport const getPeriodSeries = (\n range: AnalyticsRange,\n): AnalyticsPeriodSeries => {\n return {\n ...range,\n next: _createFactoryFn(range),\n };\n};\n\nconst _createFactoryFn = (range: AnalyticsRange) => {\n let current: DateTime | null = range.start;\n\n return () => {\n if (current == null) {\n return null;\n }\n\n let result: AnalyticsPeriod | null = null;\n switch (range.granularity) {\n case AnalyticsGranularity.Total:\n result = _nextTotalPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Annual:\n result = _nextAnnualPeriod(current, range.end);\n break;\n case AnalyticsGranularity.SemiAnnual:\n result = _nextSemiAnnualPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Quarterly:\n result = _nextQuarterlyPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Monthly:\n result = _nextMonthlyPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Weekly:\n result = _nextWeeklyPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Daily:\n result = _nextDailyPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Hourly:\n result = _nextHourlyPeriod(current, range.end);\n }\n\n // Update current to start of next period\n if (result === null) {\n current = null;\n } else {\n current = result.end.plus({ milliseconds: 1 });\n }\n\n return result;\n };\n};\n\nconst _nextTotalPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n return {\n period: \"total\",\n start: nextStart,\n end: seriesEnd,\n };\n};\n\nexport const _nextAnnualPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n const inputUtc = nextStart.toUTC();\n const oneYearLater = DateTime.utc(\n inputUtc.year,\n inputUtc.month,\n inputUtc.day,\n ).plus({ years: 1 });\n\n return {\n period: \"annual\",\n start: nextStart,\n end: oneYearLater,\n };\n};\n\nexport const _nextSemiAnnualPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n const midYear = DateTime.utc(nextStart.year, 7, 1);\n const endYear = DateTime.utc(nextStart.year, 12, 31, 23, 59, 59, 999);\n\n let endDate: DateTime;\n if (midYear > nextStart) {\n endDate = midYear;\n } else {\n endDate = endYear;\n }\n\n if (endDate > seriesEnd) {\n endDate = seriesEnd;\n }\n\n return {\n period: \"semiAnnual\",\n start: nextStart,\n end: endDate,\n };\n};\n\nexport const _nextQuarterlyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n let endDate: DateTime;\n const nextStartUtc = nextStart.toUTC();\n const startMonth = nextStartUtc.month;\n\n if (startMonth < 3) {\n endDate = DateTime.utc(nextStartUtc.year, 4, 1);\n } else if (startMonth < 6) {\n endDate = DateTime.utc(nextStartUtc.year, 7, 1);\n } else if (startMonth < 9) {\n endDate = DateTime.utc(nextStartUtc.year, 10, 1);\n } else {\n endDate = DateTime.utc(nextStartUtc.year, 12, 31, 23, 59, 59, 999);\n }\n\n if (endDate > seriesEnd) {\n endDate = seriesEnd;\n }\n\n return {\n period: \"quarterly\",\n start: nextStart,\n end: endDate,\n };\n};\nexport const _nextMonthlyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n // Get the first day of the next month\n const nextStartUtc = nextStart.toUTC();\n let endDate = DateTime.utc(\n nextStartUtc.year,\n nextStartUtc.month,\n nextStartUtc.day,\n )\n .plus({ months: 1 })\n .startOf(\"month\");\n\n // If the end date is after the series end, then use the series end as the end date\n if (endDate > seriesEnd) {\n if (!nextStart.hasSame(seriesEnd, \"month\")) {\n endDate = seriesEnd;\n }\n }\n\n // Otherwise, return the end date as the first day of the next month\n return {\n period: \"monthly\",\n start: nextStart,\n end: endDate,\n };\n};\n\nexport const _nextWeeklyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n // Calculate the start of the next week (Monday)\n const nextStartUtc = nextStart.toUTC();\n const nextWeekStartUTC = DateTime.utc(\n nextStartUtc.year,\n nextStartUtc.month,\n nextStartUtc.day,\n )\n .plus({ weeks: 1 })\n .startOf(\"week\");\n\n // If the calculated next week start is later or equal to the series end date, return the series end\n if (nextWeekStartUTC > seriesEnd)\n if (!nextWeekStartUTC.hasSame(seriesEnd, \"day\")) {\n return {\n period: \"weekly\",\n start: nextStart,\n end: seriesEnd,\n };\n }\n\n return {\n period: \"weekly\",\n start: nextStart,\n end: nextWeekStartUTC,\n };\n};\n\nexport const _nextDailyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n // Set the end date to the start of the next day\n const nextStartUtc = nextStart.toUTC();\n let endDate = nextStartUtc.plus({ days: 1 }).startOf(\"day\");\n if (endDate > seriesEnd || endDate.hasSame(seriesEnd, \"day\")) {\n endDate = seriesEnd;\n }\n\n return {\n period: \"daily\",\n start: nextStart,\n end: endDate,\n };\n};\n\nexport const _nextHourlyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n const startDate = nextStart.toUTC();\n let endDate = startDate.plus({ hours: 1 });\n\n if (endDate > seriesEnd) {\n if (nextStart.hour !== seriesEnd.hour) {\n endDate = seriesEnd.toUTC();\n }\n }\n\n return {\n period: \"hourly\",\n start: nextStart,\n end: endDate,\n };\n};\n","/* eslint-disable @typescript-eslint/no-redundant-type-constituents */\n/* eslint-disable no-case-declarations */\nimport { DateTime, Interval } from \"luxon\";\nimport type { AnalyticsGranularity } from \"./AnalyticsQuery.js\";\nimport {\n type AnalyticsDimension,\n type AnalyticsSeries,\n} from \"./AnalyticsQuery.js\";\nimport {\n type AnalyticsPeriod,\n type AnalyticsRange,\n getPeriodSeriesArray,\n} from \"./AnalyticsTimeSlicer.js\";\n\nexport const getQuarter = (date: DateTime) => {\n return Math.floor((date.month - 1) / 3) + 1;\n};\n\nexport type GroupedPeriodResult = {\n period: string;\n start: DateTime;\n end: DateTime;\n rows: Array<{\n dimensions: Record<string, AnalyticsDimension>;\n metric: string;\n unit: string | null;\n value: number;\n\n /**\n * The sum of all metric values over this period?\n */\n sum: number;\n }>;\n};\n\nexport type GroupedPeriodResults = Array<GroupedPeriodResult>;\n\nexport class AnalyticsDiscretizer {\n public static discretize(\n series: AnalyticsSeries<string>[],\n dimensions: string[],\n start: DateTime | null,\n end: DateTime | null,\n granularity: AnalyticsGranularity,\n ): GroupedPeriodResults {\n const index = this._buildIndex(series, dimensions);\n const periods = getPeriodSeriesArray(\n this._calculateRange(start, end, granularity, series),\n );\n const disretizedResults = this._discretizeNode(\n index,\n {},\n dimensions,\n periods,\n );\n const groupedResults = this._groupResultsByPeriod(\n periods,\n disretizedResults,\n );\n\n return groupedResults;\n }\n\n private static _calculateRange(\n start: DateTime | null,\n end: DateTime | null,\n granularity: AnalyticsGranularity,\n results: AnalyticsSeries<any>[],\n ) {\n let calculatedStart: DateTime | null = start || null;\n let calculatedEnd: DateTime | null = end || null;\n\n if (calculatedStart == null || calculatedEnd == null) {\n for (const r of results) {\n if (calculatedStart == null) {\n calculatedStart = r.start;\n }\n\n const endValue = r.end || r.start;\n if (calculatedEnd == null || calculatedEnd < endValue) {\n calculatedEnd = endValue;\n }\n }\n }\n\n if (calculatedStart == null || calculatedEnd == null) {\n throw new Error(\"Cannot determine query start and/or end.\");\n }\n\n return {\n start: calculatedStart,\n end: calculatedEnd,\n granularity,\n } as AnalyticsRange;\n }\n\n public static _groupResultsByPeriod(\n periods: AnalyticsPeriod[],\n dimensionedResults: DimensionedSeries[],\n ): GroupedPeriodResults {\n const result: Record<string, GroupedPeriodResult> = {};\n\n for (const p of periods) {\n const id = p.start.toISO() + \"-\" + p.period;\n const period = AnalyticsDiscretizer._getPeriodString(p);\n result[id] = {\n period: period,\n start: p.start,\n end: p.end,\n rows: [],\n };\n }\n\n for (const r of dimensionedResults) {\n for (const period of Object.keys(r.series)) {\n result[period].rows.push({\n dimensions: r.dimensions,\n metric: r.metric,\n unit: r.unit == \"__NULL__\" ? null : r.unit,\n value: r.series[period].inc,\n sum: r.series[period].sum,\n });\n }\n }\n\n return Object.values(result);\n }\n\n static _getPeriodString(p: AnalyticsPeriod) {\n switch (p.period) {\n case \"annual\":\n return p.start.year.toString();\n case \"semiAnnual\":\n return `${p.start.year}/${p.start.month < 7 ? \"H1\" : \"H2\"}`;\n case \"quarterly\":\n return `${p.start.year}/Q${getQuarter(p.start)}`;\n case \"monthly\":\n const month = p.start.toUTC().month;\n const formattedMonth = month < 10 ? `0${month}` : `${month}`;\n return `${p.start.year}/${formattedMonth}`;\n case \"weekly\":\n return `${p.start.weekYear}/W${p.start.weekNumber}`;\n case \"daily\":\n const monthD = p.start.month;\n const day = p.start.day;\n const formattedMonthD = monthD < 10 ? `0${monthD}` : `${monthD}`;\n const formattedDay = day < 10 ? `0${day}` : `${day}`;\n return `${p.start.year}/${formattedMonthD}/${formattedDay}`;\n case \"hourly\":\n const monthH = p.start.month;\n const dayH = p.start.day;\n const hourH = p.start.hour;\n const formattedMonthH = monthH < 10 ? `0${monthH}` : `${monthH}`;\n const formattedDayH = dayH < 10 ? `0${dayH}` : `${dayH}`;\n const formattedHourH = hourH < 10 ? `0${hourH}` : `${hourH}`;\n return `${p.start.year}/${formattedMonthH}/${formattedDayH}/${formattedHourH}`;\n default:\n return p.period;\n }\n }\n\n public static _discretizeNode(\n node: DiscretizerIndexNode,\n dimensionValues: Record<string, string>,\n remainingDimensions: string[],\n periods: AnalyticsPeriod[],\n ): DimensionedSeries[] {\n const result: DimensionedSeries[] = [];\n\n if (remainingDimensions.length > 0) {\n const subdimension = remainingDimensions[0] as string;\n Object.keys(node).forEach((subdimensionValue, _index, _arr) => {\n const newDimensionValues = { ...dimensionValues };\n newDimensionValues[subdimension] = subdimensionValue;\n result.push(\n ...this._discretizeNode(\n node[subdimensionValue] as DiscretizerIndexNode,\n newDimensionValues,\n remainingDimensions.slice(1),\n periods,\n ),\n );\n });\n } else {\n Object.keys(node).forEach((metric) => {\n result.push(\n ...this._discretizeLeaf(\n node[metric] as DiscretizerIndexLeaf,\n periods,\n metric,\n dimensionValues,\n ),\n );\n });\n }\n\n return result;\n }\n\n public static _discretizeLeaf(\n leaf: DiscretizerIndexLeaf,\n periods: AnalyticsPeriod[],\n metric: string,\n dimensionValues: Record<string, string>,\n ): DimensionedSeries[] {\n const result: DimensionedSeries[] = [];\n Object.keys(leaf).forEach((unit) => {\n const metaDimensions: any = {};\n Object.keys(dimensionValues).forEach((k) => {\n metaDimensions[k] = {\n path: leaf[unit][0].dimensions[k],\n icon: leaf[unit][0].dimensions.icon,\n label: leaf[unit][0].dimensions.label,\n description: leaf[unit][0].dimensions.description,\n };\n });\n result.push({\n unit,\n metric,\n dimensions: metaDimensions as any,\n series: this._discretizeSeries(leaf[unit], periods),\n });\n });\n\n return result;\n }\n\n public static _discretizeSeries(\n series: AnalyticsSeries<string>[],\n periods: AnalyticsPeriod[],\n ): Series {\n const result: Series = {};\n\n for (const s of series) {\n let oldSum = this._getValue(s, periods[0].start);\n for (const p of periods) {\n const newSum = this._getValue(s, p.end);\n const id = `${p.start.toISO()}-${p.period}`;\n\n // const id = p.period;\n if (result[id]) {\n result[id].inc += newSum - oldSum;\n result[id].sum += newSum;\n } else {\n result[id] = {\n inc: newSum - oldSum,\n sum: newSum,\n };\n }\n\n oldSum = newSum;\n }\n }\n\n return result;\n }\n\n public static _getValue(\n series: AnalyticsSeries<string>,\n when: DateTime,\n ): number {\n switch (series.fn) {\n case \"Single\":\n return this._getSingleValue(series, when);\n case \"DssVest\":\n return this._getVestValue(series, when);\n default:\n // todo: logging interface\n //console.error(`Unknown analytics series function: '${series.fn}'`);\n return 0.0;\n }\n }\n\n public static _getSingleValue(\n series: AnalyticsSeries<string>,\n when: DateTime,\n ): number {\n return when >= series.start ? series.value : 0.0;\n }\n\n public static _getVestValue(\n series: AnalyticsSeries<string>,\n when: DateTime,\n ): number {\n const now = when;\n const start = series.start;\n const end = series.end;\n\n const cliff = series.params?.cliff\n ? DateTime.fromISO(series.params.cliff! as string)\n : null;\n if (now < start || (cliff && now < cliff)) {\n return 0.0;\n } else if (end && now >= end) {\n return series.value;\n }\n\n const a = Interval.fromDateTimes(start, now);\n const b = Interval.fromDateTimes(start, end || now);\n\n return (a.length() / b.length()) * series.value;\n }\n\n public static _buildIndex(\n series: AnalyticsSeries<string>[],\n dimensions: string[],\n ): DiscretizerIndexNode {\n const result: DiscretizerIndexNode | any = {};\n const map: DiscretizerIndexLeaf = {};\n const dimName = dimensions[0] || \"\";\n\n for (const s of series) {\n const dimValue = s.dimensions[dimName];\n if (undefined === map[dimValue]) {\n map[dimValue] = [];\n }\n map[dimValue].push(s);\n }\n\n if (dimensions.length > 1) {\n const newDimensions = dimensions.slice(1);\n Object.keys(map).forEach((k) => {\n result[k] = this._buildIndex(map[k], newDimensions);\n });\n } else {\n Object.keys(map).forEach((k) => {\n result[k] = this._buildMetricsIndex(map[k]);\n });\n }\n\n return result;\n }\n\n public static _buildMetricsIndex(\n series: AnalyticsSeries<string>[],\n ): DiscretizerIndexNode {\n const result: DiscretizerIndexNode = {};\n\n const map: DiscretizerIndexLeaf = {};\n for (const s of series) {\n const metric = s.metric;\n if (undefined === map[metric]) {\n map[metric] = [];\n }\n\n map[metric].push(s);\n }\n\n Object.keys(map).forEach((k) => (result[k] = this._buildUnitIndex(map[k])));\n return result;\n }\n\n public static _buildUnitIndex(\n series: AnalyticsSeries<string>[],\n ): DiscretizerIndexLeaf {\n const result: DiscretizerIndexLeaf = {};\n\n for (const s of series) {\n const unit = s.unit || \"__NULL__\";\n if (undefined === result[unit]) {\n result[unit] = [];\n }\n\n result[unit].push(s);\n }\n\n return result;\n }\n}\n\ntype DiscretizerIndexLeaf = { [k: string]: AnalyticsSeries<string>[] };\ntype DiscretizerIndexNode = {\n [k: string]: DiscretizerIndexNode | DiscretizerIndexLeaf;\n};\ntype Series = Record<\n string,\n {\n /**\n * How much the metric increased from the beginning of the series to the end of the specified period.\n */\n inc: number;\n\n /**\n * The sum of all metric values from the beginning of the series to the end of the specified period.\n */\n sum: number;\n }\n>;\ntype DimensionedSeries = {\n unit: string;\n metric: string;\n dimensions: Record<string, AnalyticsDimension>;\n series: Series;\n};\n","/* eslint-disable @typescript-eslint/no-redundant-type-constituents */\nimport {\n AnalyticsDiscretizer,\n type GroupedPeriodResults,\n type GroupedPeriodResult,\n} from \"./AnalyticsDiscretizer.js\";\nimport { AnalyticsPath } from \"./AnalyticsPath.js\";\nimport {\n type IAnalyticsProfiler,\n PassthroughAnalyticsProfiler,\n} from \"./AnalyticsProfiler.js\";\nimport {\n type AnalyticsQuery,\n type AnalyticsSeries,\n type AnalyticsSeriesQuery,\n type CompoundAnalyticsQuery,\n type MultiCurrencyConversion,\n CompoundOperator,\n} from \"./AnalyticsQuery.js\";\nimport { type IAnalyticsStore } from \"./IAnalyticsStore.js\";\n\nexport class AnalyticsQueryEngine {\n private readonly _profiler: IAnalyticsProfiler;\n\n public constructor(\n private readonly _analyticsStore: IAnalyticsStore,\n profiler?: IAnalyticsProfiler,\n ) {\n this._profiler = profiler ?? new PassthroughAnalyticsProfiler();\n }\n\n public async executeCompound(\n query: CompoundAnalyticsQuery,\n ): Promise<GroupedPeriodResults> {\n let result: GroupedPeriodResults;\n\n const inputsQuery: AnalyticsQuery = {\n start: query.start,\n end: query.end,\n granularity: query.granularity,\n lod: query.lod,\n select: query.select,\n metrics: query.expression.inputs.metrics,\n currency: query.expression.inputs.currency,\n };\n\n const operandQuery: AnalyticsQuery = {\n start: query.start,\n end: query.end,\n granularity: query.granularity,\n lod: { priceData: 1 },\n select: { priceData: [AnalyticsPath.fromString(\"atlas\")] },\n metrics: [query.expression.operand.metric],\n currency: query.expression.operand.currency,\n };\n\n const inputExecute = await this.execute(inputsQuery);\n const operandExecute = await this.execute(operandQuery);\n\n if (\n [CompoundOperator.VectorAdd, CompoundOperator.VectorSubtract].includes(\n query.expression.operator,\n )\n ) {\n result = await this._profiler.record(\n \"ApplyVectorOperator\",\n async () =>\n await this._applyVectorOperator(\n inputExecute,\n operandExecute,\n query.expression.operator,\n query.expression.resultCurrency,\n ),\n );\n } else {\n result = await this._profiler.record(\n \"ApplyScalarOperator\",\n async () =>\n await this._applyScalarOperator(\n inputExecute,\n operandExecute,\n query.expression.operator,\n query.expression.operand.useSum,\n query.expression.resultCurrency,\n ),\n );\n }\n\n return result;\n }\n\n public async execute(query: AnalyticsQuery): Promise<GroupedPeriodResults> {\n const seriesResults = await this._executeSeriesQuery(query);\n\n const normalizedSeriesResults = this._profiler.recordSync(\"ApplyLODs\", () =>\n this._applyLods(seriesResults, query.lod),\n );\n\n const dimensions = Object.keys(query.select);\n const discretizedResult = this._profiler.recordSync(\"Discretize\", () =>\n normalizedSeriesResults.length < 1\n ? []\n : AnalyticsDiscretizer.discretize(\n normalizedSeriesResults,\n dimensions,\n query.start,\n query.end,\n query.granularity,\n ),\n );\n return discretizedResult;\n }\n\n public async executeMultiCurrency(\n query: AnalyticsQuery,\n mcc: MultiCurrencyConversion,\n ): Promise<GroupedPeriodResults> {\n const baseQuery: AnalyticsQuery = {\n ...query,\n currency: mcc.targetCurrency ?? query.currency,\n };\n let result = await this.execute(baseQuery);\n\n for (const conversion of mcc.conversions) {\n const nextQuery: CompoundAnalyticsQuery = {\n start: query.start,\n end: query.end,\n granularity: query.granularity,\n lod: query.lod,\n select: query.select,\n expression: {\n inputs: {\n metrics: baseQuery.metrics,\n currency: conversion.currency,\n },\n operator: CompoundOperator.ScalarMultiply,\n operand: {\n metric: conversion.metric,\n currency: mcc.targetCurrency,\n useSum: true,\n },\n resultCurrency: mcc.targetCurrency,\n },\n };\n\n const executedCompound = await this.executeCompound(nextQuery);\n result = await this._applyVectorOperator(\n result,\n executedCompound,\n CompoundOperator.VectorAdd,\n mcc.targetCurrency,\n );\n }\n return result;\n }\n\n private _applyVectorOperator(\n inputsA: GroupedPeriodResults,\n inputsB: GroupedPeriodResults,\n operator: CompoundOperator,\n _resultCurrency?: AnalyticsPath,\n ): Promise<GroupedPeriodResults> {\n if (\n [CompoundOperator.ScalarMultiply, CompoundOperator.ScalarDivide].includes(\n operator,\n )\n ) {\n throw new Error(\"Invalid operator for vector operation\");\n }\n return Promise.resolve(inputsB);\n }\n\n private _applyScalarOperator(\n inputs: GroupedPeriodResults, // expected input is the budget & actuals in 2022 monthly granularity in MKR\n operand: GroupedPeriodResults, // expected input is the daily mkr price change in 2022 monthly granularity in DAI\n operator: CompoundOperator, // expected to me multiply and later addition\n useOperandSum: boolean,\n resultCurrency?: AnalyticsPath, // expected to be DAI\n ): Promise<GroupedPeriodResults> {\n if (\n [CompoundOperator.VectorAdd, CompoundOperator.VectorSubtract].includes(\n operator,\n )\n ) {\n throw new Error(\"Invalid operator for scalar operation\");\n }\n\n const result: GroupedPeriodResults = [];\n const operandMap: Record<string, number> = {};\n const key = useOperandSum ? \"sum\" : \"value\";\n\n for (const operandPeriod of operand) {\n if (operandPeriod.rows.length > 0) {\n operandMap[operandPeriod.period] = operandPeriod.rows[0][key];\n }\n }\n\n // let previousValue: number = 1;\n for (const inputPeriod of inputs) {\n const outputPeriod: GroupedPeriodResult = {\n period: inputPeriod.period,\n start: inputPeriod.start,\n end: inputPeriod.end,\n rows: inputPeriod.rows.map((row) => {\n const newRow = {\n dimensions: row.dimensions,\n metric: row.metric,\n unit: resultCurrency ? resultCurrency.toString() : row.unit,\n value: this._calculateOutputValue(\n row.value,\n operator,\n operandMap[inputPeriod.period],\n ),\n sum: -1,\n };\n return newRow;\n }),\n };\n result.push(outputPeriod);\n }\n\n return Promise.resolve(result);\n }\n\n private _calculateOutputValue(\n input: number,\n operator: CompoundOperator,\n operand: number,\n ): number {\n switch (operator) {\n case CompoundOperator.VectorAdd:\n return input + operand;\n case CompoundOperator.VectorSubtract:\n return input - operand;\n case CompoundOperator.ScalarMultiply:\n return input * operand;\n case CompoundOperator.ScalarDivide:\n return input / operand;\n }\n }\n\n private async _executeSeriesQuery(\n query: AnalyticsQuery,\n ): Promise<AnalyticsSeries[]> {\n const seriesQuery: AnalyticsSeriesQuery = {\n start: query.start,\n end: query.end,\n select: query.select,\n metrics: query.metrics,\n currency: query.currency,\n };\n\n return await this._analyticsStore.getMatchingSeries(seriesQuery);\n }\n\n private _applyLods(\n series: AnalyticsSeries[],\n lods: Record<string, number | null>,\n ): AnalyticsSeries<string>[] {\n return series.map((result) => ({\n ...result,\n dimensions: this._applyDimensionsLods(result.dimensions, lods),\n }));\n }\n\n private _applyDimensionsLods(\n dimensionMap: Record<string, AnalyticsPath> | any,\n lods: Record<string, number | null>,\n ) {\n const result: Record<string, string> | any = {};\n for (const [dimension, lod] of Object.entries(lods)) {\n if (lod !== null && dimensionMap[dimension]) {\n result[dimension] = dimensionMap[dimension][\"path\"]\n .applyLod(lod)\n .toString();\n result[\"icon\"] = dimensionMap[dimension][\"icon\"].toString();\n result[\"label\"] = dimensionMap[dimension][\"label\"].toString();\n result[\"description\"] =\n dimensionMap[dimension][\"description\"].toString();\n }\n }\n return result;\n }\n\n public async getDimensions(): Promise<any> {\n return await this._analyticsStore.getDimensions();\n }\n\n /*public async getMetrics(): Promise<string[]> {\n return await this._analyticsStore.getMetrics();\n }\n\n public async getCurrencies(): Promise<string[]> {\n return await this._analyticsStore.getCurrencies();\n }*/\n}\n","import type { AnalyticsPath } from \"./AnalyticsPath.js\";\nimport type { AnalyticsUpdateCallback } from \"./IAnalyticsStore.js\";\n\nexport class NotificationError extends Error {\n public readonly innerErrors: Error[];\n\n constructor(errors: Error[]) {\n super(errors.map((e) => e.message).join(\"\\n\"));\n\n this.name = \"NotificationError\";\n this.innerErrors = errors;\n }\n}\n\n/**\n * Manages subscriptions for analytics paths.\n */\nexport class AnalyticsSubscriptionManager {\n private _subscriptions: Map<string, Set<AnalyticsUpdateCallback>> = new Map();\n\n /**\n * Subscribe to updates for an analytics path. A subscribed function will be\n * called for:\n *\n * - exact path matches\n * - matching child paths (i.e. an update to /a/b/c will trigger a callback\n * for /a/b/c, /a/b, and /a)\n * - wildcard matches\n *\n * @param path The analytics path to subscribe to.\n * @param callback Function to be called when the path is updated.\n *\n * @returns A function that, when called, unsubscribes from the updates.\n */\n public subscribeToPath(\n path: AnalyticsPath,\n callback: AnalyticsUpdateCallback,\n ): () => void {\n const pathString = this.normalizePath(path.toString(\"/\"));\n\n if (!this._subscriptions.has(pathString)) {\n this._subscriptions.set(pathString, new Set());\n }\n\n this._subscriptions.get(pathString)!.add(callback);\n\n return () => {\n const callbacks = this._subscriptions.get(pathString);\n if (callbacks) {\n callbacks.delete(callback);\n\n // only remove the path entry if there are no more callbacks\n if (callbacks.size === 0) {\n this._subscriptions.delete(pathString);\n }\n }\n };\n }\n\n /**\n * Notifies subscribers about updates to paths.\n *\n * @param paths The paths that were updated.\n */\n public notifySubscribers(paths: AnalyticsPath[]): void {\n if (paths.length === 0 || this._subscriptions.size === 0) {\n return;\n }\n\n const errors: Error[] = [];\n for (const path of paths) {\n const pathString = this.normalizePath(path.toString(\"/\"));\n const pathPrefixes = this.getPathPrefixes(pathString);\n\n const matchingSubscriptions: Array<{\n prefix: string;\n callbacks: Set<AnalyticsUpdateCallback>;\n }> = [];\n\n // add the exact prefix matches\n pathPrefixes\n .filter((prefix) => this._subscriptions.has(prefix))\n .forEach((prefix) => {\n matchingSubscriptions.push({\n prefix,\n callbacks: this._subscriptions.get(prefix)!,\n });\n });\n\n // check all wildcard patterns\n for (const [\n subscriptionPath,\n callbacks,\n ] of this._subscriptions.entries()) {\n // skip if it's already in the exact matches\n if (pathPrefixes.includes(subscriptionPath)) {\n continue;\n }\n\n // hceck if this subscription path (which might have wildcards) matches the update path\n if (this.pathMatchesWildcardPattern(pathString, subscriptionPath)) {\n matchingSubscriptions.push({\n prefix: subscriptionPath,\n callbacks,\n });\n }\n }\n\n if (matchingSubscriptions.length === 0) continue;\n\n // guarantee delivery to all subscribers\n for (const { callbacks } of matchingSubscriptions) {\n for (const callback of callbacks) {\n try {\n callback(path);\n } catch (e) {\n errors.push(e as Error);\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new NotificationError(errors);\n }\n }\n\n /**\n * Normalizes a path string to ensure consistent comparison.\n */\n private normalizePath(path: string): string {\n // Handle potential double slashes by first splitting on slashes and rejoining\n const parts = path.split(\"/\").filter((p) => p.length > 0);\n const normalized = \"/\" + parts.join(\"/\");\n return normalized;\n }\n\n /**\n * Checks if a path matches a subscription pattern that may contain wildcards.\n */\n private pathMatchesWildcardPattern(\n updatePath: string,\n subscriptionPath: string,\n ): boolean {\n // Handle the wildcard segment case\n if (subscriptionPath.includes(\"*\")) {\n const updateSegments = updatePath.split(\"/\").filter((s) => s.length > 0);\n const subscriptionSegments = subscriptionPath\n .split(\"/\")\n .filter((s) => s.length > 0);\n\n // If subscription is longer than the update path, it can't match\n if (subscriptionSegments.length > updateSegments.length) {\n return false;\n }\n\n // Compare each segment\n for (let i = 0; i < subscriptionSegments.length; i++) {\n const subSegment = subscriptionSegments[i];\n const updateSegment = updateSegments[i];\n\n // If segment is * or starts with * (wildcard), it matches anything\n if (subSegment === \"*\" || subSegment.startsWith(\"*:\")) {\n continue;\n }\n\n // Otherwise, segments should match exactly\n if (subSegment !== updateSegment) {\n return false;\n }\n }\n\n return true;\n }\n\n // If no wildcards, check if subscription is a prefix of the update path\n return (\n updatePath === subscriptionPath ||\n updatePath.startsWith(subscriptionPath + \"/\")\n );\n }\n\n /**\n * Gets all path prefixes for a given path.\n *\n * For example, for '/a/b/c' it returns ['/a', '/a/b', '/a/b/c'].\n */\n private getPathPrefixes(path: string): string[] {\n const segments = path.split(\"/\").filter((s) => s.length > 0);\n const prefixes: string[] = [];\n\n let currentPath = \"\";\n for (const segment of segments) {\n currentPath = currentPath ? `${currentPath}/${segment}` : `/${segment}`;\n prefixes.push(currentPath);\n }\n\n if (prefixes.length === 0 && path === \"/\") {\n prefixes.push(\"/\");\n }\n\n return prefixes;\n }\n}\n","import type { SqlQueryLogger, SqlResultsLogger } from \"./types.js\";\n\nexport const defaultQueryLogger =\n (tag: string): SqlQueryLogger =>\n (index: number, query: string) => {\n console.log(`[${tag}][Q:${index}]: ${query}\\n`);\n };\n\nexport const defaultResultsLogger =\n (tag: string): SqlResultsLogger =>\n (index: number, results: any) => {\n if (Array.isArray(results)) {\n console.log(`[${tag}][R:${index}]: ${results.length} results\\n`);\n } else {\n console.log(`[${tag}][R:${index}]: Received ${typeof results}.\\n`);\n }\n };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/AnalyticsProfiler.ts","../src/AnalyticsTimeSlicer.ts","../src/AnalyticsDiscretizer.ts","../src/AnalyticsQueryEngine.ts","../src/AnalyticsSubscriptionManager.ts","../src/util.ts"],"sourcesContent":["export interface IAnalyticsProfiler {\n get prefix(): string;\n\n push: (system: string) => void;\n pop: () => void;\n popAndReturn: (result: any) => any;\n\n record: <T>(metric: string, fn: () => Promise<T>) => Promise<T>;\n recordSync: <T>(metric: string, fn: () => T) => T;\n}\n\nexport class AnalyticsProfiler implements IAnalyticsProfiler {\n private readonly _stack: string[] = [];\n private _prefix: string = \"\";\n\n constructor(\n private readonly _ns: string,\n private readonly _logger: (metricName: string, ms: number) => void,\n ) {\n this._prefix = _ns;\n }\n\n get prefix(): string {\n return this._prefix;\n }\n\n push(system: string): void {\n this._stack.push(system);\n\n this.updatePrefix();\n }\n\n pop(): void {\n if (this._stack.pop()) {\n this.updatePrefix();\n }\n }\n\n popAndReturn(result: any): any {\n this.pop();\n\n return result;\n }\n\n async record<T>(metric: string, fn: () => Promise<T>): Promise<T> {\n const start = performance.now();\n const fullMetric = `${this.prefix}.${metric}`;\n\n try {\n return await fn();\n } finally {\n this._logger(fullMetric, performance.now() - start);\n }\n }\n\n recordSync<T>(metric: string, fn: () => T): T {\n const start = performance.now();\n const fullMetric = `${this.prefix}.${metric}`;\n\n try {\n return fn();\n } finally {\n this._logger(fullMetric, performance.now() - start);\n }\n }\n\n updatePrefix(): void {\n if (this._stack.length > 0) {\n this._prefix = `${this._ns}.${this._stack.join(\".\")}`;\n } else {\n this._prefix = this._ns;\n }\n }\n}\n\nexport class PassthroughAnalyticsProfiler implements IAnalyticsProfiler {\n get prefix(): string {\n return \"\";\n }\n\n push(_system: string) {\n //\n }\n\n pop() {\n //\n }\n\n popAndReturn(result: any) {\n return result;\n }\n\n async record<T>(metric: string, fn: () => Promise<T>) {\n return await fn();\n }\n\n recordSync<T>(metric: string, fn: () => T) {\n return fn();\n }\n}\n","import { DateTime } from \"luxon\";\nimport { AnalyticsGranularity } from \"./AnalyticsQuery.js\";\n\n// Boundary convention: periods are half-open intervals [start, end), where\n// each period's end is exactly the next period's start. Calendar-aligned\n// granularities anchor to the UTC calendar (ISO 8601): a period boundary sits\n// at T00:00:00.000Z of the first day of the calendar year / half-year /\n// quarter / month / ISO week / day / hour. The first period of a series may\n// be partial (it starts at the query start); subsequent periods start on the\n// calendar boundary. An instant exactly at a boundary belongs to the period\n// that starts there (see AnalyticsDiscretizer._getSingleValue).\n\nexport type AnalyticsRange = {\n start: DateTime;\n end: DateTime;\n granularity: AnalyticsGranularity;\n};\n\nexport type AnalyticsPeriod = {\n period: string;\n start: DateTime;\n end: DateTime;\n};\n\ninterface AnalyticsPeriodSeries {\n start: DateTime;\n end: DateTime;\n granularity: AnalyticsGranularity;\n next(): AnalyticsPeriod | null;\n}\n\nexport const getPeriodSeriesArray = (\n range: AnalyticsRange,\n): AnalyticsPeriod[] => {\n const result: AnalyticsPeriod[] = [];\n const series = getPeriodSeries(range);\n\n let next = series.next();\n while (next) {\n result.push(next);\n next = series.next();\n }\n\n return result;\n};\n\nexport const getPeriodSeries = (\n range: AnalyticsRange,\n): AnalyticsPeriodSeries => {\n return {\n ...range,\n next: _createFactoryFn(range),\n };\n};\n\nconst _createFactoryFn = (range: AnalyticsRange) => {\n let current: DateTime | null = range.start;\n\n return () => {\n if (current == null) {\n return null;\n }\n\n let result: AnalyticsPeriod | null = null;\n switch (range.granularity) {\n case AnalyticsGranularity.Total:\n result = _nextTotalPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Annual:\n result = _nextAnnualPeriod(current, range.end);\n break;\n case AnalyticsGranularity.SemiAnnual:\n result = _nextSemiAnnualPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Quarterly:\n result = _nextQuarterlyPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Monthly:\n result = _nextMonthlyPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Weekly:\n result = _nextWeeklyPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Daily:\n result = _nextDailyPeriod(current, range.end);\n break;\n case AnalyticsGranularity.Hourly:\n result = _nextHourlyPeriod(current, range.end);\n }\n\n // Update current to start of next period. Periods are half-open\n // [start, end), so the next period starts exactly at this period's end.\n if (result === null) {\n current = null;\n } else {\n current = result.end;\n }\n\n return result;\n };\n};\n\nconst _nextTotalPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n return {\n period: \"total\",\n start: nextStart,\n end: seriesEnd,\n };\n};\n\nexport const _nextAnnualPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n // Anchor to the UTC calendar year, like the other granularities: the\n // period ends at January 1st of the following year, so the first period\n // may be partial and every later period spans a full calendar year.\n const inputUtc = nextStart.toUTC();\n const endDate = DateTime.utc(inputUtc.year + 1, 1, 1);\n\n return {\n period: \"annual\",\n start: nextStart,\n end: endDate,\n };\n};\n\nexport const _nextSemiAnnualPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n const inputUtc = nextStart.toUTC();\n const midYear = DateTime.utc(inputUtc.year, 7, 1);\n const endYear = DateTime.utc(inputUtc.year + 1, 1, 1);\n\n let endDate: DateTime;\n if (midYear > nextStart) {\n endDate = midYear;\n } else {\n endDate = endYear;\n }\n\n if (endDate > seriesEnd) {\n endDate = seriesEnd;\n }\n\n return {\n period: \"semiAnnual\",\n start: nextStart,\n end: endDate,\n };\n};\n\nexport const _nextQuarterlyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n let endDate: DateTime;\n const nextStartUtc = nextStart.toUTC();\n const startMonth = nextStartUtc.month;\n\n if (startMonth < 3) {\n endDate = DateTime.utc(nextStartUtc.year, 4, 1);\n } else if (startMonth < 6) {\n endDate = DateTime.utc(nextStartUtc.year, 7, 1);\n } else if (startMonth < 9) {\n endDate = DateTime.utc(nextStartUtc.year, 10, 1);\n } else {\n endDate = DateTime.utc(nextStartUtc.year + 1, 1, 1);\n }\n\n if (endDate > seriesEnd) {\n endDate = seriesEnd;\n }\n\n return {\n period: \"quarterly\",\n start: nextStart,\n end: endDate,\n };\n};\nexport const _nextMonthlyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n // Get the first day of the next month\n const nextStartUtc = nextStart.toUTC();\n let endDate = DateTime.utc(\n nextStartUtc.year,\n nextStartUtc.month,\n nextStartUtc.day,\n )\n .plus({ months: 1 })\n .startOf(\"month\");\n\n // If the end date is after the series end, then use the series end as the end date\n if (endDate > seriesEnd) {\n if (!nextStart.hasSame(seriesEnd, \"month\")) {\n endDate = seriesEnd;\n }\n }\n\n // Otherwise, return the end date as the first day of the next month\n return {\n period: \"monthly\",\n start: nextStart,\n end: endDate,\n };\n};\n\nexport const _nextWeeklyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n // Calculate the start of the next week (Monday)\n const nextStartUtc = nextStart.toUTC();\n const nextWeekStartUTC = DateTime.utc(\n nextStartUtc.year,\n nextStartUtc.month,\n nextStartUtc.day,\n )\n .plus({ weeks: 1 })\n .startOf(\"week\");\n\n // If the calculated next week start is later or equal to the series end date, return the series end\n if (nextWeekStartUTC > seriesEnd)\n if (!nextWeekStartUTC.hasSame(seriesEnd, \"day\")) {\n return {\n period: \"weekly\",\n start: nextStart,\n end: seriesEnd,\n };\n }\n\n return {\n period: \"weekly\",\n start: nextStart,\n end: nextWeekStartUTC,\n };\n};\n\nexport const _nextDailyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n // Set the end date to the start of the next day\n const nextStartUtc = nextStart.toUTC();\n let endDate = nextStartUtc.plus({ days: 1 }).startOf(\"day\");\n if (endDate > seriesEnd || endDate.hasSame(seriesEnd, \"day\")) {\n endDate = seriesEnd;\n }\n\n return {\n period: \"daily\",\n start: nextStart,\n end: endDate,\n };\n};\n\nexport const _nextHourlyPeriod = (\n nextStart: DateTime,\n seriesEnd: DateTime,\n): AnalyticsPeriod | null => {\n if (seriesEnd <= nextStart) {\n return null;\n }\n\n // Anchor to the top of the hour: the first period may be partial, every\n // later period covers a full clock hour.\n const startDate = nextStart.toUTC();\n let endDate = startDate.plus({ hours: 1 }).startOf(\"hour\");\n\n if (endDate > seriesEnd) {\n if (nextStart.hour !== seriesEnd.hour) {\n endDate = seriesEnd.toUTC();\n }\n }\n\n return {\n period: \"hourly\",\n start: nextStart,\n end: endDate,\n };\n};\n","/* eslint-disable @typescript-eslint/no-redundant-type-constituents */\n/* eslint-disable no-case-declarations */\nimport { DateTime, Interval } from \"luxon\";\nimport type { AnalyticsGranularity } from \"./AnalyticsQuery.js\";\nimport {\n type AnalyticsDimension,\n type AnalyticsSeries,\n} from \"./AnalyticsQuery.js\";\nimport {\n type AnalyticsPeriod,\n type AnalyticsRange,\n getPeriodSeriesArray,\n} from \"./AnalyticsTimeSlicer.js\";\n\nexport const getQuarter = (date: DateTime) => {\n return Math.floor((date.month - 1) / 3) + 1;\n};\n\nexport type GroupedPeriodResult = {\n period: string;\n start: DateTime;\n end: DateTime;\n rows: Array<{\n dimensions: Record<string, AnalyticsDimension>;\n metric: string;\n unit: string | null;\n value: number;\n\n /**\n * The sum of all metric values over this period?\n */\n sum: number;\n }>;\n};\n\nexport type GroupedPeriodResults = Array<GroupedPeriodResult>;\n\nexport class AnalyticsDiscretizer {\n public static discretize(\n series: AnalyticsSeries<string>[],\n dimensions: string[],\n start: DateTime | null,\n end: DateTime | null,\n granularity: AnalyticsGranularity,\n ): GroupedPeriodResults {\n const index = this._buildIndex(series, dimensions);\n const periods = getPeriodSeriesArray(\n this._calculateRange(start, end, granularity, series),\n );\n const disretizedResults = this._discretizeNode(\n index,\n {},\n dimensions,\n periods,\n );\n const groupedResults = this._groupResultsByPeriod(\n periods,\n disretizedResults,\n );\n\n return groupedResults;\n }\n\n private static _calculateRange(\n start: DateTime | null,\n end: DateTime | null,\n granularity: AnalyticsGranularity,\n results: AnalyticsSeries<any>[],\n ) {\n let calculatedStart: DateTime | null = start || null;\n let calculatedEnd: DateTime | null = end || null;\n\n if (calculatedStart == null || calculatedEnd == null) {\n for (const r of results) {\n if (calculatedStart == null) {\n calculatedStart = r.start;\n }\n\n const endValue = r.end || r.start;\n if (calculatedEnd == null || calculatedEnd < endValue) {\n calculatedEnd = endValue;\n }\n }\n }\n\n if (calculatedStart == null || calculatedEnd == null) {\n throw new Error(\"Cannot determine query start and/or end.\");\n }\n\n return {\n start: calculatedStart,\n end: calculatedEnd,\n granularity,\n } as AnalyticsRange;\n }\n\n public static _groupResultsByPeriod(\n periods: AnalyticsPeriod[],\n dimensionedResults: DimensionedSeries[],\n ): GroupedPeriodResults {\n const result: Record<string, GroupedPeriodResult> = {};\n\n for (const p of periods) {\n const id = p.start.toISO() + \"-\" + p.period;\n const period = AnalyticsDiscretizer._getPeriodString(p);\n result[id] = {\n period: period,\n start: p.start,\n end: p.end,\n rows: [],\n };\n }\n\n for (const r of dimensionedResults) {\n for (const period of Object.keys(r.series)) {\n result[period].rows.push({\n dimensions: r.dimensions,\n metric: r.metric,\n unit: r.unit == \"__NULL__\" ? null : r.unit,\n value: r.series[period].inc,\n sum: r.series[period].sum,\n });\n }\n }\n\n return Object.values(result);\n }\n\n static _getPeriodString(p: AnalyticsPeriod) {\n // Period boundaries are anchored to the UTC calendar (see\n // AnalyticsTimeSlicer), so labels are derived from the UTC view of the\n // period start, never from a zoned start's local calendar fields.\n const startUtc = p.start.toUTC();\n switch (p.period) {\n case \"annual\":\n return startUtc.year.toString();\n case \"semiAnnual\":\n return `${startUtc.year}/${startUtc.month < 7 ? \"H1\" : \"H2\"}`;\n case \"quarterly\":\n return `${startUtc.year}/Q${getQuarter(startUtc)}`;\n case \"monthly\":\n const month = startUtc.month;\n const formattedMonth = month < 10 ? `0${month}` : `${month}`;\n return `${startUtc.year}/${formattedMonth}`;\n case \"weekly\":\n return `${startUtc.weekYear}/W${startUtc.weekNumber}`;\n case \"daily\":\n const monthD = startUtc.month;\n const day = startUtc.day;\n const formattedMonthD = monthD < 10 ? `0${monthD}` : `${monthD}`;\n const formattedDay = day < 10 ? `0${day}` : `${day}`;\n return `${startUtc.year}/${formattedMonthD}/${formattedDay}`;\n case \"hourly\":\n const monthH = startUtc.month;\n const dayH = startUtc.day;\n const hourH = startUtc.hour;\n const formattedMonthH = monthH < 10 ? `0${monthH}` : `${monthH}`;\n const formattedDayH = dayH < 10 ? `0${dayH}` : `${dayH}`;\n const formattedHourH = hourH < 10 ? `0${hourH}` : `${hourH}`;\n return `${startUtc.year}/${formattedMonthH}/${formattedDayH}/${formattedHourH}`;\n default:\n return p.period;\n }\n }\n\n public static _discretizeNode(\n node: DiscretizerIndexNode,\n dimensionValues: Record<string, string>,\n remainingDimensions: string[],\n periods: AnalyticsPeriod[],\n ): DimensionedSeries[] {\n const result: DimensionedSeries[] = [];\n\n if (remainingDimensions.length > 0) {\n const subdimension = remainingDimensions[0] as string;\n Object.keys(node).forEach((subdimensionValue, _index, _arr) => {\n const newDimensionValues = { ...dimensionValues };\n newDimensionValues[subdimension] = subdimensionValue;\n result.push(\n ...this._discretizeNode(\n node[subdimensionValue] as DiscretizerIndexNode,\n newDimensionValues,\n remainingDimensions.slice(1),\n periods,\n ),\n );\n });\n } else {\n Object.keys(node).forEach((metric) => {\n result.push(\n ...this._discretizeLeaf(\n node[metric] as DiscretizerIndexLeaf,\n periods,\n metric,\n dimensionValues,\n ),\n );\n });\n }\n\n return result;\n }\n\n public static _discretizeLeaf(\n leaf: DiscretizerIndexLeaf,\n periods: AnalyticsPeriod[],\n metric: string,\n dimensionValues: Record<string, string>,\n ): DimensionedSeries[] {\n const result: DimensionedSeries[] = [];\n Object.keys(leaf).forEach((unit) => {\n const metaDimensions: any = {};\n Object.keys(dimensionValues).forEach((k) => {\n metaDimensions[k] = {\n path: leaf[unit][0].dimensions[k],\n icon: leaf[unit][0].dimensions.icon,\n label: leaf[unit][0].dimensions.label,\n description: leaf[unit][0].dimensions.description,\n };\n });\n result.push({\n unit,\n metric,\n dimensions: metaDimensions as any,\n series: this._discretizeSeries(leaf[unit], periods),\n });\n });\n\n return result;\n }\n\n public static _discretizeSeries(\n series: AnalyticsSeries<string>[],\n periods: AnalyticsPeriod[],\n ): Series {\n const result: Series = {};\n\n // An empty range (start equal to end) produces no periods; there is\n // nothing to attribute values to.\n if (periods.length === 0) {\n return result;\n }\n\n for (const s of series) {\n let oldSum = this._getValue(s, periods[0].start);\n for (const p of periods) {\n // Periods are half-open [start, end): the cumulative value \"as of\"\n // a boundary excludes values stamped exactly on it, so a value at\n // an exact period start is attributed to the period that starts\n // there, not the one that ends there.\n const newSum = this._getValue(s, p.end);\n const id = `${p.start.toISO()}-${p.period}`;\n\n // const id = p.period;\n if (result[id]) {\n result[id].inc += newSum - oldSum;\n result[id].sum += newSum;\n } else {\n result[id] = {\n inc: newSum - oldSum,\n sum: newSum,\n };\n }\n\n oldSum = newSum;\n }\n }\n\n return result;\n }\n\n public static _getValue(\n series: AnalyticsSeries<string>,\n when: DateTime,\n ): number {\n switch (series.fn) {\n case \"Single\":\n return this._getSingleValue(series, when);\n case \"DssVest\":\n return this._getVestValue(series, when);\n default:\n // todo: logging interface\n //console.error(`Unknown analytics series function: '${series.fn}'`);\n return 0.0;\n }\n }\n\n public static _getSingleValue(\n series: AnalyticsSeries<string>,\n when: DateTime,\n ): number {\n // Strictly greater than: the cumulative value at instant t covers values\n // stamped before t only. Periods are half-open [start, end), so a value\n // stamped exactly at a boundary belongs to the period that starts there.\n return when > series.start ? series.value : 0.0;\n }\n\n public static _getVestValue(\n series: AnalyticsSeries<string>,\n when: DateTime,\n ): number {\n const now = when;\n const start = series.start;\n const end = series.end;\n\n const cliff = series.params?.cliff\n ? DateTime.fromISO(series.params.cliff! as string, { zone: \"utc\" })\n : null;\n if (now < start || (cliff && now < cliff)) {\n return 0.0;\n } else if (end && now >= end) {\n return series.value;\n }\n\n const a = Interval.fromDateTimes(start, now);\n const b = Interval.fromDateTimes(start, end || now);\n\n return (a.length() / b.length()) * series.value;\n }\n\n public static _buildIndex(\n series: AnalyticsSeries<string>[],\n dimensions: string[],\n ): DiscretizerIndexNode {\n const result: DiscretizerIndexNode | any = {};\n const map: DiscretizerIndexLeaf = {};\n const dimName = dimensions[0] || \"\";\n\n for (const s of series) {\n const dimValue = s.dimensions[dimName];\n if (undefined === map[dimValue]) {\n map[dimValue] = [];\n }\n map[dimValue].push(s);\n }\n\n if (dimensions.length > 1) {\n const newDimensions = dimensions.slice(1);\n Object.keys(map).forEach((k) => {\n result[k] = this._buildIndex(map[k], newDimensions);\n });\n } else {\n Object.keys(map).forEach((k) => {\n result[k] = this._buildMetricsIndex(map[k]);\n });\n }\n\n return result;\n }\n\n public static _buildMetricsIndex(\n series: AnalyticsSeries<string>[],\n ): DiscretizerIndexNode {\n const result: DiscretizerIndexNode = {};\n\n const map: DiscretizerIndexLeaf = {};\n for (const s of series) {\n const metric = s.metric;\n if (undefined === map[metric]) {\n map[metric] = [];\n }\n\n map[metric].push(s);\n }\n\n Object.keys(map).forEach((k) => (result[k] = this._buildUnitIndex(map[k])));\n return result;\n }\n\n public static _buildUnitIndex(\n series: AnalyticsSeries<string>[],\n ): DiscretizerIndexLeaf {\n const result: DiscretizerIndexLeaf = {};\n\n for (const s of series) {\n const unit = s.unit || \"__NULL__\";\n if (undefined === result[unit]) {\n result[unit] = [];\n }\n\n result[unit].push(s);\n }\n\n return result;\n }\n}\n\ntype DiscretizerIndexLeaf = { [k: string]: AnalyticsSeries<string>[] };\ntype DiscretizerIndexNode = {\n [k: string]: DiscretizerIndexNode | DiscretizerIndexLeaf;\n};\ntype Series = Record<\n string,\n {\n /**\n * How much the metric increased from the beginning of the series to the end of the specified period.\n */\n inc: number;\n\n /**\n * The sum of all metric values from the beginning of the series to the end of the specified period.\n */\n sum: number;\n }\n>;\ntype DimensionedSeries = {\n unit: string;\n metric: string;\n dimensions: Record<string, AnalyticsDimension>;\n series: Series;\n};\n","/* eslint-disable @typescript-eslint/no-redundant-type-constituents */\nimport {\n AnalyticsDiscretizer,\n type GroupedPeriodResults,\n type GroupedPeriodResult,\n} from \"./AnalyticsDiscretizer.js\";\nimport { AnalyticsPath } from \"./AnalyticsPath.js\";\nimport {\n type IAnalyticsProfiler,\n PassthroughAnalyticsProfiler,\n} from \"./AnalyticsProfiler.js\";\nimport {\n type AnalyticsQuery,\n type AnalyticsSeries,\n type AnalyticsSeriesQuery,\n type CompoundAnalyticsQuery,\n type MultiCurrencyConversion,\n CompoundOperator,\n} from \"./AnalyticsQuery.js\";\nimport { type IAnalyticsStore } from \"./IAnalyticsStore.js\";\n\nexport class AnalyticsQueryEngine {\n private readonly _profiler: IAnalyticsProfiler;\n\n public constructor(\n private readonly _analyticsStore: IAnalyticsStore,\n profiler?: IAnalyticsProfiler,\n ) {\n this._profiler = profiler ?? new PassthroughAnalyticsProfiler();\n }\n\n public async executeCompound(\n query: CompoundAnalyticsQuery,\n ): Promise<GroupedPeriodResults> {\n let result: GroupedPeriodResults;\n\n const inputsQuery: AnalyticsQuery = {\n start: query.start,\n end: query.end,\n granularity: query.granularity,\n lod: query.lod,\n select: query.select,\n metrics: query.expression.inputs.metrics,\n currency: query.expression.inputs.currency,\n };\n\n const operandQuery: AnalyticsQuery = {\n start: query.start,\n end: query.end,\n granularity: query.granularity,\n lod: { priceData: 1 },\n select: { priceData: [AnalyticsPath.fromString(\"atlas\")] },\n metrics: [query.expression.operand.metric],\n currency: query.expression.operand.currency,\n };\n\n const inputExecute = await this.execute(inputsQuery);\n const operandExecute = await this.execute(operandQuery);\n\n if (\n [CompoundOperator.VectorAdd, CompoundOperator.VectorSubtract].includes(\n query.expression.operator,\n )\n ) {\n result = await this._profiler.record(\n \"ApplyVectorOperator\",\n async () =>\n await this._applyVectorOperator(\n inputExecute,\n operandExecute,\n query.expression.operator,\n query.expression.resultCurrency,\n ),\n );\n } else {\n result = await this._profiler.record(\n \"ApplyScalarOperator\",\n async () =>\n await this._applyScalarOperator(\n inputExecute,\n operandExecute,\n query.expression.operator,\n query.expression.operand.useSum,\n query.expression.resultCurrency,\n ),\n );\n }\n\n return result;\n }\n\n public async execute(query: AnalyticsQuery): Promise<GroupedPeriodResults> {\n const seriesResults = await this._executeSeriesQuery(query);\n\n const normalizedSeriesResults = this._profiler.recordSync(\"ApplyLODs\", () =>\n this._applyLods(seriesResults, query.lod),\n );\n\n const dimensions = Object.keys(query.select);\n const discretizedResult = this._profiler.recordSync(\"Discretize\", () =>\n normalizedSeriesResults.length < 1\n ? []\n : AnalyticsDiscretizer.discretize(\n normalizedSeriesResults,\n dimensions,\n query.start,\n query.end,\n query.granularity,\n ),\n );\n return discretizedResult;\n }\n\n public async executeMultiCurrency(\n query: AnalyticsQuery,\n mcc: MultiCurrencyConversion,\n ): Promise<GroupedPeriodResults> {\n const baseQuery: AnalyticsQuery = {\n ...query,\n currency: mcc.targetCurrency ?? query.currency,\n };\n let result = await this.execute(baseQuery);\n\n for (const conversion of mcc.conversions) {\n const nextQuery: CompoundAnalyticsQuery = {\n start: query.start,\n end: query.end,\n granularity: query.granularity,\n lod: query.lod,\n select: query.select,\n expression: {\n inputs: {\n metrics: baseQuery.metrics,\n currency: conversion.currency,\n },\n operator: CompoundOperator.ScalarMultiply,\n operand: {\n metric: conversion.metric,\n currency: mcc.targetCurrency,\n useSum: true,\n },\n resultCurrency: mcc.targetCurrency,\n },\n };\n\n const executedCompound = await this.executeCompound(nextQuery);\n result = await this._applyVectorOperator(\n result,\n executedCompound,\n CompoundOperator.VectorAdd,\n mcc.targetCurrency,\n );\n }\n return result;\n }\n\n private _applyVectorOperator(\n inputsA: GroupedPeriodResults,\n inputsB: GroupedPeriodResults,\n operator: CompoundOperator,\n _resultCurrency?: AnalyticsPath,\n ): Promise<GroupedPeriodResults> {\n if (\n [CompoundOperator.ScalarMultiply, CompoundOperator.ScalarDivide].includes(\n operator,\n )\n ) {\n throw new Error(\"Invalid operator for vector operation\");\n }\n return Promise.resolve(inputsB);\n }\n\n private _applyScalarOperator(\n inputs: GroupedPeriodResults, // expected input is the budget & actuals in 2022 monthly granularity in MKR\n operand: GroupedPeriodResults, // expected input is the daily mkr price change in 2022 monthly granularity in DAI\n operator: CompoundOperator, // expected to me multiply and later addition\n useOperandSum: boolean,\n resultCurrency?: AnalyticsPath, // expected to be DAI\n ): Promise<GroupedPeriodResults> {\n if (\n [CompoundOperator.VectorAdd, CompoundOperator.VectorSubtract].includes(\n operator,\n )\n ) {\n throw new Error(\"Invalid operator for scalar operation\");\n }\n\n const result: GroupedPeriodResults = [];\n const operandMap: Record<string, number> = {};\n const key = useOperandSum ? \"sum\" : \"value\";\n\n for (const operandPeriod of operand) {\n if (operandPeriod.rows.length > 0) {\n operandMap[operandPeriod.period] = operandPeriod.rows[0][key];\n }\n }\n\n // let previousValue: number = 1;\n for (const inputPeriod of inputs) {\n const outputPeriod: GroupedPeriodResult = {\n period: inputPeriod.period,\n start: inputPeriod.start,\n end: inputPeriod.end,\n rows: inputPeriod.rows.map((row) => {\n const newRow = {\n dimensions: row.dimensions,\n metric: row.metric,\n unit: resultCurrency ? resultCurrency.toString() : row.unit,\n value: this._calculateOutputValue(\n row.value,\n operator,\n operandMap[inputPeriod.period],\n ),\n sum: -1,\n };\n return newRow;\n }),\n };\n result.push(outputPeriod);\n }\n\n return Promise.resolve(result);\n }\n\n private _calculateOutputValue(\n input: number,\n operator: CompoundOperator,\n operand: number,\n ): number {\n switch (operator) {\n case CompoundOperator.VectorAdd:\n return input + operand;\n case CompoundOperator.VectorSubtract:\n return input - operand;\n case CompoundOperator.ScalarMultiply:\n return input * operand;\n case CompoundOperator.ScalarDivide:\n return input / operand;\n }\n }\n\n private async _executeSeriesQuery(\n query: AnalyticsQuery,\n ): Promise<AnalyticsSeries[]> {\n const seriesQuery: AnalyticsSeriesQuery = {\n start: query.start,\n end: query.end,\n select: query.select,\n metrics: query.metrics,\n currency: query.currency,\n };\n\n return await this._analyticsStore.getMatchingSeries(seriesQuery);\n }\n\n private _applyLods(\n series: AnalyticsSeries[],\n lods: Record<string, number | null>,\n ): AnalyticsSeries<string>[] {\n return series.map((result) => ({\n ...result,\n dimensions: this._applyDimensionsLods(result.dimensions, lods),\n }));\n }\n\n private _applyDimensionsLods(\n dimensionMap: Record<string, AnalyticsPath> | any,\n lods: Record<string, number | null>,\n ) {\n const result: Record<string, string> | any = {};\n for (const [dimension, lod] of Object.entries(lods)) {\n if (lod !== null && dimensionMap[dimension]) {\n result[dimension] = dimensionMap[dimension][\"path\"]\n .applyLod(lod)\n .toString();\n result[\"icon\"] = dimensionMap[dimension][\"icon\"].toString();\n result[\"label\"] = dimensionMap[dimension][\"label\"].toString();\n result[\"description\"] =\n dimensionMap[dimension][\"description\"].toString();\n }\n }\n return result;\n }\n\n public async getDimensions(): Promise<any> {\n return await this._analyticsStore.getDimensions();\n }\n\n /*public async getMetrics(): Promise<string[]> {\n return await this._analyticsStore.getMetrics();\n }\n\n public async getCurrencies(): Promise<string[]> {\n return await this._analyticsStore.getCurrencies();\n }*/\n}\n","import type { AnalyticsPath } from \"./AnalyticsPath.js\";\nimport type { AnalyticsUpdateCallback } from \"./IAnalyticsStore.js\";\n\nexport class NotificationError extends Error {\n public readonly innerErrors: Error[];\n\n constructor(errors: Error[]) {\n super(errors.map((e) => e.message).join(\"\\n\"));\n\n this.name = \"NotificationError\";\n this.innerErrors = errors;\n }\n}\n\n/**\n * Manages subscriptions for analytics paths.\n */\nexport class AnalyticsSubscriptionManager {\n private _subscriptions: Map<string, Set<AnalyticsUpdateCallback>> = new Map();\n\n /**\n * Subscribe to updates for an analytics path. A subscribed function will be\n * called for:\n *\n * - exact path matches\n * - matching child paths (i.e. an update to /a/b/c will trigger a callback\n * for /a/b/c, /a/b, and /a)\n * - wildcard matches\n *\n * @param path The analytics path to subscribe to.\n * @param callback Function to be called when the path is updated.\n *\n * @returns A function that, when called, unsubscribes from the updates.\n */\n public subscribeToPath(\n path: AnalyticsPath,\n callback: AnalyticsUpdateCallback,\n ): () => void {\n const pathString = this.normalizePath(path.toString(\"/\"));\n\n if (!this._subscriptions.has(pathString)) {\n this._subscriptions.set(pathString, new Set());\n }\n\n this._subscriptions.get(pathString)!.add(callback);\n\n return () => {\n const callbacks = this._subscriptions.get(pathString);\n if (callbacks) {\n callbacks.delete(callback);\n\n // only remove the path entry if there are no more callbacks\n if (callbacks.size === 0) {\n this._subscriptions.delete(pathString);\n }\n }\n };\n }\n\n /**\n * Notifies subscribers about updates to paths.\n *\n * @param paths The paths that were updated.\n */\n public notifySubscribers(paths: AnalyticsPath[]): void {\n if (paths.length === 0 || this._subscriptions.size === 0) {\n return;\n }\n\n const errors: Error[] = [];\n for (const path of paths) {\n const pathString = this.normalizePath(path.toString(\"/\"));\n const pathPrefixes = this.getPathPrefixes(pathString);\n\n const matchingSubscriptions: Array<{\n prefix: string;\n callbacks: Set<AnalyticsUpdateCallback>;\n }> = [];\n\n // add the exact prefix matches\n pathPrefixes\n .filter((prefix) => this._subscriptions.has(prefix))\n .forEach((prefix) => {\n matchingSubscriptions.push({\n prefix,\n callbacks: this._subscriptions.get(prefix)!,\n });\n });\n\n // check all wildcard patterns\n for (const [\n subscriptionPath,\n callbacks,\n ] of this._subscriptions.entries()) {\n // skip if it's already in the exact matches\n if (pathPrefixes.includes(subscriptionPath)) {\n continue;\n }\n\n // hceck if this subscription path (which might have wildcards) matches the update path\n if (this.pathMatchesWildcardPattern(pathString, subscriptionPath)) {\n matchingSubscriptions.push({\n prefix: subscriptionPath,\n callbacks,\n });\n }\n }\n\n if (matchingSubscriptions.length === 0) continue;\n\n // guarantee delivery to all subscribers\n for (const { callbacks } of matchingSubscriptions) {\n for (const callback of callbacks) {\n try {\n callback(path);\n } catch (e) {\n errors.push(e as Error);\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new NotificationError(errors);\n }\n }\n\n /**\n * Normalizes a path string to ensure consistent comparison.\n */\n private normalizePath(path: string): string {\n // Handle potential double slashes by first splitting on slashes and rejoining\n const parts = path.split(\"/\").filter((p) => p.length > 0);\n const normalized = \"/\" + parts.join(\"/\");\n return normalized;\n }\n\n /**\n * Checks if a path matches a subscription pattern that may contain wildcards.\n */\n private pathMatchesWildcardPattern(\n updatePath: string,\n subscriptionPath: string,\n ): boolean {\n // Handle the wildcard segment case\n if (subscriptionPath.includes(\"*\")) {\n const updateSegments = updatePath.split(\"/\").filter((s) => s.length > 0);\n const subscriptionSegments = subscriptionPath\n .split(\"/\")\n .filter((s) => s.length > 0);\n\n // If subscription is longer than the update path, it can't match\n if (subscriptionSegments.length > updateSegments.length) {\n return false;\n }\n\n // Compare each segment\n for (let i = 0; i < subscriptionSegments.length; i++) {\n const subSegment = subscriptionSegments[i];\n const updateSegment = updateSegments[i];\n\n // If segment is * or starts with * (wildcard), it matches anything\n if (subSegment === \"*\" || subSegment.startsWith(\"*:\")) {\n continue;\n }\n\n // Otherwise, segments should match exactly\n if (subSegment !== updateSegment) {\n return false;\n }\n }\n\n return true;\n }\n\n // If no wildcards, check if subscription is a prefix of the update path\n return (\n updatePath === subscriptionPath ||\n updatePath.startsWith(subscriptionPath + \"/\")\n );\n }\n\n /**\n * Gets all path prefixes for a given path.\n *\n * For example, for '/a/b/c' it returns ['/a', '/a/b', '/a/b/c'].\n */\n private getPathPrefixes(path: string): string[] {\n const segments = path.split(\"/\").filter((s) => s.length > 0);\n const prefixes: string[] = [];\n\n let currentPath = \"\";\n for (const segment of segments) {\n currentPath = currentPath ? `${currentPath}/${segment}` : `/${segment}`;\n prefixes.push(currentPath);\n }\n\n if (prefixes.length === 0 && path === \"/\") {\n prefixes.push(\"/\");\n }\n\n return prefixes;\n }\n}\n","import type { SqlQueryLogger, SqlResultsLogger } from \"./types.js\";\n\nexport const defaultQueryLogger =\n (tag: string): SqlQueryLogger =>\n (index: number, query: string) => {\n console.log(`[${tag}][Q:${index}]: ${query}\\n`);\n };\n\nexport const defaultResultsLogger =\n (tag: string): SqlResultsLogger =>\n (index: number, results: any) => {\n if (Array.isArray(results)) {\n console.log(`[${tag}][R:${index}]: ${results.length} results\\n`);\n } else {\n console.log(`[${tag}][R:${index}]: Received ${typeof results}.\\n`);\n }\n };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerhousedao/analytics-engine-core",
3
- "version": "6.2.3-dev.7",
3
+ "version": "6.2.3-dev.70",
4
4
  "license": "AGPL-3.0-only",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,12 +24,12 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "luxon": "3.7.2",
27
- "@powerhousedao/shared": "6.2.3-dev.7"
27
+ "@powerhousedao/shared": "6.2.3-dev.70"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/luxon": "3.7.1",
31
- "vitest": "4.1.1",
32
- "tsdown": "0.21.1"
31
+ "vitest": "5.0.3",
32
+ "tsdown": "0.23.0"
33
33
  },
34
34
  "description": "Powerhouse analytics engine — time-series query engine for document drive metrics and usage data.",
35
35
  "keywords": [