@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
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from 'bun:test';
|
|
2
|
-
import { create, fromBinary, toBinary } from '@bufbuild/protobuf';
|
|
3
|
-
import { EmptySchema } from '@bufbuild/protobuf/wkt';
|
|
4
|
-
import { TriggerEventSchema as ActionTriggerEventSchema } from '../../generated/capabilities/internal/actionandtrigger/v1/action_and_trigger_pb';
|
|
5
|
-
import { OutputsSchema as BasicTriggerOutputsSchema } from '../../generated/capabilities/internal/basictrigger/v1/basic_trigger_pb';
|
|
6
|
-
import { ExecuteRequestSchema, ExecutionResultSchema, } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
7
|
-
import { BasicCapability as ActionAndTriggerCapability } from '../../generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen';
|
|
8
|
-
import { BasicCapability as BasicTriggerCapability } from '../../generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen';
|
|
9
|
-
import { handleExecuteRequest } from './execute';
|
|
10
|
-
import { mockHostBindings } from '../testhelpers/mock-host-bindings';
|
|
11
|
-
import { mockedRuntime } from '../testhelpers/mock-runtime';
|
|
12
|
-
import { getTypeUrl } from '../utils/typeurl';
|
|
13
|
-
import { handler } from '../workflow';
|
|
14
|
-
const emptyConfig = {};
|
|
15
|
-
const decodeExecutionResult = (result) => fromBinary(ExecutionResultSchema, result);
|
|
16
|
-
describe('engine/execute', () => {
|
|
17
|
-
test('subscribe returns TriggerSubscriptionRequest wrapped in ExecutionResult', async () => {
|
|
18
|
-
const subs = [];
|
|
19
|
-
// mock sendResponse to capture the payload
|
|
20
|
-
mockHostBindings.sendResponse.mockImplementation((resp) => {
|
|
21
|
-
const exec = decodeExecutionResult(resp);
|
|
22
|
-
const ts = exec.result.case === 'triggerSubscriptions' ? exec.result.value : undefined;
|
|
23
|
-
expect(ts).toBeDefined();
|
|
24
|
-
expect(ts.subscriptions.length).toBe(3);
|
|
25
|
-
ts.subscriptions.forEach((s) => subs.push(`${s.id}:${s.method}:${s.payload?.typeUrl}`));
|
|
26
|
-
return 0;
|
|
27
|
-
});
|
|
28
|
-
const basic = new BasicTriggerCapability();
|
|
29
|
-
const action = new ActionAndTriggerCapability();
|
|
30
|
-
const workflow = [
|
|
31
|
-
handler(basic.trigger({ name: 'first-trigger', number: 100 }), () => { }),
|
|
32
|
-
handler(action.trigger({ name: 'second-trigger', number: 150 }), () => { }),
|
|
33
|
-
handler(basic.trigger({ name: 'third-trigger', number: 200 }), () => { }),
|
|
34
|
-
];
|
|
35
|
-
const req = create(ExecuteRequestSchema, {
|
|
36
|
-
config: new Uint8Array(),
|
|
37
|
-
request: { case: 'subscribe', value: create(EmptySchema) },
|
|
38
|
-
maxResponseSize: 0n,
|
|
39
|
-
});
|
|
40
|
-
await handleExecuteRequest(req, workflow, emptyConfig, mockedRuntime);
|
|
41
|
-
expect(subs[0]).toContain('basic-test-trigger@1.0.0:Trigger:type.googleapis.com/capabilities.internal.basictrigger.v1.Config');
|
|
42
|
-
expect(subs[1]).toContain('basic-test-action-trigger@1.0.0:Trigger:type.googleapis.com/capabilities.internal.actionandtrigger.v1.Config');
|
|
43
|
-
expect(subs[2]).toContain('basic-test-trigger@1.0.0:Trigger:type.googleapis.com/capabilities.internal.basictrigger.v1.Config');
|
|
44
|
-
mockHostBindings.sendResponse.mockRestore();
|
|
45
|
-
});
|
|
46
|
-
test('trigger routes by id and decodes payload for correct handler', async () => {
|
|
47
|
-
const calls = [];
|
|
48
|
-
const basic = new BasicTriggerCapability();
|
|
49
|
-
const action = new ActionAndTriggerCapability();
|
|
50
|
-
const workflow = [
|
|
51
|
-
handler(basic.trigger({ name: 'first-trigger', number: 100 }), (_e, _r, out) => {
|
|
52
|
-
// @ts-ignore
|
|
53
|
-
calls.push(`basic:${out.coolOutput}`);
|
|
54
|
-
}),
|
|
55
|
-
handler(action.trigger({ name: 'second-trigger', number: 150 }), (_e, _r, out) => {
|
|
56
|
-
// @ts-ignore
|
|
57
|
-
calls.push(`action:${out.coolOutput}`);
|
|
58
|
-
}),
|
|
59
|
-
handler(basic.trigger({ name: 'third-trigger', number: 200 }), (_e, _r, out) => {
|
|
60
|
-
// @ts-ignore
|
|
61
|
-
calls.push(`basic2:${out.coolOutput}`);
|
|
62
|
-
}),
|
|
63
|
-
];
|
|
64
|
-
// Build ExecuteRequest with id=1 (second handler) and payload of Action TriggerEvent
|
|
65
|
-
const payloadAny = {
|
|
66
|
-
typeUrl: getTypeUrl(ActionTriggerEventSchema),
|
|
67
|
-
value: toBinary(ActionTriggerEventSchema, create(ActionTriggerEventSchema, { coolOutput: 'different' })),
|
|
68
|
-
};
|
|
69
|
-
const req = create(ExecuteRequestSchema, {
|
|
70
|
-
config: new Uint8Array(),
|
|
71
|
-
request: {
|
|
72
|
-
case: 'trigger',
|
|
73
|
-
value: { id: 1n, payload: payloadAny },
|
|
74
|
-
},
|
|
75
|
-
maxResponseSize: 0n,
|
|
76
|
-
});
|
|
77
|
-
await handleExecuteRequest(req, workflow, emptyConfig, mockedRuntime);
|
|
78
|
-
expect(calls).toEqual(['action:different']);
|
|
79
|
-
});
|
|
80
|
-
test('trigger ignores out-of-range id (no handler invoked)', async () => {
|
|
81
|
-
const calls = [];
|
|
82
|
-
const basic = new BasicTriggerCapability();
|
|
83
|
-
const action = new ActionAndTriggerCapability();
|
|
84
|
-
const workflow = [
|
|
85
|
-
handler(basic.trigger({ name: 'first-trigger', number: 100 }), () => {
|
|
86
|
-
calls.push('basic');
|
|
87
|
-
}),
|
|
88
|
-
handler(action.trigger({ name: 'second-trigger', number: 150 }), () => {
|
|
89
|
-
calls.push('action');
|
|
90
|
-
}),
|
|
91
|
-
];
|
|
92
|
-
const payloadAny = {
|
|
93
|
-
typeUrl: getTypeUrl(ActionTriggerEventSchema),
|
|
94
|
-
value: toBinary(ActionTriggerEventSchema, create(ActionTriggerEventSchema, { coolOutput: 'x' })),
|
|
95
|
-
};
|
|
96
|
-
const req = create(ExecuteRequestSchema, {
|
|
97
|
-
config: new Uint8Array(),
|
|
98
|
-
request: { case: 'trigger', value: { id: 999n, payload: payloadAny } },
|
|
99
|
-
maxResponseSize: 0n,
|
|
100
|
-
});
|
|
101
|
-
await handleExecuteRequest(req, workflow, emptyConfig, mockedRuntime);
|
|
102
|
-
expect(calls).toEqual([]);
|
|
103
|
-
});
|
|
104
|
-
test('trigger ignores typeUrl mismatch for targeted handler', async () => {
|
|
105
|
-
const calls = [];
|
|
106
|
-
const basic = new BasicTriggerCapability();
|
|
107
|
-
const action = new ActionAndTriggerCapability();
|
|
108
|
-
const workflow = [
|
|
109
|
-
handler(basic.trigger({ name: 'first-trigger', number: 100 }), () => {
|
|
110
|
-
calls.push('basic');
|
|
111
|
-
}),
|
|
112
|
-
handler(action.trigger({ name: 'second-trigger', number: 150 }), () => {
|
|
113
|
-
calls.push('action');
|
|
114
|
-
}),
|
|
115
|
-
];
|
|
116
|
-
// Intentionally send BasicTriggerOutputs payload to the action trigger handler (index 1)
|
|
117
|
-
const payloadAny = {
|
|
118
|
-
typeUrl: `type.googleapis.com/${BasicTriggerOutputsSchema.typeName}`,
|
|
119
|
-
value: toBinary(BasicTriggerOutputsSchema, create(BasicTriggerOutputsSchema, { coolOutput: 'mismatch' })),
|
|
120
|
-
};
|
|
121
|
-
const req = create(ExecuteRequestSchema, {
|
|
122
|
-
config: new Uint8Array(),
|
|
123
|
-
request: { case: 'trigger', value: { id: 1n, payload: payloadAny } },
|
|
124
|
-
maxResponseSize: 0n,
|
|
125
|
-
});
|
|
126
|
-
await handleExecuteRequest(req, workflow, emptyConfig, mockedRuntime);
|
|
127
|
-
expect(calls).toEqual([]);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { CapabilityResponse, ExecuteRequest } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
2
|
-
import type { Runtime } from '../runtime/runtime';
|
|
3
|
-
import type { Workflow } from '../workflow';
|
|
4
|
-
export declare const handleExecutionPhase: <TConfig>(req: ExecuteRequest, workflow: Workflow<TConfig>, config: TConfig, runtime: Runtime) => Promise<CapabilityResponse | undefined>;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { fromBinary } from '@bufbuild/protobuf';
|
|
2
|
-
import { getTypeUrl } from '../utils/typeurl';
|
|
3
|
-
export const handleExecutionPhase = async (req, workflow, config, runtime) => {
|
|
4
|
-
if (req.request.case !== 'trigger') {
|
|
5
|
-
return;
|
|
6
|
-
}
|
|
7
|
-
const triggerMsg = req.request.value;
|
|
8
|
-
const index = Number(triggerMsg.id);
|
|
9
|
-
if (Number.isFinite(index) && index >= 0 && index < workflow.length) {
|
|
10
|
-
const entry = workflow[index];
|
|
11
|
-
const schema = entry.trigger.outputSchema();
|
|
12
|
-
const payloadAny = triggerMsg.payload;
|
|
13
|
-
if (!payloadAny) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
// Extra safety: verify payload typeUrl matches expected schema type
|
|
17
|
-
const expectedTypeUrl = getTypeUrl(schema);
|
|
18
|
-
if (payloadAny.typeUrl && payloadAny.typeUrl !== expectedTypeUrl) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Note: do not hardcode method name; routing by id is authoritative.
|
|
23
|
-
*
|
|
24
|
-
* This matches the GO SDK behavior, which also just checks for the id.
|
|
25
|
-
*
|
|
26
|
-
* @see https://github.com/smartcontractkit/cre-sdk-go/blob/5a41d81e3e072008484e85dc96d746401aafcba2/cre/wasm/runner.go#L81
|
|
27
|
-
* */
|
|
28
|
-
const decoded = fromBinary(schema, payloadAny.value);
|
|
29
|
-
const adapted = await entry.trigger.adapt(decoded);
|
|
30
|
-
// By default config is a JSON string, send as bytes
|
|
31
|
-
const handlerEnv = config || JSON.parse(Buffer.from(req.config).toString());
|
|
32
|
-
await entry.fn(handlerEnv, runtime, adapted);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { create, toBinary } from '@bufbuild/protobuf';
|
|
2
|
-
import { ExecutionResultSchema, TriggerSubscriptionRequestSchema, } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
3
|
-
import { hostBindings } from '../runtime/host-bindings';
|
|
4
|
-
export const handleSubscribePhase = (req, workflow) => {
|
|
5
|
-
if (req.request.case !== 'subscribe') {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
// Build TriggerSubscriptionRequest from the workflow entries
|
|
9
|
-
const subscriptions = workflow.map((entry) => ({
|
|
10
|
-
id: entry.trigger.capabilityId(),
|
|
11
|
-
method: entry.trigger.method(),
|
|
12
|
-
payload: entry.trigger.configAsAny(),
|
|
13
|
-
}));
|
|
14
|
-
const subscriptionRequest = create(TriggerSubscriptionRequestSchema, {
|
|
15
|
-
subscriptions,
|
|
16
|
-
});
|
|
17
|
-
// Wrap in ExecutionResult.triggerSubscriptions
|
|
18
|
-
const execResult = create(ExecutionResultSchema, {
|
|
19
|
-
result: { case: 'triggerSubscriptions', value: subscriptionRequest },
|
|
20
|
-
});
|
|
21
|
-
const encoded = toBinary(ExecutionResultSchema, execResult);
|
|
22
|
-
hostBindings.sendResponse(encoded);
|
|
23
|
-
};
|
package/dist/sdk/logger.d.ts
DELETED
package/dist/sdk/logger.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export const logger = {
|
|
2
|
-
log: (message) => {
|
|
3
|
-
console.log(message);
|
|
4
|
-
log(message);
|
|
5
|
-
},
|
|
6
|
-
info: (message) => {
|
|
7
|
-
console.info(message);
|
|
8
|
-
log(message);
|
|
9
|
-
},
|
|
10
|
-
error: (message) => {
|
|
11
|
-
console.error(message);
|
|
12
|
-
log(message);
|
|
13
|
-
},
|
|
14
|
-
warn: (message) => {
|
|
15
|
-
console.warn(message);
|
|
16
|
-
log(message);
|
|
17
|
-
},
|
|
18
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { Mode } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
2
|
-
export declare class DonModeError extends Error {
|
|
3
|
-
name: string;
|
|
4
|
-
capabilityId?: string;
|
|
5
|
-
method?: string;
|
|
6
|
-
mode?: Mode;
|
|
7
|
-
constructor(message?: string, options?: {
|
|
8
|
-
capabilityId?: string;
|
|
9
|
-
method?: string;
|
|
10
|
-
mode?: Mode;
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
export declare class NodeModeError extends Error {
|
|
14
|
-
name: string;
|
|
15
|
-
capabilityId?: string;
|
|
16
|
-
method?: string;
|
|
17
|
-
mode?: Mode;
|
|
18
|
-
constructor(message?: string, options?: {
|
|
19
|
-
capabilityId?: string;
|
|
20
|
-
method?: string;
|
|
21
|
-
mode?: Mode;
|
|
22
|
-
});
|
|
23
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export class DonModeError extends Error {
|
|
2
|
-
name;
|
|
3
|
-
capabilityId;
|
|
4
|
-
method;
|
|
5
|
-
mode;
|
|
6
|
-
constructor(message = 'cannot use Runtime inside RunInNodeMode', options) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.name = 'DonModeError';
|
|
9
|
-
if (options) {
|
|
10
|
-
this.capabilityId = options.capabilityId;
|
|
11
|
-
this.method = options.method;
|
|
12
|
-
this.mode = options.mode;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export class NodeModeError extends Error {
|
|
17
|
-
name;
|
|
18
|
-
capabilityId;
|
|
19
|
-
method;
|
|
20
|
-
mode;
|
|
21
|
-
constructor(message = 'cannot use NodeRuntime outside RunInNodeMode', options) {
|
|
22
|
-
super(message);
|
|
23
|
-
this.name = 'NodeModeError';
|
|
24
|
-
if (options) {
|
|
25
|
-
this.capabilityId = options.capabilityId;
|
|
26
|
-
this.method = options.method;
|
|
27
|
-
this.mode = options.mode;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { type NodeRuntime } from './runtime';
|
|
2
|
-
import type { ConsensusAggregation, PrimitiveTypes, UnwrapOptions } from '../utils';
|
|
3
|
-
/**
|
|
4
|
-
* Runs the provided builder inside Node mode and returns the consensus result Value.
|
|
5
|
-
* Ensures mode is switched back to DON even if errors occur.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Runs the provided builder inside Node mode and returns the consensus result Value.
|
|
9
|
-
* For primitive types (number, bigint, Date, boolean, string), it will use unwrap()
|
|
10
|
-
* For complex types, it will use unwrapToType() with the provided options
|
|
11
|
-
*/
|
|
12
|
-
export declare function runInNodeMode<TArgs extends any[], TOutput>(fn: (nodeRuntime: NodeRuntime, ...args: TArgs) => Promise<TOutput> | TOutput, consesusAggretation: ConsensusAggregation<TOutput, true>, unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions<TOutput>): (...args: TArgs) => Promise<TOutput>;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { create } from '@bufbuild/protobuf';
|
|
2
|
-
import { Mode, SimpleConsensusInputsSchema } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
3
|
-
import { ConsensusCapability } from '../../generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen';
|
|
4
|
-
import { runtime } from './runtime';
|
|
5
|
-
import { Value } from '../utils';
|
|
6
|
-
/**
|
|
7
|
-
* Runs the provided builder inside Node mode and returns the consensus result Value.
|
|
8
|
-
* Ensures mode is switched back to DON even if errors occur.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Runs the provided builder inside Node mode and returns the consensus result Value.
|
|
12
|
-
* For primitive types (number, bigint, Date, boolean, string), it will use unwrap()
|
|
13
|
-
* For complex types, it will use unwrapToType() with the provided options
|
|
14
|
-
*/
|
|
15
|
-
export function runInNodeMode(fn, consesusAggretation, unwrapOptions) {
|
|
16
|
-
return async (...args) => {
|
|
17
|
-
const nodeRuntime = runtime.switchModes(Mode.NODE);
|
|
18
|
-
const consensusInput = create(SimpleConsensusInputsSchema, {
|
|
19
|
-
descriptors: consesusAggretation.descriptor,
|
|
20
|
-
});
|
|
21
|
-
if (consesusAggretation.defaultValue) {
|
|
22
|
-
// This cast is safe, since ConsensusAggregation can only have true its second argument if T extends CreSerializable<TOutput>
|
|
23
|
-
consensusInput.default = Value.from(consesusAggretation.defaultValue).proto();
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
const observation = await fn(nodeRuntime, ...args);
|
|
27
|
-
// This cast is safe, since ConsensusAggregation can only have true its second argument if T extends CreSerializable<TOutput>
|
|
28
|
-
consensusInput.observation = {
|
|
29
|
-
case: 'value',
|
|
30
|
-
value: Value.from(observation).proto(),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
catch (e) {
|
|
34
|
-
consensusInput.observation = { case: 'error', value: e.message || String(e) };
|
|
35
|
-
}
|
|
36
|
-
finally {
|
|
37
|
-
// Always restore DON mode before invoking consensus
|
|
38
|
-
runtime.switchModes(Mode.DON);
|
|
39
|
-
}
|
|
40
|
-
const consensus = new ConsensusCapability();
|
|
41
|
-
const result = await consensus.simple(consensusInput).result();
|
|
42
|
-
const wrappedValue = Value.wrap(result);
|
|
43
|
-
return unwrapOptions
|
|
44
|
-
? wrappedValue.unwrapToType(unwrapOptions)
|
|
45
|
-
: wrappedValue.unwrap();
|
|
46
|
-
};
|
|
47
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from 'bun:test';
|
|
2
|
-
import { create } from '@bufbuild/protobuf';
|
|
3
|
-
import { AggregationType, ConsensusDescriptorSchema, } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
4
|
-
import { BasicActionCapability } from '../../generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen';
|
|
5
|
-
import { ConsensusCapability } from '../../generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen';
|
|
6
|
-
import { runInNodeMode } from './run-in-node-mode';
|
|
7
|
-
// Mock the host bindings before importing runtime
|
|
8
|
-
import { calls } from '../testhelpers/mock-host-bindings';
|
|
9
|
-
import { consensusIdenticalAggregation, consensusMedianAggregation, Value } from '../utils';
|
|
10
|
-
function expectObservation(i) {
|
|
11
|
-
expect(i.observation.case).toEqual('value');
|
|
12
|
-
return Value.wrap(i.observation.value);
|
|
13
|
-
}
|
|
14
|
-
function expectError(i) {
|
|
15
|
-
expect(i.observation.case).toEqual('error');
|
|
16
|
-
return i.observation.value;
|
|
17
|
-
}
|
|
18
|
-
describe('runInNodeMode', () => {
|
|
19
|
-
test('successful run', async () => {
|
|
20
|
-
// Clear the calls array for this test
|
|
21
|
-
calls.length = 0;
|
|
22
|
-
const anyObservation = 120;
|
|
23
|
-
const anyResult = 123;
|
|
24
|
-
// spy on consensus.simple
|
|
25
|
-
const origSimple = ConsensusCapability.prototype.simple;
|
|
26
|
-
ConsensusCapability.prototype.simple = mock((inputs) => {
|
|
27
|
-
calls.push('CONSENSUS_SIMPLE');
|
|
28
|
-
// biome-ignore lint/suspicious/noExplicitAny: Needed for runtime type checking of protocol buffer messages
|
|
29
|
-
expect(inputs.$typeName).toBeDefined();
|
|
30
|
-
const castedInputs = inputs;
|
|
31
|
-
expect(castedInputs.default).toBeUndefined();
|
|
32
|
-
const actualObservation = expectObservation(castedInputs);
|
|
33
|
-
expect(Value.from(anyObservation)).toEqual(actualObservation);
|
|
34
|
-
const expectedDescriptor = create(ConsensusDescriptorSchema, {
|
|
35
|
-
descriptor: {
|
|
36
|
-
case: 'aggregation',
|
|
37
|
-
value: AggregationType.MEDIAN,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
expect(castedInputs.descriptors).toEqual(expectedDescriptor);
|
|
41
|
-
return { result: async () => Value.from(anyResult).proto() };
|
|
42
|
-
});
|
|
43
|
-
const res = await runInNodeMode((_) => anyObservation, consensusMedianAggregation())();
|
|
44
|
-
expect(res).toEqual(anyResult);
|
|
45
|
-
expect(calls).toEqual(['NODE', 'DON', 'CONSENSUS_SIMPLE']);
|
|
46
|
-
// Restore the original method after the test
|
|
47
|
-
ConsensusCapability.prototype.simple = origSimple;
|
|
48
|
-
});
|
|
49
|
-
test('local failure with consensus', async () => {
|
|
50
|
-
// Clear the calls array for this test
|
|
51
|
-
calls.length = 0;
|
|
52
|
-
const anyError = Error('nope');
|
|
53
|
-
const anyResult = 123;
|
|
54
|
-
// spy on consensus.simple
|
|
55
|
-
const origSimple = ConsensusCapability.prototype.simple;
|
|
56
|
-
ConsensusCapability.prototype.simple = mock((inputs) => {
|
|
57
|
-
calls.push('CONSENSUS_SIMPLE');
|
|
58
|
-
// biome-ignore lint/suspicious/noExplicitAny: Needed for runtime type checking of protocol buffer messages
|
|
59
|
-
expect(inputs.$typeName).toBeDefined();
|
|
60
|
-
const castedInputs = inputs;
|
|
61
|
-
expect(castedInputs.default).toBeUndefined();
|
|
62
|
-
const actualError = expectError(castedInputs);
|
|
63
|
-
expect(anyError.message).toEqual(actualError);
|
|
64
|
-
const expectedDescriptor = create(ConsensusDescriptorSchema, {
|
|
65
|
-
descriptor: {
|
|
66
|
-
case: 'aggregation',
|
|
67
|
-
value: AggregationType.MEDIAN,
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
expect(castedInputs.descriptors).toEqual(expectedDescriptor);
|
|
71
|
-
return { result: async () => Value.from(anyResult).proto() };
|
|
72
|
-
});
|
|
73
|
-
const errFn = (_) => {
|
|
74
|
-
throw anyError;
|
|
75
|
-
};
|
|
76
|
-
const res = await runInNodeMode(errFn, consensusMedianAggregation())();
|
|
77
|
-
expect(res).toEqual(anyResult);
|
|
78
|
-
expect(calls).toEqual(['NODE', 'DON', 'CONSENSUS_SIMPLE']);
|
|
79
|
-
// Restore the original method after the test
|
|
80
|
-
ConsensusCapability.prototype.simple = origSimple;
|
|
81
|
-
});
|
|
82
|
-
test('guards DON calls while in node mode, also local and consensus failure', async () => {
|
|
83
|
-
// Simulate switchModes by touching global function used by host
|
|
84
|
-
const origSwitch = globalThis.switchModes;
|
|
85
|
-
globalThis.switchModes = (_m) => { };
|
|
86
|
-
// spy on consensus.simple
|
|
87
|
-
const origSimple = ConsensusCapability.prototype.simple;
|
|
88
|
-
ConsensusCapability.prototype.simple = mock((inputs) => {
|
|
89
|
-
calls.push('CONSENSUS_SIMPLE');
|
|
90
|
-
// biome-ignore lint/suspicious/noExplicitAny: Needed for runtime type checking of protocol buffer messages
|
|
91
|
-
expect(inputs.$typeName).toBeDefined();
|
|
92
|
-
const castedInputs = inputs;
|
|
93
|
-
expect(castedInputs.default).toBeUndefined();
|
|
94
|
-
const actualError = expectError(castedInputs);
|
|
95
|
-
expect(actualError).toContain('cannot use Runtime inside RunInNodeMode.');
|
|
96
|
-
const expectedDescriptor = create(ConsensusDescriptorSchema, {
|
|
97
|
-
descriptor: {
|
|
98
|
-
case: 'aggregation',
|
|
99
|
-
value: AggregationType.IDENTICAL,
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
expect(castedInputs.descriptors).toEqual(expectedDescriptor);
|
|
103
|
-
throw Error(actualError);
|
|
104
|
-
});
|
|
105
|
-
expect(async () => {
|
|
106
|
-
await runInNodeMode(async (_) => {
|
|
107
|
-
const ba = new BasicActionCapability();
|
|
108
|
-
const result = await ba.performAction({ inputThing: true }).result();
|
|
109
|
-
return result.adaptedThing;
|
|
110
|
-
}, consensusIdenticalAggregation())();
|
|
111
|
-
}).toThrow(/.*cannot use Runtime inside RunInNodeMode.*/);
|
|
112
|
-
// Restore the original method and global after the test
|
|
113
|
-
ConsensusCapability.prototype.simple = origSimple;
|
|
114
|
-
globalThis.switchModes = origSwitch;
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { Mode } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
2
|
-
import { type Logger } from '../logger';
|
|
3
|
-
import type { Rand } from '../utils/random/random';
|
|
4
|
-
/**
|
|
5
|
-
* Runtime guards are not actually causing / throwing errors.
|
|
6
|
-
* They pre-set the errors, depending on the current mode and then
|
|
7
|
-
* assert methods are used to check if feature can be called in given mode.
|
|
8
|
-
* If not the prepared error will be thrown.
|
|
9
|
-
*/
|
|
10
|
-
export declare const runtimeGuards: {
|
|
11
|
-
setMode: (mode: Mode) => void;
|
|
12
|
-
assertDonSafe: () => void;
|
|
13
|
-
assertNodeSafe: () => void;
|
|
14
|
-
getMode: () => Mode;
|
|
15
|
-
};
|
|
16
|
-
export type BaseRuntime<M extends Mode = Mode> = {
|
|
17
|
-
logger: Logger;
|
|
18
|
-
mode: M;
|
|
19
|
-
assertDonSafe(): asserts this is Runtime;
|
|
20
|
-
assertNodeSafe(): asserts this is NodeRuntime;
|
|
21
|
-
getRand(): Rand;
|
|
22
|
-
now(): Date;
|
|
23
|
-
};
|
|
24
|
-
export type Runtime = BaseRuntime<Mode.DON> & {
|
|
25
|
-
isNodeRuntime: false;
|
|
26
|
-
switchModes(mode: Mode.NODE): NodeRuntime;
|
|
27
|
-
switchModes(mode: Mode.DON): Runtime;
|
|
28
|
-
getSecret(id: string): Promise<any>;
|
|
29
|
-
};
|
|
30
|
-
export type NodeRuntime = BaseRuntime<Mode.NODE> & {
|
|
31
|
-
isNodeRuntime: true;
|
|
32
|
-
switchModes(mode: Mode.NODE): NodeRuntime;
|
|
33
|
-
switchModes(mode: Mode.DON): Runtime;
|
|
34
|
-
};
|
|
35
|
-
export declare const runtime: Runtime;
|
|
36
|
-
export declare const nodeRuntime: NodeRuntime;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Mode } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
2
|
-
import { logger } from '../logger';
|
|
3
|
-
import { DonModeError, NodeModeError } from './errors';
|
|
4
|
-
import { hostBindings } from './host-bindings';
|
|
5
|
-
import { getRand } from '../utils/random/get-rand';
|
|
6
|
-
import { getSecret } from '../utils/secrets/get-secret';
|
|
7
|
-
import { getTimeAsDate } from '../utils/time/get-time';
|
|
8
|
-
/**
|
|
9
|
-
* Runtime guards are not actually causing / throwing errors.
|
|
10
|
-
* They pre-set the errors, depending on the current mode and then
|
|
11
|
-
* assert methods are used to check if feature can be called in given mode.
|
|
12
|
-
* If not the prepared error will be thrown.
|
|
13
|
-
*/
|
|
14
|
-
export const runtimeGuards = (() => {
|
|
15
|
-
let currentMode = Mode.UNSPECIFIED;
|
|
16
|
-
let donModeGuardError = null;
|
|
17
|
-
let nodeModeGuardError = new NodeModeError();
|
|
18
|
-
const setMode = (mode) => {
|
|
19
|
-
currentMode = mode;
|
|
20
|
-
if (mode === Mode.NODE) {
|
|
21
|
-
// In node mode, forbid DON runtime calls
|
|
22
|
-
donModeGuardError = new DonModeError();
|
|
23
|
-
nodeModeGuardError = null;
|
|
24
|
-
}
|
|
25
|
-
else if (mode === Mode.DON) {
|
|
26
|
-
// Back in DON mode, forbid node runtime calls
|
|
27
|
-
nodeModeGuardError = new NodeModeError();
|
|
28
|
-
donModeGuardError = null;
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
donModeGuardError = null;
|
|
32
|
-
nodeModeGuardError = null;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
const assertDonSafe = () => {
|
|
36
|
-
if (donModeGuardError) {
|
|
37
|
-
throw donModeGuardError;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
const assertNodeSafe = () => {
|
|
41
|
-
if (nodeModeGuardError) {
|
|
42
|
-
throw nodeModeGuardError;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const getMode = () => currentMode;
|
|
46
|
-
return { setMode, assertDonSafe, assertNodeSafe, getMode };
|
|
47
|
-
})();
|
|
48
|
-
function switchModes(mode) {
|
|
49
|
-
// Changing to the same mode should be a noop, we make sure to actually call switching logic if it's different mode
|
|
50
|
-
if (mode !== runtimeGuards.getMode()) {
|
|
51
|
-
hostBindings.switchModes(mode);
|
|
52
|
-
runtimeGuards.setMode(mode);
|
|
53
|
-
}
|
|
54
|
-
return mode === Mode.NODE ? nodeRuntime : runtime;
|
|
55
|
-
}
|
|
56
|
-
export const runtime = {
|
|
57
|
-
mode: Mode.DON,
|
|
58
|
-
isNodeRuntime: false,
|
|
59
|
-
logger,
|
|
60
|
-
switchModes,
|
|
61
|
-
assertDonSafe: () => {
|
|
62
|
-
runtimeGuards.assertDonSafe();
|
|
63
|
-
},
|
|
64
|
-
assertNodeSafe: () => {
|
|
65
|
-
runtimeGuards.assertNodeSafe();
|
|
66
|
-
},
|
|
67
|
-
getSecret,
|
|
68
|
-
getRand: () => getRand(Mode.DON),
|
|
69
|
-
now: () => getTimeAsDate(),
|
|
70
|
-
};
|
|
71
|
-
export const nodeRuntime = {
|
|
72
|
-
mode: Mode.NODE,
|
|
73
|
-
isNodeRuntime: true,
|
|
74
|
-
logger,
|
|
75
|
-
switchModes,
|
|
76
|
-
assertNodeSafe: () => {
|
|
77
|
-
runtimeGuards.assertNodeSafe();
|
|
78
|
-
},
|
|
79
|
-
assertDonSafe: () => {
|
|
80
|
-
runtimeGuards.assertDonSafe();
|
|
81
|
-
},
|
|
82
|
-
getRand: () => getRand(Mode.NODE),
|
|
83
|
-
now: () => getTimeAsDate(),
|
|
84
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import '../testhelpers/mock-host-bindings';
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, test } from 'bun:test';
|
|
2
|
-
import { Mode } from '../../generated/sdk/v1alpha/sdk_pb';
|
|
3
|
-
// Mock the host bindings before importing runtime
|
|
4
|
-
import '../testhelpers/mock-host-bindings';
|
|
5
|
-
import { DonModeError, NodeModeError } from './errors';
|
|
6
|
-
import { runtime, runtimeGuards } from './runtime';
|
|
7
|
-
describe('Handling runtime in TS SDK', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
runtimeGuards.setMode(Mode.DON);
|
|
10
|
-
});
|
|
11
|
-
test('runtime should be DON by default', () => {
|
|
12
|
-
expect(runtime.mode).toBe(Mode.DON);
|
|
13
|
-
});
|
|
14
|
-
test('switching modes should work', () => {
|
|
15
|
-
// Start from DON mode
|
|
16
|
-
expect(runtime.isNodeRuntime).toBe(false);
|
|
17
|
-
// Switch to NODE mode and verify it works
|
|
18
|
-
const nodeRt = runtime.switchModes(Mode.NODE);
|
|
19
|
-
expect(nodeRt.isNodeRuntime).toBe(true);
|
|
20
|
-
expect(nodeRt.mode).toBe(Mode.NODE);
|
|
21
|
-
// Switch back to DON mode and verify it works
|
|
22
|
-
const rt = nodeRt.switchModes(Mode.DON);
|
|
23
|
-
expect(rt.mode).toBe(Mode.DON);
|
|
24
|
-
expect(rt.isNodeRuntime).toBe(false);
|
|
25
|
-
});
|
|
26
|
-
test('switching modes should be noop when already in that mode', () => {
|
|
27
|
-
// By default we are in DON mode, but we call switchModes to confirm that's a noop
|
|
28
|
-
const rt = runtime.switchModes(Mode.DON);
|
|
29
|
-
expect(rt.mode).toBe(Mode.DON);
|
|
30
|
-
expect(rt.isNodeRuntime).toBe(false);
|
|
31
|
-
// // Now actually switch to NODE mode
|
|
32
|
-
let nodeRuntime = rt.switchModes(Mode.NODE);
|
|
33
|
-
expect(nodeRuntime.mode).toBe(Mode.NODE);
|
|
34
|
-
expect(nodeRuntime.isNodeRuntime).toBe(true);
|
|
35
|
-
// And now switch to Node mode again, make sure it's a noop and the runtime is still in NODE mode.
|
|
36
|
-
nodeRuntime = nodeRuntime.switchModes(Mode.NODE);
|
|
37
|
-
expect(nodeRuntime.mode).toBe(Mode.NODE);
|
|
38
|
-
expect(nodeRuntime.isNodeRuntime).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
test('assertDonSafe should throw an error if called in NODE mode', () => {
|
|
41
|
-
// DON mode is default so asserting DON safe should not throw an error
|
|
42
|
-
runtime.assertDonSafe();
|
|
43
|
-
// Switch to NODE mode and assert DON safe should throw an error
|
|
44
|
-
const nodeRuntime = runtime.switchModes(Mode.NODE);
|
|
45
|
-
expect(() => nodeRuntime.assertDonSafe()).toThrow(DonModeError);
|
|
46
|
-
// Asserting node safe should not throw however
|
|
47
|
-
nodeRuntime.assertNodeSafe();
|
|
48
|
-
const rt = nodeRuntime.switchModes(Mode.DON);
|
|
49
|
-
expect(() => rt.assertNodeSafe()).toThrow(NodeModeError);
|
|
50
|
-
});
|
|
51
|
-
test('getSecret is only available in DON mode', () => {
|
|
52
|
-
// Casting to any because typescript itself is not letting us do this
|
|
53
|
-
const nodeRuntime = runtime.switchModes(Mode.NODE);
|
|
54
|
-
expect(nodeRuntime.getSecret).toBeUndefined();
|
|
55
|
-
const rt = nodeRuntime.switchModes(Mode.DON);
|
|
56
|
-
expect(rt.getSecret).toBeDefined();
|
|
57
|
-
});
|
|
58
|
-
});
|