@happyvertical/smrt-reports 0.37.1 → 0.37.3

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/aggregate.js CHANGED
@@ -1,6 +1,2 @@
1
1
  import { bucketExpr, buildAggregate } from "@happyvertical/sql";
2
- export {
3
- bucketExpr,
4
- buildAggregate
5
- };
6
- //# sourceMappingURL=aggregate.js.map
2
+ export { bucketExpr, buildAggregate };
package/dist/compiler.js CHANGED
@@ -1,218 +1,171 @@
1
+ import { getRuntimeReportOptions } from "./decorators.js";
1
2
  import { ObjectRegistry } from "@happyvertical/smrt-core";
2
3
  import { toSnakeCase } from "@happyvertical/smrt-core/utils";
3
- import { getRuntimeReportOptions } from "./decorators.js";
4
- const REPORT_BASE_FIELDS = /* @__PURE__ */ new Set([
5
- "id",
6
- "slug",
7
- "context",
8
- "created_at",
9
- "updated_at",
10
- "createdAt",
11
- "updatedAt",
12
- "refreshedAt"
4
+ //#region src/compiler.ts
5
+ var REPORT_BASE_FIELDS = /* @__PURE__ */ new Set([
6
+ "id",
7
+ "slug",
8
+ "context",
9
+ "created_at",
10
+ "updated_at",
11
+ "createdAt",
12
+ "updatedAt",
13
+ "refreshedAt"
13
14
  ]);
14
15
  function sourceClassName(source) {
15
- if (typeof source === "string") return source;
16
- return source.name;
16
+ if (typeof source === "string") return source;
17
+ return source.name;
17
18
  }
18
19
  function reportOptionsFromRegistry(reportCtor) {
19
- const registered = ObjectRegistry.getClassByConstructor(reportCtor) ?? ObjectRegistry.getClass(reportCtor.name);
20
- return registered?.config?.report;
20
+ return (ObjectRegistry.getClassByConstructor(reportCtor) ?? ObjectRegistry.getClass(reportCtor.name))?.config?.report;
21
21
  }
22
22
  function getReportOptions(reportCtor) {
23
- const options = getRuntimeReportOptions(reportCtor) ?? reportOptionsFromRegistry(reportCtor);
24
- if (!options?.source) {
25
- throw new Error(
26
- `${reportCtor.name} is missing @report({ source }) metadata. Decorate the report class with @report({ source: SourceClass }).`
27
- );
28
- }
29
- return options;
23
+ const options = getRuntimeReportOptions(reportCtor) ?? reportOptionsFromRegistry(reportCtor);
24
+ if (!options?.source) throw new Error(`${reportCtor.name} is missing @report({ source }) metadata. Decorate the report class with @report({ source: SourceClass }).`);
25
+ return options;
30
26
  }
31
27
  function getRegisteredClassName(ctor) {
32
- const registered = ObjectRegistry.getClassByConstructor(ctor) ?? ObjectRegistry.getClass(ctor.name);
33
- return registered?.qualifiedName ?? registered?.name ?? ctor.name;
28
+ const registered = ObjectRegistry.getClassByConstructor(ctor) ?? ObjectRegistry.getClass(ctor.name);
29
+ return registered?.qualifiedName ?? registered?.name ?? ctor.name;
34
30
  }
35
31
  function resolveTableName(className) {
36
- const tableName = ObjectRegistry.getTableName(className);
37
- if (!tableName) {
38
- throw new Error(
39
- `No SMRT table registered for ${className}. Ensure the class is decorated with @smrt() and its manifest is loaded.`
40
- );
41
- }
42
- return tableName;
32
+ const tableName = ObjectRegistry.getTableName(className);
33
+ if (!tableName) throw new Error(`No SMRT table registered for ${className}. Ensure the class is decorated with @smrt() and its manifest is loaded.`);
34
+ return tableName;
43
35
  }
44
36
  function parseWhereKey(key) {
45
- const trimmed = key.trim();
46
- const operators = [
47
- " is not null",
48
- " is null",
49
- " not in",
50
- " like",
51
- " in",
52
- " !=",
53
- " >=",
54
- " <=",
55
- " >",
56
- " <",
57
- " ="
58
- ];
59
- const lower = trimmed.toLowerCase();
60
- for (const operator of operators) {
61
- if (lower.endsWith(operator)) {
62
- return {
63
- field: trimmed.slice(0, -operator.length).trim(),
64
- suffix: operator
65
- };
66
- }
67
- }
68
- return { field: trimmed, suffix: "" };
37
+ const trimmed = key.trim();
38
+ const operators = [
39
+ " is not null",
40
+ " is null",
41
+ " not in",
42
+ " like",
43
+ " in",
44
+ " !=",
45
+ " >=",
46
+ " <=",
47
+ " >",
48
+ " <",
49
+ " ="
50
+ ];
51
+ const lower = trimmed.toLowerCase();
52
+ for (const operator of operators) if (lower.endsWith(operator)) return {
53
+ field: trimmed.slice(0, -operator.length).trim(),
54
+ suffix: operator
55
+ };
56
+ return {
57
+ field: trimmed,
58
+ suffix: ""
59
+ };
69
60
  }
70
61
  function normalizeConditionKey(key, mapField) {
71
- const { field, suffix } = parseWhereKey(key);
72
- const mapped = mapField(field);
73
- return suffix ? `${mapped}${suffix}` : mapped;
62
+ const { field, suffix } = parseWhereKey(key);
63
+ const mapped = mapField(field);
64
+ return suffix ? `${mapped}${suffix}` : mapped;
74
65
  }
75
66
  function normalizeWhere(where, mapField) {
76
- if (!where) return void 0;
77
- if (Array.isArray(where)) {
78
- return where.map(
79
- (andGroup) => andGroup.map(
80
- (condition) => Object.fromEntries(
81
- Object.entries(condition).map(([key, value]) => [
82
- normalizeConditionKey(key, mapField),
83
- value
84
- ])
85
- )
86
- )
87
- );
88
- }
89
- return Object.fromEntries(
90
- Object.entries(where).map(([key, value]) => [
91
- normalizeConditionKey(key, mapField),
92
- value
93
- ])
94
- );
67
+ if (!where) return void 0;
68
+ if (Array.isArray(where)) return where.map((andGroup) => andGroup.map((condition) => Object.fromEntries(Object.entries(condition).map(([key, value]) => [normalizeConditionKey(key, mapField), value]))));
69
+ return Object.fromEntries(Object.entries(where).map(([key, value]) => [normalizeConditionKey(key, mapField), value]));
95
70
  }
96
71
  function readReportMetadata(field) {
97
- return field?._meta?.__report ?? field?.__report;
72
+ return field?._meta?.__report ?? field?.__report;
98
73
  }
99
74
  function isReportSystemField(fieldName, field) {
100
- return REPORT_BASE_FIELDS.has(fieldName) || field?._meta?.__smrtSystemField || field?._meta?.__tenancy?.isTenantIdField;
75
+ return REPORT_BASE_FIELDS.has(fieldName) || field?._meta?.__smrtSystemField || field?._meta?.__tenancy?.isTenantIdField;
101
76
  }
102
77
  async function buildReportDefinition(reportCtor) {
103
- const options = getReportOptions(reportCtor);
104
- const reportClassName = getRegisteredClassName(reportCtor);
105
- const sourceName = sourceClassName(options.source);
106
- const sourceTable = resolveTableName(sourceName);
107
- const fields = await ObjectRegistry.getAllFields(reportClassName);
108
- const reportFields = [];
109
- for (const [fieldName, field] of fields.entries()) {
110
- const report = readReportMetadata(field);
111
- if (!report) {
112
- if (!isReportSystemField(fieldName, field) && !field?.transient) {
113
- throw new Error(
114
- `Report field ${reportCtor.name}.${fieldName} is neither a grouping key nor an aggregate. Add @groupBy(), a time-bucket decorator, or an aggregate decorator.`
115
- );
116
- }
117
- continue;
118
- }
119
- reportFields.push({
120
- fieldName,
121
- columnName: toSnakeCase(fieldName),
122
- type: field.type,
123
- report
124
- });
125
- }
126
- return {
127
- reportClassName,
128
- sourceClassName: sourceName,
129
- sourceTable,
130
- fields: reportFields,
131
- where: options.where,
132
- having: options.having,
133
- refresh: options.refresh
134
- };
78
+ const options = getReportOptions(reportCtor);
79
+ const reportClassName = getRegisteredClassName(reportCtor);
80
+ const sourceName = sourceClassName(options.source);
81
+ const sourceTable = resolveTableName(sourceName);
82
+ const fields = await ObjectRegistry.getAllFields(reportClassName);
83
+ const reportFields = [];
84
+ for (const [fieldName, field] of fields.entries()) {
85
+ const report = readReportMetadata(field);
86
+ if (!report) {
87
+ if (!isReportSystemField(fieldName, field) && !field?.transient) throw new Error(`Report field ${reportCtor.name}.${fieldName} is neither a grouping key nor an aggregate. Add @groupBy(), a time-bucket decorator, or an aggregate decorator.`);
88
+ continue;
89
+ }
90
+ reportFields.push({
91
+ fieldName,
92
+ columnName: toSnakeCase(fieldName),
93
+ type: field.type,
94
+ report
95
+ });
96
+ }
97
+ return {
98
+ reportClassName,
99
+ sourceClassName: sourceName,
100
+ sourceTable,
101
+ fields: reportFields,
102
+ where: options.where,
103
+ having: options.having,
104
+ refresh: options.refresh
105
+ };
135
106
  }
136
107
  function sourceColumn(field) {
137
- const report = field.report;
138
- if (!report) {
139
- throw new Error(`Report field ${field.fieldName} is missing metadata`);
140
- }
141
- if (report.kind === "group") {
142
- return toSnakeCase(report.sourceColumn ?? field.fieldName);
143
- }
144
- if (report.kind === "bucket") {
145
- return toSnakeCase(report.sourceColumn);
146
- }
147
- if (report.kind === "aggregate") {
148
- if (!report.column) return "";
149
- return toSnakeCase(report.column);
150
- }
151
- return "";
108
+ const report = field.report;
109
+ if (!report) throw new Error(`Report field ${field.fieldName} is missing metadata`);
110
+ if (report.kind === "group") return toSnakeCase(report.sourceColumn ?? field.fieldName);
111
+ if (report.kind === "bucket") return toSnakeCase(report.sourceColumn);
112
+ if (report.kind === "aggregate") {
113
+ if (!report.column) return "";
114
+ return toSnakeCase(report.column);
115
+ }
116
+ return "";
152
117
  }
153
118
  function getReportGroupingColumns(definition) {
154
- const columns = definition.fields.filter(
155
- (field) => field.report?.kind === "group" || field.report?.kind === "bucket"
156
- ).map((field) => field.columnName ?? toSnakeCase(field.fieldName));
157
- return columns.length > 0 ? columns : ["id"];
119
+ const columns = definition.fields.filter((field) => field.report?.kind === "group" || field.report?.kind === "bucket").map((field) => field.columnName ?? toSnakeCase(field.fieldName));
120
+ return columns.length > 0 ? columns : ["id"];
158
121
  }
159
122
  function compileReportDefinition(definition) {
160
- const select = [];
161
- const groupBy = [];
162
- const reportFieldToColumn = /* @__PURE__ */ new Map();
163
- for (const field of definition.fields) {
164
- const outputColumn = field.columnName ?? toSnakeCase(field.fieldName);
165
- reportFieldToColumn.set(field.fieldName, outputColumn);
166
- reportFieldToColumn.set(outputColumn, outputColumn);
167
- const report = field.report;
168
- if (!report) continue;
169
- if (report.kind === "group") {
170
- select.push({
171
- column: sourceColumn(field),
172
- as: outputColumn
173
- });
174
- groupBy.push(outputColumn);
175
- continue;
176
- }
177
- if (report.kind === "bucket") {
178
- select.push({
179
- bucket: report.unit,
180
- column: sourceColumn(field),
181
- as: outputColumn
182
- });
183
- groupBy.push(outputColumn);
184
- continue;
185
- }
186
- select.push({
187
- fn: report.fn,
188
- column: report.column ? sourceColumn(field) : void 0,
189
- as: outputColumn,
190
- distinct: report.distinct
191
- });
192
- }
193
- if (select.length === 0) {
194
- throw new Error(
195
- `${definition.reportClassName} has no report fields. Add @groupBy(), a time-bucket decorator, or an aggregate decorator.`
196
- );
197
- }
198
- return {
199
- from: definition.sourceTable,
200
- select,
201
- groupBy,
202
- where: normalizeWhere(definition.where, (field) => toSnakeCase(field)),
203
- having: normalizeWhere(
204
- definition.having,
205
- (field) => reportFieldToColumn.get(field) ?? toSnakeCase(field)
206
- )
207
- };
123
+ const select = [];
124
+ const groupBy = [];
125
+ const reportFieldToColumn = /* @__PURE__ */ new Map();
126
+ for (const field of definition.fields) {
127
+ const outputColumn = field.columnName ?? toSnakeCase(field.fieldName);
128
+ reportFieldToColumn.set(field.fieldName, outputColumn);
129
+ reportFieldToColumn.set(outputColumn, outputColumn);
130
+ const report = field.report;
131
+ if (!report) continue;
132
+ if (report.kind === "group") {
133
+ select.push({
134
+ column: sourceColumn(field),
135
+ as: outputColumn
136
+ });
137
+ groupBy.push(outputColumn);
138
+ continue;
139
+ }
140
+ if (report.kind === "bucket") {
141
+ select.push({
142
+ bucket: report.unit,
143
+ column: sourceColumn(field),
144
+ as: outputColumn
145
+ });
146
+ groupBy.push(outputColumn);
147
+ continue;
148
+ }
149
+ select.push({
150
+ fn: report.fn,
151
+ column: report.column ? sourceColumn(field) : void 0,
152
+ as: outputColumn,
153
+ distinct: report.distinct
154
+ });
155
+ }
156
+ if (select.length === 0) throw new Error(`${definition.reportClassName} has no report fields. Add @groupBy(), a time-bucket decorator, or an aggregate decorator.`);
157
+ return {
158
+ from: definition.sourceTable,
159
+ select,
160
+ groupBy,
161
+ where: normalizeWhere(definition.where, (field) => toSnakeCase(field)),
162
+ having: normalizeWhere(definition.having, (field) => reportFieldToColumn.get(field) ?? toSnakeCase(field))
163
+ };
208
164
  }
209
165
  async function compileReportSpec(reportCtor) {
210
- return compileReportDefinition(await buildReportDefinition(reportCtor));
166
+ return compileReportDefinition(await buildReportDefinition(reportCtor));
211
167
  }
212
- export {
213
- buildReportDefinition,
214
- compileReportDefinition,
215
- compileReportSpec,
216
- getReportGroupingColumns
217
- };
218
- //# sourceMappingURL=compiler.js.map
168
+ //#endregion
169
+ export { buildReportDefinition, compileReportDefinition, compileReportSpec, getReportGroupingColumns };
170
+
171
+ //# sourceMappingURL=compiler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.js","sources":["../src/compiler.ts"],"sourcesContent":["import { ObjectRegistry, type SmrtObject } from '@happyvertical/smrt-core';\nimport { toSnakeCase } from '@happyvertical/smrt-core/utils';\nimport type { WhereClause } from '@happyvertical/sql';\nimport { getRuntimeReportOptions } from './decorators.js';\nimport type {\n AggregateSelectExpr,\n AggregateSpec,\n ReportDefinition,\n ReportFieldDefinition,\n ReportFieldMetadata,\n ReportOptions,\n ReportSource,\n} from './types.js';\n\nconst REPORT_BASE_FIELDS = new Set([\n 'id',\n 'slug',\n 'context',\n 'created_at',\n 'updated_at',\n 'createdAt',\n 'updatedAt',\n 'refreshedAt',\n]);\n\nfunction isConstructor(\n value: ReportSource,\n): value is new (\n ...args: any[]\n) => SmrtObject {\n return typeof value === 'function';\n}\n\nfunction sourceClassName(source: ReportSource): string {\n if (typeof source === 'string') return source;\n return source.name;\n}\n\nfunction reportOptionsFromRegistry(\n reportCtor: new (...args: any[]) => SmrtObject,\n): ReportOptions | undefined {\n const registered =\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name);\n return (registered?.config as { report?: ReportOptions } | undefined)?.report;\n}\n\nfunction getReportOptions(\n reportCtor: new (...args: any[]) => SmrtObject,\n): ReportOptions {\n const options =\n getRuntimeReportOptions(reportCtor) ??\n reportOptionsFromRegistry(reportCtor);\n if (!options?.source) {\n throw new Error(\n `${reportCtor.name} is missing @report({ source }) metadata. ` +\n `Decorate the report class with @report({ source: SourceClass }).`,\n );\n }\n return options;\n}\n\nfunction getRegisteredClassName(\n ctor: new (...args: any[]) => SmrtObject,\n): string {\n const registered =\n ObjectRegistry.getClassByConstructor(ctor) ??\n ObjectRegistry.getClass(ctor.name);\n return registered?.qualifiedName ?? registered?.name ?? ctor.name;\n}\n\nfunction resolveTableName(className: string): string {\n const tableName = ObjectRegistry.getTableName(className);\n if (!tableName) {\n throw new Error(\n `No SMRT table registered for ${className}. ` +\n `Ensure the class is decorated with @smrt() and its manifest is loaded.`,\n );\n }\n return tableName;\n}\n\nfunction parseWhereKey(key: string): { field: string; suffix: string } {\n const trimmed = key.trim();\n const operators = [\n ' is not null',\n ' is null',\n ' not in',\n ' like',\n ' in',\n ' !=',\n ' >=',\n ' <=',\n ' >',\n ' <',\n ' =',\n ];\n const lower = trimmed.toLowerCase();\n for (const operator of operators) {\n if (lower.endsWith(operator)) {\n return {\n field: trimmed.slice(0, -operator.length).trim(),\n suffix: operator,\n };\n }\n }\n return { field: trimmed, suffix: '' };\n}\n\nfunction normalizeConditionKey(\n key: string,\n mapField: (field: string) => string,\n): string {\n const { field, suffix } = parseWhereKey(key);\n const mapped = mapField(field);\n return suffix ? `${mapped}${suffix}` : mapped;\n}\n\nfunction normalizeWhere(\n where: WhereClause | undefined,\n mapField: (field: string) => string,\n): WhereClause | undefined {\n if (!where) return undefined;\n\n if (Array.isArray(where)) {\n return where.map((andGroup) =>\n andGroup.map((condition: Record<string, any>) =>\n Object.fromEntries(\n Object.entries(condition).map(([key, value]) => [\n normalizeConditionKey(key, mapField),\n value,\n ]),\n ),\n ),\n );\n }\n\n return Object.fromEntries(\n Object.entries(where).map(([key, value]) => [\n normalizeConditionKey(key, mapField),\n value,\n ]),\n );\n}\n\nfunction readReportMetadata(field: any): ReportFieldMetadata | undefined {\n return field?._meta?.__report ?? field?.__report;\n}\n\nfunction isReportSystemField(fieldName: string, field: any): boolean {\n return (\n REPORT_BASE_FIELDS.has(fieldName) ||\n field?._meta?.__smrtSystemField ||\n field?._meta?.__tenancy?.isTenantIdField\n );\n}\n\nexport async function buildReportDefinition(\n reportCtor: new (...args: any[]) => SmrtObject,\n): Promise<ReportDefinition> {\n const options = getReportOptions(reportCtor);\n const reportClassName = getRegisteredClassName(reportCtor);\n const sourceName = sourceClassName(options.source);\n const sourceTable = resolveTableName(sourceName);\n const fields = await ObjectRegistry.getAllFields(reportClassName);\n\n const reportFields: ReportFieldDefinition[] = [];\n for (const [fieldName, field] of fields.entries()) {\n const report = readReportMetadata(field);\n if (!report) {\n if (!isReportSystemField(fieldName, field) && !field?.transient) {\n throw new Error(\n `Report field ${reportCtor.name}.${fieldName} is neither a grouping key nor an aggregate. ` +\n `Add @groupBy(), a time-bucket decorator, or an aggregate decorator.`,\n );\n }\n continue;\n }\n\n reportFields.push({\n fieldName,\n columnName: toSnakeCase(fieldName),\n type: field.type,\n report,\n });\n }\n\n return {\n reportClassName,\n sourceClassName: sourceName,\n sourceTable,\n fields: reportFields,\n where: options.where,\n having: options.having,\n refresh: options.refresh,\n };\n}\n\nfunction sourceColumn(field: ReportFieldDefinition): string {\n const report = field.report;\n if (!report) {\n throw new Error(`Report field ${field.fieldName} is missing metadata`);\n }\n if (report.kind === 'group') {\n return toSnakeCase(report.sourceColumn ?? field.fieldName);\n }\n if (report.kind === 'bucket') {\n return toSnakeCase(report.sourceColumn);\n }\n if (report.kind === 'aggregate') {\n if (!report.column) return '';\n return toSnakeCase(report.column);\n }\n return '';\n}\n\nexport function getReportGroupingColumns(\n definition: Pick<ReportDefinition, 'fields'>,\n): string[] {\n const columns = definition.fields\n .filter(\n (field) =>\n field.report?.kind === 'group' || field.report?.kind === 'bucket',\n )\n .map((field) => field.columnName ?? toSnakeCase(field.fieldName));\n return columns.length > 0 ? columns : ['id'];\n}\n\nexport function compileReportDefinition(\n definition: ReportDefinition,\n): AggregateSpec {\n const select: AggregateSelectExpr[] = [];\n const groupBy: string[] = [];\n const reportFieldToColumn = new Map<string, string>();\n\n for (const field of definition.fields) {\n const outputColumn = field.columnName ?? toSnakeCase(field.fieldName);\n reportFieldToColumn.set(field.fieldName, outputColumn);\n reportFieldToColumn.set(outputColumn, outputColumn);\n\n const report = field.report;\n if (!report) continue;\n\n if (report.kind === 'group') {\n select.push({\n column: sourceColumn(field),\n as: outputColumn,\n });\n groupBy.push(outputColumn);\n continue;\n }\n\n if (report.kind === 'bucket') {\n select.push({\n bucket: report.unit,\n column: sourceColumn(field),\n as: outputColumn,\n });\n groupBy.push(outputColumn);\n continue;\n }\n\n select.push({\n fn: report.fn,\n column: report.column ? sourceColumn(field) : undefined,\n as: outputColumn,\n distinct: report.distinct,\n });\n }\n\n if (select.length === 0) {\n throw new Error(\n `${definition.reportClassName} has no report fields. ` +\n `Add @groupBy(), a time-bucket decorator, or an aggregate decorator.`,\n );\n }\n\n return {\n from: definition.sourceTable,\n select,\n groupBy,\n where: normalizeWhere(definition.where, (field) => toSnakeCase(field)),\n having: normalizeWhere(\n definition.having,\n (field) => reportFieldToColumn.get(field) ?? toSnakeCase(field),\n ),\n };\n}\n\nexport async function compileReportSpec(\n reportCtor: new (...args: any[]) => SmrtObject,\n): Promise<AggregateSpec> {\n return compileReportDefinition(await buildReportDefinition(reportCtor));\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compiler.js","names":[],"sources":["../src/compiler.ts"],"sourcesContent":["import { ObjectRegistry, type SmrtObject } from '@happyvertical/smrt-core';\nimport { toSnakeCase } from '@happyvertical/smrt-core/utils';\nimport type { WhereClause } from '@happyvertical/sql';\nimport { getRuntimeReportOptions } from './decorators.js';\nimport type {\n AggregateSelectExpr,\n AggregateSpec,\n ReportDefinition,\n ReportFieldDefinition,\n ReportFieldMetadata,\n ReportOptions,\n ReportSource,\n} from './types.js';\n\nconst REPORT_BASE_FIELDS = new Set([\n 'id',\n 'slug',\n 'context',\n 'created_at',\n 'updated_at',\n 'createdAt',\n 'updatedAt',\n 'refreshedAt',\n]);\n\nfunction isConstructor(\n value: ReportSource,\n): value is new (\n ...args: any[]\n) => SmrtObject {\n return typeof value === 'function';\n}\n\nfunction sourceClassName(source: ReportSource): string {\n if (typeof source === 'string') return source;\n return source.name;\n}\n\nfunction reportOptionsFromRegistry(\n reportCtor: new (...args: any[]) => SmrtObject,\n): ReportOptions | undefined {\n const registered =\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name);\n return (registered?.config as { report?: ReportOptions } | undefined)?.report;\n}\n\nfunction getReportOptions(\n reportCtor: new (...args: any[]) => SmrtObject,\n): ReportOptions {\n const options =\n getRuntimeReportOptions(reportCtor) ??\n reportOptionsFromRegistry(reportCtor);\n if (!options?.source) {\n throw new Error(\n `${reportCtor.name} is missing @report({ source }) metadata. ` +\n `Decorate the report class with @report({ source: SourceClass }).`,\n );\n }\n return options;\n}\n\nfunction getRegisteredClassName(\n ctor: new (...args: any[]) => SmrtObject,\n): string {\n const registered =\n ObjectRegistry.getClassByConstructor(ctor) ??\n ObjectRegistry.getClass(ctor.name);\n return registered?.qualifiedName ?? registered?.name ?? ctor.name;\n}\n\nfunction resolveTableName(className: string): string {\n const tableName = ObjectRegistry.getTableName(className);\n if (!tableName) {\n throw new Error(\n `No SMRT table registered for ${className}. ` +\n `Ensure the class is decorated with @smrt() and its manifest is loaded.`,\n );\n }\n return tableName;\n}\n\nfunction parseWhereKey(key: string): { field: string; suffix: string } {\n const trimmed = key.trim();\n const operators = [\n ' is not null',\n ' is null',\n ' not in',\n ' like',\n ' in',\n ' !=',\n ' >=',\n ' <=',\n ' >',\n ' <',\n ' =',\n ];\n const lower = trimmed.toLowerCase();\n for (const operator of operators) {\n if (lower.endsWith(operator)) {\n return {\n field: trimmed.slice(0, -operator.length).trim(),\n suffix: operator,\n };\n }\n }\n return { field: trimmed, suffix: '' };\n}\n\nfunction normalizeConditionKey(\n key: string,\n mapField: (field: string) => string,\n): string {\n const { field, suffix } = parseWhereKey(key);\n const mapped = mapField(field);\n return suffix ? `${mapped}${suffix}` : mapped;\n}\n\nfunction normalizeWhere(\n where: WhereClause | undefined,\n mapField: (field: string) => string,\n): WhereClause | undefined {\n if (!where) return undefined;\n\n if (Array.isArray(where)) {\n return where.map((andGroup) =>\n andGroup.map((condition: Record<string, any>) =>\n Object.fromEntries(\n Object.entries(condition).map(([key, value]) => [\n normalizeConditionKey(key, mapField),\n value,\n ]),\n ),\n ),\n );\n }\n\n return Object.fromEntries(\n Object.entries(where).map(([key, value]) => [\n normalizeConditionKey(key, mapField),\n value,\n ]),\n );\n}\n\nfunction readReportMetadata(field: any): ReportFieldMetadata | undefined {\n return field?._meta?.__report ?? field?.__report;\n}\n\nfunction isReportSystemField(fieldName: string, field: any): boolean {\n return (\n REPORT_BASE_FIELDS.has(fieldName) ||\n field?._meta?.__smrtSystemField ||\n field?._meta?.__tenancy?.isTenantIdField\n );\n}\n\nexport async function buildReportDefinition(\n reportCtor: new (...args: any[]) => SmrtObject,\n): Promise<ReportDefinition> {\n const options = getReportOptions(reportCtor);\n const reportClassName = getRegisteredClassName(reportCtor);\n const sourceName = sourceClassName(options.source);\n const sourceTable = resolveTableName(sourceName);\n const fields = await ObjectRegistry.getAllFields(reportClassName);\n\n const reportFields: ReportFieldDefinition[] = [];\n for (const [fieldName, field] of fields.entries()) {\n const report = readReportMetadata(field);\n if (!report) {\n if (!isReportSystemField(fieldName, field) && !field?.transient) {\n throw new Error(\n `Report field ${reportCtor.name}.${fieldName} is neither a grouping key nor an aggregate. ` +\n `Add @groupBy(), a time-bucket decorator, or an aggregate decorator.`,\n );\n }\n continue;\n }\n\n reportFields.push({\n fieldName,\n columnName: toSnakeCase(fieldName),\n type: field.type,\n report,\n });\n }\n\n return {\n reportClassName,\n sourceClassName: sourceName,\n sourceTable,\n fields: reportFields,\n where: options.where,\n having: options.having,\n refresh: options.refresh,\n };\n}\n\nfunction sourceColumn(field: ReportFieldDefinition): string {\n const report = field.report;\n if (!report) {\n throw new Error(`Report field ${field.fieldName} is missing metadata`);\n }\n if (report.kind === 'group') {\n return toSnakeCase(report.sourceColumn ?? field.fieldName);\n }\n if (report.kind === 'bucket') {\n return toSnakeCase(report.sourceColumn);\n }\n if (report.kind === 'aggregate') {\n if (!report.column) return '';\n return toSnakeCase(report.column);\n }\n return '';\n}\n\nexport function getReportGroupingColumns(\n definition: Pick<ReportDefinition, 'fields'>,\n): string[] {\n const columns = definition.fields\n .filter(\n (field) =>\n field.report?.kind === 'group' || field.report?.kind === 'bucket',\n )\n .map((field) => field.columnName ?? toSnakeCase(field.fieldName));\n return columns.length > 0 ? columns : ['id'];\n}\n\nexport function compileReportDefinition(\n definition: ReportDefinition,\n): AggregateSpec {\n const select: AggregateSelectExpr[] = [];\n const groupBy: string[] = [];\n const reportFieldToColumn = new Map<string, string>();\n\n for (const field of definition.fields) {\n const outputColumn = field.columnName ?? toSnakeCase(field.fieldName);\n reportFieldToColumn.set(field.fieldName, outputColumn);\n reportFieldToColumn.set(outputColumn, outputColumn);\n\n const report = field.report;\n if (!report) continue;\n\n if (report.kind === 'group') {\n select.push({\n column: sourceColumn(field),\n as: outputColumn,\n });\n groupBy.push(outputColumn);\n continue;\n }\n\n if (report.kind === 'bucket') {\n select.push({\n bucket: report.unit,\n column: sourceColumn(field),\n as: outputColumn,\n });\n groupBy.push(outputColumn);\n continue;\n }\n\n select.push({\n fn: report.fn,\n column: report.column ? sourceColumn(field) : undefined,\n as: outputColumn,\n distinct: report.distinct,\n });\n }\n\n if (select.length === 0) {\n throw new Error(\n `${definition.reportClassName} has no report fields. ` +\n `Add @groupBy(), a time-bucket decorator, or an aggregate decorator.`,\n );\n }\n\n return {\n from: definition.sourceTable,\n select,\n groupBy,\n where: normalizeWhere(definition.where, (field) => toSnakeCase(field)),\n having: normalizeWhere(\n definition.having,\n (field) => reportFieldToColumn.get(field) ?? toSnakeCase(field),\n ),\n };\n}\n\nexport async function compileReportSpec(\n reportCtor: new (...args: any[]) => SmrtObject,\n): Promise<AggregateSpec> {\n return compileReportDefinition(await buildReportDefinition(reportCtor));\n}\n"],"mappings":"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"}
@@ -1,84 +1,69 @@
1
- import { registerCompatibleFieldDecorator, ObjectRegistry } from "@happyvertical/smrt-core";
2
- const reportOptionsByConstructor = /* @__PURE__ */ new WeakMap();
1
+ import { ObjectRegistry, registerCompatibleFieldDecorator } from "@happyvertical/smrt-core";
2
+ //#region src/decorators.ts
3
+ var reportOptionsByConstructor = /* @__PURE__ */ new WeakMap();
3
4
  function report(options) {
4
- return (ctor) => {
5
- reportOptionsByConstructor.set(ctor, options);
6
- return ctor;
7
- };
5
+ return (ctor) => {
6
+ reportOptionsByConstructor.set(ctor, options);
7
+ return ctor;
8
+ };
8
9
  }
9
10
  function getRuntimeReportOptions(ctor) {
10
- return reportOptionsByConstructor.get(ctor);
11
+ return reportOptionsByConstructor.get(ctor);
11
12
  }
12
13
  function registerReportField(metadata) {
13
- return ((targetOrValue, propertyKeyOrContext) => {
14
- registerCompatibleFieldDecorator(
15
- targetOrValue,
16
- propertyKeyOrContext,
17
- (className, propertyKey) => {
18
- ObjectRegistry.registerFieldDecorator(className, propertyKey, {
19
- __report: metadata
20
- });
21
- }
22
- );
23
- });
14
+ return ((targetOrValue, propertyKeyOrContext) => {
15
+ registerCompatibleFieldDecorator(targetOrValue, propertyKeyOrContext, (className, propertyKey) => {
16
+ ObjectRegistry.registerFieldDecorator(className, propertyKey, { __report: metadata });
17
+ });
18
+ });
24
19
  }
25
20
  function groupBy(sourceColumn) {
26
- return registerReportField({ kind: "group", sourceColumn });
21
+ return registerReportField({
22
+ kind: "group",
23
+ sourceColumn
24
+ });
27
25
  }
28
26
  function bucket(unit, sourceColumn) {
29
- return registerReportField({
30
- kind: "bucket",
31
- unit,
32
- sourceColumn
33
- });
27
+ return registerReportField({
28
+ kind: "bucket",
29
+ unit,
30
+ sourceColumn
31
+ });
34
32
  }
35
- const minute = (sourceColumn) => bucket("minute", sourceColumn);
36
- const hour = (sourceColumn) => bucket("hour", sourceColumn);
37
- const day = (sourceColumn) => bucket("day", sourceColumn);
38
- const week = (sourceColumn) => bucket("week", sourceColumn);
39
- const month = (sourceColumn) => bucket("month", sourceColumn);
40
- const quarter = (sourceColumn) => bucket("quarter", sourceColumn);
41
- const year = (sourceColumn) => bucket("year", sourceColumn);
33
+ var minute = (sourceColumn) => bucket("minute", sourceColumn);
34
+ var hour = (sourceColumn) => bucket("hour", sourceColumn);
35
+ var day = (sourceColumn) => bucket("day", sourceColumn);
36
+ var week = (sourceColumn) => bucket("week", sourceColumn);
37
+ var month = (sourceColumn) => bucket("month", sourceColumn);
38
+ var quarter = (sourceColumn) => bucket("quarter", sourceColumn);
39
+ var year = (sourceColumn) => bucket("year", sourceColumn);
42
40
  function aggregate(options) {
43
- return registerReportField({
44
- kind: "aggregate",
45
- fn: options.fn,
46
- column: options.column,
47
- distinct: options.distinct
48
- });
41
+ return registerReportField({
42
+ kind: "aggregate",
43
+ fn: options.fn,
44
+ column: options.column,
45
+ distinct: options.distinct
46
+ });
49
47
  }
50
48
  function aggregateDecorator(fn) {
51
- return (column, options = {}) => aggregate({ fn, column, distinct: options.distinct });
49
+ return (column, options = {}) => aggregate({
50
+ fn,
51
+ column,
52
+ distinct: options.distinct
53
+ });
52
54
  }
53
- const sum = aggregateDecorator("sum");
54
- const avg = aggregateDecorator("avg");
55
- const min = aggregateDecorator("min");
56
- const max = aggregateDecorator("max");
55
+ var sum = aggregateDecorator("sum");
56
+ var avg = aggregateDecorator("avg");
57
+ var min = aggregateDecorator("min");
58
+ var max = aggregateDecorator("max");
57
59
  function count(columnOrOptions, options = {}) {
58
- const column = typeof columnOrOptions === "string" ? columnOrOptions : void 0;
59
- const resolvedOptions = typeof columnOrOptions === "object" ? columnOrOptions : options;
60
- return aggregate({
61
- fn: "count",
62
- column,
63
- distinct: resolvedOptions.distinct
64
- });
60
+ return aggregate({
61
+ fn: "count",
62
+ column: typeof columnOrOptions === "string" ? columnOrOptions : void 0,
63
+ distinct: (typeof columnOrOptions === "object" ? columnOrOptions : options).distinct
64
+ });
65
65
  }
66
- export {
67
- aggregate,
68
- avg,
69
- count,
70
- day,
71
- getRuntimeReportOptions,
72
- groupBy,
73
- hour,
74
- max,
75
- min,
76
- minute,
77
- month,
78
- quarter,
79
- report,
80
- sum,
81
- week,
82
- year
83
- };
84
- //# sourceMappingURL=decorators.js.map
66
+ //#endregion
67
+ export { aggregate, avg, count, day, getRuntimeReportOptions, groupBy, hour, max, min, minute, month, quarter, report, sum, week, year };
68
+
69
+ //# sourceMappingURL=decorators.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sources":["../src/decorators.ts"],"sourcesContent":["import {\n ObjectRegistry,\n registerCompatibleFieldDecorator,\n} from '@happyvertical/smrt-core';\nimport type {\n ReportAggregateFieldMetadata,\n ReportAggregateFn,\n ReportBucketFieldMetadata,\n ReportFieldMetadata,\n ReportOptions,\n ReportTimeBucketUnit,\n} from './types.js';\n\nconst reportOptionsByConstructor = new WeakMap<Function, ReportOptions>();\n\nexport function report(options: ReportOptions) {\n return <T extends abstract new (...args: any[]) => any>(ctor: T): T => {\n reportOptionsByConstructor.set(ctor, options);\n return ctor;\n };\n}\n\nexport function getRuntimeReportOptions(\n ctor: Function,\n): ReportOptions | undefined {\n return reportOptionsByConstructor.get(ctor);\n}\n\nfunction registerReportField(metadata: ReportFieldMetadata) {\n return ((targetOrValue: any, propertyKeyOrContext: any) => {\n registerCompatibleFieldDecorator(\n targetOrValue,\n propertyKeyOrContext,\n (className, propertyKey) => {\n ObjectRegistry.registerFieldDecorator(className, propertyKey, {\n __report: metadata,\n });\n },\n );\n }) as PropertyDecorator;\n}\n\nexport function groupBy(sourceColumn?: string): PropertyDecorator {\n return registerReportField({ kind: 'group', sourceColumn });\n}\n\nfunction bucket(\n unit: ReportTimeBucketUnit,\n sourceColumn: string,\n): PropertyDecorator {\n return registerReportField({\n kind: 'bucket',\n unit,\n sourceColumn,\n } satisfies ReportBucketFieldMetadata);\n}\n\nexport const minute = (sourceColumn: string) => bucket('minute', sourceColumn);\nexport const hour = (sourceColumn: string) => bucket('hour', sourceColumn);\nexport const day = (sourceColumn: string) => bucket('day', sourceColumn);\nexport const week = (sourceColumn: string) => bucket('week', sourceColumn);\nexport const month = (sourceColumn: string) => bucket('month', sourceColumn);\nexport const quarter = (sourceColumn: string) =>\n bucket('quarter', sourceColumn);\nexport const year = (sourceColumn: string) => bucket('year', sourceColumn);\n\nexport interface AggregateDecoratorOptions {\n fn: ReportAggregateFn;\n column?: string;\n distinct?: boolean;\n}\n\nexport function aggregate(\n options: AggregateDecoratorOptions,\n): PropertyDecorator {\n return registerReportField({\n kind: 'aggregate',\n fn: options.fn,\n column: options.column,\n distinct: options.distinct,\n } satisfies ReportAggregateFieldMetadata);\n}\n\nfunction aggregateDecorator(fn: ReportAggregateFn) {\n return (column?: string, options: { distinct?: boolean } = {}) =>\n aggregate({ fn, column, distinct: options.distinct });\n}\n\nexport const sum = aggregateDecorator('sum');\nexport const avg = aggregateDecorator('avg');\nexport const min = aggregateDecorator('min');\nexport const max = aggregateDecorator('max');\n\nexport function count(): PropertyDecorator;\nexport function count(\n column: string,\n options?: { distinct?: boolean },\n): PropertyDecorator;\nexport function count(options: { distinct?: boolean }): PropertyDecorator;\nexport function count(\n columnOrOptions?: string | { distinct?: boolean },\n options: { distinct?: boolean } = {},\n): PropertyDecorator {\n const column =\n typeof columnOrOptions === 'string' ? columnOrOptions : undefined;\n const resolvedOptions =\n typeof columnOrOptions === 'object' ? columnOrOptions : options;\n return aggregate({\n fn: 'count',\n column,\n distinct: resolvedOptions.distinct,\n });\n}\n"],"names":[],"mappings":";AAaA,MAAM,iDAAiC,QAAA;AAEhC,SAAS,OAAO,SAAwB;AAC7C,SAAO,CAAiD,SAAe;AACrE,+BAA2B,IAAI,MAAM,OAAO;AAC5C,WAAO;AAAA,EACT;AACF;AAEO,SAAS,wBACd,MAC2B;AAC3B,SAAO,2BAA2B,IAAI,IAAI;AAC5C;AAEA,SAAS,oBAAoB,UAA+B;AAC1D,UAAQ,CAAC,eAAoB,yBAA8B;AACzD;AAAA,MACE;AAAA,MACA;AAAA,MACA,CAAC,WAAW,gBAAgB;AAC1B,uBAAe,uBAAuB,WAAW,aAAa;AAAA,UAC5D,UAAU;AAAA,QAAA,CACX;AAAA,MACH;AAAA,IAAA;AAAA,EAEJ;AACF;AAEO,SAAS,QAAQ,cAA0C;AAChE,SAAO,oBAAoB,EAAE,MAAM,SAAS,cAAc;AAC5D;AAEA,SAAS,OACP,MACA,cACmB;AACnB,SAAO,oBAAoB;AAAA,IACzB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EAAA,CACmC;AACvC;AAEO,MAAM,SAAS,CAAC,iBAAyB,OAAO,UAAU,YAAY;AACtE,MAAM,OAAO,CAAC,iBAAyB,OAAO,QAAQ,YAAY;AAClE,MAAM,MAAM,CAAC,iBAAyB,OAAO,OAAO,YAAY;AAChE,MAAM,OAAO,CAAC,iBAAyB,OAAO,QAAQ,YAAY;AAClE,MAAM,QAAQ,CAAC,iBAAyB,OAAO,SAAS,YAAY;AACpE,MAAM,UAAU,CAAC,iBACtB,OAAO,WAAW,YAAY;AACzB,MAAM,OAAO,CAAC,iBAAyB,OAAO,QAAQ,YAAY;AAQlE,SAAS,UACd,SACmB;AACnB,SAAO,oBAAoB;AAAA,IACzB,MAAM;AAAA,IACN,IAAI,QAAQ;AAAA,IACZ,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,EAAA,CACoB;AAC1C;AAEA,SAAS,mBAAmB,IAAuB;AACjD,SAAO,CAAC,QAAiB,UAAkC,CAAA,MACzD,UAAU,EAAE,IAAI,QAAQ,UAAU,QAAQ,SAAA,CAAU;AACxD;AAEO,MAAM,MAAM,mBAAmB,KAAK;AACpC,MAAM,MAAM,mBAAmB,KAAK;AACpC,MAAM,MAAM,mBAAmB,KAAK;AACpC,MAAM,MAAM,mBAAmB,KAAK;AAQpC,SAAS,MACd,iBACA,UAAkC,IACf;AACnB,QAAM,SACJ,OAAO,oBAAoB,WAAW,kBAAkB;AAC1D,QAAM,kBACJ,OAAO,oBAAoB,WAAW,kBAAkB;AAC1D,SAAO,UAAU;AAAA,IACf,IAAI;AAAA,IACJ;AAAA,IACA,UAAU,gBAAgB;AAAA,EAAA,CAC3B;AACH;"}
1
+ {"version":3,"file":"decorators.js","names":[],"sources":["../src/decorators.ts"],"sourcesContent":["import {\n ObjectRegistry,\n registerCompatibleFieldDecorator,\n} from '@happyvertical/smrt-core';\nimport type {\n ReportAggregateFieldMetadata,\n ReportAggregateFn,\n ReportBucketFieldMetadata,\n ReportFieldMetadata,\n ReportOptions,\n ReportTimeBucketUnit,\n} from './types.js';\n\nconst reportOptionsByConstructor = new WeakMap<Function, ReportOptions>();\n\nexport function report(options: ReportOptions) {\n return <T extends abstract new (...args: any[]) => any>(ctor: T): T => {\n reportOptionsByConstructor.set(ctor, options);\n return ctor;\n };\n}\n\nexport function getRuntimeReportOptions(\n ctor: Function,\n): ReportOptions | undefined {\n return reportOptionsByConstructor.get(ctor);\n}\n\nfunction registerReportField(metadata: ReportFieldMetadata) {\n return ((targetOrValue: any, propertyKeyOrContext: any) => {\n registerCompatibleFieldDecorator(\n targetOrValue,\n propertyKeyOrContext,\n (className, propertyKey) => {\n ObjectRegistry.registerFieldDecorator(className, propertyKey, {\n __report: metadata,\n });\n },\n );\n }) as PropertyDecorator;\n}\n\nexport function groupBy(sourceColumn?: string): PropertyDecorator {\n return registerReportField({ kind: 'group', sourceColumn });\n}\n\nfunction bucket(\n unit: ReportTimeBucketUnit,\n sourceColumn: string,\n): PropertyDecorator {\n return registerReportField({\n kind: 'bucket',\n unit,\n sourceColumn,\n } satisfies ReportBucketFieldMetadata);\n}\n\nexport const minute = (sourceColumn: string) => bucket('minute', sourceColumn);\nexport const hour = (sourceColumn: string) => bucket('hour', sourceColumn);\nexport const day = (sourceColumn: string) => bucket('day', sourceColumn);\nexport const week = (sourceColumn: string) => bucket('week', sourceColumn);\nexport const month = (sourceColumn: string) => bucket('month', sourceColumn);\nexport const quarter = (sourceColumn: string) =>\n bucket('quarter', sourceColumn);\nexport const year = (sourceColumn: string) => bucket('year', sourceColumn);\n\nexport interface AggregateDecoratorOptions {\n fn: ReportAggregateFn;\n column?: string;\n distinct?: boolean;\n}\n\nexport function aggregate(\n options: AggregateDecoratorOptions,\n): PropertyDecorator {\n return registerReportField({\n kind: 'aggregate',\n fn: options.fn,\n column: options.column,\n distinct: options.distinct,\n } satisfies ReportAggregateFieldMetadata);\n}\n\nfunction aggregateDecorator(fn: ReportAggregateFn) {\n return (column?: string, options: { distinct?: boolean } = {}) =>\n aggregate({ fn, column, distinct: options.distinct });\n}\n\nexport const sum = aggregateDecorator('sum');\nexport const avg = aggregateDecorator('avg');\nexport const min = aggregateDecorator('min');\nexport const max = aggregateDecorator('max');\n\nexport function count(): PropertyDecorator;\nexport function count(\n column: string,\n options?: { distinct?: boolean },\n): PropertyDecorator;\nexport function count(options: { distinct?: boolean }): PropertyDecorator;\nexport function count(\n columnOrOptions?: string | { distinct?: boolean },\n options: { distinct?: boolean } = {},\n): PropertyDecorator {\n const column =\n typeof columnOrOptions === 'string' ? columnOrOptions : undefined;\n const resolvedOptions =\n typeof columnOrOptions === 'object' ? columnOrOptions : options;\n return aggregate({\n fn: 'count',\n column,\n distinct: resolvedOptions.distinct,\n });\n}\n"],"mappings":";;AAaA,IAAM,6CAA6B,IAAI,QAAiC;AAEjE,SAAS,OAAO,SAAwB;CAC7C,QAAwD,SAAe;EACrE,2BAA2B,IAAI,MAAM,OAAO;EAC5C,OAAO;CACT;AACF;AAEO,SAAS,wBACd,MAC2B;CAC3B,OAAO,2BAA2B,IAAI,IAAI;AAC5C;AAEA,SAAS,oBAAoB,UAA+B;CAC1D,SAAS,eAAoB,yBAA8B;EACzD,iCACE,eACA,uBACC,WAAW,gBAAgB;GAC1B,eAAe,uBAAuB,WAAW,aAAa,EAC5D,UAAU,SACZ,CAAC;EACH,CACF;CACF;AACF;AAEO,SAAS,QAAQ,cAA0C;CAChE,OAAO,oBAAoB;EAAE,MAAM;EAAS;CAAa,CAAC;AAC5D;AAEA,SAAS,OACP,MACA,cACmB;CACnB,OAAO,oBAAoB;EACzB,MAAM;EACN;EACA;CACF,CAAqC;AACvC;AAEO,IAAM,UAAU,iBAAyB,OAAO,UAAU,YAAY;AACtE,IAAM,QAAQ,iBAAyB,OAAO,QAAQ,YAAY;AAClE,IAAM,OAAO,iBAAyB,OAAO,OAAO,YAAY;AAChE,IAAM,QAAQ,iBAAyB,OAAO,QAAQ,YAAY;AAClE,IAAM,SAAS,iBAAyB,OAAO,SAAS,YAAY;AACpE,IAAM,WAAW,iBACtB,OAAO,WAAW,YAAY;AACzB,IAAM,QAAQ,iBAAyB,OAAO,QAAQ,YAAY;AAQlE,SAAS,UACd,SACmB;CACnB,OAAO,oBAAoB;EACzB,MAAM;EACN,IAAI,QAAQ;EACZ,QAAQ,QAAQ;EAChB,UAAU,QAAQ;CACpB,CAAwC;AAC1C;AAEA,SAAS,mBAAmB,IAAuB;CACjD,QAAQ,QAAiB,UAAkC,CAAC,MAC1D,UAAU;EAAE;EAAI;EAAQ,UAAU,QAAQ;CAAS,CAAC;AACxD;AAEO,IAAM,MAAM,mBAAmB,KAAK;AACpC,IAAM,MAAM,mBAAmB,KAAK;AACpC,IAAM,MAAM,mBAAmB,KAAK;AACpC,IAAM,MAAM,mBAAmB,KAAK;AAQpC,SAAS,MACd,iBACA,UAAkC,CAAC,GAChB;CAKnB,OAAO,UAAU;EACf,IAAI;EACJ,QALA,OAAO,oBAAoB,WAAW,kBAAkB,KAAA;EAMxD,WAJA,OAAO,oBAAoB,WAAW,kBAAkB,QAAA,CAI9B;CAC5B,CAAC;AACH"}