@objectstack/service-analytics 9.8.0 → 9.9.1
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.cjs +31 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +29 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -38,7 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
module.exports = __toCommonJS(index_exports);
|
|
39
39
|
|
|
40
40
|
// src/analytics-service.ts
|
|
41
|
-
var
|
|
41
|
+
var import_core2 = require("@objectstack/core");
|
|
42
42
|
|
|
43
43
|
// src/cube-registry.ts
|
|
44
44
|
var CubeRegistry = class {
|
|
@@ -675,7 +675,11 @@ var ObjectQLStrategy = class {
|
|
|
675
675
|
// contract types groupBy as string[]; the cast carries the richer shape.
|
|
676
676
|
groupBy: groupBy.length > 0 ? groupBy : void 0,
|
|
677
677
|
aggregations: aggregations.length > 0 ? aggregations : void 0,
|
|
678
|
-
filter: Object.keys(filter).length > 0 ? filter : void 0
|
|
678
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
679
|
+
// ADR-0053 Phase 2 (D2): forward the reference tz so date buckets resolve
|
|
680
|
+
// on that zone's calendar days. A non-UTC zone makes the engine bucket
|
|
681
|
+
// in-memory (uniform across drivers); UTC/unset keeps the DB fast path.
|
|
682
|
+
timezone: query.timezone
|
|
679
683
|
});
|
|
680
684
|
const mappedRows = rows.map((row) => {
|
|
681
685
|
const mapped = {};
|
|
@@ -1074,7 +1078,8 @@ var DatasetExecutor = class {
|
|
|
1074
1078
|
measures: unfiltered,
|
|
1075
1079
|
dimensions,
|
|
1076
1080
|
where: baseFilter,
|
|
1077
|
-
selection
|
|
1081
|
+
selection,
|
|
1082
|
+
contextTimezone: context?.timezone
|
|
1078
1083
|
}), context);
|
|
1079
1084
|
} else {
|
|
1080
1085
|
result = { rows: [], fields: [] };
|
|
@@ -1085,7 +1090,8 @@ var DatasetExecutor = class {
|
|
|
1085
1090
|
measures: [m],
|
|
1086
1091
|
dimensions,
|
|
1087
1092
|
where: mFilter,
|
|
1088
|
-
selection
|
|
1093
|
+
selection,
|
|
1094
|
+
contextTimezone: context?.timezone
|
|
1089
1095
|
}), context);
|
|
1090
1096
|
result.rows = mergeByDimensions(result.rows, sub.rows, dimensions, [m]);
|
|
1091
1097
|
result.fields.push({ name: m, type: "number" });
|
|
@@ -1109,7 +1115,9 @@ var DatasetExecutor = class {
|
|
|
1109
1115
|
cube: compiled.cube.name,
|
|
1110
1116
|
measures: opts.measures,
|
|
1111
1117
|
dimensions: opts.dimensions,
|
|
1112
|
-
|
|
1118
|
+
// Precedence: explicit selection tz → request's reference tz
|
|
1119
|
+
// (ExecutionContext.timezone, ADR-0053 Phase 2) → UTC.
|
|
1120
|
+
timezone: opts.selection.timezone ?? opts.contextTimezone ?? "UTC"
|
|
1113
1121
|
};
|
|
1114
1122
|
if (opts.where) q.where = opts.where;
|
|
1115
1123
|
const selTimeDims = opts.selection.timeDimensions ?? [];
|
|
@@ -1147,7 +1155,7 @@ var DatasetExecutor = class {
|
|
|
1147
1155
|
dimensions,
|
|
1148
1156
|
where: baseFilter,
|
|
1149
1157
|
timeDimensions: shiftedTd,
|
|
1150
|
-
timezone: selection.timezone ?? "UTC"
|
|
1158
|
+
timezone: selection.timezone ?? context?.timezone ?? "UTC"
|
|
1151
1159
|
}, context);
|
|
1152
1160
|
return sub.rows.map((row) => {
|
|
1153
1161
|
const out = {};
|
|
@@ -1260,6 +1268,7 @@ function pickDisplayField(fields) {
|
|
|
1260
1268
|
}
|
|
1261
1269
|
|
|
1262
1270
|
// src/preview-evaluator.ts
|
|
1271
|
+
var import_core = require("@objectstack/core");
|
|
1263
1272
|
function compare(a, b) {
|
|
1264
1273
|
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
1265
1274
|
return String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0;
|
|
@@ -1307,23 +1316,23 @@ function matchesWhere(row, where) {
|
|
|
1307
1316
|
}
|
|
1308
1317
|
return true;
|
|
1309
1318
|
}
|
|
1310
|
-
function bucketDate(value, granularity) {
|
|
1319
|
+
function bucketDate(value, granularity, timezone) {
|
|
1311
1320
|
const d = new Date(String(value));
|
|
1312
1321
|
if (Number.isNaN(d.getTime())) return null;
|
|
1313
|
-
const y =
|
|
1314
|
-
const m = `${
|
|
1315
|
-
const day = `${
|
|
1322
|
+
const { year: y, month, day: dayNum } = (0, import_core.calendarPartsInTzOrUtc)(d, timezone);
|
|
1323
|
+
const m = `${month}`.padStart(2, "0");
|
|
1324
|
+
const day = `${dayNum}`.padStart(2, "0");
|
|
1316
1325
|
switch (granularity) {
|
|
1317
1326
|
case "year":
|
|
1318
1327
|
return `${y}`;
|
|
1319
1328
|
case "quarter":
|
|
1320
|
-
return `${y}-Q${Math.floor(
|
|
1329
|
+
return `${y}-Q${Math.floor((month - 1) / 3) + 1}`;
|
|
1321
1330
|
case "month":
|
|
1322
1331
|
return `${y}-${m}`;
|
|
1323
1332
|
case "week": {
|
|
1324
|
-
const monday = new Date(
|
|
1325
|
-
const dow = (
|
|
1326
|
-
monday.setUTCDate(
|
|
1333
|
+
const monday = new Date(Date.UTC(y, month - 1, dayNum));
|
|
1334
|
+
const dow = (monday.getUTCDay() + 6) % 7;
|
|
1335
|
+
monday.setUTCDate(monday.getUTCDate() - dow);
|
|
1327
1336
|
return monday.toISOString().slice(0, 10);
|
|
1328
1337
|
}
|
|
1329
1338
|
case "day":
|
|
@@ -1368,6 +1377,7 @@ function evaluateAnalyticsQueryOverRows(query, cube, rows) {
|
|
|
1368
1377
|
});
|
|
1369
1378
|
}
|
|
1370
1379
|
const dimensions = query.dimensions ?? [];
|
|
1380
|
+
const timezone = query.timezone;
|
|
1371
1381
|
const granByDim = new Map(timeDims.filter((t) => t.granularity).map((t) => [t.dimension, t.granularity]));
|
|
1372
1382
|
const keyOf = (r) => {
|
|
1373
1383
|
const values = {};
|
|
@@ -1376,7 +1386,7 @@ function evaluateAnalyticsQueryOverRows(query, cube, rows) {
|
|
|
1376
1386
|
const field = String(dim?.sql ?? name);
|
|
1377
1387
|
const raw = r[field];
|
|
1378
1388
|
const gran = granByDim.get(name) ?? (dim?.type === "time" && dim.granularities?.length === 1 ? String(dim.granularities[0]) : void 0);
|
|
1379
|
-
values[name] = gran ? bucketDate(raw, gran) : raw ?? null;
|
|
1389
|
+
values[name] = gran ? bucketDate(raw, gran, timezone) : raw ?? null;
|
|
1380
1390
|
}
|
|
1381
1391
|
return { key: JSON.stringify(values), values };
|
|
1382
1392
|
};
|
|
@@ -1431,7 +1441,7 @@ var AnalyticsService = class {
|
|
|
1431
1441
|
constructor(config = {}) {
|
|
1432
1442
|
/** Compiled datasets by name — feeds the join allowlist (D-C) and queryDataset. */
|
|
1433
1443
|
this.datasetRegistry = /* @__PURE__ */ new Map();
|
|
1434
|
-
this.logger = config.logger || (0,
|
|
1444
|
+
this.logger = config.logger || (0, import_core2.createLogger)({ level: "info", format: "pretty" });
|
|
1435
1445
|
this.cubeRegistry = new CubeRegistry();
|
|
1436
1446
|
if (config.cubes) {
|
|
1437
1447
|
this.cubeRegistry.registerAll(config.cubes);
|
|
@@ -1857,7 +1867,7 @@ var AnalyticsServicePlugin = class {
|
|
|
1857
1867
|
'[Analytics] No "data" service registered yet at init; will retry per-query. Register ObjectQLPlugin or pass executeAggregate.'
|
|
1858
1868
|
);
|
|
1859
1869
|
}
|
|
1860
|
-
executeAggregate = async (objectName, { groupBy, aggregations, filter }) => {
|
|
1870
|
+
executeAggregate = async (objectName, { groupBy, aggregations, filter, timezone }) => {
|
|
1861
1871
|
const engine = tryGetDataEngine();
|
|
1862
1872
|
if (!engine) {
|
|
1863
1873
|
throw new Error(
|
|
@@ -1871,7 +1881,10 @@ var AnalyticsServicePlugin = class {
|
|
|
1871
1881
|
function: a.method,
|
|
1872
1882
|
field: a.field,
|
|
1873
1883
|
alias: a.alias
|
|
1874
|
-
}))
|
|
1884
|
+
})),
|
|
1885
|
+
// ADR-0053 Phase 2: thread the reference tz so date buckets resolve on
|
|
1886
|
+
// that zone's calendar days (engine buckets in-memory when non-UTC).
|
|
1887
|
+
timezone
|
|
1875
1888
|
});
|
|
1876
1889
|
return rows;
|
|
1877
1890
|
};
|