@probelabs/visor 0.1.145 → 0.1.146-ee

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.
Files changed (73) hide show
  1. package/dist/index.js +1722 -25
  2. package/dist/sdk/{check-provider-registry-HK6M4PDQ.mjs → check-provider-registry-TH25S2OB.mjs} +3 -3
  3. package/dist/sdk/{check-provider-registry-TG5G2TF3.mjs → check-provider-registry-WSFL2SVQ.mjs} +5 -5
  4. package/dist/sdk/{chunk-E6SMU2Z4.mjs → chunk-3BOOHJI5.mjs} +12 -12
  5. package/dist/sdk/{chunk-5FXGIBJQ.mjs.map → chunk-3BOOHJI5.mjs.map} +1 -1
  6. package/dist/sdk/{chunk-GZMQPC6D.mjs → chunk-74YJMONB.mjs} +14 -14
  7. package/dist/sdk/{host-BYIV4QJ3.mjs → host-GA76UESS.mjs} +2 -2
  8. package/dist/sdk/knex-store-HPXJILBL.mjs +411 -0
  9. package/dist/sdk/knex-store-HPXJILBL.mjs.map +1 -0
  10. package/dist/sdk/loader-ZC5G3JGJ.mjs +89 -0
  11. package/dist/sdk/loader-ZC5G3JGJ.mjs.map +1 -0
  12. package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs +655 -0
  13. package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs.map +1 -0
  14. package/dist/sdk/{schedule-tool-handler-4O2VKNG2.mjs → schedule-tool-handler-62K3NGH6.mjs} +2 -2
  15. package/dist/sdk/{schedule-tool-handler-BTLEDYAI.mjs → schedule-tool-handler-NYL2ONJB.mjs} +6 -6
  16. package/dist/sdk/sdk.js +1517 -259
  17. package/dist/sdk/sdk.js.map +1 -1
  18. package/dist/sdk/sdk.mjs +5 -5
  19. package/dist/sdk/validator-XTZJZZJH.mjs +134 -0
  20. package/dist/sdk/validator-XTZJZZJH.mjs.map +1 -0
  21. package/dist/sdk/{workflow-check-provider-OM62QYHF.mjs → workflow-check-provider-LVUUL2PZ.mjs} +3 -3
  22. package/dist/sdk/{workflow-check-provider-RARO4N5E.mjs → workflow-check-provider-XC7E5OFH.mjs} +5 -5
  23. package/package.json +1 -1
  24. package/dist/output/traces/run-2026-02-26T07-47-34-788Z.ndjson +0 -138
  25. package/dist/output/traces/run-2026-02-26T07-48-25-935Z.ndjson +0 -1442
  26. package/dist/sdk/check-provider-registry-HFPKHYTG.mjs +0 -30
  27. package/dist/sdk/chunk-5FXGIBJQ.mjs +0 -40651
  28. package/dist/sdk/chunk-6XPTQBXL.mjs +0 -443
  29. package/dist/sdk/chunk-6XPTQBXL.mjs.map +0 -1
  30. package/dist/sdk/chunk-E6SMU2Z4.mjs.map +0 -1
  31. package/dist/sdk/chunk-K3M5YVEU.mjs +0 -1502
  32. package/dist/sdk/chunk-K3M5YVEU.mjs.map +0 -1
  33. package/dist/sdk/chunk-L6ABOJVL.mjs +0 -739
  34. package/dist/sdk/chunk-L6ABOJVL.mjs.map +0 -1
  35. package/dist/sdk/chunk-O7WE6HCA.mjs +0 -443
  36. package/dist/sdk/chunk-O7WE6HCA.mjs.map +0 -1
  37. package/dist/sdk/chunk-PES5G5RR.mjs +0 -1502
  38. package/dist/sdk/chunk-PES5G5RR.mjs.map +0 -1
  39. package/dist/sdk/chunk-XHIXKNUF.mjs +0 -739
  40. package/dist/sdk/chunk-XHIXKNUF.mjs.map +0 -1
  41. package/dist/sdk/failure-condition-evaluator-2BJHKTMX.mjs +0 -17
  42. package/dist/sdk/failure-condition-evaluator-V3EJGD55.mjs +0 -17
  43. package/dist/sdk/github-frontend-ICKRZ4VV.mjs +0 -1356
  44. package/dist/sdk/github-frontend-ICKRZ4VV.mjs.map +0 -1
  45. package/dist/sdk/github-frontend-PSGUGYHT.mjs +0 -1356
  46. package/dist/sdk/github-frontend-PSGUGYHT.mjs.map +0 -1
  47. package/dist/sdk/host-CVJ3VG5Y.mjs +0 -63
  48. package/dist/sdk/host-CVJ3VG5Y.mjs.map +0 -1
  49. package/dist/sdk/routing-3XDRYU3Z.mjs +0 -25
  50. package/dist/sdk/routing-AJNUTCH7.mjs +0 -25
  51. package/dist/sdk/routing-AJNUTCH7.mjs.map +0 -1
  52. package/dist/sdk/schedule-tool-handler-4O2VKNG2.mjs.map +0 -1
  53. package/dist/sdk/schedule-tool-handler-BTLEDYAI.mjs.map +0 -1
  54. package/dist/sdk/schedule-tool-handler-R7PNPWWK.mjs +0 -40
  55. package/dist/sdk/schedule-tool-handler-R7PNPWWK.mjs.map +0 -1
  56. package/dist/sdk/trace-helpers-OZTZBK6T.mjs +0 -25
  57. package/dist/sdk/trace-helpers-OZTZBK6T.mjs.map +0 -1
  58. package/dist/sdk/trace-helpers-VUUP6ILH.mjs +0 -25
  59. package/dist/sdk/trace-helpers-VUUP6ILH.mjs.map +0 -1
  60. package/dist/sdk/workflow-check-provider-3IIKJFM4.mjs +0 -30
  61. package/dist/sdk/workflow-check-provider-3IIKJFM4.mjs.map +0 -1
  62. package/dist/sdk/workflow-check-provider-OM62QYHF.mjs.map +0 -1
  63. package/dist/sdk/workflow-check-provider-RARO4N5E.mjs.map +0 -1
  64. package/dist/traces/run-2026-02-26T07-47-34-788Z.ndjson +0 -138
  65. package/dist/traces/run-2026-02-26T07-48-25-935Z.ndjson +0 -1442
  66. /package/dist/sdk/{check-provider-registry-HFPKHYTG.mjs.map → check-provider-registry-TH25S2OB.mjs.map} +0 -0
  67. /package/dist/sdk/{check-provider-registry-HK6M4PDQ.mjs.map → check-provider-registry-WSFL2SVQ.mjs.map} +0 -0
  68. /package/dist/sdk/{chunk-GZMQPC6D.mjs.map → chunk-74YJMONB.mjs.map} +0 -0
  69. /package/dist/sdk/{host-BYIV4QJ3.mjs.map → host-GA76UESS.mjs.map} +0 -0
  70. /package/dist/sdk/{check-provider-registry-TG5G2TF3.mjs.map → schedule-tool-handler-62K3NGH6.mjs.map} +0 -0
  71. /package/dist/sdk/{failure-condition-evaluator-2BJHKTMX.mjs.map → schedule-tool-handler-NYL2ONJB.mjs.map} +0 -0
  72. /package/dist/sdk/{failure-condition-evaluator-V3EJGD55.mjs.map → workflow-check-provider-LVUUL2PZ.mjs.map} +0 -0
  73. /package/dist/sdk/{routing-3XDRYU3Z.mjs.map → workflow-check-provider-XC7E5OFH.mjs.map} +0 -0
@@ -53,11 +53,11 @@ import {
53
53
  init_routing,
54
54
  init_snapshot_store,
55
55
  snapshot_store_exports
56
- } from "./chunk-PES5G5RR.mjs";
56
+ } from "./chunk-I42ZCVA5.mjs";
57
57
  import {
58
58
  FailureConditionEvaluator,
59
59
  init_failure_condition_evaluator
60
- } from "./chunk-XHIXKNUF.mjs";
60
+ } from "./chunk-L3XPYQ6I.mjs";
61
61
  import {
62
62
  addEvent,
63
63
  emitNdjsonFallback,
@@ -68,7 +68,7 @@ import {
68
68
  setSpanAttributes,
69
69
  trace_helpers_exports,
70
70
  withActiveSpan
71
- } from "./chunk-O7WE6HCA.mjs";
71
+ } from "./chunk-OM3WYVFI.mjs";
72
72
  import {
73
73
  addDiagramBlock,
74
74
  init_metrics
@@ -3947,7 +3947,7 @@ async function executeWorkflowAsTool(workflowId, args, context2, argsOverrides)
3947
3947
  ...args,
3948
3948
  ...argsOverrides
3949
3949
  };
3950
- const { WorkflowCheckProvider: WorkflowCheckProvider2 } = await import("./workflow-check-provider-3IIKJFM4.mjs");
3950
+ const { WorkflowCheckProvider: WorkflowCheckProvider2 } = await import("./workflow-check-provider-XC7E5OFH.mjs");
3951
3951
  const provider = new WorkflowCheckProvider2();
3952
3952
  const checkConfig = {
3953
3953
  type: "workflow",
@@ -8202,7 +8202,7 @@ async function executeCheckWithForEachItems2(checkId, forEachParent, forEachItem
8202
8202
  }
8203
8203
  }
8204
8204
  try {
8205
- const { evaluateTransitions } = await import("./routing-3XDRYU3Z.mjs");
8205
+ const { evaluateTransitions } = await import("./routing-RIHVCEIU.mjs");
8206
8206
  const transTarget = await evaluateTransitions(
8207
8207
  onFinish.transitions,
8208
8208
  forEachParent,
@@ -8262,7 +8262,7 @@ async function executeCheckWithForEachItems2(checkId, forEachParent, forEachItem
8262
8262
  `[LevelDispatch] Error evaluating on_finish transitions for ${forEachParent}: ${e instanceof Error ? e.message : String(e)}`
8263
8263
  );
8264
8264
  }
8265
- const { evaluateGoto: evaluateGoto2 } = await import("./routing-3XDRYU3Z.mjs");
8265
+ const { evaluateGoto: evaluateGoto2 } = await import("./routing-RIHVCEIU.mjs");
8266
8266
  if (context2.debug) {
8267
8267
  logger.info(
8268
8268
  `[LevelDispatch] Evaluating on_finish.goto_js for forEach parent: ${forEachParent}`
@@ -11801,7 +11801,7 @@ var init_state_machine_execution_engine = __esm({
11801
11801
  try {
11802
11802
  const map = options?.webhookContext?.webhookData;
11803
11803
  if (map) {
11804
- const { CheckProviderRegistry: CheckProviderRegistry2 } = await import("./check-provider-registry-HFPKHYTG.mjs");
11804
+ const { CheckProviderRegistry: CheckProviderRegistry2 } = await import("./check-provider-registry-WSFL2SVQ.mjs");
11805
11805
  const reg = CheckProviderRegistry2.getInstance();
11806
11806
  const p = reg.getProvider("http_input");
11807
11807
  if (p && typeof p.setWebhookContext === "function") p.setWebhookContext(map);
@@ -11924,7 +11924,7 @@ var init_state_machine_execution_engine = __esm({
11924
11924
  tag_filter: tagFilter
11925
11925
  } : config;
11926
11926
  try {
11927
- const { CheckProviderRegistry: CheckProviderRegistry2 } = await import("./check-provider-registry-HFPKHYTG.mjs");
11927
+ const { CheckProviderRegistry: CheckProviderRegistry2 } = await import("./check-provider-registry-WSFL2SVQ.mjs");
11928
11928
  const registry = CheckProviderRegistry2.getInstance();
11929
11929
  registry.setCustomTools(configWithTagFilter.tools || {});
11930
11930
  } catch (error) {
@@ -11988,7 +11988,7 @@ var init_state_machine_execution_engine = __esm({
11988
11988
  try {
11989
11989
  const webhookData = this.executionContext?.webhookContext?.webhookData;
11990
11990
  if (webhookData instanceof Map) {
11991
- const { extractSlackContext: extractSlackContext2 } = await import("./schedule-tool-handler-BTLEDYAI.mjs");
11991
+ const { extractSlackContext: extractSlackContext2 } = await import("./schedule-tool-handler-62K3NGH6.mjs");
11992
11992
  const slackCtx = extractSlackContext2(webhookData);
11993
11993
  if (slackCtx) {
11994
11994
  const payload = Array.from(webhookData.values())[0];
@@ -12017,7 +12017,7 @@ var init_state_machine_execution_engine = __esm({
12017
12017
  if (Array.isArray(configWithTagFilter.frontends) && configWithTagFilter.frontends.length > 0) {
12018
12018
  try {
12019
12019
  const { EventBus } = await import("./event-bus-5K3Y2FCS.mjs");
12020
- const { FrontendsHost } = await import("./host-CVJ3VG5Y.mjs");
12020
+ const { FrontendsHost } = await import("./host-5BJ25CUZ.mjs");
12021
12021
  const bus = new EventBus();
12022
12022
  context2.eventBus = bus;
12023
12023
  frontendsHost = new FrontendsHost(bus, logger);
@@ -12367,9 +12367,9 @@ var init_state_machine_execution_engine = __esm({
12367
12367
  * @returns Array of failure condition evaluation results
12368
12368
  */
12369
12369
  async evaluateFailureConditions(checkName, reviewSummary, config, previousOutputs, authorAssociation) {
12370
- const { FailureConditionEvaluator: FailureConditionEvaluator2 } = await import("./failure-condition-evaluator-2BJHKTMX.mjs");
12370
+ const { FailureConditionEvaluator: FailureConditionEvaluator2 } = await import("./failure-condition-evaluator-O464EJMD.mjs");
12371
12371
  const evaluator = new FailureConditionEvaluator2();
12372
- const { addEvent: addEvent3 } = await import("./trace-helpers-VUUP6ILH.mjs");
12372
+ const { addEvent: addEvent3 } = await import("./trace-helpers-QQSTZGDT.mjs");
12373
12373
  const { addFailIfTriggered } = await import("./metrics-I6A7IHG4.mjs");
12374
12374
  const checkConfig = config.checks?.[checkName];
12375
12375
  if (!checkConfig) {
@@ -37694,7 +37694,7 @@ function buildBuiltinGlobals(opts) {
37694
37694
  const scheduleFn = async (args = {}) => {
37695
37695
  try {
37696
37696
  const { handleScheduleAction: handleScheduleAction2, buildScheduleToolContext: buildScheduleToolContext2 } = await import("./schedule-tool-2COUUTF7.mjs");
37697
- const { extractSlackContext: extractSlackContext2 } = await import("./schedule-tool-handler-BTLEDYAI.mjs");
37697
+ const { extractSlackContext: extractSlackContext2 } = await import("./schedule-tool-handler-62K3NGH6.mjs");
37698
37698
  const parentCtx = opts.sessionInfo?._parentContext;
37699
37699
  const webhookData = parentCtx?.prInfo?.eventContext?.webhookData;
37700
37700
  const visorCfg = parentCtx?.config;
@@ -40648,4 +40648,4 @@ undici/lib/fetch/body.js:
40648
40648
  undici/lib/websocket/frame.js:
40649
40649
  (*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> *)
40650
40650
  */
40651
- //# sourceMappingURL=chunk-GZMQPC6D.mjs.map
40651
+ //# sourceMappingURL=chunk-74YJMONB.mjs.map
@@ -21,7 +21,7 @@ var init_host = __esm({
21
21
  const { NdjsonSink } = await import("./ndjson-sink-FD2PSXGD.mjs");
22
22
  this.frontends.push(new NdjsonSink(spec.config));
23
23
  } else if (spec.name === "github") {
24
- const { GitHubFrontend } = await import("./github-frontend-PSGUGYHT.mjs");
24
+ const { GitHubFrontend } = await import("./github-frontend-MSX6Q2WL.mjs");
25
25
  this.frontends.push(new GitHubFrontend());
26
26
  } else if (spec.name === "slack") {
27
27
  const { SlackFrontend } = await import("./slack-frontend-TZU2HIK7.mjs");
@@ -60,4 +60,4 @@ init_host();
60
60
  export {
61
61
  FrontendsHost
62
62
  };
63
- //# sourceMappingURL=host-BYIV4QJ3.mjs.map
63
+ //# sourceMappingURL=host-GA76UESS.mjs.map
@@ -0,0 +1,411 @@
1
+ import {
2
+ init_logger,
3
+ logger
4
+ } from "./chunk-SZXICFQ3.mjs";
5
+ import "./chunk-UCMJJ3IM.mjs";
6
+ import {
7
+ __esm,
8
+ __require
9
+ } from "./chunk-J7LXIPZS.mjs";
10
+
11
+ // src/enterprise/scheduler/knex-store.ts
12
+ import * as fs from "fs";
13
+ import * as path from "path";
14
+ import { v4 as uuidv4 } from "uuid";
15
+ function toNum(val) {
16
+ if (val === null || val === void 0) return void 0;
17
+ return typeof val === "string" ? parseInt(val, 10) : val;
18
+ }
19
+ function safeJsonParse(value) {
20
+ if (!value) return void 0;
21
+ try {
22
+ return JSON.parse(value);
23
+ } catch {
24
+ return void 0;
25
+ }
26
+ }
27
+ function fromDbRow(row) {
28
+ return {
29
+ id: row.id,
30
+ creatorId: row.creator_id,
31
+ creatorContext: row.creator_context ?? void 0,
32
+ creatorName: row.creator_name ?? void 0,
33
+ timezone: row.timezone,
34
+ schedule: row.schedule_expr,
35
+ runAt: toNum(row.run_at),
36
+ isRecurring: row.is_recurring === true || row.is_recurring === 1,
37
+ originalExpression: row.original_expression,
38
+ workflow: row.workflow ?? void 0,
39
+ workflowInputs: safeJsonParse(row.workflow_inputs),
40
+ outputContext: safeJsonParse(row.output_context),
41
+ status: row.status,
42
+ createdAt: toNum(row.created_at),
43
+ lastRunAt: toNum(row.last_run_at),
44
+ nextRunAt: toNum(row.next_run_at),
45
+ runCount: row.run_count,
46
+ failureCount: row.failure_count,
47
+ lastError: row.last_error ?? void 0,
48
+ previousResponse: row.previous_response ?? void 0
49
+ };
50
+ }
51
+ function toInsertRow(schedule) {
52
+ return {
53
+ id: schedule.id,
54
+ creator_id: schedule.creatorId,
55
+ creator_context: schedule.creatorContext ?? null,
56
+ creator_name: schedule.creatorName ?? null,
57
+ timezone: schedule.timezone,
58
+ schedule_expr: schedule.schedule,
59
+ run_at: schedule.runAt ?? null,
60
+ is_recurring: schedule.isRecurring,
61
+ original_expression: schedule.originalExpression,
62
+ workflow: schedule.workflow ?? null,
63
+ workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,
64
+ output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,
65
+ status: schedule.status,
66
+ created_at: schedule.createdAt,
67
+ last_run_at: schedule.lastRunAt ?? null,
68
+ next_run_at: schedule.nextRunAt ?? null,
69
+ run_count: schedule.runCount,
70
+ failure_count: schedule.failureCount,
71
+ last_error: schedule.lastError ?? null,
72
+ previous_response: schedule.previousResponse ?? null
73
+ };
74
+ }
75
+ var KnexStoreBackend;
76
+ var init_knex_store = __esm({
77
+ "src/enterprise/scheduler/knex-store.ts"() {
78
+ init_logger();
79
+ KnexStoreBackend = class {
80
+ knex = null;
81
+ driver;
82
+ connection;
83
+ constructor(driver, storageConfig, _haConfig) {
84
+ this.driver = driver;
85
+ this.connection = storageConfig.connection || {};
86
+ }
87
+ async initialize() {
88
+ const { createRequire } = __require("module");
89
+ const runtimeRequire = createRequire(__filename);
90
+ let knexFactory;
91
+ try {
92
+ knexFactory = runtimeRequire("knex");
93
+ } catch (err) {
94
+ const code = err?.code;
95
+ if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
96
+ throw new Error(
97
+ "knex is required for PostgreSQL/MySQL/MSSQL schedule storage. Install it with: npm install knex"
98
+ );
99
+ }
100
+ throw err;
101
+ }
102
+ const clientMap = {
103
+ postgresql: "pg",
104
+ mysql: "mysql2",
105
+ mssql: "tedious"
106
+ };
107
+ const client = clientMap[this.driver];
108
+ let connection;
109
+ if (this.connection.connection_string) {
110
+ connection = this.connection.connection_string;
111
+ } else if (this.driver === "mssql") {
112
+ connection = this.buildMssqlConnection();
113
+ } else {
114
+ connection = this.buildStandardConnection();
115
+ }
116
+ this.knex = knexFactory({
117
+ client,
118
+ connection,
119
+ pool: {
120
+ min: this.connection.pool?.min ?? 0,
121
+ max: this.connection.pool?.max ?? 10
122
+ }
123
+ });
124
+ await this.migrateSchema();
125
+ logger.info(`[KnexStore] Initialized (${this.driver})`);
126
+ }
127
+ buildStandardConnection() {
128
+ return {
129
+ host: this.connection.host || "localhost",
130
+ port: this.connection.port,
131
+ database: this.connection.database || "visor",
132
+ user: this.connection.user,
133
+ password: this.connection.password,
134
+ ssl: this.resolveSslConfig()
135
+ };
136
+ }
137
+ buildMssqlConnection() {
138
+ const ssl = this.connection.ssl;
139
+ const sslEnabled = ssl === true || typeof ssl === "object" && ssl.enabled !== false;
140
+ return {
141
+ server: this.connection.host || "localhost",
142
+ port: this.connection.port,
143
+ database: this.connection.database || "visor",
144
+ user: this.connection.user,
145
+ password: this.connection.password,
146
+ options: {
147
+ encrypt: sslEnabled,
148
+ trustServerCertificate: typeof ssl === "object" ? ssl.reject_unauthorized === false : !sslEnabled
149
+ }
150
+ };
151
+ }
152
+ resolveSslConfig() {
153
+ const ssl = this.connection.ssl;
154
+ if (ssl === false || ssl === void 0) return false;
155
+ if (ssl === true) return { rejectUnauthorized: true };
156
+ if (ssl.enabled === false) return false;
157
+ const result = {
158
+ rejectUnauthorized: ssl.reject_unauthorized !== false
159
+ };
160
+ if (ssl.ca) {
161
+ const caPath = this.validateSslPath(ssl.ca, "CA certificate");
162
+ result.ca = fs.readFileSync(caPath, "utf8");
163
+ }
164
+ if (ssl.cert) {
165
+ const certPath = this.validateSslPath(ssl.cert, "client certificate");
166
+ result.cert = fs.readFileSync(certPath, "utf8");
167
+ }
168
+ if (ssl.key) {
169
+ const keyPath = this.validateSslPath(ssl.key, "client key");
170
+ result.key = fs.readFileSync(keyPath, "utf8");
171
+ }
172
+ return result;
173
+ }
174
+ validateSslPath(filePath, label) {
175
+ const resolved = path.resolve(filePath);
176
+ if (resolved !== path.normalize(resolved)) {
177
+ throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);
178
+ }
179
+ if (!fs.existsSync(resolved)) {
180
+ throw new Error(`SSL ${label} not found: ${filePath}`);
181
+ }
182
+ return resolved;
183
+ }
184
+ async shutdown() {
185
+ if (this.knex) {
186
+ await this.knex.destroy();
187
+ this.knex = null;
188
+ }
189
+ }
190
+ async migrateSchema() {
191
+ const knex = this.getKnex();
192
+ const exists = await knex.schema.hasTable("schedules");
193
+ if (!exists) {
194
+ await knex.schema.createTable("schedules", (table) => {
195
+ table.string("id", 36).primary();
196
+ table.string("creator_id", 255).notNullable().index();
197
+ table.string("creator_context", 255);
198
+ table.string("creator_name", 255);
199
+ table.string("timezone", 64).notNullable().defaultTo("UTC");
200
+ table.string("schedule_expr", 255);
201
+ table.bigInteger("run_at");
202
+ table.boolean("is_recurring").notNullable();
203
+ table.text("original_expression");
204
+ table.string("workflow", 255);
205
+ table.text("workflow_inputs");
206
+ table.text("output_context");
207
+ table.string("status", 20).notNullable().index();
208
+ table.bigInteger("created_at").notNullable();
209
+ table.bigInteger("last_run_at");
210
+ table.bigInteger("next_run_at");
211
+ table.integer("run_count").notNullable().defaultTo(0);
212
+ table.integer("failure_count").notNullable().defaultTo(0);
213
+ table.text("last_error");
214
+ table.text("previous_response");
215
+ table.index(["status", "next_run_at"]);
216
+ });
217
+ }
218
+ const locksExist = await knex.schema.hasTable("scheduler_locks");
219
+ if (!locksExist) {
220
+ await knex.schema.createTable("scheduler_locks", (table) => {
221
+ table.string("lock_id", 255).primary();
222
+ table.string("node_id", 255).notNullable();
223
+ table.string("lock_token", 36).notNullable();
224
+ table.bigInteger("acquired_at").notNullable();
225
+ table.bigInteger("expires_at").notNullable();
226
+ });
227
+ }
228
+ }
229
+ getKnex() {
230
+ if (!this.knex) {
231
+ throw new Error("[KnexStore] Not initialized. Call initialize() first.");
232
+ }
233
+ return this.knex;
234
+ }
235
+ // --- CRUD ---
236
+ async create(schedule) {
237
+ const knex = this.getKnex();
238
+ const newSchedule = {
239
+ ...schedule,
240
+ id: uuidv4(),
241
+ createdAt: Date.now(),
242
+ runCount: 0,
243
+ failureCount: 0,
244
+ status: "active"
245
+ };
246
+ await knex("schedules").insert(toInsertRow(newSchedule));
247
+ logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);
248
+ return newSchedule;
249
+ }
250
+ async importSchedule(schedule) {
251
+ const knex = this.getKnex();
252
+ const existing = await knex("schedules").where("id", schedule.id).first();
253
+ if (existing) return;
254
+ await knex("schedules").insert(toInsertRow(schedule));
255
+ }
256
+ async get(id) {
257
+ const knex = this.getKnex();
258
+ const row = await knex("schedules").where("id", id).first();
259
+ return row ? fromDbRow(row) : void 0;
260
+ }
261
+ async update(id, patch) {
262
+ const knex = this.getKnex();
263
+ const existing = await knex("schedules").where("id", id).first();
264
+ if (!existing) return void 0;
265
+ const current = fromDbRow(existing);
266
+ const updated = { ...current, ...patch, id: current.id };
267
+ const row = toInsertRow(updated);
268
+ delete row.id;
269
+ await knex("schedules").where("id", id).update(row);
270
+ return updated;
271
+ }
272
+ async delete(id) {
273
+ const knex = this.getKnex();
274
+ const deleted = await knex("schedules").where("id", id).del();
275
+ if (deleted > 0) {
276
+ logger.info(`[KnexStore] Deleted schedule ${id}`);
277
+ return true;
278
+ }
279
+ return false;
280
+ }
281
+ // --- Queries ---
282
+ async getByCreator(creatorId) {
283
+ const knex = this.getKnex();
284
+ const rows = await knex("schedules").where("creator_id", creatorId);
285
+ return rows.map((r) => fromDbRow(r));
286
+ }
287
+ async getActiveSchedules() {
288
+ const knex = this.getKnex();
289
+ const rows = await knex("schedules").where("status", "active");
290
+ return rows.map((r) => fromDbRow(r));
291
+ }
292
+ async getDueSchedules(now) {
293
+ const ts = now ?? Date.now();
294
+ const knex = this.getKnex();
295
+ const bFalse = this.driver === "mssql" ? 0 : false;
296
+ const bTrue = this.driver === "mssql" ? 1 : true;
297
+ const rows = await knex("schedules").where("status", "active").andWhere(function() {
298
+ this.where(function() {
299
+ this.where("is_recurring", bFalse).whereNotNull("run_at").where("run_at", "<=", ts);
300
+ }).orWhere(function() {
301
+ this.where("is_recurring", bTrue).whereNotNull("next_run_at").where("next_run_at", "<=", ts);
302
+ });
303
+ });
304
+ return rows.map((r) => fromDbRow(r));
305
+ }
306
+ async findByWorkflow(creatorId, workflowName) {
307
+ const knex = this.getKnex();
308
+ const escaped = workflowName.toLowerCase().replace(/[%_\\]/g, "\\$&");
309
+ const pattern = `%${escaped}%`;
310
+ const rows = await knex("schedules").where("creator_id", creatorId).where("status", "active").whereRaw("LOWER(workflow) LIKE ? ESCAPE '\\'", [pattern]);
311
+ return rows.map((r) => fromDbRow(r));
312
+ }
313
+ async getAll() {
314
+ const knex = this.getKnex();
315
+ const rows = await knex("schedules");
316
+ return rows.map((r) => fromDbRow(r));
317
+ }
318
+ async getStats() {
319
+ const knex = this.getKnex();
320
+ const boolTrue = this.driver === "mssql" ? "1" : "true";
321
+ const boolFalse = this.driver === "mssql" ? "0" : "false";
322
+ const result = await knex("schedules").select(
323
+ knex.raw("COUNT(*) as total"),
324
+ knex.raw("SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active"),
325
+ knex.raw("SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused"),
326
+ knex.raw("SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed"),
327
+ knex.raw("SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed"),
328
+ knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`),
329
+ knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`)
330
+ ).first();
331
+ return {
332
+ total: Number(result.total) || 0,
333
+ active: Number(result.active) || 0,
334
+ paused: Number(result.paused) || 0,
335
+ completed: Number(result.completed) || 0,
336
+ failed: Number(result.failed) || 0,
337
+ recurring: Number(result.recurring) || 0,
338
+ oneTime: Number(result.one_time) || 0
339
+ };
340
+ }
341
+ async validateLimits(creatorId, isRecurring, limits) {
342
+ const knex = this.getKnex();
343
+ if (limits.maxGlobal) {
344
+ const result = await knex("schedules").count("* as cnt").first();
345
+ if (Number(result?.cnt) >= limits.maxGlobal) {
346
+ throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);
347
+ }
348
+ }
349
+ if (limits.maxPerUser) {
350
+ const result = await knex("schedules").where("creator_id", creatorId).count("* as cnt").first();
351
+ if (Number(result?.cnt) >= limits.maxPerUser) {
352
+ throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);
353
+ }
354
+ }
355
+ if (isRecurring && limits.maxRecurringPerUser) {
356
+ const bTrue = this.driver === "mssql" ? 1 : true;
357
+ const result = await knex("schedules").where("creator_id", creatorId).where("is_recurring", bTrue).count("* as cnt").first();
358
+ if (Number(result?.cnt) >= limits.maxRecurringPerUser) {
359
+ throw new Error(
360
+ `You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`
361
+ );
362
+ }
363
+ }
364
+ }
365
+ // --- HA Distributed Locking (via scheduler_locks table) ---
366
+ async tryAcquireLock(lockId, nodeId, ttlSeconds) {
367
+ const knex = this.getKnex();
368
+ const now = Date.now();
369
+ const expiresAt = now + ttlSeconds * 1e3;
370
+ const token = uuidv4();
371
+ const updated = await knex("scheduler_locks").where("lock_id", lockId).where("expires_at", "<", now).update({
372
+ node_id: nodeId,
373
+ lock_token: token,
374
+ acquired_at: now,
375
+ expires_at: expiresAt
376
+ });
377
+ if (updated > 0) return token;
378
+ try {
379
+ await knex("scheduler_locks").insert({
380
+ lock_id: lockId,
381
+ node_id: nodeId,
382
+ lock_token: token,
383
+ acquired_at: now,
384
+ expires_at: expiresAt
385
+ });
386
+ return token;
387
+ } catch {
388
+ return null;
389
+ }
390
+ }
391
+ async releaseLock(lockId, lockToken) {
392
+ const knex = this.getKnex();
393
+ await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).del();
394
+ }
395
+ async renewLock(lockId, lockToken, ttlSeconds) {
396
+ const knex = this.getKnex();
397
+ const now = Date.now();
398
+ const expiresAt = now + ttlSeconds * 1e3;
399
+ const updated = await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).update({ acquired_at: now, expires_at: expiresAt });
400
+ return updated > 0;
401
+ }
402
+ async flush() {
403
+ }
404
+ };
405
+ }
406
+ });
407
+ init_knex_store();
408
+ export {
409
+ KnexStoreBackend
410
+ };
411
+ //# sourceMappingURL=knex-store-HPXJILBL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/enterprise/scheduler/knex-store.ts"],"sourcesContent":["/**\n * Copyright (c) ProbeLabs. All rights reserved.\n * Licensed under the Elastic License 2.0; you may not use this file except\n * in compliance with the Elastic License 2.0.\n */\n\n/**\n * Knex-backed schedule store for PostgreSQL, MySQL, and MSSQL (Enterprise)\n *\n * Uses Knex query builder for database-agnostic SQL. Same schema as SQLite backend\n * but with real distributed locking via row-level claims (claimed_by/claimed_at/lock_token).\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { v4 as uuidv4 } from 'uuid';\nimport { logger } from '../../logger';\nimport type { Schedule, ScheduleLimits } from '../../scheduler/schedule-store';\nimport type {\n ScheduleStoreBackend,\n ScheduleStoreStats,\n StorageConfig,\n HAConfig,\n ServerConnectionConfig,\n} from '../../scheduler/store/types';\n\n// Knex types — loaded dynamically to avoid ncc bundling\ntype Knex = import('knex').Knex;\n\n/**\n * Database row shape (snake_case)\n */\ninterface ScheduleRow {\n id: string;\n creator_id: string;\n creator_context: string | null;\n creator_name: string | null;\n timezone: string;\n schedule_expr: string;\n run_at: number | string | null;\n is_recurring: boolean | number;\n original_expression: string;\n workflow: string | null;\n workflow_inputs: string | null;\n output_context: string | null;\n status: string;\n created_at: number | string;\n last_run_at: number | string | null;\n next_run_at: number | string | null;\n run_count: number;\n failure_count: number;\n last_error: string | null;\n previous_response: string | null;\n}\n\nfunction toNum(val: number | string | null | undefined): number | undefined {\n if (val === null || val === undefined) return undefined;\n return typeof val === 'string' ? parseInt(val, 10) : val;\n}\n\nfunction safeJsonParse<T = unknown>(value: string | null): T | undefined {\n if (!value) return undefined;\n try {\n return JSON.parse(value) as T;\n } catch {\n return undefined;\n }\n}\n\nfunction fromDbRow(row: ScheduleRow): Schedule {\n return {\n id: row.id,\n creatorId: row.creator_id,\n creatorContext: row.creator_context ?? undefined,\n creatorName: row.creator_name ?? undefined,\n timezone: row.timezone,\n schedule: row.schedule_expr,\n runAt: toNum(row.run_at),\n isRecurring: row.is_recurring === true || row.is_recurring === 1,\n originalExpression: row.original_expression,\n workflow: row.workflow ?? undefined,\n workflowInputs: safeJsonParse(row.workflow_inputs),\n outputContext: safeJsonParse(row.output_context),\n status: row.status as Schedule['status'],\n createdAt: toNum(row.created_at)!,\n lastRunAt: toNum(row.last_run_at),\n nextRunAt: toNum(row.next_run_at),\n runCount: row.run_count,\n failureCount: row.failure_count,\n lastError: row.last_error ?? undefined,\n previousResponse: row.previous_response ?? undefined,\n };\n}\n\nfunction toInsertRow(schedule: Schedule): Record<string, unknown> {\n return {\n id: schedule.id,\n creator_id: schedule.creatorId,\n creator_context: schedule.creatorContext ?? null,\n creator_name: schedule.creatorName ?? null,\n timezone: schedule.timezone,\n schedule_expr: schedule.schedule,\n run_at: schedule.runAt ?? null,\n is_recurring: schedule.isRecurring,\n original_expression: schedule.originalExpression,\n workflow: schedule.workflow ?? null,\n workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,\n output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,\n status: schedule.status,\n created_at: schedule.createdAt,\n last_run_at: schedule.lastRunAt ?? null,\n next_run_at: schedule.nextRunAt ?? null,\n run_count: schedule.runCount,\n failure_count: schedule.failureCount,\n last_error: schedule.lastError ?? null,\n previous_response: schedule.previousResponse ?? null,\n };\n}\n\n/**\n * Enterprise Knex-backed store for PostgreSQL, MySQL, and MSSQL\n */\nexport class KnexStoreBackend implements ScheduleStoreBackend {\n private knex: Knex | null = null;\n private driver: 'postgresql' | 'mysql' | 'mssql';\n private connection: ServerConnectionConfig;\n\n constructor(\n driver: 'postgresql' | 'mysql' | 'mssql',\n storageConfig: StorageConfig,\n _haConfig?: HAConfig\n ) {\n this.driver = driver;\n this.connection = (storageConfig.connection || {}) as ServerConnectionConfig;\n }\n\n async initialize(): Promise<void> {\n // Load knex dynamically\n const { createRequire } = require('module') as typeof import('module');\n const runtimeRequire = createRequire(__filename);\n let knexFactory: typeof import('knex').default;\n try {\n knexFactory = runtimeRequire('knex');\n } catch (err: unknown) {\n const code = (err as { code?: string })?.code;\n if (code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND') {\n throw new Error(\n 'knex is required for PostgreSQL/MySQL/MSSQL schedule storage. ' +\n 'Install it with: npm install knex'\n );\n }\n throw err;\n }\n\n const clientMap: Record<string, string> = {\n postgresql: 'pg',\n mysql: 'mysql2',\n mssql: 'tedious',\n };\n const client = clientMap[this.driver];\n\n // Build connection config\n let connection: string | Record<string, unknown>;\n if (this.connection.connection_string) {\n connection = this.connection.connection_string;\n } else if (this.driver === 'mssql') {\n connection = this.buildMssqlConnection();\n } else {\n connection = this.buildStandardConnection();\n }\n\n this.knex = knexFactory({\n client,\n connection,\n pool: {\n min: this.connection.pool?.min ?? 0,\n max: this.connection.pool?.max ?? 10,\n },\n });\n\n // Run schema migration\n await this.migrateSchema();\n\n logger.info(`[KnexStore] Initialized (${this.driver})`);\n }\n\n private buildStandardConnection(): Record<string, unknown> {\n return {\n host: this.connection.host || 'localhost',\n port: this.connection.port,\n database: this.connection.database || 'visor',\n user: this.connection.user,\n password: this.connection.password,\n ssl: this.resolveSslConfig(),\n };\n }\n\n private buildMssqlConnection(): Record<string, unknown> {\n const ssl = this.connection.ssl;\n const sslEnabled = ssl === true || (typeof ssl === 'object' && ssl.enabled !== false);\n\n return {\n server: this.connection.host || 'localhost',\n port: this.connection.port,\n database: this.connection.database || 'visor',\n user: this.connection.user,\n password: this.connection.password,\n options: {\n encrypt: sslEnabled,\n trustServerCertificate:\n typeof ssl === 'object' ? ssl.reject_unauthorized === false : !sslEnabled,\n },\n };\n }\n\n private resolveSslConfig(): boolean | Record<string, unknown> {\n const ssl = this.connection.ssl;\n if (ssl === false || ssl === undefined) return false;\n if (ssl === true) return { rejectUnauthorized: true };\n\n // Object config\n if (ssl.enabled === false) return false;\n\n const result: Record<string, unknown> = {\n rejectUnauthorized: ssl.reject_unauthorized !== false,\n };\n\n if (ssl.ca) {\n const caPath = this.validateSslPath(ssl.ca, 'CA certificate');\n result.ca = fs.readFileSync(caPath, 'utf8');\n }\n if (ssl.cert) {\n const certPath = this.validateSslPath(ssl.cert, 'client certificate');\n result.cert = fs.readFileSync(certPath, 'utf8');\n }\n if (ssl.key) {\n const keyPath = this.validateSslPath(ssl.key, 'client key');\n result.key = fs.readFileSync(keyPath, 'utf8');\n }\n\n return result;\n }\n\n private validateSslPath(filePath: string, label: string): string {\n const resolved = path.resolve(filePath);\n if (resolved !== path.normalize(resolved)) {\n throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);\n }\n if (!fs.existsSync(resolved)) {\n throw new Error(`SSL ${label} not found: ${filePath}`);\n }\n return resolved;\n }\n\n async shutdown(): Promise<void> {\n if (this.knex) {\n await this.knex.destroy();\n this.knex = null;\n }\n }\n\n private async migrateSchema(): Promise<void> {\n const knex = this.getKnex();\n\n const exists = await knex.schema.hasTable('schedules');\n if (!exists) {\n await knex.schema.createTable('schedules', table => {\n table.string('id', 36).primary();\n table.string('creator_id', 255).notNullable().index();\n table.string('creator_context', 255);\n table.string('creator_name', 255);\n table.string('timezone', 64).notNullable().defaultTo('UTC');\n table.string('schedule_expr', 255);\n table.bigInteger('run_at');\n table.boolean('is_recurring').notNullable();\n table.text('original_expression');\n table.string('workflow', 255);\n table.text('workflow_inputs');\n table.text('output_context');\n table.string('status', 20).notNullable().index();\n table.bigInteger('created_at').notNullable();\n table.bigInteger('last_run_at');\n table.bigInteger('next_run_at');\n table.integer('run_count').notNullable().defaultTo(0);\n table.integer('failure_count').notNullable().defaultTo(0);\n table.text('last_error');\n table.text('previous_response');\n\n table.index(['status', 'next_run_at']);\n });\n }\n\n // Create scheduler_locks table for distributed locking\n const locksExist = await knex.schema.hasTable('scheduler_locks');\n if (!locksExist) {\n await knex.schema.createTable('scheduler_locks', table => {\n table.string('lock_id', 255).primary();\n table.string('node_id', 255).notNullable();\n table.string('lock_token', 36).notNullable();\n table.bigInteger('acquired_at').notNullable();\n table.bigInteger('expires_at').notNullable();\n });\n }\n }\n\n private getKnex(): Knex {\n if (!this.knex) {\n throw new Error('[KnexStore] Not initialized. Call initialize() first.');\n }\n return this.knex;\n }\n\n // --- CRUD ---\n\n async create(\n schedule: Omit<Schedule, 'id' | 'createdAt' | 'runCount' | 'failureCount' | 'status'>\n ): Promise<Schedule> {\n const knex = this.getKnex();\n\n const newSchedule: Schedule = {\n ...schedule,\n id: uuidv4(),\n createdAt: Date.now(),\n runCount: 0,\n failureCount: 0,\n status: 'active',\n };\n\n await knex('schedules').insert(toInsertRow(newSchedule));\n\n logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);\n return newSchedule;\n }\n\n async importSchedule(schedule: Schedule): Promise<void> {\n const knex = this.getKnex();\n const existing = await knex('schedules').where('id', schedule.id).first();\n if (existing) return; // Already imported (idempotent)\n await knex('schedules').insert(toInsertRow(schedule));\n }\n\n async get(id: string): Promise<Schedule | undefined> {\n const knex = this.getKnex();\n const row = await knex('schedules').where('id', id).first();\n return row ? fromDbRow(row as ScheduleRow) : undefined;\n }\n\n async update(id: string, patch: Partial<Schedule>): Promise<Schedule | undefined> {\n const knex = this.getKnex();\n\n const existing = await knex('schedules').where('id', id).first();\n if (!existing) return undefined;\n\n const current = fromDbRow(existing as ScheduleRow);\n const updated: Schedule = { ...current, ...patch, id: current.id };\n const row = toInsertRow(updated);\n // Remove id from update (PK cannot change)\n delete (row as Record<string, unknown>).id;\n\n await knex('schedules').where('id', id).update(row);\n return updated;\n }\n\n async delete(id: string): Promise<boolean> {\n const knex = this.getKnex();\n const deleted = await knex('schedules').where('id', id).del();\n if (deleted > 0) {\n logger.info(`[KnexStore] Deleted schedule ${id}`);\n return true;\n }\n return false;\n }\n\n // --- Queries ---\n\n async getByCreator(creatorId: string): Promise<Schedule[]> {\n const knex = this.getKnex();\n const rows = await knex('schedules').where('creator_id', creatorId);\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async getActiveSchedules(): Promise<Schedule[]> {\n const knex = this.getKnex();\n const rows = await knex('schedules').where('status', 'active');\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async getDueSchedules(now?: number): Promise<Schedule[]> {\n const ts = now ?? Date.now();\n const knex = this.getKnex();\n // MSSQL uses 1/0 for booleans\n const bFalse = this.driver === 'mssql' ? 0 : false;\n const bTrue = this.driver === 'mssql' ? 1 : true;\n const rows = await knex('schedules')\n .where('status', 'active')\n .andWhere(function () {\n this.where(function () {\n this.where('is_recurring', bFalse as unknown as boolean)\n .whereNotNull('run_at')\n .where('run_at', '<=', ts);\n }).orWhere(function () {\n this.where('is_recurring', bTrue as unknown as boolean)\n .whereNotNull('next_run_at')\n .where('next_run_at', '<=', ts);\n });\n });\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async findByWorkflow(creatorId: string, workflowName: string): Promise<Schedule[]> {\n const knex = this.getKnex();\n const escaped = workflowName.toLowerCase().replace(/[%_\\\\]/g, '\\\\$&');\n const pattern = `%${escaped}%`;\n const rows = await knex('schedules')\n .where('creator_id', creatorId)\n .where('status', 'active')\n .whereRaw(\"LOWER(workflow) LIKE ? ESCAPE '\\\\'\", [pattern]);\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async getAll(): Promise<Schedule[]> {\n const knex = this.getKnex();\n const rows = await knex('schedules');\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async getStats(): Promise<ScheduleStoreStats> {\n const knex = this.getKnex();\n // MSSQL uses 1/0 for booleans; PostgreSQL/MySQL accept both true/1\n const boolTrue = this.driver === 'mssql' ? '1' : 'true';\n const boolFalse = this.driver === 'mssql' ? '0' : 'false';\n const result = await knex('schedules')\n .select(\n knex.raw('COUNT(*) as total'),\n knex.raw(\"SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active\"),\n knex.raw(\"SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused\"),\n knex.raw(\"SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed\"),\n knex.raw(\"SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed\"),\n knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`),\n knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`)\n )\n .first();\n\n return {\n total: Number(result.total) || 0,\n active: Number(result.active) || 0,\n paused: Number(result.paused) || 0,\n completed: Number(result.completed) || 0,\n failed: Number(result.failed) || 0,\n recurring: Number(result.recurring) || 0,\n oneTime: Number(result.one_time) || 0,\n };\n }\n\n async validateLimits(\n creatorId: string,\n isRecurring: boolean,\n limits: ScheduleLimits\n ): Promise<void> {\n const knex = this.getKnex();\n\n if (limits.maxGlobal) {\n const result = await knex('schedules').count('* as cnt').first();\n if (Number(result?.cnt) >= limits.maxGlobal) {\n throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);\n }\n }\n\n if (limits.maxPerUser) {\n const result = await knex('schedules')\n .where('creator_id', creatorId)\n .count('* as cnt')\n .first();\n if (Number(result?.cnt) >= limits.maxPerUser) {\n throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);\n }\n }\n\n if (isRecurring && limits.maxRecurringPerUser) {\n const bTrue = this.driver === 'mssql' ? 1 : true;\n const result = await knex('schedules')\n .where('creator_id', creatorId)\n .where('is_recurring', bTrue as unknown as boolean)\n .count('* as cnt')\n .first();\n if (Number(result?.cnt) >= limits.maxRecurringPerUser) {\n throw new Error(\n `You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`\n );\n }\n }\n }\n\n // --- HA Distributed Locking (via scheduler_locks table) ---\n\n async tryAcquireLock(lockId: string, nodeId: string, ttlSeconds: number): Promise<string | null> {\n const knex = this.getKnex();\n const now = Date.now();\n const expiresAt = now + ttlSeconds * 1000;\n const token = uuidv4();\n\n // Step 1: Try to claim an existing expired lock\n const updated = await knex('scheduler_locks')\n .where('lock_id', lockId)\n .where('expires_at', '<', now)\n .update({\n node_id: nodeId,\n lock_token: token,\n acquired_at: now,\n expires_at: expiresAt,\n });\n\n if (updated > 0) return token;\n\n // Step 2: Try to INSERT a new lock row\n try {\n await knex('scheduler_locks').insert({\n lock_id: lockId,\n node_id: nodeId,\n lock_token: token,\n acquired_at: now,\n expires_at: expiresAt,\n });\n return token;\n } catch {\n // Unique constraint violation — another node holds the lock\n return null;\n }\n }\n\n async releaseLock(lockId: string, lockToken: string): Promise<void> {\n const knex = this.getKnex();\n await knex('scheduler_locks').where('lock_id', lockId).where('lock_token', lockToken).del();\n }\n\n async renewLock(lockId: string, lockToken: string, ttlSeconds: number): Promise<boolean> {\n const knex = this.getKnex();\n const now = Date.now();\n const expiresAt = now + ttlSeconds * 1000;\n\n const updated = await knex('scheduler_locks')\n .where('lock_id', lockId)\n .where('lock_token', lockToken)\n .update({ acquired_at: now, expires_at: expiresAt });\n\n return updated > 0;\n }\n\n async flush(): Promise<void> {\n // No-op for server-based backends\n }\n}\n"],"mappings":"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","names":[]}