@sonamu-kit/tasks 0.0.1

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 (280) hide show
  1. package/.swcrc +17 -0
  2. package/README.md +7 -0
  3. package/dist/backend.d.ts +107 -0
  4. package/dist/backend.d.ts.map +1 -0
  5. package/dist/backend.js +3 -0
  6. package/dist/backend.js.map +1 -0
  7. package/dist/chaos.test.d.ts +2 -0
  8. package/dist/chaos.test.d.ts.map +1 -0
  9. package/dist/chaos.test.js +92 -0
  10. package/dist/chaos.test.js.map +1 -0
  11. package/dist/client.d.ts +178 -0
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +223 -0
  14. package/dist/client.js.map +1 -0
  15. package/dist/client.test.d.ts +2 -0
  16. package/dist/client.test.d.ts.map +1 -0
  17. package/dist/client.test.js +339 -0
  18. package/dist/client.test.js.map +1 -0
  19. package/dist/config.d.ts +22 -0
  20. package/dist/config.d.ts.map +1 -0
  21. package/dist/config.js +23 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/config.test.d.ts +2 -0
  24. package/dist/config.test.d.ts.map +1 -0
  25. package/dist/config.test.js +24 -0
  26. package/dist/config.test.js.map +1 -0
  27. package/dist/core/duration.d.ts +22 -0
  28. package/dist/core/duration.d.ts.map +1 -0
  29. package/dist/core/duration.js +64 -0
  30. package/dist/core/duration.js.map +1 -0
  31. package/dist/core/duration.test.d.ts +2 -0
  32. package/dist/core/duration.test.d.ts.map +1 -0
  33. package/dist/core/duration.test.js +265 -0
  34. package/dist/core/duration.test.js.map +1 -0
  35. package/dist/core/error.d.ts +15 -0
  36. package/dist/core/error.d.ts.map +1 -0
  37. package/dist/core/error.js +25 -0
  38. package/dist/core/error.js.map +1 -0
  39. package/dist/core/error.test.d.ts +2 -0
  40. package/dist/core/error.test.d.ts.map +1 -0
  41. package/dist/core/error.test.js +63 -0
  42. package/dist/core/error.test.js.map +1 -0
  43. package/dist/core/json.d.ts +5 -0
  44. package/dist/core/json.d.ts.map +1 -0
  45. package/dist/core/json.js +3 -0
  46. package/dist/core/json.js.map +1 -0
  47. package/dist/core/result.d.ts +22 -0
  48. package/dist/core/result.d.ts.map +1 -0
  49. package/dist/core/result.js +22 -0
  50. package/dist/core/result.js.map +1 -0
  51. package/dist/core/result.test.d.ts +2 -0
  52. package/dist/core/result.test.d.ts.map +1 -0
  53. package/dist/core/result.test.js +19 -0
  54. package/dist/core/result.test.js.map +1 -0
  55. package/dist/core/retry.d.ts +21 -0
  56. package/dist/core/retry.d.ts.map +1 -0
  57. package/dist/core/retry.js +25 -0
  58. package/dist/core/retry.js.map +1 -0
  59. package/dist/core/retry.test.d.ts +2 -0
  60. package/dist/core/retry.test.d.ts.map +1 -0
  61. package/dist/core/retry.test.js +37 -0
  62. package/dist/core/retry.test.js.map +1 -0
  63. package/dist/core/schema.d.ts +57 -0
  64. package/dist/core/schema.d.ts.map +1 -0
  65. package/dist/core/schema.js +4 -0
  66. package/dist/core/schema.js.map +1 -0
  67. package/dist/core/step.d.ts +96 -0
  68. package/dist/core/step.d.ts.map +1 -0
  69. package/dist/core/step.js +78 -0
  70. package/dist/core/step.js.map +1 -0
  71. package/dist/core/step.test.d.ts +2 -0
  72. package/dist/core/step.test.d.ts.map +1 -0
  73. package/dist/core/step.test.js +356 -0
  74. package/dist/core/step.test.js.map +1 -0
  75. package/dist/core/workflow.d.ts +78 -0
  76. package/dist/core/workflow.d.ts.map +1 -0
  77. package/dist/core/workflow.js +46 -0
  78. package/dist/core/workflow.js.map +1 -0
  79. package/dist/core/workflow.test.d.ts +2 -0
  80. package/dist/core/workflow.test.d.ts.map +1 -0
  81. package/dist/core/workflow.test.js +172 -0
  82. package/dist/core/workflow.test.js.map +1 -0
  83. package/dist/database/backend.d.ts +60 -0
  84. package/dist/database/backend.d.ts.map +1 -0
  85. package/dist/database/backend.js +387 -0
  86. package/dist/database/backend.js.map +1 -0
  87. package/dist/database/backend.test.d.ts +2 -0
  88. package/dist/database/backend.test.d.ts.map +1 -0
  89. package/dist/database/backend.test.js +17 -0
  90. package/dist/database/backend.test.js.map +1 -0
  91. package/dist/database/backend.testsuite.d.ts +20 -0
  92. package/dist/database/backend.testsuite.d.ts.map +1 -0
  93. package/dist/database/backend.testsuite.js +1174 -0
  94. package/dist/database/backend.testsuite.js.map +1 -0
  95. package/dist/database/base.d.ts +12 -0
  96. package/dist/database/base.d.ts.map +1 -0
  97. package/dist/database/base.js +19 -0
  98. package/dist/database/base.js.map +1 -0
  99. package/dist/database/migrations/20251212000000_0_init.js +9 -0
  100. package/dist/database/migrations/20251212000000_0_init.js.map +1 -0
  101. package/dist/database/migrations/20251212000000_1_tables.js +88 -0
  102. package/dist/database/migrations/20251212000000_1_tables.js.map +1 -0
  103. package/dist/database/migrations/20251212000000_2_fk.js +48 -0
  104. package/dist/database/migrations/20251212000000_2_fk.js.map +1 -0
  105. package/dist/database/migrations/20251212000000_3_indexes.js +107 -0
  106. package/dist/database/migrations/20251212000000_3_indexes.js.map +1 -0
  107. package/dist/database/pubsub.d.ts +17 -0
  108. package/dist/database/pubsub.d.ts.map +1 -0
  109. package/dist/database/pubsub.js +70 -0
  110. package/dist/database/pubsub.js.map +1 -0
  111. package/dist/database/pubsub.test.d.ts +2 -0
  112. package/dist/database/pubsub.test.d.ts.map +1 -0
  113. package/dist/database/pubsub.test.js +86 -0
  114. package/dist/database/pubsub.test.js.map +1 -0
  115. package/dist/errors.d.ts +8 -0
  116. package/dist/errors.d.ts.map +1 -0
  117. package/dist/errors.js +21 -0
  118. package/dist/errors.js.map +1 -0
  119. package/dist/execution.d.ts +82 -0
  120. package/dist/execution.d.ts.map +1 -0
  121. package/dist/execution.js +182 -0
  122. package/dist/execution.js.map +1 -0
  123. package/dist/execution.test.d.ts +2 -0
  124. package/dist/execution.test.d.ts.map +1 -0
  125. package/dist/execution.test.js +556 -0
  126. package/dist/execution.test.js.map +1 -0
  127. package/dist/index.d.ts +8 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +6 -0
  130. package/dist/index.js.map +1 -0
  131. package/dist/internal.d.ts +12 -0
  132. package/dist/internal.d.ts.map +1 -0
  133. package/dist/internal.js +5 -0
  134. package/dist/internal.js.map +1 -0
  135. package/dist/practices/01-remote-workflow.d.ts +2 -0
  136. package/dist/practices/01-remote-workflow.d.ts.map +1 -0
  137. package/dist/practices/01-remote-workflow.js +69 -0
  138. package/dist/practices/01-remote-workflow.js.map +1 -0
  139. package/dist/practices/01-remote.d.ts +2 -0
  140. package/dist/practices/01-remote.d.ts.map +1 -0
  141. package/dist/practices/01-remote.js +87 -0
  142. package/dist/practices/01-remote.js.map +1 -0
  143. package/dist/practices/02-local.d.ts +2 -0
  144. package/dist/practices/02-local.d.ts.map +1 -0
  145. package/dist/practices/02-local.js +84 -0
  146. package/dist/practices/02-local.js.map +1 -0
  147. package/dist/practices/03-local-retry.d.ts +2 -0
  148. package/dist/practices/03-local-retry.d.ts.map +1 -0
  149. package/dist/practices/03-local-retry.js +85 -0
  150. package/dist/practices/03-local-retry.js.map +1 -0
  151. package/dist/practices/04-scheduler-dispose.d.ts +2 -0
  152. package/dist/practices/04-scheduler-dispose.d.ts.map +1 -0
  153. package/dist/practices/04-scheduler-dispose.js +65 -0
  154. package/dist/practices/04-scheduler-dispose.js.map +1 -0
  155. package/dist/practices/05-router.d.ts +2 -0
  156. package/dist/practices/05-router.d.ts.map +1 -0
  157. package/dist/practices/05-router.js +80 -0
  158. package/dist/practices/05-router.js.map +1 -0
  159. package/dist/registry.d.ts +33 -0
  160. package/dist/registry.d.ts.map +1 -0
  161. package/dist/registry.js +54 -0
  162. package/dist/registry.js.map +1 -0
  163. package/dist/registry.test.d.ts +2 -0
  164. package/dist/registry.test.d.ts.map +1 -0
  165. package/dist/registry.test.js +95 -0
  166. package/dist/registry.test.js.map +1 -0
  167. package/dist/scheduler.d.ts +22 -0
  168. package/dist/scheduler.d.ts.map +1 -0
  169. package/dist/scheduler.js +117 -0
  170. package/dist/scheduler.js.map +1 -0
  171. package/dist/tasks/index.d.ts +4 -0
  172. package/dist/tasks/index.d.ts.map +1 -0
  173. package/dist/tasks/index.js +5 -0
  174. package/dist/tasks/index.js.map +1 -0
  175. package/dist/tasks/local-task.d.ts +6 -0
  176. package/dist/tasks/local-task.d.ts.map +1 -0
  177. package/dist/tasks/local-task.js +95 -0
  178. package/dist/tasks/local-task.js.map +1 -0
  179. package/dist/tasks/remote-task.d.ts +11 -0
  180. package/dist/tasks/remote-task.d.ts.map +1 -0
  181. package/dist/tasks/remote-task.js +213 -0
  182. package/dist/tasks/remote-task.js.map +1 -0
  183. package/dist/tasks/shared.d.ts +8 -0
  184. package/dist/tasks/shared.d.ts.map +1 -0
  185. package/dist/tasks/shared.js +41 -0
  186. package/dist/tasks/shared.js.map +1 -0
  187. package/dist/testing/connection.d.ts +7 -0
  188. package/dist/testing/connection.d.ts.map +1 -0
  189. package/dist/testing/connection.js +38 -0
  190. package/dist/testing/connection.js.map +1 -0
  191. package/dist/types/config.d.ts +44 -0
  192. package/dist/types/config.d.ts.map +1 -0
  193. package/dist/types/config.js +3 -0
  194. package/dist/types/config.js.map +1 -0
  195. package/dist/types/context.d.ts +18 -0
  196. package/dist/types/context.d.ts.map +1 -0
  197. package/dist/types/context.js +4 -0
  198. package/dist/types/context.js.map +1 -0
  199. package/dist/types/events.d.ts +43 -0
  200. package/dist/types/events.d.ts.map +1 -0
  201. package/dist/types/events.js +3 -0
  202. package/dist/types/events.js.map +1 -0
  203. package/dist/types/index.d.ts +6 -0
  204. package/dist/types/index.d.ts.map +1 -0
  205. package/dist/types/index.js +3 -0
  206. package/dist/types/index.js.map +1 -0
  207. package/dist/types/task-items.d.ts +12 -0
  208. package/dist/types/task-items.d.ts.map +1 -0
  209. package/dist/types/task-items.js +3 -0
  210. package/dist/types/task-items.js.map +1 -0
  211. package/dist/types/utils.d.ts +4 -0
  212. package/dist/types/utils.d.ts.map +1 -0
  213. package/dist/types/utils.js +8 -0
  214. package/dist/types/utils.js.map +1 -0
  215. package/dist/worker.d.ts +61 -0
  216. package/dist/worker.d.ts.map +1 -0
  217. package/dist/worker.js +206 -0
  218. package/dist/worker.js.map +1 -0
  219. package/dist/worker.test.d.ts +2 -0
  220. package/dist/worker.test.d.ts.map +1 -0
  221. package/dist/worker.test.js +1163 -0
  222. package/dist/worker.test.js.map +1 -0
  223. package/dist/workflow.d.ts +44 -0
  224. package/dist/workflow.d.ts.map +1 -0
  225. package/dist/workflow.js +21 -0
  226. package/dist/workflow.js.map +1 -0
  227. package/dist/workflow.test.d.ts +2 -0
  228. package/dist/workflow.test.d.ts.map +1 -0
  229. package/dist/workflow.test.js +73 -0
  230. package/dist/workflow.test.js.map +1 -0
  231. package/nodemon.json +6 -0
  232. package/package.json +63 -0
  233. package/scripts/migrate.ts +11 -0
  234. package/src/backend.ts +133 -0
  235. package/src/chaos.test.ts +108 -0
  236. package/src/client.test.ts +297 -0
  237. package/src/client.ts +331 -0
  238. package/src/config.test.ts +23 -0
  239. package/src/config.ts +35 -0
  240. package/src/core/duration.test.ts +326 -0
  241. package/src/core/duration.ts +86 -0
  242. package/src/core/error.test.ts +77 -0
  243. package/src/core/error.ts +30 -0
  244. package/src/core/json.ts +2 -0
  245. package/src/core/result.test.ts +13 -0
  246. package/src/core/result.ts +29 -0
  247. package/src/core/retry.test.ts +41 -0
  248. package/src/core/retry.ts +29 -0
  249. package/src/core/schema.ts +74 -0
  250. package/src/core/step.test.ts +362 -0
  251. package/src/core/step.ts +152 -0
  252. package/src/core/workflow.test.ts +184 -0
  253. package/src/core/workflow.ts +127 -0
  254. package/src/database/backend.test.ts +16 -0
  255. package/src/database/backend.testsuite.ts +1376 -0
  256. package/src/database/backend.ts +655 -0
  257. package/src/database/base.ts +23 -0
  258. package/src/database/migrations/20251212000000_0_init.ts +10 -0
  259. package/src/database/migrations/20251212000000_1_tables.ts +54 -0
  260. package/src/database/migrations/20251212000000_2_fk.ts +46 -0
  261. package/src/database/migrations/20251212000000_3_indexes.ts +82 -0
  262. package/src/database/pubsub.test.ts +92 -0
  263. package/src/database/pubsub.ts +92 -0
  264. package/src/execution.test.ts +508 -0
  265. package/src/execution.ts +291 -0
  266. package/src/index.ts +7 -0
  267. package/src/internal.ts +11 -0
  268. package/src/practices/01-remote-workflow.ts +61 -0
  269. package/src/registry.test.ts +122 -0
  270. package/src/registry.ts +65 -0
  271. package/src/testing/connection.ts +44 -0
  272. package/src/worker.test.ts +1138 -0
  273. package/src/worker.ts +281 -0
  274. package/src/workflow.test.ts +68 -0
  275. package/src/workflow.ts +84 -0
  276. package/table_ddl.sql +60 -0
  277. package/templates/openworkflow.config.ts +22 -0
  278. package/tsconfig.json +40 -0
  279. package/tsconfig.test.json +4 -0
  280. package/vite.config.ts +13 -0
@@ -0,0 +1,184 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import type { StandardSchemaV1 } from "./schema";
3
+ import { DEFAULT_WORKFLOW_RESULT_CONFIG, isTerminalStatus, validateInput } from "./workflow";
4
+
5
+ describe("validateInput", () => {
6
+ test("returns success with input when no schema provided (null)", async () => {
7
+ const input = { name: "test", value: 42 };
8
+ const result = await validateInput(null, input);
9
+
10
+ expect(result.success).toBe(true);
11
+ if (result.success) {
12
+ expect(result.value).toBe(input);
13
+ }
14
+ });
15
+
16
+ test("returns success with input when no schema provided (undefined)", async () => {
17
+ const input = "string input";
18
+ const result = await validateInput(undefined, input);
19
+
20
+ expect(result.success).toBe(true);
21
+ if (result.success) {
22
+ expect(result.value).toBe(input);
23
+ }
24
+ });
25
+
26
+ test("validates input successfully against schema", async () => {
27
+ const schema = createMockSchema<{ name: string }>({
28
+ validate: (input) => ({ value: input as { name: string } }),
29
+ });
30
+ const input = { name: "test" };
31
+
32
+ const result = await validateInput(schema, input);
33
+
34
+ expect(result.success).toBe(true);
35
+ if (result.success) {
36
+ expect(result.value).toEqual({ name: "test" });
37
+ }
38
+ });
39
+
40
+ test("transforms input using schema", async () => {
41
+ const schema = createMockSchema<string, number>({
42
+ validate: (input) => ({ value: Number.parseInt(input as string, 10) }),
43
+ });
44
+
45
+ const result = await validateInput(schema, "42");
46
+
47
+ expect(result.success).toBe(true);
48
+ if (result.success) {
49
+ expect(result.value).toBe(42);
50
+ }
51
+ });
52
+
53
+ test("returns failure with error message when validation fails", async () => {
54
+ const schema = createMockSchema<string>({
55
+ validate: () => ({
56
+ issues: [{ message: "Invalid input" }],
57
+ }),
58
+ });
59
+
60
+ const result = await validateInput(schema, "bad input");
61
+
62
+ expect(result.success).toBe(false);
63
+ if (!result.success) {
64
+ expect(result.error).toBe("Invalid input");
65
+ }
66
+ });
67
+
68
+ test("combines multiple validation error messages", async () => {
69
+ const schema = createMockSchema<{ email: string; age: number }>({
70
+ validate: () => ({
71
+ issues: [{ message: "Invalid email format" }, { message: "Age must be positive" }],
72
+ }),
73
+ });
74
+
75
+ const result = await validateInput(schema, {
76
+ email: "invalid",
77
+ age: -5,
78
+ });
79
+
80
+ expect(result.success).toBe(false);
81
+ if (!result.success) {
82
+ expect(result.error).toBe("Invalid email format; Age must be positive");
83
+ }
84
+ });
85
+
86
+ test("returns generic message when issues array is empty", async () => {
87
+ const schema = createMockSchema<string>({
88
+ validate: () => ({
89
+ issues: [],
90
+ }),
91
+ });
92
+
93
+ const result = await validateInput(schema, "test");
94
+
95
+ expect(result.success).toBe(false);
96
+ if (!result.success) {
97
+ expect(result.error).toBe("Validation failed");
98
+ }
99
+ });
100
+
101
+ test("handles async schema validation", async () => {
102
+ const schema = createMockSchema<string>({
103
+ validate: async (input) => {
104
+ await new Promise((resolve) => setTimeout(resolve, 1));
105
+ return { value: (input as string).toUpperCase() };
106
+ },
107
+ });
108
+
109
+ const result = await validateInput(schema, "hello");
110
+
111
+ expect(result.success).toBe(true);
112
+ if (result.success) {
113
+ expect(result.value).toBe("HELLO");
114
+ }
115
+ });
116
+
117
+ test("handles undefined input when no schema", async () => {
118
+ // eslint-disable-next-line unicorn/no-useless-undefined
119
+ const result = await validateInput(null, undefined);
120
+
121
+ expect(result.success).toBe(true);
122
+ if (result.success) {
123
+ expect(result.value).toBeUndefined();
124
+ }
125
+ });
126
+ });
127
+
128
+ describe("isTerminalStatus", () => {
129
+ test("returns true for 'completed' status", () => {
130
+ expect(isTerminalStatus("completed")).toBe(true);
131
+ });
132
+
133
+ test("returns true for 'succeeded' status (deprecated)", () => {
134
+ expect(isTerminalStatus("succeeded")).toBe(true);
135
+ });
136
+
137
+ test("returns true for 'failed' status", () => {
138
+ expect(isTerminalStatus("failed")).toBe(true);
139
+ });
140
+
141
+ test("returns true for 'canceled' status", () => {
142
+ expect(isTerminalStatus("canceled")).toBe(true);
143
+ });
144
+
145
+ test("returns false for 'pending' status", () => {
146
+ expect(isTerminalStatus("pending")).toBe(false);
147
+ });
148
+
149
+ test("returns false for 'running' status", () => {
150
+ expect(isTerminalStatus("running")).toBe(false);
151
+ });
152
+
153
+ test("returns false for 'sleeping' status", () => {
154
+ expect(isTerminalStatus("sleeping")).toBe(false);
155
+ });
156
+
157
+ test("returns false for unknown status strings", () => {
158
+ expect(isTerminalStatus("unknown")).toBe(false);
159
+ expect(isTerminalStatus("")).toBe(false);
160
+ expect(isTerminalStatus("COMPLETED")).toBe(false);
161
+ });
162
+ });
163
+
164
+ describe("DEFAULT_WORKFLOW_RESULT_CONFIG", () => {
165
+ test("has expected poll interval", () => {
166
+ expect(DEFAULT_WORKFLOW_RESULT_CONFIG.pollIntervalMs).toBe(1000);
167
+ });
168
+
169
+ test("has expected timeout (5 minutes)", () => {
170
+ expect(DEFAULT_WORKFLOW_RESULT_CONFIG.timeoutMs).toBe(5 * 60 * 1000);
171
+ });
172
+ });
173
+
174
+ function createMockSchema<I, O = I>(options: {
175
+ validate: (input: unknown) => StandardSchemaV1.Result<O> | Promise<StandardSchemaV1.Result<O>>;
176
+ }): StandardSchemaV1<I, O> {
177
+ return {
178
+ "~standard": {
179
+ version: 1,
180
+ vendor: "test",
181
+ validate: options.validate,
182
+ },
183
+ };
184
+ }
@@ -0,0 +1,127 @@
1
+ import type { SerializedError } from "./error";
2
+ import type { JsonValue } from "./json";
3
+ import type { StandardSchemaV1 } from "./schema";
4
+
5
+ /**
6
+ * Status of a workflow run through its lifecycle.
7
+ */
8
+ export type WorkflowRunStatus =
9
+ | "pending"
10
+ | "running"
11
+ | "sleeping"
12
+ | "succeeded" // deprecated in favor of 'completed'
13
+ | "completed"
14
+ | "failed"
15
+ | "canceled";
16
+
17
+ /**
18
+ * WorkflowRun represents a single execution instance of a workflow.
19
+ */
20
+ export interface WorkflowRun {
21
+ namespaceId: string;
22
+ id: string;
23
+ workflowName: string;
24
+ version: string | null;
25
+ status: WorkflowRunStatus;
26
+ idempotencyKey: string | null;
27
+ config: JsonValue; // user-defined config
28
+ context: JsonValue | null; // runtime execution metadata
29
+ input: JsonValue | null;
30
+ output: JsonValue | null;
31
+ error: SerializedError | null;
32
+ attempts: number;
33
+ parentStepAttemptNamespaceId: string | null;
34
+ parentStepAttemptId: string | null;
35
+ workerId: string | null;
36
+ availableAt: Date | null;
37
+ deadlineAt: Date | null;
38
+ startedAt: Date | null;
39
+ finishedAt: Date | null;
40
+ createdAt: Date;
41
+ updatedAt: Date;
42
+ }
43
+
44
+ /**
45
+ * Infers the input type from a Standard Schema.
46
+ */
47
+ export type SchemaInput<TSchema, Fallback> = TSchema extends StandardSchemaV1
48
+ ? StandardSchemaV1.InferInput<TSchema>
49
+ : Fallback;
50
+
51
+ /**
52
+ * Infers the output type from a Standard Schema.
53
+ */
54
+ export type SchemaOutput<TSchema, Fallback> = TSchema extends StandardSchemaV1
55
+ ? StandardSchemaV1.InferOutput<TSchema>
56
+ : Fallback;
57
+
58
+ /**
59
+ * Result of input validation - either success with a value or failure with an
60
+ * error message.
61
+ */
62
+ export type ValidationResult<T> = { success: true; value: T } | { success: false; error: string };
63
+
64
+ /**
65
+ * Default configuration for result polling when awaiting workflow completion.
66
+ */
67
+ export const DEFAULT_WORKFLOW_RESULT_CONFIG = {
68
+ /** Polling interval in milliseconds (1 second) */
69
+ pollIntervalMs: 1000,
70
+ /** Timeout in milliseconds (5 minutes) */
71
+ timeoutMs: 5 * 60 * 1000,
72
+ } as const;
73
+
74
+ /**
75
+ * Validate input against a Standard Schema. Pure async function that validates
76
+ * input and returns a ValidationResult.
77
+ * @param schema - The Standard Schema to validate against (or null/undefined
78
+ * for no validation)
79
+ * @param input - The input value to validate
80
+ * @returns A ValidationResult containing either the validated value or an error
81
+ * message
82
+ */
83
+ export async function validateInput<RunInput, Input>(
84
+ schema: StandardSchemaV1<RunInput, Input> | null | undefined,
85
+ input: RunInput | undefined,
86
+ ): Promise<ValidationResult<Input>> {
87
+ // No schema means no validation - pass through as-is
88
+ if (!schema) {
89
+ return {
90
+ success: true,
91
+ value: input as unknown as Input,
92
+ };
93
+ }
94
+
95
+ // Validate using Standard Schema v1 protocol https://standardschema.dev
96
+ const result = schema["~standard"].validate(input);
97
+ const resolved = await Promise.resolve(result);
98
+
99
+ if (resolved.issues) {
100
+ const messages =
101
+ resolved.issues.length > 0
102
+ ? resolved.issues.map((issue) => issue.message).join("; ")
103
+ : "Validation failed";
104
+ return {
105
+ success: false,
106
+ error: messages,
107
+ };
108
+ }
109
+
110
+ return {
111
+ success: true,
112
+ value: resolved.value,
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Check if a workflow run status represents a terminal state.
118
+ * @param status - The workflow run status
119
+ * @returns True if the status is terminal (completed, failed, or canceled)
120
+ */
121
+ export function isTerminalStatus(
122
+ status: string,
123
+ ): status is "succeeded" | "completed" | "failed" | "canceled" {
124
+ return (
125
+ status === "succeeded" || status === "completed" || status === "failed" || status === "canceled"
126
+ );
127
+ }
@@ -0,0 +1,16 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { KNEX_GLOBAL_CONFIG } from "../testing/connection";
3
+ import { BackendPostgres } from "./backend";
4
+ import { testBackend } from "./backend.testsuite";
5
+
6
+ testBackend({
7
+ setup: async () => {
8
+ return await BackendPostgres.connect(KNEX_GLOBAL_CONFIG, {
9
+ namespaceId: randomUUID(),
10
+ runMigrations: false,
11
+ });
12
+ },
13
+ teardown: async (backend) => {
14
+ await (backend as BackendPostgres).stop();
15
+ },
16
+ });