@yieldstar/core 0.1.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.js +151 -0
- package/package.json +16 -0
- package/src/base/heap.ts +18 -0
- package/src/base/invoker.ts +7 -0
- package/src/base/runtime.ts +7 -0
- package/src/base/scheduler.ts +7 -0
- package/src/base/step.ts +96 -0
- package/src/base/workflow.ts +15 -0
- package/src/index.ts +24 -0
- package/src/lib/workflow-runner.ts +79 -0
- package/tsconfig.json +26 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/base/step.ts
|
|
3
|
+
class StepResponse {
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
class StepKey extends StepResponse {
|
|
7
|
+
static type = "step-key";
|
|
8
|
+
type = "step-key";
|
|
9
|
+
key;
|
|
10
|
+
constructor(key) {
|
|
11
|
+
super();
|
|
12
|
+
this.key = key;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class StepCacheCheck extends StepResponse {
|
|
17
|
+
static type = "cache-check";
|
|
18
|
+
type = "cache-check";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class StepInvalid extends StepResponse {
|
|
22
|
+
static type = "step-invalid";
|
|
23
|
+
type = "step-invalid";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class StepResult extends StepResponse {
|
|
27
|
+
static type = "step-result";
|
|
28
|
+
type = "step-result";
|
|
29
|
+
result;
|
|
30
|
+
constructor(result) {
|
|
31
|
+
super();
|
|
32
|
+
this.result = result;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class WorkflowResult extends StepResponse {
|
|
37
|
+
static type = "workflow-result";
|
|
38
|
+
type = "workflow-result";
|
|
39
|
+
result;
|
|
40
|
+
constructor(result) {
|
|
41
|
+
super();
|
|
42
|
+
this.result = result;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class WorkflowDelay extends StepResponse {
|
|
47
|
+
static type = "workflow-delay";
|
|
48
|
+
type = "workflow-delay";
|
|
49
|
+
resumeIn;
|
|
50
|
+
constructor(resumeIn) {
|
|
51
|
+
super();
|
|
52
|
+
this.resumeIn = resumeIn;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class WorkflowRestart extends StepResponse {
|
|
57
|
+
static type = "workflow-restart";
|
|
58
|
+
type = "workflow-restart";
|
|
59
|
+
constructor() {
|
|
60
|
+
super();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class StepError extends StepResponse {
|
|
65
|
+
static type = "step-error";
|
|
66
|
+
type = "step-error";
|
|
67
|
+
err;
|
|
68
|
+
maxAttempts;
|
|
69
|
+
retryInterval;
|
|
70
|
+
backOffStrategy;
|
|
71
|
+
constructor(err, opts) {
|
|
72
|
+
super();
|
|
73
|
+
this.err = err;
|
|
74
|
+
this.maxAttempts = opts?.maxAttempts ? opts.maxAttempts : 1;
|
|
75
|
+
this.retryInterval = opts?.retryInterval ? opts.retryInterval : 60000;
|
|
76
|
+
this.backOffStrategy = opts?.backoffStrategy ? opts.backoffStrategy : "decay";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class StepDelay extends StepResponse {
|
|
81
|
+
static type = "step-delay";
|
|
82
|
+
type = "step-delay";
|
|
83
|
+
resumeIn;
|
|
84
|
+
constructor(resumeIn) {
|
|
85
|
+
super();
|
|
86
|
+
this.resumeIn = resumeIn;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// src/lib/workflow-runner.ts
|
|
90
|
+
class WorkflowRunner {
|
|
91
|
+
heapClient;
|
|
92
|
+
schedulerClient;
|
|
93
|
+
router;
|
|
94
|
+
constructor(params) {
|
|
95
|
+
this.heapClient = params.heapClient;
|
|
96
|
+
this.schedulerClient = params.schedulerClient;
|
|
97
|
+
this.router = params.router;
|
|
98
|
+
}
|
|
99
|
+
run = async (task) => {
|
|
100
|
+
const { workflowId, executionId } = task;
|
|
101
|
+
const workflow = this.router[workflowId];
|
|
102
|
+
if (!workflow) {
|
|
103
|
+
throw new Error(`No workflow registered for "${workflowId}"`);
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const response = await this.runWorkflows({ executionId, workflow });
|
|
107
|
+
switch (response.type) {
|
|
108
|
+
case "workflow-result":
|
|
109
|
+
return response;
|
|
110
|
+
case "workflow-delay":
|
|
111
|
+
this.schedulerClient.requestWakeUp({
|
|
112
|
+
workflowId,
|
|
113
|
+
executionId,
|
|
114
|
+
resumeIn: response.resumeIn
|
|
115
|
+
});
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
} catch (err) {
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
async runWorkflows(params) {
|
|
123
|
+
const { executionId, workflow } = params;
|
|
124
|
+
const workflowIterator = workflow({
|
|
125
|
+
heapClient: this.heapClient,
|
|
126
|
+
executionId
|
|
127
|
+
});
|
|
128
|
+
const iteratorResult = await workflowIterator.next();
|
|
129
|
+
const stageResponse = iteratorResult.value;
|
|
130
|
+
if (iteratorResult.done) {
|
|
131
|
+
return stageResponse;
|
|
132
|
+
}
|
|
133
|
+
if (stageResponse instanceof StepDelay) {
|
|
134
|
+
return new WorkflowDelay(stageResponse.resumeIn - Date.now());
|
|
135
|
+
}
|
|
136
|
+
throw new Error("Workflow runner critical error");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
WorkflowRunner,
|
|
141
|
+
WorkflowResult,
|
|
142
|
+
WorkflowRestart,
|
|
143
|
+
WorkflowDelay,
|
|
144
|
+
StepResult,
|
|
145
|
+
StepResponse,
|
|
146
|
+
StepKey,
|
|
147
|
+
StepInvalid,
|
|
148
|
+
StepError,
|
|
149
|
+
StepDelay,
|
|
150
|
+
StepCacheCheck
|
|
151
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yieldstar/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "bun build src/index.ts --outfile dist/index.js --target bun --packages=external"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/bun": "latest"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"typescript": "^5.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/base/heap.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type HeapRecord = {
|
|
2
|
+
stepResponseJson: string;
|
|
3
|
+
meta: { attempt: number; done: boolean };
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export abstract class HeapClient {
|
|
7
|
+
abstract readStep(params: {
|
|
8
|
+
executionId: string;
|
|
9
|
+
stepKey: string;
|
|
10
|
+
}): Promise<HeapRecord | null>;
|
|
11
|
+
abstract writeStep(params: {
|
|
12
|
+
executionId: string;
|
|
13
|
+
stepKey: string;
|
|
14
|
+
stepAttempt: number;
|
|
15
|
+
stepDone: boolean;
|
|
16
|
+
stepResponseJson: string;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
}
|
package/src/base/step.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export abstract class StepResponse {
|
|
2
|
+
abstract type: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export class StepKey extends StepResponse {
|
|
6
|
+
static type = "step-key";
|
|
7
|
+
readonly type = "step-key";
|
|
8
|
+
key: string | null;
|
|
9
|
+
constructor(key: string | null) {
|
|
10
|
+
super();
|
|
11
|
+
this.key = key;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class StepCacheCheck extends StepResponse {
|
|
16
|
+
static type = "cache-check";
|
|
17
|
+
readonly type = "cache-check";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class StepInvalid extends StepResponse {
|
|
21
|
+
static type = "step-invalid";
|
|
22
|
+
readonly type = "step-invalid";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class StepResult extends StepResponse {
|
|
26
|
+
static type = "step-result";
|
|
27
|
+
readonly type = "step-result";
|
|
28
|
+
result: any;
|
|
29
|
+
constructor(result: any) {
|
|
30
|
+
super();
|
|
31
|
+
this.result = result;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class WorkflowResult<T extends any> extends StepResponse {
|
|
36
|
+
static type = "workflow-result";
|
|
37
|
+
readonly type = "workflow-result";
|
|
38
|
+
result: T;
|
|
39
|
+
constructor(result: T) {
|
|
40
|
+
super();
|
|
41
|
+
this.result = result;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class WorkflowDelay extends StepResponse {
|
|
46
|
+
static type = "workflow-delay";
|
|
47
|
+
readonly type = "workflow-delay";
|
|
48
|
+
resumeIn: number;
|
|
49
|
+
constructor(resumeIn: number) {
|
|
50
|
+
super();
|
|
51
|
+
this.resumeIn = resumeIn;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class WorkflowRestart extends StepResponse {
|
|
56
|
+
static type = "workflow-restart";
|
|
57
|
+
readonly type = "workflow-restart";
|
|
58
|
+
constructor() {
|
|
59
|
+
super();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class StepError extends StepResponse {
|
|
64
|
+
static type = "step-error";
|
|
65
|
+
readonly type = "step-error";
|
|
66
|
+
err: any;
|
|
67
|
+
maxAttempts: number;
|
|
68
|
+
retryInterval: number;
|
|
69
|
+
backOffStrategy: "linear" | "decay";
|
|
70
|
+
constructor(
|
|
71
|
+
err: any,
|
|
72
|
+
opts?: {
|
|
73
|
+
maxAttempts: number;
|
|
74
|
+
retryInterval?: number;
|
|
75
|
+
backoffStrategy?: "linear" | "decay";
|
|
76
|
+
}
|
|
77
|
+
) {
|
|
78
|
+
super();
|
|
79
|
+
this.err = err;
|
|
80
|
+
this.maxAttempts = opts?.maxAttempts ? opts.maxAttempts : 1;
|
|
81
|
+
this.retryInterval = opts?.retryInterval ? opts.retryInterval : 60000;
|
|
82
|
+
this.backOffStrategy = opts?.backoffStrategy
|
|
83
|
+
? opts.backoffStrategy
|
|
84
|
+
: "decay";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class StepDelay extends StepResponse {
|
|
89
|
+
static type = "step-delay";
|
|
90
|
+
readonly type = "step-delay";
|
|
91
|
+
resumeIn: number;
|
|
92
|
+
constructor(resumeIn: number) {
|
|
93
|
+
super();
|
|
94
|
+
this.resumeIn = resumeIn;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HeapClient } from "./heap";
|
|
2
|
+
import { StepResponse, WorkflowResult } from "./step";
|
|
3
|
+
|
|
4
|
+
export type WorkflowGenerator<T = any> = (params: {
|
|
5
|
+
executionId: string;
|
|
6
|
+
heapClient: HeapClient;
|
|
7
|
+
}) => AsyncGenerator<StepResponse, WorkflowResult<T>, StepResponse>;
|
|
8
|
+
|
|
9
|
+
export type WorkflowGeneratorReturnType<CG> = CG extends WorkflowGenerator<
|
|
10
|
+
infer T
|
|
11
|
+
>
|
|
12
|
+
? T
|
|
13
|
+
: never;
|
|
14
|
+
|
|
15
|
+
export type WorkflowRouter = Record<string, WorkflowGenerator>;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type { HeapClient, HeapRecord } from "./base/heap";
|
|
2
|
+
export type { WorkflowInvoker } from "./base/invoker";
|
|
3
|
+
export type { Task, TaskProcessor } from "./base/runtime";
|
|
4
|
+
export type { SchedulerClient } from "./base/scheduler";
|
|
5
|
+
export type {
|
|
6
|
+
WorkflowGenerator,
|
|
7
|
+
WorkflowGeneratorReturnType,
|
|
8
|
+
WorkflowRouter,
|
|
9
|
+
} from "./base/workflow";
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
StepCacheCheck,
|
|
13
|
+
StepDelay,
|
|
14
|
+
StepError,
|
|
15
|
+
StepInvalid,
|
|
16
|
+
StepKey,
|
|
17
|
+
StepResponse,
|
|
18
|
+
StepResult,
|
|
19
|
+
WorkflowDelay,
|
|
20
|
+
WorkflowRestart,
|
|
21
|
+
WorkflowResult,
|
|
22
|
+
} from "./base/step";
|
|
23
|
+
|
|
24
|
+
export { WorkflowRunner } from "./lib/workflow-runner";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WorkflowGenerator,
|
|
3
|
+
HeapClient,
|
|
4
|
+
SchedulerClient,
|
|
5
|
+
TaskProcessor,
|
|
6
|
+
} from "..";
|
|
7
|
+
import { StepDelay, WorkflowDelay, WorkflowResult } from "..";
|
|
8
|
+
|
|
9
|
+
export class WorkflowRunner<
|
|
10
|
+
Router extends Record<string, WorkflowGenerator<any>>
|
|
11
|
+
> {
|
|
12
|
+
private heapClient: HeapClient;
|
|
13
|
+
private schedulerClient: SchedulerClient;
|
|
14
|
+
private router: Router;
|
|
15
|
+
|
|
16
|
+
constructor(params: {
|
|
17
|
+
heapClient: HeapClient;
|
|
18
|
+
schedulerClient: SchedulerClient;
|
|
19
|
+
router: Router;
|
|
20
|
+
}) {
|
|
21
|
+
this.heapClient = params.heapClient;
|
|
22
|
+
this.schedulerClient = params.schedulerClient;
|
|
23
|
+
this.router = params.router;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
run: TaskProcessor = async (task) => {
|
|
27
|
+
const { workflowId, executionId } = task;
|
|
28
|
+
const workflow = this.router[workflowId];
|
|
29
|
+
|
|
30
|
+
if (!workflow) {
|
|
31
|
+
throw new Error(`No workflow registered for "${workflowId}"`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const response = await this.runWorkflows({ executionId, workflow });
|
|
36
|
+
|
|
37
|
+
switch (response.type) {
|
|
38
|
+
case "workflow-result":
|
|
39
|
+
return response;
|
|
40
|
+
|
|
41
|
+
case "workflow-delay":
|
|
42
|
+
this.schedulerClient.requestWakeUp({
|
|
43
|
+
workflowId,
|
|
44
|
+
executionId,
|
|
45
|
+
resumeIn: response.resumeIn,
|
|
46
|
+
});
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
} catch (err) {
|
|
50
|
+
// todo: distinguish between a workflow error and a system error
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
private async runWorkflows<T>(params: {
|
|
56
|
+
executionId: string;
|
|
57
|
+
workflow: WorkflowGenerator<T>;
|
|
58
|
+
}): Promise<WorkflowResult<T> | WorkflowDelay> {
|
|
59
|
+
const { executionId, workflow } = params;
|
|
60
|
+
|
|
61
|
+
const workflowIterator = workflow({
|
|
62
|
+
heapClient: this.heapClient,
|
|
63
|
+
executionId,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const iteratorResult = await workflowIterator.next();
|
|
67
|
+
const stageResponse = iteratorResult.value;
|
|
68
|
+
|
|
69
|
+
if (iteratorResult.done) {
|
|
70
|
+
return stageResponse as WorkflowResult<T>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (stageResponse instanceof StepDelay) {
|
|
74
|
+
return new WorkflowDelay(stageResponse.resumeIn - Date.now());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw new Error("Workflow runner critical error");
|
|
78
|
+
}
|
|
79
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Enable latest features
|
|
4
|
+
"lib": ["ESNext"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
|
|
10
|
+
// Bundler mode
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
|
|
16
|
+
// Best practices
|
|
17
|
+
"strict": true,
|
|
18
|
+
"skipLibCheck": true,
|
|
19
|
+
"noFallthroughCasesInSwitch": true,
|
|
20
|
+
|
|
21
|
+
// Some stricter flags (disabled by default)
|
|
22
|
+
"noUnusedLocals": false,
|
|
23
|
+
"noUnusedParameters": false,
|
|
24
|
+
"noPropertyAccessFromIndexSignature": false
|
|
25
|
+
}
|
|
26
|
+
}
|