@happyvertical/smrt-reports 0.57.5 → 0.57.7
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/AGENTS.md +10 -0
- package/dist/chunks/runtime-time-D7JZ1e4J.js +1052 -0
- package/dist/chunks/runtime-time-D7JZ1e4J.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -591
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +28 -4
- package/dist/playground.js +1 -1
- package/dist/playground.js.map +1 -1
- package/dist/recipes.d.ts +21 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/runtime-compiler.d.ts +7 -40
- package/dist/runtime-compiler.d.ts.map +1 -1
- package/dist/runtime-dimensions.d.ts +43 -0
- package/dist/runtime-dimensions.d.ts.map +1 -0
- package/dist/runtime-memory.d.ts +81 -0
- package/dist/runtime-memory.d.ts.map +1 -0
- package/dist/runtime-plan.d.ts +121 -0
- package/dist/runtime-plan.d.ts.map +1 -0
- package/dist/runtime-report.d.ts.map +1 -1
- package/dist/runtime-server.d.ts.map +1 -1
- package/dist/runtime-spec-hash.d.ts +11 -0
- package/dist/runtime-spec-hash.d.ts.map +1 -0
- package/dist/runtime-spec.d.ts +16 -4
- package/dist/runtime-spec.d.ts.map +1 -1
- package/dist/runtime-time.d.ts +41 -0
- package/dist/runtime-time.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +37 -12
- package/dist/spec.d.ts +25 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +2 -0
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/dist/svelte/ReportConfirmation.svelte +97 -24
- package/dist/svelte/ReportConfirmation.svelte.d.ts +20 -0
- package/dist/svelte/ReportConfirmation.svelte.d.ts.map +1 -1
- package/dist/svelte/ReportDetail.svelte +263 -12
- package/dist/svelte/ReportDetail.svelte.d.ts +10 -1
- package/dist/svelte/ReportDetail.svelte.d.ts.map +1 -1
- package/dist/svelte/ReportPicker.svelte +47 -14
- package/dist/svelte/ReportPicker.svelte.d.ts +3 -0
- package/dist/svelte/ReportPicker.svelte.d.ts.map +1 -1
- package/dist/svelte/ReportResult.svelte +216 -35
- package/dist/svelte/ReportResult.svelte.d.ts +8 -2
- package/dist/svelte/ReportResult.svelte.d.ts.map +1 -1
- package/dist/svelte/RuntimeReportWidgetOptions.svelte +9 -1
- package/dist/svelte/RuntimeReportWidgetOptions.svelte.d.ts.map +1 -1
- package/dist/svelte/SavedReportCard.svelte +242 -0
- package/dist/svelte/SavedReportCard.svelte.d.ts +30 -0
- package/dist/svelte/SavedReportCard.svelte.d.ts.map +1 -0
- package/dist/svelte/SavedReports.svelte +206 -33
- package/dist/svelte/SavedReports.svelte.d.ts +13 -0
- package/dist/svelte/SavedReports.svelte.d.ts.map +1 -1
- package/dist/svelte/SavedReportsWidget.svelte +59 -0
- package/dist/svelte/SavedReportsWidget.svelte.d.ts +19 -0
- package/dist/svelte/SavedReportsWidget.svelte.d.ts.map +1 -0
- package/dist/svelte/client.d.ts.map +1 -1
- package/dist/svelte/client.js +3 -0
- package/dist/svelte/describe.d.ts +94 -0
- package/dist/svelte/describe.d.ts.map +1 -0
- package/dist/svelte/describe.js +249 -0
- package/dist/svelte/index.d.ts +4 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +4 -1
- package/dist/svelte/messages.d.ts +48 -0
- package/dist/svelte/messages.d.ts.map +1 -1
- package/dist/svelte/messages.js +59 -11
- package/dist/svelte/playground.d.ts +2 -2
- package/dist/svelte/playground.d.ts.map +1 -1
- package/dist/svelte/playground.js +1 -1
- package/dist/svelte/types.d.ts +2 -0
- package/dist/svelte/types.d.ts.map +1 -1
- 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 {
|
|
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(
|
|
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
|
-
|
|
1374
|
-
|
|
1375
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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`]:
|
|
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]}`]:
|
|
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 ??
|
|
1136
|
+
label: prepared.source.label ?? humanizeRuntimeReportName(prepared.registeredName),
|
|
1622
1137
|
...prepared.source.description ? { description: prepared.source.description } : {},
|
|
1623
|
-
|
|
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)
|
|
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
|
|
1661
|
-
const
|
|
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
|
|
1686
|
-
const
|
|
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:
|
|
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 ??
|
|
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] =
|
|
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: "
|
|
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
|