@pgflow/core 0.4.0 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # @pgflow/core
2
2
 
3
+ ## 0.4.3
4
+
5
+ ### Patch Changes
6
+
7
+ - fa78968: Fix Supabase Security Advisor warnings by setting empty search_path on functions
8
+ - @pgflow/dsl@0.4.3
9
+
10
+ ## 0.4.2
11
+
12
+ ### Patch Changes
13
+
14
+ - 220c867: Fix step:failed events not being broadcast when steps fail
15
+
16
+ Fixed a bug where step:failed events were not being broadcast to real-time subscribers when a step failed permanently. The issue was caused by PostgreSQL optimizing away the CTE that contained the realtime.send() call. The fix replaces the CTE approach with a direct PERFORM statement in the function body, ensuring the event is always sent when a step fails.
17
+
18
+ - @pgflow/dsl@0.4.2
19
+
20
+ ## 0.4.1
21
+
22
+ ### Patch Changes
23
+
24
+ - 50ab557: feat: add multi-target build support for @pgflow/client package
25
+
26
+ The @pgflow/client package now builds for multiple environments, making it usable in Node.js, browsers, and bundlers.
27
+
28
+ **What's new:**
29
+
30
+ - ES modules (`.js`) and CommonJS (`.cjs`) builds for Node.js
31
+ - Browser bundle (`.browser.js`) with all dependencies included
32
+ - Full TypeScript declarations
33
+ - CDN support via unpkg
34
+ - Production builds with minification
35
+ - Proper tree-shaking support
36
+ - `@supabase/supabase-js` is now a regular dependency (not peer dependency)
37
+
38
+ **You can now use it in:**
39
+
40
+ - Node.js: `import { PgflowClient } from '@pgflow/client'`
41
+ - CommonJS: `const { PgflowClient } = require('@pgflow/client')`
42
+ - Browser: `<script src="https://unpkg.com/@pgflow/client"></script>` - then use `window.pgflow.createClient(supabase)`
43
+ - Bundlers: Automatically picks the right format
44
+
45
+ **Other changes:**
46
+
47
+ - Pin Supabase CLI to exact version 2.21.1 to ensure consistent type generation between local and CI environments
48
+ - @pgflow/dsl@0.4.1
49
+
3
50
  ## 0.4.0
4
51
 
5
52
  ### Minor Changes
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgflow/core",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "license": "AGPL-3.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.14.1",
23
- "supabase": "^2.21.1"
23
+ "supabase": "2.21.1"
24
24
  },
25
25
  "dependencies": {
26
26
  "@pgflow/dsl": "workspace:*",
@@ -0,0 +1,185 @@
1
+ -- Modify "fail_task" function
2
+ CREATE OR REPLACE FUNCTION "pgflow"."fail_task" ("run_id" uuid, "step_slug" text, "task_index" integer, "error_message" text) RETURNS SETOF "pgflow"."step_tasks" LANGUAGE plpgsql SET "search_path" = '' AS $$
3
+ DECLARE
4
+ v_run_failed boolean;
5
+ v_step_failed boolean;
6
+ begin
7
+
8
+ WITH run_lock AS (
9
+ SELECT * FROM pgflow.runs
10
+ WHERE pgflow.runs.run_id = fail_task.run_id
11
+ FOR UPDATE
12
+ ),
13
+ step_lock AS (
14
+ SELECT * FROM pgflow.step_states
15
+ WHERE pgflow.step_states.run_id = fail_task.run_id
16
+ AND pgflow.step_states.step_slug = fail_task.step_slug
17
+ FOR UPDATE
18
+ ),
19
+ flow_info AS (
20
+ SELECT r.flow_slug
21
+ FROM pgflow.runs r
22
+ WHERE r.run_id = fail_task.run_id
23
+ ),
24
+ config AS (
25
+ SELECT
26
+ COALESCE(s.opt_max_attempts, f.opt_max_attempts) AS opt_max_attempts,
27
+ COALESCE(s.opt_base_delay, f.opt_base_delay) AS opt_base_delay
28
+ FROM pgflow.steps s
29
+ JOIN pgflow.flows f ON f.flow_slug = s.flow_slug
30
+ JOIN flow_info fi ON fi.flow_slug = s.flow_slug
31
+ WHERE s.flow_slug = fi.flow_slug AND s.step_slug = fail_task.step_slug
32
+ ),
33
+ fail_or_retry_task as (
34
+ UPDATE pgflow.step_tasks as task
35
+ SET
36
+ status = CASE
37
+ WHEN task.attempts_count < (SELECT opt_max_attempts FROM config) THEN 'queued'
38
+ ELSE 'failed'
39
+ END,
40
+ failed_at = CASE
41
+ WHEN task.attempts_count >= (SELECT opt_max_attempts FROM config) THEN now()
42
+ ELSE NULL
43
+ END,
44
+ started_at = CASE
45
+ WHEN task.attempts_count < (SELECT opt_max_attempts FROM config) THEN NULL
46
+ ELSE task.started_at
47
+ END,
48
+ error_message = fail_task.error_message
49
+ WHERE task.run_id = fail_task.run_id
50
+ AND task.step_slug = fail_task.step_slug
51
+ AND task.task_index = fail_task.task_index
52
+ AND task.status = 'started'
53
+ RETURNING *
54
+ ),
55
+ maybe_fail_step AS (
56
+ UPDATE pgflow.step_states
57
+ SET
58
+ status = CASE
59
+ WHEN (select fail_or_retry_task.status from fail_or_retry_task) = 'failed' THEN 'failed'
60
+ ELSE pgflow.step_states.status
61
+ END,
62
+ failed_at = CASE
63
+ WHEN (select fail_or_retry_task.status from fail_or_retry_task) = 'failed' THEN now()
64
+ ELSE NULL
65
+ END,
66
+ error_message = CASE
67
+ WHEN (select fail_or_retry_task.status from fail_or_retry_task) = 'failed' THEN fail_task.error_message
68
+ ELSE NULL
69
+ END
70
+ FROM fail_or_retry_task
71
+ WHERE pgflow.step_states.run_id = fail_task.run_id
72
+ AND pgflow.step_states.step_slug = fail_task.step_slug
73
+ RETURNING pgflow.step_states.*
74
+ )
75
+ -- Update run status
76
+ UPDATE pgflow.runs
77
+ SET status = CASE
78
+ WHEN (select status from maybe_fail_step) = 'failed' THEN 'failed'
79
+ ELSE status
80
+ END,
81
+ failed_at = CASE
82
+ WHEN (select status from maybe_fail_step) = 'failed' THEN now()
83
+ ELSE NULL
84
+ END
85
+ WHERE pgflow.runs.run_id = fail_task.run_id
86
+ RETURNING (status = 'failed') INTO v_run_failed;
87
+
88
+ -- Check if step failed by querying the step_states table
89
+ SELECT (status = 'failed') INTO v_step_failed
90
+ FROM pgflow.step_states
91
+ WHERE pgflow.step_states.run_id = fail_task.run_id
92
+ AND pgflow.step_states.step_slug = fail_task.step_slug;
93
+
94
+ -- Send broadcast event for step failure if the step was failed
95
+ IF v_step_failed THEN
96
+ PERFORM realtime.send(
97
+ jsonb_build_object(
98
+ 'event_type', 'step:failed',
99
+ 'run_id', fail_task.run_id,
100
+ 'step_slug', fail_task.step_slug,
101
+ 'status', 'failed',
102
+ 'error_message', fail_task.error_message,
103
+ 'failed_at', now()
104
+ ),
105
+ concat('step:', fail_task.step_slug, ':failed'),
106
+ concat('pgflow:run:', fail_task.run_id),
107
+ false
108
+ );
109
+ END IF;
110
+
111
+ -- Send broadcast event for run failure if the run was failed
112
+ IF v_run_failed THEN
113
+ DECLARE
114
+ v_flow_slug text;
115
+ BEGIN
116
+ SELECT flow_slug INTO v_flow_slug FROM pgflow.runs WHERE pgflow.runs.run_id = fail_task.run_id;
117
+
118
+ PERFORM realtime.send(
119
+ jsonb_build_object(
120
+ 'event_type', 'run:failed',
121
+ 'run_id', fail_task.run_id,
122
+ 'flow_slug', v_flow_slug,
123
+ 'status', 'failed',
124
+ 'error_message', fail_task.error_message,
125
+ 'failed_at', now()
126
+ ),
127
+ 'run:failed',
128
+ concat('pgflow:run:', fail_task.run_id),
129
+ false
130
+ );
131
+ END;
132
+ END IF;
133
+
134
+ -- For queued tasks: delay the message for retry with exponential backoff
135
+ PERFORM (
136
+ WITH retry_config AS (
137
+ SELECT
138
+ COALESCE(s.opt_base_delay, f.opt_base_delay) AS base_delay
139
+ FROM pgflow.steps s
140
+ JOIN pgflow.flows f ON f.flow_slug = s.flow_slug
141
+ JOIN pgflow.runs r ON r.flow_slug = f.flow_slug
142
+ WHERE r.run_id = fail_task.run_id
143
+ AND s.step_slug = fail_task.step_slug
144
+ ),
145
+ queued_tasks AS (
146
+ SELECT
147
+ r.flow_slug,
148
+ st.message_id,
149
+ pgflow.calculate_retry_delay((SELECT base_delay FROM retry_config), st.attempts_count) AS calculated_delay
150
+ FROM pgflow.step_tasks st
151
+ JOIN pgflow.runs r ON st.run_id = r.run_id
152
+ WHERE st.run_id = fail_task.run_id
153
+ AND st.step_slug = fail_task.step_slug
154
+ AND st.task_index = fail_task.task_index
155
+ AND st.status = 'queued'
156
+ )
157
+ SELECT pgmq.set_vt(qt.flow_slug, qt.message_id, qt.calculated_delay)
158
+ FROM queued_tasks qt
159
+ WHERE EXISTS (SELECT 1 FROM queued_tasks)
160
+ );
161
+
162
+ -- For failed tasks: archive the message
163
+ PERFORM (
164
+ WITH failed_tasks AS (
165
+ SELECT r.flow_slug, st.message_id
166
+ FROM pgflow.step_tasks st
167
+ JOIN pgflow.runs r ON st.run_id = r.run_id
168
+ WHERE st.run_id = fail_task.run_id
169
+ AND st.step_slug = fail_task.step_slug
170
+ AND st.task_index = fail_task.task_index
171
+ AND st.status = 'failed'
172
+ )
173
+ SELECT pgmq.archive(ft.flow_slug, ft.message_id)
174
+ FROM failed_tasks ft
175
+ WHERE EXISTS (SELECT 1 FROM failed_tasks)
176
+ );
177
+
178
+ return query select *
179
+ from pgflow.step_tasks st
180
+ where st.run_id = fail_task.run_id
181
+ and st.step_slug = fail_task.step_slug
182
+ and st.task_index = fail_task.task_index;
183
+
184
+ end;
185
+ $$;
@@ -0,0 +1,6 @@
1
+ -- Add "calculate_retry_delay" function configuration parameter
2
+ ALTER FUNCTION "pgflow"."calculate_retry_delay" SET "search_path" = '';
3
+ -- Add "is_valid_slug" function configuration parameter
4
+ ALTER FUNCTION "pgflow"."is_valid_slug" SET "search_path" = '';
5
+ -- Add "read_with_poll" function configuration parameter
6
+ ALTER FUNCTION "pgflow"."read_with_poll" SET "search_path" = '';
@@ -1 +1 @@
1
- {"version":"5.6.3"}
1
+ {"version":"5.8.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgflow/core",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "license": "AGPL-3.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,11 +20,11 @@
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.14.1",
23
- "supabase": "^2.21.1"
23
+ "supabase": "2.21.1"
24
24
  },
25
25
  "dependencies": {
26
26
  "postgres": "^3.4.5",
27
- "@pgflow/dsl": "0.4.0"
27
+ "@pgflow/dsl": "0.4.3"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public"