@pgflow/core 0.0.5-prealpha.2 → 0.0.5

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.
@@ -0,0 +1,46 @@
1
+ <div align="center">
2
+ <h1>Edge Worker</h1>
3
+ <a href="https://pgflow.dev">
4
+ <h3>📚 Documentation @ pgflow.dev</h3>
5
+ </a>
6
+
7
+ <h4>⚠️ <strong>ADVANCED PROOF of CONCEPT - NOT PRODUCTION READY</strong> ⚠️</h4>
8
+ </div>
9
+
10
+ A task queue worker for Supabase Edge Functions that extends background tasks with useful features.
11
+
12
+ > [!NOTE]
13
+ > This project is licensed under [AGPL v3](./LICENSE.md) license and is part of **pgflow** stack.
14
+ > See [LICENSING_OVERVIEW.md](../../LICENSING_OVERVIEW.md) in root of this monorepo for more details.
15
+
16
+ ## What is Edge Worker?
17
+
18
+ Edge Worker processes messages from a queue and executes user-defined functions with their payloads. It builds upon [Supabase Background Tasks](https://supabase.com/docs/guides/functions/background-tasks) to add reliability features like retries, concurrency control and monitoring.
19
+
20
+ ## Key Features
21
+
22
+ - ⚡ **Reliable Processing**: Retries with configurable delays
23
+ - 🔄 **Concurrency Control**: Limit parallel task execution
24
+ - 📊 **Observability**: Built-in heartbeats and logging
25
+ - 📈 **Horizontal Scaling**: Deploy multiple edge functions for the same queue
26
+ - 🛡️ **Edge-Native**: Designed for Edge Functions' CPU/clock limits
27
+
28
+ ## How It Works
29
+
30
+ [![Architecture Diagram](https://mermaid.ink/img/pako:eNplkcFugzAMhl8lyrl9AQ47VLBxqdSqlZAGHEziASokyEkmTaXvvoR0o1VziGL_n_9Y9pULLZEnvFItwdSxc1op5o9xTUxU_OQmaMAgy2SL7N0pYXutTMUjGU5WlItYaLog1VFAJSv14paCXdweyw8f-2MZLnZ06LBelXxXRk_DztAM-Gp9KA-kpRP-W7bdvs3Ga4aNaAy0OC_WdzD4B4IQVsLMvvkIZMUiA4mu_8ZHYjW5MxNp4dUnKC9zUHJA-h9R_VQTG-sQyDYINlTs-IaPSCP00q_gGvCK2w5HP53EPyXQJczp5jlwVp9-lOCJJYcbTtq13V_gJgkW0x78lEeefMFgfHYC9an1GqPsraZ9XPiy99svlAqmtA?type=png)](https://mermaid.live/edit#pako:eNplkcFugzAMhl8lyrl9AQ47VLBxqdSqlZAGHEziASokyEkmTaXvvoR0o1VziGL_n_9Y9pULLZEnvFItwdSxc1op5o9xTUxU_OQmaMAgy2SL7N0pYXutTMUjGU5WlItYaLog1VFAJSv14paCXdweyw8f-2MZLnZ06LBelXxXRk_DztAM-Gp9KA-kpRP-W7bdvs3Ga4aNaAy0OC_WdzD4B4IQVsLMvvkIZMUiA4mu_8ZHYjW5MxNp4dUnKC9zUHJA-h9R_VQTG-sQyDYINlTs-IaPSCP00q_gGvCK2w5HP53EPyXQJczp5jlwVp9-lOCJJYcbTtq13V_gJgkW0x78lEeefMFgfHYC9an1GqPsraZ9XPiy99svlAqmtA)
31
+
32
+ ## Edge Function Optimization
33
+
34
+ Edge Worker is specifically designed to handle Edge Function limitations:
35
+
36
+ - Stops polling near CPU/clock limits
37
+ - Gracefully aborts pending tasks
38
+ - Uses PGMQ's visibility timeout to prevent message loss
39
+ - Auto-spawns new instances for continuous operation
40
+ - Monitors worker health with database heartbeats
41
+
42
+
43
+ ## Documentation
44
+
45
+ For detailed documentation and getting started guide, visit [pgflow.dev](https://pgflow.dev).
46
+
@@ -0,0 +1,152 @@
1
+ // ../dsl/src/utils.ts
2
+ function validateSlug(slug) {
3
+ if (slug.length > 128) {
4
+ throw new Error(`Slug cannot be longer than 128 characters`);
5
+ }
6
+ if (/^\d/.test(slug)) {
7
+ throw new Error(`Slug cannot start with a number`);
8
+ }
9
+ if (/^_/.test(slug)) {
10
+ throw new Error(`Slug cannot start with an underscore`);
11
+ }
12
+ if (/\s/.test(slug)) {
13
+ throw new Error(`Slug cannot contain spaces`);
14
+ }
15
+ if (/[/:#\-?]/.test(slug)) {
16
+ throw new Error(
17
+ `Slug cannot contain special characters like /, :, ?, #, -`
18
+ );
19
+ }
20
+ }
21
+ function validateRuntimeOptions(options, opts = { optional: false }) {
22
+ const { maxAttempts, baseDelay, timeout } = options;
23
+ if (maxAttempts !== void 0 && maxAttempts !== null) {
24
+ if (maxAttempts < 1) {
25
+ throw new Error("maxAttempts must be greater than or equal to 1");
26
+ }
27
+ } else if (!opts.optional) {
28
+ throw new Error("maxAttempts is required");
29
+ }
30
+ if (baseDelay !== void 0 && baseDelay !== null) {
31
+ if (baseDelay < 1) {
32
+ throw new Error("baseDelay must be greater than or equal to 1");
33
+ }
34
+ } else if (!opts.optional) {
35
+ throw new Error("baseDelay is required");
36
+ }
37
+ if (timeout !== void 0 && timeout !== null) {
38
+ if (timeout < 3) {
39
+ throw new Error("timeout must be greater than or equal to 3");
40
+ }
41
+ } else if (!opts.optional) {
42
+ throw new Error("timeout is required");
43
+ }
44
+ }
45
+
46
+ // ../dsl/src/dsl.ts
47
+ var Flow = class _Flow {
48
+ /**
49
+ * Store step definitions with their proper types
50
+ *
51
+ * This is typed as a generic record because TypeScript cannot track the exact relationship
52
+ * between step slugs and their corresponding input/output types at the container level.
53
+ * Type safety is enforced at the method level when adding or retrieving steps.
54
+ */
55
+ stepDefinitions;
56
+ stepOrder;
57
+ slug;
58
+ options;
59
+ constructor(config, stepDefinitions = {}, stepOrder = []) {
60
+ const { slug, ...options } = config;
61
+ validateSlug(slug);
62
+ validateRuntimeOptions(options, { optional: true });
63
+ this.slug = slug;
64
+ this.options = options;
65
+ this.stepDefinitions = stepDefinitions;
66
+ this.stepOrder = [...stepOrder];
67
+ }
68
+ /**
69
+ * Get a specific step definition by slug with proper typing
70
+ * @throws Error if the step with the given slug doesn't exist
71
+ */
72
+ getStepDefinition(slug) {
73
+ if (!(slug in this.stepDefinitions)) {
74
+ throw new Error(
75
+ `Step "${String(slug)}" does not exist in flow "${this.slug}"`
76
+ );
77
+ }
78
+ return this.stepDefinitions[slug];
79
+ }
80
+ // SlugType extends keyof Steps & keyof StepDependencies
81
+ step(opts, handler) {
82
+ const slug = opts.slug;
83
+ validateSlug(slug);
84
+ if (this.stepDefinitions[slug]) {
85
+ throw new Error(`Step "${slug}" already exists in flow "${this.slug}"`);
86
+ }
87
+ const dependencies = opts.dependsOn || [];
88
+ if (dependencies.length > 0) {
89
+ for (const dep of dependencies) {
90
+ if (!this.stepDefinitions[dep]) {
91
+ throw new Error(`Step "${slug}" depends on undefined step "${dep}"`);
92
+ }
93
+ }
94
+ }
95
+ const options = {};
96
+ if (opts.maxAttempts !== void 0)
97
+ options.maxAttempts = opts.maxAttempts;
98
+ if (opts.baseDelay !== void 0)
99
+ options.baseDelay = opts.baseDelay;
100
+ if (opts.timeout !== void 0)
101
+ options.timeout = opts.timeout;
102
+ validateRuntimeOptions(options, { optional: true });
103
+ const newStepDefinition = {
104
+ slug,
105
+ handler,
106
+ dependencies,
107
+ options
108
+ };
109
+ const newStepDefinitions = {
110
+ ...this.stepDefinitions,
111
+ [slug]: newStepDefinition
112
+ };
113
+ const newStepOrder = [...this.stepOrder, slug];
114
+ return new _Flow(
115
+ { slug: this.slug, ...this.options },
116
+ newStepDefinitions,
117
+ newStepOrder
118
+ );
119
+ }
120
+ };
121
+
122
+ // ../example-flows/src/example-flow.ts
123
+ var ExampleFlow = new Flow({
124
+ slug: "example_flow",
125
+ maxAttempts: 3
126
+ }).step({ slug: "rootStep" }, async (input) => ({
127
+ doubledValue: input.run.value * 2
128
+ })).step(
129
+ { slug: "normalStep", dependsOn: ["rootStep"], maxAttempts: 5 },
130
+ async (input) => ({
131
+ doubledValueArray: [input.rootStep.doubledValue]
132
+ })
133
+ ).step({ slug: "thirdStep", dependsOn: ["normalStep"] }, async (input) => ({
134
+ // input.rootStep would be a type error since it's not in dependsOn
135
+ finalValue: input.normalStep.doubledValueArray.length
136
+ }));
137
+ var stepTaskRecord = {
138
+ flow_slug: "example_flow",
139
+ run_id: "123",
140
+ step_slug: "normalStep",
141
+ input: {
142
+ run: { value: 23 },
143
+ rootStep: { doubledValue: 23 }
144
+ // thirdStep: { finalValue: 23 }, --- this should be an error
145
+ // normalStep: { doubledValueArray: [1, 2, 3] }, --- this should be an error
146
+ },
147
+ msg_id: 1
148
+ };
149
+ export {
150
+ ExampleFlow,
151
+ stepTaskRecord
152
+ };
@@ -0,0 +1,11 @@
1
+ # example-flows
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build example-flows` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test example-flows` to execute the unit tests via [Vitest](https://vitest.dev/).