@happyvertical/smrt-reports 0.57.6 → 0.57.8

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.
Files changed (74) hide show
  1. package/AGENTS.md +10 -0
  2. package/dist/chunks/runtime-time-D7JZ1e4J.js +1052 -0
  3. package/dist/chunks/runtime-time-D7JZ1e4J.js.map +1 -0
  4. package/dist/index.d.ts +7 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +58 -591
  7. package/dist/index.js.map +1 -1
  8. package/dist/manifest.json +28 -4
  9. package/dist/playground.js +1 -1
  10. package/dist/playground.js.map +1 -1
  11. package/dist/recipes.d.ts +21 -0
  12. package/dist/recipes.d.ts.map +1 -1
  13. package/dist/runtime-compiler.d.ts +7 -40
  14. package/dist/runtime-compiler.d.ts.map +1 -1
  15. package/dist/runtime-dimensions.d.ts +43 -0
  16. package/dist/runtime-dimensions.d.ts.map +1 -0
  17. package/dist/runtime-memory.d.ts +81 -0
  18. package/dist/runtime-memory.d.ts.map +1 -0
  19. package/dist/runtime-plan.d.ts +121 -0
  20. package/dist/runtime-plan.d.ts.map +1 -0
  21. package/dist/runtime-report.d.ts.map +1 -1
  22. package/dist/runtime-server.d.ts.map +1 -1
  23. package/dist/runtime-spec-hash.d.ts +11 -0
  24. package/dist/runtime-spec-hash.d.ts.map +1 -0
  25. package/dist/runtime-spec.d.ts +16 -4
  26. package/dist/runtime-spec.d.ts.map +1 -1
  27. package/dist/runtime-time.d.ts +41 -0
  28. package/dist/runtime-time.d.ts.map +1 -0
  29. package/dist/smrt-knowledge.json +37 -12
  30. package/dist/spec.d.ts +25 -0
  31. package/dist/spec.d.ts.map +1 -0
  32. package/dist/spec.js +2 -0
  33. package/dist/state.js +1 -1
  34. package/dist/state.js.map +1 -1
  35. package/dist/svelte/ReportConfirmation.svelte +97 -24
  36. package/dist/svelte/ReportConfirmation.svelte.d.ts +20 -0
  37. package/dist/svelte/ReportConfirmation.svelte.d.ts.map +1 -1
  38. package/dist/svelte/ReportDetail.svelte +263 -12
  39. package/dist/svelte/ReportDetail.svelte.d.ts +10 -1
  40. package/dist/svelte/ReportDetail.svelte.d.ts.map +1 -1
  41. package/dist/svelte/ReportPicker.svelte +47 -14
  42. package/dist/svelte/ReportPicker.svelte.d.ts +3 -0
  43. package/dist/svelte/ReportPicker.svelte.d.ts.map +1 -1
  44. package/dist/svelte/ReportResult.svelte +216 -35
  45. package/dist/svelte/ReportResult.svelte.d.ts +8 -2
  46. package/dist/svelte/ReportResult.svelte.d.ts.map +1 -1
  47. package/dist/svelte/RuntimeReportWidgetOptions.svelte +9 -1
  48. package/dist/svelte/RuntimeReportWidgetOptions.svelte.d.ts.map +1 -1
  49. package/dist/svelte/SavedReportCard.svelte +242 -0
  50. package/dist/svelte/SavedReportCard.svelte.d.ts +30 -0
  51. package/dist/svelte/SavedReportCard.svelte.d.ts.map +1 -0
  52. package/dist/svelte/SavedReports.svelte +206 -33
  53. package/dist/svelte/SavedReports.svelte.d.ts +13 -0
  54. package/dist/svelte/SavedReports.svelte.d.ts.map +1 -1
  55. package/dist/svelte/SavedReportsWidget.svelte +59 -0
  56. package/dist/svelte/SavedReportsWidget.svelte.d.ts +19 -0
  57. package/dist/svelte/SavedReportsWidget.svelte.d.ts.map +1 -0
  58. package/dist/svelte/client.d.ts.map +1 -1
  59. package/dist/svelte/client.js +3 -0
  60. package/dist/svelte/describe.d.ts +94 -0
  61. package/dist/svelte/describe.d.ts.map +1 -0
  62. package/dist/svelte/describe.js +249 -0
  63. package/dist/svelte/index.d.ts +4 -1
  64. package/dist/svelte/index.d.ts.map +1 -1
  65. package/dist/svelte/index.js +4 -1
  66. package/dist/svelte/messages.d.ts +48 -0
  67. package/dist/svelte/messages.d.ts.map +1 -1
  68. package/dist/svelte/messages.js +59 -11
  69. package/dist/svelte/playground.d.ts +2 -2
  70. package/dist/svelte/playground.d.ts.map +1 -1
  71. package/dist/svelte/playground.js +1 -1
  72. package/dist/svelte/types.d.ts +2 -0
  73. package/dist/svelte/types.d.ts.map +1 -1
  74. package/package.json +13 -9
package/dist/index.js CHANGED
@@ -4,7 +4,8 @@ import { REPORT_LOCKS_TABLE, REPORT_REFRESH_TASKS_TABLE, REPORT_RUNS_TABLE, REPO
4
4
  import { adapterTypeFromDb, refreshReport, reportRowIdentity, tenantColumn } from "./refresh.js";
5
5
  import { ReportScheduleRunner, SmrtPrincipalReportRefreshTask, SmrtReportRefreshTask, enqueueReportRefresh, ensureReportRefreshSchedules, registerReportRefreshExecutionAuthorityHost, registerReportRefreshInterceptor, registerReportRefreshJobIntegritySigner } from "./scheduler.js";
6
6
  import { bucketExpr, buildAggregate } from "./aggregate.js";
7
- import { DataQueryValidationError, ObjectRegistry, SmrtCollection, SmrtError, SmrtObject, SmrtRecipe, createDataQueryFingerprint, field, normalizeDataQueryRequest, normalizeDataQueryResult, runOnce, smrt } from "@happyvertical/smrt-core";
7
+ import { C as runtimeReportSpecDigest, S as parseRuntimeReportSpec, _ as RUNTIME_REPORT_HAVING_OPS, a as runRuntimeReportInMemory, b as RUNTIME_REPORT_SPEC_VERSION, c as coerceRuntimeReportValue, d as planRuntimeReportColumns, f as resolveRuntimeReportFieldPolicy, g as RUNTIME_REPORT_FILTER_OPS, h as RUNTIME_REPORT_CHART_TYPES, i as describeRuntimeReportMemorySource, l as humanizeRuntimeReportName, m as RUNTIME_REPORT_BUCKETS, n as resolveRuntimeReportTimeRange, o as describeRuntimeReportSourceFields, p as unknownRuntimeReportField, r as runtimeReportBucketForSpan, s as assertRuntimeReportFilterOperator, t as parseRuntimeReportTime, u as normalizeRuntimeReportCell, v as RUNTIME_REPORT_LIMITS, w as serializeRuntimeReportSpec, x as RuntimeReportError, y as RUNTIME_REPORT_MEASURE_FNS } from "./chunks/runtime-time-D7JZ1e4J.js";
8
+ import { DataQueryValidationError, ObjectRegistry, SmrtCollection, SmrtObject, SmrtRecipe, createDataQueryFingerprint, field, normalizeDataQueryRequest, normalizeDataQueryResult, runOnce, smrt } from "@happyvertical/smrt-core";
8
9
  import { toSnakeCase } from "@happyvertical/smrt-core/utils";
9
10
  import { TenantScoped, getCurrentTenant, getTenantId, isTenancyEnabled, isTenantScopedClass, tenantId, withSystemContext, withTenant } from "@happyvertical/smrt-tenancy";
10
11
  import { tableExists, validateColumnName } from "@happyvertical/sql";
@@ -980,399 +981,16 @@ function findMaterializedReportHost(hosts, id) {
980
981
  return hosts.find((host) => host.id === id);
981
982
  }
982
983
  //#endregion
983
- //#region src/runtime-spec.ts
984
- var RUNTIME_REPORT_SPEC_VERSION = 1;
985
- var RUNTIME_REPORT_LIMITS = Object.freeze({
986
- maxTitleLength: 120,
987
- maxDescriptionLength: 500,
988
- maxDimensions: 4,
989
- maxMeasures: 6,
990
- maxFilters: 12,
991
- maxHaving: 4,
992
- maxSort: 3,
993
- maxInValues: 100,
994
- maxStringValueLength: 256,
995
- defaultRowLimit: 100,
996
- maxRowLimit: 1e3,
997
- maxAliasLength: 40,
998
- maxFieldNameLength: 64,
999
- maxSourceIdLength: 120
1000
- });
1001
- var RUNTIME_REPORT_BUCKETS = [
1002
- "minute",
1003
- "hour",
1004
- "day",
1005
- "week",
1006
- "month",
1007
- "quarter",
1008
- "year"
1009
- ];
1010
- var RUNTIME_REPORT_MEASURE_FNS = [
1011
- "count",
1012
- "countDistinct",
1013
- "sum",
1014
- "avg",
1015
- "min",
1016
- "max"
1017
- ];
1018
- var RUNTIME_REPORT_FILTER_OPS = [
1019
- "eq",
1020
- "ne",
1021
- "gt",
1022
- "gte",
1023
- "lt",
1024
- "lte",
1025
- "in",
1026
- "notIn",
1027
- "contains",
1028
- "isNull",
1029
- "isNotNull"
1030
- ];
1031
- var RUNTIME_REPORT_HAVING_OPS = [
1032
- "eq",
1033
- "ne",
1034
- "gt",
1035
- "gte",
1036
- "lt",
1037
- "lte"
1038
- ];
1039
- var RUNTIME_REPORT_CHART_TYPES = [
1040
- "table",
1041
- "bar",
1042
- "line",
1043
- "pie",
1044
- "stat"
1045
- ];
1046
- var RuntimeReportError = class extends SmrtError {
1047
- code;
1048
- path;
1049
- /**
1050
- * HTTP-style status so tool loops classify it: 4xx input problems are
1051
- * repairable by the caller, `tenant_required` and `confirmation_required`
1052
- * are denials, results that cannot be represented are server errors.
1053
- */
1054
- status;
1055
- constructor(code, path, message) {
1056
- super(path ? `${path}: ${message}` : message, code, "validation");
1057
- this.name = "RuntimeReportError";
1058
- this.code = code;
1059
- this.path = path;
1060
- this.status = code === "tenant_required" || code === "confirmation_required" ? 403 : code === "invalid_result" ? 500 : 422;
1061
- }
1062
- };
1063
- function fail$1(path, message) {
1064
- throw new RuntimeReportError("invalid_spec", path, message);
1065
- }
1066
- var RESERVED_ALIASES = /* @__PURE__ */ new Set([
1067
- "all",
1068
- "and",
1069
- "as",
1070
- "asc",
1071
- "between",
1072
- "by",
1073
- "case",
1074
- "cast",
1075
- "check",
1076
- "column",
1077
- "constraint",
1078
- "create",
1079
- "cross",
1080
- "default",
1081
- "delete",
1082
- "desc",
1083
- "distinct",
1084
- "drop",
1085
- "else",
1086
- "end",
1087
- "except",
1088
- "exists",
1089
- "false",
1090
- "for",
1091
- "foreign",
1092
- "from",
1093
- "full",
1094
- "group",
1095
- "having",
1096
- "in",
1097
- "inner",
1098
- "insert",
1099
- "intersect",
1100
- "into",
1101
- "is",
1102
- "join",
1103
- "left",
1104
- "like",
1105
- "limit",
1106
- "natural",
1107
- "not",
1108
- "null",
1109
- "offset",
1110
- "on",
1111
- "or",
1112
- "order",
1113
- "outer",
1114
- "primary",
1115
- "references",
1116
- "right",
1117
- "select",
1118
- "set",
1119
- "table",
1120
- "then",
1121
- "to",
1122
- "true",
1123
- "union",
1124
- "unique",
1125
- "update",
1126
- "user",
1127
- "using",
1128
- "values",
1129
- "when",
1130
- "where",
1131
- "with"
1132
- ]);
1133
- var ALIAS_PATTERN = /^[a-z][a-z0-9_]*$/;
1134
- var FIELD_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;
1135
- var SOURCE_PATTERN = /^[A-Za-z@][A-Za-z0-9_@./:-]*$/;
1136
- function isRecord(value) {
1137
- return typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
1138
- }
1139
- function strictObject(value, path, allowed) {
1140
- if (!isRecord(value)) fail$1(path, "must be an object");
1141
- for (const key of Object.keys(value)) if (!allowed.includes(key)) fail$1(`${path}.${key}`, "is not a known key");
1142
- return value;
1143
- }
1144
- function strictArray(value, path, max, min = 0) {
1145
- if (value === void 0) return [];
1146
- if (!Array.isArray(value)) fail$1(path, "must be an array");
1147
- if (value.length > max) fail$1(path, `must have at most ${max} entries`);
1148
- if (value.length < min) fail$1(path, `must have at least ${min} entries`);
1149
- return value;
1150
- }
1151
- function text(value, path, max, options = {}) {
1152
- if (typeof value !== "string") {
1153
- if (options.optional && value === void 0) return "";
1154
- fail$1(path, "must be a string");
1155
- }
1156
- const trimmed = value.trim();
1157
- if (trimmed.length === 0 && !options.optional) fail$1(path, "must not be empty");
1158
- if (trimmed.length > max) fail$1(path, `must be at most ${max} characters`);
1159
- if (/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(trimmed)) fail$1(path, "must not contain control characters");
1160
- return trimmed;
1161
- }
1162
- function oneOf(value, path, allowed) {
1163
- if (typeof value !== "string" || !allowed.includes(value)) fail$1(path, `must be one of: ${allowed.join(", ")}`);
1164
- return value;
1165
- }
1166
- function fieldName(value, path) {
1167
- if (typeof value !== "string") fail$1(path, "must be a string");
1168
- if (value.length === 0 || value.length > RUNTIME_REPORT_LIMITS.maxFieldNameLength || !FIELD_PATTERN.test(value)) fail$1(path, "must be a plain field name (letters, digits, underscore)");
1169
- return value;
1170
- }
1171
- function alias(value, path) {
1172
- if (typeof value !== "string") fail$1(path, "must be a string");
1173
- if (value.length === 0 || value.length > RUNTIME_REPORT_LIMITS.maxAliasLength || !ALIAS_PATTERN.test(value)) fail$1(path, `must be a lowercase identifier of at most ${RUNTIME_REPORT_LIMITS.maxAliasLength} characters (a-z, 0-9, underscore)`);
1174
- if (RESERVED_ALIASES.has(value)) fail$1(path, `'${value}' is a reserved word`);
1175
- return value;
1176
- }
1177
- function scalar(value, path) {
1178
- if (typeof value === "string") {
1179
- if (value.length > RUNTIME_REPORT_LIMITS.maxStringValueLength) fail$1(path, `must be at most ${RUNTIME_REPORT_LIMITS.maxStringValueLength} characters`);
1180
- if (value.includes("\0")) fail$1(path, "must not contain NUL");
1181
- return value;
1182
- }
1183
- if (typeof value === "number") {
1184
- if (!Number.isFinite(value)) fail$1(path, "must be a finite number");
1185
- return value;
1186
- }
1187
- if (typeof value === "boolean") return value;
1188
- return fail$1(path, "must be a string, number or boolean");
1189
- }
1190
- function snake(name) {
1191
- return name.replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toLowerCase();
1192
- }
1193
- function parseDimension(value, path) {
1194
- const raw = strictObject(value, path, [
1195
- "field",
1196
- "as",
1197
- "bucket"
1198
- ]);
1199
- const field = fieldName(raw.field, `${path}.field`);
1200
- const bucket = raw.bucket === void 0 ? void 0 : oneOf(raw.bucket, `${path}.bucket`, RUNTIME_REPORT_BUCKETS);
1201
- return {
1202
- field,
1203
- as: raw.as === void 0 ? alias(bucket ? `${snake(field)}_${bucket}` : snake(field), `${path}.as`) : alias(raw.as, `${path}.as`),
1204
- ...bucket ? { bucket } : {}
1205
- };
1206
- }
1207
- function parseMeasure(value, path) {
1208
- const raw = strictObject(value, path, [
1209
- "fn",
1210
- "field",
1211
- "as"
1212
- ]);
1213
- const fn = oneOf(raw.fn, `${path}.fn`, RUNTIME_REPORT_MEASURE_FNS);
1214
- const field = raw.field === void 0 ? void 0 : fieldName(raw.field, `${path}.field`);
1215
- if (!field && fn !== "count") fail$1(`${path}.field`, `is required for ${fn}`);
1216
- const as = raw.as === void 0 ? alias(field ? `${snake(fn)}_${snake(field)}` : "row_count", `${path}.as`) : alias(raw.as, `${path}.as`);
1217
- return {
1218
- fn,
1219
- ...field ? { field } : {},
1220
- as
1221
- };
1222
- }
1223
- function parseFilter(value, path) {
1224
- const raw = strictObject(value, path, [
1225
- "field",
1226
- "op",
1227
- "value"
1228
- ]);
1229
- const field = fieldName(raw.field, `${path}.field`);
1230
- const op = oneOf(raw.op, `${path}.op`, RUNTIME_REPORT_FILTER_OPS);
1231
- if (op === "isNull" || op === "isNotNull") {
1232
- if (raw.value !== void 0) fail$1(`${path}.value`, `must be omitted for ${op}`);
1233
- return {
1234
- field,
1235
- op
1236
- };
1237
- }
1238
- if (raw.value === void 0) fail$1(`${path}.value`, `is required for ${op}`);
1239
- if (op === "in" || op === "notIn") return {
1240
- field,
1241
- op,
1242
- value: strictArray(raw.value, `${path}.value`, RUNTIME_REPORT_LIMITS.maxInValues, 1).map((entry, index) => scalar(entry, `${path}.value[${index}]`))
1243
- };
1244
- if (Array.isArray(raw.value)) fail$1(`${path}.value`, `must be a scalar for ${op}`);
1245
- const parsed = scalar(raw.value, `${path}.value`);
1246
- if (op === "contains" && typeof parsed !== "string") fail$1(`${path}.value`, "must be a string for contains");
1247
- return {
1248
- field,
1249
- op,
1250
- value: parsed
1251
- };
1252
- }
1253
- function parseHaving(value, path) {
1254
- const raw = strictObject(value, path, [
1255
- "measure",
1256
- "op",
1257
- "value"
1258
- ]);
1259
- const measure = alias(raw.measure, `${path}.measure`);
1260
- const op = oneOf(raw.op, `${path}.op`, RUNTIME_REPORT_HAVING_OPS);
1261
- if (typeof raw.value !== "number" || !Number.isFinite(raw.value)) fail$1(`${path}.value`, "must be a finite number");
1262
- return {
1263
- measure,
1264
- op,
1265
- value: raw.value
1266
- };
1267
- }
1268
- function parseSort(value, path) {
1269
- const raw = strictObject(value, path, ["by", "direction"]);
1270
- return {
1271
- by: alias(raw.by, `${path}.by`),
1272
- direction: raw.direction === void 0 ? "asc" : oneOf(raw.direction, `${path}.direction`, ["asc", "desc"])
1273
- };
1274
- }
1275
- function parseRuntimeReportSpec(input) {
1276
- let candidate = input;
1277
- if (typeof input === "string") {
1278
- if (input.length > 64 * 1024) fail$1("", "spec JSON is too large");
1279
- try {
1280
- candidate = JSON.parse(input);
1281
- } catch {
1282
- fail$1("", "spec is not valid JSON");
1283
- }
1284
- }
1285
- const raw = strictObject(candidate, "spec", [
1286
- "version",
1287
- "title",
1288
- "description",
1289
- "source",
1290
- "dimensions",
1291
- "measures",
1292
- "filters",
1293
- "having",
1294
- "sort",
1295
- "limit",
1296
- "chart"
1297
- ]);
1298
- if (raw.version !== void 0 && raw.version !== 1) fail$1("spec.version", `unsupported version (expected 1)`);
1299
- const title = text(raw.title, "spec.title", RUNTIME_REPORT_LIMITS.maxTitleLength);
1300
- const description = text(raw.description, "spec.description", RUNTIME_REPORT_LIMITS.maxDescriptionLength, { optional: true });
1301
- if (typeof raw.source !== "string" || raw.source.length === 0 || raw.source.length > RUNTIME_REPORT_LIMITS.maxSourceIdLength || !SOURCE_PATTERN.test(raw.source)) fail$1("spec.source", "must be a source id");
1302
- const dimensions = strictArray(raw.dimensions, "spec.dimensions", RUNTIME_REPORT_LIMITS.maxDimensions).map((entry, index) => parseDimension(entry, `spec.dimensions[${index}]`));
1303
- const measures = strictArray(raw.measures, "spec.measures", RUNTIME_REPORT_LIMITS.maxMeasures, 1).map((entry, index) => parseMeasure(entry, `spec.measures[${index}]`));
1304
- const filters = strictArray(raw.filters, "spec.filters", RUNTIME_REPORT_LIMITS.maxFilters).map((entry, index) => parseFilter(entry, `spec.filters[${index}]`));
1305
- const having = strictArray(raw.having, "spec.having", RUNTIME_REPORT_LIMITS.maxHaving).map((entry, index) => parseHaving(entry, `spec.having[${index}]`));
1306
- const sort = strictArray(raw.sort, "spec.sort", RUNTIME_REPORT_LIMITS.maxSort).map((entry, index) => parseSort(entry, `spec.sort[${index}]`));
1307
- const dimensionAliases = /* @__PURE__ */ new Set();
1308
- const measureAliases = /* @__PURE__ */ new Set();
1309
- const seen = /* @__PURE__ */ new Set();
1310
- for (const [index, dimension] of dimensions.entries()) {
1311
- if (seen.has(dimension.as)) fail$1(`spec.dimensions[${index}].as`, `duplicate alias '${dimension.as}'`);
1312
- seen.add(dimension.as);
1313
- dimensionAliases.add(dimension.as);
1314
- }
1315
- for (const [index, measure] of measures.entries()) {
1316
- if (seen.has(measure.as)) fail$1(`spec.measures[${index}].as`, `duplicate alias '${measure.as}'`);
1317
- seen.add(measure.as);
1318
- measureAliases.add(measure.as);
1319
- }
1320
- for (const [index, entry] of having.entries()) if (!measureAliases.has(entry.measure)) fail$1(`spec.having[${index}].measure`, "must be a declared measure alias");
1321
- for (const [index, entry] of sort.entries()) if (!seen.has(entry.by)) fail$1(`spec.sort[${index}].by`, "must be a declared dimension or measure alias");
1322
- let limit = RUNTIME_REPORT_LIMITS.defaultRowLimit;
1323
- if (raw.limit !== void 0) {
1324
- if (typeof raw.limit !== "number" || !Number.isSafeInteger(raw.limit) || raw.limit < 1 || raw.limit > RUNTIME_REPORT_LIMITS.maxRowLimit) fail$1("spec.limit", `must be an integer between 1 and ${RUNTIME_REPORT_LIMITS.maxRowLimit}`);
1325
- limit = raw.limit;
1326
- }
1327
- let chart = { type: "table" };
1328
- if (raw.chart !== void 0) {
1329
- const rawChart = strictObject(raw.chart, "spec.chart", [
1330
- "type",
1331
- "x",
1332
- "y"
1333
- ]);
1334
- const type = oneOf(rawChart.type, "spec.chart.type", RUNTIME_REPORT_CHART_TYPES);
1335
- const x = rawChart.x === void 0 ? void 0 : alias(rawChart.x, "spec.chart.x");
1336
- if (x !== void 0 && !dimensionAliases.has(x)) fail$1("spec.chart.x", "must be a declared dimension alias");
1337
- const y = strictArray(rawChart.y, "spec.chart.y", RUNTIME_REPORT_LIMITS.maxMeasures).map((entry, index) => alias(entry, `spec.chart.y[${index}]`));
1338
- for (const [index, name] of y.entries()) if (!measureAliases.has(name)) fail$1(`spec.chart.y[${index}]`, "must be a declared measure alias");
1339
- chart = {
1340
- type,
1341
- ...x ? { x } : {},
1342
- ...y.length > 0 ? { y } : {}
1343
- };
1344
- }
1345
- return {
1346
- version: 1,
1347
- title,
1348
- ...description ? { description } : {},
1349
- source: raw.source,
1350
- dimensions,
1351
- measures,
1352
- filters,
1353
- having,
1354
- sort,
1355
- limit,
1356
- chart
1357
- };
1358
- }
1359
- function canonicalize(value) {
1360
- if (Array.isArray(value)) return value.map(canonicalize);
1361
- if (isRecord(value)) return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalize(value[key])]));
1362
- return value;
1363
- }
984
+ //#region src/runtime-spec-hash.ts
1364
985
  function runtimeReportSpecHash(spec) {
1365
- return createHash("sha256").update(JSON.stringify(canonicalize(spec))).digest("hex");
1366
- }
1367
- function serializeRuntimeReportSpec(spec) {
1368
- return JSON.stringify(canonicalize(spec));
986
+ return createHash("sha256").update(serializeRuntimeReportSpec(spec)).digest("hex");
1369
987
  }
1370
988
  //#endregion
1371
989
  //#region src/runtime-compiler.ts
1372
990
  var ALIAS_PREFIX = "r_";
1373
- var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1374
- function humanize(name) {
1375
- return name.replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/^./, (value) => value.toUpperCase());
991
+ function enumOf(field) {
992
+ const strings = [field.enum, field._meta?.enum].find(Array.isArray)?.filter((v) => typeof v === "string");
993
+ return strings?.length ? strings : void 0;
1376
994
  }
1377
995
  function columnTypeFor(type) {
1378
996
  switch (type) {
@@ -1392,7 +1010,12 @@ function explicitColumn(field) {
1392
1010
  return typeof metaColumn === "string" ? metaColumn : void 0;
1393
1011
  }
1394
1012
  function strictFormat(field) {
1395
- return [field.format, field._meta?.format].find((value) => typeof value === "string" && value.length > 0);
1013
+ const ui = field._meta?.ui;
1014
+ return [
1015
+ field.format,
1016
+ field._meta?.format,
1017
+ ui?.format
1018
+ ].find((value) => typeof value === "string" && value.length > 0);
1396
1019
  }
1397
1020
  var BASE_FIELDS = {
1398
1021
  id: "id",
@@ -1435,14 +1058,7 @@ async function loadSourceFields(qualifiedName, tenantField, permissions, source)
1435
1058
  }
1436
1059
  const knownNames = /* @__PURE__ */ new Set();
1437
1060
  for (const [column, entries] of columns) for (const entry of entries) knownNames.add(baseNames.get(column) ?? entry.name);
1438
- const policy = (key) => {
1439
- const value = source[key];
1440
- if (value === void 0) return void 0;
1441
- if (!Array.isArray(value) || Array.from(value).some((name) => typeof name !== "string" || !knownNames.has(name))) throw new RuntimeReportError("unsupported_source", `source.${key}`, `${key} must contain canonical field names declared on the source`);
1442
- return new Set(value);
1443
- };
1444
- const included = policy("fields");
1445
- const excluded = policy("excludeFields");
1061
+ const { included, excluded } = resolveRuntimeReportFieldPolicy(source, knownNames);
1446
1062
  const fields = /* @__PURE__ */ new Map();
1447
1063
  for (const [column, entries] of columns) {
1448
1064
  if (column === tenantField || column === "tenant_id" || entries.some(({ name, field }) => isExcluded(name, field, tenantField, permissions) || excluded?.has(baseNames.get(column) ?? name))) continue;
@@ -1452,128 +1068,32 @@ async function loadSourceFields(qualifiedName, tenantField, permissions, source)
1452
1068
  const type = columnTypeFor(field.type);
1453
1069
  if (!type) continue;
1454
1070
  const format = strictFormat(field);
1071
+ const values = type === "string" ? enumOf(field) : void 0;
1455
1072
  fields.set(name, {
1456
1073
  name,
1457
1074
  column,
1458
1075
  type,
1459
1076
  ...format ? { format } : {},
1460
- label: typeof field.description === "string" && field.description.length > 0 ? field.description : humanize(name)
1077
+ ...values ? { enum: values } : {},
1078
+ ...type === "datetime" && format === "date" ? { businessDate: true } : {},
1079
+ label: typeof field.description === "string" && field.description.length > 0 ? field.description : humanizeRuntimeReportName(name)
1461
1080
  });
1462
1081
  }
1463
1082
  }
1464
1083
  return fields;
1465
1084
  }
1466
- function unknownField(path, name) {
1467
- throw new RuntimeReportError("unknown_field", path, `'${name}' is not a field available on this source`);
1468
- }
1469
- function badOperation(path, message) {
1470
- throw new RuntimeReportError("invalid_operation", path, message);
1471
- }
1472
- function badValue(path, message) {
1473
- throw new RuntimeReportError("invalid_value", path, message);
1474
- }
1475
- var OPERATORS_BY_TYPE = {
1476
- string: [
1477
- "eq",
1478
- "ne",
1479
- "gt",
1480
- "gte",
1481
- "lt",
1482
- "lte",
1483
- "in",
1484
- "notIn",
1485
- "contains",
1486
- "isNull",
1487
- "isNotNull"
1488
- ],
1489
- integer: [
1490
- "eq",
1491
- "ne",
1492
- "gt",
1493
- "gte",
1494
- "lt",
1495
- "lte",
1496
- "in",
1497
- "notIn",
1498
- "isNull",
1499
- "isNotNull"
1500
- ],
1501
- decimal: [
1502
- "eq",
1503
- "ne",
1504
- "gt",
1505
- "gte",
1506
- "lt",
1507
- "lte",
1508
- "in",
1509
- "notIn",
1510
- "isNull",
1511
- "isNotNull"
1512
- ],
1513
- datetime: [
1514
- "eq",
1515
- "ne",
1516
- "gt",
1517
- "gte",
1518
- "lt",
1519
- "lte",
1520
- "in",
1521
- "notIn",
1522
- "isNull",
1523
- "isNotNull"
1524
- ],
1525
- boolean: [
1526
- "eq",
1527
- "ne",
1528
- "isNull",
1529
- "isNotNull"
1530
- ],
1531
- id: [
1532
- "eq",
1533
- "ne",
1534
- "in",
1535
- "notIn",
1536
- "isNull",
1537
- "isNotNull"
1538
- ]
1539
- };
1540
- function coerceValue(type, value, path) {
1541
- switch (type) {
1542
- case "string":
1543
- if (typeof value !== "string") badValue(path, "must be a string");
1544
- return value;
1545
- case "integer":
1546
- if (typeof value !== "number" || !Number.isSafeInteger(value)) badValue(path, "must be a safe integer");
1547
- return value;
1548
- case "decimal":
1549
- if (typeof value !== "number" || !Number.isFinite(value)) badValue(path, "must be a finite number");
1550
- return value;
1551
- case "boolean":
1552
- if (typeof value !== "boolean") badValue(path, "must be true or false");
1553
- return value;
1554
- case "datetime": {
1555
- if (typeof value !== "string") badValue(path, "must be an ISO-8601 string");
1556
- const parsed = new Date(value);
1557
- if (Number.isNaN(parsed.getTime()) || !/^\d{4}-\d{2}-\d{2}/.test(value)) badValue(path, "must be an ISO-8601 date or timestamp");
1558
- return parsed;
1559
- }
1560
- case "id":
1561
- if (typeof value !== "string" || !UUID_PATTERN.test(value)) badValue(path, "must be a UUID");
1562
- return value.toLowerCase();
1563
- }
1564
- }
1565
1085
  function filterCondition(filter, field, path) {
1566
- if (!OPERATORS_BY_TYPE[field.type].includes(filter.op)) badOperation(`${path}.op`, `'${filter.op}' is not valid for ${field.type} fields`);
1086
+ assertRuntimeReportFilterOperator(filter, field.type, path);
1567
1087
  const column = field.column;
1568
1088
  switch (filter.op) {
1569
1089
  case "isNull": return { [column]: null };
1570
1090
  case "isNotNull": return { [`${column} !=`]: null };
1571
1091
  case "in":
1572
1092
  case "notIn": {
1573
- const values = filter.value.map((entry, index) => coerceValue(field.type, entry, `${path}.value[${index}]`));
1093
+ const values = filter.value.map((entry, index) => coerceRuntimeReportValue(field.type, entry, `${path}.value[${index}]`));
1574
1094
  return { [`${column} ${filter.op === "in" ? "in" : "not in"}`]: values };
1575
1095
  }
1576
- case "contains": return { [`${column} contains`]: coerceValue(field.type, filter.value, `${path}.value`) };
1096
+ case "contains": return { [`${column} contains`]: coerceRuntimeReportValue(field.type, filter.value, `${path}.value`) };
1577
1097
  default: return { [`${column}${{
1578
1098
  eq: "",
1579
1099
  ne: " !=",
@@ -1581,14 +1101,9 @@ function filterCondition(filter, field, path) {
1581
1101
  gte: " >=",
1582
1102
  lt: " <",
1583
1103
  lte: " <="
1584
- }[filter.op]}`]: coerceValue(field.type, filter.value, `${path}.value`) };
1104
+ }[filter.op]}`]: coerceRuntimeReportValue(field.type, filter.value, `${path}.value`) };
1585
1105
  }
1586
1106
  }
1587
- function measureColumnType(fn, field) {
1588
- if (fn === "count" || fn === "countDistinct") return "integer";
1589
- if (fn === "avg") return "decimal";
1590
- return field?.type ?? "decimal";
1591
- }
1592
1107
  function resolveSource(context, id) {
1593
1108
  const source = context.sources.find((candidate) => candidate.id === id);
1594
1109
  if (!source) throw new RuntimeReportError("unknown_source", "spec.source", `'${id}' is not an available report source`);
@@ -1618,49 +1133,23 @@ async function describeRuntimeReportSource(context, sourceId) {
1618
1133
  const prepared = await prepareSource(context, sourceId);
1619
1134
  return {
1620
1135
  id: prepared.source.id,
1621
- label: prepared.source.label ?? humanize(prepared.registeredName),
1136
+ label: prepared.source.label ?? humanizeRuntimeReportName(prepared.registeredName),
1622
1137
  ...prepared.source.description ? { description: prepared.source.description } : {},
1623
- fields: [...prepared.fields.values()].map((field) => ({
1624
- name: field.name,
1625
- label: field.label,
1626
- type: field.type,
1627
- ...field.format ? { format: field.format } : {},
1628
- filterOperators: OPERATORS_BY_TYPE[field.type],
1629
- aggregates: aggregatesFor(field.type)
1630
- }))
1138
+ ...describeRuntimeReportSourceFields(prepared.fields, prepared.source)
1631
1139
  };
1632
1140
  }
1633
- function aggregatesFor(type) {
1634
- if (type === "integer" || type === "decimal") return [
1635
- "count",
1636
- "countDistinct",
1637
- "sum",
1638
- "avg",
1639
- "min",
1640
- "max"
1641
- ];
1642
- if (type === "datetime") return [
1643
- "count",
1644
- "countDistinct",
1645
- "min",
1646
- "max"
1647
- ];
1648
- return ["count", "countDistinct"];
1649
- }
1650
1141
  async function compileRuntimeReportSpec(spec, context) {
1651
1142
  const { source, registeredName, qualifiedName, table, tenantCol, fields } = await prepareSource(context, spec.source);
1652
1143
  const lookup = (name, path) => {
1653
1144
  const field = fields.get(name);
1654
- if (!field) unknownField(path, name);
1145
+ if (!field) unknownRuntimeReportField(path, name);
1655
1146
  return field;
1656
1147
  };
1657
- const columns = [];
1148
+ const columns = planRuntimeReportColumns(spec, fields);
1658
1149
  const select = [];
1659
1150
  const groupBy = [];
1660
- for (const [index, dimension] of spec.dimensions.entries()) {
1661
- const path = `spec.dimensions[${index}]`;
1662
- const field = lookup(dimension.field, `${path}.field`);
1663
- if (dimension.bucket && field.type !== "datetime") badOperation(`${path}.bucket`, "buckets are only valid on datetime fields");
1151
+ for (const dimension of spec.dimensions) {
1152
+ const field = fields.get(dimension.field);
1664
1153
  const sqlAlias = `${ALIAS_PREFIX}${dimension.as}`;
1665
1154
  if (dimension.bucket) select.push({
1666
1155
  bucket: dimension.bucket,
@@ -1672,38 +1161,15 @@ async function compileRuntimeReportSpec(spec, context) {
1672
1161
  as: sqlAlias
1673
1162
  });
1674
1163
  groupBy.push(sqlAlias);
1675
- columns.push({
1676
- id: dimension.as,
1677
- label: humanize(dimension.as),
1678
- role: "dimension",
1679
- type: dimension.bucket ? "datetime" : field.type,
1680
- field: field.name,
1681
- ...dimension.bucket ? { bucket: dimension.bucket } : {},
1682
- ...field.format && !dimension.bucket ? { format: field.format } : {}
1683
- });
1684
1164
  }
1685
- for (const [index, measure] of spec.measures.entries()) {
1686
- const path = `spec.measures[${index}]`;
1687
- const field = measure.field ? lookup(measure.field, `${path}.field`) : void 0;
1688
- if ((measure.fn === "sum" || measure.fn === "avg") && field?.type !== "integer" && field?.type !== "decimal") badOperation(`${path}.fn`, `${measure.fn} requires a numeric field`);
1689
- if ((measure.fn === "min" || measure.fn === "max") && field?.type !== "integer" && field?.type !== "decimal" && field?.type !== "datetime") badOperation(`${path}.fn`, `${measure.fn} requires a numeric or datetime field`);
1690
- const sqlAlias = `${ALIAS_PREFIX}${measure.as}`;
1165
+ for (const measure of spec.measures) {
1166
+ const field = measure.field ? fields.get(measure.field) : void 0;
1691
1167
  select.push({
1692
1168
  fn: measure.fn === "countDistinct" ? "count" : measure.fn,
1693
1169
  ...field ? { column: field.column } : {},
1694
- as: sqlAlias,
1170
+ as: `${ALIAS_PREFIX}${measure.as}`,
1695
1171
  ...measure.fn === "countDistinct" ? { distinct: true } : {}
1696
1172
  });
1697
- const keepsFormat = field?.format && (measure.fn === "sum" || measure.fn === "min" || measure.fn === "max" || measure.fn === "avg");
1698
- columns.push({
1699
- id: measure.as,
1700
- label: humanize(measure.as),
1701
- role: "measure",
1702
- type: measureColumnType(measure.fn, field),
1703
- ...field ? { field: field.name } : {},
1704
- fn: measure.fn,
1705
- ...keepsFormat ? { format: field?.format } : {}
1706
- });
1707
1173
  }
1708
1174
  const conditions = [];
1709
1175
  const tenantId = context.tenantId !== void 0 ? context.tenantId : getTenantId() ?? null;
@@ -1750,7 +1216,7 @@ async function compileRuntimeReportSpec(spec, context) {
1750
1216
  source: {
1751
1217
  id: source.id,
1752
1218
  className: qualifiedName,
1753
- label: source.label ?? humanize(registeredName),
1219
+ label: source.label ?? humanizeRuntimeReportName(registeredName),
1754
1220
  table
1755
1221
  },
1756
1222
  columns,
@@ -1767,27 +1233,6 @@ function adaptWhere(where, adapterType) {
1767
1233
  const adapt = (value) => adapterType === "sqlite" && typeof value === "boolean" ? value ? 1 : 0 : value;
1768
1234
  return where.map((group) => group.map((condition) => Object.fromEntries(Object.entries(condition).map(([key, value]) => [key, Array.isArray(value) ? value.map(adapt) : adapt(value)]))));
1769
1235
  }
1770
- function normalizeCell(column, value) {
1771
- if (value === null || value === void 0) return null;
1772
- if (value instanceof Date) return value.toISOString();
1773
- if (typeof value === "bigint") {
1774
- const asNumber = Number(value);
1775
- if (!Number.isSafeInteger(asNumber)) throw new RuntimeReportError("invalid_result", column.id, "integer result exceeds the JavaScript safe range");
1776
- return asNumber;
1777
- }
1778
- if (column.type === "boolean") {
1779
- if (value === 1 || value === "1" || value === "true" || value === true) return true;
1780
- if (value === 0 || value === "0" || value === "false" || value === false) return false;
1781
- }
1782
- if (column.type === "integer" || column.type === "decimal") {
1783
- const numeric = typeof value === "number" ? value : Number(value);
1784
- if (!Number.isFinite(numeric)) throw new RuntimeReportError("invalid_result", column.id, "numeric result is not finite");
1785
- if (column.type === "integer" && !Number.isSafeInteger(numeric)) throw new RuntimeReportError("invalid_result", column.id, "integer result exceeds the JavaScript safe range");
1786
- return numeric;
1787
- }
1788
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
1789
- return String(value);
1790
- }
1791
1236
  async function runRuntimeReport(compiled, options) {
1792
1237
  const adapterType = adapterTypeFromDb(options.db, options.adapterType);
1793
1238
  const aggregate = buildAggregate({
@@ -1799,7 +1244,7 @@ async function runRuntimeReport(compiled, options) {
1799
1244
  const truncated = rawRows.length > limit;
1800
1245
  const rows = rawRows.slice(0, limit).map((row) => {
1801
1246
  const out = {};
1802
- for (const column of compiled.columns) out[column.id] = normalizeCell(column, row[`${ALIAS_PREFIX}${column.id}`]);
1247
+ for (const column of compiled.columns) out[column.id] = normalizeRuntimeReportCell(column, row[`${ALIAS_PREFIX}${column.id}`]);
1803
1248
  return out;
1804
1249
  });
1805
1250
  return {
@@ -2304,11 +1749,33 @@ var RuntimeReportsRecipe = class extends SmrtRecipe {
2304
1749
  static help = "./runtime.recipe.md";
2305
1750
  static models = [RuntimeReport];
2306
1751
  static nav = [{
2307
- label: "Shared reports",
1752
+ label: "Saved reports",
2308
1753
  model: RuntimeReport,
2309
- icon: "fileText"
1754
+ icon: "fileText",
1755
+ noun: "report",
1756
+ description: "Reports your team approved, kept up to date."
2310
1757
  }];
2311
1758
  static surfaces = [
1759
+ {
1760
+ kind: "widget",
1761
+ type: "saved-reports",
1762
+ export: "@happyvertical/smrt-reports/svelte#SavedReportsWidget",
1763
+ label: "Saved reports",
1764
+ description: "Your team’s saved reports with their latest figures.",
1765
+ icon: "fileText",
1766
+ options: [{
1767
+ key: "limit",
1768
+ type: "integer",
1769
+ label: "Reports shown",
1770
+ min: 1,
1771
+ max: 24,
1772
+ default: 6
1773
+ }],
1774
+ sectionDefault: true,
1775
+ defaultSpan: 4,
1776
+ minSpan: 2,
1777
+ maxSpan: 4
1778
+ },
2312
1779
  {
2313
1780
  kind: "route",
2314
1781
  path: "/reports",
@@ -2956,6 +2423,6 @@ function validateReportExportArtifact(descriptor, artifact, now = /* @__PURE__ *
2956
2423
  };
2957
2424
  }
2958
2425
  //#endregion
2959
- export { MaterializedReportsRecipe, REPORT_LOCKS_TABLE, REPORT_REFRESH_TASKS_TABLE, REPORT_RUNS_TABLE, REPORT_RUNTIME_TABLES, REPORT_SCHEDULER_TABLES, REPORT_SCHEDULES_TABLE, REPORT_WATERMARKS_TABLE, RUNTIME_REPORT_BUCKETS, RUNTIME_REPORT_CHART_TYPES, RUNTIME_REPORT_FILTER_OPS, RUNTIME_REPORT_HAVING_OPS, RUNTIME_REPORT_LIMITS, RUNTIME_REPORT_MEASURE_FNS, RUNTIME_REPORT_SPEC_VERSION, ReportScheduleRunner, ReportSurfaceValidationError, RuntimeReport, RuntimeReportCollection, RuntimeReportError, RuntimeReportHttpError, RuntimeReportsRecipe, SmrtPrincipalReportRefreshTask, SmrtReport, SmrtReportCollection, SmrtReportLock, SmrtReportLockCollection, SmrtReportRefreshTask, SmrtReportRun, SmrtReportRunCollection, SmrtReportSchedule, SmrtReportScheduleCollection, SmrtReportWatermark, SmrtReportWatermarkCollection, aggregate, applyReportExport, applyReportRefresh, archiveRuntimeReport, archiveRuntimeReportForPrincipal, assertMaterializedReportRead, assertReportTablesReady, avg, bucketExpr, buildAggregate, buildReportAdapterDescriptor, buildReportDefinition, buildReportDrilldownQuery, compileReportDefinition, compileReportSpec, compileRuntimeReportSpec, count, createReportExportPageRequest, createReportExportRequest, createReportExportSnapshot, createRuntimeReportConfirmHandler, day, describeRuntimeReportSource, enqueueReportRefresh, ensureReportRefreshSchedules, findMaterializedReportHost, getReportGroupingColumns, getReportLifecycle, getRuntimeReport, getRuntimeReportOptions, getRuntimeReportServerOptions, groupBy, hour, listRuntimeReports, loadRuntimeReportChart, max, migrateReportSavedView, min, minute, month, normalizeReportSavedView, parseRuntimeReportSpec, previewReportExport, previewReportRefresh, quarter, queryReportMaterializedRows, refreshReport, registerReportRefreshExecutionAuthorityHost, registerReportRefreshInterceptor, registerReportRefreshJobIntegritySigner, report, reportDefinitionFingerprint, reportMaterializedRowKey, reportRefreshOutcome, reportRowIdentity, restoreReportSavedView, runRuntimeReport, runRuntimeReportForPrincipal, runStoredRuntimeReport, runtimeReportSpecHash, saveRuntimeReport, scopeKeyForTenant, serializeRuntimeReportSpec, splitReportFilterScopes, sum, validateReportExportArtifact, validateReportExportExecution, validateReportExportRequest, verifyReportExportSnapshot, week, year };
2426
+ export { MaterializedReportsRecipe, REPORT_LOCKS_TABLE, REPORT_REFRESH_TASKS_TABLE, REPORT_RUNS_TABLE, REPORT_RUNTIME_TABLES, REPORT_SCHEDULER_TABLES, REPORT_SCHEDULES_TABLE, REPORT_WATERMARKS_TABLE, RUNTIME_REPORT_BUCKETS, RUNTIME_REPORT_CHART_TYPES, RUNTIME_REPORT_FILTER_OPS, RUNTIME_REPORT_HAVING_OPS, RUNTIME_REPORT_LIMITS, RUNTIME_REPORT_MEASURE_FNS, RUNTIME_REPORT_SPEC_VERSION, ReportScheduleRunner, ReportSurfaceValidationError, RuntimeReport, RuntimeReportCollection, RuntimeReportError, RuntimeReportHttpError, RuntimeReportsRecipe, SmrtPrincipalReportRefreshTask, SmrtReport, SmrtReportCollection, SmrtReportLock, SmrtReportLockCollection, SmrtReportRefreshTask, SmrtReportRun, SmrtReportRunCollection, SmrtReportSchedule, SmrtReportScheduleCollection, SmrtReportWatermark, SmrtReportWatermarkCollection, aggregate, applyReportExport, applyReportRefresh, archiveRuntimeReport, archiveRuntimeReportForPrincipal, assertMaterializedReportRead, assertReportTablesReady, avg, bucketExpr, buildAggregate, buildReportAdapterDescriptor, buildReportDefinition, buildReportDrilldownQuery, compileReportDefinition, compileReportSpec, compileRuntimeReportSpec, count, createReportExportPageRequest, createReportExportRequest, createReportExportSnapshot, createRuntimeReportConfirmHandler, day, describeRuntimeReportMemorySource, describeRuntimeReportSource, enqueueReportRefresh, ensureReportRefreshSchedules, findMaterializedReportHost, getReportGroupingColumns, getReportLifecycle, getRuntimeReport, getRuntimeReportOptions, getRuntimeReportServerOptions, groupBy, hour, listRuntimeReports, loadRuntimeReportChart, max, migrateReportSavedView, min, minute, month, normalizeReportSavedView, parseRuntimeReportSpec, parseRuntimeReportTime, previewReportExport, previewReportRefresh, quarter, queryReportMaterializedRows, refreshReport, registerReportRefreshExecutionAuthorityHost, registerReportRefreshInterceptor, registerReportRefreshJobIntegritySigner, report, reportDefinitionFingerprint, reportMaterializedRowKey, reportRefreshOutcome, reportRowIdentity, resolveRuntimeReportTimeRange, restoreReportSavedView, runRuntimeReport, runRuntimeReportForPrincipal, runRuntimeReportInMemory, runStoredRuntimeReport, runtimeReportBucketForSpan, runtimeReportSpecDigest, runtimeReportSpecHash, saveRuntimeReport, scopeKeyForTenant, serializeRuntimeReportSpec, splitReportFilterScopes, sum, validateReportExportArtifact, validateReportExportExecution, validateReportExportRequest, verifyReportExportSnapshot, week, year };
2960
2427
 
2961
2428
  //# sourceMappingURL=index.js.map