@chainlink/cre-sdk 0.0.1-alpha → 0.0.2-alpha
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/generated/capabilities/internal/nodeaction/v1/node_action_pb.d.ts +69 -0
- package/dist/generated/capabilities/internal/nodeaction/v1/node_action_pb.js +29 -0
- package/dist/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.d.ts +13 -18
- package/dist/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.js +71 -260
- package/dist/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen.d.ts +4 -9
- package/dist/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen.js +14 -45
- package/dist/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen.d.ts +3 -7
- package/dist/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen.js +9 -36
- package/dist/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen.d.ts +2 -8
- package/dist/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen.js +8 -21
- package/dist/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen.d.ts +5 -8
- package/dist/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen.js +16 -60
- package/dist/generated-sdk/capabilities/internal/nodeaction/v1/basicaction_sdk_gen.d.ts +19 -0
- package/dist/generated-sdk/capabilities/internal/nodeaction/v1/basicaction_sdk_gen.js +36 -0
- package/dist/generated-sdk/capabilities/networking/http/v1alpha/client_sdk_gen.d.ts +3 -7
- package/dist/generated-sdk/capabilities/networking/http/v1alpha/client_sdk_gen.js +9 -36
- package/dist/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.d.ts +2 -8
- package/dist/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.js +8 -21
- package/dist/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.d.ts +2 -8
- package/dist/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.js +8 -21
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/sdk/cre/index.d.ts +5 -19
- package/dist/sdk/cre/index.js +1 -17
- package/dist/sdk/errors.d.ts +12 -0
- package/dist/sdk/errors.js +21 -0
- package/dist/sdk/impl/runtime-impl.d.ts +38 -0
- package/dist/sdk/impl/runtime-impl.js +190 -0
- package/dist/sdk/impl/runtime-impl.test.js +334 -0
- package/dist/sdk/index.d.ts +3 -0
- package/dist/sdk/index.js +2 -0
- package/dist/sdk/runtime.d.ts +24 -0
- package/dist/sdk/utils/capabilities/capability-error.d.ts +0 -3
- package/dist/sdk/utils/capabilities/capability-error.js +0 -2
- package/dist/sdk/utils/config/configHandler.test.d.ts +1 -0
- package/dist/sdk/utils/config/configHandler.test.js +193 -0
- package/dist/sdk/utils/config/index.d.ts +4 -6
- package/dist/sdk/utils/config/index.js +10 -34
- package/dist/sdk/utils/index.d.ts +0 -3
- package/dist/sdk/utils/index.js +0 -3
- package/dist/sdk/utils/values/consensus_aggregators.d.ts +1 -1
- package/dist/sdk/{runtime → wasm}/host-bindings.d.ts +0 -1
- package/dist/sdk/{runtime → wasm}/host-bindings.js +0 -4
- package/dist/sdk/wasm/index.d.ts +1 -0
- package/dist/sdk/wasm/index.js +1 -0
- package/dist/sdk/wasm/runner.d.ts +14 -0
- package/dist/sdk/wasm/runner.js +114 -0
- package/dist/sdk/wasm/runner.test.d.ts +1 -0
- package/dist/sdk/wasm/runner.test.js +270 -0
- package/dist/sdk/wasm/runtime.d.ts +7 -0
- package/dist/sdk/wasm/runtime.js +55 -0
- package/dist/sdk/workflow.d.ts +13 -15
- package/dist/sdk/workflow.js +1 -21
- package/package.json +2 -2
- package/dist/sdk/engine/execute.d.ts +0 -4
- package/dist/sdk/engine/execute.js +0 -12
- package/dist/sdk/engine/execute.test.js +0 -129
- package/dist/sdk/engine/handleExecutionPhase.d.ts +0 -4
- package/dist/sdk/engine/handleExecutionPhase.js +0 -34
- package/dist/sdk/engine/handleSubscribePhase.d.ts +0 -3
- package/dist/sdk/engine/handleSubscribePhase.js +0 -23
- package/dist/sdk/logger.d.ts +0 -7
- package/dist/sdk/logger.js +0 -18
- package/dist/sdk/runtime/errors.d.ts +0 -23
- package/dist/sdk/runtime/errors.js +0 -30
- package/dist/sdk/runtime/index.d.ts +0 -3
- package/dist/sdk/runtime/index.js +0 -2
- package/dist/sdk/runtime/run-in-node-mode.d.ts +0 -12
- package/dist/sdk/runtime/run-in-node-mode.js +0 -47
- package/dist/sdk/runtime/run-in-node-mode.test.js +0 -116
- package/dist/sdk/runtime/runtime.d.ts +0 -36
- package/dist/sdk/runtime/runtime.js +0 -84
- package/dist/sdk/runtime/runtime.test.d.ts +0 -1
- package/dist/sdk/runtime/runtime.test.js +0 -58
- package/dist/sdk/testhelpers/dangerously-call-capability.d.ts +0 -10
- package/dist/sdk/testhelpers/dangerously-call-capability.js +0 -26
- package/dist/sdk/testhelpers/mock-host-bindings.d.ts +0 -15
- package/dist/sdk/testhelpers/mock-host-bindings.js +0 -25
- package/dist/sdk/testhelpers/mock-runtime.d.ts +0 -2
- package/dist/sdk/testhelpers/mock-runtime.js +0 -16
- package/dist/sdk/utils/await-async-request.d.ts +0 -9
- package/dist/sdk/utils/await-async-request.js +0 -27
- package/dist/sdk/utils/capabilities/call-capability.d.ts +0 -22
- package/dist/sdk/utils/capabilities/call-capability.js +0 -33
- package/dist/sdk/utils/capabilities/callback-id.d.ts +0 -3
- package/dist/sdk/utils/capabilities/callback-id.js +0 -22
- package/dist/sdk/utils/capabilities/http/fetch.d.ts +0 -44
- package/dist/sdk/utils/capabilities/http/fetch.js +0 -63
- package/dist/sdk/utils/do-request-async.d.ts +0 -12
- package/dist/sdk/utils/do-request-async.js +0 -17
- package/dist/sdk/utils/error-boundary.d.ts +0 -2
- package/dist/sdk/utils/error-boundary.js +0 -30
- package/dist/sdk/utils/get-request.d.ts +0 -1
- package/dist/sdk/utils/get-request.js +0 -15
- package/dist/sdk/utils/lazy-promise.d.ts +0 -59
- package/dist/sdk/utils/lazy-promise.js +0 -81
- package/dist/sdk/utils/random/get-rand.d.ts +0 -3
- package/dist/sdk/utils/random/get-rand.js +0 -6
- package/dist/sdk/utils/random/random.d.ts +0 -35
- package/dist/sdk/utils/random/random.js +0 -123
- package/dist/sdk/utils/secrets/await-async-secret.d.ts +0 -1
- package/dist/sdk/utils/secrets/await-async-secret.js +0 -29
- package/dist/sdk/utils/secrets/do-get-secret.d.ts +0 -1
- package/dist/sdk/utils/secrets/do-get-secret.js +0 -14
- package/dist/sdk/utils/secrets/get-secret.d.ts +0 -1
- package/dist/sdk/utils/secrets/get-secret.js +0 -10
- package/dist/sdk/utils/send-error.d.ts +0 -1
- package/dist/sdk/utils/send-error.js +0 -16
- package/dist/sdk/utils/send-response-value.d.ts +0 -2
- package/dist/sdk/utils/send-response-value.js +0 -15
- package/dist/sdk/utils/time/get-time.d.ts +0 -16
- package/dist/sdk/utils/time/get-time.js +0 -21
- /package/dist/sdk/{engine/execute.test.d.ts → impl/runtime-impl.test.d.ts} +0 -0
- /package/dist/sdk/{runtime/run-in-node-mode.test.d.ts → runtime.js} +0 -0
package/dist/sdk/utils/index.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
export * from './error-boundary';
|
|
2
|
-
export { withErrorBoundary } from './error-boundary';
|
|
3
1
|
export * from './hex-utils';
|
|
4
|
-
export * from './send-response-value';
|
|
5
2
|
export * from './values/consensus_aggregators';
|
|
6
3
|
export * from './values/serializer_types';
|
|
7
4
|
export * from './values/value';
|
|
@@ -22,6 +22,6 @@ export declare class ConsensusFieldAggregation<T, U> {
|
|
|
22
22
|
constructor(fieldDescriptor?: ConsensusDescriptor | undefined, t?: T | undefined, u?: U | undefined);
|
|
23
23
|
}
|
|
24
24
|
export type ConsensusAggregationFields<T extends object> = {
|
|
25
|
-
[K in keyof T]: () => ConsensusFieldAggregation<T[K], true>;
|
|
25
|
+
[K in keyof T as K extends '$typeName' ? never : K]: () => ConsensusFieldAggregation<T[K], true>;
|
|
26
26
|
};
|
|
27
27
|
export declare function ConsensusAggregationByFields<T extends object>(aggregation: ConsensusAggregationFields<T>): ConsensusAggregation<T, true>;
|
|
@@ -3,7 +3,6 @@ export declare const hostBindings: {
|
|
|
3
3
|
switchModes: (args_0: Mode, ...args: unknown[]) => void;
|
|
4
4
|
log: (args_0: string, ...args: unknown[]) => void;
|
|
5
5
|
sendResponse: (args_0: Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>, ...args: unknown[]) => number;
|
|
6
|
-
randomSeed: (args_0: Mode.DON | Mode.NODE, ...args: unknown[]) => number;
|
|
7
6
|
versionV2: (...args: unknown[]) => void;
|
|
8
7
|
callCapability: (args_0: Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>, ...args: unknown[]) => number;
|
|
9
8
|
awaitCapabilities: (args_0: Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>, args_1: number, ...args: unknown[]) => Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>;
|
|
@@ -8,10 +8,6 @@ const globalHostBindingsSchema = z.object({
|
|
|
8
8
|
.function()
|
|
9
9
|
.args(z.union([z.instanceof(Uint8Array), z.custom()]))
|
|
10
10
|
.returns(z.number()),
|
|
11
|
-
randomSeed: z
|
|
12
|
-
.function()
|
|
13
|
-
.args(z.union([z.literal(Mode.DON), z.literal(Mode.NODE)]))
|
|
14
|
-
.returns(z.number()),
|
|
15
11
|
versionV2: z.function().args().returns(z.void()),
|
|
16
12
|
callCapability: z
|
|
17
13
|
.function()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Runner } from './runner';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Runner } from './runner';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ExecuteRequest, type ExecutionResult } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
2
|
+
import { type ConfigHandlerParams } from '../utils/config';
|
|
3
|
+
import type { SecretsProvider, Workflow } from '../workflow';
|
|
4
|
+
import { Runtime } from './runtime';
|
|
5
|
+
export declare class Runner<TConfig> {
|
|
6
|
+
private readonly config;
|
|
7
|
+
private readonly request;
|
|
8
|
+
private constructor();
|
|
9
|
+
static newRunner<TConfig, TIntermediateConfig = TConfig>(configHandlerParams?: ConfigHandlerParams<TConfig, TIntermediateConfig>): Promise<Runner<TConfig>>;
|
|
10
|
+
private static getRequest;
|
|
11
|
+
run(initFn: (config: TConfig, secretsProvider: SecretsProvider) => Promise<Workflow<TConfig>> | Workflow<TConfig>): Promise<void>;
|
|
12
|
+
handleExecutionPhase<TConfig>(req: ExecuteRequest, workflow: Workflow<TConfig>, runtime: Runtime<TConfig>): Promise<ExecutionResult>;
|
|
13
|
+
handleSubscribePhase(req: ExecuteRequest, workflow: Workflow<TConfig>): ExecutionResult;
|
|
14
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { create, fromBinary, toBinary } from '@bufbuild/protobuf';
|
|
2
|
+
import { ExecuteRequestSchema, ExecutionResultSchema, TriggerSubscriptionRequestSchema, } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
3
|
+
import { configHandler } from '../utils/config';
|
|
4
|
+
import { Value } from '../utils';
|
|
5
|
+
import { hostBindings } from './host-bindings';
|
|
6
|
+
import { Runtime } from './runtime';
|
|
7
|
+
export class Runner {
|
|
8
|
+
config;
|
|
9
|
+
request;
|
|
10
|
+
constructor(config, request) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.request = request;
|
|
13
|
+
}
|
|
14
|
+
static async newRunner(configHandlerParams) {
|
|
15
|
+
hostBindings.versionV2();
|
|
16
|
+
const request = Runner.getRequest();
|
|
17
|
+
const config = await configHandler(request, configHandlerParams);
|
|
18
|
+
return new Runner(config, request);
|
|
19
|
+
}
|
|
20
|
+
static getRequest() {
|
|
21
|
+
const argsString = hostBindings.getWasiArgs();
|
|
22
|
+
const args = JSON.parse(argsString);
|
|
23
|
+
// SDK expects exactly 2 args:
|
|
24
|
+
// 1st is the script name
|
|
25
|
+
// 2nd is the base64 encoded request
|
|
26
|
+
if (args.length !== 2) {
|
|
27
|
+
throw new Error('Invalid request: must contain payload');
|
|
28
|
+
}
|
|
29
|
+
const base64Request = args[1];
|
|
30
|
+
const bytes = Buffer.from(base64Request, 'base64');
|
|
31
|
+
return fromBinary(ExecuteRequestSchema, bytes);
|
|
32
|
+
}
|
|
33
|
+
async run(initFn) {
|
|
34
|
+
const runtime = new Runtime(this.config, 0, this.request.maxResponseSize);
|
|
35
|
+
var result;
|
|
36
|
+
try {
|
|
37
|
+
const workflow = await initFn(this.config, {
|
|
38
|
+
getSecret: runtime.getSecret.bind(runtime),
|
|
39
|
+
});
|
|
40
|
+
switch (this.request.request.case) {
|
|
41
|
+
case 'subscribe':
|
|
42
|
+
result = this.handleSubscribePhase(this.request, workflow);
|
|
43
|
+
break;
|
|
44
|
+
case 'trigger':
|
|
45
|
+
result = this.handleExecutionPhase(this.request, workflow, runtime);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
const err = e instanceof Error ? e.message : String(e);
|
|
51
|
+
result = create(ExecutionResultSchema, {
|
|
52
|
+
result: { case: 'error', value: err },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const awaitedResult = await result;
|
|
56
|
+
hostBindings.sendResponse(toBinary(ExecutionResultSchema, awaitedResult));
|
|
57
|
+
}
|
|
58
|
+
async handleExecutionPhase(req, workflow, runtime) {
|
|
59
|
+
if (req.request.case !== 'trigger') {
|
|
60
|
+
throw new Error('cannot handle non-trigger request as a trigger');
|
|
61
|
+
}
|
|
62
|
+
const triggerMsg = req.request.value;
|
|
63
|
+
const index = Number(triggerMsg.id);
|
|
64
|
+
if (Number.isFinite(index) && index >= 0 && index < workflow.length) {
|
|
65
|
+
const entry = workflow[index];
|
|
66
|
+
const schema = entry.trigger.outputSchema();
|
|
67
|
+
const payloadAny = triggerMsg.payload;
|
|
68
|
+
/**
|
|
69
|
+
* Note: do not hardcode method name; routing by id is authoritative.
|
|
70
|
+
*
|
|
71
|
+
* This matches the GO SDK behavior, which also just checks for the id.
|
|
72
|
+
*
|
|
73
|
+
* @see https://github.com/smartcontractkit/cre-sdk-go/blob/5a41d81e3e072008484e85dc96d746401aafcba2/cre/wasm/runner.go#L81
|
|
74
|
+
* */
|
|
75
|
+
const decoded = fromBinary(schema, payloadAny.value);
|
|
76
|
+
const adapted = await entry.trigger.adapt(decoded);
|
|
77
|
+
try {
|
|
78
|
+
const result = await entry.fn(runtime, adapted);
|
|
79
|
+
const wrapped = Value.wrap(result);
|
|
80
|
+
return create(ExecutionResultSchema, {
|
|
81
|
+
result: { case: 'value', value: wrapped.proto() },
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
const err = e instanceof Error ? e.message : String(e);
|
|
86
|
+
return create(ExecutionResultSchema, {
|
|
87
|
+
result: { case: 'error', value: err },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return create(ExecutionResultSchema, {
|
|
92
|
+
result: { case: 'error', value: 'trigger not found' },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
handleSubscribePhase(req, workflow) {
|
|
96
|
+
if (req.request.case !== 'subscribe') {
|
|
97
|
+
return create(ExecutionResultSchema, {
|
|
98
|
+
result: { case: 'error', value: 'subscribe request expected' },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// Build TriggerSubscriptionRequest from the workflow entries
|
|
102
|
+
const subscriptions = workflow.map((entry) => ({
|
|
103
|
+
id: entry.trigger.capabilityId(),
|
|
104
|
+
method: entry.trigger.method(),
|
|
105
|
+
payload: entry.trigger.configAsAny(),
|
|
106
|
+
}));
|
|
107
|
+
const subscriptionRequest = create(TriggerSubscriptionRequestSchema, {
|
|
108
|
+
subscriptions,
|
|
109
|
+
});
|
|
110
|
+
return create(ExecutionResultSchema, {
|
|
111
|
+
result: { case: 'triggerSubscriptions', value: subscriptionRequest },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { afterEach, describe, expect, mock, test } from 'bun:test';
|
|
2
|
+
import { create, fromBinary, toBinary } from '@bufbuild/protobuf';
|
|
3
|
+
import { anyPack, anyUnpack, EmptySchema } from '@bufbuild/protobuf/wkt';
|
|
4
|
+
import { ConfigSchema, OutputsSchema, } from '../../generated/capabilities/internal/basictrigger/v1/basic_trigger_pb';
|
|
5
|
+
import { AwaitSecretsRequestSchema, AwaitSecretsResponseSchema, ExecuteRequestSchema, ExecutionResultSchema, GetSecretsRequestSchema, SecretResponseSchema, SecretResponsesSchema, SecretSchema, TriggerSchema, } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
6
|
+
import { BasicCapability as BasicTriggerCapability } from '../../generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen';
|
|
7
|
+
import { cre } from '../cre';
|
|
8
|
+
import { Value } from '../utils';
|
|
9
|
+
import { Runner } from './runner';
|
|
10
|
+
const anyConfig = Buffer.from('config');
|
|
11
|
+
const anyMaxResponseSize = 2048n;
|
|
12
|
+
const basicTrigger = new BasicTriggerCapability();
|
|
13
|
+
const capID = BasicTriggerCapability.CAPABILITY_ID;
|
|
14
|
+
const subscribeRequest = create(ExecuteRequestSchema, {
|
|
15
|
+
request: { case: 'subscribe', value: create(EmptySchema) },
|
|
16
|
+
maxResponseSize: anyMaxResponseSize,
|
|
17
|
+
config: anyConfig,
|
|
18
|
+
});
|
|
19
|
+
const anyExecuteRequest = create(ExecuteRequestSchema, {
|
|
20
|
+
request: {
|
|
21
|
+
case: 'trigger',
|
|
22
|
+
value: create(TriggerSchema, {
|
|
23
|
+
id: 0n,
|
|
24
|
+
payload: anyPack(OutputsSchema, create(OutputsSchema, { coolOutput: 'hi' })),
|
|
25
|
+
}),
|
|
26
|
+
},
|
|
27
|
+
maxResponseSize: anyMaxResponseSize,
|
|
28
|
+
config: anyConfig,
|
|
29
|
+
});
|
|
30
|
+
const mockHostBindings = {
|
|
31
|
+
sendResponse: mock(() => {
|
|
32
|
+
return 0;
|
|
33
|
+
}),
|
|
34
|
+
versionV2: mock(() => { }),
|
|
35
|
+
getWasiArgs: mock(() => {
|
|
36
|
+
throw new Error('override for tests');
|
|
37
|
+
}),
|
|
38
|
+
getSecrets: mock((data, maxResponseSize) => {
|
|
39
|
+
throw new Error('override for tests');
|
|
40
|
+
}),
|
|
41
|
+
awaitSecrets: mock((data, maxResponseSize) => {
|
|
42
|
+
throw new Error('override for tests');
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
const proxyHostBindings = {
|
|
46
|
+
sendResponse: (data) => {
|
|
47
|
+
return mockHostBindings.sendResponse(data);
|
|
48
|
+
},
|
|
49
|
+
versionV2: () => {
|
|
50
|
+
return mockHostBindings.versionV2();
|
|
51
|
+
},
|
|
52
|
+
getWasiArgs: () => {
|
|
53
|
+
return mockHostBindings.getWasiArgs();
|
|
54
|
+
},
|
|
55
|
+
switchModes: mock(() => { }),
|
|
56
|
+
log: (message) => {
|
|
57
|
+
throw new Error('log called unexpectedly in test');
|
|
58
|
+
},
|
|
59
|
+
callCapability: (data) => {
|
|
60
|
+
throw new Error('callCapability called unexpectedly in test');
|
|
61
|
+
},
|
|
62
|
+
awaitCapabilities: (data, id) => {
|
|
63
|
+
throw new Error('awaitCapabilities called unexpectedly in test');
|
|
64
|
+
},
|
|
65
|
+
getSecrets: (data, id) => {
|
|
66
|
+
return mockHostBindings.getSecrets(data, id);
|
|
67
|
+
},
|
|
68
|
+
awaitSecrets: (data, id) => {
|
|
69
|
+
return mockHostBindings.awaitSecrets(data, id);
|
|
70
|
+
},
|
|
71
|
+
now: () => {
|
|
72
|
+
throw new Error('now called unexpectedly in test');
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
Object.assign(globalThis, proxyHostBindings);
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
mock.restore();
|
|
78
|
+
});
|
|
79
|
+
describe('runner', () => {
|
|
80
|
+
describe('run', () => {
|
|
81
|
+
test('gathers subscriptions', async () => {
|
|
82
|
+
var sentResponse = null;
|
|
83
|
+
mockHostBindings.sendResponse = mock((input) => {
|
|
84
|
+
sentResponse = fromBinary(ExecutionResultSchema, input);
|
|
85
|
+
return 0;
|
|
86
|
+
});
|
|
87
|
+
const runner = await getTestRunner(subscribeRequest);
|
|
88
|
+
await runner.run(async (_, secretsProvider) => {
|
|
89
|
+
return [
|
|
90
|
+
cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), () => {
|
|
91
|
+
throw new Error('Must not be called during registration to tiggers');
|
|
92
|
+
}),
|
|
93
|
+
];
|
|
94
|
+
});
|
|
95
|
+
expect(sentResponse).toBeDefined();
|
|
96
|
+
expect(sentResponse.result.case).toBe('triggerSubscriptions');
|
|
97
|
+
const responseValue = sentResponse.result.value;
|
|
98
|
+
expect(responseValue.subscriptions.length).toBe(1);
|
|
99
|
+
expect(responseValue.subscriptions[0].id).toBe(capID);
|
|
100
|
+
expect(responseValue.subscriptions[0].method).toBe('Trigger');
|
|
101
|
+
expect(responseValue.subscriptions[0].payload).toBeDefined();
|
|
102
|
+
const actualConfig = anyUnpack(responseValue.subscriptions[0].payload, ConfigSchema);
|
|
103
|
+
expect(actualConfig.name).toBe('foo');
|
|
104
|
+
expect(actualConfig.number).toBe(10);
|
|
105
|
+
});
|
|
106
|
+
test('executes workflow', async () => {
|
|
107
|
+
var sentResponse = null;
|
|
108
|
+
mockHostBindings.sendResponse = mock((input) => {
|
|
109
|
+
sentResponse = fromBinary(ExecutionResultSchema, input);
|
|
110
|
+
return 0;
|
|
111
|
+
});
|
|
112
|
+
const runner = await getTestRunner(anyExecuteRequest);
|
|
113
|
+
await runner.run(async (_, secretsProvider) => {
|
|
114
|
+
return [
|
|
115
|
+
cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), (runtime, trigger) => {
|
|
116
|
+
expect(runtime.config).toBe(anyConfig.toString());
|
|
117
|
+
expect(trigger.coolOutput).toBe('hi');
|
|
118
|
+
return 10;
|
|
119
|
+
}),
|
|
120
|
+
];
|
|
121
|
+
});
|
|
122
|
+
expect(sentResponse).toBeDefined();
|
|
123
|
+
expect(sentResponse.result.case).toBe('value');
|
|
124
|
+
expect(Value.wrap(sentResponse.result.value).unwrapToType({ instance: 10 })).toBe(10);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
test('executes subscribe error', async () => {
|
|
128
|
+
var sentResponse = null;
|
|
129
|
+
const anyError = 'error';
|
|
130
|
+
mockHostBindings.sendResponse = mock((input) => {
|
|
131
|
+
sentResponse = fromBinary(ExecutionResultSchema, input);
|
|
132
|
+
expect(sentResponse.result.case).toBe('error');
|
|
133
|
+
expect(sentResponse.result.value).toBe(anyError);
|
|
134
|
+
return 0;
|
|
135
|
+
});
|
|
136
|
+
const runner = await getTestRunner(subscribeRequest);
|
|
137
|
+
await runner.run((_, secretsProvider) => {
|
|
138
|
+
throw new Error(anyError);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
test('executes subscribe resolve error', async () => {
|
|
142
|
+
var sentResponse = null;
|
|
143
|
+
const anyError = 'error';
|
|
144
|
+
mockHostBindings.sendResponse = mock((input) => {
|
|
145
|
+
sentResponse = fromBinary(ExecutionResultSchema, input);
|
|
146
|
+
expect(sentResponse.result.case).toBe('error');
|
|
147
|
+
expect(sentResponse.result.value).toBe(anyError);
|
|
148
|
+
return 0;
|
|
149
|
+
});
|
|
150
|
+
const runner = await getTestRunner(subscribeRequest);
|
|
151
|
+
await runner.run(async (_, secretsProvider) => {
|
|
152
|
+
return Promise.reject(new Error(anyError));
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
test('executes trigger error', async () => {
|
|
156
|
+
var sentResponse = null;
|
|
157
|
+
const anyError = 'error';
|
|
158
|
+
mockHostBindings.sendResponse = mock((input) => {
|
|
159
|
+
sentResponse = fromBinary(ExecutionResultSchema, input);
|
|
160
|
+
expect(sentResponse.result.case).toBe('error');
|
|
161
|
+
expect(sentResponse.result.value).toBe(anyError);
|
|
162
|
+
return 0;
|
|
163
|
+
});
|
|
164
|
+
const runner = await getTestRunner(anyExecuteRequest);
|
|
165
|
+
await runner.run(async (_, secretsProvider) => {
|
|
166
|
+
throw new Error(anyError);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
test('executes workflow with multiple triggers', async () => {
|
|
170
|
+
var sentResponse = null;
|
|
171
|
+
mockHostBindings.sendResponse = mock((input) => {
|
|
172
|
+
sentResponse = fromBinary(ExecutionResultSchema, input);
|
|
173
|
+
return 0;
|
|
174
|
+
});
|
|
175
|
+
const testRequest = structuredClone(anyExecuteRequest);
|
|
176
|
+
const trigger = testRequest.request.value;
|
|
177
|
+
trigger.id = 1n;
|
|
178
|
+
const runner = await getTestRunner(testRequest);
|
|
179
|
+
await runner.run(async (_, secretsProvider) => {
|
|
180
|
+
return [
|
|
181
|
+
cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), (runtime, trigger) => {
|
|
182
|
+
expect(runtime.config).toBe(anyConfig.toString());
|
|
183
|
+
expect(trigger.coolOutput).toBe('hi');
|
|
184
|
+
return 10;
|
|
185
|
+
}),
|
|
186
|
+
cre.handler(basicTrigger.trigger({ name: 'bar', number: 20 }), (runtime, trigger) => {
|
|
187
|
+
expect(runtime.config).toBe(anyConfig.toString());
|
|
188
|
+
expect(trigger.coolOutput).toBe('hi');
|
|
189
|
+
return 20;
|
|
190
|
+
}),
|
|
191
|
+
cre.handler(basicTrigger.trigger({ name: 'baz', number: 30 }), (runtime, trigger) => {
|
|
192
|
+
expect(runtime.config).toBe(anyConfig.toString());
|
|
193
|
+
expect(trigger.coolOutput).toBe('hi');
|
|
194
|
+
return 30;
|
|
195
|
+
}),
|
|
196
|
+
];
|
|
197
|
+
});
|
|
198
|
+
expect(sentResponse).toBeDefined();
|
|
199
|
+
expect(sentResponse.result.case).toBe('value');
|
|
200
|
+
expect(Value.wrap(sentResponse.result.value).unwrapToType({ instance: 10 })).toBe(20);
|
|
201
|
+
});
|
|
202
|
+
test('get secrets passes max response size', async () => {
|
|
203
|
+
const anySecretResponse = create(SecretResponseSchema, {
|
|
204
|
+
response: {
|
|
205
|
+
case: 'secret',
|
|
206
|
+
value: create(SecretSchema, { id: 'Bar', namespace: 'Foo', owner: 'Baz', value: 'Qux' }),
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
const anySecretsResponse = create(SecretResponsesSchema, {
|
|
210
|
+
responses: [anySecretResponse],
|
|
211
|
+
});
|
|
212
|
+
mockHostBindings.getSecrets = (data, maxResponseSize) => {
|
|
213
|
+
const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data;
|
|
214
|
+
const secretsRequest = fromBinary(GetSecretsRequestSchema, dataBytes);
|
|
215
|
+
expect(secretsRequest.requests.length).toBe(1);
|
|
216
|
+
expect(secretsRequest.requests[0].namespace).toBe('Foo');
|
|
217
|
+
expect(secretsRequest.requests[0].id).toBe('Bar');
|
|
218
|
+
expect(secretsRequest.callbackId).toBe(0);
|
|
219
|
+
expect(maxResponseSize).toBe(Number(anyMaxResponseSize));
|
|
220
|
+
return 0;
|
|
221
|
+
};
|
|
222
|
+
mockHostBindings.awaitSecrets = (data, maxResponseSize) => {
|
|
223
|
+
const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data;
|
|
224
|
+
const awaitSecretsRequest = fromBinary(AwaitSecretsRequestSchema, dataBytes);
|
|
225
|
+
expect(awaitSecretsRequest.ids.length).toBe(1);
|
|
226
|
+
expect(awaitSecretsRequest.ids[0]).toBe(0);
|
|
227
|
+
expect(maxResponseSize).toBe(Number(anyMaxResponseSize));
|
|
228
|
+
// Create the proper AwaitSecretsResponse with a map
|
|
229
|
+
const awaitSecretsResponse = create(AwaitSecretsResponseSchema, {
|
|
230
|
+
responses: {
|
|
231
|
+
0: anySecretsResponse,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
return toBinary(AwaitSecretsResponseSchema, awaitSecretsResponse);
|
|
235
|
+
};
|
|
236
|
+
const dr = getTestRunner(subscribeRequest);
|
|
237
|
+
await (await dr).run(async (_, secretsProvider) => {
|
|
238
|
+
const secret = await secretsProvider.getSecret({ namespace: 'Foo', id: 'Bar' }).result();
|
|
239
|
+
expect(secret.namespace).toBe('Foo');
|
|
240
|
+
expect(secret.id).toBe('Bar');
|
|
241
|
+
expect(secret.owner).toBe('Baz');
|
|
242
|
+
expect(secret.value).toBe('Qux');
|
|
243
|
+
return [cre.handler(basicTrigger.trigger({}), () => 10)];
|
|
244
|
+
});
|
|
245
|
+
expect(true).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
function assertEnv(r) {
|
|
249
|
+
let ran = false;
|
|
250
|
+
const verifyEnv = (config, _) => {
|
|
251
|
+
ran = true;
|
|
252
|
+
expect(config).toBe(anyConfig.toString());
|
|
253
|
+
return [];
|
|
254
|
+
};
|
|
255
|
+
r.run(verifyEnv);
|
|
256
|
+
expect(ran).toBe(true);
|
|
257
|
+
}
|
|
258
|
+
function getTestRunner(request) {
|
|
259
|
+
const serialized = toBinary(ExecuteRequestSchema, request);
|
|
260
|
+
const encoded = Buffer.from(serialized).toString('base64');
|
|
261
|
+
// Update the mock to return the specific request
|
|
262
|
+
mockHostBindings.getWasiArgs = mock(() => JSON.stringify(['program', encoded]));
|
|
263
|
+
return Runner.newRunner({
|
|
264
|
+
configParser: (b) => {
|
|
265
|
+
const stringConfig = Buffer.from(b).toString();
|
|
266
|
+
expect(stringConfig).toBe(anyConfig.toString());
|
|
267
|
+
return stringConfig;
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NodeRuntimeImpl, RuntimeImpl } from '../impl/runtime-impl';
|
|
2
|
+
export declare class Runtime<C> extends RuntimeImpl<C> {
|
|
3
|
+
constructor(config: C, nextCallId: number, maxResponseSize: bigint);
|
|
4
|
+
}
|
|
5
|
+
export declare class NodeRuntime<C> extends NodeRuntimeImpl<C> {
|
|
6
|
+
constructor(config: C, nextCallId: number, maxResponseSize: bigint);
|
|
7
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { fromBinary, toBinary } from '@bufbuild/protobuf';
|
|
2
|
+
import { AwaitCapabilitiesRequestSchema, AwaitCapabilitiesResponseSchema, AwaitSecretsRequestSchema, AwaitSecretsResponseSchema, CapabilityRequestSchema, GetSecretsRequestSchema, } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
3
|
+
import { NodeRuntimeImpl, RuntimeImpl } from '../impl/runtime-impl';
|
|
4
|
+
import { hostBindings } from './host-bindings';
|
|
5
|
+
export class Runtime extends RuntimeImpl {
|
|
6
|
+
constructor(config, nextCallId, maxResponseSize) {
|
|
7
|
+
super(config, nextCallId, WasmRuntimeHelpers.getInstance(), maxResponseSize);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class NodeRuntime extends NodeRuntimeImpl {
|
|
11
|
+
constructor(config, nextCallId, maxResponseSize) {
|
|
12
|
+
super(config, nextCallId, WasmRuntimeHelpers.getInstance(), maxResponseSize);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class WasmRuntimeHelpers {
|
|
16
|
+
static instance;
|
|
17
|
+
constructor() { }
|
|
18
|
+
now() {
|
|
19
|
+
return hostBindings.now();
|
|
20
|
+
}
|
|
21
|
+
static getInstance() {
|
|
22
|
+
if (!WasmRuntimeHelpers.instance) {
|
|
23
|
+
WasmRuntimeHelpers.instance = new WasmRuntimeHelpers();
|
|
24
|
+
}
|
|
25
|
+
return WasmRuntimeHelpers.instance;
|
|
26
|
+
}
|
|
27
|
+
call(request) {
|
|
28
|
+
return hostBindings.callCapability(toBinary(CapabilityRequestSchema, request)) >= 0;
|
|
29
|
+
}
|
|
30
|
+
await(request, maxResponseSize) {
|
|
31
|
+
// Convert bigint to integer for WASM host binding
|
|
32
|
+
// it's an i32, so we shouldn't need to trunc it (all i32 numbers are within the range of float64 without percision loss), but somehow it makes a difference...
|
|
33
|
+
const responseSize = Math.trunc(Number(maxResponseSize));
|
|
34
|
+
const response = hostBindings.awaitCapabilities(toBinary(AwaitCapabilitiesRequestSchema, request), responseSize);
|
|
35
|
+
const responseBytes = Array.isArray(response) ? new Uint8Array(response) : response;
|
|
36
|
+
return fromBinary(AwaitCapabilitiesResponseSchema, responseBytes);
|
|
37
|
+
}
|
|
38
|
+
getSecrets(request, maxResponseSize) {
|
|
39
|
+
// Convert bigint to integer for WASM host binding
|
|
40
|
+
// it's an i32, so we shouldn't need to trunc it (all i32 numbers are within the range of float64 without percision loss), but somehow it makes a difference...
|
|
41
|
+
const responseSize = Math.trunc(Number(maxResponseSize));
|
|
42
|
+
return hostBindings.getSecrets(toBinary(GetSecretsRequestSchema, request), responseSize) >= 0;
|
|
43
|
+
}
|
|
44
|
+
awaitSecrets(request, maxResponseSize) {
|
|
45
|
+
// Convert bigint to integer for WASM host binding
|
|
46
|
+
// it's an i32, so we shouldn't need to trunc it (all i32 numbers are within the range of float64 without percision loss), but somehow it makes a difference...
|
|
47
|
+
const responseSize = Math.trunc(Number(maxResponseSize));
|
|
48
|
+
const response = hostBindings.awaitSecrets(toBinary(AwaitSecretsRequestSchema, request), responseSize);
|
|
49
|
+
const responseBytes = Array.isArray(response) ? new Uint8Array(response) : response;
|
|
50
|
+
return fromBinary(AwaitSecretsResponseSchema, responseBytes);
|
|
51
|
+
}
|
|
52
|
+
switchModes(mode) {
|
|
53
|
+
hostBindings.switchModes(mode);
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/sdk/workflow.d.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import type { Message } from '@bufbuild/protobuf';
|
|
2
|
-
import type {
|
|
3
|
-
import { type Runtime } from './runtime
|
|
4
|
-
import { type ConfigHandlerParams } from './utils/config';
|
|
2
|
+
import type { Secret, SecretRequest, SecretRequestJson } from '../generated/sdk/v1alpha/sdk_pb';
|
|
3
|
+
import { type Runtime } from './runtime';
|
|
5
4
|
import type { Trigger } from './utils/triggers/trigger-interface';
|
|
6
|
-
|
|
7
|
-
export
|
|
5
|
+
import type { CreSerializable } from './utils';
|
|
6
|
+
export type HandlerFn<TConfig, TTriggerOutput, TResult> = (runtime: Runtime<TConfig>, triggerOutput: TTriggerOutput) => Promise<CreSerializable<TResult>> | CreSerializable<TResult>;
|
|
7
|
+
export interface HandlerEntry<TConfig, TRawTriggerOutput extends Message<string>, TTriggerOutput, TResult> {
|
|
8
8
|
trigger: Trigger<TRawTriggerOutput, TTriggerOutput>;
|
|
9
|
-
fn: HandlerFn<TConfig, TTriggerOutput>;
|
|
10
|
-
}
|
|
11
|
-
export type Workflow<TConfig = unknown> = ReadonlyArray<HandlerEntry<TConfig, any, any>>;
|
|
12
|
-
export declare const handler: <TRawTriggerOutput extends Message<string>, TTriggerOutput = TRawTriggerOutput, TConfig = unknown>(trigger: Trigger<TRawTriggerOutput, TTriggerOutput>, fn: HandlerFn<TConfig, TTriggerOutput>) => HandlerEntry<TConfig, TRawTriggerOutput, TTriggerOutput>;
|
|
13
|
-
export declare class Runner<TConfig> {
|
|
14
|
-
private readonly config;
|
|
15
|
-
private readonly runtime;
|
|
16
|
-
private constructor();
|
|
17
|
-
static newRunner<T>(configHandlerParams?: ConfigHandlerParams): Promise<Runner<T>>;
|
|
18
|
-
run(initFn: (config: TConfig) => Promise<Workflow<TConfig>> | Workflow<TConfig>): Promise<CapabilityResponse | void>;
|
|
9
|
+
fn: HandlerFn<TConfig, TTriggerOutput, TResult>;
|
|
19
10
|
}
|
|
11
|
+
export type Workflow<TConfig> = ReadonlyArray<HandlerEntry<TConfig, any, any, any>>;
|
|
12
|
+
export declare const handler: <TRawTriggerOutput extends Message<string>, TTriggerOutput, TConfig, TResult>(trigger: Trigger<TRawTriggerOutput, TTriggerOutput>, fn: HandlerFn<TConfig, TTriggerOutput, TResult>) => HandlerEntry<TConfig, TRawTriggerOutput, TTriggerOutput, TResult>;
|
|
13
|
+
export type SecretsProvider = {
|
|
14
|
+
getSecret(request: SecretRequest | SecretRequestJson): {
|
|
15
|
+
result: () => Promise<Secret>;
|
|
16
|
+
};
|
|
17
|
+
};
|
package/dist/sdk/workflow.js
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { runtime } from './runtime/runtime';
|
|
3
|
-
import { configHandler } from './utils/config';
|
|
4
|
-
import { getRequest } from './utils/get-request';
|
|
1
|
+
import {} from './runtime';
|
|
5
2
|
export const handler = (trigger, fn) => ({
|
|
6
3
|
trigger,
|
|
7
4
|
fn,
|
|
8
5
|
});
|
|
9
|
-
export class Runner {
|
|
10
|
-
config;
|
|
11
|
-
runtime;
|
|
12
|
-
constructor(config) {
|
|
13
|
-
this.config = config;
|
|
14
|
-
this.runtime = runtime;
|
|
15
|
-
}
|
|
16
|
-
static async newRunner(configHandlerParams = {}) {
|
|
17
|
-
const config = await configHandler(configHandlerParams);
|
|
18
|
-
return new Runner(config);
|
|
19
|
-
}
|
|
20
|
-
async run(initFn) {
|
|
21
|
-
const req = getRequest();
|
|
22
|
-
const workflow = await initFn(this.config);
|
|
23
|
-
return await handleExecuteRequest(req, workflow, this.config, this.runtime);
|
|
24
|
-
}
|
|
25
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chainlink/cre-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2-alpha",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@bufbuild/protobuf": "2.6.3",
|
|
46
46
|
"@bufbuild/protoc-gen-es": "2.6.3",
|
|
47
|
-
"@chainlink/cre-sdk-javy-plugin": "0.0.
|
|
47
|
+
"@chainlink/cre-sdk-javy-plugin": "0.0.2-alpha",
|
|
48
48
|
"@standard-schema/spec": "1.0.0",
|
|
49
49
|
"viem": "2.34.0",
|
|
50
50
|
"zod": "3.25.76"
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { type CapabilityResponse, type ExecuteRequest } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
2
|
-
import type { Runtime } from '../runtime/runtime';
|
|
3
|
-
import type { Workflow } from '../workflow';
|
|
4
|
-
export declare const handleExecuteRequest: <TConfig>(req: ExecuteRequest, workflow: Workflow<TConfig>, config: TConfig, runtime: Runtime) => Promise<CapabilityResponse | undefined>;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Mode, } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
2
|
-
import { handleExecutionPhase } from './handleExecutionPhase';
|
|
3
|
-
import { handleSubscribePhase } from './handleSubscribePhase';
|
|
4
|
-
export const handleExecuteRequest = async (req, workflow, config, runtime) => {
|
|
5
|
-
if (req.request.case === 'subscribe') {
|
|
6
|
-
return handleSubscribePhase(req, workflow);
|
|
7
|
-
}
|
|
8
|
-
if (req.request.case === 'trigger') {
|
|
9
|
-
runtime.switchModes(Mode.DON);
|
|
10
|
-
return handleExecutionPhase(req, workflow, config, runtime);
|
|
11
|
-
}
|
|
12
|
-
};
|