@happyvertical/smrt-reports 0.43.1 → 0.43.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/AGENTS.md +6 -3
- package/README.md +8 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +3 -3
- package/dist/smrt-knowledge.json +7 -7
- package/package.json +6 -6
package/AGENTS.md
CHANGED
|
@@ -36,9 +36,12 @@ Materialized aggregate report models for SMRT.
|
|
|
36
36
|
do not become public columns when no principal is available.
|
|
37
37
|
- `tenantScoped`/`tenantField` reflect actual registered tenant metadata. A
|
|
38
38
|
`tenantScope` option only contributes to the stable resource id; it is not
|
|
39
|
-
authorization.
|
|
40
|
-
`
|
|
41
|
-
|
|
39
|
+
authorization. Pass the same `adapter` options to
|
|
40
|
+
`queryReportMaterializedRows()` that were used to build the selected
|
|
41
|
+
descriptor, so results and background tasks retain that stable resource id.
|
|
42
|
+
The default query path resolves the registered collection via `ObjectRegistry`,
|
|
43
|
+
so normal collection tenancy interceptors apply. An injected collection is
|
|
44
|
+
application-owned and must preserve the same boundary.
|
|
42
45
|
- `refresh` is a declaration, not execution. It describes configured mode,
|
|
43
46
|
triggers, positive-TTL stale-read behavior, and a permissioned/audited action
|
|
44
47
|
with preview/apply phases. The adapter remains read-only, while
|
package/README.md
CHANGED
|
@@ -145,7 +145,11 @@ const result = await queryReportMaterializedRows(
|
|
|
145
145
|
page: { kind: 'offset', offset: 0, limit: 25 },
|
|
146
146
|
sort: [{ field: 'revenue', direction: 'desc' }],
|
|
147
147
|
},
|
|
148
|
-
{
|
|
148
|
+
{
|
|
149
|
+
collection: reports,
|
|
150
|
+
// Preserve the resource identity of the descriptor selected above.
|
|
151
|
+
adapter: { tenantScope: 'current' },
|
|
152
|
+
},
|
|
149
153
|
);
|
|
150
154
|
const rowKey = reportMaterializedRowKey(result.rows[0]);
|
|
151
155
|
const drilldown = await buildReportDrilldownQuery(MonthlyRevenue, result.rows[0]);
|
|
@@ -192,6 +196,9 @@ and `background` delegates a normalized, authority-free task to the application'
|
|
|
192
196
|
materialized rows. The host retains the authenticated principal, tenant, report
|
|
193
197
|
definition, field policy, database, and eventual job execution; none can be
|
|
194
198
|
supplied in a query request or background task.
|
|
199
|
+
When a consumer selects a descriptor with `ReportAdapterOptions`, pass those
|
|
200
|
+
same options through the query's `adapter` option so returned and background
|
|
201
|
+
task resource ids stay aligned with the selected descriptor.
|
|
195
202
|
`buildReportDrilldownQuery()` carries only the row's declared groups/buckets
|
|
196
203
|
and a fixed inheritance contract for the current principal, tenant, report
|
|
197
204
|
definition, and field policy; an authenticated source adapter must enforce that
|
package/dist/index.d.ts
CHANGED
|
@@ -16,8 +16,12 @@ import { DataQuerySort } from '@happyvertical/smrt-types';
|
|
|
16
16
|
import { EventEmitter } from 'node:events';
|
|
17
17
|
import { SmrtCollection } from '@happyvertical/smrt-core';
|
|
18
18
|
import { SmrtJob } from '@happyvertical/smrt-jobs';
|
|
19
|
+
import { SmrtListOptions } from '@happyvertical/smrt-core';
|
|
19
20
|
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
20
21
|
import { SmrtObjectOptions } from '@happyvertical/smrt-core';
|
|
22
|
+
import { SmrtSelectedRow } from '@happyvertical/smrt-core';
|
|
23
|
+
import { SmrtSelectField } from '@happyvertical/smrt-core';
|
|
24
|
+
import { SmrtStiReadScope } from '@happyvertical/smrt-core';
|
|
21
25
|
import { SqlAdapterType } from '@happyvertical/sql';
|
|
22
26
|
import { WhereClause } from '@happyvertical/sql';
|
|
23
27
|
|
|
@@ -757,6 +761,11 @@ declare interface ReportQueryCommonOptions {
|
|
|
757
761
|
}>>;
|
|
758
762
|
};
|
|
759
763
|
db?: DatabaseInterface;
|
|
764
|
+
/**
|
|
765
|
+
* Consumer-owned descriptor preferences. They contribute to the stable
|
|
766
|
+
* resource identity and must match the catalog used to select this report.
|
|
767
|
+
*/
|
|
768
|
+
adapter?: ReportAdapterOptions;
|
|
760
769
|
/**
|
|
761
770
|
* Opt in to a tenant-safe lifecycle snapshot alongside this materialized
|
|
762
771
|
* read. It never makes a generic read mutate.
|
|
@@ -1053,7 +1062,29 @@ export declare class SmrtReport extends SmrtObject {
|
|
|
1053
1062
|
export declare class SmrtReportCollection<ModelType extends SmrtReport> extends SmrtCollection<ModelType> {
|
|
1054
1063
|
private refreshIfStale;
|
|
1055
1064
|
refresh(options?: Omit<ReportRefreshOptions, 'db'>): Promise<ReportRefreshResult>;
|
|
1056
|
-
list
|
|
1065
|
+
list<const Select extends readonly SmrtSelectField<ModelType>[]>(options: Omit<SmrtListOptions<ModelType>, 'select' | 'stiScope'> & {
|
|
1066
|
+
select: Select;
|
|
1067
|
+
include?: never;
|
|
1068
|
+
stiScope: SmrtStiReadScope;
|
|
1069
|
+
}): Promise<Record<string, unknown>[]>;
|
|
1070
|
+
list<const Select extends readonly SmrtSelectField<ModelType>[]>(options: Omit<SmrtListOptions<ModelType>, 'select' | 'stiScope'> & {
|
|
1071
|
+
select: Select;
|
|
1072
|
+
include?: never;
|
|
1073
|
+
stiScope?: undefined;
|
|
1074
|
+
}): Promise<SmrtSelectedRow<ModelType, Select>[]>;
|
|
1075
|
+
list(options: Omit<SmrtListOptions<ModelType>, 'select' | 'stiScope'> & {
|
|
1076
|
+
select?: undefined;
|
|
1077
|
+
stiScope: SmrtStiReadScope;
|
|
1078
|
+
}): Promise<SmrtObject[]>;
|
|
1079
|
+
list(options?: undefined): Promise<ModelType[]>;
|
|
1080
|
+
list(options: Omit<SmrtListOptions<ModelType>, 'select' | 'stiScope'> & {
|
|
1081
|
+
select?: undefined;
|
|
1082
|
+
stiScope?: undefined;
|
|
1083
|
+
}): Promise<ModelType[]>;
|
|
1084
|
+
list(options: (Omit<SmrtListOptions<ModelType>, 'select'> & {
|
|
1085
|
+
select?: undefined;
|
|
1086
|
+
}) | undefined): Promise<SmrtObject[]>;
|
|
1087
|
+
list(options: SmrtListOptions<ModelType> | undefined): Promise<SmrtObject[] | Record<string, unknown>[]>;
|
|
1057
1088
|
get(filter: Parameters<SmrtCollection<ModelType>['get']>[0], options?: Parameters<SmrtCollection<ModelType>['get']>[1]): Promise<ModelType | null>;
|
|
1058
1089
|
}
|
|
1059
1090
|
|
package/dist/index.js
CHANGED
|
@@ -776,7 +776,7 @@ function queryFreshness(lifecycle) {
|
|
|
776
776
|
}
|
|
777
777
|
}
|
|
778
778
|
async function queryReportMaterializedRows(reportCtor, input, options = {}) {
|
|
779
|
-
const descriptor = await buildReportAdapterDescriptor(reportCtor);
|
|
779
|
+
const descriptor = await buildReportAdapterDescriptor(reportCtor, options.adapter);
|
|
780
780
|
const request = normalizeDataQueryRequest(input, descriptor.schema);
|
|
781
781
|
splitReportFilterScopes(descriptor, request.filter);
|
|
782
782
|
const queryFingerprint = createDataQueryFingerprint(request, descriptor.schema);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["suffix","children","filter"],"sources":["../src/lifecycle.ts","../src/adapter.ts","../src/report.ts","../src/views.ts"],"sourcesContent":["import { ObjectRegistry, type SmrtObject } from '@happyvertical/smrt-core';\nimport { getTenantId, withSystemContext } from '@happyvertical/smrt-tenancy';\nimport {\n type DatabaseInterface,\n tableExists,\n validateColumnName,\n} from '@happyvertical/sql';\nimport type { ReportRefreshActionDescriptor } from './adapter.js';\nimport { buildReportDefinition } from './compiler.js';\nimport { enqueueReportRefresh } from './scheduler.js';\nimport {\n assertReportTablesReady,\n REPORT_LOCKS_TABLE,\n REPORT_RUNS_TABLE,\n scopeKeyForTenant,\n} from './state.js';\nimport type {\n ReportRefreshMode,\n ReportRefreshResult,\n ReportRefreshTrigger,\n} from './types.js';\n\ntype ReportCtor = new (...args: any[]) => SmrtObject;\n\nfunction registeredReport(reportCtor: ReportCtor) {\n return (\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name)\n );\n}\n\nexport type ReportLifecycleState =\n | 'current'\n | 'stale'\n | 'refreshing'\n | 'lock-skipped'\n | 'failed';\n\nexport interface ReportLifecycleRun {\n id: string;\n status: 'running' | 'success' | 'failed' | 'skipped';\n mode: ReportRefreshMode;\n trigger: ReportRefreshTrigger;\n startedAt?: string;\n completedAt?: string;\n rowCount: number;\n changedGroupCount: number;\n /** A failed run is safe to retry through the separately permissioned action. */\n mayRetry: boolean;\n}\n\n/**\n * Transport-neutral report lifecycle state. It deliberately never includes a\n * lock owner, a raw failure message, or tenant fanout identifiers.\n */\nexport interface ReportLifecycleSnapshot {\n version: 1;\n state: ReportLifecycleState;\n asOf?: string;\n refreshedAt?: string;\n /** Whether existing materialized rows can remain visible while work changes state. */\n hasUsableRows: boolean;\n mode: ReportRefreshMode;\n run?: ReportLifecycleRun;\n /** A stable, redacted failure signal; raw executor errors stay server-side. */\n failure?: {\n code: 'refresh_failed';\n retryable: true;\n };\n lock: {\n held: boolean;\n expiresAt?: string;\n };\n}\n\nexport interface ReportLifecycleOptions {\n db: DatabaseInterface;\n /** Test seam; production callers use the current clock. */\n now?: Date;\n}\n\nexport interface ReportRefreshJobHandle {\n jobId: string;\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';\n attempts: number;\n maxAttempts: number;\n /** Bounded polling guidance; this API does not create a second queue. */\n pollAfterMs: number;\n}\n\nexport interface ReportRefreshActionContext {\n phase: 'preview' | 'apply';\n reportClassName: string;\n /** The action always applies to the caller's already-bound tenant scope. */\n tenantScope: 'ambient';\n mode: ReportRefreshMode;\n requiredPermission: string;\n}\n\n/** Authority and audit stay with the application action host, not reports. */\nexport interface ReportRefreshActionHost {\n authorize(context: ReportRefreshActionContext): Promise<void> | void;\n audit(context: ReportRefreshActionContext): Promise<void> | void;\n}\n\nexport interface PreviewReportRefreshOptions extends ReportLifecycleOptions {\n host: ReportRefreshActionHost;\n mode?: ReportRefreshMode;\n /**\n * Pass `descriptor.refresh.action` when the adapter declares a custom\n * refresh permission, so the displayed and enforced action stay aligned.\n */\n refreshAction?: Pick<ReportRefreshActionDescriptor, 'requiredPermission'>;\n}\n\nexport interface ReportRefreshPreview {\n phase: 'preview';\n lifecycle: ReportLifecycleSnapshot;\n action: ReportRefreshActionContext;\n execution: 'background';\n}\n\nexport interface ApplyReportRefreshOptions extends PreviewReportRefreshOptions {\n queue?: string;\n priority?: number;\n timeout?: number;\n maxAttempts?: number;\n tenantJobCap?: number;\n}\n\nexport interface AppliedReportRefresh {\n phase: 'apply';\n job: ReportRefreshJobHandle;\n}\n\nexport interface ReportRefreshOutcome {\n state: 'current' | 'lock-skipped' | 'partial';\n rowCount: number;\n changedGroupCount: number;\n completedScopes: number;\n lockSkippedScopes: number;\n mode: ReportRefreshMode;\n refreshedAt: string;\n runId?: string;\n}\n\nfunction canonicalClassName(reportCtor: ReportCtor): string {\n const registered = registeredReport(reportCtor);\n return registered?.qualifiedName ?? registered?.name ?? reportCtor.name;\n}\n\nfunction reportTableName(reportCtor: ReportCtor): string {\n const reportClassName = canonicalClassName(reportCtor);\n const tableName = ObjectRegistry.getTableName(reportClassName);\n if (!tableName) {\n throw new Error(`No report table registered for ${reportCtor.name}`);\n }\n return validateColumnName(tableName);\n}\n\nasync function reportColumnName(\n reportClassName: string,\n fieldName: string,\n): Promise<string> {\n const fields = await ObjectRegistry.getAllFields(reportClassName);\n const direct = fields.get(fieldName) as\n | { columnName?: string; _meta?: { columnName?: string } }\n | undefined;\n return validateColumnName(\n direct?.columnName ??\n direct?._meta?.columnName ??\n fieldName.replace(/([A-Z])/g, '_$1').toLowerCase(),\n );\n}\n\nfunction toIso(value: unknown): string | undefined {\n if (!value) return undefined;\n const date = value instanceof Date ? value : new Date(String(value));\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString();\n}\n\nfunction latestIso(\n first: string | undefined,\n second: string | undefined,\n): string | undefined {\n if (!first) return second;\n if (!second) return first;\n return new Date(first).getTime() >= new Date(second).getTime()\n ? first\n : second;\n}\n\nfunction lifecycleTenantId(reportCtor: ReportCtor): string | null {\n return registeredReport(reportCtor)?.tenantScopedConfig\n ? (getTenantId() ?? null)\n : null;\n}\n\nfunction numberValue(value: unknown): number {\n const numeric = Number(value ?? 0);\n return Number.isFinite(numeric) ? numeric : 0;\n}\n\nfunction rowWhere(\n reportClassName: string,\n scopeKey: string,\n tenantId: string | null,\n): { sql: string; values: unknown[] } {\n if (tenantId) {\n return {\n sql: 'report_class = ? AND scope_key = ? AND tenant_id = ?',\n values: [reportClassName, scopeKey, tenantId],\n };\n }\n return {\n sql: 'report_class = ? AND scope_key = ? AND tenant_id IS NULL',\n values: [reportClassName, scopeKey],\n };\n}\n\nfunction runFromRow(\n row: Record<string, unknown> | undefined,\n): ReportLifecycleRun | undefined {\n if (!row || typeof row.id !== 'string') return undefined;\n const status = row.status;\n if (\n status !== 'running' &&\n status !== 'success' &&\n status !== 'failed' &&\n status !== 'skipped'\n ) {\n return undefined;\n }\n const mode: ReportRefreshMode =\n row.mode === 'incremental' ? 'incremental' : 'rebuild';\n const trigger: ReportRefreshTrigger =\n row.trigger === 'schedule' ||\n row.trigger === 'change' ||\n row.trigger === 'ttl' ||\n row.trigger === 'job'\n ? row.trigger\n : 'manual';\n return {\n id: row.id,\n status,\n mode,\n trigger,\n ...(toIso(row.started_at) ? { startedAt: toIso(row.started_at) } : {}),\n ...(toIso(row.completed_at)\n ? { completedAt: toIso(row.completed_at) }\n : {}),\n rowCount: numberValue(row.row_count),\n changedGroupCount: numberValue(row.changed_group_count),\n mayRetry: status === 'failed',\n };\n}\n\nfunction jobHandle(job: {\n id?: unknown;\n status: ReportRefreshJobHandle['status'];\n attempts: number;\n maxAttempts: number;\n}): ReportRefreshJobHandle {\n if (typeof job.id !== 'string' || job.id.length === 0) {\n throw new Error('Queued report refresh is missing its job id');\n }\n return {\n jobId: job.id,\n status: job.status,\n attempts: job.attempts,\n maxAttempts: job.maxAttempts,\n pollAfterMs: 1_000,\n };\n}\n\n/**\n * Read only the ambient tenant's report state. The caller cannot supply a\n * tenant selector, which prevents lifecycle lookup from becoming a tenant\n * enumeration surface.\n */\nexport async function getReportLifecycle(\n reportCtor: ReportCtor,\n options: ReportLifecycleOptions,\n): Promise<ReportLifecycleSnapshot> {\n await assertReportTablesReady(options.db, [\n REPORT_RUNS_TABLE,\n REPORT_LOCKS_TABLE,\n ]);\n\n const definition = await buildReportDefinition(reportCtor);\n const reportClassName = canonicalClassName(reportCtor);\n const tenantId = lifecycleTenantId(reportCtor);\n const scopeKey = scopeKeyForTenant(tenantId);\n const where = rowWhere(reportClassName, scopeKey, tenantId);\n const now = options.now ?? new Date();\n\n const [runResult, lockResult] = await Promise.all([\n options.db.query(\n `SELECT id, mode, trigger, status, started_at, completed_at, row_count, changed_group_count\n FROM ${REPORT_RUNS_TABLE}\n WHERE ${where.sql}\n ORDER BY started_at DESC, created_at DESC, id DESC\n LIMIT 1`,\n ...where.values,\n ),\n options.db.query(\n `SELECT expires_at\n FROM ${REPORT_LOCKS_TABLE}\n WHERE ${where.sql} AND expires_at > ?\n LIMIT 1`,\n ...where.values,\n now.toISOString(),\n ),\n ]);\n\n const tableName = reportTableName(reportCtor);\n if (!(await tableExists(options.db, tableName))) {\n throw new Error(\n `Report table '${tableName}' does not exist for ${reportClassName}`,\n );\n }\n const refreshedAtColumn = await reportColumnName(\n reportClassName,\n 'refreshedAt',\n );\n const registered = registeredReport(reportCtor);\n const configuredTenantField = registered?.tenantScopedConfig?.field;\n const tenantColumn = configuredTenantField\n ? await reportColumnName(reportClassName, configuredTenantField)\n : undefined;\n const materialized = tenantColumn\n ? await options.db.query(\n `SELECT MAX(${refreshedAtColumn}) AS refreshed_at FROM ${tableName} WHERE ${tenantColumn} ${tenantId ? '= ?' : 'IS NULL'}`,\n ...(tenantId ? [tenantId] : []),\n )\n : await options.db.query(\n `SELECT MAX(${refreshedAtColumn}) AS refreshed_at FROM ${tableName}`,\n );\n\n const run = runFromRow(\n runResult.rows[0] as Record<string, unknown> | undefined,\n );\n const refreshedAt = toIso(materialized.rows[0]?.refreshed_at);\n const completedAt = run?.status === 'success' ? run.completedAt : undefined;\n const asOf = latestIso(refreshedAt, completedAt);\n const ttlMs = definition.refresh?.ttl;\n const isStale =\n !asOf ||\n (ttlMs !== undefined && now.getTime() - new Date(asOf).getTime() > ttlMs);\n const lockExpiresAt = toIso(lockResult.rows[0]?.expires_at);\n const lockHeld = Boolean(lockExpiresAt);\n const state: ReportLifecycleState =\n run?.status === 'skipped'\n ? 'lock-skipped'\n : lockHeld\n ? 'refreshing'\n : run?.status === 'failed'\n ? 'failed'\n : run?.status === 'running'\n ? 'stale'\n : isStale\n ? 'stale'\n : 'current';\n\n return {\n version: 1,\n state,\n ...(asOf ? { asOf } : {}),\n ...(refreshedAt ? { refreshedAt } : {}),\n hasUsableRows: Boolean(refreshedAt),\n mode: run?.mode ?? definition.refresh?.mode ?? 'rebuild',\n ...(run ? { run } : {}),\n ...(run?.status === 'failed'\n ? { failure: { code: 'refresh_failed' as const, retryable: true } }\n : {}),\n lock: {\n held: lockHeld,\n ...(lockExpiresAt ? { expiresAt: lockExpiresAt } : {}),\n },\n };\n}\n\nfunction actionContext(\n reportCtor: ReportCtor,\n phase: ReportRefreshActionContext['phase'],\n mode: ReportRefreshMode,\n refreshAction:\n | Pick<ReportRefreshActionDescriptor, 'requiredPermission'>\n | undefined,\n): ReportRefreshActionContext {\n return {\n phase,\n reportClassName: canonicalClassName(reportCtor),\n tenantScope: 'ambient',\n mode,\n requiredPermission:\n refreshAction?.requiredPermission &&\n refreshAction.requiredPermission.trim().length > 0\n ? refreshAction.requiredPermission\n : 'reports.refresh',\n };\n}\n\nasync function actionMode(\n reportCtor: ReportCtor,\n requested: ReportRefreshMode | undefined,\n): Promise<ReportRefreshMode> {\n if (requested) return requested;\n const definition = await buildReportDefinition(reportCtor);\n return definition.refresh?.mode ?? 'rebuild';\n}\n\n/** Preview only declares a separately authorized and audited refresh request. */\nexport async function previewReportRefresh(\n reportCtor: ReportCtor,\n options: PreviewReportRefreshOptions,\n): Promise<ReportRefreshPreview> {\n const mode = await actionMode(reportCtor, options.mode);\n const action = actionContext(\n reportCtor,\n 'preview',\n mode,\n options.refreshAction,\n );\n await options.host.authorize(action);\n await options.host.audit(action);\n return {\n phase: 'preview',\n lifecycle: await getReportLifecycle(reportCtor, options),\n action,\n execution: 'background',\n };\n}\n\n/** Queue a manual refresh only after the action host authorizes and audits it. */\nexport async function applyReportRefresh(\n reportCtor: ReportCtor,\n options: ApplyReportRefreshOptions,\n): Promise<AppliedReportRefresh> {\n const mode = await actionMode(reportCtor, options.mode);\n const action = actionContext(\n reportCtor,\n 'apply',\n mode,\n options.refreshAction,\n );\n await options.host.authorize(action);\n await options.host.audit(action);\n const tenantId = lifecycleTenantId(reportCtor);\n const enqueue = () =>\n enqueueReportRefresh({\n db: options.db,\n reportClass: canonicalClassName(reportCtor),\n mode,\n trigger: 'manual',\n tenantId,\n queue: options.queue,\n priority: options.priority,\n timeout: options.timeout,\n maxAttempts: options.maxAttempts,\n tenantJobCap: options.tenantJobCap,\n });\n const job =\n tenantId === null && getTenantId()\n ? await withSystemContext(enqueue)\n : await enqueue();\n return { phase: 'apply', job: jobHandle(job) };\n}\n\n/** Normalize executor results without exposing the tenant identifiers in a fanout. */\nexport function reportRefreshOutcome(\n result: ReportRefreshResult,\n): ReportRefreshOutcome {\n const scopes = result.tenantResults ?? [result];\n const lockSkippedScopes = scopes.filter((scope) => scope.skipped).length;\n const completedScopes = scopes.length - lockSkippedScopes;\n return {\n state:\n scopes.length > 1 && lockSkippedScopes > 0\n ? 'partial'\n : lockSkippedScopes > 0\n ? 'lock-skipped'\n : 'current',\n rowCount: result.rowCount,\n changedGroupCount: result.changedGroupCount ?? 0,\n completedScopes,\n lockSkippedScopes,\n mode: result.mode,\n refreshedAt: result.refreshedAt.toISOString(),\n ...(result.runId ? { runId: result.runId } : {}),\n };\n}\n","import {\n createDataQueryFingerprint,\n DataQueryValidationError,\n normalizeDataQueryRequest,\n normalizeDataQueryResult,\n ObjectRegistry,\n type SmrtObject,\n} from '@happyvertical/smrt-core';\nimport { toSnakeCase } from '@happyvertical/smrt-core/utils';\nimport type {\n DataQueryCondition,\n DataQueryFieldDescriptor,\n DataQueryFilter,\n DataQueryFilterOperator,\n DataQueryFreshness,\n DataQueryRequest,\n DataQueryResult,\n DataQuerySchema,\n} from '@happyvertical/smrt-types';\nimport { buildReportDefinition } from './compiler.js';\nimport {\n getReportLifecycle,\n type ReportLifecycleOptions,\n type ReportLifecycleSnapshot,\n} from './lifecycle.js';\nimport type {\n ReportAggregateFn,\n ReportDefinition,\n ReportFieldDefinition,\n ReportRefreshConfig,\n ReportTimeBucketUnit,\n} from './types.js';\n\n/** Presentation and policy metadata understood by the report adapter. */\nexport type ReportColumnSensitivity =\n | 'public'\n | 'personal'\n | 'sensitive'\n | 'secret';\n\nexport type ReportColumnCapability =\n | 'read'\n | 'project'\n | 'filter'\n | 'sort'\n | 'facet'\n | 'group'\n | 'aggregate';\n\nexport type ReportColumnKind = 'identity' | 'group' | 'bucket' | 'aggregate';\n\n/**\n * Source-query clause a field belongs to. The materialized read executor\n * applies both kinds through its tenant-scoped collection, but consumers use\n * this declaration to keep dimensions/periods (WHERE) distinct from measures\n * (HAVING) when constructing drilldowns, saved views, or a live query.\n */\nexport type ReportFilterScope = 'where' | 'having';\n\nexport interface ReportColumnDescriptor extends DataQueryFieldDescriptor {\n /** Stable output column id (never a property path). */\n id: string;\n fieldName: string;\n label: string;\n kind: ReportColumnKind;\n filterScope: ReportFilterScope;\n /** Capabilities available from this adapter revision. */\n capabilities: ReportColumnCapability[];\n format?: string;\n sensitivity?: ReportColumnSensitivity;\n sourceColumn?: string;\n bucket?: ReportTimeBucketUnit;\n aggregate?: ReportAggregateFn;\n distinct?: boolean;\n}\n\nexport interface ReportRefreshDescriptor {\n mode: 'rebuild' | 'incremental';\n /**\n * Whether the report configuration lets a registered SmrtReportCollection\n * synchronously refresh a stale read. Generic collection reads stay\n * read-only and report unknown freshness until the lifecycle adapter runs.\n */\n mayRefreshOnRead: boolean;\n ttlMs?: number;\n triggers: Array<'manual' | 'schedule' | 'change' | 'ttl' | 'job'>;\n /** Declared only; the lifecycle adapter owns authorization, audit, and run state. */\n action: ReportRefreshActionDescriptor;\n}\n\n/** A report-wide lifecycle action, never a mutation performed by this adapter. */\nexport interface ReportRefreshActionDescriptor {\n id: 'refresh';\n label: 'Refresh report';\n scope: 'surface';\n phases: Array<'preview' | 'apply'>;\n requiresPermission: true;\n requiredPermission: string;\n auditRequired: true;\n}\n\n/**\n * The report-owned, transport-neutral descriptor. Consumers can map `schema`\n * to the canonical data-query runtime and `columns` to their presentation\n * contract without importing smrt-ui or a report domain class.\n */\nexport interface ReportAdapterDescriptor {\n version: 1;\n resourceId: string;\n reportClassName: string;\n sourceClassName: string;\n tenantScoped: boolean;\n tenantField?: string;\n identityField: 'id';\n columns: ReportColumnDescriptor[];\n schema: DataQuerySchema;\n queryExecution: ReportQueryExecutionDescriptor;\n dataTable: ReportDataTableDescriptor;\n drilldown: ReportDrilldownDescriptor;\n refresh: ReportRefreshDescriptor;\n}\n\n/**\n * Delivery choices for the same bounded report query. They never add\n * authority: a host owns visible state and background-job execution.\n */\nexport type ReportQueryExecutionMode = 'visible' | 'background' | 'silent';\n\nexport interface ReportQueryExecutionDescriptor {\n modes: ReportQueryExecutionMode[];\n /** The caller may apply the returned rows to an already-authorized surface. */\n visible: { delivery: 'result' };\n /** A host queues the authority-free task and returns no materialized rows yet. */\n background: { delivery: 'queued'; requiresHost: true };\n /** The caller receives rows but the adapter makes no visible-surface change. */\n silent: { delivery: 'result'; mutatesVisibleSurface: false };\n}\n\n/**\n * The exact bounded request a background host may persist. Tenant, principal,\n * collection, database, and display state deliberately remain with that host.\n */\nexport interface ReportBackgroundQueryTask {\n version: 1;\n execution: 'background';\n resourceId: string;\n reportClassName: string;\n request: DataQueryRequest;\n inherits: Array<\n 'principal' | 'tenant' | 'report-definition' | 'field-policy'\n >;\n}\n\n/** Queuing a background query returns a handle, never rows from another scope. */\nexport interface ReportBackgroundQueryResult {\n version: 1;\n execution: 'background';\n status: 'queued';\n taskId: string;\n queryFingerprint: string;\n}\n\n/** One row value that can safely constrain a source-record drilldown. */\nexport interface ReportDrilldownFieldDescriptor {\n /** Stable report column id; never a display label. */\n id: string;\n /** Declared source field/column chosen by report metadata, not caller input. */\n sourceColumn: string;\n kind: 'group' | 'bucket';\n /** A bucket stays declarative so the source adapter preserves report timezone semantics. */\n bucket?: ReportTimeBucketUnit;\n}\n\n/**\n * Declarative source-query handoff for a report row. No client can supply a\n * principal, tenant, report definition, or arbitrary source field here.\n */\nexport interface ReportDrilldownDescriptor {\n id: 'drilldown';\n sourceClassName: string;\n fields: ReportDrilldownFieldDescriptor[];\n inherits: Array<\n 'principal' | 'tenant' | 'report-definition' | 'field-policy'\n >;\n}\n\nexport interface ReportDrilldownConstraint {\n id: string;\n sourceColumn: string;\n kind: 'group' | 'bucket';\n value: unknown;\n bucket?: ReportTimeBucketUnit;\n}\n\n/** A server adapter uses this authority-free handoff to execute a source drilldown. */\nexport interface ReportDrilldownQuery {\n version: 1;\n resourceId: string;\n reportClassName: string;\n sourceClassName: string;\n constraints: ReportDrilldownConstraint[];\n inherits: Array<\n 'principal' | 'tenant' | 'report-definition' | 'field-policy'\n >;\n}\n\n/** Structural DataTable view hints; deliberately not a smrt-ui dependency. */\nexport interface ReportDataTableHeaderPathSegment {\n /** Stable group identity within a header level. */\n id: string;\n /** Human-readable grouped-header label. */\n label: string;\n}\n\n/** Responsive metadata that maps directly to a consumer's table contract. */\nexport interface ReportDataTableColumnResponsive {\n /** Higher values make a column more important during responsive collapse. */\n priority?: number;\n /** Keep key dimensions reachable during responsive collapse. */\n keepVisible?: boolean;\n}\n\n/**\n * A rendering instruction for a raw materialized value. Formatting is never\n * applied to a query row, so sorting, export, and agent consumers retain it.\n */\nexport type ReportDataTableValueFormat =\n | 'text'\n | 'date'\n | 'datetime'\n | 'percentage'\n | 'count'\n | 'money'\n | 'number';\n\nexport type ReportDataTableColumnRole = 'data' | 'status' | 'action';\n\nexport interface ReportDataTableColumn {\n id: string;\n label: string;\n accessor: string;\n sortable: boolean;\n searchable: false;\n filterable: boolean;\n /** Group ancestry for consumers with multi-level table headers. */\n headerPath: ReportDataTableHeaderPathSegment[];\n /** Consumer-side display instruction; materialized rows remain raw. */\n valueFormat: ReportDataTableValueFormat;\n /** The default alignment for the formatted display value. */\n align: 'left' | 'right';\n /** Generic semantic role for status/action columns introduced by later slices. */\n role: ReportDataTableColumnRole;\n responsive: ReportDataTableColumnResponsive;\n}\n\nexport type ReportDataTableStructuralRowKind =\n | 'summary'\n | 'subtotal'\n | 'aggregate'\n | 'footer';\n\n/** Input supplied by a report consumer that computes a summary or subtotal. */\nexport interface ReportDataTableStructuralRowInput {\n id: string;\n kind: ReportDataTableStructuralRowKind;\n label: string;\n /** Raw values keyed by the adapter's stable column id. */\n values?: Readonly<Record<string, unknown>>;\n /** Column that renders this row's accessible row header. */\n labelColumnId?: string;\n}\n\n/**\n * Structural rows are intentionally separate from materialized data rows.\n * Consumers pass them to their DataTable's structural-row slot/prop, never its\n * selectable data collection.\n */\nexport interface ReportDataTableStructuralRow\n extends ReportDataTableStructuralRowInput {\n selection: 'excluded';\n actions: 'excluded';\n}\n\n/** Per-column presentation overrides owned by the consuming report surface. */\nexport interface ReportDataTableColumnOverride {\n label?: string;\n headerPath?: readonly ReportDataTableHeaderPathSegment[];\n valueFormat?: ReportDataTableValueFormat;\n align?: 'left' | 'right';\n role?: ReportDataTableColumnRole;\n responsive?: ReportDataTableColumnResponsive;\n}\n\nexport interface ReportDataTablePresentationOptions {\n /** Overrides are keyed by stable adapter column id, not display labels. */\n columns?: Readonly<Record<string, ReportDataTableColumnOverride>>;\n /** Summary/subtotal rows remain structural rather than materialized data rows. */\n structuralRows?: readonly ReportDataTableStructuralRowInput[];\n}\n\nexport interface ReportDataTableDescriptor {\n rowKey: 'id';\n manualPagination: true;\n manualSorting: true;\n enableFiltering: true;\n enableSearch: false;\n columns: ReportDataTableColumn[];\n structuralRows: ReportDataTableStructuralRow[];\n}\n\nexport interface ReportAdapterOptions {\n /** Scope used only to make a stable resource id; it is not authority. */\n tenantScope?: 'current' | 'global' | 'tenant';\n /** Permission that a lifecycle action host must require before refresh. */\n refreshPermission?: string;\n /** Consumer-owned presentational preferences; they never affect query rows. */\n dataTable?: ReportDataTablePresentationOptions;\n}\n\ninterface ReportQueryCommonOptions {\n /** Injected collection seam for tests and application-owned adapters. */\n collection?: {\n list(\n options: Record<string, unknown>,\n ): Promise<Array<Record<string, unknown>>>;\n count(options?: Record<string, unknown>): Promise<number>;\n facets?(options: Record<string, unknown>): Promise<\n Array<{\n field: string;\n values: Array<{\n value: string | number | boolean | null;\n count: number;\n }>;\n }>\n >;\n };\n db?: import('@happyvertical/sql').DatabaseInterface;\n /**\n * Opt in to a tenant-safe lifecycle snapshot alongside this materialized\n * read. It never makes a generic read mutate.\n */\n lifecycle?: Omit<ReportLifecycleOptions, 'db'>;\n}\n\n/** Visible is the default; silent has identical data authority but no UI intent. */\nexport interface ReportQueryOptions extends ReportQueryCommonOptions {\n execution?: 'visible' | 'silent';\n}\n\n/** Background execution is possible only through an application-owned queue host. */\nexport interface ReportBackgroundQueryOptions extends ReportQueryCommonOptions {\n execution: 'background';\n enqueueBackgroundQuery(\n task: ReportBackgroundQueryTask,\n ): Promise<{ taskId: string }>;\n}\n\n/** Lifecycle context attached only when a caller explicitly opts in. */\nexport interface ReportMaterializedReadLifecycle {\n snapshot: ReportLifecycleSnapshot;\n /** Whether this collection read appears to have completed a TTL refresh. */\n read: 'current' | 'stale' | 'refresh-triggered';\n}\n\nexport interface ReportDataQueryResult extends DataQueryResult {\n /** Delivery intent only; the adapter never mutates a visible surface. */\n execution: 'visible' | 'silent';\n reportLifecycle?: ReportMaterializedReadLifecycle;\n}\n\ntype RegistryField = {\n type?: string;\n transient?: boolean;\n description?: string;\n sensitive?: boolean;\n readPermission?: string;\n format?: unknown;\n sensitivity?: unknown;\n _meta?: Record<string, unknown>;\n};\nconst SENSITIVITIES = new Set<ReportColumnSensitivity>([\n 'public',\n 'personal',\n 'sensitive',\n 'secret',\n]);\n\nfunction registeredClass(\n ctor: new (...args: any[]) => SmrtObject,\n): ReturnType<typeof ObjectRegistry.getClassByConstructor> {\n return (\n ObjectRegistry.getClassByConstructor(ctor) ??\n ObjectRegistry.getClass(ctor.name)\n );\n}\n\nfunction fieldPolicy(field: RegistryField | undefined): {\n sensitive: boolean;\n readPermission?: string;\n} {\n const meta = field?._meta ?? {};\n const sensitivity = [field?.sensitivity, meta.sensitivity].find(\n (value): value is string => typeof value === 'string',\n );\n return {\n sensitive:\n field?.sensitive === true ||\n meta.sensitive === true ||\n sensitivity === 'sensitive' ||\n sensitivity === 'secret',\n readPermission:\n typeof field?.readPermission === 'string'\n ? field.readPermission\n : typeof meta.readPermission === 'string'\n ? meta.readPermission\n : undefined,\n };\n}\n\nfunction descriptorMetadata(field: RegistryField | undefined): {\n format?: string;\n sensitivity?: ReportColumnSensitivity;\n} {\n const metadata = field?._meta ?? {};\n const format = [field?.format, metadata.format].find(\n (value): value is string => typeof value === 'string' && value.length > 0,\n );\n const rawSensitivity = [field?.sensitivity, metadata.sensitivity].find(\n (value): value is string =>\n typeof value === 'string' &&\n SENSITIVITIES.has(value as ReportColumnSensitivity),\n );\n return {\n ...(format ? { format } : {}),\n ...(rawSensitivity\n ? { sensitivity: rawSensitivity as ReportColumnSensitivity }\n : {}),\n };\n}\n\nfunction labelFor(fieldName: string): string {\n return fieldName\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .replace(/^./, (value) => value.toUpperCase());\n}\n\nfunction queryType(type: string | undefined): DataQueryFieldDescriptor['type'] {\n switch (type) {\n case 'integer':\n case 'decimal':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'datetime':\n return 'datetime';\n case 'json':\n return 'json';\n default:\n return 'string';\n }\n}\n\nfunction configuredTenantField(configured?: string): string | undefined {\n return configured ? toSnakeCase(configured) : undefined;\n}\n\nfunction isColumnBackedField(field: RegistryField | undefined): boolean {\n if (!field) return false;\n if (field.transient === true || field._meta?.transient === true) return false;\n return !['meta', 'oneToMany', 'manyToMany'].includes(field.type ?? '');\n}\n\nfunction reportColumn(\n field: ReportFieldDefinition,\n registryField: RegistryField | undefined,\n): ReportColumnDescriptor | undefined {\n const policy = fieldPolicy(registryField);\n // A descriptor is an exposure boundary. Without a principal/permission\n // context, sensitive, permission-gated, and non-column fields fail closed.\n if (\n policy.sensitive ||\n policy.readPermission ||\n !isColumnBackedField(registryField)\n ) {\n return undefined;\n }\n\n const id = field.columnName ?? toSnakeCase(field.fieldName);\n const report = field.report;\n if (!report) return undefined;\n const type = queryType(field.type ?? registryField?.type);\n const filterOperators = filterOperatorsFor(type);\n const base: ReportColumnDescriptor = {\n id,\n fieldName: field.fieldName,\n label: registryField?.description || labelFor(field.fieldName),\n kind: report.kind,\n filterScope: report.kind === 'aggregate' ? 'having' : 'where',\n type,\n projectable: true,\n sortable: true,\n facetable: report.kind !== 'aggregate',\n ...(filterOperators ? { filterOperators } : {}),\n capabilities: [\n 'project',\n 'read',\n ...(filterOperators ? (['filter'] as const) : []),\n 'sort',\n ...(report.kind !== 'aggregate' ? (['facet'] as const) : []),\n ...(report.kind === 'aggregate'\n ? (['aggregate'] as const)\n : report.kind === 'group'\n ? (['group'] as const)\n : []),\n ],\n ...(report.kind === 'group'\n ? { sourceColumn: toSnakeCase(report.sourceColumn ?? field.fieldName) }\n : {}),\n ...(report.kind === 'bucket'\n ? { bucket: report.unit, sourceColumn: toSnakeCase(report.sourceColumn) }\n : {}),\n ...(report.kind === 'aggregate'\n ? {\n aggregate: report.fn,\n ...(report.column\n ? { sourceColumn: toSnakeCase(report.column) }\n : {}),\n ...(report.distinct === undefined\n ? {}\n : { distinct: report.distinct }),\n }\n : {}),\n ...descriptorMetadata(registryField),\n };\n return base;\n}\n\nfunction identityColumn(): ReportColumnDescriptor {\n return {\n id: 'id',\n fieldName: 'id',\n label: 'ID',\n kind: 'identity',\n filterScope: 'where',\n type: 'string',\n projectable: true,\n sortable: true,\n facetable: false,\n filterOperators: filterOperatorsFor('string'),\n capabilities: ['project', 'read', 'filter', 'sort'],\n };\n}\n\nfunction toQueryField(\n column: ReportColumnDescriptor,\n): DataQueryFieldDescriptor {\n return {\n id: column.id,\n type: column.type,\n ...(column.projectable === undefined\n ? {}\n : { projectable: column.projectable }),\n sortable: column.sortable === true,\n facetable: column.facetable === true,\n ...(column.filterOperators === undefined\n ? {}\n : { filterOperators: [...column.filterOperators] }),\n };\n}\n\nfunction filterOperatorsFor(\n type: DataQueryFieldDescriptor['type'],\n): DataQueryFilterOperator[] | undefined {\n switch (type) {\n case 'string':\n return ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn', 'like'];\n case 'number':\n case 'datetime':\n return ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn'];\n case 'boolean':\n return ['eq', 'ne', 'in', 'notIn'];\n case 'json':\n return undefined;\n }\n}\n\nfunction refreshDescriptor(\n refresh?: ReportRefreshConfig,\n refreshPermission = 'reports.refresh',\n): ReportRefreshDescriptor {\n const config = refresh ?? {};\n const triggers = new Set<ReportRefreshDescriptor['triggers'][number]>([\n 'manual',\n ]);\n if (!config.manual) {\n const hasSchedule = Boolean(config.schedule || config.fullRebuildSchedule);\n const hasChangeTrigger = Boolean(config.onChange?.length);\n if (hasSchedule) triggers.add('schedule');\n if (hasChangeTrigger) triggers.add('change');\n if (config.ttl !== undefined && config.ttl > 0) triggers.add('ttl');\n // Scheduler and on-change refreshes enqueue a durable job in either mode;\n // incremental mode alone is not itself a trigger.\n if (hasSchedule || hasChangeTrigger) triggers.add('job');\n }\n return {\n mode: config.mode ?? 'rebuild',\n mayRefreshOnRead:\n config.ttl !== undefined && config.ttl > 0 && !config.manual,\n ...(config.ttl === undefined ? {} : { ttlMs: config.ttl }),\n triggers: [...triggers],\n action: {\n id: 'refresh',\n label: 'Refresh report',\n scope: 'surface',\n phases: ['preview', 'apply'],\n requiresPermission: true,\n requiredPermission:\n refreshPermission.trim().length > 0\n ? refreshPermission\n : 'reports.refresh',\n auditRequired: true,\n },\n };\n}\n\nfunction resourceId(\n definition: ReportDefinition,\n scope: ReportAdapterOptions['tenantScope'],\n): string {\n return `${definition.reportClassName}#${scope ?? 'current'}`;\n}\n\nconst VALUE_FORMAT_ALIASES: Readonly<\n Record<string, ReportDataTableValueFormat>\n> = {\n text: 'text',\n date: 'date',\n datetime: 'datetime',\n 'date-time': 'datetime',\n percentage: 'percentage',\n percent: 'percentage',\n count: 'count',\n money: 'money',\n currency: 'money',\n number: 'number',\n decimal: 'number',\n integer: 'number',\n};\n\nconst VALUE_FORMATS = new Set<ReportDataTableValueFormat>(\n Object.values(VALUE_FORMAT_ALIASES),\n);\nconst COLUMN_ROLES = new Set<ReportDataTableColumnRole>([\n 'data',\n 'status',\n 'action',\n]);\nconst STRUCTURAL_ROW_KINDS = new Set<ReportDataTableStructuralRowKind>([\n 'summary',\n 'subtotal',\n 'aggregate',\n 'footer',\n]);\n\nfunction valueFormat(\n column: ReportColumnDescriptor,\n): ReportDataTableValueFormat {\n const configured = column.format?.trim().toLowerCase();\n if (configured && Object.hasOwn(VALUE_FORMAT_ALIASES, configured)) {\n return VALUE_FORMAT_ALIASES[configured];\n }\n if (column.kind === 'bucket') {\n return column.bucket === 'minute' || column.bucket === 'hour'\n ? 'datetime'\n : 'date';\n }\n if (column.kind === 'aggregate' && column.aggregate === 'count') {\n return 'count';\n }\n if (column.type === 'datetime') return 'datetime';\n if (column.type === 'number') return 'number';\n return 'text';\n}\n\nfunction headerPath(\n column: ReportColumnDescriptor,\n): ReportDataTableHeaderPathSegment[] {\n if (column.kind === 'aggregate') {\n return [\n { id: 'measures', label: 'Measures' },\n {\n id: `aggregate:${column.aggregate ?? 'value'}`,\n label: labelFor(column.aggregate ?? 'value'),\n },\n ];\n }\n if (column.kind === 'bucket') {\n return [\n { id: 'dimensions', label: 'Dimensions' },\n { id: 'time', label: 'Time' },\n ];\n }\n if (column.kind === 'group') {\n return [\n { id: 'dimensions', label: 'Dimensions' },\n { id: 'groups', label: 'Groups' },\n ];\n }\n return [{ id: 'dimensions', label: 'Dimensions' }];\n}\n\nfunction responsive(\n column: ReportColumnDescriptor,\n): ReportDataTableColumnResponsive {\n if (column.kind === 'group' || column.kind === 'bucket') {\n return { priority: 100, keepVisible: true };\n }\n if (column.kind === 'identity') return { priority: 80 };\n return { priority: 20 };\n}\n\nfunction assertHeaderPath(\n columnId: string,\n path: readonly ReportDataTableHeaderPathSegment[],\n): ReportDataTableHeaderPathSegment[] {\n if (!Array.isArray(path)) {\n throw new TypeError(\n `Report DataTable headerPath for ${columnId} must be an array`,\n );\n }\n return path.map((segment) => {\n if (\n !segment ||\n typeof segment.id !== 'string' ||\n segment.id.trim().length === 0 ||\n typeof segment.label !== 'string' ||\n segment.label.trim().length === 0\n ) {\n throw new TypeError(\n `Report DataTable headerPath entries for ${columnId} require non-empty id and label`,\n );\n }\n return { id: segment.id, label: segment.label };\n });\n}\n\nfunction dataTableColumn(\n column: ReportColumnDescriptor,\n override?: ReportDataTableColumnOverride,\n): ReportDataTableColumn {\n const format = override?.valueFormat ?? valueFormat(column);\n const path = override?.headerPath ?? headerPath(column);\n if (!VALUE_FORMATS.has(format)) {\n throw new TypeError(\n `Report DataTable valueFormat for ${column.id} is not supported: ${String(format)}`,\n );\n }\n if (override?.role !== undefined && !COLUMN_ROLES.has(override.role)) {\n throw new TypeError(\n `Report DataTable role for ${column.id} is not supported: ${String(override.role)}`,\n );\n }\n if (\n override?.label !== undefined &&\n (typeof override.label !== 'string' || override.label.trim().length === 0)\n ) {\n throw new TypeError(\n `Report DataTable label for ${column.id} must not be empty`,\n );\n }\n if (\n override?.align !== undefined &&\n override.align !== 'left' &&\n override.align !== 'right'\n ) {\n throw new TypeError(\n `Report DataTable align for ${column.id} is not supported: ${String(override.align)}`,\n );\n }\n if (\n override?.responsive?.priority !== undefined &&\n (!Number.isFinite(override.responsive.priority) ||\n override.responsive.priority < 0)\n ) {\n throw new TypeError(\n `Report DataTable responsive priority for ${column.id} must be a non-negative finite number`,\n );\n }\n if (\n override?.responsive?.keepVisible !== undefined &&\n typeof override.responsive.keepVisible !== 'boolean'\n ) {\n throw new TypeError(\n `Report DataTable keepVisible for ${column.id} must be a boolean`,\n );\n }\n return {\n id: column.id,\n label: override?.label ?? column.label,\n accessor: column.id,\n sortable: column.sortable === true,\n searchable: false,\n filterable: column.filterOperators !== undefined,\n headerPath: assertHeaderPath(column.id, path),\n valueFormat: format,\n align:\n override?.align ??\n (['count', 'money', 'number', 'percentage'].includes(format)\n ? 'right'\n : 'left'),\n role: override?.role ?? 'data',\n responsive: { ...responsive(column), ...override?.responsive },\n };\n}\n\nfunction structuralRows(\n columnIds: ReadonlySet<string>,\n rows: readonly ReportDataTableStructuralRowInput[] = [],\n): ReportDataTableStructuralRow[] {\n const ids = new Set<string>();\n return rows.map((row) => {\n if (\n !row ||\n typeof row.id !== 'string' ||\n row.id.trim().length === 0 ||\n typeof row.label !== 'string' ||\n row.label.trim().length === 0\n ) {\n throw new TypeError(\n 'Report structural rows require non-empty id and label values',\n );\n }\n if (ids.has(row.id)) {\n throw new TypeError(`Report structural row id must be unique: ${row.id}`);\n }\n if (!STRUCTURAL_ROW_KINDS.has(row.kind)) {\n throw new TypeError(\n `Report structural row kind is not supported: ${String(row.kind)}`,\n );\n }\n if (row.labelColumnId !== undefined) {\n if (\n typeof row.labelColumnId !== 'string' ||\n row.labelColumnId.trim().length === 0 ||\n !columnIds.has(row.labelColumnId)\n ) {\n throw new TypeError(\n `Report structural row labelColumnId must name an adapter column: ${String(row.labelColumnId)}`,\n );\n }\n }\n if (\n row.values !== undefined &&\n (typeof row.values !== 'object' ||\n row.values === null ||\n Array.isArray(row.values))\n ) {\n throw new TypeError('Report structural row values must be an object');\n }\n if (\n row.values &&\n Object.keys(row.values).some((columnId) => !columnIds.has(columnId))\n ) {\n throw new TypeError(\n 'Report structural row values must use adapter column ids',\n );\n }\n ids.add(row.id);\n return {\n id: row.id,\n kind: row.kind,\n label: row.label,\n ...(row.values !== undefined\n ? {\n values: jsonSafeValue(row.values) as Record<string, unknown>,\n }\n : {}),\n ...(row.labelColumnId !== undefined\n ? { labelColumnId: row.labelColumnId }\n : {}),\n selection: 'excluded' as const,\n actions: 'excluded' as const,\n };\n });\n}\n\nfunction drilldownDescriptor(\n columns: readonly ReportColumnDescriptor[],\n sourceClassName: string,\n): ReportDrilldownDescriptor {\n return {\n id: 'drilldown',\n sourceClassName,\n fields: columns\n .filter(\n (\n column,\n ): column is ReportColumnDescriptor & {\n kind: 'group' | 'bucket';\n sourceColumn: string;\n } =>\n (column.kind === 'group' || column.kind === 'bucket') &&\n typeof column.sourceColumn === 'string',\n )\n .map((column) => ({\n id: column.id,\n sourceColumn: column.sourceColumn,\n kind: column.kind,\n ...(column.kind === 'bucket' && column.bucket\n ? { bucket: column.bucket }\n : {}),\n })),\n inherits: ['principal', 'tenant', 'report-definition', 'field-policy'],\n };\n}\n\nexport async function buildReportAdapterDescriptor(\n reportCtor: new (...args: any[]) => SmrtObject,\n options: ReportAdapterOptions = {},\n): Promise<ReportAdapterDescriptor> {\n const definition = await buildReportDefinition(reportCtor);\n const registered = registeredClass(reportCtor);\n const fields = (await ObjectRegistry.getAllFields(\n definition.reportClassName,\n )) as Map<string, RegistryField>;\n // A tenant-looking field is not an isolation boundary. Only registered\n // tenant scoping installs the collection interceptor used by the executor.\n const tenantField = configuredTenantField(\n registered?.tenantScopedConfig?.field,\n );\n const columns = [\n identityColumn(),\n ...definition.fields\n .map((field) => reportColumn(field, fields.get(field.fieldName)))\n .filter((field): field is ReportColumnDescriptor => Boolean(field)),\n ].sort((left, right) => left.id.localeCompare(right.id));\n const schema: DataQuerySchema = {\n version: 1,\n identityField: 'id',\n fields: columns.map(toQueryField),\n defaultSort: [{ field: 'id', direction: 'asc' }],\n supports: { cursorPagination: false, consistency: false, facets: true },\n };\n const dataTable: ReportDataTableDescriptor = {\n rowKey: 'id',\n manualPagination: true,\n manualSorting: true,\n enableFiltering: true,\n enableSearch: false,\n columns: columns.map((column) =>\n dataTableColumn(column, options.dataTable?.columns?.[column.id]),\n ),\n structuralRows: structuralRows(\n new Set(columns.map((column) => column.id)),\n options.dataTable?.structuralRows,\n ),\n };\n return {\n version: 1,\n resourceId: resourceId(definition, options.tenantScope),\n reportClassName: definition.reportClassName,\n sourceClassName: definition.sourceClassName,\n tenantScoped: Boolean(registered?.tenantScopedConfig),\n ...(tenantField ? { tenantField } : {}),\n identityField: 'id',\n columns,\n schema,\n queryExecution: {\n modes: ['visible', 'background', 'silent'],\n visible: { delivery: 'result' },\n background: { delivery: 'queued', requiresHost: true },\n silent: { delivery: 'result', mutatesVisibleSurface: false },\n },\n dataTable,\n drilldown: drilldownDescriptor(columns, definition.sourceClassName),\n refresh: refreshDescriptor(definition.refresh, options.refreshPermission),\n };\n}\n\nfunction publicFieldMap(\n descriptor: ReportAdapterDescriptor,\n): Map<string, string> {\n return new Map(\n descriptor.columns.map((column) => [column.id, column.fieldName]),\n );\n}\n\n/**\n * Bind a materialized report row to its declared source dimensions. This is a\n * query handoff, not an executor: an authenticated source adapter must apply\n * the inherited principal, tenant, definition, and field policy before it\n * reads source records.\n */\nexport async function buildReportDrilldownQuery(\n reportCtor: new (...args: any[]) => SmrtObject,\n row: Readonly<Record<string, unknown>>,\n options: ReportAdapterOptions = {},\n): Promise<ReportDrilldownQuery> {\n const descriptor = await buildReportAdapterDescriptor(reportCtor, options);\n const constraints = descriptor.drilldown.fields.map((field) => {\n if (!Object.hasOwn(row, field.id)) {\n throw new DataQueryValidationError(\n `Report drilldown row is missing grouping field: ${field.id}`,\n 'DATA_QUERY_RESULT_INVALID',\n );\n }\n return {\n id: field.id,\n sourceColumn: field.sourceColumn,\n kind: field.kind,\n value: jsonSafeValue(row[field.id]),\n ...(field.bucket ? { bucket: field.bucket } : {}),\n };\n });\n return {\n version: 1,\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n sourceClassName: descriptor.drilldown.sourceClassName,\n constraints,\n inherits: [...descriptor.drilldown.inherits],\n };\n}\n\ntype MaterializedWhere = Array<Array<Record<string, unknown>>>;\n\n/** Keep OR expansion bounded before it reaches the database collection. */\nconst MAX_REPORT_FILTER_OR_GROUPS = 128;\n\nfunction reportFilterError(message: string): never {\n throw new DataQueryValidationError(message, 'DATA_QUERY_UNSUPPORTED');\n}\n\nfunction crossProductWhere(\n left: MaterializedWhere,\n right: MaterializedWhere,\n): MaterializedWhere {\n if (left.length * right.length > MAX_REPORT_FILTER_OR_GROUPS) {\n return reportFilterError(\n `Report filter expands beyond ${MAX_REPORT_FILTER_OR_GROUPS} OR groups`,\n );\n }\n return left.flatMap((leftGroup) =>\n right.map((rightGroup) => [...leftGroup, ...rightGroup]),\n );\n}\n\nfunction inverseOperator(\n operator: DataQueryFilterOperator,\n): DataQueryFilterOperator {\n switch (operator) {\n case 'eq':\n return 'ne';\n case 'ne':\n return 'eq';\n case 'gt':\n return 'lte';\n case 'gte':\n return 'lt';\n case 'lt':\n return 'gte';\n case 'lte':\n return 'gt';\n case 'in':\n return 'notIn';\n case 'notIn':\n return 'in';\n case 'like':\n return reportFilterError(\n 'Report filters do not support negating a LIKE predicate',\n );\n }\n}\n\nfunction collectionCondition(\n field: string,\n operator: DataQueryFilterOperator,\n value: DataQueryCondition['value'],\n): MaterializedWhere {\n const keyFor = (suffix: string) => (suffix ? `${field} ${suffix}` : field);\n const scalar = (key: string, scalarValue: unknown): MaterializedWhere => [\n [{ [key]: scalarValue }],\n ];\n\n if (operator === 'in') {\n const values = value as Array<string | number | boolean | null>;\n const nonNull = values.filter((entry) => entry !== null);\n if (nonNull.length === 0) return scalar(field, null);\n if (nonNull.length === values.length) return scalar(keyFor('in'), nonNull);\n // SQL IN does not match NULL. Model the user-visible union explicitly.\n return [[{ [field]: null }], [{ [keyFor('in')]: nonNull }]];\n }\n\n if (operator === 'notIn') {\n const values = value as Array<string | number | boolean | null>;\n // `buildWhere()` does not have a `NOT IN` primitive. A bounded AND of\n // inequality predicates has the same null-safe semantics and remains fully\n // validated by the collection.\n return [values.map((entry) => ({ [keyFor('!=')]: entry }))];\n }\n\n const suffix: Record<\n Exclude<DataQueryFilterOperator, 'in' | 'notIn'>,\n string\n > = {\n eq: '',\n ne: '!=',\n gt: '>',\n gte: '>=',\n lt: '<',\n lte: '<=',\n like: 'like',\n };\n return scalar(keyFor(suffix[operator]), value);\n}\n\nfunction materializedWhereForFilter(\n filter: DataQueryFilter,\n fields: Map<string, string>,\n negate = false,\n): MaterializedWhere {\n if (filter.kind === 'condition') {\n const field = fields.get(filter.field);\n if (!field) {\n return reportFilterError(\n `Report filter field is not declared: ${filter.field}`,\n );\n }\n return collectionCondition(\n field,\n negate ? inverseOperator(filter.operator) : filter.operator,\n filter.value,\n );\n }\n\n if (filter.kind === 'not') {\n return materializedWhereForFilter(filter.filter, fields, !negate);\n }\n\n const combineWithAnd =\n (filter.kind === 'all' && !negate) || (filter.kind === 'any' && negate);\n if (combineWithAnd) {\n return filter.filters.reduce<MaterializedWhere>(\n (combined, child) =>\n crossProductWhere(\n combined,\n materializedWhereForFilter(child, fields, negate),\n ),\n [[]],\n );\n }\n\n const groups = filter.filters.flatMap((child) =>\n materializedWhereForFilter(child, fields, negate),\n );\n if (groups.length > MAX_REPORT_FILTER_OR_GROUPS) {\n return reportFilterError(\n `Report filter expands beyond ${MAX_REPORT_FILTER_OR_GROUPS} OR groups`,\n );\n }\n return groups;\n}\n\n/**\n * Converts a normalized report filter to collection-owned, parameterized\n * predicates. The returned DNF form gives each OR branch a complete AND\n * clause, so tenant interception can add its tenant predicate to every branch.\n */\nfunction materializedWhere(\n filter: DataQueryFilter | undefined,\n fields: Map<string, string>,\n): MaterializedWhere | undefined {\n return filter ? materializedWhereForFilter(filter, fields) : undefined;\n}\n\nfunction filterScopeForColumn(\n column: ReportColumnDescriptor,\n): ReportFilterScope {\n return column.filterScope;\n}\n\n/**\n * Split the declared filter language by report semantics for consumers that\n * construct a live source query. Mixed AND expressions are represented as two\n * independent clauses; mixed OR/NOT expressions are rejected because moving\n * either half across WHERE/HAVING would change their meaning.\n */\nexport function splitReportFilterScopes(\n descriptor: ReportAdapterDescriptor,\n filter: DataQueryFilter | undefined,\n): { where?: DataQueryFilter; having?: DataQueryFilter } {\n if (!filter) return {};\n const columns = new Map(\n descriptor.columns.map((column) => [column.id, column]),\n );\n const combineAll = (\n filters: DataQueryFilter[],\n ): DataQueryFilter | undefined => {\n const flattened = filters.flatMap((candidate) =>\n candidate.kind === 'all' ? candidate.filters : [candidate],\n );\n return flattened.length === 0\n ? undefined\n : flattened.length === 1\n ? flattened[0]\n : { kind: 'all', filters: flattened };\n };\n const split = (\n candidate: DataQueryFilter,\n ): { where?: DataQueryFilter; having?: DataQueryFilter } => {\n if (candidate.kind === 'condition') {\n const column = columns.get(candidate.field);\n if (!column) {\n reportFilterError(\n `Report filter field is not declared: ${candidate.field}`,\n );\n }\n return filterScopeForColumn(column) === 'where'\n ? { where: candidate }\n : { having: candidate };\n }\n\n if (candidate.kind === 'all') {\n const children = candidate.filters.map(split);\n const where = combineAll(\n children.flatMap((child) => (child.where ? [child.where] : [])),\n );\n const having = combineAll(\n children.flatMap((child) => (child.having ? [child.having] : [])),\n );\n return {\n ...(where ? { where } : {}),\n ...(having ? { having } : {}),\n };\n }\n\n const children =\n candidate.kind === 'not'\n ? [split(candidate.filter)]\n : candidate.filters.map(split);\n const scopes = new Set(\n children.flatMap((child) => [\n ...(child.where ? (['where'] as const) : []),\n ...(child.having ? (['having'] as const) : []),\n ]),\n );\n if (scopes.size !== 1) {\n reportFilterError(\n 'Report WHERE and HAVING filters cannot be mixed inside one OR or NOT expression',\n );\n }\n const scope = scopes.has('where') ? 'where' : 'having';\n const filters = children.flatMap((child) => {\n const filter = scope === 'where' ? child.where : child.having;\n return filter ? [filter] : [];\n });\n if (candidate.kind === 'not') {\n const [filter] = filters;\n if (!filter) {\n reportFilterError('Report NOT expressions must contain one filter');\n }\n return scope === 'where'\n ? { where: { kind: 'not', filter } }\n : { having: { kind: 'not', filter } };\n }\n return scope === 'where'\n ? { where: { kind: 'any', filters } }\n : { having: { kind: 'any', filters } };\n };\n\n return split(filter);\n}\n\nfunction jsonSafeValue(\n value: unknown,\n ancestors = new WeakSet<object>(),\n): unknown {\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'bigint') {\n const numeric = Number(value);\n if (!Number.isSafeInteger(numeric)) {\n throw new RangeError(\n 'Materialized bigint values must be safely representable as numbers',\n );\n }\n return numeric;\n }\n if (value && typeof value === 'object') {\n if (ancestors.has(value)) {\n throw new TypeError('Values must not contain circular references');\n }\n ancestors.add(value);\n try {\n if (Array.isArray(value)) {\n return value.map((entry) => jsonSafeValue(entry, ancestors));\n }\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [\n key,\n jsonSafeValue(entry, ancestors),\n ]),\n );\n } finally {\n ancestors.delete(value);\n }\n }\n return value;\n}\n\nfunction mapMaterializedRow(\n row: Record<string, unknown>,\n projection: readonly string[],\n fieldMap: Map<string, string>,\n): Record<string, unknown> {\n const mapped: Record<string, unknown> = {};\n for (const id of projection) {\n const sourceField = fieldMap.get(id) ?? id;\n if (sourceField in row) mapped[id] = jsonSafeValue(row[sourceField]);\n }\n return mapped;\n}\n\nfunction queryFreshness(\n lifecycle: ReportLifecycleSnapshot,\n): DataQueryFreshness {\n switch (lifecycle.state) {\n case 'current':\n return {\n state: 'fresh',\n ...(lifecycle.asOf ? { asOf: lifecycle.asOf } : {}),\n };\n case 'stale':\n case 'lock-skipped':\n case 'failed':\n return {\n state: 'stale',\n ...(lifecycle.asOf ? { asOf: lifecycle.asOf } : {}),\n };\n case 'refreshing':\n return lifecycle.hasUsableRows\n ? {\n state: 'stale',\n ...(lifecycle.asOf ? { asOf: lifecycle.asOf } : {}),\n }\n : { state: 'unknown' };\n }\n}\n\n/**\n * Execute bounded materialized report reads through the canonical query\n * envelope. Every predicate is an adapter-declared field/operator pair; the\n * collection converts it to parameterized SQL and applies tenant interceptors\n * to list, count, and facet reads alike.\n */\nexport function queryReportMaterializedRows(\n reportCtor: new (...args: any[]) => SmrtObject,\n input: unknown,\n options: ReportBackgroundQueryOptions,\n): Promise<ReportBackgroundQueryResult>;\nexport function queryReportMaterializedRows(\n reportCtor: new (...args: any[]) => SmrtObject,\n input: unknown,\n options?: ReportQueryOptions,\n): Promise<ReportDataQueryResult>;\nexport async function queryReportMaterializedRows(\n reportCtor: new (...args: any[]) => SmrtObject,\n input: unknown,\n options: ReportQueryOptions | ReportBackgroundQueryOptions = {},\n): Promise<ReportDataQueryResult | ReportBackgroundQueryResult> {\n const descriptor = await buildReportAdapterDescriptor(reportCtor);\n const request = normalizeDataQueryRequest(input, descriptor.schema);\n // Validate the semantic split even though materialized-row reads execute the\n // complete predicate against one persisted table. It prevents an adapter\n // consumer from silently treating a mixed source WHERE/HAVING expression as\n // either side of the aggregate boundary.\n splitReportFilterScopes(descriptor, request.filter);\n const queryFingerprint = createDataQueryFingerprint(\n request,\n descriptor.schema,\n );\n if (options.execution === 'background') {\n const queued = await options.enqueueBackgroundQuery({\n version: 1,\n execution: 'background',\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n request,\n inherits: ['principal', 'tenant', 'report-definition', 'field-policy'],\n });\n if (typeof queued.taskId !== 'string' || queued.taskId.length === 0) {\n throw new Error('Background report query hosts must return a task id');\n }\n return {\n version: 1,\n execution: 'background',\n status: 'queued',\n taskId: queued.taskId,\n queryFingerprint,\n };\n }\n const execution = options.execution ?? 'visible';\n const lifecycleDb = options.db;\n if (options.lifecycle && !lifecycleDb) {\n throw new Error('Report lifecycle disclosure requires a database handle');\n }\n const lifecycleOptions =\n options.lifecycle && lifecycleDb\n ? { db: lifecycleDb, ...options.lifecycle }\n : undefined;\n const lifecycleBefore = lifecycleOptions\n ? await getReportLifecycle(reportCtor, lifecycleOptions)\n : undefined;\n const fieldMap = publicFieldMap(descriptor);\n const where = materializedWhere(request.filter, fieldMap);\n const collection: NonNullable<ReportQueryOptions['collection']> =\n options.collection ??\n ((await ObjectRegistry.getCollection(descriptor.reportClassName, {\n db: options.db,\n })) as unknown as NonNullable<ReportQueryOptions['collection']>);\n let rows: Record<string, unknown>[] = [];\n let page: DataQueryResult['page'];\n let total: number;\n let facets: DataQueryResult['facets'];\n if (request.mode === 'rows') {\n const projection = request.projection ?? [descriptor.identityField];\n const select = projection.map((id) => fieldMap.get(id) ?? id);\n const offset = request.page?.kind === 'offset' ? request.page.offset : 0;\n const limit = request.page?.limit ?? 50;\n const orderBy = request.sort?.map(\n ({ field, direction }) =>\n `${fieldMap.get(field) ?? field} ${direction.toUpperCase()}`,\n );\n const materialized = await collection.list({\n select,\n offset,\n limit,\n orderBy: orderBy?.length === 1 ? orderBy[0] : orderBy,\n ...(where === undefined ? {} : { where }),\n });\n rows = materialized.map((row) =>\n mapMaterializedRow(row, projection, fieldMap),\n );\n rows.forEach((row) => {\n if (typeof row.id !== 'string' || row.id.length === 0) {\n throw new Error(\n 'Materialized report rows require a non-empty string id',\n );\n }\n });\n // SmrtReportCollection performs an eligible TTL refresh from list(). Count\n // must follow that operation so total/hasMore describe the same snapshot.\n total = await collection.count(where === undefined ? undefined : { where });\n page = {\n kind: 'offset',\n offset,\n limit,\n hasMore: offset + rows.length < total,\n };\n } else {\n // Count-only reads still enter SmrtReportCollection's list lifecycle to\n // refresh an eligible stale report before calculating its exact total.\n await collection.list({\n select: ['id'],\n offset: 0,\n limit: 1,\n orderBy: 'id ASC',\n ...(where === undefined ? {} : { where }),\n });\n total = await collection.count(where === undefined ? undefined : { where });\n if (request.mode === 'facets') {\n if (!collection.facets) {\n throw new Error(\n 'Report materialized facet reads require a collection facets() implementation',\n );\n }\n const requested = request.facets ?? [];\n const sourceFacets = await collection.facets({\n fields: requested.map((facet) => ({\n field: fieldMap.get(facet.field) ?? facet.field,\n limit: facet.limit,\n })),\n ...(where === undefined ? {} : { where }),\n });\n const byField = new Map(\n sourceFacets.map((facet) => [facet.field, facet]),\n );\n facets = requested.map((facet) => {\n const sourceField = fieldMap.get(facet.field) ?? facet.field;\n const values = byField.get(sourceField)?.values ?? [];\n return {\n field: facet.field,\n values: values.map((value) => ({\n value: value.value,\n count: value.count,\n })),\n // The collection deliberately bounds this database grouping query;\n // an exactly-full page may have more values, so report conservatively.\n truncated: values.length >= facet.limit,\n };\n });\n }\n }\n const result = {\n version: 1 as const,\n requestId: request.requestId,\n queryFingerprint,\n identityField: descriptor.identityField,\n rows,\n ...(page ? { page } : {}),\n total: { kind: 'exact' as const, value: total },\n ...(facets === undefined ? {} : { facets }),\n freshness: { state: 'unknown' as const },\n warnings: [],\n truncated: false,\n };\n const normalized = normalizeDataQueryResult(\n result,\n request,\n descriptor.schema,\n );\n if (!lifecycleBefore || !lifecycleOptions) {\n return { ...normalized, execution };\n }\n\n const lifecycleAfter = await getReportLifecycle(reportCtor, lifecycleOptions);\n return {\n ...normalized,\n execution,\n freshness: queryFreshness(lifecycleAfter),\n reportLifecycle: {\n snapshot: lifecycleAfter,\n read:\n lifecycleBefore.state !== 'current' &&\n lifecycleAfter.state === 'current'\n ? 'refresh-triggered'\n : lifecycleAfter.state === 'current'\n ? 'current'\n : 'stale',\n },\n };\n}\n\n/** Read the already-materialized primary key; never use a display/page index. */\nexport function reportMaterializedRowKey(row: Record<string, unknown>): string {\n const id = row.id;\n if (typeof id !== 'string' || id.length === 0) {\n throw new Error('Materialized report rows require a non-empty string id');\n }\n return id;\n}\n","import {\n ObjectRegistry,\n SmrtCollection,\n SmrtObject,\n type SmrtObjectOptions,\n} from '@happyvertical/smrt-core';\nimport { toSnakeCase } from '@happyvertical/smrt-core/utils';\nimport { getTenantId } from '@happyvertical/smrt-tenancy';\nimport { validateColumnName } from '@happyvertical/sql';\nimport { buildReportDefinition } from './compiler.js';\nimport { refreshReport } from './refresh.js';\nimport type { ReportRefreshOptions, ReportRefreshResult } from './types.js';\n\ntype RegistryField = {\n columnName?: string;\n _meta?: {\n columnName?: string;\n __tenancy?: { isTenantIdField?: boolean };\n };\n};\n\nfunction registryColumnName(fieldName: string, field?: RegistryField): string {\n return validateColumnName(\n field?.columnName ?? field?._meta?.columnName ?? toSnakeCase(fieldName),\n );\n}\n\nfunction findFieldColumn(\n fields: Map<string, RegistryField>,\n fieldName: string,\n): string {\n const direct = fields.get(fieldName);\n if (direct) return registryColumnName(fieldName, direct);\n const requestedColumn = toSnakeCase(fieldName);\n for (const [name, field] of fields.entries()) {\n if (toSnakeCase(name) === requestedColumn) {\n return registryColumnName(name, field);\n }\n }\n return validateColumnName(requestedColumn);\n}\n\nfunction findTenantColumn(\n fields: Map<string, RegistryField>,\n configuredField?: string,\n): string | null {\n if (configuredField) {\n return registryColumnName(configuredField, fields.get(configuredField));\n }\n for (const [fieldName, field] of fields.entries()) {\n if (fieldName === 'tenantId' || field?._meta?.__tenancy?.isTenantIdField) {\n return registryColumnName(fieldName, field);\n }\n }\n return null;\n}\n\nexport class SmrtReport extends SmrtObject {\n static readonly _isReportBase = true as const;\n\n refreshedAt: Date | null = null;\n\n constructor(options: SmrtObjectOptions = {}) {\n super(options);\n if (options.refreshedAt !== undefined) {\n this.refreshedAt =\n options.refreshedAt instanceof Date\n ? options.refreshedAt\n : options.refreshedAt\n ? new Date(options.refreshedAt)\n : null;\n }\n }\n\n isStale(ttlMs?: number): boolean {\n if (!this.refreshedAt) return true;\n if (ttlMs === undefined) return false;\n return Date.now() - this.refreshedAt.getTime() > ttlMs;\n }\n\n async refresh(\n options: Omit<ReportRefreshOptions, 'db'> = {},\n ): Promise<ReportRefreshResult> {\n const result = await refreshReport(this.constructor as typeof SmrtReport, {\n ...options,\n db: this.db,\n });\n this.refreshedAt = result.refreshedAt;\n return result;\n }\n}\n\nexport class SmrtReportCollection<\n ModelType extends SmrtReport,\n> extends SmrtCollection<ModelType> {\n private async refreshIfStale(): Promise<void> {\n const reportCtor = this.getItemClass();\n const definition = await buildReportDefinition(reportCtor);\n const refresh = definition.refresh;\n if (!refresh?.ttl || refresh.manual) return;\n\n const registered =\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name);\n const reportClass =\n registered?.qualifiedName ?? registered?.name ?? reportCtor.name;\n const tableName = ObjectRegistry.getTableName(reportClass);\n if (!tableName) return;\n\n const fields = (await ObjectRegistry.getAllFields(reportClass)) as Map<\n string,\n RegistryField\n >;\n const safeTableName = validateColumnName(tableName);\n const refreshedAtColumn = findFieldColumn(fields, 'refreshedAt');\n const tenantColumn = findTenantColumn(\n fields,\n registered?.tenantScopedConfig?.field,\n );\n const tenantId = getTenantId() ?? null;\n const result = tenantColumn\n ? await this.db.query(\n `SELECT MAX(${refreshedAtColumn}) AS refreshed_at FROM ${safeTableName} WHERE ${tenantColumn} ${tenantId ? '= ?' : 'IS NULL'}`,\n ...(tenantId ? [tenantId] : []),\n )\n : await this.db.query(\n `SELECT MAX(${refreshedAtColumn}) AS refreshed_at FROM ${safeTableName}`,\n );\n const refreshedAt = result.rows[0]?.refreshed_at\n ? new Date(result.rows[0].refreshed_at as string)\n : null;\n const stale =\n !refreshedAt || Date.now() - refreshedAt.getTime() > refresh.ttl;\n if (!stale) return;\n\n await refreshReport(reportCtor, {\n db: this.db,\n mode: refresh.mode ?? 'rebuild',\n trigger: 'ttl',\n tenantId,\n });\n }\n\n async refresh(\n options: Omit<ReportRefreshOptions, 'db'> = {},\n ): Promise<ReportRefreshResult> {\n return refreshReport(this.getItemClass(), {\n ...options,\n db: this.db,\n });\n }\n\n override async list(\n options: Parameters<SmrtCollection<ModelType>['list']>[0] = {},\n ): Promise<ModelType[]> {\n await this.refreshIfStale();\n return super.list(options);\n }\n\n override async get(\n filter: Parameters<SmrtCollection<ModelType>['get']>[0],\n options: Parameters<SmrtCollection<ModelType>['get']>[1] = {},\n ): Promise<ModelType | null> {\n await this.refreshIfStale();\n return super.get(filter, options);\n }\n}\n","/**\n * Saved report-view and export contracts.\n *\n * This module owns only the serializable report boundary. Applications own\n * durable view storage, principal binding, authorization, auditing, artifact\n * storage, download tokens, and background-job execution.\n */\nimport { createHash } from 'node:crypto';\nimport {\n createDataQueryFingerprint,\n normalizeDataQueryRequest,\n} from '@happyvertical/smrt-core';\nimport type {\n DataQueryFreshness,\n DataQueryRequest,\n DataQueryResult,\n DataQuerySort,\n} from '@happyvertical/smrt-types';\nimport {\n type ReportAdapterDescriptor,\n type ReportColumnCapability,\n type ReportDataQueryResult,\n splitReportFilterScopes,\n} from './adapter.js';\n\nconst INHERITED_REPORT_SCOPE = [\n 'principal',\n 'tenant',\n 'report-definition',\n 'field-policy',\n] as const;\n\nconst EXPORT_FORMATS = new Set<ReportExportFormat>(['csv', 'json']);\nconst PINNED_COLUMN_SIDES = new Set<ReportSavedViewPinnedSide>([\n 'start',\n 'end',\n]);\nconst DISPLAY_DENSITIES = new Set<ReportSavedViewDisplay['density']>([\n 'compact',\n 'comfortable',\n]);\nconst RFC_3339_INSTANT =\n /^(\\d{4})-(\\d{2})-(\\d{2})T([01]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)(?:\\.\\d{1,9})?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$/;\n\nconst DEFAULT_EXPORT_LIMITS: ReportExportLimits = {\n maxRows: 100_000,\n maxBytes: 25_000_000,\n deadlineMs: 60_000,\n foregroundRowLimit: 1_000,\n};\n\nconst MAX_EXPORT_LIMITS = {\n maxRows: 1_000_000,\n maxBytes: 100_000_000,\n deadlineMs: 300_000,\n foregroundRowLimit: 100_000,\n} as const;\n\nexport class ReportSurfaceValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ReportSurfaceValidationError';\n }\n}\n\nexport type ReportSavedViewPinnedSide = 'start' | 'end';\n\n/** A view's column order is its array order; ids are always adapter-defined. */\nexport interface ReportSavedViewColumn {\n id: string;\n width?: number;\n pinned?: ReportSavedViewPinnedSide;\n}\n\nexport interface ReportSavedViewGrouping {\n fields: string[];\n expanded?: boolean;\n}\n\nexport interface ReportSavedViewDisplay {\n density: 'compact' | 'comfortable';\n showTotals?: boolean;\n}\n\n/**\n * A normalized, policy-neutral stored view. The storage host associates it\n * with its owner and tenant; neither identity is accepted here.\n */\nexport interface ReportSavedView {\n version: 1;\n id: string;\n title: string;\n resourceId: string;\n reportClassName: string;\n definitionFingerprint: string;\n query: DataQueryRequest;\n columns: ReportSavedViewColumn[];\n grouping?: ReportSavedViewGrouping;\n display?: ReportSavedViewDisplay;\n}\n\nexport type ReportExportFormat = 'csv' | 'json';\n\n/**\n * Opaque identity of a host-captured materialization snapshot. The application\n * creates and verifies it while bound to its principal and tenant; callers\n * cannot turn an `asOf` label into a snapshot by supplying this id alone.\n */\nexport interface ReportExportSnapshotBinding {\n id: string;\n}\n\nexport interface ReportExportSnapshot {\n version: 1;\n resourceId: string;\n reportClassName: string;\n definitionFingerprint: string;\n binding: ReportExportSnapshotBinding;\n request: DataQueryRequest;\n queryFingerprint: string;\n projection: string[];\n sort: DataQuerySort[];\n freshness: DataQueryFreshness;\n snapshot: {\n asOf: string;\n refreshedAt?: string;\n stale: boolean;\n };\n total: {\n kind: 'exact';\n value: number;\n };\n inherits: Array<(typeof INHERITED_REPORT_SCOPE)[number]>;\n}\n\n/** Every export is bounded before it is streamed or delegated to a worker. */\nexport interface ReportExportLimits {\n maxRows: number;\n maxBytes: number;\n deadlineMs: number;\n /** Exports above this exact row count must use the application queue. */\n foregroundRowLimit: number;\n}\n\n/**\n * A deterministic offset-page plan over the immutable host snapshot. A worker\n * starts at offset zero and advances by `page.limit` until `rowCount` rows have\n * been produced or the byte/deadline limits are reached.\n */\nexport interface ReportExportReadPlan {\n snapshotId: string;\n queryFingerprint: string;\n asOf: string;\n page: {\n kind: 'offset';\n offset: 0;\n limit: number;\n };\n}\n\nexport interface ReportExportRequest {\n version: 1;\n format: ReportExportFormat;\n execution: 'stream' | 'background';\n snapshot: ReportExportSnapshot;\n limits: ReportExportLimits;\n read: ReportExportReadPlan;\n rowCount: number;\n truncated: boolean;\n action: {\n id: 'export';\n requiredPermission: string;\n auditRequired: true;\n confirmationRequired: boolean;\n };\n}\n\n/**\n * An authority-free job handoff. The queue must re-enter the bound principal\n * and tenant, resolve the current report definition/field policy, and verify\n * this snapshot before materializing an artifact.\n */\nexport interface ReportBackgroundExportTask {\n version: 1;\n execution: 'background';\n request: ReportExportRequest;\n inherits: Array<(typeof INHERITED_REPORT_SCOPE)[number]>;\n}\n\nexport interface ReportExportProgress {\n state: 'queued' | 'running' | 'completed' | 'failed';\n rowCount: number;\n byteCount: number;\n truncated: boolean;\n}\n\n/**\n * Artifact metadata is deliberately URL- and token-free. An application-owned\n * serving boundary must authorize every download using the caller's current\n * principal and tenant before it reveals any artifact location.\n */\nexport interface ReportExportArtifact {\n id: string;\n request: ReportExportRequest;\n progress: ReportExportProgress;\n expiresAt: string;\n}\n\nexport interface ReportExportActionContext {\n phase: 'preview' | 'apply';\n reportClassName: string;\n resourceId: string;\n requiredPermission: string;\n confirmationRequired: boolean;\n execution: 'stream' | 'background';\n queryFingerprint: string;\n definitionFingerprint: string;\n snapshotId: string;\n asOf: string;\n pageSize: number;\n}\n\n/** The same host is used by human and agent callers. */\nexport interface ReportExportActionHost {\n /**\n * Prove that this opaque binding still resolves to the exact immutable\n * materialization snapshot under the host's current principal and tenant.\n * The background worker must call `validateReportExportExecution()` again\n * before reading or serving any rows.\n */\n assertSnapshot(context: ReportExportSnapshotContext): Promise<void> | void;\n authorize(context: ReportExportActionContext): Promise<void> | void;\n audit(context: ReportExportActionContext): Promise<void> | void;\n enqueue?(\n task: ReportBackgroundExportTask,\n ): Promise<{ taskId: string }> | { taskId: string };\n}\n\nexport interface ReportExportSnapshotContext {\n bindingId: string;\n resourceId: string;\n reportClassName: string;\n definitionFingerprint: string;\n queryFingerprint: string;\n asOf: string;\n}\n\nexport interface ReportExportPreview {\n phase: 'preview';\n action: ReportExportActionContext;\n request: ReportExportRequest;\n}\n\nexport type AppliedReportExport =\n | {\n phase: 'apply';\n execution: 'stream';\n status: 'ready';\n request: ReportExportRequest;\n }\n | {\n phase: 'apply';\n execution: 'background';\n status: 'queued';\n taskId: string;\n request: ReportExportRequest;\n };\n\ninterface SavedViewInput {\n version?: unknown;\n id: unknown;\n title: unknown;\n resourceId?: unknown;\n reportClassName?: unknown;\n definitionFingerprint?: unknown;\n query: unknown;\n columns?: unknown;\n grouping?: unknown;\n display?: unknown;\n}\n\ninterface ExportRequestInput {\n format: unknown;\n limits?: unknown;\n}\n\nfunction fail(message: string): never {\n throw new ReportSurfaceValidationError(message);\n}\n\nfunction plainObject(value: unknown, label: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return fail(`${label} must be a plain object`);\n }\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) {\n return fail(`${label} must be a plain object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction exactKeys(\n value: Record<string, unknown>,\n allowed: readonly string[],\n label: string,\n): void {\n for (const key of Object.keys(value)) {\n if (!allowed.includes(key)) {\n fail(`${label} contains an unsupported field: ${key}`);\n }\n }\n}\n\nfunction requiredString(\n value: unknown,\n label: string,\n maxLength = 256,\n): string {\n if (typeof value !== 'string') return fail(`${label} must be a string`);\n const normalized = value.trim();\n if (!normalized) return fail(`${label} cannot be empty`);\n if (normalized.length > maxLength) {\n return fail(`${label} cannot exceed ${String(maxLength)} characters`);\n }\n return normalized;\n}\n\nfunction optionalBoolean(value: unknown, label: string): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'boolean') return fail(`${label} must be a boolean`);\n return value;\n}\n\nfunction boundedInteger(\n value: unknown,\n label: string,\n minimum: number,\n maximum: number,\n): number {\n if (!Number.isInteger(value) || (value as number) < minimum) {\n return fail(`${label} must be an integer of at least ${String(minimum)}`);\n }\n if ((value as number) > maximum) {\n return fail(`${label} cannot exceed ${String(maximum)}`);\n }\n return value as number;\n}\n\nfunction normalizeIso(value: unknown, label: string): string {\n const text = requiredString(value, label, 64);\n const match = RFC_3339_INSTANT.exec(text);\n if (!match) return fail(`${label} must be an RFC 3339 instant`);\n const [year, month, day] = match.slice(1, 4).map(Number);\n const calendar = new Date(0);\n calendar.setUTCFullYear(year, month - 1, day);\n calendar.setUTCHours(0, 0, 0, 0);\n if (\n calendar.getUTCFullYear() !== year ||\n calendar.getUTCMonth() !== month - 1 ||\n calendar.getUTCDate() !== day\n ) {\n return fail(`${label} must be an RFC 3339 instant`);\n }\n const parsed = new Date(text);\n if (Number.isNaN(parsed.getTime())) {\n return fail(`${label} must be an RFC 3339 instant`);\n }\n return parsed.toISOString();\n}\n\nfunction normalizeSnapshotBinding(value: unknown): ReportExportSnapshotBinding {\n const input = plainObject(value, 'Report export snapshot binding');\n exactKeys(input, ['id'], 'Report export snapshot binding');\n return {\n id: requiredString(input.id, 'Report export snapshot binding id'),\n };\n}\n\nfunction stable(value: unknown): string {\n if (\n value === null ||\n typeof value === 'boolean' ||\n typeof value === 'number'\n ) {\n return JSON.stringify(value);\n }\n if (typeof value === 'string') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`;\n const object = plainObject(value, 'Report surface value');\n return (\n '{' +\n Object.keys(object)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${stable(object[key])}`)\n .join(',') +\n '}'\n );\n}\n\n/**\n * A change to report fields, their capabilities, schema, scope, or source\n * invalidates a stored view/export snapshot rather than silently widening it.\n */\nexport function reportDefinitionFingerprint(\n descriptor: ReportAdapterDescriptor,\n): string {\n const definition = {\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n sourceClassName: descriptor.sourceClassName,\n tenantScoped: descriptor.tenantScoped,\n tenantField: descriptor.tenantField ?? null,\n identityField: descriptor.identityField,\n columns: descriptor.columns.map((column) => ({\n id: column.id,\n fieldName: column.fieldName,\n kind: column.kind,\n filterScope: column.filterScope,\n type: column.type,\n projectable: column.projectable === true,\n sortable: column.sortable === true,\n facetable: column.facetable === true,\n filterOperators: column.filterOperators ?? [],\n capabilities: [...column.capabilities].sort(),\n sensitivity: column.sensitivity ?? null,\n sourceColumn: column.sourceColumn ?? null,\n bucket: column.bucket ?? null,\n aggregate: column.aggregate ?? null,\n distinct: column.distinct ?? null,\n })),\n schema: descriptor.schema,\n };\n return (\n 'rv1_' +\n createHash('sha256').update(stable(definition), 'utf8').digest('base64url')\n );\n}\n\nfunction columnMap(\n descriptor: ReportAdapterDescriptor,\n): Map<string, ReportAdapterDescriptor['columns'][number]> {\n return new Map(descriptor.columns.map((column) => [column.id, column]));\n}\n\nfunction requireColumnCapability(\n descriptor: ReportAdapterDescriptor,\n id: unknown,\n capability: ReportColumnCapability,\n label: string,\n): string {\n const columnId = requiredString(id, label);\n const column = columnMap(descriptor).get(columnId);\n if (!column?.capabilities.includes(capability)) {\n return fail(`${label} is not available under the current report policy`);\n }\n return columnId;\n}\n\nfunction normalizeColumns(\n descriptor: ReportAdapterDescriptor,\n value: unknown,\n): ReportSavedViewColumn[] {\n if (value === undefined) {\n return descriptor.columns\n .filter((column) => column.capabilities.includes('project'))\n .map((column) => ({ id: column.id }));\n }\n if (!Array.isArray(value))\n return fail('Saved report view columns must be an array');\n if (value.length > descriptor.columns.length) {\n return fail(\n 'Saved report view columns cannot repeat or exceed exposed columns',\n );\n }\n const ids = new Set<string>();\n return value.map((entry, index) => {\n const input = plainObject(\n entry,\n `Saved report view columns[${String(index)}]`,\n );\n const id = requireColumnCapability(\n descriptor,\n input.id,\n 'project',\n `Saved report view columns[${String(index)}].id`,\n );\n if (ids.has(id)) return fail('Saved report view columns cannot repeat ids');\n ids.add(id);\n const width =\n input.width === undefined\n ? undefined\n : boundedInteger(\n input.width,\n `Saved report view columns[${String(index)}].width`,\n 40,\n 2_000,\n );\n const pinned =\n input.pinned === undefined\n ? undefined\n : requiredString(\n input.pinned,\n `Saved report view columns[${String(index)}].pinned`,\n 16,\n );\n if (\n pinned !== undefined &&\n !PINNED_COLUMN_SIDES.has(pinned as ReportSavedViewPinnedSide)\n ) {\n return fail(\n `Saved report view columns[${String(index)}].pinned is invalid`,\n );\n }\n return {\n id,\n ...(width === undefined ? {} : { width }),\n ...(pinned === undefined\n ? {}\n : { pinned: pinned as ReportSavedViewPinnedSide }),\n };\n });\n}\n\nfunction normalizeGrouping(\n descriptor: ReportAdapterDescriptor,\n value: unknown,\n): ReportSavedViewGrouping | undefined {\n if (value === undefined) return undefined;\n const input = plainObject(value, 'Saved report view grouping');\n if (!Array.isArray(input.fields)) {\n return fail('Saved report view grouping.fields must be an array');\n }\n const fields = input.fields.map((field, index) =>\n requireColumnCapability(\n descriptor,\n field,\n 'group',\n `Saved report view grouping.fields[${String(index)}]`,\n ),\n );\n if (new Set(fields).size !== fields.length) {\n return fail('Saved report view grouping.fields cannot repeat ids');\n }\n const expanded = optionalBoolean(\n input.expanded,\n 'Saved report view grouping.expanded',\n );\n return {\n fields,\n ...(expanded === undefined ? {} : { expanded }),\n };\n}\n\nfunction normalizeDisplay(value: unknown): ReportSavedViewDisplay | undefined {\n if (value === undefined) return undefined;\n const input = plainObject(value, 'Saved report view display');\n const density = requiredString(\n input.density,\n 'Saved report view display.density',\n 16,\n );\n if (!DISPLAY_DENSITIES.has(density as ReportSavedViewDisplay['density'])) {\n return fail('Saved report view display.density is invalid');\n }\n const showTotals = optionalBoolean(\n input.showTotals,\n 'Saved report view display.showTotals',\n );\n return {\n density: density as ReportSavedViewDisplay['density'],\n ...(showTotals === undefined ? {} : { showTotals }),\n };\n}\n\n/**\n * Version 0 is the original unversioned persisted layout shape. It contains\n * the same allowlisted fields as v1, so the migration only records the schema\n * version; current descriptor normalization still revalidates every field.\n */\nexport function migrateReportSavedView(value: unknown): SavedViewInput {\n const input = plainObject(\n value,\n 'Saved report view',\n ) as unknown as SavedViewInput;\n if (input.version === undefined || input.version === 0) {\n return { ...input, version: 1 };\n }\n if (input.version === 1) return input;\n return fail('Saved report view version is unsupported');\n}\n\n/**\n * Normalize a view both when it is saved and when it is restored. Re-running\n * this against the live descriptor is what prevents a former field/action\n * grant from surviving a policy or definition change.\n */\nexport function normalizeReportSavedView(\n descriptor: ReportAdapterDescriptor,\n value: unknown,\n): ReportSavedView {\n const input = migrateReportSavedView(value);\n const fingerprint = reportDefinitionFingerprint(descriptor);\n if (\n input.resourceId !== undefined &&\n input.resourceId !== descriptor.resourceId\n ) {\n return fail('Saved report view belongs to a different report resource');\n }\n if (\n input.reportClassName !== undefined &&\n input.reportClassName !== descriptor.reportClassName\n ) {\n return fail('Saved report view belongs to a different report class');\n }\n if (\n input.definitionFingerprint !== undefined &&\n input.definitionFingerprint !== fingerprint\n ) {\n return fail('Saved report view definition has changed');\n }\n const query = normalizeDataQueryRequest(input.query, descriptor.schema);\n if (query.mode !== 'rows') {\n return fail('Saved report views must use a rows query');\n }\n splitReportFilterScopes(descriptor, query.filter);\n const columns = normalizeColumns(descriptor, input.columns);\n const grouping = normalizeGrouping(descriptor, input.grouping);\n const display = normalizeDisplay(input.display);\n return {\n version: 1,\n id: requiredString(input.id, 'Saved report view id'),\n title: requiredString(input.title, 'Saved report view title', 240),\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n definitionFingerprint: fingerprint,\n query,\n columns,\n ...(grouping === undefined ? {} : { grouping }),\n ...(display === undefined ? {} : { display }),\n };\n}\n\n/** Restore through current policy rather than trusting a stored JSON record. */\nexport function restoreReportSavedView(\n descriptor: ReportAdapterDescriptor,\n savedView: ReportSavedView,\n): ReportSavedView {\n return normalizeReportSavedView(descriptor, savedView);\n}\n\n/**\n * Freeze a previously executed materialized read for export. The caller must\n * supply the exact request and a result that exposes lifecycle freshness; an\n * export never invents an as-of value or accepts a caller-supplied tenant.\n */\nexport function createReportExportSnapshot(\n descriptor: ReportAdapterDescriptor,\n input: unknown,\n result: Pick<\n DataQueryResult,\n 'queryFingerprint' | 'identityField' | 'total' | 'freshness' | 'truncated'\n > &\n Pick<ReportDataQueryResult, 'reportLifecycle'>,\n binding: unknown,\n): ReportExportSnapshot {\n const request = normalizeDataQueryRequest(input, descriptor.schema);\n if (request.mode !== 'rows') {\n return fail('Report export snapshots require a rows query');\n }\n const queryFingerprint = createDataQueryFingerprint(\n request,\n descriptor.schema,\n );\n if (result.queryFingerprint !== queryFingerprint) {\n return fail(\n 'Report export result does not match the requested query fingerprint',\n );\n }\n if (result.identityField !== descriptor.identityField) {\n return fail('Report export result has an unexpected row identity field');\n }\n if (result.total.kind !== 'exact') {\n return fail('Report export snapshots require an exact row count');\n }\n if (!Number.isSafeInteger(result.total.value) || result.total.value < 0) {\n return fail(\n 'Report export snapshots require a safe non-negative row count',\n );\n }\n if (result.truncated) {\n return fail('Report export snapshots cannot use a truncated query result');\n }\n if (result.freshness.state === 'unknown') {\n return fail('Report export snapshots require lifecycle freshness');\n }\n const lifecycleAsOf = result.reportLifecycle?.snapshot.asOf;\n const freshnessAsOf = result.freshness.asOf;\n const normalizedLifecycleAsOf = lifecycleAsOf\n ? normalizeIso(lifecycleAsOf, 'Report export lifecycle snapshot asOf')\n : undefined;\n const normalizedFreshnessAsOf = freshnessAsOf\n ? normalizeIso(freshnessAsOf, 'Report export freshness asOf')\n : undefined;\n if (\n normalizedLifecycleAsOf !== undefined &&\n normalizedFreshnessAsOf !== undefined &&\n normalizedLifecycleAsOf !== normalizedFreshnessAsOf\n ) {\n return fail(\n 'Report export lifecycle freshness does not match its snapshot',\n );\n }\n const normalizedAsOf = normalizedLifecycleAsOf ?? normalizedFreshnessAsOf;\n if (!normalizedAsOf) {\n return fail(\n 'Report export snapshots require an as-of materialization instant',\n );\n }\n const refreshedAt = result.reportLifecycle?.snapshot.refreshedAt;\n return {\n version: 1,\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n definitionFingerprint: reportDefinitionFingerprint(descriptor),\n binding: normalizeSnapshotBinding(binding),\n request,\n queryFingerprint,\n projection: [...(request.projection ?? [descriptor.identityField])],\n sort: [\n ...(request.sort ??\n descriptor.schema.defaultSort ?? [\n { field: descriptor.identityField, direction: 'asc' },\n ]),\n ],\n freshness: {\n state: result.freshness.state,\n asOf: normalizedAsOf,\n },\n snapshot: {\n asOf: normalizedAsOf,\n ...(refreshedAt\n ? {\n refreshedAt: normalizeIso(\n refreshedAt,\n 'Report export snapshot refreshedAt',\n ),\n }\n : {}),\n stale: result.freshness.state === 'stale',\n },\n total: { kind: 'exact', value: result.total.value },\n inherits: [...INHERITED_REPORT_SCOPE],\n };\n}\n\n/** Reject definitions/policies that changed after a snapshot was frozen. */\nexport function verifyReportExportSnapshot(\n descriptor: ReportAdapterDescriptor,\n snapshot: ReportExportSnapshot,\n): ReportExportSnapshot {\n const input = plainObject(snapshot, 'Report export snapshot');\n exactKeys(\n input,\n [\n 'version',\n 'resourceId',\n 'reportClassName',\n 'definitionFingerprint',\n 'binding',\n 'request',\n 'queryFingerprint',\n 'projection',\n 'sort',\n 'freshness',\n 'snapshot',\n 'total',\n 'inherits',\n ],\n 'Report export snapshot',\n );\n if (snapshot.version !== 1)\n return fail('Report export snapshot version is unsupported');\n if (\n snapshot.resourceId !== descriptor.resourceId ||\n snapshot.reportClassName !== descriptor.reportClassName\n ) {\n return fail('Report export snapshot belongs to a different report');\n }\n if (\n snapshot.definitionFingerprint !== reportDefinitionFingerprint(descriptor)\n ) {\n return fail('Report export definition has changed');\n }\n const binding = normalizeSnapshotBinding(snapshot.binding);\n const normalized = normalizeDataQueryRequest(\n snapshot.request,\n descriptor.schema,\n );\n if (normalized.mode !== 'rows') {\n return fail('Report export snapshot query must remain a rows query');\n }\n if (\n createDataQueryFingerprint(normalized, descriptor.schema) !==\n snapshot.queryFingerprint\n ) {\n return fail('Report export snapshot query fingerprint is invalid');\n }\n const expectedProjection = normalized.projection ?? [\n descriptor.identityField,\n ];\n const expectedSort = normalized.sort ??\n descriptor.schema.defaultSort ?? [\n { field: descriptor.identityField, direction: 'asc' },\n ];\n if (\n !Array.isArray(snapshot.projection) ||\n snapshot.projection.some((field) => typeof field !== 'string')\n ) {\n return fail('Report export snapshot projection is invalid');\n }\n if (\n snapshot.projection.length !== expectedProjection.length ||\n snapshot.projection.some(\n (field, index) => field !== expectedProjection[index],\n )\n ) {\n return fail('Report export snapshot projection or sort has changed');\n }\n if (!Array.isArray(snapshot.sort)) {\n return fail('Report export snapshot sort is invalid');\n }\n const sort = snapshot.sort.map((value) => {\n const sortInput = plainObject(value, 'Report export snapshot sort');\n exactKeys(sortInput, ['field', 'direction'], 'Report export snapshot sort');\n return {\n field: requiredString(\n sortInput.field,\n 'Report export snapshot sort field',\n ),\n direction: requiredString(\n sortInput.direction,\n 'Report export snapshot sort direction',\n ),\n };\n });\n if (stable(sort) !== stable(expectedSort)) {\n return fail('Report export snapshot projection or sort has changed');\n }\n const freshness = plainObject(\n snapshot.freshness,\n 'Report export snapshot freshness',\n );\n exactKeys(freshness, ['state', 'asOf'], 'Report export snapshot freshness');\n const materialization = plainObject(\n snapshot.snapshot,\n 'Report export snapshot materialization',\n );\n exactKeys(\n materialization,\n ['asOf', 'refreshedAt', 'stale'],\n 'Report export snapshot materialization',\n );\n const total = plainObject(snapshot.total, 'Report export snapshot total');\n exactKeys(total, ['kind', 'value'], 'Report export snapshot total');\n const totalValue = total.value;\n const asOf = normalizeIso(\n materialization.asOf,\n 'Report export snapshot asOf',\n );\n if (freshness.state !== 'fresh' && freshness.state !== 'stale') {\n return fail('Report export snapshot freshness state is invalid');\n }\n const freshnessAsOf = normalizeIso(\n freshness.asOf,\n 'Report export snapshot freshness asOf',\n );\n if (freshnessAsOf !== asOf) {\n return fail(\n 'Report export snapshot freshness does not match its as-of instant',\n );\n }\n const refreshedAt = materialization.refreshedAt\n ? normalizeIso(\n materialization.refreshedAt,\n 'Report export snapshot refreshedAt',\n )\n : undefined;\n if (materialization.stale !== (freshness.state === 'stale')) {\n return fail('Report export snapshot stale state is invalid');\n }\n if (\n total.kind !== 'exact' ||\n typeof totalValue !== 'number' ||\n !Number.isSafeInteger(totalValue) ||\n totalValue < 0\n ) {\n return fail('Report export snapshot row count is invalid');\n }\n if (\n !Array.isArray(snapshot.inherits) ||\n snapshot.inherits.some((value) => typeof value !== 'string') ||\n snapshot.inherits.length !== INHERITED_REPORT_SCOPE.length ||\n snapshot.inherits.some(\n (value, index) => value !== INHERITED_REPORT_SCOPE[index],\n )\n ) {\n return fail('Report export snapshot inheritance contract is invalid');\n }\n return {\n version: 1,\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n definitionFingerprint: reportDefinitionFingerprint(descriptor),\n binding,\n request: normalized,\n queryFingerprint: createDataQueryFingerprint(normalized, descriptor.schema),\n projection: [...expectedProjection],\n sort: expectedSort.map(({ field, direction }) => ({ field, direction })),\n freshness: { state: freshness.state, asOf: freshnessAsOf },\n snapshot: {\n asOf,\n stale: materialization.stale,\n ...(refreshedAt === undefined ? {} : { refreshedAt }),\n },\n total: { kind: 'exact', value: totalValue },\n inherits: [...INHERITED_REPORT_SCOPE],\n };\n}\n\nfunction normalizeExportLimits(value: unknown): ReportExportLimits {\n if (value === undefined) return { ...DEFAULT_EXPORT_LIMITS };\n const input = plainObject(value, 'Report export limits');\n const maxRows = boundedInteger(\n input.maxRows ?? DEFAULT_EXPORT_LIMITS.maxRows,\n 'Report export limits.maxRows',\n 1,\n MAX_EXPORT_LIMITS.maxRows,\n );\n const maxBytes = boundedInteger(\n input.maxBytes ?? DEFAULT_EXPORT_LIMITS.maxBytes,\n 'Report export limits.maxBytes',\n 1,\n MAX_EXPORT_LIMITS.maxBytes,\n );\n const deadlineMs = boundedInteger(\n input.deadlineMs ?? DEFAULT_EXPORT_LIMITS.deadlineMs,\n 'Report export limits.deadlineMs',\n 1,\n MAX_EXPORT_LIMITS.deadlineMs,\n );\n const foregroundRowLimit = boundedInteger(\n input.foregroundRowLimit ??\n Math.min(DEFAULT_EXPORT_LIMITS.foregroundRowLimit, maxRows),\n 'Report export limits.foregroundRowLimit',\n 1,\n Math.min(maxRows, MAX_EXPORT_LIMITS.foregroundRowLimit),\n );\n return { maxRows, maxBytes, deadlineMs, foregroundRowLimit };\n}\n\nfunction hasSensitiveProjection(\n descriptor: ReportAdapterDescriptor,\n snapshot: ReportExportSnapshot,\n): boolean {\n const columns = columnMap(descriptor);\n return snapshot.projection.some((id) => {\n const sensitivity = columns.get(id)?.sensitivity;\n return (\n sensitivity === 'personal' ||\n sensitivity === 'sensitive' ||\n sensitivity === 'secret'\n );\n });\n}\n\nfunction createReadPlan(\n descriptor: ReportAdapterDescriptor,\n snapshot: ReportExportSnapshot,\n limits: ReportExportLimits,\n): ReportExportReadPlan {\n return {\n snapshotId: snapshot.binding.id,\n queryFingerprint: snapshot.queryFingerprint,\n asOf: snapshot.snapshot.asOf,\n page: {\n kind: 'offset',\n offset: 0,\n limit: Math.min(\n limits.maxRows,\n descriptor.schema.maxPageLimit ??\n DEFAULT_EXPORT_LIMITS.foregroundRowLimit,\n ),\n },\n };\n}\n\n/**\n * Build one common request for both agent and human export clients. The\n * resulting request carries no actor, tenant, storage location, or token.\n */\nexport function createReportExportRequest(\n descriptor: ReportAdapterDescriptor,\n snapshot: ReportExportSnapshot,\n value: unknown,\n): ReportExportRequest {\n const input = plainObject(\n value,\n 'Report export request',\n ) as unknown as ExportRequestInput;\n const verified = verifyReportExportSnapshot(descriptor, snapshot);\n const format = requiredString(input.format, 'Report export format', 16);\n if (!EXPORT_FORMATS.has(format as ReportExportFormat)) {\n return fail('Report export format is unsupported');\n }\n const limits = normalizeExportLimits(input.limits);\n const rowCount = Math.min(verified.total.value, limits.maxRows);\n const confirmationRequired = hasSensitiveProjection(descriptor, verified);\n return {\n version: 1,\n format: format as ReportExportFormat,\n execution:\n verified.total.value > limits.foregroundRowLimit\n ? 'background'\n : 'stream',\n snapshot: verified,\n limits,\n read: createReadPlan(descriptor, verified, limits),\n rowCount,\n truncated: verified.total.value > limits.maxRows,\n action: {\n id: 'export',\n requiredPermission: 'reports.export',\n auditRequired: true,\n confirmationRequired,\n },\n };\n}\n\n/**\n * Recreate the deterministic request from its current descriptor and frozen\n * snapshot. Preview, apply, queue, and serving hosts call this instead of\n * trusting browser- or agent-supplied action metadata.\n */\nexport function validateReportExportRequest(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n): ReportExportRequest {\n const expected = createReportExportRequest(descriptor, request.snapshot, {\n format: request.format,\n limits: request.limits,\n });\n if (stable(request) !== stable(expected)) {\n return fail('Report export request does not match current policy');\n }\n return expected;\n}\n\n/**\n * Build one bounded page of the immutable export read. The host applies the\n * opaque snapshot binding separately; this function preserves the frozen\n * projection, predicate, and deterministic sort while replacing only visible\n * pagination. A zero-row export therefore has no pages to execute.\n */\nexport function createReportExportPageRequest(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n offset: number,\n): DataQueryRequest {\n const verified = validateReportExportRequest(descriptor, request);\n if (\n !Number.isSafeInteger(offset) ||\n offset < 0 ||\n offset >= verified.rowCount\n ) {\n return fail('Report export page offset is outside the bounded export');\n }\n const limit = Math.min(verified.read.page.limit, verified.rowCount - offset);\n return normalizeDataQueryRequest(\n {\n ...verified.snapshot.request,\n page: { kind: 'offset', offset, limit },\n },\n descriptor.schema,\n );\n}\n\nfunction actionContext(\n phase: ReportExportActionContext['phase'],\n request: ReportExportRequest,\n): ReportExportActionContext {\n return {\n phase,\n reportClassName: request.snapshot.reportClassName,\n resourceId: request.snapshot.resourceId,\n requiredPermission: request.action.requiredPermission,\n confirmationRequired: request.action.confirmationRequired,\n execution: request.execution,\n queryFingerprint: request.snapshot.queryFingerprint,\n definitionFingerprint: request.snapshot.definitionFingerprint,\n snapshotId: request.read.snapshotId,\n asOf: request.read.asOf,\n pageSize: request.read.page.limit,\n };\n}\n\nfunction snapshotContext(\n request: ReportExportRequest,\n): ReportExportSnapshotContext {\n return {\n bindingId: request.snapshot.binding.id,\n resourceId: request.snapshot.resourceId,\n reportClassName: request.snapshot.reportClassName,\n definitionFingerprint: request.snapshot.definitionFingerprint,\n queryFingerprint: request.snapshot.queryFingerprint,\n asOf: request.snapshot.snapshot.asOf,\n };\n}\n\n/**\n * Revalidate the request and prove the opaque snapshot binding through the\n * application host. Preview, apply, and every background worker use this same\n * operation before reading or serving export data.\n */\nexport async function validateReportExportExecution(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n host: Pick<ReportExportActionHost, 'assertSnapshot'>,\n): Promise<ReportExportRequest> {\n const verified = validateReportExportRequest(descriptor, request);\n await host.assertSnapshot(snapshotContext(verified));\n return verified;\n}\n\nexport async function previewReportExport(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n host: ReportExportActionHost,\n): Promise<ReportExportPreview> {\n const initial = validateReportExportRequest(descriptor, request);\n const action = actionContext('preview', initial);\n await host.authorize(action);\n const verified = await validateReportExportExecution(\n descriptor,\n initial,\n host,\n );\n await host.audit(action);\n return { phase: 'preview', action, request: verified };\n}\n\n/**\n * Queue large exports without embedding rows in a browser/agent response.\n * A host must verify current principal, tenant, report definition, and field\n * policy again before it renders or serves the artifact.\n */\nexport async function applyReportExport(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n host: ReportExportActionHost,\n options: { confirmed?: boolean } = {},\n): Promise<AppliedReportExport> {\n const initial = validateReportExportRequest(descriptor, request);\n if (initial.action.confirmationRequired && options.confirmed !== true) {\n return fail('Sensitive report exports require explicit confirmation');\n }\n const action = actionContext('apply', initial);\n await host.authorize(action);\n const verified = await validateReportExportExecution(\n descriptor,\n initial,\n host,\n );\n await host.audit(action);\n if (verified.execution === 'stream') {\n return {\n phase: 'apply',\n execution: 'stream',\n status: 'ready',\n request: verified,\n };\n }\n if (!host.enqueue) {\n return fail('Background report exports require an application queue host');\n }\n const queued = await host.enqueue({\n version: 1,\n execution: 'background',\n request: verified,\n inherits: [...INHERITED_REPORT_SCOPE],\n });\n if (typeof queued.taskId !== 'string' || queued.taskId.trim().length === 0) {\n return fail('Background report export hosts must return a task id');\n }\n return {\n phase: 'apply',\n execution: 'background',\n status: 'queued',\n taskId: queued.taskId,\n request: verified,\n };\n}\n\n/**\n * Validate artifact metadata before a host serves it. Download locations and\n * tokens stay application-private; expired artifacts fail before that boundary.\n */\nexport function validateReportExportArtifact(\n descriptor: ReportAdapterDescriptor,\n artifact: ReportExportArtifact,\n now = new Date(),\n): ReportExportArtifact {\n const input = plainObject(artifact, 'Report export artifact');\n exactKeys(\n input,\n ['id', 'request', 'progress', 'expiresAt'],\n 'Report export artifact',\n );\n const request = validateReportExportRequest(descriptor, artifact.request);\n const id = requiredString(artifact.id, 'Report export artifact id');\n const expiresAt = normalizeIso(\n artifact.expiresAt,\n 'Report export artifact expiresAt',\n );\n if (new Date(expiresAt).getTime() <= now.getTime()) {\n return fail('Report export artifact has expired');\n }\n const progress = plainObject(\n artifact.progress,\n 'Report export artifact progress',\n );\n exactKeys(\n progress,\n ['state', 'rowCount', 'byteCount', 'truncated'],\n 'Report export artifact progress',\n );\n const state = progress.state;\n if (\n state !== 'queued' &&\n state !== 'running' &&\n state !== 'completed' &&\n state !== 'failed'\n ) {\n return fail('Report export artifact progress state is invalid');\n }\n const rowCount = boundedInteger(\n progress.rowCount,\n 'Report export artifact rowCount',\n 0,\n request.rowCount,\n );\n const byteCount = boundedInteger(\n progress.byteCount,\n 'Report export artifact byteCount',\n 0,\n request.limits.maxBytes,\n );\n const truncated = progress.truncated;\n if (typeof truncated !== 'boolean') {\n return fail('Report export artifact truncated must be a boolean');\n }\n if (state === 'completed' && rowCount !== request.rowCount) {\n return fail(\n 'Completed report export artifacts must contain the requested row count',\n );\n }\n if (state === 'completed' && truncated !== request.truncated) {\n return fail(\n 'Completed report export artifacts must match the requested truncation state',\n );\n }\n return {\n id,\n request,\n expiresAt,\n progress: { state, rowCount, byteCount, truncated },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["suffix","children","filter"],"sources":["../src/lifecycle.ts","../src/adapter.ts","../src/report.ts","../src/views.ts"],"sourcesContent":["import { ObjectRegistry, type SmrtObject } from '@happyvertical/smrt-core';\nimport { getTenantId, withSystemContext } from '@happyvertical/smrt-tenancy';\nimport {\n type DatabaseInterface,\n tableExists,\n validateColumnName,\n} from '@happyvertical/sql';\nimport type { ReportRefreshActionDescriptor } from './adapter.js';\nimport { buildReportDefinition } from './compiler.js';\nimport { enqueueReportRefresh } from './scheduler.js';\nimport {\n assertReportTablesReady,\n REPORT_LOCKS_TABLE,\n REPORT_RUNS_TABLE,\n scopeKeyForTenant,\n} from './state.js';\nimport type {\n ReportRefreshMode,\n ReportRefreshResult,\n ReportRefreshTrigger,\n} from './types.js';\n\ntype ReportCtor = new (...args: any[]) => SmrtObject;\n\nfunction registeredReport(reportCtor: ReportCtor) {\n return (\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name)\n );\n}\n\nexport type ReportLifecycleState =\n | 'current'\n | 'stale'\n | 'refreshing'\n | 'lock-skipped'\n | 'failed';\n\nexport interface ReportLifecycleRun {\n id: string;\n status: 'running' | 'success' | 'failed' | 'skipped';\n mode: ReportRefreshMode;\n trigger: ReportRefreshTrigger;\n startedAt?: string;\n completedAt?: string;\n rowCount: number;\n changedGroupCount: number;\n /** A failed run is safe to retry through the separately permissioned action. */\n mayRetry: boolean;\n}\n\n/**\n * Transport-neutral report lifecycle state. It deliberately never includes a\n * lock owner, a raw failure message, or tenant fanout identifiers.\n */\nexport interface ReportLifecycleSnapshot {\n version: 1;\n state: ReportLifecycleState;\n asOf?: string;\n refreshedAt?: string;\n /** Whether existing materialized rows can remain visible while work changes state. */\n hasUsableRows: boolean;\n mode: ReportRefreshMode;\n run?: ReportLifecycleRun;\n /** A stable, redacted failure signal; raw executor errors stay server-side. */\n failure?: {\n code: 'refresh_failed';\n retryable: true;\n };\n lock: {\n held: boolean;\n expiresAt?: string;\n };\n}\n\nexport interface ReportLifecycleOptions {\n db: DatabaseInterface;\n /** Test seam; production callers use the current clock. */\n now?: Date;\n}\n\nexport interface ReportRefreshJobHandle {\n jobId: string;\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';\n attempts: number;\n maxAttempts: number;\n /** Bounded polling guidance; this API does not create a second queue. */\n pollAfterMs: number;\n}\n\nexport interface ReportRefreshActionContext {\n phase: 'preview' | 'apply';\n reportClassName: string;\n /** The action always applies to the caller's already-bound tenant scope. */\n tenantScope: 'ambient';\n mode: ReportRefreshMode;\n requiredPermission: string;\n}\n\n/** Authority and audit stay with the application action host, not reports. */\nexport interface ReportRefreshActionHost {\n authorize(context: ReportRefreshActionContext): Promise<void> | void;\n audit(context: ReportRefreshActionContext): Promise<void> | void;\n}\n\nexport interface PreviewReportRefreshOptions extends ReportLifecycleOptions {\n host: ReportRefreshActionHost;\n mode?: ReportRefreshMode;\n /**\n * Pass `descriptor.refresh.action` when the adapter declares a custom\n * refresh permission, so the displayed and enforced action stay aligned.\n */\n refreshAction?: Pick<ReportRefreshActionDescriptor, 'requiredPermission'>;\n}\n\nexport interface ReportRefreshPreview {\n phase: 'preview';\n lifecycle: ReportLifecycleSnapshot;\n action: ReportRefreshActionContext;\n execution: 'background';\n}\n\nexport interface ApplyReportRefreshOptions extends PreviewReportRefreshOptions {\n queue?: string;\n priority?: number;\n timeout?: number;\n maxAttempts?: number;\n tenantJobCap?: number;\n}\n\nexport interface AppliedReportRefresh {\n phase: 'apply';\n job: ReportRefreshJobHandle;\n}\n\nexport interface ReportRefreshOutcome {\n state: 'current' | 'lock-skipped' | 'partial';\n rowCount: number;\n changedGroupCount: number;\n completedScopes: number;\n lockSkippedScopes: number;\n mode: ReportRefreshMode;\n refreshedAt: string;\n runId?: string;\n}\n\nfunction canonicalClassName(reportCtor: ReportCtor): string {\n const registered = registeredReport(reportCtor);\n return registered?.qualifiedName ?? registered?.name ?? reportCtor.name;\n}\n\nfunction reportTableName(reportCtor: ReportCtor): string {\n const reportClassName = canonicalClassName(reportCtor);\n const tableName = ObjectRegistry.getTableName(reportClassName);\n if (!tableName) {\n throw new Error(`No report table registered for ${reportCtor.name}`);\n }\n return validateColumnName(tableName);\n}\n\nasync function reportColumnName(\n reportClassName: string,\n fieldName: string,\n): Promise<string> {\n const fields = await ObjectRegistry.getAllFields(reportClassName);\n const direct = fields.get(fieldName) as\n | { columnName?: string; _meta?: { columnName?: string } }\n | undefined;\n return validateColumnName(\n direct?.columnName ??\n direct?._meta?.columnName ??\n fieldName.replace(/([A-Z])/g, '_$1').toLowerCase(),\n );\n}\n\nfunction toIso(value: unknown): string | undefined {\n if (!value) return undefined;\n const date = value instanceof Date ? value : new Date(String(value));\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString();\n}\n\nfunction latestIso(\n first: string | undefined,\n second: string | undefined,\n): string | undefined {\n if (!first) return second;\n if (!second) return first;\n return new Date(first).getTime() >= new Date(second).getTime()\n ? first\n : second;\n}\n\nfunction lifecycleTenantId(reportCtor: ReportCtor): string | null {\n return registeredReport(reportCtor)?.tenantScopedConfig\n ? (getTenantId() ?? null)\n : null;\n}\n\nfunction numberValue(value: unknown): number {\n const numeric = Number(value ?? 0);\n return Number.isFinite(numeric) ? numeric : 0;\n}\n\nfunction rowWhere(\n reportClassName: string,\n scopeKey: string,\n tenantId: string | null,\n): { sql: string; values: unknown[] } {\n if (tenantId) {\n return {\n sql: 'report_class = ? AND scope_key = ? AND tenant_id = ?',\n values: [reportClassName, scopeKey, tenantId],\n };\n }\n return {\n sql: 'report_class = ? AND scope_key = ? AND tenant_id IS NULL',\n values: [reportClassName, scopeKey],\n };\n}\n\nfunction runFromRow(\n row: Record<string, unknown> | undefined,\n): ReportLifecycleRun | undefined {\n if (!row || typeof row.id !== 'string') return undefined;\n const status = row.status;\n if (\n status !== 'running' &&\n status !== 'success' &&\n status !== 'failed' &&\n status !== 'skipped'\n ) {\n return undefined;\n }\n const mode: ReportRefreshMode =\n row.mode === 'incremental' ? 'incremental' : 'rebuild';\n const trigger: ReportRefreshTrigger =\n row.trigger === 'schedule' ||\n row.trigger === 'change' ||\n row.trigger === 'ttl' ||\n row.trigger === 'job'\n ? row.trigger\n : 'manual';\n return {\n id: row.id,\n status,\n mode,\n trigger,\n ...(toIso(row.started_at) ? { startedAt: toIso(row.started_at) } : {}),\n ...(toIso(row.completed_at)\n ? { completedAt: toIso(row.completed_at) }\n : {}),\n rowCount: numberValue(row.row_count),\n changedGroupCount: numberValue(row.changed_group_count),\n mayRetry: status === 'failed',\n };\n}\n\nfunction jobHandle(job: {\n id?: unknown;\n status: ReportRefreshJobHandle['status'];\n attempts: number;\n maxAttempts: number;\n}): ReportRefreshJobHandle {\n if (typeof job.id !== 'string' || job.id.length === 0) {\n throw new Error('Queued report refresh is missing its job id');\n }\n return {\n jobId: job.id,\n status: job.status,\n attempts: job.attempts,\n maxAttempts: job.maxAttempts,\n pollAfterMs: 1_000,\n };\n}\n\n/**\n * Read only the ambient tenant's report state. The caller cannot supply a\n * tenant selector, which prevents lifecycle lookup from becoming a tenant\n * enumeration surface.\n */\nexport async function getReportLifecycle(\n reportCtor: ReportCtor,\n options: ReportLifecycleOptions,\n): Promise<ReportLifecycleSnapshot> {\n await assertReportTablesReady(options.db, [\n REPORT_RUNS_TABLE,\n REPORT_LOCKS_TABLE,\n ]);\n\n const definition = await buildReportDefinition(reportCtor);\n const reportClassName = canonicalClassName(reportCtor);\n const tenantId = lifecycleTenantId(reportCtor);\n const scopeKey = scopeKeyForTenant(tenantId);\n const where = rowWhere(reportClassName, scopeKey, tenantId);\n const now = options.now ?? new Date();\n\n const [runResult, lockResult] = await Promise.all([\n options.db.query(\n `SELECT id, mode, trigger, status, started_at, completed_at, row_count, changed_group_count\n FROM ${REPORT_RUNS_TABLE}\n WHERE ${where.sql}\n ORDER BY started_at DESC, created_at DESC, id DESC\n LIMIT 1`,\n ...where.values,\n ),\n options.db.query(\n `SELECT expires_at\n FROM ${REPORT_LOCKS_TABLE}\n WHERE ${where.sql} AND expires_at > ?\n LIMIT 1`,\n ...where.values,\n now.toISOString(),\n ),\n ]);\n\n const tableName = reportTableName(reportCtor);\n if (!(await tableExists(options.db, tableName))) {\n throw new Error(\n `Report table '${tableName}' does not exist for ${reportClassName}`,\n );\n }\n const refreshedAtColumn = await reportColumnName(\n reportClassName,\n 'refreshedAt',\n );\n const registered = registeredReport(reportCtor);\n const configuredTenantField = registered?.tenantScopedConfig?.field;\n const tenantColumn = configuredTenantField\n ? await reportColumnName(reportClassName, configuredTenantField)\n : undefined;\n const materialized = tenantColumn\n ? await options.db.query(\n `SELECT MAX(${refreshedAtColumn}) AS refreshed_at FROM ${tableName} WHERE ${tenantColumn} ${tenantId ? '= ?' : 'IS NULL'}`,\n ...(tenantId ? [tenantId] : []),\n )\n : await options.db.query(\n `SELECT MAX(${refreshedAtColumn}) AS refreshed_at FROM ${tableName}`,\n );\n\n const run = runFromRow(\n runResult.rows[0] as Record<string, unknown> | undefined,\n );\n const refreshedAt = toIso(materialized.rows[0]?.refreshed_at);\n const completedAt = run?.status === 'success' ? run.completedAt : undefined;\n const asOf = latestIso(refreshedAt, completedAt);\n const ttlMs = definition.refresh?.ttl;\n const isStale =\n !asOf ||\n (ttlMs !== undefined && now.getTime() - new Date(asOf).getTime() > ttlMs);\n const lockExpiresAt = toIso(lockResult.rows[0]?.expires_at);\n const lockHeld = Boolean(lockExpiresAt);\n const state: ReportLifecycleState =\n run?.status === 'skipped'\n ? 'lock-skipped'\n : lockHeld\n ? 'refreshing'\n : run?.status === 'failed'\n ? 'failed'\n : run?.status === 'running'\n ? 'stale'\n : isStale\n ? 'stale'\n : 'current';\n\n return {\n version: 1,\n state,\n ...(asOf ? { asOf } : {}),\n ...(refreshedAt ? { refreshedAt } : {}),\n hasUsableRows: Boolean(refreshedAt),\n mode: run?.mode ?? definition.refresh?.mode ?? 'rebuild',\n ...(run ? { run } : {}),\n ...(run?.status === 'failed'\n ? { failure: { code: 'refresh_failed' as const, retryable: true } }\n : {}),\n lock: {\n held: lockHeld,\n ...(lockExpiresAt ? { expiresAt: lockExpiresAt } : {}),\n },\n };\n}\n\nfunction actionContext(\n reportCtor: ReportCtor,\n phase: ReportRefreshActionContext['phase'],\n mode: ReportRefreshMode,\n refreshAction:\n | Pick<ReportRefreshActionDescriptor, 'requiredPermission'>\n | undefined,\n): ReportRefreshActionContext {\n return {\n phase,\n reportClassName: canonicalClassName(reportCtor),\n tenantScope: 'ambient',\n mode,\n requiredPermission:\n refreshAction?.requiredPermission &&\n refreshAction.requiredPermission.trim().length > 0\n ? refreshAction.requiredPermission\n : 'reports.refresh',\n };\n}\n\nasync function actionMode(\n reportCtor: ReportCtor,\n requested: ReportRefreshMode | undefined,\n): Promise<ReportRefreshMode> {\n if (requested) return requested;\n const definition = await buildReportDefinition(reportCtor);\n return definition.refresh?.mode ?? 'rebuild';\n}\n\n/** Preview only declares a separately authorized and audited refresh request. */\nexport async function previewReportRefresh(\n reportCtor: ReportCtor,\n options: PreviewReportRefreshOptions,\n): Promise<ReportRefreshPreview> {\n const mode = await actionMode(reportCtor, options.mode);\n const action = actionContext(\n reportCtor,\n 'preview',\n mode,\n options.refreshAction,\n );\n await options.host.authorize(action);\n await options.host.audit(action);\n return {\n phase: 'preview',\n lifecycle: await getReportLifecycle(reportCtor, options),\n action,\n execution: 'background',\n };\n}\n\n/** Queue a manual refresh only after the action host authorizes and audits it. */\nexport async function applyReportRefresh(\n reportCtor: ReportCtor,\n options: ApplyReportRefreshOptions,\n): Promise<AppliedReportRefresh> {\n const mode = await actionMode(reportCtor, options.mode);\n const action = actionContext(\n reportCtor,\n 'apply',\n mode,\n options.refreshAction,\n );\n await options.host.authorize(action);\n await options.host.audit(action);\n const tenantId = lifecycleTenantId(reportCtor);\n const enqueue = () =>\n enqueueReportRefresh({\n db: options.db,\n reportClass: canonicalClassName(reportCtor),\n mode,\n trigger: 'manual',\n tenantId,\n queue: options.queue,\n priority: options.priority,\n timeout: options.timeout,\n maxAttempts: options.maxAttempts,\n tenantJobCap: options.tenantJobCap,\n });\n const job =\n tenantId === null && getTenantId()\n ? await withSystemContext(enqueue)\n : await enqueue();\n return { phase: 'apply', job: jobHandle(job) };\n}\n\n/** Normalize executor results without exposing the tenant identifiers in a fanout. */\nexport function reportRefreshOutcome(\n result: ReportRefreshResult,\n): ReportRefreshOutcome {\n const scopes = result.tenantResults ?? [result];\n const lockSkippedScopes = scopes.filter((scope) => scope.skipped).length;\n const completedScopes = scopes.length - lockSkippedScopes;\n return {\n state:\n scopes.length > 1 && lockSkippedScopes > 0\n ? 'partial'\n : lockSkippedScopes > 0\n ? 'lock-skipped'\n : 'current',\n rowCount: result.rowCount,\n changedGroupCount: result.changedGroupCount ?? 0,\n completedScopes,\n lockSkippedScopes,\n mode: result.mode,\n refreshedAt: result.refreshedAt.toISOString(),\n ...(result.runId ? { runId: result.runId } : {}),\n };\n}\n","import {\n createDataQueryFingerprint,\n DataQueryValidationError,\n normalizeDataQueryRequest,\n normalizeDataQueryResult,\n ObjectRegistry,\n type SmrtObject,\n} from '@happyvertical/smrt-core';\nimport { toSnakeCase } from '@happyvertical/smrt-core/utils';\nimport type {\n DataQueryCondition,\n DataQueryFieldDescriptor,\n DataQueryFilter,\n DataQueryFilterOperator,\n DataQueryFreshness,\n DataQueryRequest,\n DataQueryResult,\n DataQuerySchema,\n} from '@happyvertical/smrt-types';\nimport { buildReportDefinition } from './compiler.js';\nimport {\n getReportLifecycle,\n type ReportLifecycleOptions,\n type ReportLifecycleSnapshot,\n} from './lifecycle.js';\nimport type {\n ReportAggregateFn,\n ReportDefinition,\n ReportFieldDefinition,\n ReportRefreshConfig,\n ReportTimeBucketUnit,\n} from './types.js';\n\n/** Presentation and policy metadata understood by the report adapter. */\nexport type ReportColumnSensitivity =\n | 'public'\n | 'personal'\n | 'sensitive'\n | 'secret';\n\nexport type ReportColumnCapability =\n | 'read'\n | 'project'\n | 'filter'\n | 'sort'\n | 'facet'\n | 'group'\n | 'aggregate';\n\nexport type ReportColumnKind = 'identity' | 'group' | 'bucket' | 'aggregate';\n\n/**\n * Source-query clause a field belongs to. The materialized read executor\n * applies both kinds through its tenant-scoped collection, but consumers use\n * this declaration to keep dimensions/periods (WHERE) distinct from measures\n * (HAVING) when constructing drilldowns, saved views, or a live query.\n */\nexport type ReportFilterScope = 'where' | 'having';\n\nexport interface ReportColumnDescriptor extends DataQueryFieldDescriptor {\n /** Stable output column id (never a property path). */\n id: string;\n fieldName: string;\n label: string;\n kind: ReportColumnKind;\n filterScope: ReportFilterScope;\n /** Capabilities available from this adapter revision. */\n capabilities: ReportColumnCapability[];\n format?: string;\n sensitivity?: ReportColumnSensitivity;\n sourceColumn?: string;\n bucket?: ReportTimeBucketUnit;\n aggregate?: ReportAggregateFn;\n distinct?: boolean;\n}\n\nexport interface ReportRefreshDescriptor {\n mode: 'rebuild' | 'incremental';\n /**\n * Whether the report configuration lets a registered SmrtReportCollection\n * synchronously refresh a stale read. Generic collection reads stay\n * read-only and report unknown freshness until the lifecycle adapter runs.\n */\n mayRefreshOnRead: boolean;\n ttlMs?: number;\n triggers: Array<'manual' | 'schedule' | 'change' | 'ttl' | 'job'>;\n /** Declared only; the lifecycle adapter owns authorization, audit, and run state. */\n action: ReportRefreshActionDescriptor;\n}\n\n/** A report-wide lifecycle action, never a mutation performed by this adapter. */\nexport interface ReportRefreshActionDescriptor {\n id: 'refresh';\n label: 'Refresh report';\n scope: 'surface';\n phases: Array<'preview' | 'apply'>;\n requiresPermission: true;\n requiredPermission: string;\n auditRequired: true;\n}\n\n/**\n * The report-owned, transport-neutral descriptor. Consumers can map `schema`\n * to the canonical data-query runtime and `columns` to their presentation\n * contract without importing smrt-ui or a report domain class.\n */\nexport interface ReportAdapterDescriptor {\n version: 1;\n resourceId: string;\n reportClassName: string;\n sourceClassName: string;\n tenantScoped: boolean;\n tenantField?: string;\n identityField: 'id';\n columns: ReportColumnDescriptor[];\n schema: DataQuerySchema;\n queryExecution: ReportQueryExecutionDescriptor;\n dataTable: ReportDataTableDescriptor;\n drilldown: ReportDrilldownDescriptor;\n refresh: ReportRefreshDescriptor;\n}\n\n/**\n * Delivery choices for the same bounded report query. They never add\n * authority: a host owns visible state and background-job execution.\n */\nexport type ReportQueryExecutionMode = 'visible' | 'background' | 'silent';\n\nexport interface ReportQueryExecutionDescriptor {\n modes: ReportQueryExecutionMode[];\n /** The caller may apply the returned rows to an already-authorized surface. */\n visible: { delivery: 'result' };\n /** A host queues the authority-free task and returns no materialized rows yet. */\n background: { delivery: 'queued'; requiresHost: true };\n /** The caller receives rows but the adapter makes no visible-surface change. */\n silent: { delivery: 'result'; mutatesVisibleSurface: false };\n}\n\n/**\n * The exact bounded request a background host may persist. Tenant, principal,\n * collection, database, and display state deliberately remain with that host.\n */\nexport interface ReportBackgroundQueryTask {\n version: 1;\n execution: 'background';\n resourceId: string;\n reportClassName: string;\n request: DataQueryRequest;\n inherits: Array<\n 'principal' | 'tenant' | 'report-definition' | 'field-policy'\n >;\n}\n\n/** Queuing a background query returns a handle, never rows from another scope. */\nexport interface ReportBackgroundQueryResult {\n version: 1;\n execution: 'background';\n status: 'queued';\n taskId: string;\n queryFingerprint: string;\n}\n\n/** One row value that can safely constrain a source-record drilldown. */\nexport interface ReportDrilldownFieldDescriptor {\n /** Stable report column id; never a display label. */\n id: string;\n /** Declared source field/column chosen by report metadata, not caller input. */\n sourceColumn: string;\n kind: 'group' | 'bucket';\n /** A bucket stays declarative so the source adapter preserves report timezone semantics. */\n bucket?: ReportTimeBucketUnit;\n}\n\n/**\n * Declarative source-query handoff for a report row. No client can supply a\n * principal, tenant, report definition, or arbitrary source field here.\n */\nexport interface ReportDrilldownDescriptor {\n id: 'drilldown';\n sourceClassName: string;\n fields: ReportDrilldownFieldDescriptor[];\n inherits: Array<\n 'principal' | 'tenant' | 'report-definition' | 'field-policy'\n >;\n}\n\nexport interface ReportDrilldownConstraint {\n id: string;\n sourceColumn: string;\n kind: 'group' | 'bucket';\n value: unknown;\n bucket?: ReportTimeBucketUnit;\n}\n\n/** A server adapter uses this authority-free handoff to execute a source drilldown. */\nexport interface ReportDrilldownQuery {\n version: 1;\n resourceId: string;\n reportClassName: string;\n sourceClassName: string;\n constraints: ReportDrilldownConstraint[];\n inherits: Array<\n 'principal' | 'tenant' | 'report-definition' | 'field-policy'\n >;\n}\n\n/** Structural DataTable view hints; deliberately not a smrt-ui dependency. */\nexport interface ReportDataTableHeaderPathSegment {\n /** Stable group identity within a header level. */\n id: string;\n /** Human-readable grouped-header label. */\n label: string;\n}\n\n/** Responsive metadata that maps directly to a consumer's table contract. */\nexport interface ReportDataTableColumnResponsive {\n /** Higher values make a column more important during responsive collapse. */\n priority?: number;\n /** Keep key dimensions reachable during responsive collapse. */\n keepVisible?: boolean;\n}\n\n/**\n * A rendering instruction for a raw materialized value. Formatting is never\n * applied to a query row, so sorting, export, and agent consumers retain it.\n */\nexport type ReportDataTableValueFormat =\n | 'text'\n | 'date'\n | 'datetime'\n | 'percentage'\n | 'count'\n | 'money'\n | 'number';\n\nexport type ReportDataTableColumnRole = 'data' | 'status' | 'action';\n\nexport interface ReportDataTableColumn {\n id: string;\n label: string;\n accessor: string;\n sortable: boolean;\n searchable: false;\n filterable: boolean;\n /** Group ancestry for consumers with multi-level table headers. */\n headerPath: ReportDataTableHeaderPathSegment[];\n /** Consumer-side display instruction; materialized rows remain raw. */\n valueFormat: ReportDataTableValueFormat;\n /** The default alignment for the formatted display value. */\n align: 'left' | 'right';\n /** Generic semantic role for status/action columns introduced by later slices. */\n role: ReportDataTableColumnRole;\n responsive: ReportDataTableColumnResponsive;\n}\n\nexport type ReportDataTableStructuralRowKind =\n | 'summary'\n | 'subtotal'\n | 'aggregate'\n | 'footer';\n\n/** Input supplied by a report consumer that computes a summary or subtotal. */\nexport interface ReportDataTableStructuralRowInput {\n id: string;\n kind: ReportDataTableStructuralRowKind;\n label: string;\n /** Raw values keyed by the adapter's stable column id. */\n values?: Readonly<Record<string, unknown>>;\n /** Column that renders this row's accessible row header. */\n labelColumnId?: string;\n}\n\n/**\n * Structural rows are intentionally separate from materialized data rows.\n * Consumers pass them to their DataTable's structural-row slot/prop, never its\n * selectable data collection.\n */\nexport interface ReportDataTableStructuralRow\n extends ReportDataTableStructuralRowInput {\n selection: 'excluded';\n actions: 'excluded';\n}\n\n/** Per-column presentation overrides owned by the consuming report surface. */\nexport interface ReportDataTableColumnOverride {\n label?: string;\n headerPath?: readonly ReportDataTableHeaderPathSegment[];\n valueFormat?: ReportDataTableValueFormat;\n align?: 'left' | 'right';\n role?: ReportDataTableColumnRole;\n responsive?: ReportDataTableColumnResponsive;\n}\n\nexport interface ReportDataTablePresentationOptions {\n /** Overrides are keyed by stable adapter column id, not display labels. */\n columns?: Readonly<Record<string, ReportDataTableColumnOverride>>;\n /** Summary/subtotal rows remain structural rather than materialized data rows. */\n structuralRows?: readonly ReportDataTableStructuralRowInput[];\n}\n\nexport interface ReportDataTableDescriptor {\n rowKey: 'id';\n manualPagination: true;\n manualSorting: true;\n enableFiltering: true;\n enableSearch: false;\n columns: ReportDataTableColumn[];\n structuralRows: ReportDataTableStructuralRow[];\n}\n\nexport interface ReportAdapterOptions {\n /** Scope used only to make a stable resource id; it is not authority. */\n tenantScope?: 'current' | 'global' | 'tenant';\n /** Permission that a lifecycle action host must require before refresh. */\n refreshPermission?: string;\n /** Consumer-owned presentational preferences; they never affect query rows. */\n dataTable?: ReportDataTablePresentationOptions;\n}\n\ninterface ReportQueryCommonOptions {\n /** Injected collection seam for tests and application-owned adapters. */\n collection?: {\n list(\n options: Record<string, unknown>,\n ): Promise<Array<Record<string, unknown>>>;\n count(options?: Record<string, unknown>): Promise<number>;\n facets?(options: Record<string, unknown>): Promise<\n Array<{\n field: string;\n values: Array<{\n value: string | number | boolean | null;\n count: number;\n }>;\n }>\n >;\n };\n db?: import('@happyvertical/sql').DatabaseInterface;\n /**\n * Consumer-owned descriptor preferences. They contribute to the stable\n * resource identity and must match the catalog used to select this report.\n */\n adapter?: ReportAdapterOptions;\n /**\n * Opt in to a tenant-safe lifecycle snapshot alongside this materialized\n * read. It never makes a generic read mutate.\n */\n lifecycle?: Omit<ReportLifecycleOptions, 'db'>;\n}\n\n/** Visible is the default; silent has identical data authority but no UI intent. */\nexport interface ReportQueryOptions extends ReportQueryCommonOptions {\n execution?: 'visible' | 'silent';\n}\n\n/** Background execution is possible only through an application-owned queue host. */\nexport interface ReportBackgroundQueryOptions extends ReportQueryCommonOptions {\n execution: 'background';\n enqueueBackgroundQuery(\n task: ReportBackgroundQueryTask,\n ): Promise<{ taskId: string }>;\n}\n\n/** Lifecycle context attached only when a caller explicitly opts in. */\nexport interface ReportMaterializedReadLifecycle {\n snapshot: ReportLifecycleSnapshot;\n /** Whether this collection read appears to have completed a TTL refresh. */\n read: 'current' | 'stale' | 'refresh-triggered';\n}\n\nexport interface ReportDataQueryResult extends DataQueryResult {\n /** Delivery intent only; the adapter never mutates a visible surface. */\n execution: 'visible' | 'silent';\n reportLifecycle?: ReportMaterializedReadLifecycle;\n}\n\ntype RegistryField = {\n type?: string;\n transient?: boolean;\n description?: string;\n sensitive?: boolean;\n readPermission?: string;\n format?: unknown;\n sensitivity?: unknown;\n _meta?: Record<string, unknown>;\n};\nconst SENSITIVITIES = new Set<ReportColumnSensitivity>([\n 'public',\n 'personal',\n 'sensitive',\n 'secret',\n]);\n\nfunction registeredClass(\n ctor: new (...args: any[]) => SmrtObject,\n): ReturnType<typeof ObjectRegistry.getClassByConstructor> {\n return (\n ObjectRegistry.getClassByConstructor(ctor) ??\n ObjectRegistry.getClass(ctor.name)\n );\n}\n\nfunction fieldPolicy(field: RegistryField | undefined): {\n sensitive: boolean;\n readPermission?: string;\n} {\n const meta = field?._meta ?? {};\n const sensitivity = [field?.sensitivity, meta.sensitivity].find(\n (value): value is string => typeof value === 'string',\n );\n return {\n sensitive:\n field?.sensitive === true ||\n meta.sensitive === true ||\n sensitivity === 'sensitive' ||\n sensitivity === 'secret',\n readPermission:\n typeof field?.readPermission === 'string'\n ? field.readPermission\n : typeof meta.readPermission === 'string'\n ? meta.readPermission\n : undefined,\n };\n}\n\nfunction descriptorMetadata(field: RegistryField | undefined): {\n format?: string;\n sensitivity?: ReportColumnSensitivity;\n} {\n const metadata = field?._meta ?? {};\n const format = [field?.format, metadata.format].find(\n (value): value is string => typeof value === 'string' && value.length > 0,\n );\n const rawSensitivity = [field?.sensitivity, metadata.sensitivity].find(\n (value): value is string =>\n typeof value === 'string' &&\n SENSITIVITIES.has(value as ReportColumnSensitivity),\n );\n return {\n ...(format ? { format } : {}),\n ...(rawSensitivity\n ? { sensitivity: rawSensitivity as ReportColumnSensitivity }\n : {}),\n };\n}\n\nfunction labelFor(fieldName: string): string {\n return fieldName\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .replace(/^./, (value) => value.toUpperCase());\n}\n\nfunction queryType(type: string | undefined): DataQueryFieldDescriptor['type'] {\n switch (type) {\n case 'integer':\n case 'decimal':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'datetime':\n return 'datetime';\n case 'json':\n return 'json';\n default:\n return 'string';\n }\n}\n\nfunction configuredTenantField(configured?: string): string | undefined {\n return configured ? toSnakeCase(configured) : undefined;\n}\n\nfunction isColumnBackedField(field: RegistryField | undefined): boolean {\n if (!field) return false;\n if (field.transient === true || field._meta?.transient === true) return false;\n return !['meta', 'oneToMany', 'manyToMany'].includes(field.type ?? '');\n}\n\nfunction reportColumn(\n field: ReportFieldDefinition,\n registryField: RegistryField | undefined,\n): ReportColumnDescriptor | undefined {\n const policy = fieldPolicy(registryField);\n // A descriptor is an exposure boundary. Without a principal/permission\n // context, sensitive, permission-gated, and non-column fields fail closed.\n if (\n policy.sensitive ||\n policy.readPermission ||\n !isColumnBackedField(registryField)\n ) {\n return undefined;\n }\n\n const id = field.columnName ?? toSnakeCase(field.fieldName);\n const report = field.report;\n if (!report) return undefined;\n const type = queryType(field.type ?? registryField?.type);\n const filterOperators = filterOperatorsFor(type);\n const base: ReportColumnDescriptor = {\n id,\n fieldName: field.fieldName,\n label: registryField?.description || labelFor(field.fieldName),\n kind: report.kind,\n filterScope: report.kind === 'aggregate' ? 'having' : 'where',\n type,\n projectable: true,\n sortable: true,\n facetable: report.kind !== 'aggregate',\n ...(filterOperators ? { filterOperators } : {}),\n capabilities: [\n 'project',\n 'read',\n ...(filterOperators ? (['filter'] as const) : []),\n 'sort',\n ...(report.kind !== 'aggregate' ? (['facet'] as const) : []),\n ...(report.kind === 'aggregate'\n ? (['aggregate'] as const)\n : report.kind === 'group'\n ? (['group'] as const)\n : []),\n ],\n ...(report.kind === 'group'\n ? { sourceColumn: toSnakeCase(report.sourceColumn ?? field.fieldName) }\n : {}),\n ...(report.kind === 'bucket'\n ? { bucket: report.unit, sourceColumn: toSnakeCase(report.sourceColumn) }\n : {}),\n ...(report.kind === 'aggregate'\n ? {\n aggregate: report.fn,\n ...(report.column\n ? { sourceColumn: toSnakeCase(report.column) }\n : {}),\n ...(report.distinct === undefined\n ? {}\n : { distinct: report.distinct }),\n }\n : {}),\n ...descriptorMetadata(registryField),\n };\n return base;\n}\n\nfunction identityColumn(): ReportColumnDescriptor {\n return {\n id: 'id',\n fieldName: 'id',\n label: 'ID',\n kind: 'identity',\n filterScope: 'where',\n type: 'string',\n projectable: true,\n sortable: true,\n facetable: false,\n filterOperators: filterOperatorsFor('string'),\n capabilities: ['project', 'read', 'filter', 'sort'],\n };\n}\n\nfunction toQueryField(\n column: ReportColumnDescriptor,\n): DataQueryFieldDescriptor {\n return {\n id: column.id,\n type: column.type,\n ...(column.projectable === undefined\n ? {}\n : { projectable: column.projectable }),\n sortable: column.sortable === true,\n facetable: column.facetable === true,\n ...(column.filterOperators === undefined\n ? {}\n : { filterOperators: [...column.filterOperators] }),\n };\n}\n\nfunction filterOperatorsFor(\n type: DataQueryFieldDescriptor['type'],\n): DataQueryFilterOperator[] | undefined {\n switch (type) {\n case 'string':\n return ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn', 'like'];\n case 'number':\n case 'datetime':\n return ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn'];\n case 'boolean':\n return ['eq', 'ne', 'in', 'notIn'];\n case 'json':\n return undefined;\n }\n}\n\nfunction refreshDescriptor(\n refresh?: ReportRefreshConfig,\n refreshPermission = 'reports.refresh',\n): ReportRefreshDescriptor {\n const config = refresh ?? {};\n const triggers = new Set<ReportRefreshDescriptor['triggers'][number]>([\n 'manual',\n ]);\n if (!config.manual) {\n const hasSchedule = Boolean(config.schedule || config.fullRebuildSchedule);\n const hasChangeTrigger = Boolean(config.onChange?.length);\n if (hasSchedule) triggers.add('schedule');\n if (hasChangeTrigger) triggers.add('change');\n if (config.ttl !== undefined && config.ttl > 0) triggers.add('ttl');\n // Scheduler and on-change refreshes enqueue a durable job in either mode;\n // incremental mode alone is not itself a trigger.\n if (hasSchedule || hasChangeTrigger) triggers.add('job');\n }\n return {\n mode: config.mode ?? 'rebuild',\n mayRefreshOnRead:\n config.ttl !== undefined && config.ttl > 0 && !config.manual,\n ...(config.ttl === undefined ? {} : { ttlMs: config.ttl }),\n triggers: [...triggers],\n action: {\n id: 'refresh',\n label: 'Refresh report',\n scope: 'surface',\n phases: ['preview', 'apply'],\n requiresPermission: true,\n requiredPermission:\n refreshPermission.trim().length > 0\n ? refreshPermission\n : 'reports.refresh',\n auditRequired: true,\n },\n };\n}\n\nfunction resourceId(\n definition: ReportDefinition,\n scope: ReportAdapterOptions['tenantScope'],\n): string {\n return `${definition.reportClassName}#${scope ?? 'current'}`;\n}\n\nconst VALUE_FORMAT_ALIASES: Readonly<\n Record<string, ReportDataTableValueFormat>\n> = {\n text: 'text',\n date: 'date',\n datetime: 'datetime',\n 'date-time': 'datetime',\n percentage: 'percentage',\n percent: 'percentage',\n count: 'count',\n money: 'money',\n currency: 'money',\n number: 'number',\n decimal: 'number',\n integer: 'number',\n};\n\nconst VALUE_FORMATS = new Set<ReportDataTableValueFormat>(\n Object.values(VALUE_FORMAT_ALIASES),\n);\nconst COLUMN_ROLES = new Set<ReportDataTableColumnRole>([\n 'data',\n 'status',\n 'action',\n]);\nconst STRUCTURAL_ROW_KINDS = new Set<ReportDataTableStructuralRowKind>([\n 'summary',\n 'subtotal',\n 'aggregate',\n 'footer',\n]);\n\nfunction valueFormat(\n column: ReportColumnDescriptor,\n): ReportDataTableValueFormat {\n const configured = column.format?.trim().toLowerCase();\n if (configured && Object.hasOwn(VALUE_FORMAT_ALIASES, configured)) {\n return VALUE_FORMAT_ALIASES[configured];\n }\n if (column.kind === 'bucket') {\n return column.bucket === 'minute' || column.bucket === 'hour'\n ? 'datetime'\n : 'date';\n }\n if (column.kind === 'aggregate' && column.aggregate === 'count') {\n return 'count';\n }\n if (column.type === 'datetime') return 'datetime';\n if (column.type === 'number') return 'number';\n return 'text';\n}\n\nfunction headerPath(\n column: ReportColumnDescriptor,\n): ReportDataTableHeaderPathSegment[] {\n if (column.kind === 'aggregate') {\n return [\n { id: 'measures', label: 'Measures' },\n {\n id: `aggregate:${column.aggregate ?? 'value'}`,\n label: labelFor(column.aggregate ?? 'value'),\n },\n ];\n }\n if (column.kind === 'bucket') {\n return [\n { id: 'dimensions', label: 'Dimensions' },\n { id: 'time', label: 'Time' },\n ];\n }\n if (column.kind === 'group') {\n return [\n { id: 'dimensions', label: 'Dimensions' },\n { id: 'groups', label: 'Groups' },\n ];\n }\n return [{ id: 'dimensions', label: 'Dimensions' }];\n}\n\nfunction responsive(\n column: ReportColumnDescriptor,\n): ReportDataTableColumnResponsive {\n if (column.kind === 'group' || column.kind === 'bucket') {\n return { priority: 100, keepVisible: true };\n }\n if (column.kind === 'identity') return { priority: 80 };\n return { priority: 20 };\n}\n\nfunction assertHeaderPath(\n columnId: string,\n path: readonly ReportDataTableHeaderPathSegment[],\n): ReportDataTableHeaderPathSegment[] {\n if (!Array.isArray(path)) {\n throw new TypeError(\n `Report DataTable headerPath for ${columnId} must be an array`,\n );\n }\n return path.map((segment) => {\n if (\n !segment ||\n typeof segment.id !== 'string' ||\n segment.id.trim().length === 0 ||\n typeof segment.label !== 'string' ||\n segment.label.trim().length === 0\n ) {\n throw new TypeError(\n `Report DataTable headerPath entries for ${columnId} require non-empty id and label`,\n );\n }\n return { id: segment.id, label: segment.label };\n });\n}\n\nfunction dataTableColumn(\n column: ReportColumnDescriptor,\n override?: ReportDataTableColumnOverride,\n): ReportDataTableColumn {\n const format = override?.valueFormat ?? valueFormat(column);\n const path = override?.headerPath ?? headerPath(column);\n if (!VALUE_FORMATS.has(format)) {\n throw new TypeError(\n `Report DataTable valueFormat for ${column.id} is not supported: ${String(format)}`,\n );\n }\n if (override?.role !== undefined && !COLUMN_ROLES.has(override.role)) {\n throw new TypeError(\n `Report DataTable role for ${column.id} is not supported: ${String(override.role)}`,\n );\n }\n if (\n override?.label !== undefined &&\n (typeof override.label !== 'string' || override.label.trim().length === 0)\n ) {\n throw new TypeError(\n `Report DataTable label for ${column.id} must not be empty`,\n );\n }\n if (\n override?.align !== undefined &&\n override.align !== 'left' &&\n override.align !== 'right'\n ) {\n throw new TypeError(\n `Report DataTable align for ${column.id} is not supported: ${String(override.align)}`,\n );\n }\n if (\n override?.responsive?.priority !== undefined &&\n (!Number.isFinite(override.responsive.priority) ||\n override.responsive.priority < 0)\n ) {\n throw new TypeError(\n `Report DataTable responsive priority for ${column.id} must be a non-negative finite number`,\n );\n }\n if (\n override?.responsive?.keepVisible !== undefined &&\n typeof override.responsive.keepVisible !== 'boolean'\n ) {\n throw new TypeError(\n `Report DataTable keepVisible for ${column.id} must be a boolean`,\n );\n }\n return {\n id: column.id,\n label: override?.label ?? column.label,\n accessor: column.id,\n sortable: column.sortable === true,\n searchable: false,\n filterable: column.filterOperators !== undefined,\n headerPath: assertHeaderPath(column.id, path),\n valueFormat: format,\n align:\n override?.align ??\n (['count', 'money', 'number', 'percentage'].includes(format)\n ? 'right'\n : 'left'),\n role: override?.role ?? 'data',\n responsive: { ...responsive(column), ...override?.responsive },\n };\n}\n\nfunction structuralRows(\n columnIds: ReadonlySet<string>,\n rows: readonly ReportDataTableStructuralRowInput[] = [],\n): ReportDataTableStructuralRow[] {\n const ids = new Set<string>();\n return rows.map((row) => {\n if (\n !row ||\n typeof row.id !== 'string' ||\n row.id.trim().length === 0 ||\n typeof row.label !== 'string' ||\n row.label.trim().length === 0\n ) {\n throw new TypeError(\n 'Report structural rows require non-empty id and label values',\n );\n }\n if (ids.has(row.id)) {\n throw new TypeError(`Report structural row id must be unique: ${row.id}`);\n }\n if (!STRUCTURAL_ROW_KINDS.has(row.kind)) {\n throw new TypeError(\n `Report structural row kind is not supported: ${String(row.kind)}`,\n );\n }\n if (row.labelColumnId !== undefined) {\n if (\n typeof row.labelColumnId !== 'string' ||\n row.labelColumnId.trim().length === 0 ||\n !columnIds.has(row.labelColumnId)\n ) {\n throw new TypeError(\n `Report structural row labelColumnId must name an adapter column: ${String(row.labelColumnId)}`,\n );\n }\n }\n if (\n row.values !== undefined &&\n (typeof row.values !== 'object' ||\n row.values === null ||\n Array.isArray(row.values))\n ) {\n throw new TypeError('Report structural row values must be an object');\n }\n if (\n row.values &&\n Object.keys(row.values).some((columnId) => !columnIds.has(columnId))\n ) {\n throw new TypeError(\n 'Report structural row values must use adapter column ids',\n );\n }\n ids.add(row.id);\n return {\n id: row.id,\n kind: row.kind,\n label: row.label,\n ...(row.values !== undefined\n ? {\n values: jsonSafeValue(row.values) as Record<string, unknown>,\n }\n : {}),\n ...(row.labelColumnId !== undefined\n ? { labelColumnId: row.labelColumnId }\n : {}),\n selection: 'excluded' as const,\n actions: 'excluded' as const,\n };\n });\n}\n\nfunction drilldownDescriptor(\n columns: readonly ReportColumnDescriptor[],\n sourceClassName: string,\n): ReportDrilldownDescriptor {\n return {\n id: 'drilldown',\n sourceClassName,\n fields: columns\n .filter(\n (\n column,\n ): column is ReportColumnDescriptor & {\n kind: 'group' | 'bucket';\n sourceColumn: string;\n } =>\n (column.kind === 'group' || column.kind === 'bucket') &&\n typeof column.sourceColumn === 'string',\n )\n .map((column) => ({\n id: column.id,\n sourceColumn: column.sourceColumn,\n kind: column.kind,\n ...(column.kind === 'bucket' && column.bucket\n ? { bucket: column.bucket }\n : {}),\n })),\n inherits: ['principal', 'tenant', 'report-definition', 'field-policy'],\n };\n}\n\nexport async function buildReportAdapterDescriptor(\n reportCtor: new (...args: any[]) => SmrtObject,\n options: ReportAdapterOptions = {},\n): Promise<ReportAdapterDescriptor> {\n const definition = await buildReportDefinition(reportCtor);\n const registered = registeredClass(reportCtor);\n const fields = (await ObjectRegistry.getAllFields(\n definition.reportClassName,\n )) as Map<string, RegistryField>;\n // A tenant-looking field is not an isolation boundary. Only registered\n // tenant scoping installs the collection interceptor used by the executor.\n const tenantField = configuredTenantField(\n registered?.tenantScopedConfig?.field,\n );\n const columns = [\n identityColumn(),\n ...definition.fields\n .map((field) => reportColumn(field, fields.get(field.fieldName)))\n .filter((field): field is ReportColumnDescriptor => Boolean(field)),\n ].sort((left, right) => left.id.localeCompare(right.id));\n const schema: DataQuerySchema = {\n version: 1,\n identityField: 'id',\n fields: columns.map(toQueryField),\n defaultSort: [{ field: 'id', direction: 'asc' }],\n supports: { cursorPagination: false, consistency: false, facets: true },\n };\n const dataTable: ReportDataTableDescriptor = {\n rowKey: 'id',\n manualPagination: true,\n manualSorting: true,\n enableFiltering: true,\n enableSearch: false,\n columns: columns.map((column) =>\n dataTableColumn(column, options.dataTable?.columns?.[column.id]),\n ),\n structuralRows: structuralRows(\n new Set(columns.map((column) => column.id)),\n options.dataTable?.structuralRows,\n ),\n };\n return {\n version: 1,\n resourceId: resourceId(definition, options.tenantScope),\n reportClassName: definition.reportClassName,\n sourceClassName: definition.sourceClassName,\n tenantScoped: Boolean(registered?.tenantScopedConfig),\n ...(tenantField ? { tenantField } : {}),\n identityField: 'id',\n columns,\n schema,\n queryExecution: {\n modes: ['visible', 'background', 'silent'],\n visible: { delivery: 'result' },\n background: { delivery: 'queued', requiresHost: true },\n silent: { delivery: 'result', mutatesVisibleSurface: false },\n },\n dataTable,\n drilldown: drilldownDescriptor(columns, definition.sourceClassName),\n refresh: refreshDescriptor(definition.refresh, options.refreshPermission),\n };\n}\n\nfunction publicFieldMap(\n descriptor: ReportAdapterDescriptor,\n): Map<string, string> {\n return new Map(\n descriptor.columns.map((column) => [column.id, column.fieldName]),\n );\n}\n\n/**\n * Bind a materialized report row to its declared source dimensions. This is a\n * query handoff, not an executor: an authenticated source adapter must apply\n * the inherited principal, tenant, definition, and field policy before it\n * reads source records.\n */\nexport async function buildReportDrilldownQuery(\n reportCtor: new (...args: any[]) => SmrtObject,\n row: Readonly<Record<string, unknown>>,\n options: ReportAdapterOptions = {},\n): Promise<ReportDrilldownQuery> {\n const descriptor = await buildReportAdapterDescriptor(reportCtor, options);\n const constraints = descriptor.drilldown.fields.map((field) => {\n if (!Object.hasOwn(row, field.id)) {\n throw new DataQueryValidationError(\n `Report drilldown row is missing grouping field: ${field.id}`,\n 'DATA_QUERY_RESULT_INVALID',\n );\n }\n return {\n id: field.id,\n sourceColumn: field.sourceColumn,\n kind: field.kind,\n value: jsonSafeValue(row[field.id]),\n ...(field.bucket ? { bucket: field.bucket } : {}),\n };\n });\n return {\n version: 1,\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n sourceClassName: descriptor.drilldown.sourceClassName,\n constraints,\n inherits: [...descriptor.drilldown.inherits],\n };\n}\n\ntype MaterializedWhere = Array<Array<Record<string, unknown>>>;\n\n/** Keep OR expansion bounded before it reaches the database collection. */\nconst MAX_REPORT_FILTER_OR_GROUPS = 128;\n\nfunction reportFilterError(message: string): never {\n throw new DataQueryValidationError(message, 'DATA_QUERY_UNSUPPORTED');\n}\n\nfunction crossProductWhere(\n left: MaterializedWhere,\n right: MaterializedWhere,\n): MaterializedWhere {\n if (left.length * right.length > MAX_REPORT_FILTER_OR_GROUPS) {\n return reportFilterError(\n `Report filter expands beyond ${MAX_REPORT_FILTER_OR_GROUPS} OR groups`,\n );\n }\n return left.flatMap((leftGroup) =>\n right.map((rightGroup) => [...leftGroup, ...rightGroup]),\n );\n}\n\nfunction inverseOperator(\n operator: DataQueryFilterOperator,\n): DataQueryFilterOperator {\n switch (operator) {\n case 'eq':\n return 'ne';\n case 'ne':\n return 'eq';\n case 'gt':\n return 'lte';\n case 'gte':\n return 'lt';\n case 'lt':\n return 'gte';\n case 'lte':\n return 'gt';\n case 'in':\n return 'notIn';\n case 'notIn':\n return 'in';\n case 'like':\n return reportFilterError(\n 'Report filters do not support negating a LIKE predicate',\n );\n }\n}\n\nfunction collectionCondition(\n field: string,\n operator: DataQueryFilterOperator,\n value: DataQueryCondition['value'],\n): MaterializedWhere {\n const keyFor = (suffix: string) => (suffix ? `${field} ${suffix}` : field);\n const scalar = (key: string, scalarValue: unknown): MaterializedWhere => [\n [{ [key]: scalarValue }],\n ];\n\n if (operator === 'in') {\n const values = value as Array<string | number | boolean | null>;\n const nonNull = values.filter((entry) => entry !== null);\n if (nonNull.length === 0) return scalar(field, null);\n if (nonNull.length === values.length) return scalar(keyFor('in'), nonNull);\n // SQL IN does not match NULL. Model the user-visible union explicitly.\n return [[{ [field]: null }], [{ [keyFor('in')]: nonNull }]];\n }\n\n if (operator === 'notIn') {\n const values = value as Array<string | number | boolean | null>;\n // `buildWhere()` does not have a `NOT IN` primitive. A bounded AND of\n // inequality predicates has the same null-safe semantics and remains fully\n // validated by the collection.\n return [values.map((entry) => ({ [keyFor('!=')]: entry }))];\n }\n\n const suffix: Record<\n Exclude<DataQueryFilterOperator, 'in' | 'notIn'>,\n string\n > = {\n eq: '',\n ne: '!=',\n gt: '>',\n gte: '>=',\n lt: '<',\n lte: '<=',\n like: 'like',\n };\n return scalar(keyFor(suffix[operator]), value);\n}\n\nfunction materializedWhereForFilter(\n filter: DataQueryFilter,\n fields: Map<string, string>,\n negate = false,\n): MaterializedWhere {\n if (filter.kind === 'condition') {\n const field = fields.get(filter.field);\n if (!field) {\n return reportFilterError(\n `Report filter field is not declared: ${filter.field}`,\n );\n }\n return collectionCondition(\n field,\n negate ? inverseOperator(filter.operator) : filter.operator,\n filter.value,\n );\n }\n\n if (filter.kind === 'not') {\n return materializedWhereForFilter(filter.filter, fields, !negate);\n }\n\n const combineWithAnd =\n (filter.kind === 'all' && !negate) || (filter.kind === 'any' && negate);\n if (combineWithAnd) {\n return filter.filters.reduce<MaterializedWhere>(\n (combined, child) =>\n crossProductWhere(\n combined,\n materializedWhereForFilter(child, fields, negate),\n ),\n [[]],\n );\n }\n\n const groups = filter.filters.flatMap((child) =>\n materializedWhereForFilter(child, fields, negate),\n );\n if (groups.length > MAX_REPORT_FILTER_OR_GROUPS) {\n return reportFilterError(\n `Report filter expands beyond ${MAX_REPORT_FILTER_OR_GROUPS} OR groups`,\n );\n }\n return groups;\n}\n\n/**\n * Converts a normalized report filter to collection-owned, parameterized\n * predicates. The returned DNF form gives each OR branch a complete AND\n * clause, so tenant interception can add its tenant predicate to every branch.\n */\nfunction materializedWhere(\n filter: DataQueryFilter | undefined,\n fields: Map<string, string>,\n): MaterializedWhere | undefined {\n return filter ? materializedWhereForFilter(filter, fields) : undefined;\n}\n\nfunction filterScopeForColumn(\n column: ReportColumnDescriptor,\n): ReportFilterScope {\n return column.filterScope;\n}\n\n/**\n * Split the declared filter language by report semantics for consumers that\n * construct a live source query. Mixed AND expressions are represented as two\n * independent clauses; mixed OR/NOT expressions are rejected because moving\n * either half across WHERE/HAVING would change their meaning.\n */\nexport function splitReportFilterScopes(\n descriptor: ReportAdapterDescriptor,\n filter: DataQueryFilter | undefined,\n): { where?: DataQueryFilter; having?: DataQueryFilter } {\n if (!filter) return {};\n const columns = new Map(\n descriptor.columns.map((column) => [column.id, column]),\n );\n const combineAll = (\n filters: DataQueryFilter[],\n ): DataQueryFilter | undefined => {\n const flattened = filters.flatMap((candidate) =>\n candidate.kind === 'all' ? candidate.filters : [candidate],\n );\n return flattened.length === 0\n ? undefined\n : flattened.length === 1\n ? flattened[0]\n : { kind: 'all', filters: flattened };\n };\n const split = (\n candidate: DataQueryFilter,\n ): { where?: DataQueryFilter; having?: DataQueryFilter } => {\n if (candidate.kind === 'condition') {\n const column = columns.get(candidate.field);\n if (!column) {\n reportFilterError(\n `Report filter field is not declared: ${candidate.field}`,\n );\n }\n return filterScopeForColumn(column) === 'where'\n ? { where: candidate }\n : { having: candidate };\n }\n\n if (candidate.kind === 'all') {\n const children = candidate.filters.map(split);\n const where = combineAll(\n children.flatMap((child) => (child.where ? [child.where] : [])),\n );\n const having = combineAll(\n children.flatMap((child) => (child.having ? [child.having] : [])),\n );\n return {\n ...(where ? { where } : {}),\n ...(having ? { having } : {}),\n };\n }\n\n const children =\n candidate.kind === 'not'\n ? [split(candidate.filter)]\n : candidate.filters.map(split);\n const scopes = new Set(\n children.flatMap((child) => [\n ...(child.where ? (['where'] as const) : []),\n ...(child.having ? (['having'] as const) : []),\n ]),\n );\n if (scopes.size !== 1) {\n reportFilterError(\n 'Report WHERE and HAVING filters cannot be mixed inside one OR or NOT expression',\n );\n }\n const scope = scopes.has('where') ? 'where' : 'having';\n const filters = children.flatMap((child) => {\n const filter = scope === 'where' ? child.where : child.having;\n return filter ? [filter] : [];\n });\n if (candidate.kind === 'not') {\n const [filter] = filters;\n if (!filter) {\n reportFilterError('Report NOT expressions must contain one filter');\n }\n return scope === 'where'\n ? { where: { kind: 'not', filter } }\n : { having: { kind: 'not', filter } };\n }\n return scope === 'where'\n ? { where: { kind: 'any', filters } }\n : { having: { kind: 'any', filters } };\n };\n\n return split(filter);\n}\n\nfunction jsonSafeValue(\n value: unknown,\n ancestors = new WeakSet<object>(),\n): unknown {\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'bigint') {\n const numeric = Number(value);\n if (!Number.isSafeInteger(numeric)) {\n throw new RangeError(\n 'Materialized bigint values must be safely representable as numbers',\n );\n }\n return numeric;\n }\n if (value && typeof value === 'object') {\n if (ancestors.has(value)) {\n throw new TypeError('Values must not contain circular references');\n }\n ancestors.add(value);\n try {\n if (Array.isArray(value)) {\n return value.map((entry) => jsonSafeValue(entry, ancestors));\n }\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [\n key,\n jsonSafeValue(entry, ancestors),\n ]),\n );\n } finally {\n ancestors.delete(value);\n }\n }\n return value;\n}\n\nfunction mapMaterializedRow(\n row: Record<string, unknown>,\n projection: readonly string[],\n fieldMap: Map<string, string>,\n): Record<string, unknown> {\n const mapped: Record<string, unknown> = {};\n for (const id of projection) {\n const sourceField = fieldMap.get(id) ?? id;\n if (sourceField in row) mapped[id] = jsonSafeValue(row[sourceField]);\n }\n return mapped;\n}\n\nfunction queryFreshness(\n lifecycle: ReportLifecycleSnapshot,\n): DataQueryFreshness {\n switch (lifecycle.state) {\n case 'current':\n return {\n state: 'fresh',\n ...(lifecycle.asOf ? { asOf: lifecycle.asOf } : {}),\n };\n case 'stale':\n case 'lock-skipped':\n case 'failed':\n return {\n state: 'stale',\n ...(lifecycle.asOf ? { asOf: lifecycle.asOf } : {}),\n };\n case 'refreshing':\n return lifecycle.hasUsableRows\n ? {\n state: 'stale',\n ...(lifecycle.asOf ? { asOf: lifecycle.asOf } : {}),\n }\n : { state: 'unknown' };\n }\n}\n\n/**\n * Execute bounded materialized report reads through the canonical query\n * envelope. Every predicate is an adapter-declared field/operator pair; the\n * collection converts it to parameterized SQL and applies tenant interceptors\n * to list, count, and facet reads alike.\n */\nexport function queryReportMaterializedRows(\n reportCtor: new (...args: any[]) => SmrtObject,\n input: unknown,\n options: ReportBackgroundQueryOptions,\n): Promise<ReportBackgroundQueryResult>;\nexport function queryReportMaterializedRows(\n reportCtor: new (...args: any[]) => SmrtObject,\n input: unknown,\n options?: ReportQueryOptions,\n): Promise<ReportDataQueryResult>;\nexport async function queryReportMaterializedRows(\n reportCtor: new (...args: any[]) => SmrtObject,\n input: unknown,\n options: ReportQueryOptions | ReportBackgroundQueryOptions = {},\n): Promise<ReportDataQueryResult | ReportBackgroundQueryResult> {\n const descriptor = await buildReportAdapterDescriptor(\n reportCtor,\n options.adapter,\n );\n const request = normalizeDataQueryRequest(input, descriptor.schema);\n // Validate the semantic split even though materialized-row reads execute the\n // complete predicate against one persisted table. It prevents an adapter\n // consumer from silently treating a mixed source WHERE/HAVING expression as\n // either side of the aggregate boundary.\n splitReportFilterScopes(descriptor, request.filter);\n const queryFingerprint = createDataQueryFingerprint(\n request,\n descriptor.schema,\n );\n if (options.execution === 'background') {\n const queued = await options.enqueueBackgroundQuery({\n version: 1,\n execution: 'background',\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n request,\n inherits: ['principal', 'tenant', 'report-definition', 'field-policy'],\n });\n if (typeof queued.taskId !== 'string' || queued.taskId.length === 0) {\n throw new Error('Background report query hosts must return a task id');\n }\n return {\n version: 1,\n execution: 'background',\n status: 'queued',\n taskId: queued.taskId,\n queryFingerprint,\n };\n }\n const execution = options.execution ?? 'visible';\n const lifecycleDb = options.db;\n if (options.lifecycle && !lifecycleDb) {\n throw new Error('Report lifecycle disclosure requires a database handle');\n }\n const lifecycleOptions =\n options.lifecycle && lifecycleDb\n ? { db: lifecycleDb, ...options.lifecycle }\n : undefined;\n const lifecycleBefore = lifecycleOptions\n ? await getReportLifecycle(reportCtor, lifecycleOptions)\n : undefined;\n const fieldMap = publicFieldMap(descriptor);\n const where = materializedWhere(request.filter, fieldMap);\n const collection: NonNullable<ReportQueryOptions['collection']> =\n options.collection ??\n ((await ObjectRegistry.getCollection(descriptor.reportClassName, {\n db: options.db,\n })) as unknown as NonNullable<ReportQueryOptions['collection']>);\n let rows: Record<string, unknown>[] = [];\n let page: DataQueryResult['page'];\n let total: number;\n let facets: DataQueryResult['facets'];\n if (request.mode === 'rows') {\n const projection = request.projection ?? [descriptor.identityField];\n const select = projection.map((id) => fieldMap.get(id) ?? id);\n const offset = request.page?.kind === 'offset' ? request.page.offset : 0;\n const limit = request.page?.limit ?? 50;\n const orderBy = request.sort?.map(\n ({ field, direction }) =>\n `${fieldMap.get(field) ?? field} ${direction.toUpperCase()}`,\n );\n const materialized = await collection.list({\n select,\n offset,\n limit,\n orderBy: orderBy?.length === 1 ? orderBy[0] : orderBy,\n ...(where === undefined ? {} : { where }),\n });\n rows = materialized.map((row) =>\n mapMaterializedRow(row, projection, fieldMap),\n );\n rows.forEach((row) => {\n if (typeof row.id !== 'string' || row.id.length === 0) {\n throw new Error(\n 'Materialized report rows require a non-empty string id',\n );\n }\n });\n // SmrtReportCollection performs an eligible TTL refresh from list(). Count\n // must follow that operation so total/hasMore describe the same snapshot.\n total = await collection.count(where === undefined ? undefined : { where });\n page = {\n kind: 'offset',\n offset,\n limit,\n hasMore: offset + rows.length < total,\n };\n } else {\n // Count-only reads still enter SmrtReportCollection's list lifecycle to\n // refresh an eligible stale report before calculating its exact total.\n await collection.list({\n select: ['id'],\n offset: 0,\n limit: 1,\n orderBy: 'id ASC',\n ...(where === undefined ? {} : { where }),\n });\n total = await collection.count(where === undefined ? undefined : { where });\n if (request.mode === 'facets') {\n if (!collection.facets) {\n throw new Error(\n 'Report materialized facet reads require a collection facets() implementation',\n );\n }\n const requested = request.facets ?? [];\n const sourceFacets = await collection.facets({\n fields: requested.map((facet) => ({\n field: fieldMap.get(facet.field) ?? facet.field,\n limit: facet.limit,\n })),\n ...(where === undefined ? {} : { where }),\n });\n const byField = new Map(\n sourceFacets.map((facet) => [facet.field, facet]),\n );\n facets = requested.map((facet) => {\n const sourceField = fieldMap.get(facet.field) ?? facet.field;\n const values = byField.get(sourceField)?.values ?? [];\n return {\n field: facet.field,\n values: values.map((value) => ({\n value: value.value,\n count: value.count,\n })),\n // The collection deliberately bounds this database grouping query;\n // an exactly-full page may have more values, so report conservatively.\n truncated: values.length >= facet.limit,\n };\n });\n }\n }\n const result = {\n version: 1 as const,\n requestId: request.requestId,\n queryFingerprint,\n identityField: descriptor.identityField,\n rows,\n ...(page ? { page } : {}),\n total: { kind: 'exact' as const, value: total },\n ...(facets === undefined ? {} : { facets }),\n freshness: { state: 'unknown' as const },\n warnings: [],\n truncated: false,\n };\n const normalized = normalizeDataQueryResult(\n result,\n request,\n descriptor.schema,\n );\n if (!lifecycleBefore || !lifecycleOptions) {\n return { ...normalized, execution };\n }\n\n const lifecycleAfter = await getReportLifecycle(reportCtor, lifecycleOptions);\n return {\n ...normalized,\n execution,\n freshness: queryFreshness(lifecycleAfter),\n reportLifecycle: {\n snapshot: lifecycleAfter,\n read:\n lifecycleBefore.state !== 'current' &&\n lifecycleAfter.state === 'current'\n ? 'refresh-triggered'\n : lifecycleAfter.state === 'current'\n ? 'current'\n : 'stale',\n },\n };\n}\n\n/** Read the already-materialized primary key; never use a display/page index. */\nexport function reportMaterializedRowKey(row: Record<string, unknown>): string {\n const id = row.id;\n if (typeof id !== 'string' || id.length === 0) {\n throw new Error('Materialized report rows require a non-empty string id');\n }\n return id;\n}\n","import {\n ObjectRegistry,\n SmrtCollection,\n type SmrtListOptions,\n SmrtObject,\n type SmrtObjectOptions,\n type SmrtSelectedRow,\n type SmrtSelectField,\n type SmrtStiReadScope,\n} from '@happyvertical/smrt-core';\nimport { toSnakeCase } from '@happyvertical/smrt-core/utils';\nimport { getTenantId } from '@happyvertical/smrt-tenancy';\nimport { validateColumnName } from '@happyvertical/sql';\nimport { buildReportDefinition } from './compiler.js';\nimport { refreshReport } from './refresh.js';\nimport type { ReportRefreshOptions, ReportRefreshResult } from './types.js';\n\ntype RegistryField = {\n columnName?: string;\n _meta?: {\n columnName?: string;\n __tenancy?: { isTenantIdField?: boolean };\n };\n};\n\nfunction registryColumnName(fieldName: string, field?: RegistryField): string {\n return validateColumnName(\n field?.columnName ?? field?._meta?.columnName ?? toSnakeCase(fieldName),\n );\n}\n\nfunction findFieldColumn(\n fields: Map<string, RegistryField>,\n fieldName: string,\n): string {\n const direct = fields.get(fieldName);\n if (direct) return registryColumnName(fieldName, direct);\n const requestedColumn = toSnakeCase(fieldName);\n for (const [name, field] of fields.entries()) {\n if (toSnakeCase(name) === requestedColumn) {\n return registryColumnName(name, field);\n }\n }\n return validateColumnName(requestedColumn);\n}\n\nfunction findTenantColumn(\n fields: Map<string, RegistryField>,\n configuredField?: string,\n): string | null {\n if (configuredField) {\n return registryColumnName(configuredField, fields.get(configuredField));\n }\n for (const [fieldName, field] of fields.entries()) {\n if (fieldName === 'tenantId' || field?._meta?.__tenancy?.isTenantIdField) {\n return registryColumnName(fieldName, field);\n }\n }\n return null;\n}\n\nexport class SmrtReport extends SmrtObject {\n static readonly _isReportBase = true as const;\n\n refreshedAt: Date | null = null;\n\n constructor(options: SmrtObjectOptions = {}) {\n super(options);\n if (options.refreshedAt !== undefined) {\n this.refreshedAt =\n options.refreshedAt instanceof Date\n ? options.refreshedAt\n : options.refreshedAt\n ? new Date(options.refreshedAt)\n : null;\n }\n }\n\n isStale(ttlMs?: number): boolean {\n if (!this.refreshedAt) return true;\n if (ttlMs === undefined) return false;\n return Date.now() - this.refreshedAt.getTime() > ttlMs;\n }\n\n async refresh(\n options: Omit<ReportRefreshOptions, 'db'> = {},\n ): Promise<ReportRefreshResult> {\n const result = await refreshReport(this.constructor as typeof SmrtReport, {\n ...options,\n db: this.db,\n });\n this.refreshedAt = result.refreshedAt;\n return result;\n }\n}\n\nexport class SmrtReportCollection<\n ModelType extends SmrtReport,\n> extends SmrtCollection<ModelType> {\n private async refreshIfStale(): Promise<void> {\n const reportCtor = this.getItemClass();\n const definition = await buildReportDefinition(reportCtor);\n const refresh = definition.refresh;\n if (!refresh?.ttl || refresh.manual) return;\n\n const registered =\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name);\n const reportClass =\n registered?.qualifiedName ?? registered?.name ?? reportCtor.name;\n const tableName = ObjectRegistry.getTableName(reportClass);\n if (!tableName) return;\n\n const fields = (await ObjectRegistry.getAllFields(reportClass)) as Map<\n string,\n RegistryField\n >;\n const safeTableName = validateColumnName(tableName);\n const refreshedAtColumn = findFieldColumn(fields, 'refreshedAt');\n const tenantColumn = findTenantColumn(\n fields,\n registered?.tenantScopedConfig?.field,\n );\n const tenantId = getTenantId() ?? null;\n const result = tenantColumn\n ? await this.db.query(\n `SELECT MAX(${refreshedAtColumn}) AS refreshed_at FROM ${safeTableName} WHERE ${tenantColumn} ${tenantId ? '= ?' : 'IS NULL'}`,\n ...(tenantId ? [tenantId] : []),\n )\n : await this.db.query(\n `SELECT MAX(${refreshedAtColumn}) AS refreshed_at FROM ${safeTableName}`,\n );\n const refreshedAt = result.rows[0]?.refreshed_at\n ? new Date(result.rows[0].refreshed_at as string)\n : null;\n const stale =\n !refreshedAt || Date.now() - refreshedAt.getTime() > refresh.ttl;\n if (!stale) return;\n\n await refreshReport(reportCtor, {\n db: this.db,\n mode: refresh.mode ?? 'rebuild',\n trigger: 'ttl',\n tenantId,\n });\n }\n\n async refresh(\n options: Omit<ReportRefreshOptions, 'db'> = {},\n ): Promise<ReportRefreshResult> {\n return refreshReport(this.getItemClass(), {\n ...options,\n db: this.db,\n });\n }\n\n override async list<\n const Select extends readonly SmrtSelectField<ModelType>[],\n >(\n options: Omit<SmrtListOptions<ModelType>, 'select' | 'stiScope'> & {\n select: Select;\n include?: never;\n stiScope: SmrtStiReadScope;\n },\n ): Promise<Record<string, unknown>[]>;\n override async list<\n const Select extends readonly SmrtSelectField<ModelType>[],\n >(\n options: Omit<SmrtListOptions<ModelType>, 'select' | 'stiScope'> & {\n select: Select;\n include?: never;\n stiScope?: undefined;\n },\n ): Promise<SmrtSelectedRow<ModelType, Select>[]>;\n override async list(\n options: Omit<SmrtListOptions<ModelType>, 'select' | 'stiScope'> & {\n select?: undefined;\n stiScope: SmrtStiReadScope;\n },\n ): Promise<SmrtObject[]>;\n override async list(options?: undefined): Promise<ModelType[]>;\n override async list(\n options: Omit<SmrtListOptions<ModelType>, 'select' | 'stiScope'> & {\n select?: undefined;\n stiScope?: undefined;\n },\n ): Promise<ModelType[]>;\n override async list(\n options:\n | (Omit<SmrtListOptions<ModelType>, 'select'> & { select?: undefined })\n | undefined,\n ): Promise<SmrtObject[]>;\n override async list(\n options: SmrtListOptions<ModelType> | undefined,\n ): Promise<SmrtObject[] | Record<string, unknown>[]>;\n override async list(\n options: SmrtListOptions<ModelType> = {},\n ): Promise<SmrtObject[] | Record<string, unknown>[]> {\n await this.refreshIfStale();\n return super.list(options);\n }\n\n override async get(\n filter: Parameters<SmrtCollection<ModelType>['get']>[0],\n options: Parameters<SmrtCollection<ModelType>['get']>[1] = {},\n ): Promise<ModelType | null> {\n await this.refreshIfStale();\n return super.get(filter, options);\n }\n}\n","/**\n * Saved report-view and export contracts.\n *\n * This module owns only the serializable report boundary. Applications own\n * durable view storage, principal binding, authorization, auditing, artifact\n * storage, download tokens, and background-job execution.\n */\nimport { createHash } from 'node:crypto';\nimport {\n createDataQueryFingerprint,\n normalizeDataQueryRequest,\n} from '@happyvertical/smrt-core';\nimport type {\n DataQueryFreshness,\n DataQueryRequest,\n DataQueryResult,\n DataQuerySort,\n} from '@happyvertical/smrt-types';\nimport {\n type ReportAdapterDescriptor,\n type ReportColumnCapability,\n type ReportDataQueryResult,\n splitReportFilterScopes,\n} from './adapter.js';\n\nconst INHERITED_REPORT_SCOPE = [\n 'principal',\n 'tenant',\n 'report-definition',\n 'field-policy',\n] as const;\n\nconst EXPORT_FORMATS = new Set<ReportExportFormat>(['csv', 'json']);\nconst PINNED_COLUMN_SIDES = new Set<ReportSavedViewPinnedSide>([\n 'start',\n 'end',\n]);\nconst DISPLAY_DENSITIES = new Set<ReportSavedViewDisplay['density']>([\n 'compact',\n 'comfortable',\n]);\nconst RFC_3339_INSTANT =\n /^(\\d{4})-(\\d{2})-(\\d{2})T([01]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)(?:\\.\\d{1,9})?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$/;\n\nconst DEFAULT_EXPORT_LIMITS: ReportExportLimits = {\n maxRows: 100_000,\n maxBytes: 25_000_000,\n deadlineMs: 60_000,\n foregroundRowLimit: 1_000,\n};\n\nconst MAX_EXPORT_LIMITS = {\n maxRows: 1_000_000,\n maxBytes: 100_000_000,\n deadlineMs: 300_000,\n foregroundRowLimit: 100_000,\n} as const;\n\nexport class ReportSurfaceValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ReportSurfaceValidationError';\n }\n}\n\nexport type ReportSavedViewPinnedSide = 'start' | 'end';\n\n/** A view's column order is its array order; ids are always adapter-defined. */\nexport interface ReportSavedViewColumn {\n id: string;\n width?: number;\n pinned?: ReportSavedViewPinnedSide;\n}\n\nexport interface ReportSavedViewGrouping {\n fields: string[];\n expanded?: boolean;\n}\n\nexport interface ReportSavedViewDisplay {\n density: 'compact' | 'comfortable';\n showTotals?: boolean;\n}\n\n/**\n * A normalized, policy-neutral stored view. The storage host associates it\n * with its owner and tenant; neither identity is accepted here.\n */\nexport interface ReportSavedView {\n version: 1;\n id: string;\n title: string;\n resourceId: string;\n reportClassName: string;\n definitionFingerprint: string;\n query: DataQueryRequest;\n columns: ReportSavedViewColumn[];\n grouping?: ReportSavedViewGrouping;\n display?: ReportSavedViewDisplay;\n}\n\nexport type ReportExportFormat = 'csv' | 'json';\n\n/**\n * Opaque identity of a host-captured materialization snapshot. The application\n * creates and verifies it while bound to its principal and tenant; callers\n * cannot turn an `asOf` label into a snapshot by supplying this id alone.\n */\nexport interface ReportExportSnapshotBinding {\n id: string;\n}\n\nexport interface ReportExportSnapshot {\n version: 1;\n resourceId: string;\n reportClassName: string;\n definitionFingerprint: string;\n binding: ReportExportSnapshotBinding;\n request: DataQueryRequest;\n queryFingerprint: string;\n projection: string[];\n sort: DataQuerySort[];\n freshness: DataQueryFreshness;\n snapshot: {\n asOf: string;\n refreshedAt?: string;\n stale: boolean;\n };\n total: {\n kind: 'exact';\n value: number;\n };\n inherits: Array<(typeof INHERITED_REPORT_SCOPE)[number]>;\n}\n\n/** Every export is bounded before it is streamed or delegated to a worker. */\nexport interface ReportExportLimits {\n maxRows: number;\n maxBytes: number;\n deadlineMs: number;\n /** Exports above this exact row count must use the application queue. */\n foregroundRowLimit: number;\n}\n\n/**\n * A deterministic offset-page plan over the immutable host snapshot. A worker\n * starts at offset zero and advances by `page.limit` until `rowCount` rows have\n * been produced or the byte/deadline limits are reached.\n */\nexport interface ReportExportReadPlan {\n snapshotId: string;\n queryFingerprint: string;\n asOf: string;\n page: {\n kind: 'offset';\n offset: 0;\n limit: number;\n };\n}\n\nexport interface ReportExportRequest {\n version: 1;\n format: ReportExportFormat;\n execution: 'stream' | 'background';\n snapshot: ReportExportSnapshot;\n limits: ReportExportLimits;\n read: ReportExportReadPlan;\n rowCount: number;\n truncated: boolean;\n action: {\n id: 'export';\n requiredPermission: string;\n auditRequired: true;\n confirmationRequired: boolean;\n };\n}\n\n/**\n * An authority-free job handoff. The queue must re-enter the bound principal\n * and tenant, resolve the current report definition/field policy, and verify\n * this snapshot before materializing an artifact.\n */\nexport interface ReportBackgroundExportTask {\n version: 1;\n execution: 'background';\n request: ReportExportRequest;\n inherits: Array<(typeof INHERITED_REPORT_SCOPE)[number]>;\n}\n\nexport interface ReportExportProgress {\n state: 'queued' | 'running' | 'completed' | 'failed';\n rowCount: number;\n byteCount: number;\n truncated: boolean;\n}\n\n/**\n * Artifact metadata is deliberately URL- and token-free. An application-owned\n * serving boundary must authorize every download using the caller's current\n * principal and tenant before it reveals any artifact location.\n */\nexport interface ReportExportArtifact {\n id: string;\n request: ReportExportRequest;\n progress: ReportExportProgress;\n expiresAt: string;\n}\n\nexport interface ReportExportActionContext {\n phase: 'preview' | 'apply';\n reportClassName: string;\n resourceId: string;\n requiredPermission: string;\n confirmationRequired: boolean;\n execution: 'stream' | 'background';\n queryFingerprint: string;\n definitionFingerprint: string;\n snapshotId: string;\n asOf: string;\n pageSize: number;\n}\n\n/** The same host is used by human and agent callers. */\nexport interface ReportExportActionHost {\n /**\n * Prove that this opaque binding still resolves to the exact immutable\n * materialization snapshot under the host's current principal and tenant.\n * The background worker must call `validateReportExportExecution()` again\n * before reading or serving any rows.\n */\n assertSnapshot(context: ReportExportSnapshotContext): Promise<void> | void;\n authorize(context: ReportExportActionContext): Promise<void> | void;\n audit(context: ReportExportActionContext): Promise<void> | void;\n enqueue?(\n task: ReportBackgroundExportTask,\n ): Promise<{ taskId: string }> | { taskId: string };\n}\n\nexport interface ReportExportSnapshotContext {\n bindingId: string;\n resourceId: string;\n reportClassName: string;\n definitionFingerprint: string;\n queryFingerprint: string;\n asOf: string;\n}\n\nexport interface ReportExportPreview {\n phase: 'preview';\n action: ReportExportActionContext;\n request: ReportExportRequest;\n}\n\nexport type AppliedReportExport =\n | {\n phase: 'apply';\n execution: 'stream';\n status: 'ready';\n request: ReportExportRequest;\n }\n | {\n phase: 'apply';\n execution: 'background';\n status: 'queued';\n taskId: string;\n request: ReportExportRequest;\n };\n\ninterface SavedViewInput {\n version?: unknown;\n id: unknown;\n title: unknown;\n resourceId?: unknown;\n reportClassName?: unknown;\n definitionFingerprint?: unknown;\n query: unknown;\n columns?: unknown;\n grouping?: unknown;\n display?: unknown;\n}\n\ninterface ExportRequestInput {\n format: unknown;\n limits?: unknown;\n}\n\nfunction fail(message: string): never {\n throw new ReportSurfaceValidationError(message);\n}\n\nfunction plainObject(value: unknown, label: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return fail(`${label} must be a plain object`);\n }\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) {\n return fail(`${label} must be a plain object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction exactKeys(\n value: Record<string, unknown>,\n allowed: readonly string[],\n label: string,\n): void {\n for (const key of Object.keys(value)) {\n if (!allowed.includes(key)) {\n fail(`${label} contains an unsupported field: ${key}`);\n }\n }\n}\n\nfunction requiredString(\n value: unknown,\n label: string,\n maxLength = 256,\n): string {\n if (typeof value !== 'string') return fail(`${label} must be a string`);\n const normalized = value.trim();\n if (!normalized) return fail(`${label} cannot be empty`);\n if (normalized.length > maxLength) {\n return fail(`${label} cannot exceed ${String(maxLength)} characters`);\n }\n return normalized;\n}\n\nfunction optionalBoolean(value: unknown, label: string): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'boolean') return fail(`${label} must be a boolean`);\n return value;\n}\n\nfunction boundedInteger(\n value: unknown,\n label: string,\n minimum: number,\n maximum: number,\n): number {\n if (!Number.isInteger(value) || (value as number) < minimum) {\n return fail(`${label} must be an integer of at least ${String(minimum)}`);\n }\n if ((value as number) > maximum) {\n return fail(`${label} cannot exceed ${String(maximum)}`);\n }\n return value as number;\n}\n\nfunction normalizeIso(value: unknown, label: string): string {\n const text = requiredString(value, label, 64);\n const match = RFC_3339_INSTANT.exec(text);\n if (!match) return fail(`${label} must be an RFC 3339 instant`);\n const [year, month, day] = match.slice(1, 4).map(Number);\n const calendar = new Date(0);\n calendar.setUTCFullYear(year, month - 1, day);\n calendar.setUTCHours(0, 0, 0, 0);\n if (\n calendar.getUTCFullYear() !== year ||\n calendar.getUTCMonth() !== month - 1 ||\n calendar.getUTCDate() !== day\n ) {\n return fail(`${label} must be an RFC 3339 instant`);\n }\n const parsed = new Date(text);\n if (Number.isNaN(parsed.getTime())) {\n return fail(`${label} must be an RFC 3339 instant`);\n }\n return parsed.toISOString();\n}\n\nfunction normalizeSnapshotBinding(value: unknown): ReportExportSnapshotBinding {\n const input = plainObject(value, 'Report export snapshot binding');\n exactKeys(input, ['id'], 'Report export snapshot binding');\n return {\n id: requiredString(input.id, 'Report export snapshot binding id'),\n };\n}\n\nfunction stable(value: unknown): string {\n if (\n value === null ||\n typeof value === 'boolean' ||\n typeof value === 'number'\n ) {\n return JSON.stringify(value);\n }\n if (typeof value === 'string') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`;\n const object = plainObject(value, 'Report surface value');\n return (\n '{' +\n Object.keys(object)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${stable(object[key])}`)\n .join(',') +\n '}'\n );\n}\n\n/**\n * A change to report fields, their capabilities, schema, scope, or source\n * invalidates a stored view/export snapshot rather than silently widening it.\n */\nexport function reportDefinitionFingerprint(\n descriptor: ReportAdapterDescriptor,\n): string {\n const definition = {\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n sourceClassName: descriptor.sourceClassName,\n tenantScoped: descriptor.tenantScoped,\n tenantField: descriptor.tenantField ?? null,\n identityField: descriptor.identityField,\n columns: descriptor.columns.map((column) => ({\n id: column.id,\n fieldName: column.fieldName,\n kind: column.kind,\n filterScope: column.filterScope,\n type: column.type,\n projectable: column.projectable === true,\n sortable: column.sortable === true,\n facetable: column.facetable === true,\n filterOperators: column.filterOperators ?? [],\n capabilities: [...column.capabilities].sort(),\n sensitivity: column.sensitivity ?? null,\n sourceColumn: column.sourceColumn ?? null,\n bucket: column.bucket ?? null,\n aggregate: column.aggregate ?? null,\n distinct: column.distinct ?? null,\n })),\n schema: descriptor.schema,\n };\n return (\n 'rv1_' +\n createHash('sha256').update(stable(definition), 'utf8').digest('base64url')\n );\n}\n\nfunction columnMap(\n descriptor: ReportAdapterDescriptor,\n): Map<string, ReportAdapterDescriptor['columns'][number]> {\n return new Map(descriptor.columns.map((column) => [column.id, column]));\n}\n\nfunction requireColumnCapability(\n descriptor: ReportAdapterDescriptor,\n id: unknown,\n capability: ReportColumnCapability,\n label: string,\n): string {\n const columnId = requiredString(id, label);\n const column = columnMap(descriptor).get(columnId);\n if (!column?.capabilities.includes(capability)) {\n return fail(`${label} is not available under the current report policy`);\n }\n return columnId;\n}\n\nfunction normalizeColumns(\n descriptor: ReportAdapterDescriptor,\n value: unknown,\n): ReportSavedViewColumn[] {\n if (value === undefined) {\n return descriptor.columns\n .filter((column) => column.capabilities.includes('project'))\n .map((column) => ({ id: column.id }));\n }\n if (!Array.isArray(value))\n return fail('Saved report view columns must be an array');\n if (value.length > descriptor.columns.length) {\n return fail(\n 'Saved report view columns cannot repeat or exceed exposed columns',\n );\n }\n const ids = new Set<string>();\n return value.map((entry, index) => {\n const input = plainObject(\n entry,\n `Saved report view columns[${String(index)}]`,\n );\n const id = requireColumnCapability(\n descriptor,\n input.id,\n 'project',\n `Saved report view columns[${String(index)}].id`,\n );\n if (ids.has(id)) return fail('Saved report view columns cannot repeat ids');\n ids.add(id);\n const width =\n input.width === undefined\n ? undefined\n : boundedInteger(\n input.width,\n `Saved report view columns[${String(index)}].width`,\n 40,\n 2_000,\n );\n const pinned =\n input.pinned === undefined\n ? undefined\n : requiredString(\n input.pinned,\n `Saved report view columns[${String(index)}].pinned`,\n 16,\n );\n if (\n pinned !== undefined &&\n !PINNED_COLUMN_SIDES.has(pinned as ReportSavedViewPinnedSide)\n ) {\n return fail(\n `Saved report view columns[${String(index)}].pinned is invalid`,\n );\n }\n return {\n id,\n ...(width === undefined ? {} : { width }),\n ...(pinned === undefined\n ? {}\n : { pinned: pinned as ReportSavedViewPinnedSide }),\n };\n });\n}\n\nfunction normalizeGrouping(\n descriptor: ReportAdapterDescriptor,\n value: unknown,\n): ReportSavedViewGrouping | undefined {\n if (value === undefined) return undefined;\n const input = plainObject(value, 'Saved report view grouping');\n if (!Array.isArray(input.fields)) {\n return fail('Saved report view grouping.fields must be an array');\n }\n const fields = input.fields.map((field, index) =>\n requireColumnCapability(\n descriptor,\n field,\n 'group',\n `Saved report view grouping.fields[${String(index)}]`,\n ),\n );\n if (new Set(fields).size !== fields.length) {\n return fail('Saved report view grouping.fields cannot repeat ids');\n }\n const expanded = optionalBoolean(\n input.expanded,\n 'Saved report view grouping.expanded',\n );\n return {\n fields,\n ...(expanded === undefined ? {} : { expanded }),\n };\n}\n\nfunction normalizeDisplay(value: unknown): ReportSavedViewDisplay | undefined {\n if (value === undefined) return undefined;\n const input = plainObject(value, 'Saved report view display');\n const density = requiredString(\n input.density,\n 'Saved report view display.density',\n 16,\n );\n if (!DISPLAY_DENSITIES.has(density as ReportSavedViewDisplay['density'])) {\n return fail('Saved report view display.density is invalid');\n }\n const showTotals = optionalBoolean(\n input.showTotals,\n 'Saved report view display.showTotals',\n );\n return {\n density: density as ReportSavedViewDisplay['density'],\n ...(showTotals === undefined ? {} : { showTotals }),\n };\n}\n\n/**\n * Version 0 is the original unversioned persisted layout shape. It contains\n * the same allowlisted fields as v1, so the migration only records the schema\n * version; current descriptor normalization still revalidates every field.\n */\nexport function migrateReportSavedView(value: unknown): SavedViewInput {\n const input = plainObject(\n value,\n 'Saved report view',\n ) as unknown as SavedViewInput;\n if (input.version === undefined || input.version === 0) {\n return { ...input, version: 1 };\n }\n if (input.version === 1) return input;\n return fail('Saved report view version is unsupported');\n}\n\n/**\n * Normalize a view both when it is saved and when it is restored. Re-running\n * this against the live descriptor is what prevents a former field/action\n * grant from surviving a policy or definition change.\n */\nexport function normalizeReportSavedView(\n descriptor: ReportAdapterDescriptor,\n value: unknown,\n): ReportSavedView {\n const input = migrateReportSavedView(value);\n const fingerprint = reportDefinitionFingerprint(descriptor);\n if (\n input.resourceId !== undefined &&\n input.resourceId !== descriptor.resourceId\n ) {\n return fail('Saved report view belongs to a different report resource');\n }\n if (\n input.reportClassName !== undefined &&\n input.reportClassName !== descriptor.reportClassName\n ) {\n return fail('Saved report view belongs to a different report class');\n }\n if (\n input.definitionFingerprint !== undefined &&\n input.definitionFingerprint !== fingerprint\n ) {\n return fail('Saved report view definition has changed');\n }\n const query = normalizeDataQueryRequest(input.query, descriptor.schema);\n if (query.mode !== 'rows') {\n return fail('Saved report views must use a rows query');\n }\n splitReportFilterScopes(descriptor, query.filter);\n const columns = normalizeColumns(descriptor, input.columns);\n const grouping = normalizeGrouping(descriptor, input.grouping);\n const display = normalizeDisplay(input.display);\n return {\n version: 1,\n id: requiredString(input.id, 'Saved report view id'),\n title: requiredString(input.title, 'Saved report view title', 240),\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n definitionFingerprint: fingerprint,\n query,\n columns,\n ...(grouping === undefined ? {} : { grouping }),\n ...(display === undefined ? {} : { display }),\n };\n}\n\n/** Restore through current policy rather than trusting a stored JSON record. */\nexport function restoreReportSavedView(\n descriptor: ReportAdapterDescriptor,\n savedView: ReportSavedView,\n): ReportSavedView {\n return normalizeReportSavedView(descriptor, savedView);\n}\n\n/**\n * Freeze a previously executed materialized read for export. The caller must\n * supply the exact request and a result that exposes lifecycle freshness; an\n * export never invents an as-of value or accepts a caller-supplied tenant.\n */\nexport function createReportExportSnapshot(\n descriptor: ReportAdapterDescriptor,\n input: unknown,\n result: Pick<\n DataQueryResult,\n 'queryFingerprint' | 'identityField' | 'total' | 'freshness' | 'truncated'\n > &\n Pick<ReportDataQueryResult, 'reportLifecycle'>,\n binding: unknown,\n): ReportExportSnapshot {\n const request = normalizeDataQueryRequest(input, descriptor.schema);\n if (request.mode !== 'rows') {\n return fail('Report export snapshots require a rows query');\n }\n const queryFingerprint = createDataQueryFingerprint(\n request,\n descriptor.schema,\n );\n if (result.queryFingerprint !== queryFingerprint) {\n return fail(\n 'Report export result does not match the requested query fingerprint',\n );\n }\n if (result.identityField !== descriptor.identityField) {\n return fail('Report export result has an unexpected row identity field');\n }\n if (result.total.kind !== 'exact') {\n return fail('Report export snapshots require an exact row count');\n }\n if (!Number.isSafeInteger(result.total.value) || result.total.value < 0) {\n return fail(\n 'Report export snapshots require a safe non-negative row count',\n );\n }\n if (result.truncated) {\n return fail('Report export snapshots cannot use a truncated query result');\n }\n if (result.freshness.state === 'unknown') {\n return fail('Report export snapshots require lifecycle freshness');\n }\n const lifecycleAsOf = result.reportLifecycle?.snapshot.asOf;\n const freshnessAsOf = result.freshness.asOf;\n const normalizedLifecycleAsOf = lifecycleAsOf\n ? normalizeIso(lifecycleAsOf, 'Report export lifecycle snapshot asOf')\n : undefined;\n const normalizedFreshnessAsOf = freshnessAsOf\n ? normalizeIso(freshnessAsOf, 'Report export freshness asOf')\n : undefined;\n if (\n normalizedLifecycleAsOf !== undefined &&\n normalizedFreshnessAsOf !== undefined &&\n normalizedLifecycleAsOf !== normalizedFreshnessAsOf\n ) {\n return fail(\n 'Report export lifecycle freshness does not match its snapshot',\n );\n }\n const normalizedAsOf = normalizedLifecycleAsOf ?? normalizedFreshnessAsOf;\n if (!normalizedAsOf) {\n return fail(\n 'Report export snapshots require an as-of materialization instant',\n );\n }\n const refreshedAt = result.reportLifecycle?.snapshot.refreshedAt;\n return {\n version: 1,\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n definitionFingerprint: reportDefinitionFingerprint(descriptor),\n binding: normalizeSnapshotBinding(binding),\n request,\n queryFingerprint,\n projection: [...(request.projection ?? [descriptor.identityField])],\n sort: [\n ...(request.sort ??\n descriptor.schema.defaultSort ?? [\n { field: descriptor.identityField, direction: 'asc' },\n ]),\n ],\n freshness: {\n state: result.freshness.state,\n asOf: normalizedAsOf,\n },\n snapshot: {\n asOf: normalizedAsOf,\n ...(refreshedAt\n ? {\n refreshedAt: normalizeIso(\n refreshedAt,\n 'Report export snapshot refreshedAt',\n ),\n }\n : {}),\n stale: result.freshness.state === 'stale',\n },\n total: { kind: 'exact', value: result.total.value },\n inherits: [...INHERITED_REPORT_SCOPE],\n };\n}\n\n/** Reject definitions/policies that changed after a snapshot was frozen. */\nexport function verifyReportExportSnapshot(\n descriptor: ReportAdapterDescriptor,\n snapshot: ReportExportSnapshot,\n): ReportExportSnapshot {\n const input = plainObject(snapshot, 'Report export snapshot');\n exactKeys(\n input,\n [\n 'version',\n 'resourceId',\n 'reportClassName',\n 'definitionFingerprint',\n 'binding',\n 'request',\n 'queryFingerprint',\n 'projection',\n 'sort',\n 'freshness',\n 'snapshot',\n 'total',\n 'inherits',\n ],\n 'Report export snapshot',\n );\n if (snapshot.version !== 1)\n return fail('Report export snapshot version is unsupported');\n if (\n snapshot.resourceId !== descriptor.resourceId ||\n snapshot.reportClassName !== descriptor.reportClassName\n ) {\n return fail('Report export snapshot belongs to a different report');\n }\n if (\n snapshot.definitionFingerprint !== reportDefinitionFingerprint(descriptor)\n ) {\n return fail('Report export definition has changed');\n }\n const binding = normalizeSnapshotBinding(snapshot.binding);\n const normalized = normalizeDataQueryRequest(\n snapshot.request,\n descriptor.schema,\n );\n if (normalized.mode !== 'rows') {\n return fail('Report export snapshot query must remain a rows query');\n }\n if (\n createDataQueryFingerprint(normalized, descriptor.schema) !==\n snapshot.queryFingerprint\n ) {\n return fail('Report export snapshot query fingerprint is invalid');\n }\n const expectedProjection = normalized.projection ?? [\n descriptor.identityField,\n ];\n const expectedSort = normalized.sort ??\n descriptor.schema.defaultSort ?? [\n { field: descriptor.identityField, direction: 'asc' },\n ];\n if (\n !Array.isArray(snapshot.projection) ||\n snapshot.projection.some((field) => typeof field !== 'string')\n ) {\n return fail('Report export snapshot projection is invalid');\n }\n if (\n snapshot.projection.length !== expectedProjection.length ||\n snapshot.projection.some(\n (field, index) => field !== expectedProjection[index],\n )\n ) {\n return fail('Report export snapshot projection or sort has changed');\n }\n if (!Array.isArray(snapshot.sort)) {\n return fail('Report export snapshot sort is invalid');\n }\n const sort = snapshot.sort.map((value) => {\n const sortInput = plainObject(value, 'Report export snapshot sort');\n exactKeys(sortInput, ['field', 'direction'], 'Report export snapshot sort');\n return {\n field: requiredString(\n sortInput.field,\n 'Report export snapshot sort field',\n ),\n direction: requiredString(\n sortInput.direction,\n 'Report export snapshot sort direction',\n ),\n };\n });\n if (stable(sort) !== stable(expectedSort)) {\n return fail('Report export snapshot projection or sort has changed');\n }\n const freshness = plainObject(\n snapshot.freshness,\n 'Report export snapshot freshness',\n );\n exactKeys(freshness, ['state', 'asOf'], 'Report export snapshot freshness');\n const materialization = plainObject(\n snapshot.snapshot,\n 'Report export snapshot materialization',\n );\n exactKeys(\n materialization,\n ['asOf', 'refreshedAt', 'stale'],\n 'Report export snapshot materialization',\n );\n const total = plainObject(snapshot.total, 'Report export snapshot total');\n exactKeys(total, ['kind', 'value'], 'Report export snapshot total');\n const totalValue = total.value;\n const asOf = normalizeIso(\n materialization.asOf,\n 'Report export snapshot asOf',\n );\n if (freshness.state !== 'fresh' && freshness.state !== 'stale') {\n return fail('Report export snapshot freshness state is invalid');\n }\n const freshnessAsOf = normalizeIso(\n freshness.asOf,\n 'Report export snapshot freshness asOf',\n );\n if (freshnessAsOf !== asOf) {\n return fail(\n 'Report export snapshot freshness does not match its as-of instant',\n );\n }\n const refreshedAt = materialization.refreshedAt\n ? normalizeIso(\n materialization.refreshedAt,\n 'Report export snapshot refreshedAt',\n )\n : undefined;\n if (materialization.stale !== (freshness.state === 'stale')) {\n return fail('Report export snapshot stale state is invalid');\n }\n if (\n total.kind !== 'exact' ||\n typeof totalValue !== 'number' ||\n !Number.isSafeInteger(totalValue) ||\n totalValue < 0\n ) {\n return fail('Report export snapshot row count is invalid');\n }\n if (\n !Array.isArray(snapshot.inherits) ||\n snapshot.inherits.some((value) => typeof value !== 'string') ||\n snapshot.inherits.length !== INHERITED_REPORT_SCOPE.length ||\n snapshot.inherits.some(\n (value, index) => value !== INHERITED_REPORT_SCOPE[index],\n )\n ) {\n return fail('Report export snapshot inheritance contract is invalid');\n }\n return {\n version: 1,\n resourceId: descriptor.resourceId,\n reportClassName: descriptor.reportClassName,\n definitionFingerprint: reportDefinitionFingerprint(descriptor),\n binding,\n request: normalized,\n queryFingerprint: createDataQueryFingerprint(normalized, descriptor.schema),\n projection: [...expectedProjection],\n sort: expectedSort.map(({ field, direction }) => ({ field, direction })),\n freshness: { state: freshness.state, asOf: freshnessAsOf },\n snapshot: {\n asOf,\n stale: materialization.stale,\n ...(refreshedAt === undefined ? {} : { refreshedAt }),\n },\n total: { kind: 'exact', value: totalValue },\n inherits: [...INHERITED_REPORT_SCOPE],\n };\n}\n\nfunction normalizeExportLimits(value: unknown): ReportExportLimits {\n if (value === undefined) return { ...DEFAULT_EXPORT_LIMITS };\n const input = plainObject(value, 'Report export limits');\n const maxRows = boundedInteger(\n input.maxRows ?? DEFAULT_EXPORT_LIMITS.maxRows,\n 'Report export limits.maxRows',\n 1,\n MAX_EXPORT_LIMITS.maxRows,\n );\n const maxBytes = boundedInteger(\n input.maxBytes ?? DEFAULT_EXPORT_LIMITS.maxBytes,\n 'Report export limits.maxBytes',\n 1,\n MAX_EXPORT_LIMITS.maxBytes,\n );\n const deadlineMs = boundedInteger(\n input.deadlineMs ?? DEFAULT_EXPORT_LIMITS.deadlineMs,\n 'Report export limits.deadlineMs',\n 1,\n MAX_EXPORT_LIMITS.deadlineMs,\n );\n const foregroundRowLimit = boundedInteger(\n input.foregroundRowLimit ??\n Math.min(DEFAULT_EXPORT_LIMITS.foregroundRowLimit, maxRows),\n 'Report export limits.foregroundRowLimit',\n 1,\n Math.min(maxRows, MAX_EXPORT_LIMITS.foregroundRowLimit),\n );\n return { maxRows, maxBytes, deadlineMs, foregroundRowLimit };\n}\n\nfunction hasSensitiveProjection(\n descriptor: ReportAdapterDescriptor,\n snapshot: ReportExportSnapshot,\n): boolean {\n const columns = columnMap(descriptor);\n return snapshot.projection.some((id) => {\n const sensitivity = columns.get(id)?.sensitivity;\n return (\n sensitivity === 'personal' ||\n sensitivity === 'sensitive' ||\n sensitivity === 'secret'\n );\n });\n}\n\nfunction createReadPlan(\n descriptor: ReportAdapterDescriptor,\n snapshot: ReportExportSnapshot,\n limits: ReportExportLimits,\n): ReportExportReadPlan {\n return {\n snapshotId: snapshot.binding.id,\n queryFingerprint: snapshot.queryFingerprint,\n asOf: snapshot.snapshot.asOf,\n page: {\n kind: 'offset',\n offset: 0,\n limit: Math.min(\n limits.maxRows,\n descriptor.schema.maxPageLimit ??\n DEFAULT_EXPORT_LIMITS.foregroundRowLimit,\n ),\n },\n };\n}\n\n/**\n * Build one common request for both agent and human export clients. The\n * resulting request carries no actor, tenant, storage location, or token.\n */\nexport function createReportExportRequest(\n descriptor: ReportAdapterDescriptor,\n snapshot: ReportExportSnapshot,\n value: unknown,\n): ReportExportRequest {\n const input = plainObject(\n value,\n 'Report export request',\n ) as unknown as ExportRequestInput;\n const verified = verifyReportExportSnapshot(descriptor, snapshot);\n const format = requiredString(input.format, 'Report export format', 16);\n if (!EXPORT_FORMATS.has(format as ReportExportFormat)) {\n return fail('Report export format is unsupported');\n }\n const limits = normalizeExportLimits(input.limits);\n const rowCount = Math.min(verified.total.value, limits.maxRows);\n const confirmationRequired = hasSensitiveProjection(descriptor, verified);\n return {\n version: 1,\n format: format as ReportExportFormat,\n execution:\n verified.total.value > limits.foregroundRowLimit\n ? 'background'\n : 'stream',\n snapshot: verified,\n limits,\n read: createReadPlan(descriptor, verified, limits),\n rowCount,\n truncated: verified.total.value > limits.maxRows,\n action: {\n id: 'export',\n requiredPermission: 'reports.export',\n auditRequired: true,\n confirmationRequired,\n },\n };\n}\n\n/**\n * Recreate the deterministic request from its current descriptor and frozen\n * snapshot. Preview, apply, queue, and serving hosts call this instead of\n * trusting browser- or agent-supplied action metadata.\n */\nexport function validateReportExportRequest(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n): ReportExportRequest {\n const expected = createReportExportRequest(descriptor, request.snapshot, {\n format: request.format,\n limits: request.limits,\n });\n if (stable(request) !== stable(expected)) {\n return fail('Report export request does not match current policy');\n }\n return expected;\n}\n\n/**\n * Build one bounded page of the immutable export read. The host applies the\n * opaque snapshot binding separately; this function preserves the frozen\n * projection, predicate, and deterministic sort while replacing only visible\n * pagination. A zero-row export therefore has no pages to execute.\n */\nexport function createReportExportPageRequest(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n offset: number,\n): DataQueryRequest {\n const verified = validateReportExportRequest(descriptor, request);\n if (\n !Number.isSafeInteger(offset) ||\n offset < 0 ||\n offset >= verified.rowCount\n ) {\n return fail('Report export page offset is outside the bounded export');\n }\n const limit = Math.min(verified.read.page.limit, verified.rowCount - offset);\n return normalizeDataQueryRequest(\n {\n ...verified.snapshot.request,\n page: { kind: 'offset', offset, limit },\n },\n descriptor.schema,\n );\n}\n\nfunction actionContext(\n phase: ReportExportActionContext['phase'],\n request: ReportExportRequest,\n): ReportExportActionContext {\n return {\n phase,\n reportClassName: request.snapshot.reportClassName,\n resourceId: request.snapshot.resourceId,\n requiredPermission: request.action.requiredPermission,\n confirmationRequired: request.action.confirmationRequired,\n execution: request.execution,\n queryFingerprint: request.snapshot.queryFingerprint,\n definitionFingerprint: request.snapshot.definitionFingerprint,\n snapshotId: request.read.snapshotId,\n asOf: request.read.asOf,\n pageSize: request.read.page.limit,\n };\n}\n\nfunction snapshotContext(\n request: ReportExportRequest,\n): ReportExportSnapshotContext {\n return {\n bindingId: request.snapshot.binding.id,\n resourceId: request.snapshot.resourceId,\n reportClassName: request.snapshot.reportClassName,\n definitionFingerprint: request.snapshot.definitionFingerprint,\n queryFingerprint: request.snapshot.queryFingerprint,\n asOf: request.snapshot.snapshot.asOf,\n };\n}\n\n/**\n * Revalidate the request and prove the opaque snapshot binding through the\n * application host. Preview, apply, and every background worker use this same\n * operation before reading or serving export data.\n */\nexport async function validateReportExportExecution(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n host: Pick<ReportExportActionHost, 'assertSnapshot'>,\n): Promise<ReportExportRequest> {\n const verified = validateReportExportRequest(descriptor, request);\n await host.assertSnapshot(snapshotContext(verified));\n return verified;\n}\n\nexport async function previewReportExport(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n host: ReportExportActionHost,\n): Promise<ReportExportPreview> {\n const initial = validateReportExportRequest(descriptor, request);\n const action = actionContext('preview', initial);\n await host.authorize(action);\n const verified = await validateReportExportExecution(\n descriptor,\n initial,\n host,\n );\n await host.audit(action);\n return { phase: 'preview', action, request: verified };\n}\n\n/**\n * Queue large exports without embedding rows in a browser/agent response.\n * A host must verify current principal, tenant, report definition, and field\n * policy again before it renders or serves the artifact.\n */\nexport async function applyReportExport(\n descriptor: ReportAdapterDescriptor,\n request: ReportExportRequest,\n host: ReportExportActionHost,\n options: { confirmed?: boolean } = {},\n): Promise<AppliedReportExport> {\n const initial = validateReportExportRequest(descriptor, request);\n if (initial.action.confirmationRequired && options.confirmed !== true) {\n return fail('Sensitive report exports require explicit confirmation');\n }\n const action = actionContext('apply', initial);\n await host.authorize(action);\n const verified = await validateReportExportExecution(\n descriptor,\n initial,\n host,\n );\n await host.audit(action);\n if (verified.execution === 'stream') {\n return {\n phase: 'apply',\n execution: 'stream',\n status: 'ready',\n request: verified,\n };\n }\n if (!host.enqueue) {\n return fail('Background report exports require an application queue host');\n }\n const queued = await host.enqueue({\n version: 1,\n execution: 'background',\n request: verified,\n inherits: [...INHERITED_REPORT_SCOPE],\n });\n if (typeof queued.taskId !== 'string' || queued.taskId.trim().length === 0) {\n return fail('Background report export hosts must return a task id');\n }\n return {\n phase: 'apply',\n execution: 'background',\n status: 'queued',\n taskId: queued.taskId,\n request: verified,\n };\n}\n\n/**\n * Validate artifact metadata before a host serves it. Download locations and\n * tokens stay application-private; expired artifacts fail before that boundary.\n */\nexport function validateReportExportArtifact(\n descriptor: ReportAdapterDescriptor,\n artifact: ReportExportArtifact,\n now = new Date(),\n): ReportExportArtifact {\n const input = plainObject(artifact, 'Report export artifact');\n exactKeys(\n input,\n ['id', 'request', 'progress', 'expiresAt'],\n 'Report export artifact',\n );\n const request = validateReportExportRequest(descriptor, artifact.request);\n const id = requiredString(artifact.id, 'Report export artifact id');\n const expiresAt = normalizeIso(\n artifact.expiresAt,\n 'Report export artifact expiresAt',\n );\n if (new Date(expiresAt).getTime() <= now.getTime()) {\n return fail('Report export artifact has expired');\n }\n const progress = plainObject(\n artifact.progress,\n 'Report export artifact progress',\n );\n exactKeys(\n progress,\n ['state', 'rowCount', 'byteCount', 'truncated'],\n 'Report export artifact progress',\n );\n const state = progress.state;\n if (\n state !== 'queued' &&\n state !== 'running' &&\n state !== 'completed' &&\n state !== 'failed'\n ) {\n return fail('Report export artifact progress state is invalid');\n }\n const rowCount = boundedInteger(\n progress.rowCount,\n 'Report export artifact rowCount',\n 0,\n request.rowCount,\n );\n const byteCount = boundedInteger(\n progress.byteCount,\n 'Report export artifact byteCount',\n 0,\n request.limits.maxBytes,\n );\n const truncated = progress.truncated;\n if (typeof truncated !== 'boolean') {\n return fail('Report export artifact truncated must be a boolean');\n }\n if (state === 'completed' && rowCount !== request.rowCount) {\n return fail(\n 'Completed report export artifacts must contain the requested row count',\n );\n }\n if (state === 'completed' && truncated !== request.truncated) {\n return fail(\n 'Completed report export artifacts must match the requested truncation state',\n );\n }\n return {\n id,\n request,\n expiresAt,\n progress: { state, rowCount, byteCount, truncated },\n };\n}\n"],"mappings":"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"}
|
package/dist/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"timestamp": 0,
|
|
4
4
|
"packageName": "@happyvertical/smrt-reports",
|
|
5
|
-
"packageVersion": "0.43.
|
|
5
|
+
"packageVersion": "0.43.3",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-reports:SmrtReport": {
|
|
8
8
|
"name": "smrtreport",
|
|
@@ -81,11 +81,11 @@
|
|
|
81
81
|
"parameters": [
|
|
82
82
|
{
|
|
83
83
|
"name": "options",
|
|
84
|
-
"type": "
|
|
84
|
+
"type": "SmrtListOptions<ModelType>",
|
|
85
85
|
"optional": true
|
|
86
86
|
}
|
|
87
87
|
],
|
|
88
|
-
"returnType": "Promise<
|
|
88
|
+
"returnType": "Promise<SmrtObject[] | Record<string>[]>",
|
|
89
89
|
"isStatic": false,
|
|
90
90
|
"isPublic": true
|
|
91
91
|
},
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-reports",
|
|
6
|
-
"packageVersion": "0.43.
|
|
6
|
+
"packageVersion": "0.43.3",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "765ba45ef2dfb2bea6921e32327a560ab94bbe6cbf66e06a67714b3d8396bb5d",
|
|
11
|
+
"packageJson": "123bb25fdda1d488568ee5b12c8d318746c0036e2cb10c944f75a76abf0cdc5f",
|
|
12
|
+
"agents": "1328f1da3d58fba2369d696cb9fa42c9d324255257978fa3ae9d1086e9de5af6"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
@@ -114,9 +114,9 @@
|
|
|
114
114
|
"name": "list",
|
|
115
115
|
"async": true,
|
|
116
116
|
"params": [
|
|
117
|
-
"options?:
|
|
117
|
+
"options?: SmrtListOptions<ModelType>"
|
|
118
118
|
],
|
|
119
|
-
"returns": "Promise<
|
|
119
|
+
"returns": "Promise<SmrtObject[] | Record<string>[]>"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"name": "refresh",
|
|
@@ -814,5 +814,5 @@
|
|
|
814
814
|
"polymorphicAssociations": 0,
|
|
815
815
|
"uuidColumns": 14
|
|
816
816
|
},
|
|
817
|
-
"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| adapter | Transport-neutral report descriptor, canonical materialized-row reads, and stable `id` row identity |\n| lifecycle | Tenant-safe freshness, run, lock, failure, and manual refresh preview/apply surfaces |\n| views | Policy-revalidated saved views, snapshot-bound exports, bounded job handoff, and artifact metadata validation |\n\n## Adapter contract\n\n- `buildReportAdapterDescriptor()` returns deterministic, serializable metadata\n for a report surface: a stable resource id, typed persisted report columns,\n the canonical `DataQuerySchema`, and UI-neutral DataTable hints. It must not\n import `smrt-ui` or expose a report-domain class to the consumer.\n- `queryReportMaterializedRows()` owns only the bounded read slice for rows that\n are already materialized. It supports projection, offset/limit paging,\n validated filters, deterministic multi-sort with an `id` tie-breaker, exact\n totals, and dimension facets. At source-query compilation,\n dimension and bucket filters compile to `WHERE`, aggregate-measure filters\n compile to `HAVING`, and mixed `OR`/`NOT` filter scopes fail closed.\n- `id` is the only row identity. It must be a non-empty persisted string and is\n never replaced by a display index or page position.\n- The descriptor is an exposure boundary. Sensitive/secret fields, fields with\n `readPermission`, and transient, system, or non-column fields fail closed and\n do not become public columns when no principal is available.\n- `tenantScoped`/`tenantField` reflect actual registered tenant metadata. A\n `tenantScope` option only contributes to the stable resource id; it is not\n authorization. The default query path resolves the registered collection via
|
|
817
|
+
"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| adapter | Transport-neutral report descriptor, canonical materialized-row reads, and stable `id` row identity |\n| lifecycle | Tenant-safe freshness, run, lock, failure, and manual refresh preview/apply surfaces |\n| views | Policy-revalidated saved views, snapshot-bound exports, bounded job handoff, and artifact metadata validation |\n\n## Adapter contract\n\n- `buildReportAdapterDescriptor()` returns deterministic, serializable metadata\n for a report surface: a stable resource id, typed persisted report columns,\n the canonical `DataQuerySchema`, and UI-neutral DataTable hints. It must not\n import `smrt-ui` or expose a report-domain class to the consumer.\n- `queryReportMaterializedRows()` owns only the bounded read slice for rows that\n are already materialized. It supports projection, offset/limit paging,\n validated filters, deterministic multi-sort with an `id` tie-breaker, exact\n totals, and dimension facets. At source-query compilation,\n dimension and bucket filters compile to `WHERE`, aggregate-measure filters\n compile to `HAVING`, and mixed `OR`/`NOT` filter scopes fail closed.\n- `id` is the only row identity. It must be a non-empty persisted string and is\n never replaced by a display index or page position.\n- The descriptor is an exposure boundary. Sensitive/secret fields, fields with\n `readPermission`, and transient, system, or non-column fields fail closed and\n do not become public columns when no principal is available.\n- `tenantScoped`/`tenantField` reflect actual registered tenant metadata. A\n `tenantScope` option only contributes to the stable resource id; it is not\n authorization. Pass the same `adapter` options to\n `queryReportMaterializedRows()` that were used to build the selected\n descriptor, so results and background tasks retain that stable resource id.\n The default query path resolves the registered collection via `ObjectRegistry`,\n so normal collection tenancy interceptors apply. An injected collection is\n application-owned and must preserve the same boundary.\n- `refresh` is a declaration, not execution. It describes configured mode,\n triggers, positive-TTL stale-read behavior, and a permissioned/audited action\n with preview/apply phases. The adapter remains read-only, while\n `getReportLifecycle()` provides an explicit, tenant-safe lifecycle snapshot\n and `previewReportRefresh()` / `applyReportRefresh()` delegate authorization,\n audit, and queueing through an application action host. Only a registered\n `SmrtReportCollection` may synchronously refresh stale reads, when its TTL is\n positive and the report is not manual.\n- `views` accepts no authority. Persisted views must be normalized again through\n the current descriptor before restoring them, so changed column policy or a\n changed report definition fails closed. Treat unversioned persisted layouts as\n legacy v0 and migrate them to v1 before normalization; unknown versions must\n fail rather than being inferred. Export snapshots retain the canonical\n query fingerprint, projection, sort, as-of/freshness state, exact row count,\n and the fixed principal/tenant/report-definition/field-policy inheritance\n contract. A snapshot needs an application-host-issued opaque binding and an\n explicit offset-page read plan: workers advance it from offset zero instead\n of reusing a visible page. Preview, apply, worker, and artifact-serving\n boundaries must call the validators, including the host's immutable-snapshot\n assertion; application hosts own authorization, audit records, durable\n storage, download tokens, and queue execution.\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"
|
|
818
818
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-reports",
|
|
3
|
-
"version": "0.43.
|
|
3
|
+
"version": "0.43.3",
|
|
4
4
|
"description": "Materialized aggregate report models for SMRT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,17 +44,17 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@happyvertical/sql": "^0.88.0",
|
|
47
|
-
"@happyvertical/smrt-core": "0.43.
|
|
48
|
-
"@happyvertical/smrt-jobs": "0.43.
|
|
49
|
-
"@happyvertical/smrt-
|
|
50
|
-
"@happyvertical/smrt-
|
|
47
|
+
"@happyvertical/smrt-core": "0.43.3",
|
|
48
|
+
"@happyvertical/smrt-jobs": "0.43.3",
|
|
49
|
+
"@happyvertical/smrt-tenancy": "0.43.3",
|
|
50
|
+
"@happyvertical/smrt-types": "0.43.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "24.13.2",
|
|
54
54
|
"typescript": "5.9.3",
|
|
55
55
|
"vite": "8.1.4",
|
|
56
56
|
"vitest": "4.1.10",
|
|
57
|
-
"@happyvertical/smrt-vitest": "0.43.
|
|
57
|
+
"@happyvertical/smrt-vitest": "0.43.3"
|
|
58
58
|
},
|
|
59
59
|
"keywords": [
|
|
60
60
|
"ai",
|