@happyvertical/smrt-reports 0.36.5
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 +26 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/dist/aggregate.d.ts +25 -0
- package/dist/aggregate.js +6 -0
- package/dist/aggregate.js.map +1 -0
- package/dist/compiler.d.ts +78 -0
- package/dist/compiler.js +218 -0
- package/dist/compiler.js.map +1 -0
- package/dist/decorators.d.ts +90 -0
- package/dist/decorators.js +84 -0
- package/dist/decorators.js.map +1 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.js +162 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +1639 -0
- package/dist/refresh.d.ts +106 -0
- package/dist/refresh.js +778 -0
- package/dist/refresh.js.map +1 -0
- package/dist/scheduler.d.ts +112 -0
- package/dist/scheduler.js +449 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/smrt-knowledge.json +584 -0
- package/dist/state.d.ts +108 -0
- package/dist/state.js +320 -0
- package/dist/state.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sources":["../src/scheduler.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport {\n field,\n GlobalInterceptors,\n type InterceptorContext,\n ObjectRegistry,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n backgroundEligible,\n getNextCronDate,\n type SmrtJob,\n SmrtJobCollection,\n validateCronExpression,\n} from '@happyvertical/smrt-jobs';\nimport {\n getTenantId,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport type { DatabaseInterface, SqlAdapterType } from '@happyvertical/sql';\nimport { buildReportDefinition } from './compiler.js';\nimport { refreshReport } from './refresh.js';\nimport {\n assertReportTablesReady,\n REPORT_SCHEDULER_TABLES,\n REPORT_SCHEDULES_TABLE,\n scopeKeyForTenant,\n} from './state.js';\nimport type {\n ReportDefinition,\n ReportRefreshMode,\n ReportRefreshTrigger,\n ReportSource,\n} from './types.js';\n\ntype ReportCtor = new (...args: any[]) => SmrtObject;\n\nexport interface ReportRefreshJobArgs {\n reportClass?: string;\n mode?: ReportRefreshMode;\n trigger?: ReportRefreshTrigger;\n tenantId?: string | null;\n tenantIds?: string[];\n scheduleId?: string;\n adapterType?: SqlAdapterType;\n changedRows?: Record<string, unknown>[];\n _scheduleId?: string;\n}\n\nexport interface EnqueueReportRefreshOptions extends ReportRefreshJobArgs {\n report?: ReportCtor;\n reportClass: string;\n db: DatabaseInterface;\n queue?: string;\n priority?: number;\n timeout?: number;\n maxAttempts?: number;\n tenantJobCap?: number;\n}\n\nexport interface EnsureReportSchedulesOptions {\n db: DatabaseInterface;\n reports: ReportCtor[];\n tenantIds?: string[];\n queue?: string;\n priority?: number;\n timeout?: number;\n}\n\nexport interface ReportScheduleRunnerConfig {\n id?: string;\n pollInterval?: number;\n batchSize?: number;\n}\n\nexport interface ReportScheduleInfo {\n id: string;\n reportClass: string;\n tenantId: string | null;\n cron: string;\n mode: ReportRefreshMode;\n}\n\nexport interface ReportScheduleRunnerEvents {\n 'schedule:triggered': (schedule: ReportScheduleInfo) => void;\n 'schedule:error': (schedule: ReportScheduleInfo, error: Error) => void;\n 'schedule:completed': (scheduleId: string) => void;\n 'schedule:failed': (scheduleId: string, error: string) => void;\n 'runner:started': () => void;\n 'runner:stopped': () => void;\n 'runner:error': (error: Error) => void;\n}\n\nexport interface ReportRefreshInterceptorOptions {\n db: DatabaseInterface;\n reports: ReportCtor[];\n enqueue?: boolean;\n queue?: string;\n priority?: number;\n timeout?: number;\n tenantJobCap?: number;\n name?: string;\n}\n\nconst INTERNAL_SURFACE = {\n api: false,\n cli: {\n include: ['list', 'get'],\n skipApiCheck: true,\n http: false,\n },\n mcp: false,\n};\n\nfunction stableUuid(values: unknown[]): string {\n const hash = createHash('sha256')\n .update(JSON.stringify(values))\n .digest('hex');\n const variant = ((Number.parseInt(hash[16], 16) & 0x3) | 0x8).toString(16);\n return [\n hash.slice(0, 8),\n hash.slice(8, 12),\n `4${hash.slice(13, 16)}`,\n `${variant}${hash.slice(17, 20)}`,\n hash.slice(20, 32),\n ].join('-');\n}\n\nfunction canonicalClassName(reportCtor: ReportCtor): string {\n const registered =\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name);\n return registered?.qualifiedName ?? registered?.name ?? reportCtor.name;\n}\n\nfunction resolveReportClass(name: string): ReportCtor {\n const registered =\n ObjectRegistry.getClassByQualifiedName(name) ??\n ObjectRegistry.getClass(name);\n if (!registered) {\n throw new Error(`Unknown report class: ${name}`);\n }\n return registered.constructor as unknown as ReportCtor;\n}\n\nfunction reportSourceName(source: ReportSource): string {\n if (typeof source === 'string') return source;\n return source.name;\n}\n\nfunction sourceMatches(\n definition: ReportDefinition,\n instance: SmrtObject,\n context: InterceptorContext,\n): boolean {\n const configured = definition.refresh?.onChange;\n if (!configured || configured.length === 0) return false;\n\n const eventNames = new Set<string>([\n context.className,\n instance.constructor.name,\n ]);\n const registered = ObjectRegistry.getClassByConstructor(\n instance.constructor as ReportCtor,\n );\n if (registered?.qualifiedName) eventNames.add(registered.qualifiedName);\n if (registered?.name) eventNames.add(registered.name);\n\n for (const source of configured) {\n const name = reportSourceName(source);\n const registeredSource =\n ObjectRegistry.getClassByQualifiedName(name) ??\n ObjectRegistry.getClass(name);\n if (\n eventNames.has(name) ||\n (registeredSource?.name && eventNames.has(registeredSource.name)) ||\n (registeredSource?.qualifiedName &&\n eventNames.has(registeredSource.qualifiedName))\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction tenantIdFromInstance(instance: SmrtObject): string | null {\n const value = (instance as unknown as { tenantId?: unknown }).tenantId;\n return typeof value === 'string' && value.length > 0\n ? value\n : (getTenantId() ?? null);\n}\n\nfunction changedRowSnapshot(instance: SmrtObject): Record<string, unknown> {\n const serializable = instance.toJSON();\n return serializable && typeof serializable === 'object'\n ? (serializable as Record<string, unknown>)\n : {};\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_report_refresh_tasks',\n ...INTERNAL_SURFACE,\n})\nexport class SmrtReportRefreshTask extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @field({ type: 'text', required: true })\n reportClass: string = '';\n\n @field({ type: 'text', required: true })\n mode: ReportRefreshMode = 'incremental';\n\n @field({ type: 'text', required: true })\n trigger: ReportRefreshTrigger = 'job';\n\n @field({ type: 'json' })\n args: ReportRefreshJobArgs = {};\n\n @backgroundEligible()\n async run(args: ReportRefreshJobArgs = {}): Promise<unknown> {\n const reportClass = args.reportClass || this.reportClass;\n if (!reportClass) {\n throw new Error('Report refresh job requires reportClass');\n }\n\n const reportCtor = resolveReportClass(reportClass);\n return refreshReport(reportCtor, {\n db: this.db,\n mode: args.mode ?? this.mode,\n trigger: args.trigger ?? this.trigger,\n tenantId: args.tenantId,\n tenantIds: args.tenantIds,\n adapterType: args.adapterType,\n scheduleId: args.scheduleId ?? args._scheduleId,\n changedRows: args.changedRows,\n });\n }\n}\n\nexport async function enqueueReportRefresh(\n options: EnqueueReportRefreshOptions,\n): Promise<SmrtJob> {\n await ObjectRegistry.ensureManifestLoaded('SmrtJob');\n const collection = await SmrtJobCollection.create({ db: options.db });\n const taskType = canonicalClassName(SmrtReportRefreshTask);\n const scheduleId = options.scheduleId ?? options._scheduleId;\n\n return collection.enqueueJob(\n {\n tenantId: options.tenantId ?? null,\n queue: options.queue ?? 'reports',\n objectType: taskType,\n objectId: null,\n method: 'run',\n args: {\n reportClass: options.reportClass,\n mode: options.mode,\n trigger: options.trigger ?? 'job',\n tenantId: options.tenantId,\n tenantIds: options.tenantIds,\n adapterType: options.adapterType,\n changedRows: options.changedRows,\n scheduleId,\n _scheduleId: scheduleId,\n },\n priority: options.priority ?? 70,\n timeout: options.timeout ?? 3600000,\n maxAttempts: options.maxAttempts ?? 3,\n },\n { tenantJobCap: options.tenantJobCap },\n );\n}\n\nexport async function ensureReportRefreshSchedules(\n options: EnsureReportSchedulesOptions,\n): Promise<void> {\n await assertReportTablesReady(options.db, REPORT_SCHEDULER_TABLES);\n\n for (const reportCtor of options.reports) {\n const definition = await buildReportDefinition(reportCtor);\n const refresh = definition.refresh;\n if (!refresh || refresh.manual) continue;\n\n const reportClass = canonicalClassName(reportCtor);\n const targetTenants = refresh.tenantFanout\n ? options.tenantIds\n : [null as string | null];\n if (\n refresh.tenantFanout &&\n (!targetTenants || targetTenants.length === 0)\n ) {\n throw new Error(\n `${definition.reportClassName} refresh.tenantFanout requires tenantIds when creating schedules.`,\n );\n }\n\n const schedules = [\n refresh.schedule\n ? {\n cron: refresh.schedule,\n mode: refresh.mode ?? 'incremental',\n trigger: 'schedule' as const,\n }\n : null,\n refresh.fullRebuildSchedule\n ? {\n cron: refresh.fullRebuildSchedule,\n mode: 'rebuild' as const,\n trigger: 'schedule' as const,\n }\n : null,\n ].filter(Boolean) as Array<{\n cron: string;\n mode: ReportRefreshMode;\n trigger: ReportRefreshTrigger;\n }>;\n\n for (const schedule of schedules) {\n validateCronExpression(schedule.cron);\n for (const tenantId of targetTenants ?? []) {\n const scopeKey = scopeKeyForTenant(tenantId);\n const id = stableUuid([\n 'schedule',\n reportClass,\n scopeKey,\n schedule.cron,\n schedule.mode,\n ]);\n const now = new Date().toISOString();\n await options.db.upsert(\n REPORT_SCHEDULES_TABLE,\n ['report_class', 'scope_key', 'cron', 'mode'],\n {\n id,\n slug: id,\n context: scopeKey,\n tenant_id: tenantId,\n scope_key: scopeKey,\n report_class: reportClass,\n cron: schedule.cron,\n trigger: schedule.trigger,\n mode: schedule.mode,\n enabled: true,\n status: 'active',\n next_run: getNextCronDate(schedule.cron).toISOString(),\n last_run: null,\n last_status: null,\n last_error: null,\n run_count: 0,\n success_count: 0,\n failure_count: 0,\n running_count: 0,\n max_concurrent: 1,\n queue: options.queue ?? 'reports',\n priority: options.priority ?? 70,\n timeout: options.timeout ?? 3600000,\n created_at: now,\n updated_at: now,\n },\n );\n }\n }\n }\n}\n\nexport class ReportScheduleRunner extends EventEmitter {\n readonly id: string;\n private readonly config: Required<ReportScheduleRunnerConfig>;\n private db: DatabaseInterface | null = null;\n private running = false;\n private pollTimer: NodeJS.Timeout | null = null;\n\n constructor(config: ReportScheduleRunnerConfig = {}) {\n super();\n this.config = {\n id: config.id || `reports_${stableUuid([Date.now()]).slice(0, 8)}`,\n pollInterval: config.pollInterval ?? 60000,\n batchSize: config.batchSize ?? 50,\n };\n this.id = this.config.id;\n }\n\n async initialize(db: DatabaseInterface): Promise<void> {\n this.db = db;\n await assertReportTablesReady(db, REPORT_SCHEDULER_TABLES);\n }\n\n async start(): Promise<void> {\n if (this.running) return;\n if (!this.db) {\n throw new Error(\n 'ReportScheduleRunner not initialized. Call initialize() first.',\n );\n }\n this.running = true;\n this.startPolling();\n this.emit('runner:started');\n }\n\n async stop(): Promise<void> {\n if (!this.running) return;\n this.running = false;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.emit('runner:stopped');\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n async handleJobCompletion(\n scheduleId: string,\n success: boolean,\n errorMessage?: string,\n ): Promise<void> {\n if (!this.db) return;\n const now = new Date().toISOString();\n if (success) {\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = CASE WHEN COALESCE(running_count, 0) > 0 THEN running_count - 1 ELSE 0 END,\n last_run = ?,\n last_status = 'success',\n last_error = NULL,\n run_count = COALESCE(run_count, 0) + 1,\n success_count = COALESCE(success_count, 0) + 1,\n updated_at = ?\n WHERE id = ?`,\n now,\n now,\n scheduleId,\n );\n this.emit('schedule:completed', scheduleId);\n return;\n }\n\n const safeError = errorMessage ?? 'Unknown error';\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = CASE WHEN COALESCE(running_count, 0) > 0 THEN running_count - 1 ELSE 0 END,\n last_run = ?,\n last_status = 'failed',\n last_error = ?,\n run_count = COALESCE(run_count, 0) + 1,\n failure_count = COALESCE(failure_count, 0) + 1,\n updated_at = ?\n WHERE id = ?`,\n now,\n safeError,\n now,\n scheduleId,\n );\n this.emit('schedule:failed', scheduleId, safeError);\n }\n\n private startPolling(): void {\n const poll = async () => {\n if (!this.running) return;\n try {\n await this.poll();\n } catch (error) {\n this.emit('runner:error', error as Error);\n }\n if (this.running) {\n this.pollTimer = setTimeout(poll, this.config.pollInterval);\n if (typeof this.pollTimer.unref === 'function') {\n this.pollTimer.unref();\n }\n }\n };\n poll();\n }\n\n async poll(): Promise<void> {\n if (!this.db) return;\n const result = await this.db.query(\n `SELECT * FROM ${REPORT_SCHEDULES_TABLE}\n WHERE enabled = true\n AND status = 'active'\n AND next_run <= ?\n AND COALESCE(running_count, 0) < COALESCE(max_concurrent, 1)\n ORDER BY next_run ASC\n LIMIT ?`,\n new Date().toISOString(),\n this.config.batchSize,\n );\n\n for (const row of result.rows) {\n await this.triggerSchedule(row as ReportScheduleRow);\n }\n }\n\n private async triggerSchedule(row: ReportScheduleRow): Promise<void> {\n if (!this.db) return;\n const schedule: ReportScheduleInfo = {\n id: String(row.id),\n reportClass: String(row.report_class),\n tenantId:\n typeof row.tenant_id === 'string' && row.tenant_id.length > 0\n ? row.tenant_id\n : null,\n cron: String(row.cron),\n mode: (row.mode as ReportRefreshMode) || 'incremental',\n };\n\n try {\n const nextRun = getNextCronDate(schedule.cron);\n await enqueueReportRefresh({\n db: this.db,\n reportClass: schedule.reportClass,\n mode: schedule.mode,\n trigger: (row.trigger as ReportRefreshTrigger) || 'schedule',\n tenantId: schedule.tenantId,\n scheduleId: schedule.id,\n queue: String(row.queue || 'reports'),\n priority: Number(row.priority ?? 70),\n timeout: Number(row.timeout ?? 3600000),\n });\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = COALESCE(running_count, 0) + 1,\n next_run = ?,\n updated_at = ?\n WHERE id = ?`,\n nextRun.toISOString(),\n new Date().toISOString(),\n schedule.id,\n );\n this.emit('schedule:triggered', schedule);\n } catch (error) {\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET last_error = ?,\n updated_at = ?\n WHERE id = ?`,\n error instanceof Error ? error.message : String(error),\n new Date().toISOString(),\n schedule.id,\n );\n this.emit('schedule:error', schedule, error as Error);\n }\n }\n}\n\ninterface ReportScheduleRow {\n id: unknown;\n tenant_id: unknown;\n report_class: unknown;\n cron: unknown;\n trigger: unknown;\n mode: unknown;\n queue: unknown;\n priority: unknown;\n timeout: unknown;\n}\n\nexport function registerReportRefreshInterceptor(\n options: ReportRefreshInterceptorOptions,\n): () => boolean {\n const name = options.name ?? 'smrt-reports-refresh';\n GlobalInterceptors.register({\n name,\n priority: -10,\n async afterSave(instance, context) {\n await triggerReportsForInstance(options, instance, context);\n },\n async afterDelete(instance, context) {\n await triggerReportsForInstance(options, instance, context);\n },\n });\n return () => GlobalInterceptors.unregister(name);\n}\n\nasync function triggerReportsForInstance(\n options: ReportRefreshInterceptorOptions,\n instance: SmrtObject,\n context: InterceptorContext,\n): Promise<void> {\n for (const reportCtor of options.reports) {\n const definition = await buildReportDefinition(reportCtor);\n if (definition.refresh?.manual) continue;\n if (!sourceMatches(definition, instance, context)) continue;\n\n const mode = definition.refresh?.mode ?? 'incremental';\n const tenantId = tenantIdFromInstance(instance);\n const changedRows = [changedRowSnapshot(instance)];\n if (options.enqueue === false) {\n await refreshReport(reportCtor, {\n db: options.db,\n mode,\n trigger: 'change',\n tenantId,\n changedRows,\n });\n continue;\n }\n\n await enqueueReportRefresh({\n db: options.db,\n reportClass: canonicalClassName(reportCtor),\n mode,\n trigger: 'change',\n tenantId,\n queue: options.queue,\n priority: options.priority,\n timeout: options.timeout,\n tenantJobCap: options.tenantJobCap,\n changedRows,\n });\n }\n}\n"],"names":["tenantId"],"mappings":"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"}
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"generatedAt": "2026-06-26T14:50:49.071Z",
|
|
4
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
5
|
+
"packageVersion": "0.36.5",
|
|
6
|
+
"sourceManifestPath": "dist/manifest.json",
|
|
7
|
+
"agentDocPath": "AGENTS.md",
|
|
8
|
+
"sourceHashes": {
|
|
9
|
+
"manifest": "d900b3fec866546ff0fc51c734d1f2eed3745ee7824e2907f8bf92f60ec03d09",
|
|
10
|
+
"packageJson": "749901e81a0bdcc9c7916cdeda483bd4e94c0a4bac2e1de051c55e1fbaef5414",
|
|
11
|
+
"agents": "bf851be60baab2fd98149165885f70c4f4979123ff72bf8f4b43edfae4478e87"
|
|
12
|
+
},
|
|
13
|
+
"exports": [
|
|
14
|
+
".",
|
|
15
|
+
"./aggregate",
|
|
16
|
+
"./compiler",
|
|
17
|
+
"./decorators",
|
|
18
|
+
"./manifest",
|
|
19
|
+
"./manifest.json",
|
|
20
|
+
"./refresh",
|
|
21
|
+
"./scheduler",
|
|
22
|
+
"./state"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@happyvertical/smrt-core": "workspace:*",
|
|
26
|
+
"@happyvertical/smrt-jobs": "workspace:*",
|
|
27
|
+
"@happyvertical/smrt-tenancy": "workspace:*",
|
|
28
|
+
"@happyvertical/sql": "catalog:",
|
|
29
|
+
"@happyvertical/smrt-vitest": "workspace:*",
|
|
30
|
+
"@types/node": "25.0.9",
|
|
31
|
+
"typescript": "^5.9.3",
|
|
32
|
+
"vite": "^7.3.1",
|
|
33
|
+
"vitest": "^4.0.17"
|
|
34
|
+
},
|
|
35
|
+
"smrtDependencies": [
|
|
36
|
+
"@happyvertical/smrt-core",
|
|
37
|
+
"@happyvertical/smrt-jobs",
|
|
38
|
+
"@happyvertical/smrt-tenancy",
|
|
39
|
+
"@happyvertical/smrt-vitest"
|
|
40
|
+
],
|
|
41
|
+
"sdkDependencies": [
|
|
42
|
+
"@happyvertical/sql"
|
|
43
|
+
],
|
|
44
|
+
"tags": [],
|
|
45
|
+
"risks": [],
|
|
46
|
+
"objects": [
|
|
47
|
+
{
|
|
48
|
+
"name": "SmrtReport",
|
|
49
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReport",
|
|
50
|
+
"collection": "smrtreports",
|
|
51
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
52
|
+
"extends": "SmrtObject",
|
|
53
|
+
"fields": [
|
|
54
|
+
{
|
|
55
|
+
"name": "refreshedAt",
|
|
56
|
+
"type": "datetime",
|
|
57
|
+
"required": false
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"relationships": [],
|
|
61
|
+
"methods": [
|
|
62
|
+
"isStale",
|
|
63
|
+
"refresh"
|
|
64
|
+
],
|
|
65
|
+
"surfaces": [],
|
|
66
|
+
"relationshipFeatures": [],
|
|
67
|
+
"tags": [],
|
|
68
|
+
"risks": []
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "SmrtReportCollection",
|
|
72
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportCollection",
|
|
73
|
+
"collection": "smrtreports",
|
|
74
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
75
|
+
"extends": "SmrtCollection",
|
|
76
|
+
"fields": [],
|
|
77
|
+
"relationships": [],
|
|
78
|
+
"methods": [
|
|
79
|
+
"get",
|
|
80
|
+
"list",
|
|
81
|
+
"refresh"
|
|
82
|
+
],
|
|
83
|
+
"surfaces": [],
|
|
84
|
+
"relationshipFeatures": [],
|
|
85
|
+
"tags": [],
|
|
86
|
+
"risks": []
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "SmrtReportRefreshTask",
|
|
90
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportRefreshTask",
|
|
91
|
+
"collection": "smrtreportrefreshtasks",
|
|
92
|
+
"tableName": "_smrt_report_refresh_tasks",
|
|
93
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
94
|
+
"extends": "SmrtObject",
|
|
95
|
+
"fields": [
|
|
96
|
+
{
|
|
97
|
+
"name": "tenantId",
|
|
98
|
+
"type": "text",
|
|
99
|
+
"required": false,
|
|
100
|
+
"columnType": "UUID"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "reportClass",
|
|
104
|
+
"type": "text",
|
|
105
|
+
"required": true,
|
|
106
|
+
"columnType": "TEXT"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"name": "mode",
|
|
110
|
+
"type": "text",
|
|
111
|
+
"required": true,
|
|
112
|
+
"columnType": "TEXT"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "trigger",
|
|
116
|
+
"type": "text",
|
|
117
|
+
"required": true,
|
|
118
|
+
"columnType": "TEXT"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"name": "args",
|
|
122
|
+
"type": "json",
|
|
123
|
+
"required": false,
|
|
124
|
+
"columnType": "JSON"
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"relationships": [],
|
|
128
|
+
"methods": [
|
|
129
|
+
"run"
|
|
130
|
+
],
|
|
131
|
+
"surfaces": [],
|
|
132
|
+
"relationshipFeatures": [
|
|
133
|
+
"uuidColumns"
|
|
134
|
+
],
|
|
135
|
+
"tags": [],
|
|
136
|
+
"risks": []
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "SmrtReportRun",
|
|
140
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportRun",
|
|
141
|
+
"collection": "smrtreportruns",
|
|
142
|
+
"tableName": "_smrt_report_runs",
|
|
143
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
144
|
+
"extends": "SmrtObject",
|
|
145
|
+
"fields": [
|
|
146
|
+
{
|
|
147
|
+
"name": "tenantId",
|
|
148
|
+
"type": "text",
|
|
149
|
+
"required": false,
|
|
150
|
+
"columnType": "UUID"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "scopeKey",
|
|
154
|
+
"type": "text",
|
|
155
|
+
"required": true,
|
|
156
|
+
"columnType": "TEXT"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "reportClass",
|
|
160
|
+
"type": "text",
|
|
161
|
+
"required": true,
|
|
162
|
+
"columnType": "TEXT"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "sourceClass",
|
|
166
|
+
"type": "text",
|
|
167
|
+
"required": true,
|
|
168
|
+
"columnType": "TEXT"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "mode",
|
|
172
|
+
"type": "text",
|
|
173
|
+
"required": true,
|
|
174
|
+
"columnType": "TEXT"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"name": "trigger",
|
|
178
|
+
"type": "text",
|
|
179
|
+
"required": true,
|
|
180
|
+
"columnType": "TEXT"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"name": "status",
|
|
184
|
+
"type": "text",
|
|
185
|
+
"required": true,
|
|
186
|
+
"columnType": "TEXT"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "startedAt",
|
|
190
|
+
"type": "datetime",
|
|
191
|
+
"required": true,
|
|
192
|
+
"columnType": "TIMESTAMP"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"name": "completedAt",
|
|
196
|
+
"type": "datetime",
|
|
197
|
+
"required": false,
|
|
198
|
+
"columnType": "TIMESTAMP"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "rowCount",
|
|
202
|
+
"type": "integer",
|
|
203
|
+
"required": true,
|
|
204
|
+
"columnType": "INTEGER"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"name": "changedGroupCount",
|
|
208
|
+
"type": "integer",
|
|
209
|
+
"required": true,
|
|
210
|
+
"columnType": "INTEGER"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "watermarkBefore",
|
|
214
|
+
"type": "text",
|
|
215
|
+
"required": false,
|
|
216
|
+
"columnType": "TEXT"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"name": "watermarkAfter",
|
|
220
|
+
"type": "text",
|
|
221
|
+
"required": false,
|
|
222
|
+
"columnType": "TEXT"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"name": "error",
|
|
226
|
+
"type": "text",
|
|
227
|
+
"required": false,
|
|
228
|
+
"columnType": "TEXT"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"name": "metadata",
|
|
232
|
+
"type": "json",
|
|
233
|
+
"required": false,
|
|
234
|
+
"columnType": "JSON"
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
"relationships": [],
|
|
238
|
+
"methods": [],
|
|
239
|
+
"surfaces": [],
|
|
240
|
+
"relationshipFeatures": [
|
|
241
|
+
"uuidColumns"
|
|
242
|
+
],
|
|
243
|
+
"tags": [],
|
|
244
|
+
"risks": []
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"name": "SmrtReportRunCollection",
|
|
248
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportRunCollection",
|
|
249
|
+
"collection": "smrtreportruns",
|
|
250
|
+
"tableName": "_smrt_report_runs",
|
|
251
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
252
|
+
"extends": "SmrtCollection",
|
|
253
|
+
"fields": [],
|
|
254
|
+
"relationships": [],
|
|
255
|
+
"methods": [],
|
|
256
|
+
"surfaces": [],
|
|
257
|
+
"relationshipFeatures": [
|
|
258
|
+
"uuidColumns"
|
|
259
|
+
],
|
|
260
|
+
"tags": [],
|
|
261
|
+
"risks": []
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"name": "SmrtReportWatermark",
|
|
265
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportWatermark",
|
|
266
|
+
"collection": "smrtreportwatermarks",
|
|
267
|
+
"tableName": "_smrt_report_watermarks",
|
|
268
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
269
|
+
"extends": "SmrtObject",
|
|
270
|
+
"fields": [
|
|
271
|
+
{
|
|
272
|
+
"name": "tenantId",
|
|
273
|
+
"type": "text",
|
|
274
|
+
"required": false,
|
|
275
|
+
"columnType": "UUID"
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"name": "scopeKey",
|
|
279
|
+
"type": "text",
|
|
280
|
+
"required": true,
|
|
281
|
+
"columnType": "TEXT"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"name": "reportClass",
|
|
285
|
+
"type": "text",
|
|
286
|
+
"required": true,
|
|
287
|
+
"columnType": "TEXT"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"name": "sourceClass",
|
|
291
|
+
"type": "text",
|
|
292
|
+
"required": true,
|
|
293
|
+
"columnType": "TEXT"
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"name": "watermarkColumn",
|
|
297
|
+
"type": "text",
|
|
298
|
+
"required": true,
|
|
299
|
+
"columnType": "TEXT"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"name": "watermarkValue",
|
|
303
|
+
"type": "text",
|
|
304
|
+
"required": false,
|
|
305
|
+
"columnType": "TEXT"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"name": "lastRunId",
|
|
309
|
+
"type": "text",
|
|
310
|
+
"required": false,
|
|
311
|
+
"columnType": "TEXT"
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"relationships": [],
|
|
315
|
+
"methods": [],
|
|
316
|
+
"surfaces": [],
|
|
317
|
+
"relationshipFeatures": [
|
|
318
|
+
"uuidColumns"
|
|
319
|
+
],
|
|
320
|
+
"tags": [],
|
|
321
|
+
"risks": []
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"name": "SmrtReportWatermarkCollection",
|
|
325
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportWatermarkCollection",
|
|
326
|
+
"collection": "smrtreportwatermarks",
|
|
327
|
+
"tableName": "_smrt_report_watermarks",
|
|
328
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
329
|
+
"extends": "SmrtCollection",
|
|
330
|
+
"fields": [],
|
|
331
|
+
"relationships": [],
|
|
332
|
+
"methods": [],
|
|
333
|
+
"surfaces": [],
|
|
334
|
+
"relationshipFeatures": [
|
|
335
|
+
"uuidColumns"
|
|
336
|
+
],
|
|
337
|
+
"tags": [],
|
|
338
|
+
"risks": []
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"name": "SmrtReportLock",
|
|
342
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportLock",
|
|
343
|
+
"collection": "smrtreportlocks",
|
|
344
|
+
"tableName": "_smrt_report_locks",
|
|
345
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
346
|
+
"extends": "SmrtObject",
|
|
347
|
+
"fields": [
|
|
348
|
+
{
|
|
349
|
+
"name": "tenantId",
|
|
350
|
+
"type": "text",
|
|
351
|
+
"required": false,
|
|
352
|
+
"columnType": "UUID"
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
"name": "scopeKey",
|
|
356
|
+
"type": "text",
|
|
357
|
+
"required": true,
|
|
358
|
+
"columnType": "TEXT"
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"name": "reportClass",
|
|
362
|
+
"type": "text",
|
|
363
|
+
"required": true,
|
|
364
|
+
"columnType": "TEXT"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"name": "ownerId",
|
|
368
|
+
"type": "text",
|
|
369
|
+
"required": false,
|
|
370
|
+
"columnType": "TEXT"
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"name": "acquiredAt",
|
|
374
|
+
"type": "datetime",
|
|
375
|
+
"required": false,
|
|
376
|
+
"columnType": "TIMESTAMP"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"name": "heartbeatAt",
|
|
380
|
+
"type": "datetime",
|
|
381
|
+
"required": false,
|
|
382
|
+
"columnType": "TIMESTAMP"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"name": "expiresAt",
|
|
386
|
+
"type": "datetime",
|
|
387
|
+
"required": false,
|
|
388
|
+
"columnType": "TIMESTAMP"
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
"relationships": [],
|
|
392
|
+
"methods": [],
|
|
393
|
+
"surfaces": [],
|
|
394
|
+
"relationshipFeatures": [
|
|
395
|
+
"uuidColumns"
|
|
396
|
+
],
|
|
397
|
+
"tags": [],
|
|
398
|
+
"risks": []
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
"name": "SmrtReportLockCollection",
|
|
402
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportLockCollection",
|
|
403
|
+
"collection": "smrtreportlocks",
|
|
404
|
+
"tableName": "_smrt_report_locks",
|
|
405
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
406
|
+
"extends": "SmrtCollection",
|
|
407
|
+
"fields": [],
|
|
408
|
+
"relationships": [],
|
|
409
|
+
"methods": [],
|
|
410
|
+
"surfaces": [],
|
|
411
|
+
"relationshipFeatures": [
|
|
412
|
+
"uuidColumns"
|
|
413
|
+
],
|
|
414
|
+
"tags": [],
|
|
415
|
+
"risks": []
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
"name": "SmrtReportSchedule",
|
|
419
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportSchedule",
|
|
420
|
+
"collection": "smrtreportschedules",
|
|
421
|
+
"tableName": "_smrt_report_schedules",
|
|
422
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
423
|
+
"extends": "SmrtObject",
|
|
424
|
+
"fields": [
|
|
425
|
+
{
|
|
426
|
+
"name": "tenantId",
|
|
427
|
+
"type": "text",
|
|
428
|
+
"required": false,
|
|
429
|
+
"columnType": "UUID"
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"name": "scopeKey",
|
|
433
|
+
"type": "text",
|
|
434
|
+
"required": true,
|
|
435
|
+
"columnType": "TEXT"
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
"name": "reportClass",
|
|
439
|
+
"type": "text",
|
|
440
|
+
"required": true,
|
|
441
|
+
"columnType": "TEXT"
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"name": "cron",
|
|
445
|
+
"type": "text",
|
|
446
|
+
"required": true,
|
|
447
|
+
"columnType": "TEXT"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
"name": "trigger",
|
|
451
|
+
"type": "text",
|
|
452
|
+
"required": true,
|
|
453
|
+
"columnType": "TEXT"
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
"name": "mode",
|
|
457
|
+
"type": "text",
|
|
458
|
+
"required": true,
|
|
459
|
+
"columnType": "TEXT"
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
"name": "enabled",
|
|
463
|
+
"type": "boolean",
|
|
464
|
+
"required": true,
|
|
465
|
+
"columnType": "BOOLEAN"
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
"name": "status",
|
|
469
|
+
"type": "text",
|
|
470
|
+
"required": true,
|
|
471
|
+
"columnType": "TEXT"
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"name": "nextRun",
|
|
475
|
+
"type": "datetime",
|
|
476
|
+
"required": false,
|
|
477
|
+
"columnType": "TIMESTAMP"
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
"name": "lastRun",
|
|
481
|
+
"type": "datetime",
|
|
482
|
+
"required": false,
|
|
483
|
+
"columnType": "TIMESTAMP"
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
"name": "lastStatus",
|
|
487
|
+
"type": "text",
|
|
488
|
+
"required": false,
|
|
489
|
+
"columnType": "TEXT"
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
"name": "lastError",
|
|
493
|
+
"type": "text",
|
|
494
|
+
"required": false,
|
|
495
|
+
"columnType": "TEXT"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"name": "runCount",
|
|
499
|
+
"type": "integer",
|
|
500
|
+
"required": true,
|
|
501
|
+
"columnType": "INTEGER"
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
"name": "successCount",
|
|
505
|
+
"type": "integer",
|
|
506
|
+
"required": true,
|
|
507
|
+
"columnType": "INTEGER"
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"name": "failureCount",
|
|
511
|
+
"type": "integer",
|
|
512
|
+
"required": true,
|
|
513
|
+
"columnType": "INTEGER"
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
"name": "runningCount",
|
|
517
|
+
"type": "integer",
|
|
518
|
+
"required": true,
|
|
519
|
+
"columnType": "INTEGER"
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
"name": "maxConcurrent",
|
|
523
|
+
"type": "integer",
|
|
524
|
+
"required": true,
|
|
525
|
+
"columnType": "INTEGER"
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
"name": "queue",
|
|
529
|
+
"type": "text",
|
|
530
|
+
"required": true,
|
|
531
|
+
"columnType": "TEXT"
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
"name": "priority",
|
|
535
|
+
"type": "integer",
|
|
536
|
+
"required": true,
|
|
537
|
+
"columnType": "INTEGER"
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
"name": "timeout",
|
|
541
|
+
"type": "integer",
|
|
542
|
+
"required": true,
|
|
543
|
+
"columnType": "INTEGER"
|
|
544
|
+
}
|
|
545
|
+
],
|
|
546
|
+
"relationships": [],
|
|
547
|
+
"methods": [],
|
|
548
|
+
"surfaces": [],
|
|
549
|
+
"relationshipFeatures": [
|
|
550
|
+
"uuidColumns"
|
|
551
|
+
],
|
|
552
|
+
"tags": [],
|
|
553
|
+
"risks": []
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
"name": "SmrtReportScheduleCollection",
|
|
557
|
+
"qualifiedName": "@happyvertical/smrt-reports:SmrtReportScheduleCollection",
|
|
558
|
+
"collection": "smrtreportschedules",
|
|
559
|
+
"tableName": "_smrt_report_schedules",
|
|
560
|
+
"packageName": "@happyvertical/smrt-reports",
|
|
561
|
+
"extends": "SmrtCollection",
|
|
562
|
+
"fields": [],
|
|
563
|
+
"relationships": [],
|
|
564
|
+
"methods": [],
|
|
565
|
+
"surfaces": [],
|
|
566
|
+
"relationshipFeatures": [
|
|
567
|
+
"uuidColumns"
|
|
568
|
+
],
|
|
569
|
+
"tags": [],
|
|
570
|
+
"risks": []
|
|
571
|
+
}
|
|
572
|
+
],
|
|
573
|
+
"surfaces": [],
|
|
574
|
+
"prompts": [],
|
|
575
|
+
"relationshipsV2": {
|
|
576
|
+
"foreignKeyFields": 0,
|
|
577
|
+
"crossPackageRefFields": 0,
|
|
578
|
+
"junctionCollections": 0,
|
|
579
|
+
"hierarchicalObjects": 0,
|
|
580
|
+
"polymorphicAssociations": 0,
|
|
581
|
+
"uuidColumns": 14
|
|
582
|
+
},
|
|
583
|
+
"agentDoc": "# @happyvertical/smrt-reports\n\nMaterialized aggregate report models for SMRT.\n\n## Key Pieces\n\n| Module | Purpose |\n| --- | --- |\n| `SmrtReport` | Abstract report row base with `refreshedAt`, `isStale()`, and manual `refresh()` |\n| decorators | `@report`, grouping decorators, time buckets, and aggregate measure decorators |\n| compiler | Pure `ReportDefinition -> AggregateSpec` compiler and ObjectRegistry adapter |\n| aggregate | Compatibility re-export of the SDK aggregate query builder |\n| refresh | Rebuild and incremental refresh engine with run tracking, watermarks, locks, and tenant scoping |\n| state | Internal `_smrt_report_*` system models for runs, watermarks, locks, schedules, and refresh tasks |\n| scheduler | Cron schedule runner, durable refresh job enqueueing, and `onChange` interceptor registration |\n\n## Conventions\n\n- Report cache tables are normal `@smrt()` tables. Runtime refresh must not create schema.\n- Store report metadata under field `_meta.__report`; scanner and runtime decorators must stay aligned.\n- Keep SQL generation portable. Use the SDK `buildAggregate()`/`bucketExpr()` helpers for time buckets and `$N` placeholders.\n- Do not add a local aggregate SQL builder here; the implementation lives in `@happyvertical/sql`.\n- Refresh runtime tables are schema-managed. Runtime refresh must fail clearly when `_smrt_report_runs`, `_smrt_report_watermarks`, or `_smrt_report_locks` have not been migrated.\n- Incremental refresh requires a source watermark column (default `updatedAt`) and soft-delete column (default `deletedAt`); it recomputes affected groups and deletes empty report groups instead of applying aggregate deltas.\n- Raw aggregate refreshes must explicitly filter `tenant_id`; the tenancy interceptor only protects normal collection reads.\n- Scheduled/on-change refreshes enqueue `SmrtReportRefreshTask.run()` through `@happyvertical/smrt-jobs`; do not add a separate report queue.\n"
|
|
584
|
+
}
|