@meetploy/emulator 1.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.
Files changed (49) hide show
  1. package/dist/bundler/bundler.d.ts +9 -0
  2. package/dist/bundler/bundler.js +174 -0
  3. package/dist/bundler/bundler.js.map +1 -0
  4. package/dist/bundler/watcher.d.ts +5 -0
  5. package/dist/bundler/watcher.js +65 -0
  6. package/dist/bundler/watcher.js.map +1 -0
  7. package/dist/config/ploy-config.d.ts +13 -0
  8. package/dist/config/ploy-config.js +40 -0
  9. package/dist/config/ploy-config.js.map +1 -0
  10. package/dist/config/workerd-config.d.ts +10 -0
  11. package/dist/config/workerd-config.js +49 -0
  12. package/dist/config/workerd-config.js.map +1 -0
  13. package/dist/emulator.d.ts +27 -0
  14. package/dist/emulator.js +247 -0
  15. package/dist/emulator.js.map +1 -0
  16. package/dist/index.d.ts +2 -0
  17. package/dist/index.js +2 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/runtime/db-runtime.d.ts +1 -0
  20. package/dist/runtime/db-runtime.js +142 -0
  21. package/dist/runtime/db-runtime.js.map +1 -0
  22. package/dist/runtime/queue-runtime.d.ts +1 -0
  23. package/dist/runtime/queue-runtime.js +60 -0
  24. package/dist/runtime/queue-runtime.js.map +1 -0
  25. package/dist/runtime/workflow-runtime.d.ts +1 -0
  26. package/dist/runtime/workflow-runtime.js +206 -0
  27. package/dist/runtime/workflow-runtime.js.map +1 -0
  28. package/dist/services/db-service.d.ts +22 -0
  29. package/dist/services/db-service.js +103 -0
  30. package/dist/services/db-service.js.map +1 -0
  31. package/dist/services/mock-server.d.ts +10 -0
  32. package/dist/services/mock-server.js +46 -0
  33. package/dist/services/mock-server.js.map +1 -0
  34. package/dist/services/queue-service.d.ts +64 -0
  35. package/dist/services/queue-service.js +198 -0
  36. package/dist/services/queue-service.js.map +1 -0
  37. package/dist/services/workflow-service.d.ts +108 -0
  38. package/dist/services/workflow-service.js +217 -0
  39. package/dist/services/workflow-service.js.map +1 -0
  40. package/dist/utils/logger.d.ts +5 -0
  41. package/dist/utils/logger.js +30 -0
  42. package/dist/utils/logger.js.map +1 -0
  43. package/dist/utils/paths.d.ts +6 -0
  44. package/dist/utils/paths.js +29 -0
  45. package/dist/utils/paths.js.map +1 -0
  46. package/dist/utils/sqlite.d.ts +7 -0
  47. package/dist/utils/sqlite.js +82 -0
  48. package/dist/utils/sqlite.js.map +1 -0
  49. package/package.json +41 -0
@@ -0,0 +1,46 @@
1
+ import { serve } from "@hono/node-server";
2
+ import { Hono } from "hono";
3
+ import { createDbHandler } from "./db-service.js";
4
+ import { createQueueHandlers } from "./queue-service.js";
5
+ import { createWorkflowHandlers } from "./workflow-service.js";
6
+ export async function startMockServer(dbManager, config, options = {}) {
7
+ const app = new Hono();
8
+ if (config.db) {
9
+ const dbHandler = createDbHandler(dbManager.getD1Database);
10
+ app.post("/db", dbHandler);
11
+ }
12
+ if (config.queue) {
13
+ const queueHandlers = createQueueHandlers(dbManager.emulatorDb);
14
+ app.post("/queue/send", queueHandlers.sendHandler);
15
+ app.post("/queue/batch-send", queueHandlers.batchSendHandler);
16
+ app.post("/queue/receive", queueHandlers.receiveHandler);
17
+ app.post("/queue/ack", queueHandlers.ackHandler);
18
+ app.post("/queue/retry", queueHandlers.retryHandler);
19
+ }
20
+ if (config.workflow) {
21
+ const workflowHandlers = createWorkflowHandlers(dbManager.emulatorDb, options.workerUrl);
22
+ app.post("/workflow/trigger", workflowHandlers.triggerHandler);
23
+ app.post("/workflow/status", workflowHandlers.statusHandler);
24
+ app.post("/workflow/cancel", workflowHandlers.cancelHandler);
25
+ app.post("/workflow/step/start", workflowHandlers.stepStartHandler);
26
+ app.post("/workflow/step/complete", workflowHandlers.stepCompleteHandler);
27
+ app.post("/workflow/step/fail", workflowHandlers.stepFailHandler);
28
+ app.post("/workflow/complete", workflowHandlers.completeHandler);
29
+ app.post("/workflow/fail", workflowHandlers.failHandler);
30
+ }
31
+ app.get("/health", (c) => c.json({ status: "ok" }));
32
+ return await new Promise((resolve) => {
33
+ const server = serve({
34
+ fetch: app.fetch,
35
+ port: 0,
36
+ }, (info) => {
37
+ resolve({
38
+ port: info.port,
39
+ close: () => new Promise((res) => {
40
+ server.close(() => res());
41
+ }),
42
+ });
43
+ });
44
+ });
45
+ }
46
+ //# sourceMappingURL=mock-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-server.js","sourceRoot":"","sources":["../../src/services/mock-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type Database from "better-sqlite3";
2
+ import type { Context } from "hono";
3
+ export interface QueueMessage {
4
+ id: string;
5
+ queueName: string;
6
+ payload: unknown;
7
+ status: string;
8
+ attempt: number;
9
+ deliveryId: string | null;
10
+ visibleAt: number;
11
+ createdAt: number;
12
+ }
13
+ export declare function createQueueHandlers(db: Database.Database): {
14
+ sendHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
15
+ success: true;
16
+ messageId: `${string}-${string}-${string}-${string}-${string}`;
17
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
18
+ success: false;
19
+ error: string;
20
+ }, 500, "json">)>;
21
+ batchSendHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
22
+ success: true;
23
+ messageIds: string[];
24
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
25
+ success: false;
26
+ error: string;
27
+ }, 500, "json">)>;
28
+ receiveHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
29
+ success: true;
30
+ messages: {
31
+ id: string;
32
+ payload: any;
33
+ attempt: number;
34
+ deliveryId: `${string}-${string}-${string}-${string}-${string}`;
35
+ }[];
36
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
37
+ success: false;
38
+ messages: never[];
39
+ error: string;
40
+ }, 500, "json">)>;
41
+ ackHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
42
+ success: false;
43
+ error: string;
44
+ }, 404, "json">) | (Response & import("hono").TypedResponse<{
45
+ success: true;
46
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
47
+ success: false;
48
+ error: string;
49
+ }, 500, "json">)>;
50
+ retryHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
51
+ success: false;
52
+ error: string;
53
+ }, 404, "json">) | (Response & import("hono").TypedResponse<{
54
+ success: true;
55
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
56
+ success: false;
57
+ error: string;
58
+ }, 500, "json">)>;
59
+ };
60
+ export interface QueueProcessor {
61
+ start: () => void;
62
+ stop: () => void;
63
+ }
64
+ export declare function createQueueProcessor(db: Database.Database, queueBindings: Record<string, string>, workerUrl: string): QueueProcessor;
@@ -0,0 +1,198 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export function createQueueHandlers(db) {
3
+ const sendHandler = async (c) => {
4
+ try {
5
+ const body = await c.req.json();
6
+ const { queueName, payload, delaySeconds = 0 } = body;
7
+ const id = randomUUID();
8
+ const now = Math.floor(Date.now() / 1000);
9
+ const visibleAt = now + delaySeconds;
10
+ db.prepare(`INSERT INTO queue_messages (id, queue_name, payload, visible_at)
11
+ VALUES (?, ?, ?, ?)`).run(id, queueName, JSON.stringify(payload), visibleAt);
12
+ return c.json({ success: true, messageId: id });
13
+ }
14
+ catch (err) {
15
+ const message = err instanceof Error ? err.message : String(err);
16
+ return c.json({ success: false, error: message }, 500);
17
+ }
18
+ };
19
+ const batchSendHandler = async (c) => {
20
+ try {
21
+ const body = await c.req.json();
22
+ const { queueName, messages } = body;
23
+ const now = Math.floor(Date.now() / 1000);
24
+ const messageIds = [];
25
+ const insert = db.prepare(`INSERT INTO queue_messages (id, queue_name, payload, visible_at)
26
+ VALUES (?, ?, ?, ?)`);
27
+ const transaction = db.transaction(() => {
28
+ for (const msg of messages) {
29
+ const id = randomUUID();
30
+ const visibleAt = now + (msg.delaySeconds || 0);
31
+ insert.run(id, queueName, JSON.stringify(msg.payload), visibleAt);
32
+ messageIds.push(id);
33
+ }
34
+ });
35
+ transaction();
36
+ return c.json({ success: true, messageIds });
37
+ }
38
+ catch (err) {
39
+ const message = err instanceof Error ? err.message : String(err);
40
+ return c.json({ success: false, error: message }, 500);
41
+ }
42
+ };
43
+ const receiveHandler = async (c) => {
44
+ try {
45
+ const body = await c.req.json();
46
+ const { queueName, maxMessages = 10, visibilityTimeoutSeconds = 30, } = body;
47
+ const now = Math.floor(Date.now() / 1000);
48
+ const deliveryId = randomUUID();
49
+ const newVisibleAt = now + visibilityTimeoutSeconds;
50
+ const rows = db
51
+ .prepare(`SELECT id, queue_name, payload, attempt
52
+ FROM queue_messages
53
+ WHERE queue_name = ? AND status = 'pending' AND visible_at <= ?
54
+ ORDER BY visible_at ASC
55
+ LIMIT ?`)
56
+ .all(queueName, now, maxMessages);
57
+ if (rows.length === 0) {
58
+ return c.json({ success: true, messages: [] });
59
+ }
60
+ const ids = rows.map((r) => r.id);
61
+ const placeholders = ids.map(() => "?").join(",");
62
+ db.prepare(`UPDATE queue_messages
63
+ SET status = 'processing', delivery_id = ?, visible_at = ?, attempt = attempt + 1, updated_at = ?
64
+ WHERE id IN (${placeholders})`).run(deliveryId, newVisibleAt, now, ...ids);
65
+ const messages = rows.map((row) => ({
66
+ id: row.id,
67
+ payload: JSON.parse(row.payload),
68
+ attempt: row.attempt + 1,
69
+ deliveryId,
70
+ }));
71
+ return c.json({ success: true, messages });
72
+ }
73
+ catch (err) {
74
+ const message = err instanceof Error ? err.message : String(err);
75
+ return c.json({ success: false, messages: [], error: message }, 500);
76
+ }
77
+ };
78
+ const ackHandler = async (c) => {
79
+ try {
80
+ const body = await c.req.json();
81
+ const { messageId, deliveryId } = body;
82
+ const result = db
83
+ .prepare(`DELETE FROM queue_messages
84
+ WHERE id = ? AND delivery_id = ?`)
85
+ .run(messageId, deliveryId);
86
+ if (result.changes === 0) {
87
+ return c.json({ success: false, error: "Message not found or already processed" }, 404);
88
+ }
89
+ return c.json({ success: true });
90
+ }
91
+ catch (err) {
92
+ const message = err instanceof Error ? err.message : String(err);
93
+ return c.json({ success: false, error: message }, 500);
94
+ }
95
+ };
96
+ const retryHandler = async (c) => {
97
+ try {
98
+ const body = await c.req.json();
99
+ const { messageId, deliveryId } = body;
100
+ const now = Math.floor(Date.now() / 1000);
101
+ const backoffSeconds = 5;
102
+ const result = db
103
+ .prepare(`UPDATE queue_messages
104
+ SET status = 'pending', delivery_id = NULL, visible_at = ?, updated_at = ?
105
+ WHERE id = ? AND delivery_id = ?`)
106
+ .run(now + backoffSeconds, now, messageId, deliveryId);
107
+ if (result.changes === 0) {
108
+ return c.json({ success: false, error: "Message not found or already processed" }, 404);
109
+ }
110
+ return c.json({ success: true });
111
+ }
112
+ catch (err) {
113
+ const message = err instanceof Error ? err.message : String(err);
114
+ return c.json({ success: false, error: message }, 500);
115
+ }
116
+ };
117
+ return {
118
+ sendHandler,
119
+ batchSendHandler,
120
+ receiveHandler,
121
+ ackHandler,
122
+ retryHandler,
123
+ };
124
+ }
125
+ export function createQueueProcessor(db, queueBindings, workerUrl) {
126
+ let interval = null;
127
+ async function processQueues() {
128
+ for (const [bindingName, queueName] of Object.entries(queueBindings)) {
129
+ await processQueue(bindingName, queueName);
130
+ }
131
+ }
132
+ async function processQueue(bindingName, queueName) {
133
+ const now = Math.floor(Date.now() / 1000);
134
+ const deliveryId = randomUUID();
135
+ const visibilityTimeout = 30;
136
+ const rows = db
137
+ .prepare(`SELECT id, payload, attempt
138
+ FROM queue_messages
139
+ WHERE queue_name = ? AND status = 'pending' AND visible_at <= ?
140
+ LIMIT 10`)
141
+ .all(queueName, now);
142
+ if (rows.length === 0) {
143
+ return;
144
+ }
145
+ const ids = rows.map((r) => r.id);
146
+ const placeholders = ids.map(() => "?").join(",");
147
+ db.prepare(`UPDATE queue_messages
148
+ SET status = 'processing', delivery_id = ?, visible_at = ?, attempt = attempt + 1, updated_at = ?
149
+ WHERE id IN (${placeholders})`).run(deliveryId, now + visibilityTimeout, now, ...ids);
150
+ for (const row of rows) {
151
+ try {
152
+ const response = await fetch(workerUrl, {
153
+ method: "POST",
154
+ headers: {
155
+ "Content-Type": "application/json",
156
+ "X-Ploy-Queue-Delivery": "true",
157
+ "X-Ploy-Queue-Name": queueName,
158
+ "X-Ploy-Queue-Binding": bindingName,
159
+ "X-Ploy-Message-Id": row.id,
160
+ "X-Ploy-Delivery-Id": deliveryId,
161
+ "X-Ploy-Message-Attempt": String(row.attempt + 1),
162
+ },
163
+ body: row.payload,
164
+ });
165
+ if (response.ok) {
166
+ db.prepare(`DELETE FROM queue_messages WHERE id = ?`).run(row.id);
167
+ }
168
+ else {
169
+ db.prepare(`UPDATE queue_messages
170
+ SET status = 'pending', delivery_id = NULL, visible_at = ?, updated_at = ?
171
+ WHERE id = ?`).run(now + 5, now, row.id);
172
+ }
173
+ }
174
+ catch {
175
+ db.prepare(`UPDATE queue_messages
176
+ SET status = 'pending', delivery_id = NULL, visible_at = ?, updated_at = ?
177
+ WHERE id = ?`).run(now + 5, now, row.id);
178
+ }
179
+ }
180
+ }
181
+ return {
182
+ start() {
183
+ if (interval) {
184
+ return;
185
+ }
186
+ interval = setInterval(() => {
187
+ processQueues().catch(() => { });
188
+ }, 1000);
189
+ },
190
+ stop() {
191
+ if (interval) {
192
+ clearInterval(interval);
193
+ interval = null;
194
+ }
195
+ },
196
+ };
197
+ }
198
+ //# sourceMappingURL=queue-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-service.js","sourceRoot":"","sources":["../../src/services/queue-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import type Database from "better-sqlite3";
2
+ import type { Context } from "hono";
3
+ export interface WorkflowExecution {
4
+ id: string;
5
+ workflowName: string;
6
+ status: "pending" | "running" | "completed" | "failed" | "cancelled";
7
+ input: unknown;
8
+ output: unknown;
9
+ error: string | null;
10
+ startedAt: number | null;
11
+ completedAt: number | null;
12
+ createdAt: number;
13
+ }
14
+ export interface WorkflowStep {
15
+ id: number;
16
+ executionId: string;
17
+ stepName: string;
18
+ stepIndex: number;
19
+ status: "pending" | "running" | "completed" | "failed";
20
+ output: unknown;
21
+ error: string | null;
22
+ durationMs: number | null;
23
+ createdAt: number;
24
+ }
25
+ export declare function createWorkflowHandlers(db: Database.Database, workerUrl?: string): {
26
+ triggerHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
27
+ success: true;
28
+ executionId: `${string}-${string}-${string}-${string}-${string}`;
29
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
30
+ success: false;
31
+ error: string;
32
+ }, 500, "json">)>;
33
+ statusHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
34
+ success: false;
35
+ error: string;
36
+ }, 404, "json">) | (Response & import("hono").TypedResponse<{
37
+ success: true;
38
+ execution: {
39
+ id: string;
40
+ workflowName: string;
41
+ status: string;
42
+ input: any;
43
+ output: any;
44
+ error: string | null;
45
+ startedAt: string | null;
46
+ completedAt: string | null;
47
+ steps: {
48
+ stepName: string;
49
+ stepIndex: number;
50
+ status: string;
51
+ output: any;
52
+ error: string | null;
53
+ durationMs: number | null;
54
+ }[];
55
+ };
56
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
57
+ success: false;
58
+ error: string;
59
+ }, 500, "json">)>;
60
+ cancelHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
61
+ success: false;
62
+ error: string;
63
+ }, 404, "json">) | (Response & import("hono").TypedResponse<{
64
+ success: true;
65
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
66
+ success: false;
67
+ error: string;
68
+ }, 500, "json">)>;
69
+ stepStartHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
70
+ success: true;
71
+ alreadyCompleted: true;
72
+ output: any;
73
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
74
+ success: true;
75
+ alreadyCompleted: false;
76
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
77
+ success: false;
78
+ error: string;
79
+ }, 500, "json">)>;
80
+ stepCompleteHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
81
+ success: true;
82
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
83
+ success: false;
84
+ error: string;
85
+ }, 500, "json">)>;
86
+ stepFailHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
87
+ success: true;
88
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
89
+ success: false;
90
+ error: string;
91
+ }, 500, "json">)>;
92
+ completeHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
93
+ success: true;
94
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
95
+ success: false;
96
+ error: string;
97
+ }, 500, "json">)>;
98
+ failHandler: (c: Context) => Promise<(Response & import("hono").TypedResponse<{
99
+ success: true;
100
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
101
+ success: false;
102
+ error: string;
103
+ }, 500, "json">)>;
104
+ };
105
+ export interface WorkflowExecutor {
106
+ triggerWorkflow: (workflowName: string, input: unknown, workerUrl: string) => Promise<string>;
107
+ }
108
+ export declare function createWorkflowExecutor(db: Database.Database): WorkflowExecutor;
@@ -0,0 +1,217 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export function createWorkflowHandlers(db, workerUrl) {
3
+ const triggerHandler = async (c) => {
4
+ try {
5
+ const body = await c.req.json();
6
+ const { workflowName, input } = body;
7
+ const id = randomUUID();
8
+ const now = Math.floor(Date.now() / 1000);
9
+ db.prepare(`INSERT INTO workflow_executions (id, workflow_name, status, input, started_at, created_at)
10
+ VALUES (?, ?, 'running', ?, ?, ?)`).run(id, workflowName, JSON.stringify(input), now, now);
11
+ if (workerUrl) {
12
+ fetch(workerUrl, {
13
+ method: "POST",
14
+ headers: {
15
+ "Content-Type": "application/json",
16
+ "X-Ploy-Workflow-Execution": "true",
17
+ "X-Ploy-Workflow-Name": workflowName,
18
+ "X-Ploy-Execution-Id": id,
19
+ },
20
+ body: JSON.stringify(input),
21
+ }).catch(() => {
22
+ });
23
+ }
24
+ return c.json({ success: true, executionId: id });
25
+ }
26
+ catch (err) {
27
+ const message = err instanceof Error ? err.message : String(err);
28
+ return c.json({ success: false, error: message }, 500);
29
+ }
30
+ };
31
+ const statusHandler = async (c) => {
32
+ try {
33
+ const body = await c.req.json();
34
+ const { executionId } = body;
35
+ const execution = db
36
+ .prepare(`SELECT id, workflow_name, status, input, output, error, started_at, completed_at, created_at
37
+ FROM workflow_executions WHERE id = ?`)
38
+ .get(executionId);
39
+ if (!execution) {
40
+ return c.json({ success: false, error: "Execution not found" }, 404);
41
+ }
42
+ const steps = db
43
+ .prepare(`SELECT step_name, step_index, status, output, error, duration_ms
44
+ FROM workflow_steps WHERE execution_id = ? ORDER BY step_index`)
45
+ .all(executionId);
46
+ return c.json({
47
+ success: true,
48
+ execution: {
49
+ id: execution.id,
50
+ workflowName: execution.workflow_name,
51
+ status: execution.status,
52
+ input: execution.input ? JSON.parse(execution.input) : null,
53
+ output: execution.output ? JSON.parse(execution.output) : null,
54
+ error: execution.error,
55
+ startedAt: execution.started_at
56
+ ? new Date(execution.started_at * 1000).toISOString()
57
+ : null,
58
+ completedAt: execution.completed_at
59
+ ? new Date(execution.completed_at * 1000).toISOString()
60
+ : null,
61
+ steps: steps.map((s) => ({
62
+ stepName: s.step_name,
63
+ stepIndex: s.step_index,
64
+ status: s.status,
65
+ output: s.output ? JSON.parse(s.output) : null,
66
+ error: s.error,
67
+ durationMs: s.duration_ms,
68
+ })),
69
+ },
70
+ });
71
+ }
72
+ catch (err) {
73
+ const message = err instanceof Error ? err.message : String(err);
74
+ return c.json({ success: false, error: message }, 500);
75
+ }
76
+ };
77
+ const cancelHandler = async (c) => {
78
+ try {
79
+ const body = await c.req.json();
80
+ const { executionId } = body;
81
+ const now = Math.floor(Date.now() / 1000);
82
+ const result = db
83
+ .prepare(`UPDATE workflow_executions
84
+ SET status = 'cancelled', completed_at = ?
85
+ WHERE id = ? AND status IN ('pending', 'running')`)
86
+ .run(now, executionId);
87
+ if (result.changes === 0) {
88
+ return c.json({ success: false, error: "Execution not found or already completed" }, 404);
89
+ }
90
+ return c.json({ success: true });
91
+ }
92
+ catch (err) {
93
+ const message = err instanceof Error ? err.message : String(err);
94
+ return c.json({ success: false, error: message }, 500);
95
+ }
96
+ };
97
+ const stepStartHandler = async (c) => {
98
+ try {
99
+ const body = await c.req.json();
100
+ const { executionId, stepName, stepIndex } = body;
101
+ const existing = db
102
+ .prepare(`SELECT status, output FROM workflow_steps
103
+ WHERE execution_id = ? AND step_name = ? AND step_index = ?`)
104
+ .get(executionId, stepName, stepIndex);
105
+ if (existing && existing.status === "completed") {
106
+ return c.json({
107
+ success: true,
108
+ alreadyCompleted: true,
109
+ output: existing.output ? JSON.parse(existing.output) : null,
110
+ });
111
+ }
112
+ const now = Math.floor(Date.now() / 1000);
113
+ if (existing) {
114
+ db.prepare(`UPDATE workflow_steps SET status = 'running' WHERE execution_id = ? AND step_name = ?`).run(executionId, stepName);
115
+ }
116
+ else {
117
+ db.prepare(`INSERT INTO workflow_steps (execution_id, step_name, step_index, status, created_at)
118
+ VALUES (?, ?, ?, 'running', ?)`).run(executionId, stepName, stepIndex, now);
119
+ }
120
+ return c.json({ success: true, alreadyCompleted: false });
121
+ }
122
+ catch (err) {
123
+ const message = err instanceof Error ? err.message : String(err);
124
+ return c.json({ success: false, error: message }, 500);
125
+ }
126
+ };
127
+ const stepCompleteHandler = async (c) => {
128
+ try {
129
+ const body = await c.req.json();
130
+ const { executionId, stepName, output, durationMs } = body;
131
+ db.prepare(`UPDATE workflow_steps
132
+ SET status = 'completed', output = ?, duration_ms = ?
133
+ WHERE execution_id = ? AND step_name = ?`).run(JSON.stringify(output), durationMs, executionId, stepName);
134
+ return c.json({ success: true });
135
+ }
136
+ catch (err) {
137
+ const message = err instanceof Error ? err.message : String(err);
138
+ return c.json({ success: false, error: message }, 500);
139
+ }
140
+ };
141
+ const stepFailHandler = async (c) => {
142
+ try {
143
+ const body = await c.req.json();
144
+ const { executionId, stepName, error, durationMs } = body;
145
+ db.prepare(`UPDATE workflow_steps
146
+ SET status = 'failed', error = ?, duration_ms = ?
147
+ WHERE execution_id = ? AND step_name = ?`).run(error, durationMs, executionId, stepName);
148
+ return c.json({ success: true });
149
+ }
150
+ catch (err) {
151
+ const message = err instanceof Error ? err.message : String(err);
152
+ return c.json({ success: false, error: message }, 500);
153
+ }
154
+ };
155
+ const completeHandler = async (c) => {
156
+ try {
157
+ const body = await c.req.json();
158
+ const { executionId, output } = body;
159
+ const now = Math.floor(Date.now() / 1000);
160
+ db.prepare(`UPDATE workflow_executions
161
+ SET status = 'completed', output = ?, completed_at = ?
162
+ WHERE id = ?`).run(JSON.stringify(output), now, executionId);
163
+ return c.json({ success: true });
164
+ }
165
+ catch (err) {
166
+ const message = err instanceof Error ? err.message : String(err);
167
+ return c.json({ success: false, error: message }, 500);
168
+ }
169
+ };
170
+ const failHandler = async (c) => {
171
+ try {
172
+ const body = await c.req.json();
173
+ const { executionId, error } = body;
174
+ const now = Math.floor(Date.now() / 1000);
175
+ db.prepare(`UPDATE workflow_executions
176
+ SET status = 'failed', error = ?, completed_at = ?
177
+ WHERE id = ?`).run(error, now, executionId);
178
+ return c.json({ success: true });
179
+ }
180
+ catch (err) {
181
+ const message = err instanceof Error ? err.message : String(err);
182
+ return c.json({ success: false, error: message }, 500);
183
+ }
184
+ };
185
+ return {
186
+ triggerHandler,
187
+ statusHandler,
188
+ cancelHandler,
189
+ stepStartHandler,
190
+ stepCompleteHandler,
191
+ stepFailHandler,
192
+ completeHandler,
193
+ failHandler,
194
+ };
195
+ }
196
+ export function createWorkflowExecutor(db) {
197
+ return {
198
+ async triggerWorkflow(workflowName, input, workerUrl) {
199
+ const id = randomUUID();
200
+ const now = Math.floor(Date.now() / 1000);
201
+ db.prepare(`INSERT INTO workflow_executions (id, workflow_name, status, input, started_at, created_at)
202
+ VALUES (?, ?, 'running', ?, ?, ?)`).run(id, workflowName, JSON.stringify(input), now, now);
203
+ fetch(workerUrl, {
204
+ method: "POST",
205
+ headers: {
206
+ "Content-Type": "application/json",
207
+ "X-Ploy-Workflow-Execution": "true",
208
+ "X-Ploy-Workflow-Name": workflowName,
209
+ "X-Ploy-Execution-Id": id,
210
+ },
211
+ body: JSON.stringify(input),
212
+ }).catch(() => { });
213
+ return id;
214
+ },
215
+ };
216
+ }
217
+ //# sourceMappingURL=workflow-service.js.map