@duraflows/kysely 5.2.1 → 6.0.0

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/README.md CHANGED
@@ -104,6 +104,8 @@ interface MyDatabase extends WorkflowDatabase {
104
104
  const db = new Kysely<MyDatabase>({ ... });
105
105
  ```
106
106
 
107
+ A database type declared as `interface AppDb extends WorkflowDatabase` picks up the `workflow_instances` columns automatically, including `timeout_attempts`, `timeout_retry_at`, `timeout_last_error` and `timeout_parked_at`. If you instead hand-write your own `workflow_instances` table type rather than extending `WorkflowDatabase`, it must declare all four: `timeout_attempts: Generated<number>`, `timeout_retry_at: Date | null`, `timeout_last_error: string | null`, `timeout_parked_at: Date | null`.
108
+
107
109
  ## Database Setup
108
110
 
109
111
  This package uses the same database schema as `@duraflows/pg`. Use the migration generator from `@duraflows/pg` or copy the reference migration:
@@ -24,6 +24,10 @@ export interface WorkflowInstancesTable {
24
24
  metadata_json: JsonObjectColumn;
25
25
  created_at: Generated<Date>;
26
26
  updated_at: Generated<Date>;
27
+ timeout_attempts: Generated<number>;
28
+ timeout_retry_at: Date | null;
29
+ timeout_last_error: string | null;
30
+ timeout_parked_at: Date | null;
27
31
  }
28
32
  export interface WorkflowHistoryTable {
29
33
  uuid: Generated<string>;
@@ -10,6 +10,10 @@ export declare class KyselyWorkflowInstanceStore implements WorkflowInstanceStor
10
10
  lockByUuid(uuid: string): Promise<WorkflowInstance | null>;
11
11
  update(instance: WorkflowInstance): Promise<void>;
12
12
  findExpired(limit: number, now: Date): Promise<WorkflowInstance[]>;
13
+ findParkedTimeouts(options: {
14
+ limit: number;
15
+ workflowName?: string;
16
+ }): Promise<WorkflowInstance[]>;
13
17
  private mapRow;
14
18
  }
15
19
  //# sourceMappingURL=kysely-instance-store.d.ts.map
@@ -3,6 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KyselyWorkflowInstanceStore = void 0;
4
4
  const core_1 = require("@duraflows/core");
5
5
  const kysely_transaction_context_js_1 = require("./kysely-transaction-context.js");
6
+ /** The four timeout-retry column values for `retry` (`null` ⇒ never failed). */
7
+ function timeoutRetryColumns(retry) {
8
+ return {
9
+ timeout_attempts: retry?.attempts ?? 0,
10
+ timeout_retry_at: retry?.retryAt ?? null,
11
+ timeout_last_error: retry?.lastError ?? null,
12
+ timeout_parked_at: retry?.parkedAt ?? null,
13
+ };
14
+ }
6
15
  class KyselyWorkflowInstanceStore {
7
16
  db;
8
17
  constructor(db) {
@@ -27,6 +36,7 @@ class KyselyWorkflowInstanceStore {
27
36
  metadata_json: JSON.stringify(instance.metadata),
28
37
  created_at: instance.createdAt,
29
38
  updated_at: instance.updatedAt,
39
+ ...timeoutRetryColumns(instance.timeoutRetry),
30
40
  })
31
41
  .execute();
32
42
  }
@@ -65,6 +75,7 @@ class KyselyWorkflowInstanceStore {
65
75
  last_transition_at: instance.lastTransitionAt,
66
76
  context_json: JSON.stringify(instance.context),
67
77
  updated_at: instance.updatedAt,
78
+ ...timeoutRetryColumns(instance.timeoutRetry),
68
79
  })
69
80
  .where("uuid", "=", instance.uuid)
70
81
  .where("version", "=", expectedVersion)
@@ -84,13 +95,30 @@ class KyselyWorkflowInstanceStore {
84
95
  .selectAll()
85
96
  .where("expires_at", "is not", null)
86
97
  .where("expires_at", "<", now)
87
- .orderBy("expires_at")
98
+ .where("timeout_parked_at", "is", null)
99
+ .where((eb) => eb.or([eb("timeout_retry_at", "is", null), eb("timeout_retry_at", "<", now)]))
100
+ // Redundant (every due row already satisfies it); it gives the planner a
101
+ // range condition so workflow_instances_timeout_due_idx is range-scanned
102
+ // instead of walked in order with every entry filtered against the heap.
103
+ .where((eb) => eb(eb.fn.coalesce("timeout_retry_at", "expires_at"), "<", now))
104
+ .orderBy((eb) => eb.fn.coalesce("timeout_retry_at", "expires_at"))
88
105
  .forUpdate()
89
106
  .skipLocked()
90
107
  .limit(limit)
91
108
  .execute();
92
109
  return rows.map((row) => this.mapRow(row));
93
110
  }
111
+ async findParkedTimeouts(options) {
112
+ let query = this.getExecutor()
113
+ .selectFrom("workflow_instances")
114
+ .selectAll()
115
+ .where("timeout_parked_at", "is not", null);
116
+ if (options.workflowName !== undefined) {
117
+ query = query.where("workflow_name", "=", options.workflowName);
118
+ }
119
+ const rows = await query.orderBy("timeout_parked_at").orderBy("uuid").limit(options.limit).execute();
120
+ return rows.map((row) => this.mapRow(row));
121
+ }
94
122
  mapRow(row) {
95
123
  return {
96
124
  uuid: row.uuid,
@@ -99,6 +127,14 @@ class KyselyWorkflowInstanceStore {
99
127
  version: row.version,
100
128
  definitionVersion: row.definition_version ?? null,
101
129
  expiresAt: row.expires_at,
130
+ timeoutRetry: row.timeout_attempts > 0
131
+ ? {
132
+ attempts: row.timeout_attempts,
133
+ lastError: row.timeout_last_error ?? "",
134
+ retryAt: row.timeout_retry_at,
135
+ parkedAt: row.timeout_parked_at,
136
+ }
137
+ : null,
102
138
  lastTransitionAt: row.last_transition_at,
103
139
  context: row.context_json,
104
140
  metadata: row.metadata_json,
@@ -24,6 +24,10 @@ export interface WorkflowInstancesTable {
24
24
  metadata_json: JsonObjectColumn;
25
25
  created_at: Generated<Date>;
26
26
  updated_at: Generated<Date>;
27
+ timeout_attempts: Generated<number>;
28
+ timeout_retry_at: Date | null;
29
+ timeout_last_error: string | null;
30
+ timeout_parked_at: Date | null;
27
31
  }
28
32
  export interface WorkflowHistoryTable {
29
33
  uuid: Generated<string>;
@@ -10,6 +10,10 @@ export declare class KyselyWorkflowInstanceStore implements WorkflowInstanceStor
10
10
  lockByUuid(uuid: string): Promise<WorkflowInstance | null>;
11
11
  update(instance: WorkflowInstance): Promise<void>;
12
12
  findExpired(limit: number, now: Date): Promise<WorkflowInstance[]>;
13
+ findParkedTimeouts(options: {
14
+ limit: number;
15
+ workflowName?: string;
16
+ }): Promise<WorkflowInstance[]>;
13
17
  private mapRow;
14
18
  }
15
19
  //# sourceMappingURL=kysely-instance-store.d.ts.map
@@ -1,5 +1,14 @@
1
1
  import { WorkflowError } from "@duraflows/core";
2
2
  import { KyselyTransactionContext } from "./kysely-transaction-context.js";
3
+ /** The four timeout-retry column values for `retry` (`null` ⇒ never failed). */
4
+ function timeoutRetryColumns(retry) {
5
+ return {
6
+ timeout_attempts: retry?.attempts ?? 0,
7
+ timeout_retry_at: retry?.retryAt ?? null,
8
+ timeout_last_error: retry?.lastError ?? null,
9
+ timeout_parked_at: retry?.parkedAt ?? null,
10
+ };
11
+ }
3
12
  export class KyselyWorkflowInstanceStore {
4
13
  db;
5
14
  constructor(db) {
@@ -24,6 +33,7 @@ export class KyselyWorkflowInstanceStore {
24
33
  metadata_json: JSON.stringify(instance.metadata),
25
34
  created_at: instance.createdAt,
26
35
  updated_at: instance.updatedAt,
36
+ ...timeoutRetryColumns(instance.timeoutRetry),
27
37
  })
28
38
  .execute();
29
39
  }
@@ -62,6 +72,7 @@ export class KyselyWorkflowInstanceStore {
62
72
  last_transition_at: instance.lastTransitionAt,
63
73
  context_json: JSON.stringify(instance.context),
64
74
  updated_at: instance.updatedAt,
75
+ ...timeoutRetryColumns(instance.timeoutRetry),
65
76
  })
66
77
  .where("uuid", "=", instance.uuid)
67
78
  .where("version", "=", expectedVersion)
@@ -81,13 +92,30 @@ export class KyselyWorkflowInstanceStore {
81
92
  .selectAll()
82
93
  .where("expires_at", "is not", null)
83
94
  .where("expires_at", "<", now)
84
- .orderBy("expires_at")
95
+ .where("timeout_parked_at", "is", null)
96
+ .where((eb) => eb.or([eb("timeout_retry_at", "is", null), eb("timeout_retry_at", "<", now)]))
97
+ // Redundant (every due row already satisfies it); it gives the planner a
98
+ // range condition so workflow_instances_timeout_due_idx is range-scanned
99
+ // instead of walked in order with every entry filtered against the heap.
100
+ .where((eb) => eb(eb.fn.coalesce("timeout_retry_at", "expires_at"), "<", now))
101
+ .orderBy((eb) => eb.fn.coalesce("timeout_retry_at", "expires_at"))
85
102
  .forUpdate()
86
103
  .skipLocked()
87
104
  .limit(limit)
88
105
  .execute();
89
106
  return rows.map((row) => this.mapRow(row));
90
107
  }
108
+ async findParkedTimeouts(options) {
109
+ let query = this.getExecutor()
110
+ .selectFrom("workflow_instances")
111
+ .selectAll()
112
+ .where("timeout_parked_at", "is not", null);
113
+ if (options.workflowName !== undefined) {
114
+ query = query.where("workflow_name", "=", options.workflowName);
115
+ }
116
+ const rows = await query.orderBy("timeout_parked_at").orderBy("uuid").limit(options.limit).execute();
117
+ return rows.map((row) => this.mapRow(row));
118
+ }
91
119
  mapRow(row) {
92
120
  return {
93
121
  uuid: row.uuid,
@@ -96,6 +124,14 @@ export class KyselyWorkflowInstanceStore {
96
124
  version: row.version,
97
125
  definitionVersion: row.definition_version ?? null,
98
126
  expiresAt: row.expires_at,
127
+ timeoutRetry: row.timeout_attempts > 0
128
+ ? {
129
+ attempts: row.timeout_attempts,
130
+ lastError: row.timeout_last_error ?? "",
131
+ retryAt: row.timeout_retry_at,
132
+ parkedAt: row.timeout_parked_at,
133
+ }
134
+ : null,
99
135
  lastTransitionAt: row.last_transition_at,
100
136
  context: row.context_json,
101
137
  metadata: row.metadata_json,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duraflows/kysely",
3
- "version": "5.2.1",
3
+ "version": "6.0.0",
4
4
  "description": "PostgreSQL persistence adapter for duraflows using `kysely`. Integrates with kysely's type-safe query builder and AsyncLocalStorage-based transaction context.",
5
5
  "keywords": [
6
6
  "duraflows",
@@ -51,15 +51,15 @@
51
51
  "access": "public"
52
52
  },
53
53
  "peerDependencies": {
54
- "@duraflows/core": "^5.2.1",
54
+ "@duraflows/core": "^6.0.0",
55
55
  "kysely": "^0.29.6"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/pg": "^8.23.1",
59
59
  "kysely": "^0.29.6",
60
60
  "pg": "^8.23.0",
61
- "@duraflows/core": "5.2.1",
62
- "@duraflows/pg": "5.2.1"
61
+ "@duraflows/core": "6.0.0",
62
+ "@duraflows/pg": "6.0.0"
63
63
  },
64
64
  "scripts": {
65
65
  "build": "tsc --build"