@aikirun/task 0.5.3 → 0.6.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.
- package/dist/index.d.ts +10 -1
- package/dist/index.js +30 -31
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,15 @@ type PathFromObjectInternal<T, IncludeArrayKeys extends boolean> = And<[
|
|
|
22
22
|
type ExtractObjectType<T> = T extends object ? T : never;
|
|
23
23
|
type TypeOfValueAtPath<T extends object, Path extends PathFromObject<T>> = Path extends keyof T ? T[Path] : Path extends `${infer First}.${infer Rest}` ? First extends keyof T ? undefined extends T[First] ? Rest extends PathFromObject<ExtractObjectType<T[First]>> ? TypeOfValueAtPath<ExtractObjectType<T[First]>, Rest> | undefined : never : Rest extends PathFromObject<ExtractObjectType<T[First]>> ? TypeOfValueAtPath<ExtractObjectType<T[First]>, Rest> : never : never : never;
|
|
24
24
|
|
|
25
|
+
interface Schema<Data> {
|
|
26
|
+
parse: (data: unknown) => Data;
|
|
27
|
+
}
|
|
28
|
+
interface EventDefinition<Data> {
|
|
29
|
+
_type: Data;
|
|
30
|
+
schema?: Schema<Data>;
|
|
31
|
+
}
|
|
32
|
+
type EventsDefinition = Record<string, EventDefinition<unknown>>;
|
|
33
|
+
|
|
25
34
|
/**
|
|
26
35
|
* Defines a durable task with deterministic execution and automatic retries.
|
|
27
36
|
*
|
|
@@ -82,7 +91,7 @@ interface TaskBuilder<Input, Output> {
|
|
|
82
91
|
interface Task<Input, Output> {
|
|
83
92
|
id: TaskId;
|
|
84
93
|
with(): TaskBuilder<Input, Output>;
|
|
85
|
-
start: <WorkflowInput, WorkflowOutput>(run: WorkflowRunContext<WorkflowInput, WorkflowOutput>, ...args: Input extends null ? [] : [Input]) => Promise<Output>;
|
|
94
|
+
start: <WorkflowInput, WorkflowOutput>(run: WorkflowRunContext<WorkflowInput, WorkflowOutput, EventsDefinition>, ...args: Input extends null ? [] : [Input]) => Promise<Output>;
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
export { type Task, type TaskParams, task };
|
package/dist/index.js
CHANGED
|
@@ -142,6 +142,7 @@ function getRetryParams(attempts, strategy) {
|
|
|
142
142
|
// task.ts
|
|
143
143
|
import { INTERNAL } from "@aikirun/types/symbols";
|
|
144
144
|
import { TaskFailedError } from "@aikirun/types/task";
|
|
145
|
+
import { WorkflowRunSuspendedError } from "@aikirun/types/workflow-run";
|
|
145
146
|
function task(params) {
|
|
146
147
|
return new TaskImpl(params);
|
|
147
148
|
}
|
|
@@ -171,22 +172,25 @@ var TaskImpl = class _TaskImpl {
|
|
|
171
172
|
if (taskState.status === "completed") {
|
|
172
173
|
return taskState.output;
|
|
173
174
|
}
|
|
175
|
+
if (taskState.status === "failed") {
|
|
176
|
+
throw new TaskFailedError(path, taskState.attempts, taskState.error.message);
|
|
177
|
+
}
|
|
174
178
|
const logger = run.logger.child({
|
|
175
179
|
"aiki.component": "task-execution",
|
|
176
180
|
"aiki.taskPath": path
|
|
177
181
|
});
|
|
178
182
|
let attempts = 0;
|
|
179
183
|
const retryStrategy = this.params.opts?.retry ?? { type: "never" };
|
|
180
|
-
if ("
|
|
184
|
+
if (taskState.status !== "none") {
|
|
181
185
|
this.assertRetryAllowed(path, taskState.attempts, retryStrategy, logger);
|
|
182
|
-
logger.
|
|
186
|
+
logger.debug("Retrying task", {
|
|
183
187
|
"aiki.attempts": taskState.attempts,
|
|
184
188
|
"aiki.taskStatus": taskState.status
|
|
185
189
|
});
|
|
186
190
|
attempts = taskState.attempts;
|
|
187
191
|
}
|
|
188
|
-
if (taskState.status === "
|
|
189
|
-
|
|
192
|
+
if (taskState.status === "awaiting_retry" && handle.run.state.status === "running") {
|
|
193
|
+
throw new WorkflowRunSuspendedError(run.id);
|
|
190
194
|
}
|
|
191
195
|
attempts++;
|
|
192
196
|
logger.info("Starting task", { "aiki.attempts": attempts });
|
|
@@ -199,37 +203,41 @@ var TaskImpl = class _TaskImpl {
|
|
|
199
203
|
async tryExecuteTask(run, input, path, retryStrategy, currentAttempt, logger) {
|
|
200
204
|
let attempts = currentAttempt;
|
|
201
205
|
while (true) {
|
|
202
|
-
const attemptedAt = Date.now();
|
|
203
206
|
try {
|
|
204
207
|
const output = await this.params.handler(input);
|
|
205
208
|
return { output, lastAttempt: attempts };
|
|
206
209
|
} catch (error) {
|
|
207
210
|
const serializableError = createSerializableError(error);
|
|
208
|
-
const taskFailedState = {
|
|
209
|
-
status: "failed",
|
|
210
|
-
reason: serializableError.message,
|
|
211
|
-
attempts,
|
|
212
|
-
attemptedAt,
|
|
213
|
-
error: serializableError
|
|
214
|
-
};
|
|
215
211
|
const retryParams = getRetryParams(attempts, retryStrategy);
|
|
216
212
|
if (!retryParams.retriesLeft) {
|
|
217
|
-
await run[INTERNAL].handle[INTERNAL].transitionTaskState(path, taskFailedState);
|
|
218
213
|
logger.error("Task failed", {
|
|
219
214
|
"aiki.attempts": attempts,
|
|
220
|
-
"aiki.reason":
|
|
215
|
+
"aiki.reason": serializableError.message
|
|
216
|
+
});
|
|
217
|
+
await run[INTERNAL].handle[INTERNAL].transitionTaskState(path, {
|
|
218
|
+
status: "failed",
|
|
219
|
+
attempts,
|
|
220
|
+
error: serializableError
|
|
221
221
|
});
|
|
222
|
-
throw new TaskFailedError(path, attempts,
|
|
222
|
+
throw new TaskFailedError(path, attempts, serializableError.message);
|
|
223
223
|
}
|
|
224
|
-
|
|
225
|
-
await run[INTERNAL].handle[INTERNAL].transitionTaskState(path, { ...taskFailedState, nextAttemptAt });
|
|
226
|
-
logger.debug("Task failed. Retrying", {
|
|
224
|
+
logger.debug("Task failed. It will be retried", {
|
|
227
225
|
"aiki.attempts": attempts,
|
|
228
|
-
"aiki.
|
|
229
|
-
"aiki.reason":
|
|
226
|
+
"aiki.nextAttemptInMs": retryParams.delayMs,
|
|
227
|
+
"aiki.reason": serializableError.message
|
|
230
228
|
});
|
|
231
|
-
|
|
232
|
-
|
|
229
|
+
if (retryParams.delayMs <= run[INTERNAL].options.spinThresholdMs) {
|
|
230
|
+
await delay(retryParams.delayMs);
|
|
231
|
+
attempts++;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
await run[INTERNAL].handle[INTERNAL].transitionTaskState(path, {
|
|
235
|
+
status: "awaiting_retry",
|
|
236
|
+
attempts,
|
|
237
|
+
error: serializableError,
|
|
238
|
+
nextAttemptInMs: retryParams.delayMs
|
|
239
|
+
});
|
|
240
|
+
throw new WorkflowRunSuspendedError(run.id);
|
|
233
241
|
}
|
|
234
242
|
}
|
|
235
243
|
}
|
|
@@ -242,15 +250,6 @@ var TaskImpl = class _TaskImpl {
|
|
|
242
250
|
throw new TaskFailedError(path, attempts, "Task retry not allowed");
|
|
243
251
|
}
|
|
244
252
|
}
|
|
245
|
-
async delayIfNecessary(taskState) {
|
|
246
|
-
if (taskState.nextAttemptAt !== void 0) {
|
|
247
|
-
const now = Date.now();
|
|
248
|
-
const remainingDelay = Math.max(0, taskState.nextAttemptAt - now);
|
|
249
|
-
if (remainingDelay > 0) {
|
|
250
|
-
await delay(remainingDelay);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
253
|
async getPath(input) {
|
|
255
254
|
const inputHash = await sha256(stableStringify(input));
|
|
256
255
|
const taskPath = this.params.opts?.idempotencyKey ? `${this.id}/${inputHash}/${this.params.opts.idempotencyKey}` : `${this.id}/${inputHash}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikirun/task",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Task SDK for Aiki - define reliable tasks with automatic retries, idempotency, and error handling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"build": "tsup"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@aikirun/types": "0.
|
|
22
|
-
"@aikirun/workflow": "0.
|
|
21
|
+
"@aikirun/types": "0.6.0",
|
|
22
|
+
"@aikirun/workflow": "0.6.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|