@pgflow/client 0.0.0-update-supabase-ee31ce05-20251119075709 → 0.0.0-worker-compilation-b0fe6186-20251201124606
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/CHANGELOG.md +31 -7
- package/README.md +1 -1
- package/dist/package.json +2 -3
- package/dist/src/browser.d.ts +7 -0
- package/dist/src/browser.d.ts.map +1 -0
- package/dist/src/browser.js +10 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +7 -0
- package/dist/src/lib/FlowRun.d.ts +125 -0
- package/dist/src/lib/FlowRun.d.ts.map +1 -0
- package/dist/src/lib/FlowRun.js +368 -0
- package/dist/src/lib/FlowStep.d.ts +90 -0
- package/dist/src/lib/FlowStep.d.ts.map +1 -0
- package/dist/src/lib/FlowStep.js +244 -0
- package/dist/src/lib/PgflowClient.d.ts +75 -0
- package/dist/src/lib/PgflowClient.d.ts.map +1 -0
- package/dist/src/lib/PgflowClient.js +227 -0
- package/dist/src/lib/SupabaseBroadcastAdapter.d.ts +65 -0
- package/dist/src/lib/SupabaseBroadcastAdapter.d.ts.map +1 -0
- package/dist/src/lib/SupabaseBroadcastAdapter.js +332 -0
- package/dist/src/lib/eventAdapters.d.ts +20 -0
- package/dist/src/lib/eventAdapters.d.ts.map +1 -0
- package/dist/src/lib/eventAdapters.js +142 -0
- package/dist/src/lib/types.d.ts +307 -0
- package/dist/src/lib/types.d.ts.map +1 -0
- package/dist/src/lib/types.js +91 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
# @pgflow/client
|
|
2
2
|
|
|
3
|
-
## 0.0.0-
|
|
3
|
+
## 0.0.0-worker-compilation-b0fe6186-20251201124606
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @pgflow/core@0.0.0-worker-compilation-b0fe6186-20251201124606
|
|
9
|
+
- @pgflow/dsl@0.0.0-worker-compilation-b0fe6186-20251201124606
|
|
10
|
+
|
|
11
|
+
## 0.9.0
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- @pgflow/core@0.9.0
|
|
16
|
+
- @pgflow/dsl@0.9.0
|
|
17
|
+
|
|
18
|
+
## 0.8.1
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- f1d3c32: Fix incorrect Supabase CLI version requirement from 2.34.3 to 2.50.3. CLI 2.50.3 is the first version to include pgmq 1.5.0+, which is required for pgflow 0.8.0+.
|
|
23
|
+
- Updated dependencies [f1d3c32]
|
|
24
|
+
- @pgflow/core@0.8.1
|
|
25
|
+
- @pgflow/dsl@0.8.1
|
|
26
|
+
|
|
27
|
+
## 0.8.0
|
|
4
28
|
|
|
5
29
|
### Minor Changes
|
|
6
30
|
|
|
7
|
-
-
|
|
31
|
+
- 7380237: BREAKING CHANGE: pgflow 0.8.0 requires pgmq 1.5.0+, PostgreSQL 17, and Supabase CLI 2.50.3+
|
|
8
32
|
|
|
9
33
|
This version modernizes infrastructure dependencies and will NOT work with pgmq 1.4.x or earlier. The migration includes a compatibility check that aborts with a clear error message if requirements are not met.
|
|
10
34
|
|
|
@@ -12,9 +36,9 @@
|
|
|
12
36
|
|
|
13
37
|
- pgmq 1.5.0 or higher (previously supported 1.4.x)
|
|
14
38
|
- PostgreSQL 17 (from 15)
|
|
15
|
-
- Supabase CLI 2.
|
|
39
|
+
- Supabase CLI 2.50.3 or higher (includes pgmq 1.5.0+)
|
|
16
40
|
|
|
17
|
-
**For Supabase users:** Upgrade your Supabase CLI to 2.
|
|
41
|
+
**For Supabase users:** Upgrade your Supabase CLI to 2.50.3+ which includes pgmq 1.5.0 by default.
|
|
18
42
|
|
|
19
43
|
**For self-hosted users:** Upgrade pgmq to 1.5.0+ and PostgreSQL to 17 before upgrading pgflow.
|
|
20
44
|
|
|
@@ -22,9 +46,9 @@
|
|
|
22
46
|
|
|
23
47
|
### Patch Changes
|
|
24
48
|
|
|
25
|
-
- Updated dependencies [
|
|
26
|
-
- @pgflow/core@0.
|
|
27
|
-
- @pgflow/dsl@0.
|
|
49
|
+
- Updated dependencies [7380237]
|
|
50
|
+
- @pgflow/core@0.8.0
|
|
51
|
+
- @pgflow/dsl@0.8.0
|
|
28
52
|
|
|
29
53
|
## 0.7.3
|
|
30
54
|
|
package/README.md
CHANGED
|
@@ -230,7 +230,7 @@ When using with `@pgflow/dsl`, you get full type safety:
|
|
|
230
230
|
import { Flow } from '@pgflow/dsl';
|
|
231
231
|
|
|
232
232
|
// Define your flow
|
|
233
|
-
const AnalyzeWebsite = new Flow<{ url: string }>({ slug: '
|
|
233
|
+
const AnalyzeWebsite = new Flow<{ url: string }>({ slug: 'analyzeWebsite' })
|
|
234
234
|
.step({ slug: 'scrape' }, async (input) => ({ content: 'html...' }))
|
|
235
235
|
.step({ slug: 'analyze' }, async (input) => ({ sentiment: 0.8 }));
|
|
236
236
|
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgflow/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -44,9 +44,8 @@
|
|
|
44
44
|
"@pgflow/dsl": "workspace:*",
|
|
45
45
|
"@types/uuid": "^10.0.0",
|
|
46
46
|
"postgres": "^3.4.5",
|
|
47
|
-
"supabase": "^2.34.3",
|
|
48
47
|
"terser": "^5.43.0",
|
|
49
48
|
"vite-plugin-dts": "~3.8.1",
|
|
50
49
|
"vitest": "1.3.1"
|
|
51
50
|
}
|
|
52
|
-
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { PgflowClient } from './lib/PgflowClient.js';
|
|
2
|
+
export { FlowRunStatus, FlowStepStatus } from './lib/types.js';
|
|
3
|
+
export * from './lib/types.js';
|
|
4
|
+
import { PgflowClient } from './lib/PgflowClient.js';
|
|
5
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
6
|
+
export declare function createClient(supabaseClient: SupabaseClient): PgflowClient<import("pkgs/dsl/dist/dsl.js").AnyFlow>;
|
|
7
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/browser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC/D,cAAc,gBAAgB,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,wBAAgB,YAAY,CAAC,cAAc,EAAE,cAAc,wDAE1D"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Browser-specific entry point
|
|
2
|
+
export { PgflowClient } from './lib/PgflowClient.js';
|
|
3
|
+
export { FlowRunStatus, FlowStepStatus } from './lib/types.js';
|
|
4
|
+
export * from './lib/types.js';
|
|
5
|
+
// Import the PgflowClient constructor for the factory
|
|
6
|
+
import { PgflowClient } from './lib/PgflowClient.js';
|
|
7
|
+
// Factory function for creating PgflowClient instances
|
|
8
|
+
export function createClient(supabaseClient) {
|
|
9
|
+
return new PgflowClient(supabaseClient);
|
|
10
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './lib/types.js';
|
|
2
|
+
export { PgflowClient } from './lib/PgflowClient.js';
|
|
3
|
+
export { FlowRun } from './lib/FlowRun.js';
|
|
4
|
+
export { FlowStep } from './lib/FlowStep.js';
|
|
5
|
+
export type { Unsubscribe } from './lib/types.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG7C,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Export all types from types.ts
|
|
2
|
+
export * from './lib/types.js';
|
|
3
|
+
// Export main client class
|
|
4
|
+
export { PgflowClient } from './lib/PgflowClient.js';
|
|
5
|
+
// Export FlowRun and FlowStep classes (not just types)
|
|
6
|
+
export { FlowRun } from './lib/FlowRun.js';
|
|
7
|
+
export { FlowStep } from './lib/FlowStep.js';
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { AnyFlow, ExtractFlowInput, ExtractFlowOutput, ExtractFlowSteps } from '@pgflow/dsl';
|
|
2
|
+
import { FlowRunStatus } from './types.js';
|
|
3
|
+
import type { FlowRunState, FlowRunEvents, Unsubscribe, FlowRunBase, FlowRunEvent, StepEvent } from './types.js';
|
|
4
|
+
import { FlowStep } from './FlowStep.js';
|
|
5
|
+
/**
|
|
6
|
+
* Represents a single execution of a flow
|
|
7
|
+
*/
|
|
8
|
+
export declare class FlowRun<TFlow extends AnyFlow> implements FlowRunBase<FlowRunEvent<TFlow>> {
|
|
9
|
+
#private;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new FlowRun instance
|
|
12
|
+
*
|
|
13
|
+
* @param initialState - Initial state for the run
|
|
14
|
+
*/
|
|
15
|
+
constructor(initialState: FlowRunState<TFlow>);
|
|
16
|
+
/**
|
|
17
|
+
* Get the run ID
|
|
18
|
+
*/
|
|
19
|
+
get run_id(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get the flow slug
|
|
22
|
+
*/
|
|
23
|
+
get flow_slug(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Get the current status
|
|
26
|
+
*/
|
|
27
|
+
get status(): FlowRunStatus;
|
|
28
|
+
/**
|
|
29
|
+
* Get the started_at timestamp
|
|
30
|
+
*/
|
|
31
|
+
get started_at(): Date | null;
|
|
32
|
+
/**
|
|
33
|
+
* Get the completed_at timestamp
|
|
34
|
+
*/
|
|
35
|
+
get completed_at(): Date | null;
|
|
36
|
+
/**
|
|
37
|
+
* Get the failed_at timestamp
|
|
38
|
+
*/
|
|
39
|
+
get failed_at(): Date | null;
|
|
40
|
+
/**
|
|
41
|
+
* Get the flow input
|
|
42
|
+
*/
|
|
43
|
+
get input(): ExtractFlowInput<TFlow>;
|
|
44
|
+
/**
|
|
45
|
+
* Get the flow output
|
|
46
|
+
*/
|
|
47
|
+
get output(): ExtractFlowOutput<TFlow> | null;
|
|
48
|
+
/**
|
|
49
|
+
* Get the error object
|
|
50
|
+
*/
|
|
51
|
+
get error(): Error | null;
|
|
52
|
+
/**
|
|
53
|
+
* Get the error message
|
|
54
|
+
*/
|
|
55
|
+
get error_message(): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Get the number of remaining steps
|
|
58
|
+
*/
|
|
59
|
+
get remaining_steps(): number;
|
|
60
|
+
/**
|
|
61
|
+
* Register an event handler for a run event
|
|
62
|
+
*
|
|
63
|
+
* @param event - Event type to listen for
|
|
64
|
+
* @param callback - Callback function to execute when event is emitted
|
|
65
|
+
* @returns Function to unsubscribe from the event
|
|
66
|
+
*/
|
|
67
|
+
on<E extends keyof FlowRunEvents<TFlow>>(event: E, callback: FlowRunEvents<TFlow>[E]): Unsubscribe;
|
|
68
|
+
/**
|
|
69
|
+
* Get a FlowStep instance for a specific step
|
|
70
|
+
*
|
|
71
|
+
* @param stepSlug - Step slug to get
|
|
72
|
+
* @returns FlowStep instance for the specified step
|
|
73
|
+
*/
|
|
74
|
+
step<TStepSlug extends keyof ExtractFlowSteps<TFlow> & string>(stepSlug: TStepSlug): FlowStep<TFlow, TStepSlug>;
|
|
75
|
+
/**
|
|
76
|
+
* Check if this run has a specific step
|
|
77
|
+
*
|
|
78
|
+
* @param stepSlug - Step slug to check
|
|
79
|
+
* @returns true if the step exists, false otherwise
|
|
80
|
+
*/
|
|
81
|
+
hasStep(stepSlug: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Wait for the run to reach a specific status
|
|
84
|
+
*
|
|
85
|
+
* @param targetStatus - The status to wait for
|
|
86
|
+
* @param options - Optional timeout and abort signal
|
|
87
|
+
* @returns Promise that resolves with the run instance when the status is reached
|
|
88
|
+
*/
|
|
89
|
+
waitForStatus(targetStatus: FlowRunStatus.Completed | FlowRunStatus.Failed, options?: {
|
|
90
|
+
timeoutMs?: number;
|
|
91
|
+
signal?: AbortSignal;
|
|
92
|
+
}): Promise<this>;
|
|
93
|
+
/**
|
|
94
|
+
* Apply state from database snapshot (no events emitted)
|
|
95
|
+
* Used when initializing state from start_flow_with_states() or get_run_with_states()
|
|
96
|
+
*
|
|
97
|
+
* @internal This method is only intended for use by PgflowClient.
|
|
98
|
+
* Applications should not call this directly.
|
|
99
|
+
*/
|
|
100
|
+
applySnapshot(row: import('@pgflow/core').RunRow): void;
|
|
101
|
+
/**
|
|
102
|
+
* Updates the run state based on an event
|
|
103
|
+
*
|
|
104
|
+
* @internal This method is only intended for use by PgflowClient and tests.
|
|
105
|
+
* Applications should not call this directly - state updates should come from
|
|
106
|
+
* database events through the PgflowClient.
|
|
107
|
+
*
|
|
108
|
+
* TODO: After v1.0, make this method private and refactor tests to use PgflowClient
|
|
109
|
+
* with event emission instead of direct state manipulation.
|
|
110
|
+
*/
|
|
111
|
+
updateState(event: FlowRunEvent<TFlow>): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Updates a step state based on an event
|
|
114
|
+
*
|
|
115
|
+
* @param stepSlug - Step slug to update
|
|
116
|
+
* @param event - Event data to update the step with
|
|
117
|
+
* @returns true if the state was updated, false otherwise
|
|
118
|
+
*/
|
|
119
|
+
updateStepState<TStepSlug extends keyof ExtractFlowSteps<TFlow> & string>(stepSlug: TStepSlug, event: StepEvent<TFlow, TStepSlug>): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Clean up all resources held by this run
|
|
122
|
+
*/
|
|
123
|
+
dispose(): void;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=FlowRun.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlowRun.d.ts","sourceRoot":"","sources":["../../../src/lib/FlowRun.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAkB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,WAAW,EACX,WAAW,EAEX,YAAY,EACZ,SAAS,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;GAEG;AACH,qBAAa,OAAO,CAAC,KAAK,SAAS,OAAO,CACxC,YAAW,WAAW,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;;IAY3C;;;;OAIG;gBACS,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC;IAI7C;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,aAAa,CAE1B;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,IAAI,GAAG,IAAI,CAE5B;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,IAAI,GAAG,IAAI,CAE9B;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,IAAI,GAAG,IAAI,CAE3B;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAEnC;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,iBAAiB,CAAC,KAAK,CAAC,GAAG,IAAI,CAE5C;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,KAAK,GAAG,IAAI,CAExB;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,GAAG,IAAI,CAEjC;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED;;;;;;OAMG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,aAAa,CAAC,KAAK,CAAC,EACrC,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAChC,WAAW;IAad;;;;;OAKG;IACH,IAAI,CAAC,SAAS,SAAS,MAAM,gBAAgB,CAAC,KAAK,CAAC,GAAG,MAAM,EAC3D,QAAQ,EAAE,SAAS,GAClB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IA8B7B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKlC;;;;;;OAMG;IACH,aAAa,CACX,YAAY,EAAE,aAAa,CAAC,SAAS,GAAG,aAAa,CAAC,MAAM,EAC5D,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACrD,OAAO,CAAC,IAAI,CAAC;IA+DhB;;;;;;OAMG;IACH,aAAa,CAAC,GAAG,EAAE,OAAO,cAAc,EAAE,MAAM,GAAG,IAAI;IAavD;;;;;;;;;OASG;IACH,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,GAAG,OAAO;IAmFhD;;;;;;OAMG;IACH,eAAe,CAAC,SAAS,SAAS,MAAM,gBAAgB,CAAC,KAAK,CAAC,GAAG,MAAM,EACtE,QAAQ,EAAE,SAAS,EACnB,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,GACjC,OAAO;IAyDV;;OAEG;IACH,OAAO,IAAI,IAAI;CAgBhB"}
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { createNanoEvents } from 'nanoevents';
|
|
2
|
+
import { FlowRunStatus, FlowStepStatus } from './types.js';
|
|
3
|
+
import { FlowStep } from './FlowStep.js';
|
|
4
|
+
/**
|
|
5
|
+
* Represents a single execution of a flow
|
|
6
|
+
*/
|
|
7
|
+
export class FlowRun {
|
|
8
|
+
#state;
|
|
9
|
+
#events = createNanoEvents();
|
|
10
|
+
#steps = new Map();
|
|
11
|
+
#statusPrecedence = {
|
|
12
|
+
[FlowRunStatus.Started]: 0,
|
|
13
|
+
[FlowRunStatus.Completed]: 1,
|
|
14
|
+
[FlowRunStatus.Failed]: 2,
|
|
15
|
+
};
|
|
16
|
+
#disposed = false;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new FlowRun instance
|
|
19
|
+
*
|
|
20
|
+
* @param initialState - Initial state for the run
|
|
21
|
+
*/
|
|
22
|
+
constructor(initialState) {
|
|
23
|
+
this.#state = initialState;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the run ID
|
|
27
|
+
*/
|
|
28
|
+
get run_id() {
|
|
29
|
+
return this.#state.run_id;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the flow slug
|
|
33
|
+
*/
|
|
34
|
+
get flow_slug() {
|
|
35
|
+
return this.#state.flow_slug;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get the current status
|
|
39
|
+
*/
|
|
40
|
+
get status() {
|
|
41
|
+
return this.#state.status;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the started_at timestamp
|
|
45
|
+
*/
|
|
46
|
+
get started_at() {
|
|
47
|
+
return this.#state.started_at;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the completed_at timestamp
|
|
51
|
+
*/
|
|
52
|
+
get completed_at() {
|
|
53
|
+
return this.#state.completed_at;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the failed_at timestamp
|
|
57
|
+
*/
|
|
58
|
+
get failed_at() {
|
|
59
|
+
return this.#state.failed_at;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get the flow input
|
|
63
|
+
*/
|
|
64
|
+
get input() {
|
|
65
|
+
return this.#state.input;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the flow output
|
|
69
|
+
*/
|
|
70
|
+
get output() {
|
|
71
|
+
return this.#state.output;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the error object
|
|
75
|
+
*/
|
|
76
|
+
get error() {
|
|
77
|
+
return this.#state.error;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the error message
|
|
81
|
+
*/
|
|
82
|
+
get error_message() {
|
|
83
|
+
return this.#state.error_message;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the number of remaining steps
|
|
87
|
+
*/
|
|
88
|
+
get remaining_steps() {
|
|
89
|
+
return this.#state.remaining_steps;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Register an event handler for a run event
|
|
93
|
+
*
|
|
94
|
+
* @param event - Event type to listen for
|
|
95
|
+
* @param callback - Callback function to execute when event is emitted
|
|
96
|
+
* @returns Function to unsubscribe from the event
|
|
97
|
+
*/
|
|
98
|
+
on(event, callback) {
|
|
99
|
+
this.#listenerCount++;
|
|
100
|
+
// Wrap the unsubscribe function to track listener count
|
|
101
|
+
const unsubscribe = this.#events.on(event, callback);
|
|
102
|
+
return () => {
|
|
103
|
+
unsubscribe();
|
|
104
|
+
this.#listenerCount--;
|
|
105
|
+
this.#checkAutoDispose();
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get a FlowStep instance for a specific step
|
|
110
|
+
*
|
|
111
|
+
* @param stepSlug - Step slug to get
|
|
112
|
+
* @returns FlowStep instance for the specified step
|
|
113
|
+
*/
|
|
114
|
+
step(stepSlug) {
|
|
115
|
+
// Look up if we already have this step cached
|
|
116
|
+
const existingStep = this.#steps.get(stepSlug);
|
|
117
|
+
if (existingStep) {
|
|
118
|
+
// Safe to cast since we only store steps with matching slugs
|
|
119
|
+
return existingStep;
|
|
120
|
+
}
|
|
121
|
+
// Create a new step instance with default state
|
|
122
|
+
const step = new FlowStep({
|
|
123
|
+
run_id: this.run_id,
|
|
124
|
+
step_slug: stepSlug,
|
|
125
|
+
status: FlowStepStatus.Created,
|
|
126
|
+
output: null,
|
|
127
|
+
error: null,
|
|
128
|
+
error_message: null,
|
|
129
|
+
started_at: null,
|
|
130
|
+
completed_at: null,
|
|
131
|
+
failed_at: null,
|
|
132
|
+
});
|
|
133
|
+
// Cache the step
|
|
134
|
+
this.#steps.set(stepSlug, step);
|
|
135
|
+
return step;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Check if this run has a specific step
|
|
139
|
+
*
|
|
140
|
+
* @param stepSlug - Step slug to check
|
|
141
|
+
* @returns true if the step exists, false otherwise
|
|
142
|
+
*/
|
|
143
|
+
hasStep(stepSlug) {
|
|
144
|
+
// Check if we have this step cached
|
|
145
|
+
return this.#steps.has(stepSlug);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Wait for the run to reach a specific status
|
|
149
|
+
*
|
|
150
|
+
* @param targetStatus - The status to wait for
|
|
151
|
+
* @param options - Optional timeout and abort signal
|
|
152
|
+
* @returns Promise that resolves with the run instance when the status is reached
|
|
153
|
+
*/
|
|
154
|
+
waitForStatus(targetStatus, options) {
|
|
155
|
+
const timeoutMs = options?.timeoutMs ?? 5 * 60 * 1000; // Default 5 minutes
|
|
156
|
+
const { signal } = options || {};
|
|
157
|
+
// If we already have the target status, resolve immediately
|
|
158
|
+
if (this.status === targetStatus) {
|
|
159
|
+
return Promise.resolve(this);
|
|
160
|
+
}
|
|
161
|
+
// Otherwise, wait for the status to change
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
let timeoutId;
|
|
164
|
+
let cleanedUp = false;
|
|
165
|
+
// Set up timeout if provided
|
|
166
|
+
if (timeoutMs > 0) {
|
|
167
|
+
timeoutId = setTimeout(() => {
|
|
168
|
+
if (cleanedUp)
|
|
169
|
+
return; // Prevent firing if already cleaned up
|
|
170
|
+
cleanedUp = true;
|
|
171
|
+
unbind();
|
|
172
|
+
reject(new Error(`Timeout waiting for run ${this.run_id} to reach status '${targetStatus}'`));
|
|
173
|
+
}, timeoutMs);
|
|
174
|
+
}
|
|
175
|
+
// Set up abort signal if provided
|
|
176
|
+
let abortCleanup;
|
|
177
|
+
if (signal) {
|
|
178
|
+
const abortHandler = () => {
|
|
179
|
+
if (cleanedUp)
|
|
180
|
+
return; // Prevent double cleanup
|
|
181
|
+
cleanedUp = true;
|
|
182
|
+
if (timeoutId)
|
|
183
|
+
clearTimeout(timeoutId);
|
|
184
|
+
unbind();
|
|
185
|
+
reject(new Error(`Aborted waiting for run ${this.run_id} to reach status '${targetStatus}'`));
|
|
186
|
+
};
|
|
187
|
+
signal.addEventListener('abort', abortHandler);
|
|
188
|
+
abortCleanup = () => {
|
|
189
|
+
signal.removeEventListener('abort', abortHandler);
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// Subscribe to all events
|
|
193
|
+
const unbind = this.on('*', (event) => {
|
|
194
|
+
if (event.status === targetStatus) {
|
|
195
|
+
if (cleanedUp)
|
|
196
|
+
return; // Prevent double cleanup
|
|
197
|
+
cleanedUp = true;
|
|
198
|
+
if (timeoutId)
|
|
199
|
+
clearTimeout(timeoutId);
|
|
200
|
+
if (abortCleanup)
|
|
201
|
+
abortCleanup();
|
|
202
|
+
unbind();
|
|
203
|
+
resolve(this);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Apply state from database snapshot (no events emitted)
|
|
210
|
+
* Used when initializing state from start_flow_with_states() or get_run_with_states()
|
|
211
|
+
*
|
|
212
|
+
* @internal This method is only intended for use by PgflowClient.
|
|
213
|
+
* Applications should not call this directly.
|
|
214
|
+
*/
|
|
215
|
+
applySnapshot(row) {
|
|
216
|
+
// Direct state assignment from database row (no event conversion)
|
|
217
|
+
this.#state.status = row.status;
|
|
218
|
+
this.#state.input = row.input;
|
|
219
|
+
this.#state.output = row.output;
|
|
220
|
+
this.#state.started_at = row.started_at ? new Date(row.started_at) : null;
|
|
221
|
+
this.#state.completed_at = row.completed_at ? new Date(row.completed_at) : null;
|
|
222
|
+
this.#state.failed_at = row.failed_at ? new Date(row.failed_at) : null;
|
|
223
|
+
this.#state.remaining_steps = row.remaining_steps;
|
|
224
|
+
this.#state.error_message = null; // Database doesn't have error_message for runs
|
|
225
|
+
this.#state.error = null;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Updates the run state based on an event
|
|
229
|
+
*
|
|
230
|
+
* @internal This method is only intended for use by PgflowClient and tests.
|
|
231
|
+
* Applications should not call this directly - state updates should come from
|
|
232
|
+
* database events through the PgflowClient.
|
|
233
|
+
*
|
|
234
|
+
* TODO: After v1.0, make this method private and refactor tests to use PgflowClient
|
|
235
|
+
* with event emission instead of direct state manipulation.
|
|
236
|
+
*/
|
|
237
|
+
updateState(event) {
|
|
238
|
+
// Validate the event is for this run
|
|
239
|
+
if (event.run_id !== this.#state.run_id) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
// Check if the event status has higher precedence than current status
|
|
243
|
+
if (!this.#shouldUpdateStatus(this.#state.status, event.status)) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
// Update state based on event type using narrowing type guards
|
|
247
|
+
switch (event.status) {
|
|
248
|
+
case FlowRunStatus.Started:
|
|
249
|
+
this.#state = {
|
|
250
|
+
...this.#state,
|
|
251
|
+
status: FlowRunStatus.Started,
|
|
252
|
+
started_at: typeof event.started_at === 'string'
|
|
253
|
+
? new Date(event.started_at)
|
|
254
|
+
: new Date(),
|
|
255
|
+
remaining_steps: 'remaining_steps' in event
|
|
256
|
+
? Number(event.remaining_steps)
|
|
257
|
+
: this.#state.remaining_steps,
|
|
258
|
+
};
|
|
259
|
+
this.#events.emit('started', event);
|
|
260
|
+
break;
|
|
261
|
+
case FlowRunStatus.Completed:
|
|
262
|
+
this.#state = {
|
|
263
|
+
...this.#state,
|
|
264
|
+
status: FlowRunStatus.Completed,
|
|
265
|
+
completed_at: typeof event.completed_at === 'string'
|
|
266
|
+
? new Date(event.completed_at)
|
|
267
|
+
: new Date(),
|
|
268
|
+
output: event.output,
|
|
269
|
+
remaining_steps: 0,
|
|
270
|
+
};
|
|
271
|
+
this.#events.emit('completed', event);
|
|
272
|
+
// Check for auto-dispose
|
|
273
|
+
this.#checkAutoDispose();
|
|
274
|
+
break;
|
|
275
|
+
case FlowRunStatus.Failed:
|
|
276
|
+
this.#state = {
|
|
277
|
+
...this.#state,
|
|
278
|
+
status: FlowRunStatus.Failed,
|
|
279
|
+
failed_at: typeof event.failed_at === 'string'
|
|
280
|
+
? new Date(event.failed_at)
|
|
281
|
+
: new Date(),
|
|
282
|
+
error_message: typeof event.error_message === 'string'
|
|
283
|
+
? event.error_message
|
|
284
|
+
: 'Unknown error',
|
|
285
|
+
error: new Error(typeof event.error_message === 'string'
|
|
286
|
+
? event.error_message
|
|
287
|
+
: 'Unknown error'),
|
|
288
|
+
};
|
|
289
|
+
this.#events.emit('failed', event);
|
|
290
|
+
// Check for auto-dispose
|
|
291
|
+
this.#checkAutoDispose();
|
|
292
|
+
break;
|
|
293
|
+
default: {
|
|
294
|
+
// Exhaustiveness check - ensures all event statuses are handled
|
|
295
|
+
event;
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Also emit to the catch-all listener
|
|
300
|
+
this.#events.emit('*', event);
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Updates a step state based on an event
|
|
305
|
+
*
|
|
306
|
+
* @param stepSlug - Step slug to update
|
|
307
|
+
* @param event - Event data to update the step with
|
|
308
|
+
* @returns true if the state was updated, false otherwise
|
|
309
|
+
*/
|
|
310
|
+
updateStepState(stepSlug, event) {
|
|
311
|
+
const step = this.step(stepSlug);
|
|
312
|
+
return step.updateState(event);
|
|
313
|
+
}
|
|
314
|
+
// Track number of listeners
|
|
315
|
+
#listenerCount = 0;
|
|
316
|
+
/**
|
|
317
|
+
* Checks if auto-dispose should be triggered (when in terminal state with no listeners)
|
|
318
|
+
*/
|
|
319
|
+
#checkAutoDispose() {
|
|
320
|
+
// Don't auto-dispose multiple times
|
|
321
|
+
if (this.#disposed) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
// Only auto-dispose in terminal states
|
|
325
|
+
if (this.status !== FlowRunStatus.Completed &&
|
|
326
|
+
this.status !== FlowRunStatus.Failed) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
// If there are no listeners, auto-dispose
|
|
330
|
+
if (this.#listenerCount === 0) {
|
|
331
|
+
this.dispose();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Determines if a status should be updated based on precedence
|
|
336
|
+
*
|
|
337
|
+
* @param currentStatus - Current status
|
|
338
|
+
* @param newStatus - New status
|
|
339
|
+
* @returns true if the status should be updated, false otherwise
|
|
340
|
+
*/
|
|
341
|
+
#shouldUpdateStatus(currentStatus, newStatus) {
|
|
342
|
+
// Don't allow changes to terminal states
|
|
343
|
+
if (currentStatus === FlowRunStatus.Completed ||
|
|
344
|
+
currentStatus === FlowRunStatus.Failed) {
|
|
345
|
+
return false; // Terminal states should never change
|
|
346
|
+
}
|
|
347
|
+
const currentPrecedence = this.#statusPrecedence[currentStatus];
|
|
348
|
+
const newPrecedence = this.#statusPrecedence[newStatus];
|
|
349
|
+
// Only allow transitions to higher precedence non-terminal status
|
|
350
|
+
return newPrecedence > currentPrecedence;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Clean up all resources held by this run
|
|
354
|
+
*/
|
|
355
|
+
dispose() {
|
|
356
|
+
if (this.#disposed) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
// Clear the map to allow garbage collection of steps
|
|
360
|
+
this.#steps.clear();
|
|
361
|
+
// Create a new events object - this effectively clears all listeners
|
|
362
|
+
// without accessing the private internals of nanoevents
|
|
363
|
+
this.#events = createNanoEvents();
|
|
364
|
+
this.#listenerCount = 0;
|
|
365
|
+
// Mark as disposed
|
|
366
|
+
this.#disposed = true;
|
|
367
|
+
}
|
|
368
|
+
}
|