@happyvertical/smrt-reports 0.55.21 → 0.55.23
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 +21 -1
- package/README.md +38 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +101 -25
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/runtime-compiler.d.ts +4 -0
- package/dist/runtime-compiler.d.ts.map +1 -1
- package/dist/runtime-report.d.ts +1 -0
- package/dist/runtime-report.d.ts.map +1 -1
- package/dist/scheduler.d.ts +6 -2
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +84 -15
- package/dist/scheduler.js.map +1 -1
- package/dist/smrt-knowledge.json +13 -7
- package/dist/state.d.ts +1 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +4 -1
- package/dist/state.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/manifest.json
CHANGED
|
@@ -40,6 +40,10 @@ export interface RuntimeReportSourceDefinition {
|
|
|
40
40
|
*/
|
|
41
41
|
collection?: string;
|
|
42
42
|
description?: string;
|
|
43
|
+
/** Canonical public field names to expose; omitted means all eligible fields. */
|
|
44
|
+
fields?: readonly string[];
|
|
45
|
+
/** Canonical public field names to remove; exclusions always win. */
|
|
46
|
+
excludeFields?: readonly string[];
|
|
43
47
|
}
|
|
44
48
|
export interface RuntimeReportCompileContext {
|
|
45
49
|
/** Server-owned allow-list. A spec can reach nothing outside it. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-compiler.d.ts","sourceRoot":"","sources":["../src/runtime-compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AASH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAG5E,OAAO,EAEL,KAAK,kBAAkB,EAGvB,KAAK,iBAAiB,EAEvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAuB,aAAa,EAAE,MAAM,YAAY,CAAC;AAErE,wEAAwE;AACxE,MAAM,WAAW,6BAA6B;IAC5C,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-compiler.d.ts","sourceRoot":"","sources":["../src/runtime-compiler.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime-report.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-report.d.ts","sourceRoot":"","sources":["../src/runtime-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAEL,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAMlC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EACL,KAAK,qBAAqB,EAE1B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAGL,KAAK,iBAAiB,EAGvB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAYxD,qBAea,aAAc,SAAQ,UAAU;IAE3C,QAAQ,EAAE,MAAM,CAAM;IAGtB,KAAK,EAAE,MAAM,CAAM;IAGnB,WAAW,EAAE,MAAM,CAAM;IAEzB,2EAA2E;IAE3E,QAAQ,EAAE,MAAM,CAAM;IAEtB,iEAAiE;IAEjE,IAAI,EAAE,MAAM,CAAM;IAElB,wEAAwE;IAExE,QAAQ,EAAE,MAAM,CAAM;IAGtB,MAAM,EAAE,mBAAmB,CAAY;IAEvC,yEAAyE;IAEzE,eAAe,EAAE,MAAM,CAAM;gBAEjB,OAAO,GAAE,iBAAsB;IAM3C,2EAA2E;IAC3E,OAAO,IAAI,iBAAiB;IAY5B,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI;cASb,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5D;;;;;;OAMG;YACW,oBAAoB;CAgCnC;AAED,qBAAa,uBAAwB,SAAQ,cAAc,CAAC,aAAa,CAAC;IACxE,MAAM,CAAC,QAAQ,CAAC,UAAU,uBAAiB;CAC5C;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,iBAAiB,CAAC;CACvB;AAED,MAAM,WAAW,wBAAyB,SAAQ,yBAAyB;IACzE,yEAAyE;IACzE,QAAQ,EAAE,qBAAqB,CAAC;IAChC;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAUD,yEAAyE;AACzE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,aAAa,CAAC,CAuCxB;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,yBAAyB,GAAG;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAO/B;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GAAG;IACnC,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,aAAa,EAAE,CAAC,CAc1B;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,yBAAyB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACnD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAM/B;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,yBAAyB,GAAG;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,2BAA2B,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACA,OAAO,CAAC,mBAAmB,CAAC,CAgB9B"}
|
|
1
|
+
{"version":3,"file":"runtime-report.d.ts","sourceRoot":"","sources":["../src/runtime-report.ts"],"names":[],"mappings":"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"}
|
package/dist/scheduler.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import './__smrt-register__.js';
|
|
1
2
|
import { EventEmitter } from 'node:events';
|
|
2
3
|
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
3
|
-
import { type DurableJobPayloadIntegrity, type DurableJobPayloadSigner, type JobExecutionContext, type SmrtJob } from '@happyvertical/smrt-jobs';
|
|
4
|
-
import type
|
|
4
|
+
import { type DurableJobPayloadIntegrity, type DurableJobPayloadSigner, type JobExecutionContext, type SmrtJob, type TaskRunner } from '@happyvertical/smrt-jobs';
|
|
5
|
+
import { type DatabaseInterface, type SqlAdapterType } from '@happyvertical/sql';
|
|
5
6
|
import type { ReportRefreshMode, ReportRefreshTrigger } from './types.js';
|
|
6
7
|
type ReportCtor = new (...args: any[]) => SmrtObject;
|
|
7
8
|
export interface ReportRefreshJobArgs {
|
|
@@ -121,11 +122,14 @@ export declare class ReportScheduleRunner extends EventEmitter {
|
|
|
121
122
|
private db;
|
|
122
123
|
private running;
|
|
123
124
|
private pollTimer;
|
|
125
|
+
private readonly taskBindings;
|
|
124
126
|
constructor(config?: ReportScheduleRunnerConfig);
|
|
125
127
|
initialize(db: DatabaseInterface): Promise<void>;
|
|
126
128
|
start(): Promise<void>;
|
|
127
129
|
stop(): Promise<void>;
|
|
128
130
|
isRunning(): boolean;
|
|
131
|
+
/** Bind terminal report-job events once; call the returned function on shutdown. */
|
|
132
|
+
bindTaskRunner(taskRunner: Pick<TaskRunner, 'on' | 'off'>): () => void;
|
|
129
133
|
handleJobCompletion(scheduleId: string, success: boolean, errorMessage?: string): Promise<void>;
|
|
130
134
|
private startPolling;
|
|
131
135
|
poll(): Promise<void>;
|
package/dist/scheduler.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
|
package/dist/scheduler.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { buildReportDefinition } from "./compiler.js";
|
|
2
2
|
import { REPORT_SCHEDULER_TABLES, REPORT_SCHEDULES_TABLE, assertReportTablesReady, scopeKeyForTenant } from "./state.js";
|
|
3
|
-
import { refreshReport } from "./refresh.js";
|
|
3
|
+
import { refreshReport, tenantColumn } from "./refresh.js";
|
|
4
4
|
import { GlobalInterceptors, ObjectRegistry, SmrtObject, field, smrt } from "@happyvertical/smrt-core";
|
|
5
5
|
import { TenantScoped, getTenantId, tenantId } from "@happyvertical/smrt-tenancy";
|
|
6
|
+
import { validateColumnName } from "@happyvertical/sql";
|
|
6
7
|
import { createHash } from "node:crypto";
|
|
7
8
|
import { EventEmitter } from "node:events";
|
|
8
|
-
import { SmrtJobCollection, backgroundEligible, getActiveJobExecutionContext, getNextCronDate, isRunnerExecutionContext, validateCronExpression } from "@happyvertical/smrt-jobs";
|
|
9
|
+
import { SmrtJobCollection, backgroundEligible, getActiveJobExecutionContext, getNextCronDate, isRunnerExecutionContext, redactErrorForPersistence, redactErrorMessage, validateCronExpression } from "@happyvertical/smrt-jobs";
|
|
9
10
|
//#region src/scheduler.ts
|
|
10
11
|
var __defProp = Object.defineProperty;
|
|
11
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -173,12 +174,43 @@ var SmrtReportRefreshTask = class extends SmrtObject {
|
|
|
173
174
|
else throw new Error("Invalid report refresh execution tenant");
|
|
174
175
|
} else jobTenantId = tenantIdFromInstance(this);
|
|
175
176
|
await authorizeReportRefreshExecution(args, reportClass, jobTenantId);
|
|
177
|
+
let tenantIds = args.tenantIds;
|
|
178
|
+
if (trigger === "schedule" && jobTenantId === null) {
|
|
179
|
+
const definition = await buildReportDefinition(reportCtor);
|
|
180
|
+
if (definition.refresh?.tenantFanout === "discover") {
|
|
181
|
+
if (tenantIds !== void 0) throw new Error("Discovered report refresh cannot also specify tenantIds");
|
|
182
|
+
const sourceTenant = await tenantColumn(definition.sourceClassName);
|
|
183
|
+
const reportTenant = await tenantColumn(definition.reportClassName);
|
|
184
|
+
if (!sourceTenant || !reportTenant) throw new Error("Tenant discovery requires tenant columns on the source and report");
|
|
185
|
+
const column = validateColumnName(sourceTenant);
|
|
186
|
+
const table = validateColumnName(definition.sourceTable);
|
|
187
|
+
const materializedName = ObjectRegistry.getTableName(definition.reportClassName);
|
|
188
|
+
if (!materializedName) throw new Error("Report table is not registered");
|
|
189
|
+
const materializedTable = validateColumnName(materializedName);
|
|
190
|
+
const materializedTenant = validateColumnName(reportTenant);
|
|
191
|
+
tenantIds = (await this.db.query(`SELECT CAST(${column} AS TEXT) AS tenant_id FROM ${table} WHERE ${column} IS NOT NULL
|
|
192
|
+
UNION
|
|
193
|
+
SELECT CAST(${materializedTenant} AS TEXT) AS tenant_id FROM ${materializedTable} WHERE ${materializedTenant} IS NOT NULL
|
|
194
|
+
ORDER BY tenant_id`)).rows.map((row) => {
|
|
195
|
+
if (typeof row.tenant_id !== "string" || row.tenant_id.trim().length === 0) throw new Error("Invalid tenant id discovered in report source");
|
|
196
|
+
return row.tenant_id;
|
|
197
|
+
});
|
|
198
|
+
if (tenantIds.length === 0) return {
|
|
199
|
+
rowCount: 0,
|
|
200
|
+
refreshedAt: /* @__PURE__ */ new Date(),
|
|
201
|
+
mode,
|
|
202
|
+
tenantId: null,
|
|
203
|
+
changedGroupCount: 0,
|
|
204
|
+
tenantResults: []
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
176
208
|
return refreshReport(reportCtor, {
|
|
177
209
|
db: this.db,
|
|
178
210
|
mode,
|
|
179
211
|
trigger,
|
|
180
212
|
tenantId: jobTenantId,
|
|
181
|
-
tenantIds
|
|
213
|
+
tenantIds,
|
|
182
214
|
adapterType: args.adapterType,
|
|
183
215
|
scheduleId: args.scheduleId ?? args._scheduleId,
|
|
184
216
|
changedRows: args.changedRows
|
|
@@ -264,8 +296,8 @@ async function ensureReportRefreshSchedules(options) {
|
|
|
264
296
|
const refresh = definition.refresh;
|
|
265
297
|
if (!refresh || refresh.manual) continue;
|
|
266
298
|
const reportClass = canonicalClassName(reportCtor);
|
|
267
|
-
const targetTenants = refresh.tenantFanout ? options.tenantIds : [null];
|
|
268
|
-
if (refresh.tenantFanout && (!targetTenants || targetTenants.length === 0)) throw new Error(`${definition.reportClassName} refresh.tenantFanout requires tenantIds when creating schedules.`);
|
|
299
|
+
const targetTenants = refresh.tenantFanout === true ? options.tenantIds : [null];
|
|
300
|
+
if (refresh.tenantFanout === true && (!targetTenants || targetTenants.length === 0)) throw new Error(`${definition.reportClassName} refresh.tenantFanout requires tenantIds when creating schedules.`);
|
|
269
301
|
const schedules = [refresh.schedule ? {
|
|
270
302
|
cron: refresh.schedule,
|
|
271
303
|
mode: refresh.mode ?? "incremental",
|
|
@@ -287,12 +319,7 @@ async function ensureReportRefreshSchedules(options) {
|
|
|
287
319
|
schedule.mode
|
|
288
320
|
]);
|
|
289
321
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
290
|
-
|
|
291
|
-
"report_class",
|
|
292
|
-
"scope_key",
|
|
293
|
-
"cron",
|
|
294
|
-
"mode"
|
|
295
|
-
], {
|
|
322
|
+
const values = {
|
|
296
323
|
id,
|
|
297
324
|
slug: id,
|
|
298
325
|
context: scopeKey,
|
|
@@ -318,7 +345,11 @@ async function ensureReportRefreshSchedules(options) {
|
|
|
318
345
|
timeout: options.timeout ?? 36e5,
|
|
319
346
|
created_at: now,
|
|
320
347
|
updated_at: now
|
|
321
|
-
}
|
|
348
|
+
};
|
|
349
|
+
const columns = Object.keys(values);
|
|
350
|
+
await options.db.query(`INSERT INTO ${REPORT_SCHEDULES_TABLE} (${columns.join(", ")})
|
|
351
|
+
VALUES (${columns.map((_, index) => `$${index + 1}`).join(", ")})
|
|
352
|
+
ON CONFLICT (report_class, scope_key, cron, mode) DO NOTHING`, ...Object.values(values));
|
|
322
353
|
}
|
|
323
354
|
}
|
|
324
355
|
}
|
|
@@ -329,6 +360,7 @@ var ReportScheduleRunner = class extends EventEmitter {
|
|
|
329
360
|
db = null;
|
|
330
361
|
running = false;
|
|
331
362
|
pollTimer = null;
|
|
363
|
+
taskBindings = /* @__PURE__ */ new WeakMap();
|
|
332
364
|
constructor(config = {}) {
|
|
333
365
|
super();
|
|
334
366
|
this.config = {
|
|
@@ -362,6 +394,41 @@ var ReportScheduleRunner = class extends EventEmitter {
|
|
|
362
394
|
isRunning() {
|
|
363
395
|
return this.running;
|
|
364
396
|
}
|
|
397
|
+
/** Bind terminal report-job events once; call the returned function on shutdown. */
|
|
398
|
+
bindTaskRunner(taskRunner) {
|
|
399
|
+
if (!this.db) throw new Error("ReportScheduleRunner not initialized. Call initialize() first.");
|
|
400
|
+
const existing = this.taskBindings.get(taskRunner);
|
|
401
|
+
if (existing) return existing;
|
|
402
|
+
const complete = (job, success, error) => {
|
|
403
|
+
if (job.objectType !== canonicalClassName(SmrtReportRefreshTask) || job.method !== "run") return;
|
|
404
|
+
const args = job.args;
|
|
405
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return;
|
|
406
|
+
const scheduleId = args.scheduleId;
|
|
407
|
+
if (typeof scheduleId !== "string" || !scheduleId) return;
|
|
408
|
+
try {
|
|
409
|
+
assertReportRefreshJobIntegrity(args);
|
|
410
|
+
if ((args.tenantId ?? null) !== (job.tenantId ?? null)) throw new Error("Invalid report refresh completion tenant");
|
|
411
|
+
} catch (error2) {
|
|
412
|
+
this.emit("runner:error", error2 instanceof Error ? error2 : new Error(String(error2)));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
this.handleJobCompletion(scheduleId, success, error?.message).catch((failure) => {
|
|
416
|
+
this.emit("runner:error", failure instanceof Error ? failure : new Error(String(failure)));
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
const completed = (job) => complete(job, true);
|
|
420
|
+
const failed = (job, error) => complete(job, false, error);
|
|
421
|
+
taskRunner.on("job:completed", completed);
|
|
422
|
+
taskRunner.on("job:failed", failed);
|
|
423
|
+
const unbind = () => {
|
|
424
|
+
if (this.taskBindings.get(taskRunner) !== unbind) return;
|
|
425
|
+
taskRunner.off("job:completed", completed);
|
|
426
|
+
taskRunner.off("job:failed", failed);
|
|
427
|
+
this.taskBindings.delete(taskRunner);
|
|
428
|
+
};
|
|
429
|
+
this.taskBindings.set(taskRunner, unbind);
|
|
430
|
+
return unbind;
|
|
431
|
+
}
|
|
365
432
|
async handleJobCompletion(scheduleId, success, errorMessage) {
|
|
366
433
|
if (!this.db) return;
|
|
367
434
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -378,7 +445,7 @@ var ReportScheduleRunner = class extends EventEmitter {
|
|
|
378
445
|
this.emit("schedule:completed", scheduleId);
|
|
379
446
|
return;
|
|
380
447
|
}
|
|
381
|
-
const safeError = errorMessage ?? "Unknown error";
|
|
448
|
+
const safeError = redactErrorMessage(errorMessage ?? "Unknown error");
|
|
382
449
|
await this.db.query(`UPDATE ${REPORT_SCHEDULES_TABLE}
|
|
383
450
|
SET running_count = CASE WHEN COALESCE(running_count, 0) > 0 THEN running_count - 1 ELSE 0 END,
|
|
384
451
|
last_run = ?,
|
|
@@ -407,7 +474,9 @@ var ReportScheduleRunner = class extends EventEmitter {
|
|
|
407
474
|
}
|
|
408
475
|
async poll() {
|
|
409
476
|
if (!this.db) return;
|
|
410
|
-
const result = await this.db.query(`SELECT
|
|
477
|
+
const result = await this.db.query(`SELECT CAST(id AS TEXT) AS id, CAST(tenant_id AS TEXT) AS tenant_id,
|
|
478
|
+
report_class, cron, mode, trigger, queue, priority, timeout
|
|
479
|
+
FROM ${REPORT_SCHEDULES_TABLE}
|
|
411
480
|
WHERE enabled = true
|
|
412
481
|
AND status = 'active'
|
|
413
482
|
AND next_run <= ?
|
|
@@ -449,7 +518,7 @@ var ReportScheduleRunner = class extends EventEmitter {
|
|
|
449
518
|
await this.db.query(`UPDATE ${REPORT_SCHEDULES_TABLE}
|
|
450
519
|
SET last_error = ?,
|
|
451
520
|
updated_at = ?
|
|
452
|
-
WHERE id = ?`,
|
|
521
|
+
WHERE id = ?`, redactErrorForPersistence(error), (/* @__PURE__ */ new Date()).toISOString(), schedule.id);
|
|
453
522
|
this.emit("schedule:error", schedule, error);
|
|
454
523
|
}
|
|
455
524
|
}
|
package/dist/scheduler.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.js","names":["tenantId"],"sources":["../src/scheduler.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport {\n field,\n GlobalInterceptors,\n type InterceptorContext,\n ObjectRegistry,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n backgroundEligible,\n type DurableJobPayloadIntegrity,\n type DurableJobPayloadSigner,\n getActiveJobExecutionContext,\n getNextCronDate,\n isRunnerExecutionContext,\n type JobExecutionContext,\n type SmrtJob,\n SmrtJobCollection,\n validateCronExpression,\n} from '@happyvertical/smrt-jobs';\nimport {\n getTenantId,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport type { DatabaseInterface, SqlAdapterType } from '@happyvertical/sql';\nimport { buildReportDefinition } from './compiler.js';\nimport { refreshReport } from './refresh.js';\nimport {\n assertReportTablesReady,\n REPORT_SCHEDULER_TABLES,\n REPORT_SCHEDULES_TABLE,\n scopeKeyForTenant,\n} from './state.js';\nimport type {\n ReportDefinition,\n ReportRefreshMode,\n ReportRefreshTrigger,\n ReportSource,\n} from './types.js';\n\ntype ReportCtor = new (...args: any[]) => SmrtObject;\n\nexport interface ReportRefreshJobArgs {\n reportClass?: string;\n mode?: ReportRefreshMode;\n trigger?: ReportRefreshTrigger;\n tenantId?: string | null;\n tenantIds?: string[];\n scheduleId?: string;\n adapterType?: SqlAdapterType;\n changedRows?: Record<string, unknown>[];\n _scheduleId?: string;\n executionAuthority?: ReportRefreshExecutionAuthority;\n integrity?: DurableJobPayloadIntegrity;\n}\n\nexport interface ReportExecutionPrincipalReference {\n version: 1;\n actorUserId: string;\n tenantId: string | null;\n onBehalfOfUserId?: string | null;\n actsAsProfileId?: string | null;\n agentClass?: string | null;\n}\n\nexport interface ReportRefreshExecutionAuthority {\n version: 1;\n hostId: string;\n principal: ReportExecutionPrincipalReference;\n}\n\nexport interface ReportRefreshExecutionAuthorityContext {\n phase: 'execute';\n reportClass: string;\n mode: ReportRefreshMode;\n trigger: ReportRefreshTrigger;\n tenantId: string | null;\n}\n\nexport interface ReportRefreshExecutionAuditEvent\n extends ReportRefreshExecutionAuthorityContext {\n outcome: 'allowed' | 'denied';\n principal: ReportExecutionPrincipalReference;\n reason?: string;\n}\n\nexport interface ReportRefreshExecutionAuthorityHost {\n authorize(\n principal: Readonly<ReportExecutionPrincipalReference>,\n context: Readonly<ReportRefreshExecutionAuthorityContext>,\n ): Promise<void> | void;\n audit(\n event: Readonly<ReportRefreshExecutionAuditEvent>,\n ): Promise<void> | void;\n}\n\nexport interface EnqueueReportRefreshOptions extends ReportRefreshJobArgs {\n report?: ReportCtor;\n reportClass: string;\n db: DatabaseInterface;\n queue?: string;\n priority?: number;\n timeout?: number;\n maxAttempts?: number;\n tenantJobCap?: number;\n /** Server-only signer; register the same key in every worker process. */\n integritySigner?: DurableJobPayloadSigner;\n}\n\nexport interface EnsureReportSchedulesOptions {\n db: DatabaseInterface;\n reports: ReportCtor[];\n tenantIds?: string[];\n queue?: string;\n priority?: number;\n timeout?: number;\n}\n\nexport interface ReportScheduleRunnerConfig {\n id?: string;\n pollInterval?: number;\n batchSize?: number;\n integritySigner?: DurableJobPayloadSigner;\n}\n\nexport interface ReportScheduleInfo {\n id: string;\n reportClass: string;\n tenantId: string | null;\n cron: string;\n mode: ReportRefreshMode;\n}\n\nexport interface ReportScheduleRunnerEvents {\n 'schedule:triggered': (schedule: ReportScheduleInfo) => void;\n 'schedule:error': (schedule: ReportScheduleInfo, error: Error) => void;\n 'schedule:completed': (scheduleId: string) => void;\n 'schedule:failed': (scheduleId: string, error: string) => void;\n 'runner:started': () => void;\n 'runner:stopped': () => void;\n 'runner:error': (error: Error) => void;\n}\n\nexport interface ReportRefreshInterceptorOptions {\n db: DatabaseInterface;\n reports: ReportCtor[];\n enqueue?: boolean;\n queue?: string;\n priority?: number;\n timeout?: number;\n tenantJobCap?: number;\n name?: string;\n integritySigner?: DurableJobPayloadSigner;\n}\n\nconst INTERNAL_SURFACE = {\n api: false,\n cli: {\n include: ['list', 'get'],\n skipApiCheck: true,\n http: false,\n },\n mcp: false,\n};\n\nconst executionAuthorityHosts = new Map<\n string,\n ReportRefreshExecutionAuthorityHost\n>();\nconst jobIntegritySigners = new Map<string, DurableJobPayloadSigner>();\n\nexport function registerReportRefreshJobIntegritySigner(\n signer: DurableJobPayloadSigner,\n): () => void {\n const existing = jobIntegritySigners.get(signer.keyId);\n if (existing && existing !== signer) {\n throw new Error(\n `Report refresh job integrity signer already registered: ${signer.keyId}`,\n );\n }\n jobIntegritySigners.set(signer.keyId, signer);\n return () => {\n if (jobIntegritySigners.get(signer.keyId) === signer) {\n jobIntegritySigners.delete(signer.keyId);\n }\n };\n}\n\n/**\n * Maintenance callers historically configured only their runner/interceptor;\n * they do not have a request host from which to obtain a signer. When one\n * worker-shared signer is registered, use it as the application default. More\n * than one key is ambiguous and remains fail-closed until the caller selects\n * one explicitly.\n */\nfunction resolveReportRefreshIntegritySigner(\n configured: DurableJobPayloadSigner | undefined,\n): DurableJobPayloadSigner | undefined {\n if (configured) return configured;\n if (jobIntegritySigners.size !== 1) return undefined;\n return jobIntegritySigners.values().next().value;\n}\n\nfunction unsignedReportRefreshJobArgs(\n args: ReportRefreshJobArgs,\n): Omit<ReportRefreshJobArgs, 'integrity'> {\n const {\n integrity: _integrity,\n _scheduleId: _internalScheduleId,\n ...unsigned\n } = args;\n return unsigned;\n}\n\nfunction assertReportRefreshJobIntegrity(args: ReportRefreshJobArgs): void {\n const integrity = args.integrity;\n const signer = integrity\n ? jobIntegritySigners.get(integrity.keyId)\n : undefined;\n if (\n !integrity ||\n !signer?.verify(unsignedReportRefreshJobArgs(args), integrity)\n ) {\n throw new Error('Invalid durable report refresh job integrity binding');\n }\n}\n\nfunction assertReportRefreshJobTarget(\n args: ReportRefreshJobArgs,\n context: JobExecutionContext | undefined,\n): void {\n if (!context) return;\n if (!isRunnerExecutionContext(context)) {\n throw new Error('Invalid durable report refresh job context');\n }\n const expectedType = canonicalClassName(\n (args.trigger ?? 'job') === 'manual'\n ? SmrtPrincipalReportRefreshTask\n : SmrtReportRefreshTask,\n );\n if (context.job.objectType !== expectedType || context.job.method !== 'run') {\n throw new Error('Invalid durable report refresh job target');\n }\n}\n\nexport function registerReportRefreshExecutionAuthorityHost(\n hostId: string,\n host: ReportRefreshExecutionAuthorityHost,\n): () => void {\n if (!hostId || hostId.length > 256) {\n throw new Error(\n 'Report refresh authority hostId must contain 1-256 characters',\n );\n }\n const existing = executionAuthorityHosts.get(hostId);\n if (existing && existing !== host) {\n throw new Error(\n `Report refresh authority host already registered: ${hostId}`,\n );\n }\n executionAuthorityHosts.set(hostId, host);\n return () => {\n if (executionAuthorityHosts.get(hostId) === host) {\n executionAuthorityHosts.delete(hostId);\n }\n };\n}\n\nfunction stableUuid(values: unknown[]): string {\n const hash = createHash('sha256')\n .update(JSON.stringify(values))\n .digest('hex');\n const variant = ((Number.parseInt(hash[16], 16) & 0x3) | 0x8).toString(16);\n return [\n hash.slice(0, 8),\n hash.slice(8, 12),\n `4${hash.slice(13, 16)}`,\n `${variant}${hash.slice(17, 20)}`,\n hash.slice(20, 32),\n ].join('-');\n}\n\nfunction canonicalClassName(reportCtor: ReportCtor): string {\n const registered =\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name);\n return registered?.qualifiedName ?? registered?.name ?? reportCtor.name;\n}\n\nfunction resolveReportClass(name: string): ReportCtor {\n const registered =\n ObjectRegistry.getClassByQualifiedName(name) ??\n ObjectRegistry.getClass(name);\n if (!registered) {\n throw new Error(`Unknown report class: ${name}`);\n }\n return registered.constructor as unknown as ReportCtor;\n}\n\nfunction reportSourceName(source: ReportSource): string {\n if (typeof source === 'string') return source;\n return source.name;\n}\n\nfunction sourceMatches(\n definition: ReportDefinition,\n instance: SmrtObject,\n context: InterceptorContext,\n): boolean {\n const configured = definition.refresh?.onChange;\n if (!configured || configured.length === 0) return false;\n\n const eventNames = new Set<string>([\n context.className,\n instance.constructor.name,\n ]);\n const registered = ObjectRegistry.getClassByConstructor(\n instance.constructor as ReportCtor,\n );\n if (registered?.qualifiedName) eventNames.add(registered.qualifiedName);\n if (registered?.name) eventNames.add(registered.name);\n\n for (const source of configured) {\n const name = reportSourceName(source);\n const registeredSource =\n ObjectRegistry.getClassByQualifiedName(name) ??\n ObjectRegistry.getClass(name);\n if (\n eventNames.has(name) ||\n (registeredSource?.name && eventNames.has(registeredSource.name)) ||\n (registeredSource?.qualifiedName &&\n eventNames.has(registeredSource.qualifiedName))\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction tenantIdFromInstance(instance: SmrtObject): string | null {\n const value = (instance as unknown as { tenantId?: unknown }).tenantId;\n return typeof value === 'string' && value.length > 0\n ? value\n : (getTenantId() ?? null);\n}\n\nfunction changedRowSnapshot(instance: SmrtObject): Record<string, unknown> {\n const serializable = instance.toJSON();\n return serializable && typeof serializable === 'object'\n ? (serializable as Record<string, unknown>)\n : {};\n}\n\nasync function authorizeReportRefreshExecution(\n args: ReportRefreshJobArgs,\n reportClass: string,\n jobTenantId: string | null,\n): Promise<void> {\n if ((args.tenantId ?? null) !== jobTenantId) {\n throw new Error('Invalid report refresh execution tenant');\n }\n const authority = args.executionAuthority;\n if ((args.trigger ?? 'job') === 'manual' && !authority) {\n throw new Error('Manual report refresh execution authority is missing');\n }\n // Scheduled and on-change maintenance jobs predate user-bound actions and\n // intentionally run under the worker's system authority.\n if (!authority) return;\n const principal = authority.principal;\n const tenantId = args.tenantId ?? null;\n if (\n authority.version !== 1 ||\n principal?.version !== 1 ||\n !authority.hostId ||\n !principal.actorUserId ||\n principal.tenantId !== tenantId ||\n principal.tenantId !== jobTenantId\n ) {\n throw new Error('Invalid report refresh execution authority');\n }\n const host = executionAuthorityHosts.get(authority.hostId);\n if (!host) {\n throw new Error(\n `No report refresh authority host registered for ${authority.hostId}`,\n );\n }\n const context: ReportRefreshExecutionAuthorityContext = {\n phase: 'execute',\n reportClass,\n mode: args.mode ?? 'incremental',\n trigger: args.trigger ?? 'job',\n tenantId,\n };\n try {\n await host.authorize(\n Object.freeze({ ...principal }),\n Object.freeze(context),\n );\n } catch {\n await host.audit({\n ...context,\n outcome: 'denied',\n principal: Object.freeze({ ...principal }),\n reason: 'current_authority_denied',\n });\n throw new Error('Report refresh execution authority denied');\n }\n await host.audit({\n ...context,\n outcome: 'allowed',\n principal: Object.freeze({ ...principal }),\n });\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_report_refresh_tasks',\n ...INTERNAL_SURFACE,\n})\nexport class SmrtReportRefreshTask extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-reports\";\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @field({ type: 'text', required: true })\n reportClass: string = '';\n\n @field({ type: 'text', required: true })\n mode: ReportRefreshMode = 'incremental';\n\n @field({ type: 'text', required: true })\n trigger: ReportRefreshTrigger = 'job';\n\n @field({ type: 'json' })\n args: ReportRefreshJobArgs = {};\n\n @backgroundEligible()\n async run(\n args: ReportRefreshJobArgs = {},\n context?: JobExecutionContext,\n ): Promise<unknown> {\n assertReportRefreshJobIntegrity(args);\n const executionContext = getActiveJobExecutionContext() ?? context;\n assertReportRefreshJobTarget(args, executionContext);\n const reportClass = args.reportClass;\n if (!reportClass) {\n throw new Error('Report refresh job requires reportClass');\n }\n const mode = args.mode ?? 'incremental';\n const trigger = args.trigger ?? 'job';\n\n const reportCtor = resolveReportClass(reportClass);\n // A runner context, including its explicit global `null` tenant, owns the\n // scope. Only direct callers without runner context may use instance scope.\n let jobTenantId: string | null;\n if (executionContext) {\n const runnerTenantId = executionContext.job.tenantId;\n if (runnerTenantId === null) {\n jobTenantId = null;\n } else if (\n typeof runnerTenantId === 'string' &&\n runnerTenantId.length > 0\n ) {\n jobTenantId = runnerTenantId;\n } else {\n throw new Error('Invalid report refresh execution tenant');\n }\n } else {\n jobTenantId = tenantIdFromInstance(this);\n }\n await authorizeReportRefreshExecution(args, reportClass, jobTenantId);\n return refreshReport(reportCtor, {\n db: this.db,\n mode,\n trigger,\n tenantId: jobTenantId,\n tenantIds: args.tenantIds,\n adapterType: args.adapterType,\n scheduleId: args.scheduleId ?? args._scheduleId,\n changedRows: args.changedRows,\n });\n }\n}\n\n/** Worker target whose authority requirement cannot be downgraded by job args. */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_principal_report_refresh_tasks',\n ...INTERNAL_SURFACE,\n})\nexport class SmrtPrincipalReportRefreshTask extends SmrtReportRefreshTask { static __smrtPackage__ = \"@happyvertical/smrt-reports\";\n override async run(\n args: ReportRefreshJobArgs = {},\n context?: JobExecutionContext,\n ): Promise<unknown> {\n return super.run({ ...args, trigger: 'manual' }, context);\n }\n}\n\nexport async function enqueueReportRefresh(\n options: EnqueueReportRefreshOptions,\n): Promise<SmrtJob> {\n const integritySigner = resolveReportRefreshIntegritySigner(\n options.integritySigner,\n );\n if (!integritySigner) {\n throw new Error(\n 'Report refresh queue requires a durable job integrity signer',\n );\n }\n if (options.trigger === 'manual' && !options.executionAuthority) {\n throw new Error('Manual report refresh requires execution-time authority');\n }\n if (\n options.executionAuthority &&\n (options.executionAuthority.version !== 1 ||\n options.executionAuthority.principal?.version !== 1 ||\n !options.executionAuthority.hostId ||\n !options.executionAuthority.principal.actorUserId ||\n options.executionAuthority.principal.tenantId !==\n (options.tenantId ?? null))\n ) {\n throw new Error('Invalid report refresh execution authority');\n }\n if (\n options.executionAuthority &&\n (options.trigger !== 'manual' || (options.tenantIds?.length ?? 0) > 0)\n ) {\n throw new Error(\n 'Principal-bound report refresh requires one manual tenant scope',\n );\n }\n await ObjectRegistry.ensureManifestLoaded('SmrtJob');\n const collection = await SmrtJobCollection.create({ db: options.db });\n const taskType = canonicalClassName(\n options.trigger === 'manual'\n ? SmrtPrincipalReportRefreshTask\n : SmrtReportRefreshTask,\n );\n const scheduleId = options.scheduleId ?? options._scheduleId;\n\n const unsignedArgs: Omit<ReportRefreshJobArgs, 'integrity'> = {\n reportClass: options.reportClass,\n mode: options.mode ?? 'incremental',\n trigger: options.trigger ?? 'job',\n tenantId: options.tenantId,\n tenantIds: options.tenantIds,\n adapterType: options.adapterType,\n changedRows: options.changedRows,\n scheduleId,\n executionAuthority: options.executionAuthority,\n };\n const integrity = integritySigner.sign(unsignedArgs);\n if (!integritySigner.verify(unsignedArgs, integrity)) {\n throw new Error(\n 'Report refresh job integrity signer rejected its queued payload',\n );\n }\n return collection.enqueueJob(\n {\n tenantId: options.tenantId ?? null,\n queue: options.queue ?? 'reports',\n objectType: taskType,\n objectId: null,\n method: 'run',\n args: { ...unsignedArgs, _scheduleId: scheduleId, integrity },\n priority: options.priority ?? 70,\n timeout: options.timeout ?? 3600000,\n maxAttempts: options.maxAttempts ?? 3,\n },\n { tenantJobCap: options.tenantJobCap },\n );\n}\n\nexport async function ensureReportRefreshSchedules(\n options: EnsureReportSchedulesOptions,\n): Promise<void> {\n await assertReportTablesReady(options.db, REPORT_SCHEDULER_TABLES);\n\n for (const reportCtor of options.reports) {\n const definition = await buildReportDefinition(reportCtor);\n const refresh = definition.refresh;\n if (!refresh || refresh.manual) continue;\n\n const reportClass = canonicalClassName(reportCtor);\n const targetTenants = refresh.tenantFanout\n ? options.tenantIds\n : [null as string | null];\n if (\n refresh.tenantFanout &&\n (!targetTenants || targetTenants.length === 0)\n ) {\n throw new Error(\n `${definition.reportClassName} refresh.tenantFanout requires tenantIds when creating schedules.`,\n );\n }\n\n const schedules = [\n refresh.schedule\n ? {\n cron: refresh.schedule,\n mode: refresh.mode ?? 'incremental',\n trigger: 'schedule' as const,\n }\n : null,\n refresh.fullRebuildSchedule\n ? {\n cron: refresh.fullRebuildSchedule,\n mode: 'rebuild' as const,\n trigger: 'schedule' as const,\n }\n : null,\n ].filter(Boolean) as Array<{\n cron: string;\n mode: ReportRefreshMode;\n trigger: ReportRefreshTrigger;\n }>;\n\n for (const schedule of schedules) {\n validateCronExpression(schedule.cron);\n for (const tenantId of targetTenants ?? []) {\n const scopeKey = scopeKeyForTenant(tenantId);\n const id = stableUuid([\n 'schedule',\n reportClass,\n scopeKey,\n schedule.cron,\n schedule.mode,\n ]);\n const now = new Date().toISOString();\n await options.db.upsert(\n REPORT_SCHEDULES_TABLE,\n ['report_class', 'scope_key', 'cron', 'mode'],\n {\n id,\n slug: id,\n context: scopeKey,\n tenant_id: tenantId,\n scope_key: scopeKey,\n report_class: reportClass,\n cron: schedule.cron,\n trigger: schedule.trigger,\n mode: schedule.mode,\n enabled: true,\n status: 'active',\n next_run: getNextCronDate(schedule.cron).toISOString(),\n last_run: null,\n last_status: null,\n last_error: null,\n run_count: 0,\n success_count: 0,\n failure_count: 0,\n running_count: 0,\n max_concurrent: 1,\n queue: options.queue ?? 'reports',\n priority: options.priority ?? 70,\n timeout: options.timeout ?? 3600000,\n created_at: now,\n updated_at: now,\n },\n );\n }\n }\n }\n}\n\nexport class ReportScheduleRunner extends EventEmitter {\n readonly id: string;\n private readonly config: Required<\n Omit<ReportScheduleRunnerConfig, 'integritySigner'>\n > &\n Pick<ReportScheduleRunnerConfig, 'integritySigner'>;\n private db: DatabaseInterface | null = null;\n private running = false;\n private pollTimer: NodeJS.Timeout | null = null;\n\n constructor(config: ReportScheduleRunnerConfig = {}) {\n super();\n this.config = {\n id: config.id || `reports_${stableUuid([Date.now()]).slice(0, 8)}`,\n pollInterval: config.pollInterval ?? 60000,\n batchSize: config.batchSize ?? 50,\n integritySigner: config.integritySigner,\n };\n this.id = this.config.id;\n }\n\n async initialize(db: DatabaseInterface): Promise<void> {\n this.db = db;\n await assertReportTablesReady(db, REPORT_SCHEDULER_TABLES);\n }\n\n async start(): Promise<void> {\n if (this.running) return;\n if (!this.db) {\n throw new Error(\n 'ReportScheduleRunner not initialized. Call initialize() first.',\n );\n }\n this.running = true;\n this.startPolling();\n this.emit('runner:started');\n }\n\n async stop(): Promise<void> {\n if (!this.running) return;\n this.running = false;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.emit('runner:stopped');\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n async handleJobCompletion(\n scheduleId: string,\n success: boolean,\n errorMessage?: string,\n ): Promise<void> {\n if (!this.db) return;\n const now = new Date().toISOString();\n if (success) {\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = CASE WHEN COALESCE(running_count, 0) > 0 THEN running_count - 1 ELSE 0 END,\n last_run = ?,\n last_status = 'success',\n last_error = NULL,\n run_count = COALESCE(run_count, 0) + 1,\n success_count = COALESCE(success_count, 0) + 1,\n updated_at = ?\n WHERE id = ?`,\n now,\n now,\n scheduleId,\n );\n this.emit('schedule:completed', scheduleId);\n return;\n }\n\n const safeError = errorMessage ?? 'Unknown error';\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = CASE WHEN COALESCE(running_count, 0) > 0 THEN running_count - 1 ELSE 0 END,\n last_run = ?,\n last_status = 'failed',\n last_error = ?,\n run_count = COALESCE(run_count, 0) + 1,\n failure_count = COALESCE(failure_count, 0) + 1,\n updated_at = ?\n WHERE id = ?`,\n now,\n safeError,\n now,\n scheduleId,\n );\n this.emit('schedule:failed', scheduleId, safeError);\n }\n\n private startPolling(): void {\n const poll = async () => {\n if (!this.running) return;\n try {\n await this.poll();\n } catch (error) {\n this.emit('runner:error', error as Error);\n }\n if (this.running) {\n this.pollTimer = setTimeout(poll, this.config.pollInterval);\n if (typeof this.pollTimer.unref === 'function') {\n this.pollTimer.unref();\n }\n }\n };\n poll();\n }\n\n async poll(): Promise<void> {\n if (!this.db) return;\n const result = await this.db.query(\n `SELECT * FROM ${REPORT_SCHEDULES_TABLE}\n WHERE enabled = true\n AND status = 'active'\n AND next_run <= ?\n AND COALESCE(running_count, 0) < COALESCE(max_concurrent, 1)\n ORDER BY next_run ASC\n LIMIT ?`,\n new Date().toISOString(),\n this.config.batchSize,\n );\n\n for (const row of result.rows) {\n await this.triggerSchedule(row as ReportScheduleRow);\n }\n }\n\n private async triggerSchedule(row: ReportScheduleRow): Promise<void> {\n if (!this.db) return;\n const schedule: ReportScheduleInfo = {\n id: String(row.id),\n reportClass: String(row.report_class),\n tenantId:\n typeof row.tenant_id === 'string' && row.tenant_id.length > 0\n ? row.tenant_id\n : null,\n cron: String(row.cron),\n mode: (row.mode as ReportRefreshMode) || 'incremental',\n };\n\n try {\n const nextRun = getNextCronDate(schedule.cron);\n await enqueueReportRefresh({\n db: this.db,\n reportClass: schedule.reportClass,\n mode: schedule.mode,\n trigger: (row.trigger as ReportRefreshTrigger) || 'schedule',\n tenantId: schedule.tenantId,\n scheduleId: schedule.id,\n queue: String(row.queue || 'reports'),\n priority: Number(row.priority ?? 70),\n timeout: Number(row.timeout ?? 3600000),\n integritySigner: this.config.integritySigner,\n });\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = COALESCE(running_count, 0) + 1,\n next_run = ?,\n updated_at = ?\n WHERE id = ?`,\n nextRun.toISOString(),\n new Date().toISOString(),\n schedule.id,\n );\n this.emit('schedule:triggered', schedule);\n } catch (error) {\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET last_error = ?,\n updated_at = ?\n WHERE id = ?`,\n error instanceof Error ? error.message : String(error),\n new Date().toISOString(),\n schedule.id,\n );\n this.emit('schedule:error', schedule, error as Error);\n }\n }\n}\n\ninterface ReportScheduleRow {\n id: unknown;\n tenant_id: unknown;\n report_class: unknown;\n cron: unknown;\n trigger: unknown;\n mode: unknown;\n queue: unknown;\n priority: unknown;\n timeout: unknown;\n}\n\nexport function registerReportRefreshInterceptor(\n options: ReportRefreshInterceptorOptions,\n): () => boolean {\n const name = options.name ?? 'smrt-reports-refresh';\n GlobalInterceptors.register({\n name,\n priority: -10,\n async afterSave(instance, context) {\n await triggerReportsForInstance(options, instance, context);\n },\n async afterDelete(instance, context) {\n await triggerReportsForInstance(options, instance, context);\n },\n });\n return () => GlobalInterceptors.unregister(name);\n}\n\nasync function triggerReportsForInstance(\n options: ReportRefreshInterceptorOptions,\n instance: SmrtObject,\n context: InterceptorContext,\n): Promise<void> {\n for (const reportCtor of options.reports) {\n const definition = await buildReportDefinition(reportCtor);\n if (definition.refresh?.manual) continue;\n if (!sourceMatches(definition, instance, context)) continue;\n\n const mode = definition.refresh?.mode ?? 'incremental';\n const tenantId = tenantIdFromInstance(instance);\n const changedRows = [changedRowSnapshot(instance)];\n if (options.enqueue === false) {\n await refreshReport(reportCtor, {\n db: options.db,\n mode,\n trigger: 'change',\n tenantId,\n changedRows,\n });\n continue;\n }\n\n await enqueueReportRefresh({\n db: options.db,\n reportClass: canonicalClassName(reportCtor),\n mode,\n trigger: 'change',\n tenantId,\n queue: options.queue,\n priority: options.priority,\n timeout: options.timeout,\n tenantJobCap: options.tenantJobCap,\n changedRows,\n integritySigner: options.integritySigner,\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scheduler.js","names":["tenantId","error"],"sources":["../src/scheduler.ts"],"sourcesContent":["import './__smrt-register__.js';\n\nimport { createHash } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport {\n field,\n GlobalInterceptors,\n type InterceptorContext,\n ObjectRegistry,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n backgroundEligible,\n type DurableJobPayloadIntegrity,\n type DurableJobPayloadSigner,\n getActiveJobExecutionContext,\n getNextCronDate,\n isRunnerExecutionContext,\n type JobExecutionContext,\n redactErrorForPersistence,\n redactErrorMessage,\n type SmrtJob,\n SmrtJobCollection,\n type TaskRunner,\n validateCronExpression,\n} from '@happyvertical/smrt-jobs';\nimport {\n getTenantId,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport {\n type DatabaseInterface,\n type SqlAdapterType,\n validateColumnName,\n} from '@happyvertical/sql';\nimport { buildReportDefinition } from './compiler.js';\nimport { refreshReport, tenantColumn } from './refresh.js';\nimport {\n assertReportTablesReady,\n REPORT_SCHEDULER_TABLES,\n REPORT_SCHEDULES_TABLE,\n scopeKeyForTenant,\n} from './state.js';\nimport type {\n ReportDefinition,\n ReportRefreshMode,\n ReportRefreshTrigger,\n ReportSource,\n} from './types.js';\n\ntype ReportCtor = new (...args: any[]) => SmrtObject;\n\nexport interface ReportRefreshJobArgs {\n reportClass?: string;\n mode?: ReportRefreshMode;\n trigger?: ReportRefreshTrigger;\n tenantId?: string | null;\n tenantIds?: string[];\n scheduleId?: string;\n adapterType?: SqlAdapterType;\n changedRows?: Record<string, unknown>[];\n _scheduleId?: string;\n executionAuthority?: ReportRefreshExecutionAuthority;\n integrity?: DurableJobPayloadIntegrity;\n}\n\nexport interface ReportExecutionPrincipalReference {\n version: 1;\n actorUserId: string;\n tenantId: string | null;\n onBehalfOfUserId?: string | null;\n actsAsProfileId?: string | null;\n agentClass?: string | null;\n}\n\nexport interface ReportRefreshExecutionAuthority {\n version: 1;\n hostId: string;\n principal: ReportExecutionPrincipalReference;\n}\n\nexport interface ReportRefreshExecutionAuthorityContext {\n phase: 'execute';\n reportClass: string;\n mode: ReportRefreshMode;\n trigger: ReportRefreshTrigger;\n tenantId: string | null;\n}\n\nexport interface ReportRefreshExecutionAuditEvent\n extends ReportRefreshExecutionAuthorityContext {\n outcome: 'allowed' | 'denied';\n principal: ReportExecutionPrincipalReference;\n reason?: string;\n}\n\nexport interface ReportRefreshExecutionAuthorityHost {\n authorize(\n principal: Readonly<ReportExecutionPrincipalReference>,\n context: Readonly<ReportRefreshExecutionAuthorityContext>,\n ): Promise<void> | void;\n audit(\n event: Readonly<ReportRefreshExecutionAuditEvent>,\n ): Promise<void> | void;\n}\n\nexport interface EnqueueReportRefreshOptions extends ReportRefreshJobArgs {\n report?: ReportCtor;\n reportClass: string;\n db: DatabaseInterface;\n queue?: string;\n priority?: number;\n timeout?: number;\n maxAttempts?: number;\n tenantJobCap?: number;\n /** Server-only signer; register the same key in every worker process. */\n integritySigner?: DurableJobPayloadSigner;\n}\n\nexport interface EnsureReportSchedulesOptions {\n db: DatabaseInterface;\n reports: ReportCtor[];\n tenantIds?: string[];\n queue?: string;\n priority?: number;\n timeout?: number;\n}\n\nexport interface ReportScheduleRunnerConfig {\n id?: string;\n pollInterval?: number;\n batchSize?: number;\n integritySigner?: DurableJobPayloadSigner;\n}\n\nexport interface ReportScheduleInfo {\n id: string;\n reportClass: string;\n tenantId: string | null;\n cron: string;\n mode: ReportRefreshMode;\n}\n\nexport interface ReportScheduleRunnerEvents {\n 'schedule:triggered': (schedule: ReportScheduleInfo) => void;\n 'schedule:error': (schedule: ReportScheduleInfo, error: Error) => void;\n 'schedule:completed': (scheduleId: string) => void;\n 'schedule:failed': (scheduleId: string, error: string) => void;\n 'runner:started': () => void;\n 'runner:stopped': () => void;\n 'runner:error': (error: Error) => void;\n}\n\nexport interface ReportRefreshInterceptorOptions {\n db: DatabaseInterface;\n reports: ReportCtor[];\n enqueue?: boolean;\n queue?: string;\n priority?: number;\n timeout?: number;\n tenantJobCap?: number;\n name?: string;\n integritySigner?: DurableJobPayloadSigner;\n}\n\nconst INTERNAL_SURFACE = {\n api: false,\n cli: {\n include: ['list', 'get'],\n skipApiCheck: true,\n http: false,\n },\n mcp: false,\n};\n\nconst executionAuthorityHosts = new Map<\n string,\n ReportRefreshExecutionAuthorityHost\n>();\nconst jobIntegritySigners = new Map<string, DurableJobPayloadSigner>();\n\nexport function registerReportRefreshJobIntegritySigner(\n signer: DurableJobPayloadSigner,\n): () => void {\n const existing = jobIntegritySigners.get(signer.keyId);\n if (existing && existing !== signer) {\n throw new Error(\n `Report refresh job integrity signer already registered: ${signer.keyId}`,\n );\n }\n jobIntegritySigners.set(signer.keyId, signer);\n return () => {\n if (jobIntegritySigners.get(signer.keyId) === signer) {\n jobIntegritySigners.delete(signer.keyId);\n }\n };\n}\n\n/**\n * Maintenance callers historically configured only their runner/interceptor;\n * they do not have a request host from which to obtain a signer. When one\n * worker-shared signer is registered, use it as the application default. More\n * than one key is ambiguous and remains fail-closed until the caller selects\n * one explicitly.\n */\nfunction resolveReportRefreshIntegritySigner(\n configured: DurableJobPayloadSigner | undefined,\n): DurableJobPayloadSigner | undefined {\n if (configured) return configured;\n if (jobIntegritySigners.size !== 1) return undefined;\n return jobIntegritySigners.values().next().value;\n}\n\nfunction unsignedReportRefreshJobArgs(\n args: ReportRefreshJobArgs,\n): Omit<ReportRefreshJobArgs, 'integrity'> {\n const {\n integrity: _integrity,\n _scheduleId: _internalScheduleId,\n ...unsigned\n } = args;\n return unsigned;\n}\n\nfunction assertReportRefreshJobIntegrity(args: ReportRefreshJobArgs): void {\n const integrity = args.integrity;\n const signer = integrity\n ? jobIntegritySigners.get(integrity.keyId)\n : undefined;\n if (\n !integrity ||\n !signer?.verify(unsignedReportRefreshJobArgs(args), integrity)\n ) {\n throw new Error('Invalid durable report refresh job integrity binding');\n }\n}\n\nfunction assertReportRefreshJobTarget(\n args: ReportRefreshJobArgs,\n context: JobExecutionContext | undefined,\n): void {\n if (!context) return;\n if (!isRunnerExecutionContext(context)) {\n throw new Error('Invalid durable report refresh job context');\n }\n const expectedType = canonicalClassName(\n (args.trigger ?? 'job') === 'manual'\n ? SmrtPrincipalReportRefreshTask\n : SmrtReportRefreshTask,\n );\n if (context.job.objectType !== expectedType || context.job.method !== 'run') {\n throw new Error('Invalid durable report refresh job target');\n }\n}\n\nexport function registerReportRefreshExecutionAuthorityHost(\n hostId: string,\n host: ReportRefreshExecutionAuthorityHost,\n): () => void {\n if (!hostId || hostId.length > 256) {\n throw new Error(\n 'Report refresh authority hostId must contain 1-256 characters',\n );\n }\n const existing = executionAuthorityHosts.get(hostId);\n if (existing && existing !== host) {\n throw new Error(\n `Report refresh authority host already registered: ${hostId}`,\n );\n }\n executionAuthorityHosts.set(hostId, host);\n return () => {\n if (executionAuthorityHosts.get(hostId) === host) {\n executionAuthorityHosts.delete(hostId);\n }\n };\n}\n\nfunction stableUuid(values: unknown[]): string {\n const hash = createHash('sha256')\n .update(JSON.stringify(values))\n .digest('hex');\n const variant = ((Number.parseInt(hash[16], 16) & 0x3) | 0x8).toString(16);\n return [\n hash.slice(0, 8),\n hash.slice(8, 12),\n `4${hash.slice(13, 16)}`,\n `${variant}${hash.slice(17, 20)}`,\n hash.slice(20, 32),\n ].join('-');\n}\n\nfunction canonicalClassName(reportCtor: ReportCtor): string {\n const registered =\n ObjectRegistry.getClassByConstructor(reportCtor) ??\n ObjectRegistry.getClass(reportCtor.name);\n return registered?.qualifiedName ?? registered?.name ?? reportCtor.name;\n}\n\nfunction resolveReportClass(name: string): ReportCtor {\n const registered =\n ObjectRegistry.getClassByQualifiedName(name) ??\n ObjectRegistry.getClass(name);\n if (!registered) {\n throw new Error(`Unknown report class: ${name}`);\n }\n return registered.constructor as unknown as ReportCtor;\n}\n\nfunction reportSourceName(source: ReportSource): string {\n if (typeof source === 'string') return source;\n return source.name;\n}\n\nfunction sourceMatches(\n definition: ReportDefinition,\n instance: SmrtObject,\n context: InterceptorContext,\n): boolean {\n const configured = definition.refresh?.onChange;\n if (!configured || configured.length === 0) return false;\n\n const eventNames = new Set<string>([\n context.className,\n instance.constructor.name,\n ]);\n const registered = ObjectRegistry.getClassByConstructor(\n instance.constructor as ReportCtor,\n );\n if (registered?.qualifiedName) eventNames.add(registered.qualifiedName);\n if (registered?.name) eventNames.add(registered.name);\n\n for (const source of configured) {\n const name = reportSourceName(source);\n const registeredSource =\n ObjectRegistry.getClassByQualifiedName(name) ??\n ObjectRegistry.getClass(name);\n if (\n eventNames.has(name) ||\n (registeredSource?.name && eventNames.has(registeredSource.name)) ||\n (registeredSource?.qualifiedName &&\n eventNames.has(registeredSource.qualifiedName))\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction tenantIdFromInstance(instance: SmrtObject): string | null {\n const value = (instance as unknown as { tenantId?: unknown }).tenantId;\n return typeof value === 'string' && value.length > 0\n ? value\n : (getTenantId() ?? null);\n}\n\nfunction changedRowSnapshot(instance: SmrtObject): Record<string, unknown> {\n const serializable = instance.toJSON();\n return serializable && typeof serializable === 'object'\n ? (serializable as Record<string, unknown>)\n : {};\n}\n\nasync function authorizeReportRefreshExecution(\n args: ReportRefreshJobArgs,\n reportClass: string,\n jobTenantId: string | null,\n): Promise<void> {\n if ((args.tenantId ?? null) !== jobTenantId) {\n throw new Error('Invalid report refresh execution tenant');\n }\n const authority = args.executionAuthority;\n if ((args.trigger ?? 'job') === 'manual' && !authority) {\n throw new Error('Manual report refresh execution authority is missing');\n }\n // Scheduled and on-change maintenance jobs predate user-bound actions and\n // intentionally run under the worker's system authority.\n if (!authority) return;\n const principal = authority.principal;\n const tenantId = args.tenantId ?? null;\n if (\n authority.version !== 1 ||\n principal?.version !== 1 ||\n !authority.hostId ||\n !principal.actorUserId ||\n principal.tenantId !== tenantId ||\n principal.tenantId !== jobTenantId\n ) {\n throw new Error('Invalid report refresh execution authority');\n }\n const host = executionAuthorityHosts.get(authority.hostId);\n if (!host) {\n throw new Error(\n `No report refresh authority host registered for ${authority.hostId}`,\n );\n }\n const context: ReportRefreshExecutionAuthorityContext = {\n phase: 'execute',\n reportClass,\n mode: args.mode ?? 'incremental',\n trigger: args.trigger ?? 'job',\n tenantId,\n };\n try {\n await host.authorize(\n Object.freeze({ ...principal }),\n Object.freeze(context),\n );\n } catch {\n await host.audit({\n ...context,\n outcome: 'denied',\n principal: Object.freeze({ ...principal }),\n reason: 'current_authority_denied',\n });\n throw new Error('Report refresh execution authority denied');\n }\n await host.audit({\n ...context,\n outcome: 'allowed',\n principal: Object.freeze({ ...principal }),\n });\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_report_refresh_tasks',\n ...INTERNAL_SURFACE,\n})\nexport class SmrtReportRefreshTask extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-reports\";\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @field({ type: 'text', required: true })\n reportClass: string = '';\n\n @field({ type: 'text', required: true })\n mode: ReportRefreshMode = 'incremental';\n\n @field({ type: 'text', required: true })\n trigger: ReportRefreshTrigger = 'job';\n\n @field({ type: 'json' })\n args: ReportRefreshJobArgs = {};\n\n @backgroundEligible()\n async run(\n args: ReportRefreshJobArgs = {},\n context?: JobExecutionContext,\n ): Promise<unknown> {\n assertReportRefreshJobIntegrity(args);\n const executionContext = getActiveJobExecutionContext() ?? context;\n assertReportRefreshJobTarget(args, executionContext);\n const reportClass = args.reportClass;\n if (!reportClass) {\n throw new Error('Report refresh job requires reportClass');\n }\n const mode = args.mode ?? 'incremental';\n const trigger = args.trigger ?? 'job';\n\n const reportCtor = resolveReportClass(reportClass);\n // A runner context, including its explicit global `null` tenant, owns the\n // scope. Only direct callers without runner context may use instance scope.\n let jobTenantId: string | null;\n if (executionContext) {\n const runnerTenantId = executionContext.job.tenantId;\n if (runnerTenantId === null) {\n jobTenantId = null;\n } else if (\n typeof runnerTenantId === 'string' &&\n runnerTenantId.length > 0\n ) {\n jobTenantId = runnerTenantId;\n } else {\n throw new Error('Invalid report refresh execution tenant');\n }\n } else {\n jobTenantId = tenantIdFromInstance(this);\n }\n await authorizeReportRefreshExecution(args, reportClass, jobTenantId);\n let tenantIds = args.tenantIds;\n // Only a signed global schedule can discover scopes. Tenant-bound jobs\n // (including older queued static schedules) retain their single scope.\n if (trigger === 'schedule' && jobTenantId === null) {\n const definition = await buildReportDefinition(reportCtor);\n if (definition.refresh?.tenantFanout === 'discover') {\n if (tenantIds !== undefined) {\n throw new Error(\n 'Discovered report refresh cannot also specify tenantIds',\n );\n }\n const sourceTenant = await tenantColumn(definition.sourceClassName);\n const reportTenant = await tenantColumn(definition.reportClassName);\n if (!sourceTenant || !reportTenant) {\n throw new Error(\n 'Tenant discovery requires tenant columns on the source and report',\n );\n }\n const column = validateColumnName(sourceTenant);\n const table = validateColumnName(definition.sourceTable);\n const materializedName = ObjectRegistry.getTableName(\n definition.reportClassName,\n );\n if (!materializedName)\n throw new Error('Report table is not registered');\n const materializedTable = validateColumnName(materializedName);\n const materializedTenant = validateColumnName(reportTenant);\n // Retain scopes with cached rows after their final source row disappears,\n // so a rebuild can clear stale groups without touching the global slice.\n const result = await this.db.query(\n `SELECT CAST(${column} AS TEXT) AS tenant_id FROM ${table} WHERE ${column} IS NOT NULL\n UNION\n SELECT CAST(${materializedTenant} AS TEXT) AS tenant_id FROM ${materializedTable} WHERE ${materializedTenant} IS NOT NULL\n ORDER BY tenant_id`,\n );\n tenantIds = result.rows.map((row) => {\n if (\n typeof row.tenant_id !== 'string' ||\n row.tenant_id.trim().length === 0\n ) {\n throw new Error('Invalid tenant id discovered in report source');\n }\n return row.tenant_id;\n });\n // An empty array otherwise means a global refresh in refreshReport.\n if (tenantIds.length === 0) {\n return {\n rowCount: 0,\n refreshedAt: new Date(),\n mode,\n tenantId: null,\n changedGroupCount: 0,\n tenantResults: [],\n };\n }\n }\n }\n return refreshReport(reportCtor, {\n db: this.db,\n mode,\n trigger,\n tenantId: jobTenantId,\n tenantIds,\n adapterType: args.adapterType,\n scheduleId: args.scheduleId ?? args._scheduleId,\n changedRows: args.changedRows,\n });\n }\n}\n\n/** Worker target whose authority requirement cannot be downgraded by job args. */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_principal_report_refresh_tasks',\n ...INTERNAL_SURFACE,\n})\nexport class SmrtPrincipalReportRefreshTask extends SmrtReportRefreshTask { static __smrtPackage__ = \"@happyvertical/smrt-reports\";\n override async run(\n args: ReportRefreshJobArgs = {},\n context?: JobExecutionContext,\n ): Promise<unknown> {\n return super.run({ ...args, trigger: 'manual' }, context);\n }\n}\n\nexport async function enqueueReportRefresh(\n options: EnqueueReportRefreshOptions,\n): Promise<SmrtJob> {\n const integritySigner = resolveReportRefreshIntegritySigner(\n options.integritySigner,\n );\n if (!integritySigner) {\n throw new Error(\n 'Report refresh queue requires a durable job integrity signer',\n );\n }\n if (options.trigger === 'manual' && !options.executionAuthority) {\n throw new Error('Manual report refresh requires execution-time authority');\n }\n if (\n options.executionAuthority &&\n (options.executionAuthority.version !== 1 ||\n options.executionAuthority.principal?.version !== 1 ||\n !options.executionAuthority.hostId ||\n !options.executionAuthority.principal.actorUserId ||\n options.executionAuthority.principal.tenantId !==\n (options.tenantId ?? null))\n ) {\n throw new Error('Invalid report refresh execution authority');\n }\n if (\n options.executionAuthority &&\n (options.trigger !== 'manual' || (options.tenantIds?.length ?? 0) > 0)\n ) {\n throw new Error(\n 'Principal-bound report refresh requires one manual tenant scope',\n );\n }\n await ObjectRegistry.ensureManifestLoaded('SmrtJob');\n const collection = await SmrtJobCollection.create({ db: options.db });\n const taskType = canonicalClassName(\n options.trigger === 'manual'\n ? SmrtPrincipalReportRefreshTask\n : SmrtReportRefreshTask,\n );\n const scheduleId = options.scheduleId ?? options._scheduleId;\n\n const unsignedArgs: Omit<ReportRefreshJobArgs, 'integrity'> = {\n reportClass: options.reportClass,\n mode: options.mode ?? 'incremental',\n trigger: options.trigger ?? 'job',\n tenantId: options.tenantId,\n tenantIds: options.tenantIds,\n adapterType: options.adapterType,\n changedRows: options.changedRows,\n scheduleId,\n executionAuthority: options.executionAuthority,\n };\n const integrity = integritySigner.sign(unsignedArgs);\n if (!integritySigner.verify(unsignedArgs, integrity)) {\n throw new Error(\n 'Report refresh job integrity signer rejected its queued payload',\n );\n }\n return collection.enqueueJob(\n {\n tenantId: options.tenantId ?? null,\n queue: options.queue ?? 'reports',\n objectType: taskType,\n objectId: null,\n method: 'run',\n args: { ...unsignedArgs, _scheduleId: scheduleId, integrity },\n priority: options.priority ?? 70,\n timeout: options.timeout ?? 3600000,\n maxAttempts: options.maxAttempts ?? 3,\n },\n { tenantJobCap: options.tenantJobCap },\n );\n}\n\nexport async function ensureReportRefreshSchedules(\n options: EnsureReportSchedulesOptions,\n): Promise<void> {\n await assertReportTablesReady(options.db, REPORT_SCHEDULER_TABLES);\n\n for (const reportCtor of options.reports) {\n const definition = await buildReportDefinition(reportCtor);\n const refresh = definition.refresh;\n if (!refresh || refresh.manual) continue;\n\n const reportClass = canonicalClassName(reportCtor);\n const targetTenants =\n refresh.tenantFanout === true\n ? options.tenantIds\n : [null as string | null];\n if (\n refresh.tenantFanout === true &&\n (!targetTenants || targetTenants.length === 0)\n ) {\n throw new Error(\n `${definition.reportClassName} refresh.tenantFanout requires tenantIds when creating schedules.`,\n );\n }\n\n const schedules = [\n refresh.schedule\n ? {\n cron: refresh.schedule,\n mode: refresh.mode ?? 'incremental',\n trigger: 'schedule' as const,\n }\n : null,\n refresh.fullRebuildSchedule\n ? {\n cron: refresh.fullRebuildSchedule,\n mode: 'rebuild' as const,\n trigger: 'schedule' as const,\n }\n : null,\n ].filter(Boolean) as Array<{\n cron: string;\n mode: ReportRefreshMode;\n trigger: ReportRefreshTrigger;\n }>;\n\n for (const schedule of schedules) {\n validateCronExpression(schedule.cron);\n for (const tenantId of targetTenants ?? []) {\n const scopeKey = scopeKeyForTenant(tenantId);\n const id = stableUuid([\n 'schedule',\n reportClass,\n scopeKey,\n schedule.cron,\n schedule.mode,\n ]);\n const now = new Date().toISOString();\n const values = {\n id,\n slug: id,\n context: scopeKey,\n tenant_id: tenantId,\n scope_key: scopeKey,\n report_class: reportClass,\n cron: schedule.cron,\n trigger: schedule.trigger,\n mode: schedule.mode,\n enabled: true,\n status: 'active',\n next_run: getNextCronDate(schedule.cron).toISOString(),\n last_run: null,\n last_status: null,\n last_error: null,\n run_count: 0,\n success_count: 0,\n failure_count: 0,\n running_count: 0,\n max_concurrent: 1,\n queue: options.queue ?? 'reports',\n priority: options.priority ?? 70,\n timeout: options.timeout ?? 3600000,\n created_at: now,\n updated_at: now,\n };\n const columns = Object.keys(values);\n await options.db.query(\n `INSERT INTO ${REPORT_SCHEDULES_TABLE} (${columns.join(', ')})\n VALUES (${columns.map((_, index) => `$${index + 1}`).join(', ')})\n ON CONFLICT (report_class, scope_key, cron, mode) DO NOTHING`,\n ...Object.values(values),\n );\n }\n }\n }\n}\n\nexport class ReportScheduleRunner extends EventEmitter {\n readonly id: string;\n private readonly config: Required<\n Omit<ReportScheduleRunnerConfig, 'integritySigner'>\n > &\n Pick<ReportScheduleRunnerConfig, 'integritySigner'>;\n private db: DatabaseInterface | null = null;\n private running = false;\n private pollTimer: NodeJS.Timeout | null = null;\n private readonly taskBindings = new WeakMap<object, () => void>();\n\n constructor(config: ReportScheduleRunnerConfig = {}) {\n super();\n this.config = {\n id: config.id || `reports_${stableUuid([Date.now()]).slice(0, 8)}`,\n pollInterval: config.pollInterval ?? 60000,\n batchSize: config.batchSize ?? 50,\n integritySigner: config.integritySigner,\n };\n this.id = this.config.id;\n }\n\n async initialize(db: DatabaseInterface): Promise<void> {\n this.db = db;\n await assertReportTablesReady(db, REPORT_SCHEDULER_TABLES);\n }\n\n async start(): Promise<void> {\n if (this.running) return;\n if (!this.db) {\n throw new Error(\n 'ReportScheduleRunner not initialized. Call initialize() first.',\n );\n }\n this.running = true;\n this.startPolling();\n this.emit('runner:started');\n }\n\n async stop(): Promise<void> {\n if (!this.running) return;\n this.running = false;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.emit('runner:stopped');\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n /** Bind terminal report-job events once; call the returned function on shutdown. */\n bindTaskRunner(taskRunner: Pick<TaskRunner, 'on' | 'off'>): () => void {\n if (!this.db)\n throw new Error(\n 'ReportScheduleRunner not initialized. Call initialize() first.',\n );\n const existing = this.taskBindings.get(taskRunner);\n if (existing) return existing;\n const complete = (job: SmrtJob, success: boolean, error?: Error) => {\n if (\n job.objectType !== canonicalClassName(SmrtReportRefreshTask) ||\n job.method !== 'run'\n )\n return;\n const args = job.args;\n if (!args || typeof args !== 'object' || Array.isArray(args)) return;\n // The compatibility _scheduleId is unsigned; only the signed id may\n // identify the schedule whose counters this event is allowed to change.\n const scheduleId = args.scheduleId;\n if (typeof scheduleId !== 'string' || !scheduleId) return;\n try {\n assertReportRefreshJobIntegrity(args);\n if ((args.tenantId ?? null) !== (job.tenantId ?? null)) {\n throw new Error('Invalid report refresh completion tenant');\n }\n } catch (error) {\n this.emit(\n 'runner:error',\n error instanceof Error ? error : new Error(String(error)),\n );\n return;\n }\n void this.handleJobCompletion(scheduleId, success, error?.message).catch(\n (failure) => {\n this.emit(\n 'runner:error',\n failure instanceof Error ? failure : new Error(String(failure)),\n );\n },\n );\n };\n const completed = (job: SmrtJob) => complete(job, true);\n const failed = (job: SmrtJob, error: Error) => complete(job, false, error);\n taskRunner.on('job:completed', completed);\n taskRunner.on('job:failed', failed);\n const unbind = () => {\n if (this.taskBindings.get(taskRunner) !== unbind) return;\n taskRunner.off('job:completed', completed);\n taskRunner.off('job:failed', failed);\n this.taskBindings.delete(taskRunner);\n };\n this.taskBindings.set(taskRunner, unbind);\n return unbind;\n }\n\n async handleJobCompletion(\n scheduleId: string,\n success: boolean,\n errorMessage?: string,\n ): Promise<void> {\n if (!this.db) return;\n const now = new Date().toISOString();\n if (success) {\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = CASE WHEN COALESCE(running_count, 0) > 0 THEN running_count - 1 ELSE 0 END,\n last_run = ?,\n last_status = 'success',\n last_error = NULL,\n run_count = COALESCE(run_count, 0) + 1,\n success_count = COALESCE(success_count, 0) + 1,\n updated_at = ?\n WHERE id = ?`,\n now,\n now,\n scheduleId,\n );\n this.emit('schedule:completed', scheduleId);\n return;\n }\n\n const safeError = redactErrorMessage(errorMessage ?? 'Unknown error');\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = CASE WHEN COALESCE(running_count, 0) > 0 THEN running_count - 1 ELSE 0 END,\n last_run = ?,\n last_status = 'failed',\n last_error = ?,\n run_count = COALESCE(run_count, 0) + 1,\n failure_count = COALESCE(failure_count, 0) + 1,\n updated_at = ?\n WHERE id = ?`,\n now,\n safeError,\n now,\n scheduleId,\n );\n this.emit('schedule:failed', scheduleId, safeError);\n }\n\n private startPolling(): void {\n const poll = async () => {\n if (!this.running) return;\n try {\n await this.poll();\n } catch (error) {\n this.emit('runner:error', error as Error);\n }\n if (this.running) {\n this.pollTimer = setTimeout(poll, this.config.pollInterval);\n if (typeof this.pollTimer.unref === 'function') {\n this.pollTimer.unref();\n }\n }\n };\n poll();\n }\n\n async poll(): Promise<void> {\n if (!this.db) return;\n const result = await this.db.query(\n `SELECT CAST(id AS TEXT) AS id, CAST(tenant_id AS TEXT) AS tenant_id,\n report_class, cron, mode, trigger, queue, priority, timeout\n FROM ${REPORT_SCHEDULES_TABLE}\n WHERE enabled = true\n AND status = 'active'\n AND next_run <= ?\n AND COALESCE(running_count, 0) < COALESCE(max_concurrent, 1)\n ORDER BY next_run ASC\n LIMIT ?`,\n new Date().toISOString(),\n this.config.batchSize,\n );\n\n for (const row of result.rows) {\n await this.triggerSchedule(row as ReportScheduleRow);\n }\n }\n\n private async triggerSchedule(row: ReportScheduleRow): Promise<void> {\n if (!this.db) return;\n const schedule: ReportScheduleInfo = {\n id: String(row.id),\n reportClass: String(row.report_class),\n tenantId:\n typeof row.tenant_id === 'string' && row.tenant_id.length > 0\n ? row.tenant_id\n : null,\n cron: String(row.cron),\n mode: (row.mode as ReportRefreshMode) || 'incremental',\n };\n\n try {\n const nextRun = getNextCronDate(schedule.cron);\n await enqueueReportRefresh({\n db: this.db,\n reportClass: schedule.reportClass,\n mode: schedule.mode,\n trigger: (row.trigger as ReportRefreshTrigger) || 'schedule',\n tenantId: schedule.tenantId,\n scheduleId: schedule.id,\n queue: String(row.queue || 'reports'),\n priority: Number(row.priority ?? 70),\n timeout: Number(row.timeout ?? 3600000),\n integritySigner: this.config.integritySigner,\n });\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET running_count = COALESCE(running_count, 0) + 1,\n next_run = ?,\n updated_at = ?\n WHERE id = ?`,\n nextRun.toISOString(),\n new Date().toISOString(),\n schedule.id,\n );\n this.emit('schedule:triggered', schedule);\n } catch (error) {\n await this.db.query(\n `UPDATE ${REPORT_SCHEDULES_TABLE}\n SET last_error = ?,\n updated_at = ?\n WHERE id = ?`,\n redactErrorForPersistence(error),\n new Date().toISOString(),\n schedule.id,\n );\n this.emit('schedule:error', schedule, error as Error);\n }\n }\n}\n\ninterface ReportScheduleRow {\n id: unknown;\n tenant_id: unknown;\n report_class: unknown;\n cron: unknown;\n trigger: unknown;\n mode: unknown;\n queue: unknown;\n priority: unknown;\n timeout: unknown;\n}\n\nexport function registerReportRefreshInterceptor(\n options: ReportRefreshInterceptorOptions,\n): () => boolean {\n const name = options.name ?? 'smrt-reports-refresh';\n GlobalInterceptors.register({\n name,\n priority: -10,\n async afterSave(instance, context) {\n await triggerReportsForInstance(options, instance, context);\n },\n async afterDelete(instance, context) {\n await triggerReportsForInstance(options, instance, context);\n },\n });\n return () => GlobalInterceptors.unregister(name);\n}\n\nasync function triggerReportsForInstance(\n options: ReportRefreshInterceptorOptions,\n instance: SmrtObject,\n context: InterceptorContext,\n): Promise<void> {\n for (const reportCtor of options.reports) {\n const definition = await buildReportDefinition(reportCtor);\n if (definition.refresh?.manual) continue;\n if (!sourceMatches(definition, instance, context)) continue;\n\n const mode = definition.refresh?.mode ?? 'incremental';\n const tenantId = tenantIdFromInstance(instance);\n const changedRows = [changedRowSnapshot(instance)];\n if (options.enqueue === false) {\n await refreshReport(reportCtor, {\n db: options.db,\n mode,\n trigger: 'change',\n tenantId,\n changedRows,\n });\n continue;\n }\n\n await enqueueReportRefresh({\n db: options.db,\n reportClass: canonicalClassName(reportCtor),\n mode,\n trigger: 'change',\n tenantId,\n queue: options.queue,\n priority: options.priority,\n timeout: options.timeout,\n tenantJobCap: options.tenantJobCap,\n changedRows,\n integritySigner: options.integritySigner,\n });\n }\n}\n"],"mappings":"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"}
|