From 10e13e85648d7be3a722dedd21971cd5e8e378cf Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Tue, 5 May 2026 15:40:40 -0400 Subject: [PATCH 01/20] Added getSecrets method to fetch multiple secrets --- packages/cre-sdk-examples/.env.example | 7 +- packages/cre-sdk-examples/package.json | 2 +- packages/cre-sdk-examples/secrets.yaml | 10 +- .../src/workflows/secrets/index.ts | 69 +- packages/cre-sdk/src/sdk/errors.ts | 12 + .../cre-sdk/src/sdk/impl/runtime-impl.test.ts | 1424 ++++++++++------- packages/cre-sdk/src/sdk/impl/runtime-impl.ts | 386 +++-- .../src/sdk/testutils/test-runtime.test.ts | 499 +++--- packages/cre-sdk/src/sdk/wasm/runner.test.ts | 438 ++--- packages/cre-sdk/src/sdk/wasm/runner.ts | 137 +- packages/cre-sdk/src/sdk/workflow.ts | 34 +- 11 files changed, 1761 insertions(+), 1257 deletions(-) diff --git a/packages/cre-sdk-examples/.env.example b/packages/cre-sdk-examples/.env.example index e10aed2a..fe668a14 100644 --- a/packages/cre-sdk-examples/.env.example +++ b/packages/cre-sdk-examples/.env.example @@ -13,7 +13,10 @@ CRE_ETH_PRIVATE_KEY=000000000000000000000000000000000000000000000000000000000000 CRE_TARGET=local-simulation # This one will be used in PoR workflow SECRET_ADDRESS_ALL=0x4700A50d858Cb281847ca4Ee0938F80DEfB3F1dd -# This one will be used in secrets workflow -SECRET_CHARACTER_ID=5 +# These will be used in secrets workflow +SECRET_URL_VALUE="https://swapi.info/api/people/{characterId}" +SECRET_CHARACTER_ID1=5 +SECRET_CHARACTER_ID2=6 +SECRET_CHARACTER_ID3=7 # Secret header value for HTTP trigger SECRET_HEADER_VALUE=abcd1234 diff --git a/packages/cre-sdk-examples/package.json b/packages/cre-sdk-examples/package.json index a051e179..3e55227c 100644 --- a/packages/cre-sdk-examples/package.json +++ b/packages/cre-sdk-examples/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@bufbuild/protobuf": "2.6.3", - "@chainlink/cre-sdk": "workspace:*", + "@chainlink/cre-sdk": "1.7.0-alpha.1", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/packages/cre-sdk-examples/secrets.yaml b/packages/cre-sdk-examples/secrets.yaml index 1691a194..871840e6 100644 --- a/packages/cre-sdk-examples/secrets.yaml +++ b/packages/cre-sdk-examples/secrets.yaml @@ -1,7 +1,13 @@ secretsNames: SECRET_ADDRESS: - SECRET_ADDRESS_ALL - CHARACTER_ID: - - SECRET_CHARACTER_ID + SECRET_URL: + - SECRET_URL_VALUE + CHARACTER_ID1: + - SECRET_CHARACTER_ID1 + CHARACTER_ID2: + - SECRET_CHARACTER_ID2 + CHARACTER_ID3: + - SECRET_CHARACTER_ID3 SECRET_HEADER: - SECRET_HEADER_VALUE diff --git a/packages/cre-sdk-examples/src/workflows/secrets/index.ts b/packages/cre-sdk-examples/src/workflows/secrets/index.ts index 7b7e7089..b00c0efa 100644 --- a/packages/cre-sdk-examples/src/workflows/secrets/index.ts +++ b/packages/cre-sdk-examples/src/workflows/secrets/index.ts @@ -8,14 +8,14 @@ import { ok, Runner, type Runtime, -} from '@chainlink/cre-sdk' -import { z } from 'zod' +} from "@chainlink/cre-sdk"; +import { z } from "zod"; const configSchema = z.object({ url: z.string(), -}) +}); -type Config = z.infer +type Config = z.infer; const responseSchema = z.object({ name: z.string(), @@ -34,52 +34,75 @@ const responseSchema = z.object({ created: z.string().datetime(), edited: z.string().datetime(), url: z.string(), -}) +}); -type StarWarsCharacter = z.infer +type StarWarsCharacter = z.infer; const fetchStarWarsCharacter = ( sendRequester: HTTPSendRequester, config: Config, + url: string, characterId: string, ): StarWarsCharacter => { - const url = config.url.replace('{characterId}', characterId) - const response = sendRequester.sendRequest({ url, method: 'GET' }).result() + url = config.url.replace("{characterId}", characterId); + const response = sendRequester.sendRequest({ url, method: "GET" }).result(); // Check if the response is successful using the helper function if (!ok(response)) { - throw new Error(`HTTP request failed with status: ${response.statusCode}`) + throw new Error(`HTTP request failed with status: ${response.statusCode}`); } - const character = responseSchema.parse(json(response)) + const character = responseSchema.parse(json(response)); - return character -} + return character; +}; const onHTTPTrigger = async (runtime: Runtime) => { - const httpCapability = new HTTPClient() - const characterId = runtime.getSecret({ id: 'CHARACTER_ID' }).result().value + const httpCapability = new HTTPClient(); + // Fetch a single secret + const secretUrlValue = runtime.getSecret({ id: "SECRET_URL" }).result().value; + + // Fetch multiple secrets + const secretsToFetch = [ + { id: "CHARACTER_ID1" }, + { id: "CHARACTER_ID2" }, + { id: "CHARACTER_ID3" }, + ]; + const secretResponses = runtime.getSecrets(secretsToFetch).result(); + const characterIds = secretResponses.flatMap((response) => + response.response.case === "secret" && response.response.value?.id + ? [response.response.value.value] + : [], + ); + if (characterIds.length === 0) { + throw new Error("No character ID secrets available"); + } + + // choose a random character id + // Math.random() is safe to use in the workflow + const characterId = + characterIds[Math.floor(Math.random() * characterIds.length)]; const result: StarWarsCharacter = httpCapability .sendRequest( runtime, fetchStarWarsCharacter, consensusIdenticalAggregation(), - )(runtime.config, characterId) - .result() + )(runtime.config, secretUrlValue, characterId) + .result(); - return result -} + return result; +}; const initWorkflow = () => { - const httpTrigger = new HTTPCapability() + const httpTrigger = new HTTPCapability(); - return [handler(httpTrigger.trigger({}), onHTTPTrigger)] -} + return [handler(httpTrigger.trigger({}), onHTTPTrigger)]; +}; export async function main() { const runner = await Runner.newRunner({ configSchema, - }) - await runner.run(initWorkflow) + }); + await runner.run(initWorkflow); } diff --git a/packages/cre-sdk/src/sdk/errors.ts b/packages/cre-sdk/src/sdk/errors.ts index 02fa2186..b3681532 100644 --- a/packages/cre-sdk/src/sdk/errors.ts +++ b/packages/cre-sdk/src/sdk/errors.ts @@ -26,6 +26,18 @@ export class SecretsError extends Error { } } +export class SecretsBatchError extends Error { + constructor( + public readonly secretRequests: SecretRequest[], + public readonly error: string, + ) { + super( + `batch secret retrieval failed for ${secretRequests.length} request(s): ${error}. Verify the host response is complete and that the workflow has access to the requested secrets`, + ) + this.name = 'SecretsBatchError' + } +} + export class NullReportError extends Error { constructor() { super('null report') diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts index 154c8d68..8c8476f9 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts @@ -1,21 +1,21 @@ -import { afterEach, describe, expect, mock, test } from 'bun:test' -import { create } from '@bufbuild/protobuf' -import { type Any, anyPack, anyUnpack } from '@bufbuild/protobuf/wkt' +import { afterEach, describe, expect, mock, test } from "bun:test"; +import { create } from "@bufbuild/protobuf"; +import { type Any, anyPack, anyUnpack } from "@bufbuild/protobuf/wkt"; import { InputSchema, OutputSchema, -} from '@cre/generated/capabilities/internal/actionandtrigger/v1/action_and_trigger_pb' +} from "@cre/generated/capabilities/internal/actionandtrigger/v1/action_and_trigger_pb"; import { InputsSchema, OutputsSchema, -} from '@cre/generated/capabilities/internal/basicaction/v1/basic_action_pb' +} from "@cre/generated/capabilities/internal/basicaction/v1/basic_action_pb"; import { type NodeInputs, type NodeInputsJson, NodeInputsSchema, type NodeOutputs, NodeOutputsSchema, -} from '@cre/generated/capabilities/internal/nodeaction/v1/node_action_pb' +} from "@cre/generated/capabilities/internal/nodeaction/v1/node_action_pb"; import { AggregationType, type AwaitCapabilitiesRequest, @@ -31,13 +31,13 @@ import { SecretResponsesSchema, type SimpleConsensusInputs, type SimpleConsensusInputsJson, -} from '@cre/generated/sdk/v1alpha/sdk_pb' -import type { Value as ProtoValue } from '@cre/generated/values/v1/values_pb' -import { BasicCapability } from '@cre/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen' -import { BasicActionCapability } from '@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen' -import { ConsensusCapability } from '@cre/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen' -import { BasicActionCapability as NodeActionCapability } from '@cre/generated-sdk/capabilities/internal/nodeaction/v1/basicaction_sdk_gen' -import type { NodeRuntime, Runtime } from '@cre/sdk/cre' +} from "@cre/generated/sdk/v1alpha/sdk_pb"; +import type { Value as ProtoValue } from "@cre/generated/values/v1/values_pb"; +import { BasicCapability } from "@cre/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen"; +import { BasicActionCapability } from "@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen"; +import { ConsensusCapability } from "@cre/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen"; +import { BasicActionCapability as NodeActionCapability } from "@cre/generated-sdk/capabilities/internal/nodeaction/v1/basicaction_sdk_gen"; +import type { NodeRuntime, Runtime } from "@cre/sdk/cre"; import { ConsensusAggregationByFields, ConsensusFieldAggregation, @@ -46,141 +46,162 @@ import { ignore, median, Value, -} from '@cre/sdk/utils' -import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' -import { DonModeError, NodeModeError, SecretsError } from '../errors' -import { RESPONSE_BUFFER_TOO_SMALL } from '../testutils/test-runtime' -import { type RuntimeHelpers, RuntimeImpl } from './runtime-impl' +} from "@cre/sdk/utils"; +import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; +import { + DonModeError, + NodeModeError, + SecretsBatchError, + SecretsError, +} from "../errors"; +import { RESPONSE_BUFFER_TOO_SMALL } from "../testutils/test-runtime"; +import { type RuntimeHelpers, RuntimeImpl } from "./runtime-impl"; // Helper function to create a RuntimeHelpers mock with error-throwing defaults -function createRuntimeHelpersMock(overrides: Partial = {}): RuntimeHelpers { +function createRuntimeHelpersMock( + overrides: Partial = {}, +): RuntimeHelpers { // Create default implementation that throws errors for all methods const defaultMock: RuntimeHelpers = { call: mock(() => { - throw new Error('Method not implemented: call') + throw new Error("Method not implemented: call"); }), await: mock(() => { - throw new Error('Method not implemented: await') + throw new Error("Method not implemented: await"); }), getSecrets: mock(() => { - throw new Error('Method not implemented: getSecrets') + throw new Error("Method not implemented: getSecrets"); }), awaitSecrets: mock(() => { - throw new Error('Method not implemented: awaitSecrets') + throw new Error("Method not implemented: awaitSecrets"); }), // switchModes is used in every test, most will ignore it, so it's safe to default to a no-op. switchModes: mock(() => {}), now: mock(() => { - throw new Error('Method not implemented: now') + throw new Error("Method not implemented: now"); }), sleep: mock(() => { throw new Error('Method not implemented: sleep') }), log: mock(() => {}), - } + }; // Return a merged object with overrides taking precedence - return { ...defaultMock, ...overrides } + return { ...defaultMock, ...overrides }; } -const anyMaxSize = 1024n * 1024n +const anyMaxSize = 1024n * 1024n; // Store original prototypes for manual restoration -const originalConsensusSimple = ConsensusCapability.prototype.simple -const originalNodeActionPerformAction = NodeActionCapability.prototype.performAction +const originalConsensusSimple = ConsensusCapability.prototype.simple; +const originalNodeActionPerformAction = + NodeActionCapability.prototype.performAction; afterEach(() => { // Restore all mocks after each test - mock.restore() + mock.restore(); // Manually restore prototype methods - ConsensusCapability.prototype.simple = originalConsensusSimple - NodeActionCapability.prototype.performAction = originalNodeActionPerformAction -}) - -describe('test runtime', () => { - describe('test call capability', () => { - test('allows awaiting multiple capability results in different order than calls', () => { - const anyResult1 = 'ok1' - const anyResult2 = 'ok2' - var expectedCall = 1 - var expectedAwait = 2 - - const input1 = create(InputsSchema, { inputThing: true }) - const input2 = create(InputSchema, { name: 'input' }) + ConsensusCapability.prototype.simple = originalConsensusSimple; + NodeActionCapability.prototype.performAction = + originalNodeActionPerformAction; +}); + +describe("test runtime", () => { + describe("test call capability", () => { + test("allows awaiting multiple capability results in different order than calls", () => { + const anyResult1 = "ok1"; + const anyResult2 = "ok2"; + var expectedCall = 1; + var expectedAwait = 2; + + const input1 = create(InputsSchema, { inputThing: true }); + const input2 = create(InputSchema, { name: "input" }); const helpers = createRuntimeHelpersMock({ call: mock((request: CapabilityRequest) => { switch (request.callbackId) { case 1: - expect(expectedCall).toEqual(1) - expectedCall++ - expect(request.id).toEqual(BasicActionCapability.CAPABILITY_ID) - expect(request.method).toEqual('PerformAction') - expect(anyUnpack(request.payload as Any, InputsSchema)).toEqual(input1) - return true + expect(expectedCall).toEqual(1); + expectedCall++; + expect(request.id).toEqual(BasicActionCapability.CAPABILITY_ID); + expect(request.method).toEqual("PerformAction"); + expect(anyUnpack(request.payload as Any, InputsSchema)).toEqual( + input1, + ); + return true; case 2: - expect(expectedCall).toEqual(2) - expectedCall++ - expect(request.id).toEqual(BasicCapability.CAPABILITY_ID) - expect(request.method).toEqual('Action') - expect(anyUnpack(request.payload as Any, InputSchema)).toEqual(input2) - return true + expect(expectedCall).toEqual(2); + expectedCall++; + expect(request.id).toEqual(BasicCapability.CAPABILITY_ID); + expect(request.method).toEqual("Action"); + expect(anyUnpack(request.payload as Any, InputSchema)).toEqual( + input2, + ); + return true; default: - throw new Error(`Unexpected call with callbackId: ${request.callbackId}`) + throw new Error( + `Unexpected call with callbackId: ${request.callbackId}`, + ); } }), await: mock((request: AwaitCapabilitiesRequest) => { - expect(request.ids.length).toEqual(1) - var payload: Any - const id = request.ids[0] + expect(request.ids.length).toEqual(1); + var payload: Any; + const id = request.ids[0]; switch (id) { case 1: - expect(1).toEqual(expectedAwait) - expectedAwait-- - payload = anyPack(OutputsSchema, create(OutputsSchema, { adaptedThing: anyResult1 })) - break + expect(1).toEqual(expectedAwait); + expectedAwait--; + payload = anyPack( + OutputsSchema, + create(OutputsSchema, { adaptedThing: anyResult1 }), + ); + break; case 2: - expect(2).toEqual(expectedAwait) - expectedAwait-- - payload = anyPack(OutputSchema, create(OutputSchema, { welcome: anyResult2 })) - break + expect(2).toEqual(expectedAwait); + expectedAwait--; + payload = anyPack( + OutputSchema, + create(OutputSchema, { welcome: anyResult2 }), + ); + break; default: - throw new Error(`Unexpected await with id: ${request.ids[0]}`) + throw new Error(`Unexpected await with id: ${request.ids[0]}`); } return create(AwaitCapabilitiesResponseSchema, { responses: { [id]: create(CapabilityResponseSchema, { - response: { case: 'payload', value: payload }, + response: { case: "payload", value: payload }, }), }, - }) + }); }), - }) - - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const workflowAction1 = new BasicActionCapability() - const call1 = workflowAction1.performAction(runtime, input1) - const workflowAction2 = new BasicCapability() - const call2 = workflowAction2.action(runtime, input2) - const result2 = call2.result() - expect(result2.welcome).toEqual(anyResult2) - const result1 = call1.result() - expect(result1.adaptedThing).toEqual(anyResult1) - }) - - test('call capability errors', () => { + }); + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const workflowAction1 = new BasicActionCapability(); + const call1 = workflowAction1.performAction(runtime, input1); + const workflowAction2 = new BasicCapability(); + const call2 = workflowAction2.action(runtime, input2); + const result2 = call2.result(); + expect(result2.welcome).toEqual(anyResult2); + const result1 = call1.result(); + expect(result1.adaptedThing).toEqual(anyResult1); + }); + + test("call capability errors", () => { const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => { - return false + return false; }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const workflowAction1 = new BasicActionCapability() + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const workflowAction1 = new BasicActionCapability(); const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ) + ); expect(() => call1.result()).toThrow( new CapabilityError( @@ -188,36 +209,36 @@ describe('test runtime', () => { { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: 'PerformAction', + method: "PerformAction", }, ), - ) - }) + ); + }); - test('capability errors are returned to the caller', () => { - const anyError = 'error' + test("capability errors are returned to the caller", () => { + const anyError = "error"; const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => { - return true + return true; }), await: mock((request: AwaitCapabilitiesRequest) => { - expect(request.ids.length).toEqual(1) + expect(request.ids.length).toEqual(1); return create(AwaitCapabilitiesResponseSchema, { responses: { [request.ids[0]]: create(CapabilityResponseSchema, { - response: { case: 'error', value: anyError }, + response: { case: "error", value: anyError }, }), }, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const workflowAction1 = new BasicActionCapability() + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const workflowAction1 = new BasicActionCapability(); const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ) + ); expect(() => call1.result()).toThrow( new CapabilityError( @@ -225,55 +246,55 @@ describe('test runtime', () => { { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: 'PerformAction', + method: "PerformAction", }, ), - ) - }) + ); + }); - test('await errors', () => { - const anyError = 'error' + test("await errors", () => { + const anyError = "error"; const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => { - return true + return true; }), await: mock((_: AwaitCapabilitiesRequest) => { - throw new Error(anyError) + throw new Error(anyError); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const workflowAction1 = new BasicActionCapability() + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const workflowAction1 = new BasicActionCapability(); const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ) + ); expect(() => call1.result()).toThrow( new CapabilityError(anyError, { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: 'PerformAction', + method: "PerformAction", }), - ) - }) + ); + }); - test('await missing response', () => { + test("await missing response", () => { const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => { - return true + return true; }), await: mock((_: AwaitCapabilitiesRequest) => { - return create(AwaitCapabilitiesResponseSchema, { responses: {} }) + return create(AwaitCapabilitiesResponseSchema, { responses: {} }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const workflowAction1 = new BasicActionCapability() + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const workflowAction1 = new BasicActionCapability(); const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ) + ); expect(() => call1.result()).toThrow( new CapabilityError( @@ -281,58 +302,61 @@ describe('test runtime', () => { { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: 'PerformAction', + method: "PerformAction", }, ), - ) - }) + ); + }); - test('await throws RESPONSE_BUFFER_TOO_SMALL when response exceeds max size', () => { + test("await throws RESPONSE_BUFFER_TOO_SMALL when response exceeds max size", () => { const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => true), await: mock((_: AwaitCapabilitiesRequest) => { - throw new Error(RESPONSE_BUFFER_TOO_SMALL) + throw new Error(RESPONSE_BUFFER_TOO_SMALL); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const workflowAction1 = new BasicActionCapability() + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const workflowAction1 = new BasicActionCapability(); const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ) + ); - expect(() => call1.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL) - }) + expect(() => call1.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL); + }); - test('await returns unparsable payload throws CapabilityError', () => { + test("await returns unparsable payload throws CapabilityError", () => { // Any with correct type_url but invalid value bytes so fromBinary throws - const validAny = anyPack(OutputsSchema, create(OutputsSchema, { adaptedThing: 'x' })) + const validAny = anyPack( + OutputsSchema, + create(OutputsSchema, { adaptedThing: "x" }), + ); const corruptPayload = { typeUrl: validAny.typeUrl, value: new Uint8Array([0xff, 0xff]), - } + }; const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => true), await: mock((request: AwaitCapabilitiesRequest) => { - const id = request.ids[0] + const id = request.ids[0]; return create(AwaitCapabilitiesResponseSchema, { responses: { [id]: create(CapabilityResponseSchema, { - response: { case: 'payload', value: corruptPayload as Any }, + response: { case: "payload", value: corruptPayload as Any }, }), }, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const workflowAction1 = new BasicActionCapability() + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const workflowAction1 = new BasicActionCapability(); const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ) + ); expect(() => call1.result()).toThrow( new CapabilityError( @@ -340,236 +364,390 @@ describe('test runtime', () => { { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: 'PerformAction', + method: "PerformAction", }, ), - ) - }) - }) -}) + ); + }); + }); +}); -describe('test now converts to date', () => { - test('now converts to date', () => { +describe("test now converts to date", () => { + test("now converts to date", () => { const helpers = createRuntimeHelpersMock({ now: mock(() => 1716153600000), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const now = runtime.now() - expect(now).toEqual(new Date(1716153600000)) - }) -}) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const now = runtime.now(); + expect(now).toEqual(new Date(1716153600000)); + }); +}); -describe('test sleep delegates to helpers', () => { - test('sleep calls helpers.sleep with the provided milliseconds', () => { - const sleepMock = mock(() => {}) +describe("test sleep delegates to helpers", () => { + test("sleep calls helpers.sleep with the provided milliseconds", () => { + const sleepMock = mock(() => {}); const helpers = createRuntimeHelpersMock({ sleep: sleepMock, - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - runtime.sleep(500) - expect(sleepMock).toHaveBeenCalledTimes(1) - expect(sleepMock).toHaveBeenCalledWith(500) - }) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + runtime.sleep(500); + expect(sleepMock).toHaveBeenCalledTimes(1); + expect(sleepMock).toHaveBeenCalledWith(500); + }); - test('sleep passes zero milliseconds to helpers.sleep', () => { - const sleepMock = mock(() => {}) + test("sleep passes zero milliseconds to helpers.sleep", () => { + const sleepMock = mock(() => {}); const helpers = createRuntimeHelpersMock({ sleep: sleepMock, - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - runtime.sleep(0) - expect(sleepMock).toHaveBeenCalledWith(0) - }) -}) - -describe('test getSecret', () => { - test('successfully gets secret with SecretRequest (proto message)', () => { - const secretRequest = create(SecretRequestSchema, { - id: 'my-secret', - namespace: 'test-ns', - }) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + runtime.sleep(0); + expect(sleepMock).toHaveBeenCalledWith(0); + }); +}); +describe("test getSecret", () => { + test("getSecrets returns ordered batched responses", () => { const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - expect(request.callbackId).toEqual(1) - expect(request.requests.length).toEqual(1) + expect(request.callbackId).toEqual(1); + expect(request.requests.length).toEqual(2); + expect(request.requests[0].id).toEqual("secret-1"); + expect(request.requests[1].id).toEqual("secret-2"); }), awaitSecrets: mock((request) => { - expect(request.ids.length).toEqual(1) - expect(request.ids[0]).toEqual(1) + expect(request.ids.length).toEqual(1); + expect(request.ids[0]).toEqual(1); + return create(AwaitSecretsResponseSchema, { + responses: { + 1: create(SecretResponsesSchema, { + responses: [ + create(SecretResponseSchema, { + response: { + case: "secret", + value: { + id: "secret-1", + namespace: "ns", + owner: "owner-1", + value: "value-1", + }, + }, + }), + create(SecretResponseSchema, { + response: { + case: "secret", + value: { + id: "secret-2", + namespace: "ns", + owner: "owner-2", + value: "value-2", + }, + }, + }), + ], + }), + }, + }); + }), + }); + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const responses = runtime + .getSecrets([ + { id: "secret-1", namespace: "ns" }, + { id: "secret-2", namespace: "ns" }, + ]) + .result(); + + expect(responses.length).toEqual(2); + expect(responses[0].response.case).toEqual("secret"); + expect(responses[1].response.case).toEqual("secret"); + if (responses[0].response.case === "secret") { + expect(responses[0].response.value.id).toEqual("secret-1"); + } + if (responses[1].response.case === "secret") { + expect(responses[1].response.value.id).toEqual("secret-2"); + } + }); + + test("getSecrets returns mixed success and error responses without throwing", () => { + const helpers = createRuntimeHelpersMock({ + getSecrets: mock(() => undefined), + awaitSecrets: mock(() => { return create(AwaitSecretsResponseSchema, { responses: { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { response: { - case: 'secret', + case: "secret", + value: { + id: "ok-secret", + namespace: "ns", + owner: "owner", + value: "ok-value", + }, + }, + }), + create(SecretResponseSchema, { + response: { + case: "error", + value: { + id: "missing-secret", + namespace: "ns", + owner: "owner", + error: "secret not found", + }, + }, + }), + ], + }), + }, + }); + }), + }); + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const responses = runtime + .getSecrets([ + { id: "ok-secret", namespace: "ns" }, + { id: "missing-secret", namespace: "ns" }, + ]) + .result(); + + expect(responses.length).toEqual(2); + expect(responses[0].response.case).toEqual("secret"); + expect(responses[1].response.case).toEqual("error"); + }); + + test("normalizes missing secret namespace to default for JSON and protobuf requests", () => { + const observedNamespaces: string[] = []; + const helpers = createRuntimeHelpersMock({ + getSecrets: mock((request) => { + expect(request.requests.length).toEqual(1); + observedNamespaces.push(request.requests[0].namespace); + }), + awaitSecrets: mock((request) => { + const id = request.ids[0]; + return create(AwaitSecretsResponseSchema, { + responses: { + [id]: create(SecretResponsesSchema, { + responses: [ + create(SecretResponseSchema, { + response: { + case: "secret", value: { - id: 'my-secret', - namespace: 'test-ns', - owner: 'test-owner', - value: 'secret-value-123', + id: "secret", + namespace: "main", + owner: "owner", + value: "value", }, }, }), ], }), }, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const result = runtime.getSecret(secretRequest).result() - expect(result.id).toEqual('my-secret') - expect(result.namespace).toEqual('test-ns') - expect(result.value).toEqual('secret-value-123') - }) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + runtime.getSecret({ id: "json-secret" }).result(); + runtime + .getSecret(create(SecretRequestSchema, { id: "proto-secret" })) + .result(); - test('successfully gets secret with SecretRequestJson (plain JSON)', () => { - const secretRequestJson = { id: 'another-secret', namespace: 'another-ns' } + expect(observedNamespaces).toEqual(["main", "main"]); + }); + + test("getSecrets throws SecretsBatchError when host getSecrets call fails", () => { + const helpers = createRuntimeHelpersMock({ + getSecrets: mock(() => { + throw new Error("vault: signer unreachable"); + }), + }); + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + expect(() => + runtime + .getSecrets([ + { id: "secret-a", namespace: "ns" }, + { id: "secret-b", namespace: "ns" }, + ]) + .result(), + ).toThrow(SecretsBatchError); + }); + + test("getSecrets throws SecretsBatchError for malformed batched response envelope", () => { + const helpers = createRuntimeHelpersMock({ + getSecrets: mock(() => undefined), + awaitSecrets: mock(() => + create(AwaitSecretsResponseSchema, { + responses: {}, + }), + ), + }); + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + expect(() => + runtime + .getSecrets([ + { id: "secret-a", namespace: "ns" }, + { id: "secret-b", namespace: "ns" }, + ]) + .result(), + ).toThrow(SecretsBatchError); + }); + + test("successfully gets secret with SecretRequest (proto message)", () => { + const secretRequest = create(SecretRequestSchema, { + id: "my-secret", + namespace: "test-ns", + }); const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - expect(request.callbackId).toEqual(1) - expect(request.requests.length).toEqual(1) + expect(request.callbackId).toEqual(1); + expect(request.requests.length).toEqual(1); }), awaitSecrets: mock((request) => { - expect(request.ids.length).toEqual(1) - expect(request.ids[0]).toEqual(1) + expect(request.ids.length).toEqual(1); + expect(request.ids[0]).toEqual(1); return create(AwaitSecretsResponseSchema, { responses: { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { response: { - case: 'secret', + case: "secret", value: { - id: 'another-secret', - namespace: 'another-ns', - owner: 'another-owner', - value: 'value-456', + id: "my-secret", + namespace: "test-ns", + owner: "test-owner", + value: "secret-value-123", }, }, }), ], }), }, - }) + }); }), - }) + }); + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const result = runtime.getSecret(secretRequest).result(); + expect(result.id).toEqual("my-secret"); + expect(result.namespace).toEqual("test-ns"); + expect(result.value).toEqual("secret-value-123"); + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const result = runtime.getSecret(secretRequestJson).result() - expect(result.id).toEqual('another-secret') - expect(result.namespace).toEqual('another-ns') - expect(result.value).toEqual('value-456') - }) + test("successfully gets secret with SecretRequestJson (plain JSON)", () => { + const secretRequestJson = { id: "another-secret", namespace: "another-ns" }; - test('normalizes missing secret namespace to default for JSON and protobuf requests', () => { - const observedNamespaces: string[] = [] const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - expect(request.requests.length).toEqual(1) - observedNamespaces.push(request.requests[0].namespace) + expect(request.callbackId).toEqual(1); + expect(request.requests.length).toEqual(1); }), awaitSecrets: mock((request) => { - const id = request.ids[0] + expect(request.ids.length).toEqual(1); + expect(request.ids[0]).toEqual(1); return create(AwaitSecretsResponseSchema, { responses: { - [id]: create(SecretResponsesSchema, { + 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { response: { - case: 'secret', + case: "secret", value: { - id: 'secret', - namespace: 'main', - owner: 'owner', - value: 'value', + id: "another-secret", + namespace: "another-ns", + owner: "another-owner", + value: "value-456", }, }, }), ], }), }, - }) + }); }), - }) - - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - runtime.getSecret({ id: 'json-secret' }).result() - runtime.getSecret(create(SecretRequestSchema, { id: 'proto-secret' })).result() + }); - expect(observedNamespaces).toEqual(['main', 'main']) - }) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const result = runtime.getSecret(secretRequestJson).result(); + expect(result.id).toEqual("another-secret"); + expect(result.namespace).toEqual("another-ns"); + expect(result.value).toEqual("value-456"); + }); - test('getSecrets throws → wrapped as SecretsError', () => { + test("getSecrets throws → wrapped as SecretsError", () => { const secretRequest = create(SecretRequestSchema, { - id: 'test-secret', - namespace: 'test-ns', - }) + id: "test-secret", + namespace: "test-ns", + }); const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => { - throw new Error('vault: signer unreachable') + throw new Error("vault: signer unreachable"); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, 'vault: signer unreachable'), - ) - }) + new SecretsError(secretRequest, "vault: signer unreachable"), + ); + }); - test('awaitSecrets throws → wrapped as SecretsError', () => { + test("awaitSecrets throws → wrapped as SecretsError", () => { const secretRequest = create(SecretRequestSchema, { - id: 'test-secret', - namespace: 'test-ns', - }) + id: "test-secret", + namespace: "test-ns", + }); const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), awaitSecrets: mock(() => { - throw new Error('vault: timeout fetching secret') + throw new Error("vault: timeout fetching secret"); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, 'vault: timeout fetching secret'), - ) - }) + new SecretsError(secretRequest, "vault: timeout fetching secret"), + ); + }); - test('awaitSecrets returns no response for callback ID', () => { + test("awaitSecrets returns no response for callback ID", () => { const secretRequest = create(SecretRequestSchema, { - id: 'test-secret', - namespace: 'test-ns', - }) + id: "test-secret", + namespace: "test-ns", + }); const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), awaitSecrets: mock(() => { return create(AwaitSecretsResponseSchema, { responses: {}, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, 'no response'), - ) - }) + new SecretsError(secretRequest, "no response"), + ); + }); - test('awaitSecrets returns invalid number of responses', () => { + test("awaitSecrets returns invalid number of responses", () => { const secretRequest = create(SecretRequestSchema, { - id: 'test-secret', - namespace: 'test-ns', - }) + id: "test-secret", + namespace: "test-ns", + }); const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), @@ -580,27 +758,27 @@ describe('test getSecret', () => { responses: [], }), }, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, 'invalid value returned from host'), - ) - }) + new SecretsError(secretRequest, "invalid value returned from host"), + ); + }); - test('awaitSecrets returns too many responses', () => { + test("awaitSecrets returns too many responses", () => { const secretRequest = create(SecretRequestSchema, { - id: 'test-secret', - namespace: 'test-ns', - }) + id: "test-secret", + namespace: "test-ns", + }); const secretValue = { - id: 'secret1', - namespace: 'test-ns', - owner: 'test-owner', - value: 'value1', - } + id: "secret1", + namespace: "test-ns", + owner: "test-owner", + value: "value1", + }; const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), @@ -610,30 +788,30 @@ describe('test getSecret', () => { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { - response: { case: 'secret', value: secretValue }, + response: { case: "secret", value: secretValue }, }), create(SecretResponseSchema, { - response: { case: 'secret', value: secretValue }, + response: { case: "secret", value: secretValue }, }), ], }), }, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, 'invalid value returned from host'), - ) - }) + new SecretsError(secretRequest, "invalid value returned from host"), + ); + }); - test('awaitSecrets returns error response', () => { + test("awaitSecrets returns error response", () => { const secretRequest = create(SecretRequestSchema, { - id: 'test-secret', - namespace: 'test-ns', - }) - const errorMessage = 'secret not found' + id: "test-secret", + namespace: "test-ns", + }); + const errorMessage = "secret not found"; const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), @@ -643,26 +821,26 @@ describe('test getSecret', () => { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { - response: { case: 'error', value: { error: errorMessage } }, + response: { case: "error", value: { error: errorMessage } }, }), ], }), }, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); expect(() => runtime.getSecret(secretRequest).result()).toThrow( new SecretsError(secretRequest, errorMessage), - ) - }) + ); + }); - test('awaitSecrets returns unknown response case', () => { + test("awaitSecrets returns unknown response case", () => { const secretRequest = create(SecretRequestSchema, { - id: 'test-secret', - namespace: 'test-ns', - }) + id: "test-secret", + namespace: "test-ns", + }); const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), @@ -677,504 +855,582 @@ describe('test getSecret', () => { ], }), }, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, 'cannot unmarshal returned value from host'), - ) - }) - - test('getSecret increments callback ID correctly', () => { - const callbackIds: number[] = [] + new SecretsError( + secretRequest, + "cannot unmarshal returned value from host", + ), + ); + }); + + test("getSecret increments callback ID correctly", () => { + const callbackIds: number[] = []; const secretValue = { - id: 'secret', - namespace: 'test-ns', - owner: 'test-owner', - value: 'value', - } + id: "secret", + namespace: "test-ns", + owner: "test-owner", + value: "value", + }; const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - callbackIds.push(request.callbackId) + callbackIds.push(request.callbackId); }), awaitSecrets: mock((request) => { - const id = request.ids[0] + const id = request.ids[0]; return create(AwaitSecretsResponseSchema, { responses: { [id]: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { - response: { case: 'secret', value: secretValue }, + response: { case: "secret", value: secretValue }, }), ], }), }, - }) + }); }), - }) + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - runtime.getSecret({ id: 'secret1', namespace: 'ns1' }).result() - runtime.getSecret({ id: 'secret2', namespace: 'ns2' }).result() - runtime.getSecret({ id: 'secret3', namespace: 'ns3' }).result() + runtime.getSecret({ id: "secret1", namespace: "ns1" }).result(); + runtime.getSecret({ id: "secret2", namespace: "ns2" }).result(); + runtime.getSecret({ id: "secret3", namespace: "ns3" }).result(); - expect(callbackIds).toEqual([1, 2, 3]) - }) + expect(callbackIds).toEqual([1, 2, 3]); + }); - test('getSecret in node mode throws DonModeError', () => { - const helpers = createRuntimeHelpersMock() + test("getSecret in node mode throws DonModeError", () => { + const helpers = createRuntimeHelpersMock(); - ;(ConsensusCapability.prototype as any).simple = mock(() => { - return { result: () => Value.from(0).proto() } - }) + (ConsensusCapability.prototype as any).simple = mock(() => { + return { result: () => Value.from(0).proto() }; + }); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - let capturedError: Error | undefined + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + let capturedError: Error | undefined; runtime.runInNodeMode((_nodeRuntime: NodeRuntime) => { // Try to call getSecret from within node mode (should fail) try { - runtime.getSecret({ id: 'test', namespace: 'test-ns' }).result() + runtime.getSecret({ id: "test", namespace: "test-ns" }).result(); } catch (e) { - capturedError = e as Error + capturedError = e as Error; } - return 0 - }, consensusMedianAggregation())() - - expect(capturedError).toBeDefined() - expect(capturedError).toBeInstanceOf(DonModeError) - }) -}) - -describe('test run in node mode', () => { - test('successful consensus', () => { - const anyObservation = 10 - const anyMedian = 11 - const modes: Mode[] = [] + return 0; + }, consensusMedianAggregation())(); + + expect(capturedError).toBeDefined(); + expect(capturedError).toBeInstanceOf(DonModeError); + }); +}); + +describe("test run in node mode", () => { + test("successful consensus", () => { + const anyObservation = 10; + const anyMedian = 11; + const modes: Mode[] = []; const helpers = createRuntimeHelpersMock({ switchModes: mock((mode: Mode) => { - modes.push(mode) + modes.push(mode); }), - }) - - ;(ConsensusCapability.prototype as any).simple = mock( - (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { - expect(modes).toEqual([Mode.DON, Mode.NODE, Mode.DON]) - expect(inputs.default).toBeUndefined() + }); + + (ConsensusCapability.prototype as any).simple = mock( + ( + _: Runtime, + inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, + ) => { + expect(modes).toEqual([Mode.DON, Mode.NODE, Mode.DON]); + expect(inputs.default).toBeUndefined(); const consensusDescriptor = create(ConsensusDescriptorSchema, { descriptor: { - case: 'fieldsMap', + case: "fieldsMap", value: create(FieldsMapSchema, { fields: { outputThing: create(ConsensusDescriptorSchema, { descriptor: { - case: 'aggregation', + case: "aggregation", value: AggregationType.MEDIAN, }, }), }, }), }, - }) - expect(inputs.descriptors).toEqual(consensusDescriptor) - expect((inputs as { $typeName?: string }).$typeName).not.toBeUndefined() - const inputsProto = inputs as SimpleConsensusInputs - expect(inputsProto.observation.case).toEqual('value') + }); + expect(inputs.descriptors).toEqual(consensusDescriptor); + expect( + (inputs as { $typeName?: string }).$typeName, + ).not.toBeUndefined(); + const inputsProto = inputs as SimpleConsensusInputs; + expect(inputsProto.observation.case).toEqual("value"); expect( Value.wrap(inputsProto.observation.value as ProtoValue).unwrapToType({ factory: () => create(NodeOutputsSchema), }).outputThing, - ).toEqual(anyObservation) + ).toEqual(anyObservation); return { - result: () => Value.from(create(NodeOutputsSchema, { outputThing: anyMedian })).proto(), - } + result: () => + Value.from( + create(NodeOutputsSchema, { outputThing: anyMedian }), + ).proto(), + }; }, - ) + ); // Create a mock that handles both overloads properly - const performActionMock = function (this: NodeActionCapability, ...args: unknown[]): unknown { + const performActionMock = function ( + this: NodeActionCapability, + ...args: unknown[] + ): unknown { // Check if this is the sugar syntax overload (has function parameter) - if (typeof args[0] === 'function') { + if (typeof args[0] === "function") { // This test doesn't expect sugar syntax to be used - throw new Error('Sugar syntax should not be used in this test') + throw new Error("Sugar syntax should not be used in this test"); } // Otherwise, this is the basic call overload - const [_, __] = args as [NodeRuntime, NodeInputs | NodeInputsJson] - expect(modes).toEqual([Mode.DON, Mode.NODE]) + const [_, __] = args as [ + NodeRuntime, + NodeInputs | NodeInputsJson, + ]; + expect(modes).toEqual([Mode.DON, Mode.NODE]); return { - result: () => create(NodeOutputsSchema, { outputThing: anyObservation }), - } - } + result: () => + create(NodeOutputsSchema, { outputThing: anyObservation }), + }; + }; // Apply the mock with proper typing // biome-ignore lint/suspicious/noExplicitAny: Mock assignment requires any due to overloaded function signature - ;(NodeActionCapability.prototype as any).performAction = mock(performActionMock) + (NodeActionCapability.prototype as any).performAction = + mock(performActionMock); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); const result = runtime .runInNodeMode( (nodeRuntime: NodeRuntime) => { - const capability = new NodeActionCapability() + const capability = new NodeActionCapability(); return capability - .performAction(nodeRuntime, create(NodeInputsSchema, { inputThing: true })) - .result() + .performAction( + nodeRuntime, + create(NodeInputsSchema, { inputThing: true }), + ) + .result(); }, ConsensusAggregationByFields({ outputThing: median }), )() - .result() + .result(); - expect(result.outputThing).toEqual(anyMedian) - }) + expect(result.outputThing).toEqual(anyMedian); + }); - test('failed consensus', () => { - const anyError = 'error' + test("failed consensus", () => { + const anyError = "error"; const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }) - - ;(ConsensusCapability.prototype as any).simple = mock( - (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { - expect(inputs.default).toBeUndefined() + }); + + (ConsensusCapability.prototype as any).simple = mock( + ( + _: Runtime, + inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, + ) => { + expect(inputs.default).toBeUndefined(); expect(inputs.descriptors).toEqual( create(ConsensusDescriptorSchema, { - descriptor: { case: 'aggregation', value: AggregationType.MEDIAN }, + descriptor: { case: "aggregation", value: AggregationType.MEDIAN }, }), - ) - expect((inputs as { $typeName?: string }).$typeName).not.toBeUndefined() - const inputsProto = inputs as SimpleConsensusInputs - expect(inputsProto.observation.case).toEqual('error') - expect(inputsProto.observation.value).toEqual(anyError) + ); + expect( + (inputs as { $typeName?: string }).$typeName, + ).not.toBeUndefined(); + const inputsProto = inputs as SimpleConsensusInputs; + expect(inputsProto.observation.case).toEqual("error"); + expect(inputsProto.observation.value).toEqual(anyError); return { result: () => { - throw new Error(anyError) + throw new Error(anyError); }, - } + }; }, - ) + ); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); const result = runtime.runInNodeMode((_: NodeRuntime) => { - throw new Error(anyError) - }, consensusMedianAggregation())() - expect(() => result.result()).toThrow(new Error(anyError)) - }) - - test('primitive consensus with unused default returns observation value', () => { - const observationValue = 99 - const defaultValue = 100 + throw new Error(anyError); + }, consensusMedianAggregation())(); + expect(() => result.result()).toThrow(new Error(anyError)); + }); + + test("primitive consensus with unused default returns observation value", () => { + const observationValue = 99; + const defaultValue = 100; const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }) - - ;(ConsensusCapability.prototype as any).simple = mock( - (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { - const inputsProto = inputs as SimpleConsensusInputs - expect(inputsProto.observation.case).toEqual('value') - expect(Value.wrap(inputsProto.observation.value as ProtoValue).unwrap()).toEqual( - observationValue, - ) - expect(inputsProto.default).toBeDefined() - expect(Value.wrap(inputsProto.default as ProtoValue).unwrap()).toEqual(defaultValue) + }); + + (ConsensusCapability.prototype as any).simple = mock( + ( + _: Runtime, + inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, + ) => { + const inputsProto = inputs as SimpleConsensusInputs; + expect(inputsProto.observation.case).toEqual("value"); + expect( + Value.wrap(inputsProto.observation.value as ProtoValue).unwrap(), + ).toEqual(observationValue); + expect(inputsProto.default).toBeDefined(); + expect(Value.wrap(inputsProto.default as ProtoValue).unwrap()).toEqual( + defaultValue, + ); return { result: () => Value.from(observationValue).proto(), - } + }; }, - ) + ); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); const result = runtime .runInNodeMode( (_: NodeRuntime) => observationValue, consensusMedianAggregation().withDefault(defaultValue), )() - .result() + .result(); - expect(result).toEqual(observationValue) - }) + expect(result).toEqual(observationValue); + }); - test('primitive consensus with used default returns default when function errors', () => { - const defaultVal = 100 - const anyError = 'error' + test("primitive consensus with used default returns default when function errors", () => { + const defaultVal = 100; + const anyError = "error"; const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }) - - ;(ConsensusCapability.prototype as any).simple = mock( - (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { - const inputsProto = inputs as SimpleConsensusInputs - expect(inputsProto.observation.case).toEqual('error') - expect(inputsProto.observation.value).toEqual(anyError) - expect(inputsProto.default).toBeDefined() - expect(Value.wrap(inputsProto.default as ProtoValue).unwrap()).toEqual(defaultVal) + }); + + (ConsensusCapability.prototype as any).simple = mock( + ( + _: Runtime, + inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, + ) => { + const inputsProto = inputs as SimpleConsensusInputs; + expect(inputsProto.observation.case).toEqual("error"); + expect(inputsProto.observation.value).toEqual(anyError); + expect(inputsProto.default).toBeDefined(); + expect(Value.wrap(inputsProto.default as ProtoValue).unwrap()).toEqual( + defaultVal, + ); return { result: () => Value.from(defaultVal).proto(), - } + }; }, - ) + ); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); const result = runtime .runInNodeMode((_: NodeRuntime) => { - throw new Error(anyError) + throw new Error(anyError); }, consensusMedianAggregation().withDefault(defaultVal))() - .result() + .result(); - expect(result).toEqual(defaultVal) - }) + expect(result).toEqual(defaultVal); + }); - test('node runtime in don mode fails', () => { + test("node runtime in don mode fails", () => { const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), call: mock((_: CapabilityRequest) => { - expect(false).toBe(true) - return false + expect(false).toBe(true); + return false; }), - }) - - ;(ConsensusCapability.prototype as any).simple = mock( - (_: Runtime, __: SimpleConsensusInputs | SimpleConsensusInputsJson) => { - return { result: () => Value.from(0).proto() } + }); + + (ConsensusCapability.prototype as any).simple = mock( + ( + _: Runtime, + __: SimpleConsensusInputs | SimpleConsensusInputsJson, + ) => { + return { result: () => Value.from(0).proto() }; }, - ) + ); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - var nrt: NodeRuntime | undefined + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + var nrt: NodeRuntime | undefined; runtime.runInNodeMode((nodeRuntime: NodeRuntime) => { - nrt = nodeRuntime - return 0 - }, consensusMedianAggregation())() + nrt = nodeRuntime; + return 0; + }, consensusMedianAggregation())(); - const capability = new NodeActionCapability() - expect(nrt).toBeDefined() + const capability = new NodeActionCapability(); + expect(nrt).toBeDefined(); expect(() => capability - .performAction(nrt as NodeRuntime, create(NodeInputsSchema, { inputThing: true })) + .performAction( + nrt as NodeRuntime, + create(NodeInputsSchema, { inputThing: true }), + ) .result(), - ).toThrow(new NodeModeError()) - }) + ).toThrow(new NodeModeError()); + }); - test('don runtime in node mode fails', () => { + test("don runtime in node mode fails", () => { const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }) - - ;(ConsensusCapability.prototype as any).simple = mock( - (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { - expect(inputs.default).toBeUndefined() + }); + + (ConsensusCapability.prototype as any).simple = mock( + ( + _: Runtime, + inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, + ) => { + expect(inputs.default).toBeUndefined(); expect(inputs.descriptors).toEqual( create(ConsensusDescriptorSchema, { - descriptor: { case: 'aggregation', value: AggregationType.MEDIAN }, + descriptor: { case: "aggregation", value: AggregationType.MEDIAN }, }), - ) - expect((inputs as { $typeName?: string }).$typeName).not.toBeUndefined() - const inputsProto = inputs as SimpleConsensusInputs - expect(inputsProto.observation.case).toEqual('error') - expect(inputsProto.observation.value).toEqual(new DonModeError().message) + ); + expect( + (inputs as { $typeName?: string }).$typeName, + ).not.toBeUndefined(); + const inputsProto = inputs as SimpleConsensusInputs; + expect(inputsProto.observation.case).toEqual("error"); + expect(inputsProto.observation.value).toEqual( + new DonModeError().message, + ); return { result: () => { - throw new DonModeError() + throw new DonModeError(); }, - } + }; }, - ) + ); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); const result = runtime.runInNodeMode((_: NodeRuntime) => { - const capability = new BasicActionCapability() - capability.performAction(runtime, create(InputsSchema, { inputThing: true })).result() - return 0 - }, consensusMedianAggregation())() - expect(() => result.result()).toThrow(new DonModeError()) - }) - - test('multiple runInNodeMode calls have unique callback IDs', () => { - const callbackIds: number[] = [] + const capability = new BasicActionCapability(); + capability + .performAction(runtime, create(InputsSchema, { inputThing: true })) + .result(); + return 0; + }, consensusMedianAggregation())(); + expect(() => result.result()).toThrow(new DonModeError()); + }); + + test("multiple runInNodeMode calls have unique callback IDs", () => { + const callbackIds: number[] = []; const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), call: mock((request: CapabilityRequest) => { - callbackIds.push(request.callbackId) - return true + callbackIds.push(request.callbackId); + return true; }), await: mock((request: AwaitCapabilitiesRequest) => { - const id = request.ids[0] + const id = request.ids[0]; return create(AwaitCapabilitiesResponseSchema, { responses: { [id]: create(CapabilityResponseSchema, { response: { - case: 'payload', - value: anyPack(NodeOutputsSchema, create(NodeOutputsSchema, { outputThing: 42 })), + case: "payload", + value: anyPack( + NodeOutputsSchema, + create(NodeOutputsSchema, { outputThing: 42 }), + ), }, }), }, - }) + }); }), - }) + }); - ;(ConsensusCapability.prototype as any).simple = mock( - (_: Runtime, __: SimpleConsensusInputs | SimpleConsensusInputsJson) => { + (ConsensusCapability.prototype as any).simple = mock( + ( + _: Runtime, + __: SimpleConsensusInputs | SimpleConsensusInputsJson, + ) => { return { - result: () => Value.from(create(NodeOutputsSchema, { outputThing: 42 })).proto(), - } + result: () => + Value.from(create(NodeOutputsSchema, { outputThing: 42 })).proto(), + }; }, - ) + ); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); // First runInNodeMode call with capability inside const call1 = runtime.runInNodeMode( (nodeRuntime: NodeRuntime) => { - const capability = new NodeActionCapability() + const capability = new NodeActionCapability(); return capability - .performAction(nodeRuntime, create(NodeInputsSchema, { inputThing: true })) - .result() + .performAction( + nodeRuntime, + create(NodeInputsSchema, { inputThing: true }), + ) + .result(); }, ConsensusAggregationByFields({ outputThing: median }), - ) + ); - call1().result() + call1().result(); // Second runInNodeMode call with capability inside const call2 = runtime.runInNodeMode( (nodeRuntime: NodeRuntime) => { - const capability = new NodeActionCapability() + const capability = new NodeActionCapability(); return capability - .performAction(nodeRuntime, create(NodeInputsSchema, { inputThing: false })) - .result() + .performAction( + nodeRuntime, + create(NodeInputsSchema, { inputThing: false }), + ) + .result(); }, ConsensusAggregationByFields({ outputThing: median }), - ) + ); - call2().result() + call2().result(); // Verify that we have two distinct callback IDs - expect(callbackIds.length).toEqual(2) - expect(callbackIds[0]).toEqual(-1) // First node mode call - expect(callbackIds[1]).toEqual(-2) // Second node mode call + expect(callbackIds.length).toEqual(2); + expect(callbackIds[0]).toEqual(-1); // First node mode call + expect(callbackIds[1]).toEqual(-2); // Second node mode call // Ensure they are different (no reuse/collision) - expect(callbackIds[0]).not.toEqual(callbackIds[1]) - }) + expect(callbackIds[0]).not.toEqual(callbackIds[1]); + }); - test('clears ignored fields from default and response values', () => { + test("clears ignored fields from default and response values", () => { type NestedStruct = { - nestedIncluded: string - nestedIgnored: string - } + nestedIncluded: string; + nestedIgnored: string; + }; type TestStruct = { - includedField: string - ignoredField: string - nested: NestedStruct - } + includedField: string; + ignoredField: string; + nested: NestedStruct; + }; const defaultVal: TestStruct = { - includedField: 'default_included', - ignoredField: 'default_ignored', + includedField: "default_included", + ignoredField: "default_ignored", nested: { - nestedIncluded: 'default_nested_included', - nestedIgnored: 'default_nested_ignored', + nestedIncluded: "default_nested_included", + nestedIgnored: "default_nested_ignored", }, - } + }; const responseVal: TestStruct = { - includedField: 'response_included', - ignoredField: 'response_ignored', + includedField: "response_included", + ignoredField: "response_ignored", nested: { - nestedIncluded: 'response_nested_included', - nestedIgnored: 'response_nested_ignored', + nestedIncluded: "response_nested_included", + nestedIgnored: "response_nested_ignored", }, - } + }; const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }) - - ;(ConsensusCapability.prototype as any).simple = mock( - (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { - const inputsProto = inputs as SimpleConsensusInputs - if (inputsProto.observation.case === 'value') { + }); + + (ConsensusCapability.prototype as any).simple = mock( + ( + _: Runtime, + inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, + ) => { + const inputsProto = inputs as SimpleConsensusInputs; + if (inputsProto.observation.case === "value") { const unwrapped = Value.wrap( inputsProto.observation.value as ProtoValue, - ).unwrap() as TestStruct - expect(unwrapped.includedField).toEqual('response_included') - expect(unwrapped.ignoredField).toBeUndefined() - expect(unwrapped.nested.nestedIncluded).toEqual('response_nested_included') - expect(unwrapped.nested.nestedIgnored).toBeUndefined() + ).unwrap() as TestStruct; + expect(unwrapped.includedField).toEqual("response_included"); + expect(unwrapped.ignoredField).toBeUndefined(); + expect(unwrapped.nested.nestedIncluded).toEqual( + "response_nested_included", + ); + expect(unwrapped.nested.nestedIgnored).toBeUndefined(); return { result: () => inputsProto.observation.value as ProtoValue, - } + }; } if (inputsProto.default) { - const unwrapped = Value.wrap(inputsProto.default as ProtoValue).unwrap() as TestStruct - expect(unwrapped.includedField).toEqual('default_included') - expect(unwrapped.ignoredField).toBeUndefined() - expect(unwrapped.nested.nestedIncluded).toEqual('default_nested_included') - expect(unwrapped.nested.nestedIgnored).toBeUndefined() + const unwrapped = Value.wrap( + inputsProto.default as ProtoValue, + ).unwrap() as TestStruct; + expect(unwrapped.includedField).toEqual("default_included"); + expect(unwrapped.ignoredField).toBeUndefined(); + expect(unwrapped.nested.nestedIncluded).toEqual( + "default_nested_included", + ); + expect(unwrapped.nested.nestedIgnored).toBeUndefined(); return { result: () => inputsProto.default as ProtoValue, - } + }; } - if (inputsProto.observation.case === 'error') { + if (inputsProto.observation.case === "error") { return { result: () => { - throw new Error(inputsProto.observation.value as string) + throw new Error(inputsProto.observation.value as string); }, - } + }; } return { result: () => { - throw new Error('unexpected case') + throw new Error("unexpected case"); }, - } + }; }, - ) + ); - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); const nestedAggregation = ConsensusAggregationByFields({ nestedIncluded: identical, nestedIgnored: ignore, - }) + }); const result = runtime .runInNodeMode( (_nodeRuntime: NodeRuntime) => { - return responseVal + return responseVal; }, ConsensusAggregationByFields({ includedField: identical, ignoredField: ignore, nested: () => - new ConsensusFieldAggregation(nestedAggregation.descriptor), + new ConsensusFieldAggregation( + nestedAggregation.descriptor, + ), }).withDefault(defaultVal), )() - .result() + .result(); - expect(result.includedField).toEqual('response_included') - expect(result.ignoredField).toBeUndefined() - expect(result.nested.nestedIncluded).toEqual('response_nested_included') - expect(result.nested.nestedIgnored).toBeUndefined() + expect(result.includedField).toEqual("response_included"); + expect(result.ignoredField).toBeUndefined(); + expect(result.nested.nestedIncluded).toEqual("response_nested_included"); + expect(result.nested.nestedIgnored).toBeUndefined(); const result2 = runtime .runInNodeMode( (_nodeRuntime: NodeRuntime) => { - throw new Error('error') + throw new Error("error"); }, ConsensusAggregationByFields({ includedField: identical, ignoredField: ignore, nested: () => - new ConsensusFieldAggregation(nestedAggregation.descriptor), + new ConsensusFieldAggregation( + nestedAggregation.descriptor, + ), }).withDefault(defaultVal), )() - .result() - - expect(result2.includedField).toEqual('default_included') - expect(result2.ignoredField).toBeUndefined() - expect(result2.nested.nestedIncluded).toEqual('default_nested_included') - expect(result2.nested.nestedIgnored).toBeUndefined() - }) -}) + .result(); + + expect(result2.includedField).toEqual("default_included"); + expect(result2.ignoredField).toBeUndefined(); + expect(result2.nested.nestedIncluded).toEqual("default_nested_included"); + expect(result2.nested.nestedIgnored).toBeUndefined(); + }); +}); diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.ts index 82f340cd..c0477344 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.ts @@ -1,6 +1,6 @@ -import { create, type Message } from '@bufbuild/protobuf' -import type { GenMessage } from '@bufbuild/protobuf/codegenv2' -import { type Any, anyPack, anyUnpack } from '@bufbuild/protobuf/wkt' +import { create, type Message } from "@bufbuild/protobuf"; +import type { GenMessage } from "@bufbuild/protobuf/codegenv2"; +import { type Any, anyPack, anyUnpack } from "@bufbuild/protobuf/wkt"; import { type AwaitCapabilitiesRequest, AwaitCapabilitiesRequestSchema, @@ -18,10 +18,11 @@ import { type SecretRequest, type SecretRequestJson, SecretRequestSchema, + type SecretResponse, SimpleConsensusInputsSchema, -} from '@cre/generated/sdk/v1alpha/sdk_pb' -import type { Value as ProtoValue } from '@cre/generated/values/v1/values_pb' -import { ConsensusCapability } from '@cre/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen' +} from "@cre/generated/sdk/v1alpha/sdk_pb"; +import type { Value as ProtoValue } from "@cre/generated/values/v1/values_pb"; +import { ConsensusCapability } from "@cre/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen"; import type { BaseRuntime, CallCapabilityParams, @@ -29,17 +30,22 @@ import type { ReportRequest, ReportRequestJson, Runtime, -} from '@cre/sdk' -import type { Report } from '@cre/sdk/report' +} from "@cre/sdk"; +import type { Report } from "@cre/sdk/report"; import { type ConsensusAggregation, type CreSerializable, type PrimitiveTypes, type UnwrapOptions, Value, -} from '@cre/sdk/utils' -import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' -import { DonModeError, NodeModeError, SecretsError } from '../errors' +} from "@cre/sdk/utils"; +import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; +import { + DonModeError, + NodeModeError, + SecretsBatchError, + SecretsError, +} from "../errors"; const DEFAULT_SECRET_NAMESPACE = 'main' @@ -57,7 +63,7 @@ export class BaseRuntimeImpl implements BaseRuntime { * - Set in DON mode when code tries to use NodeRuntime * - Set in Node mode when code tries to use Runtime */ - public modeError?: Error + public modeError?: Error; constructor( public config: C, @@ -82,22 +88,22 @@ export class BaseRuntimeImpl implements BaseRuntime { if (this.modeError) { return { result: () => { - throw this.modeError + throw this.modeError; }, - } + }; } // Allocate unique callback ID for this request - const callbackId = this.allocateCallbackId() + const callbackId = this.allocateCallbackId(); // Send request to WASM host - const anyPayload = anyPack(inputSchema, payload) + const anyPayload = anyPack(inputSchema, payload); const req = create(CapabilityRequestSchema, { id: capabilityId, method, payload: anyPayload, callbackId, - }) + }); if (!this.helpers.call(req)) { return { @@ -109,16 +115,21 @@ export class BaseRuntimeImpl implements BaseRuntime { method, capabilityId, }, - ) + ); }, - } + }; } // Return lazy result - await and unwrap when .result() is called return { result: () => - this.awaitAndUnwrapCapabilityResponse(callbackId, capabilityId, method, outputSchema), - } + this.awaitAndUnwrapCapabilityResponse( + callbackId, + capabilityId, + method, + outputSchema, + ), + }; } /** @@ -126,13 +137,13 @@ export class BaseRuntimeImpl implements BaseRuntime { * DON mode increments, Node mode decrements (prevents collisions). */ private allocateCallbackId(): number { - const callbackId = this.nextCallId + const callbackId = this.nextCallId; if (this.mode === Mode.DON) { - this.nextCallId++ + this.nextCallId++; } else { - this.nextCallId-- + this.nextCallId--; } - return callbackId + return callbackId; } /** @@ -146,9 +157,12 @@ export class BaseRuntimeImpl implements BaseRuntime { ): O { const awaitRequest = create(AwaitCapabilitiesRequestSchema, { ids: [callbackId], - }) - const awaitResponse = this.helpers.await(awaitRequest, this.maxResponseSize) - const capabilityResponse = awaitResponse.responses[callbackId] + }); + const awaitResponse = this.helpers.await( + awaitRequest, + this.maxResponseSize, + ); + const capabilityResponse = awaitResponse.responses[callbackId]; if (!capabilityResponse) { throw new CapabilityError( @@ -158,14 +172,14 @@ export class BaseRuntimeImpl implements BaseRuntime { method, callbackId, }, - ) + ); } - const response = capabilityResponse.response + const response = capabilityResponse.response; switch (response.case) { - case 'payload': { + case "payload": { try { - return anyUnpack(response.value as Any, outputSchema) as O + return anyUnpack(response.value as Any, outputSchema) as O; } catch { throw new CapabilityError( `Failed to deserialize response payload for capability '${capabilityId}' method '${method}': the response could not be unpacked into the expected output schema`, @@ -174,10 +188,10 @@ export class BaseRuntimeImpl implements BaseRuntime { method, callbackId, }, - ) + ); } } - case 'error': + case "error": throw new CapabilityError( `Capability '${capabilityId}' method '${method}' returned an error: ${response.value}`, { @@ -185,7 +199,7 @@ export class BaseRuntimeImpl implements BaseRuntime { method, callbackId, }, - ) + ); default: throw new CapabilityError( `Unexpected response type '${response.case}' for capability '${capabilityId}' method '${method}': expected 'payload' or 'error'`, @@ -194,17 +208,17 @@ export class BaseRuntimeImpl implements BaseRuntime { method, callbackId, }, - ) + ); } } getNextCallId(): number { - return this.nextCallId + return this.nextCallId; } now(): Date { // date is already in milliseconds - return new Date(this.helpers.now()) + return new Date(this.helpers.now()); } sleep(ms: number): void { @@ -212,7 +226,7 @@ export class BaseRuntimeImpl implements BaseRuntime { } log(message: string): void { - this.helpers.log(message) + this.helpers.log(message); } } @@ -224,12 +238,20 @@ export class BaseRuntimeImpl implements BaseRuntime { * Useful in situation where you already expect non-determinism (e.g., inherently variable HTTP responses). * Switching from Node Mode back to DON mode requires workflow authors to handle consensus themselves. */ -export class NodeRuntimeImpl extends BaseRuntimeImpl implements NodeRuntime { - _isNodeRuntime: true = true +export class NodeRuntimeImpl + extends BaseRuntimeImpl + implements NodeRuntime +{ + _isNodeRuntime: true = true; - constructor(config: C, nextCallId: number, helpers: RuntimeHelpers, maxResponseSize: bigint) { - helpers.switchModes(Mode.NODE) - super(config, nextCallId, helpers, maxResponseSize, Mode.NODE) + constructor( + config: C, + nextCallId: number, + helpers: RuntimeHelpers, + maxResponseSize: bigint, + ) { + helpers.switchModes(Mode.NODE); + super(config, nextCallId, helpers, maxResponseSize, Mode.NODE); } } @@ -238,11 +260,16 @@ export class NodeRuntimeImpl extends BaseRuntimeImpl implements NodeRuntim * You ask the network to execute something, and CRE handles the underlying complexity to ensure you get back one final, secure, and trustworthy result. */ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { - private nextNodeCallId: number = -1 + private nextNodeCallId: number = -1; - constructor(config: C, nextCallId: number, helpers: RuntimeHelpers, maxResponseSize: bigint) { - helpers.switchModes(Mode.DON) - super(config, nextCallId, helpers, maxResponseSize, Mode.DON) + constructor( + config: C, + nextCallId: number, + helpers: RuntimeHelpers, + maxResponseSize: bigint, + ) { + helpers.switchModes(Mode.DON); + super(config, nextCallId, helpers, maxResponseSize, Mode.DON); } /** @@ -259,35 +286,41 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { runInNodeMode( fn: (nodeRuntime: NodeRuntime, ...args: TArgs) => TOutput, consensusAggregation: ConsensusAggregation, - unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions, + unwrapOptions?: TOutput extends PrimitiveTypes + ? never + : UnwrapOptions, ): (...args: TArgs) => { result: () => TOutput } { return (...args: TArgs): { result: () => TOutput } => { // Step 1: Create node runtime and prevent DON operations - this.modeError = new DonModeError() + this.modeError = new DonModeError(); const nodeRuntime = new NodeRuntimeImpl( this.config, this.nextNodeCallId, this.helpers, this.maxResponseSize, - ) + ); // Step 2: Prepare consensus input with config - const consensusInput = this.prepareConsensusInput(consensusAggregation) + const consensusInput = this.prepareConsensusInput(consensusAggregation); // Step 3: Execute node function and capture result/error try { - const observation = fn(nodeRuntime, ...args) - this.captureObservation(consensusInput, observation, consensusAggregation.descriptor) + const observation = fn(nodeRuntime, ...args); + this.captureObservation( + consensusInput, + observation, + consensusAggregation.descriptor, + ); } catch (e: unknown) { - this.captureError(consensusInput, e) + this.captureError(consensusInput, e); } finally { // Step 4: Always restore DON mode - this.restoreDonMode(nodeRuntime) + this.restoreDonMode(nodeRuntime); } // Step 5: Run consensus and return lazy result - return this.runConsensusAndWrap(consensusInput, unwrapOptions) - } + return this.runConsensusAndWrap(consensusInput, unwrapOptions); + }; } private prepareConsensusInput( @@ -295,18 +328,18 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { ) { const consensusInput = create(SimpleConsensusInputsSchema, { descriptors: consensusAggregation.descriptor, - }) + }); if (consensusAggregation.defaultValue) { // Safe cast: ConsensusAggregation implies T extends CreSerializable const defaultValue = Value.from( consensusAggregation.defaultValue as CreSerializable, - ).proto() - clearIgnoredFields(defaultValue, consensusAggregation.descriptor) - consensusInput.default = defaultValue + ).proto(); + clearIgnoredFields(defaultValue, consensusAggregation.descriptor); + consensusInput.default = defaultValue; } - return consensusInput + return consensusInput; } private captureObservation( @@ -315,117 +348,181 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { descriptor: ConsensusDescriptor, ) { // Safe cast: ConsensusAggregation implies T extends CreSerializable - const observationValue = Value.from(observation as CreSerializable).proto() - clearIgnoredFields(observationValue, descriptor) + const observationValue = Value.from( + observation as CreSerializable, + ).proto(); + clearIgnoredFields(observationValue, descriptor); consensusInput.observation = { - case: 'value', + case: "value", value: observationValue, - } + }; } private captureError(consensusInput: any, e: unknown) { consensusInput.observation = { - case: 'error', + case: "error", value: (e instanceof Error && e.message) || String(e), - } + }; } private restoreDonMode(nodeRuntime: NodeRuntimeImpl) { - this.modeError = undefined - this.nextNodeCallId = nodeRuntime.nextCallId - nodeRuntime.modeError = new NodeModeError() - this.helpers.switchModes(Mode.DON) + this.modeError = undefined; + this.nextNodeCallId = nodeRuntime.nextCallId; + nodeRuntime.modeError = new NodeModeError(); + this.helpers.switchModes(Mode.DON); } private runConsensusAndWrap( consensusInput: any, - unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions, + unwrapOptions?: TOutput extends PrimitiveTypes + ? never + : UnwrapOptions, ): { result: () => TOutput } { - const consensus = new ConsensusCapability() - const call = consensus.simple(this, consensusInput) + const consensus = new ConsensusCapability(); + const call = consensus.simple(this, consensusInput); return { result: () => { - const result = call.result() - const wrappedValue = Value.wrap(result) + const result = call.result(); + const wrappedValue = Value.wrap(result); return unwrapOptions ? wrappedValue.unwrapToType(unwrapOptions) - : (wrappedValue.unwrap() as TOutput) + : (wrappedValue.unwrap() as TOutput); }, - } + }; } - getSecret(request: SecretRequest | SecretRequestJson): { - result: () => Secret + getSecrets(requests: Array): { + result: () => SecretResponse[]; } { // Enforce mode restrictions if (this.modeError) { return { result: () => { - throw this.modeError + throw this.modeError; }, - } + }; } - const secretRequest = create(SecretRequestSchema, { - id: request.id, - namespace: request.namespace || DEFAULT_SECRET_NAMESPACE, - }) + // Normalize requests (accept both protobuf and JSON formats) + const normalizedRequests = requests.map((request) => + (request as unknown as { $typeName?: string }).$typeName + ? create(SecretRequestSchema, { + id: (request as SecretRequest).id, + namespace: + (request as SecretRequest).namespace || + DEFAULT_SECRET_NAMESPACE, + }) + : create(SecretRequestSchema, { + id: request.id, + namespace: request.namespace || DEFAULT_SECRET_NAMESPACE, + }), + ); + if (normalizedRequests.length === 0) { + return { + result: () => [], + }; + } // Allocate callback ID and send request - const id = this.nextCallId - this.nextCallId++ + const id = this.nextCallId; + this.nextCallId++; const secretsReq = create(GetSecretsRequestSchema, { callbackId: id, - requests: [secretRequest], - }) + requests: normalizedRequests, + }); try { - this.helpers.getSecrets(secretsReq, this.maxResponseSize) + this.helpers.getSecrets(secretsReq, this.maxResponseSize); } catch (err) { - const message = err instanceof Error ? err.message : String(err) + const message = err instanceof Error ? err.message : String(err); return { result: () => { - throw new SecretsError(secretRequest, message) + throw new SecretsBatchError(normalizedRequests, message); }, - } + }; } // Return lazy result return { - result: () => this.awaitAndUnwrapSecret(id, secretRequest), - } + result: () => this.awaitAndUnwrapSecrets(id, normalizedRequests), + }; } - private awaitAndUnwrapSecret(id: number, secretRequest: SecretRequest): Secret { - const awaitRequest = create(AwaitSecretsRequestSchema, { ids: [id] }) - let awaitResponse: AwaitSecretsResponse + getSecret(request: SecretRequest | SecretRequestJson): { + result: () => Secret; + } { + const secretRequest = (request as unknown as { $typeName?: string }) + .$typeName + ? (request as SecretRequest) + : create(SecretRequestSchema, request); + + const getSecretsCall = this.getSecrets([secretRequest]); + return { + result: () => { + let responseList: SecretResponse[]; + try { + responseList = getSecretsCall.result(); + } catch (err) { + if (err instanceof SecretsBatchError) { + throw new SecretsError(secretRequest, err.error); + } + throw err; + } + + return this.unwrapSingleSecretResult(responseList, secretRequest); + }, + }; + } + + private awaitAndUnwrapSecrets( + id: number, + requests: SecretRequest[], + ): SecretResponse[] { + const awaitRequest = create(AwaitSecretsRequestSchema, { ids: [id] }); + let awaitResponse: AwaitSecretsResponse; try { - awaitResponse = this.helpers.awaitSecrets(awaitRequest, this.maxResponseSize) + awaitResponse = this.helpers.awaitSecrets( + awaitRequest, + this.maxResponseSize, + ); } catch (err) { - const message = err instanceof Error ? err.message : String(err) - throw new SecretsError(secretRequest, message) + const message = err instanceof Error ? err.message : String(err); + throw new SecretsBatchError(requests, message); } - const secretsResponse = awaitResponse.responses[id] + const secretsResponse = awaitResponse.responses[id]; if (!secretsResponse) { - throw new SecretsError(secretRequest, 'no response') + throw new SecretsBatchError(requests, "no response"); } - const responses = secretsResponse.responses - if (responses.length !== 1) { - throw new SecretsError(secretRequest, 'invalid value returned from host') + if (secretsResponse.responses.length !== requests.length) { + throw new SecretsBatchError(requests, "invalid value returned from host"); } - const response = responses[0].response + return secretsResponse.responses; + } + + private unwrapSingleSecretResult( + responseList: SecretResponse[], + request: SecretRequest, + ): Secret { + if (responseList.length !== 1) { + throw new SecretsError(request, "invalid value returned from host"); + } + + const response = responseList[0].response; switch (response.case) { - case 'secret': - return response.value - case 'error': - throw new SecretsError(secretRequest, response.value.error) + case "secret": + return response.value; + case "error": + throw new SecretsError(request, response.value.error); default: - throw new SecretsError(secretRequest, 'cannot unmarshal returned value from host') + throw new SecretsError( + request, + "cannot unmarshal returned value from host", + ); } } @@ -433,12 +530,12 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { * Generates a report via consensus mechanism. */ report(input: ReportRequest | ReportRequestJson): { result: () => Report } { - const consensus = new ConsensusCapability() + const consensus = new ConsensusCapability(); // Cast to native overload signature - the impl dispatches on $typeName. - const call = consensus.report(this, input as ReportRequest) + const call = consensus.report(this, input as ReportRequest); return { result: () => call.result(), - } + }; } } @@ -448,60 +545,71 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { */ export interface RuntimeHelpers { /** Initiates a capability call. Returns false if capability not found. */ - call(request: CapabilityRequest): boolean + call(request: CapabilityRequest): boolean; /** Awaits capability responses. Blocks until responses are ready. */ - await(request: AwaitCapabilitiesRequest, maxResponseSize: bigint): AwaitCapabilitiesResponse + await( + request: AwaitCapabilitiesRequest, + maxResponseSize: bigint, + ): AwaitCapabilitiesResponse; /** Requests secrets from host. Throws if host rejects the request. */ - getSecrets(request: GetSecretsRequest, maxResponseSize: bigint): void + getSecrets(request: GetSecretsRequest, maxResponseSize: bigint): void; /** Awaits secret responses. Blocks until secrets are ready. */ - awaitSecrets(request: AwaitSecretsRequest, maxResponseSize: bigint): AwaitSecretsResponse + awaitSecrets( + request: AwaitSecretsRequest, + maxResponseSize: bigint, + ): AwaitSecretsResponse; /** Switches execution mode (DON vs Node). Affects available operations. */ - switchModes(mode: Mode): void + switchModes(mode: Mode): void; /** Returns current time in milliseconds since Unix epoch. */ - now(): number + now(): number; /** Sleeps for the specified duration. */ sleep(ms: number): void /** Logs a message to the host environment. */ - log(message: string): void + log(message: string): void; } -function clearIgnoredFields(value: ProtoValue, descriptor: ConsensusDescriptor): void { +function clearIgnoredFields( + value: ProtoValue, + descriptor: ConsensusDescriptor, +): void { if (!descriptor || !value) { - return + return; } const fieldsMap = - descriptor.descriptor?.case === 'fieldsMap' ? descriptor.descriptor.value : undefined + descriptor.descriptor?.case === "fieldsMap" + ? descriptor.descriptor.value + : undefined; if (!fieldsMap) { - return + return; } - if (value.value?.case === 'mapValue') { - const mapValue = value.value.value + if (value.value?.case === "mapValue") { + const mapValue = value.value.value; if (!mapValue || !mapValue.fields) { - return + return; } for (const [key, val] of Object.entries(mapValue.fields)) { - const nestedDescriptor = fieldsMap.fields[key] + const nestedDescriptor = fieldsMap.fields[key]; if (!nestedDescriptor) { - delete mapValue.fields[key] - continue + delete mapValue.fields[key]; + continue; } const nestedFieldsMap = - nestedDescriptor.descriptor?.case === 'fieldsMap' + nestedDescriptor.descriptor?.case === "fieldsMap" ? nestedDescriptor.descriptor.value - : undefined - if (nestedFieldsMap && val.value?.case === 'mapValue') { - clearIgnoredFields(val, nestedDescriptor) + : undefined; + if (nestedFieldsMap && val.value?.case === "mapValue") { + clearIgnoredFields(val, nestedDescriptor); } } } diff --git a/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts b/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts index 63c0959d..eadf0505 100644 --- a/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts +++ b/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts @@ -3,14 +3,14 @@ * createTestRuntimeHelpers, default consensus handler, and TestRuntime getLogs/setTimeProvider. * Does not re-test RuntimeImpl behaviour covered in runtime-impl.test.ts. */ -import { test as bunTest, describe, expect } from 'bun:test' -import { create } from '@bufbuild/protobuf' -import { AnySchema } from '@bufbuild/protobuf/wkt' -import { BasicActionCapability } from '@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen' -import { consensusMedianAggregation } from '@cre/sdk/utils' -import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' -import { SecretsError } from '../errors' -import { BasicTestActionMock } from '../test/generated/capabilities/internal/basicaction/v1/basic_test_action_mock_gen' +import { test as bunTest, describe, expect } from "bun:test"; +import { create } from "@bufbuild/protobuf"; +import { AnySchema } from "@bufbuild/protobuf/wkt"; +import { BasicActionCapability } from "@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen"; +import { consensusMedianAggregation } from "@cre/sdk/utils"; +import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; +import { SecretsError } from "../errors"; +import { BasicTestActionMock } from "../test/generated/capabilities/internal/basicaction/v1/basic_test_action_mock_gen"; import { __testOnlyRegistryStore, __testOnlyRunWithRegistry, @@ -20,261 +20,300 @@ import { RESPONSE_BUFFER_TOO_SMALL, registerTestCapability, test, -} from './test-runtime' +} from "./test-runtime"; -describe('Registry (via test)', () => { - test('get returns undefined for unregistered id', async () => { - expect(getTestCapabilityHandler('missing')).toBeUndefined() - }) +describe("Registry (via test)", () => { + test("get returns undefined for unregistered id", async () => { + expect(getTestCapabilityHandler("missing")).toBeUndefined(); + }); - test('register and get return handler', async () => { + test("register and get return handler", async () => { const handler = () => ({ - response: { case: 'error' as const, value: 'x' }, - }) - registerTestCapability('my-cap', handler) - expect(getTestCapabilityHandler('my-cap')).toBe(handler) - }) - - test('register throws when id already exists', async () => { - registerTestCapability('dup', () => ({ - response: { case: 'error' as const, value: '' }, - })) + response: { case: "error" as const, value: "x" }, + }); + registerTestCapability("my-cap", handler); + expect(getTestCapabilityHandler("my-cap")).toBe(handler); + }); + + test("register throws when id already exists", async () => { + registerTestCapability("dup", () => ({ + response: { case: "error" as const, value: "" }, + })); expect(() => - registerTestCapability('dup', () => ({ - response: { case: 'error' as const, value: '' }, + registerTestCapability("dup", () => ({ + response: { case: "error" as const, value: "" }, })), - ).toThrow('capability already exists: dup') - }) -}) - -describe('TestRuntime / helper layer', () => { - test('getLogs returns messages written via helper log()', () => { - const rt = newTestRuntime() - rt.log('msg1') - rt.log('msg2') - expect(rt.getLogs()).toEqual(['msg1', 'msg2']) - }) - - test('now() uses Date.now() when setTimeProvider not set', () => { - const rt = newTestRuntime() - const before = Date.now() - const t = rt.now().getTime() - const after = Date.now() - expect(t).toBeGreaterThanOrEqual(before) - expect(t).toBeLessThanOrEqual(after) - }) - - test('setTimeProvider causes helper now() to return provided value', () => { - const rt = newTestRuntime() - const fixed = 999888777666 - rt.setTimeProvider(() => fixed) - expect(rt.now().getTime()).toBe(fixed) - }) - - test('sleep() completes without throwing', () => { - const rt = newTestRuntime() - expect(() => rt.sleep(100)).not.toThrow() - }) - - test('sleep() with zero milliseconds completes without throwing', () => { - const rt = newTestRuntime() - expect(() => rt.sleep(0)).not.toThrow() - }) - - test('sleep() returns undefined (no-op in test runtime)', () => { - const rt = newTestRuntime() - const result = rt.sleep(250) - expect(result).toBeUndefined() - }) - - test('helper call returns false for unregistered capability', () => { - const rt = newTestRuntime() - const cap = new BasicActionCapability() - const call = cap.performAction(rt, { inputThing: true }) - expect(() => call.result()).toThrow(CapabilityError) - expect(() => call.result()).toThrow(/not found/) - }) - - test('registered capability: callCapability and await path both route to handler and return result', () => { - const expectedResult = 'result-from-registered-handler' - const mock = BasicTestActionMock.testInstance() - mock.performAction = () => ({ adaptedThing: expectedResult }) - const rt = newTestRuntime() + ).toThrow("capability already exists: dup"); + }); +}); + +describe("TestRuntime / helper layer", () => { + test("getLogs returns messages written via helper log()", () => { + const rt = newTestRuntime(); + rt.log("msg1"); + rt.log("msg2"); + expect(rt.getLogs()).toEqual(["msg1", "msg2"]); + }); + + test("now() uses Date.now() when setTimeProvider not set", () => { + const rt = newTestRuntime(); + const before = Date.now(); + const t = rt.now().getTime(); + const after = Date.now(); + expect(t).toBeGreaterThanOrEqual(before); + expect(t).toBeLessThanOrEqual(after); + }); + + test("setTimeProvider causes helper now() to return provided value", () => { + const rt = newTestRuntime(); + const fixed = 999888777666; + rt.setTimeProvider(() => fixed); + expect(rt.now().getTime()).toBe(fixed); + }); + + test("sleep() completes without throwing", () => { + const rt = newTestRuntime(); + expect(() => rt.sleep(100)).not.toThrow(); + }); + + test("sleep() with zero milliseconds completes without throwing", () => { + const rt = newTestRuntime(); + expect(() => rt.sleep(0)).not.toThrow(); + }); + + test("sleep() returns undefined (no-op in test runtime)", () => { + const rt = newTestRuntime(); + const result = rt.sleep(250); + expect(result).toBeUndefined(); + }); + + test("helper call returns false for unregistered capability", () => { + const rt = newTestRuntime(); + const cap = new BasicActionCapability(); + const call = cap.performAction(rt, { inputThing: true }); + expect(() => call.result()).toThrow(CapabilityError); + expect(() => call.result()).toThrow(/not found/); + }); + + test("registered capability: callCapability and await path both route to handler and return result", () => { + const expectedResult = "result-from-registered-handler"; + const mock = BasicTestActionMock.testInstance(); + mock.performAction = () => ({ adaptedThing: expectedResult }); + const rt = newTestRuntime(); // Sync path: callCapability (via performAction) then .result() triggers internal await const call1 = new BasicActionCapability().performAction(rt, { inputThing: true, - }) - expect(call1.result().adaptedThing).toBe(expectedResult) + }); + expect(call1.result().adaptedThing).toBe(expectedResult); // Async path: two in-flight calls, then both .result() — helper routes by callbackId const call2 = new BasicActionCapability().performAction(rt, { inputThing: false, - }) + }); const call3 = new BasicActionCapability().performAction(rt, { inputThing: true, - }) - expect(call2.result().adaptedThing).toBe(expectedResult) - expect(call3.result().adaptedThing).toBe(expectedResult) - }) - - test('helper call catches handler throw and await returns error response', () => { - const rt = newTestRuntime() - const errMsg = 'node function error' + }); + expect(call2.result().adaptedThing).toBe(expectedResult); + expect(call3.result().adaptedThing).toBe(expectedResult); + }); + + test("helper call catches handler throw and await returns error response", () => { + const rt = newTestRuntime(); + const errMsg = "node function error"; const p = rt.runInNodeMode(() => { - throw new Error(errMsg) - }, consensusMedianAggregation())() - expect(() => p.result()).toThrow(errMsg) - }) - - test('helper await throws RESPONSE_BUFFER_TOO_SMALL when serialized response exceeds maxResponseSize', () => { - const rt = newTestRuntime(null, { maxResponseSize: 1 }) - const payload = new Uint8Array(new ArrayBuffer(3)) - payload.set([1, 2, 3]) + throw new Error(errMsg); + }, consensusMedianAggregation())(); + expect(() => p.result()).toThrow(errMsg); + }); + + test("helper await throws RESPONSE_BUFFER_TOO_SMALL when serialized response exceeds maxResponseSize", () => { + const rt = newTestRuntime(null, { maxResponseSize: 1 }); + const payload = new Uint8Array(new ArrayBuffer(3)); + payload.set([1, 2, 3]); const reportCall = rt.report({ - encodedPayload: Buffer.from(payload).toString('base64'), - }) - expect(() => reportCall.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL) - }) - - test('default Report handler: defaultReport metadata + payload + sigs', () => { - const rt = newTestRuntime() - const payloadBytes = new TextEncoder().encode('some_encoded_report_data') - const payload = new Uint8Array(new ArrayBuffer(payloadBytes.length)) - payload.set(payloadBytes) - const result = rt.report({ encodedPayload: Buffer.from(payload).toString('base64') }).result() - const unwrapped = result.x_generatedCodeOnly_unwrap() - expect(unwrapped.rawReport.length).toBe(REPORT_METADATA_HEADER_LENGTH + payload.length) - const expectedMetadata = new Uint8Array(REPORT_METADATA_HEADER_LENGTH) + encodedPayload: Buffer.from(payload).toString("base64"), + }); + expect(() => reportCall.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL); + }); + + test("default Report handler: defaultReport metadata + payload + sigs", () => { + const rt = newTestRuntime(); + const payloadBytes = new TextEncoder().encode("some_encoded_report_data"); + const payload = new Uint8Array(new ArrayBuffer(payloadBytes.length)); + payload.set(payloadBytes); + const result = rt + .report({ encodedPayload: Buffer.from(payload).toString("base64") }) + .result(); + const unwrapped = result.x_generatedCodeOnly_unwrap(); + expect(unwrapped.rawReport.length).toBe( + REPORT_METADATA_HEADER_LENGTH + payload.length, + ); + const expectedMetadata = new Uint8Array(REPORT_METADATA_HEADER_LENGTH); for (let i = 0; i < REPORT_METADATA_HEADER_LENGTH; i++) { - expectedMetadata[i] = i % 256 + expectedMetadata[i] = i % 256; } - expect(unwrapped.rawReport.slice(0, REPORT_METADATA_HEADER_LENGTH)).toEqual(expectedMetadata) - expect(unwrapped.rawReport.slice(REPORT_METADATA_HEADER_LENGTH)).toEqual(payload) - expect(unwrapped.sigs).toHaveLength(2) - expect(new TextDecoder().decode(unwrapped.sigs[0].signature)).toBe('default_signature_1') - expect(new TextDecoder().decode(unwrapped.sigs[1].signature)).toBe('default_signature_2') - }) - - test('default Simple handler: observation value branch returns value', () => { - const rt = newTestRuntime() - const p = rt.runInNodeMode(() => 42, consensusMedianAggregation())() - expect(p.result()).toBe(42) - }) - - test('default Simple handler: observation error with default returns default', () => { - const rt = newTestRuntime() + expect(unwrapped.rawReport.slice(0, REPORT_METADATA_HEADER_LENGTH)).toEqual( + expectedMetadata, + ); + expect(unwrapped.rawReport.slice(REPORT_METADATA_HEADER_LENGTH)).toEqual( + payload, + ); + expect(unwrapped.sigs).toHaveLength(2); + expect(new TextDecoder().decode(unwrapped.sigs[0].signature)).toBe( + "default_signature_1", + ); + expect(new TextDecoder().decode(unwrapped.sigs[1].signature)).toBe( + "default_signature_2", + ); + }); + + test("default Simple handler: observation value branch returns value", () => { + const rt = newTestRuntime(); + const p = rt.runInNodeMode(() => 42, consensusMedianAggregation())(); + expect(p.result()).toBe(42); + }); + + test("default Simple handler: observation error with default returns default", () => { + const rt = newTestRuntime(); const p = rt.runInNodeMode(() => { - throw new Error('fail') - }, consensusMedianAggregation().withDefault(100))() - expect(p.result()).toBe(100) - }) + throw new Error("fail"); + }, consensusMedianAggregation().withDefault(100))(); + expect(p.result()).toBe(100); + }); - test('default Simple handler: observation error without default throws', () => { - const rt = newTestRuntime() + test("default Simple handler: observation error without default throws", () => { + const rt = newTestRuntime(); const p = rt.runInNodeMode(() => { - throw new Error('no default') - }, consensusMedianAggregation())() - expect(() => p.result()).toThrow('no default') - }) - - test('default consensus handler returns error for unknown method', async () => { - newTestRuntime() // registers consensus handler on current registry - const handler = getTestCapabilityHandler('consensus@1.0.0-alpha') - if (!handler) throw new Error('expected handler') + throw new Error("no default"); + }, consensusMedianAggregation())(); + expect(() => p.result()).toThrow("no default"); + }); + + test("default consensus handler returns error for unknown method", async () => { + newTestRuntime(); // registers consensus handler on current registry + const handler = getTestCapabilityHandler("consensus@1.0.0-alpha"); + if (!handler) throw new Error("expected handler"); const res = handler({ - id: 'consensus@1.0.0-alpha', - method: 'Other', + id: "consensus@1.0.0-alpha", + method: "Other", payload: create(AnySchema, { value: new Uint8Array(0) }), - }) - expect(res.response.case).toBe('error') - if (res.response.case === 'error') { - expect(res.response.value).toBe('unknown method Other') + }); + expect(res.response.case).toBe("error"); + if (res.response.case === "error") { + expect(res.response.value).toBe("unknown method Other"); } - }) - - test('helper getSecrets: secret found returns value', () => { - const secrets = new Map>() - secrets.set('ns1', new Map([['id1', 'val1']])) - const rt = newTestRuntime(secrets) - const result = rt.getSecret({ id: 'id1', namespace: 'ns1' }).result() - expect(result.value).toBe('val1') - expect(result.id).toBe('id1') - expect(result.namespace).toBe('ns1') - }) - - test('helper getSecrets: secret not found returns error response', () => { - const rt = newTestRuntime() - expect(() => rt.getSecret({ id: 'missing', namespace: 'ns' }).result()).toThrow(SecretsError) - }) - - test('newTestRuntime uses options.maxResponseSize', () => { - const rt = newTestRuntime(null, { maxResponseSize: 1 }) - const payload = new Uint8Array(new ArrayBuffer(2)) - payload.set([1, 2]) + }); + + test("helper getSecrets: secret found returns value", () => { + const secrets = new Map>(); + secrets.set("ns1", new Map([["id1", "val1"]])); + const rt = newTestRuntime(secrets); + const result = rt.getSecret({ id: "id1", namespace: "ns1" }).result(); + expect(result.value).toBe("val1"); + expect(result.id).toBe("id1"); + expect(result.namespace).toBe("ns1"); + }); + + test("helper getSecrets: batched call returns mixed secret/error responses", () => { + const secrets = new Map>(); + secrets.set("ns1", new Map([["id1", "val1"]])); + const rt = newTestRuntime(secrets); + + const responses = rt + .getSecrets([ + { id: "id1", namespace: "ns1" }, + { id: "missing", namespace: "ns1" }, + ]) + .result(); + + expect(responses.length).toBe(2); + expect(responses[0].response.case).toBe("secret"); + expect(responses[1].response.case).toBe("error"); + }); + + test("helper getSecrets: secret not found returns error response", () => { + const rt = newTestRuntime(); + expect(() => + rt.getSecret({ id: "missing", namespace: "ns" }).result(), + ).toThrow(SecretsError); + }); + + test("newTestRuntime rejects unsafe maxResponseSize values", () => { expect(() => - rt.report({ encodedPayload: Buffer.from(payload).toString('base64') }).result(), - ).toThrow(RESPONSE_BUFFER_TOO_SMALL) - }) - - test('newTestRuntime rejects unsafe maxResponseSize values', () => { - expect(() => newTestRuntime(null, { maxResponseSize: Number.MAX_SAFE_INTEGER + 1 })).toThrow( - 'newTestRuntime maxResponseSize must be a non-negative safe integer number', - ) - }) - - test('newTestRuntime with null/undefined secrets uses empty map', () => { - const rt = newTestRuntime(null) - expect(() => rt.getSecret({ id: 'x', namespace: 'y' }).result()).toThrow(SecretsError) - const rt2 = newTestRuntime() - expect(rt2.getLogs()).toEqual([]) - }) -}) - -describe('test wrapper', () => { - test('registry is available inside test body (set/read without passing registry)', async () => { - registerTestCapability('cap-a', () => ({ - response: { case: 'error' as const, value: 'a' }, - })) - const handler = getTestCapabilityHandler('cap-a') - if (!handler) throw new Error('expected handler') + newTestRuntime(null, { + maxResponseSize: Number.MAX_SAFE_INTEGER + 1, + }), + ).toThrow( + "newTestRuntime maxResponseSize must be a non-negative safe integer number", + ); + }); + + test("newTestRuntime uses options.maxResponseSize", () => { + const rt = newTestRuntime(null, { maxResponseSize: 1 }); + const payload = new Uint8Array(new ArrayBuffer(2)); + payload.set([1, 2]); + expect(() => + rt + .report({ encodedPayload: Buffer.from(payload).toString("base64") }) + .result(), + ).toThrow(RESPONSE_BUFFER_TOO_SMALL); + }); + + test("newTestRuntime with null/undefined secrets uses empty map", () => { + const rt = newTestRuntime(null); + expect(() => rt.getSecret({ id: "x", namespace: "y" }).result()).toThrow( + SecretsError, + ); + const rt2 = newTestRuntime(); + expect(rt2.getLogs()).toEqual([]); + }); +}); + +describe("test wrapper", () => { + test("registry is available inside test body (set/read without passing registry)", async () => { + registerTestCapability("cap-a", () => ({ + response: { case: "error" as const, value: "a" }, + })); + const handler = getTestCapabilityHandler("cap-a"); + if (!handler) throw new Error("expected handler"); expect( handler({ - id: 'cap-a', - method: 'M', + id: "cap-a", + method: "M", payload: create(AnySchema, { value: new Uint8Array(0) }), }).response, ).toEqual({ - case: 'error', - value: 'a', - }) - }) - - test('isolation: test A registers only-a', async () => { - registerTestCapability('only-a', () => ({ - response: { case: 'error' as const, value: 'a' }, - })) - expect(getTestCapabilityHandler('only-a')).toBeDefined() - }) - - test('isolation: test B does not see test A registry', async () => { - expect(getTestCapabilityHandler('only-a')).toBeUndefined() - }) - - bunTest('cleanup: after test finishes, store is undefined', async () => { + case: "error", + value: "a", + }); + }); + + test("isolation: test A registers only-a", async () => { + registerTestCapability("only-a", () => ({ + response: { case: "error" as const, value: "a" }, + })); + expect(getTestCapabilityHandler("only-a")).toBeDefined(); + }); + + test("isolation: test B does not see test A registry", async () => { + expect(getTestCapabilityHandler("only-a")).toBeUndefined(); + }); + + bunTest("cleanup: after test finishes, store is undefined", async () => { await __testOnlyRunWithRegistry(async () => { - expect(__testOnlyRegistryStore()).toBeDefined() - }) - expect(__testOnlyRegistryStore()).toBeUndefined() - }) + expect(__testOnlyRegistryStore()).toBeDefined(); + }); + expect(__testOnlyRegistryStore()).toBeUndefined(); + }); - bunTest('failure path: cleanup happens when test body throws', async () => { + bunTest("failure path: cleanup happens when test body throws", async () => { await expect( __testOnlyRunWithRegistry(async () => { - expect(__testOnlyRegistryStore()).toBeDefined() - throw new Error('intentional failure') + expect(__testOnlyRegistryStore()).toBeDefined(); + throw new Error("intentional failure"); }), - ).rejects.toThrow('intentional failure') - expect(__testOnlyRegistryStore()).toBeUndefined() - }) -}) + ).rejects.toThrow("intentional failure"); + expect(__testOnlyRegistryStore()).toBeUndefined(); + }); +}); diff --git a/packages/cre-sdk/src/sdk/wasm/runner.test.ts b/packages/cre-sdk/src/sdk/wasm/runner.test.ts index 4ff3edd7..b1cb5df3 100644 --- a/packages/cre-sdk/src/sdk/wasm/runner.test.ts +++ b/packages/cre-sdk/src/sdk/wasm/runner.test.ts @@ -1,10 +1,10 @@ -import { afterEach, describe, expect, mock, test } from 'bun:test' -import { create, fromBinary, toBinary } from '@bufbuild/protobuf' -import { anyPack, anyUnpack, EmptySchema } from '@bufbuild/protobuf/wkt' +import { afterEach, describe, expect, mock, test } from "bun:test"; +import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; +import { anyPack, anyUnpack, EmptySchema } from "@bufbuild/protobuf/wkt"; import { ConfigSchema, OutputsSchema, -} from '@cre/generated/capabilities/internal/basictrigger/v1/basic_trigger_pb' +} from "@cre/generated/capabilities/internal/basictrigger/v1/basic_trigger_pb"; import { AwaitSecretsRequestSchema, AwaitSecretsResponseSchema, @@ -19,308 +19,354 @@ import { type Trigger, TriggerSchema, type TriggerSubscriptionRequest, -} from '@cre/generated/sdk/v1alpha/sdk_pb' -import type { Value as ProtoValue } from '@cre/generated/values/v1/values_pb' -import { BasicCapability as BasicTriggerCapability } from '@cre/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen' -import { cre } from '@cre/sdk/cre' -import { Value } from '../utils' -import type { SecretsProvider } from '../workflow' -import { Runner } from './runner' +} from "@cre/generated/sdk/v1alpha/sdk_pb"; +import type { Value as ProtoValue } from "@cre/generated/values/v1/values_pb"; +import { BasicCapability as BasicTriggerCapability } from "@cre/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen"; +import { cre } from "@cre/sdk/cre"; +import { Value } from "../utils"; +import type { SecretsProvider } from "../workflow"; +import { Runner } from "./runner"; -const anyConfig = Buffer.from('config') -const anyMaxResponseSize = 2048n -const basicTrigger = new BasicTriggerCapability() -const capID = BasicTriggerCapability.CAPABILITY_ID +const anyConfig = Buffer.from("config"); +const anyMaxResponseSize = 2048n; +const basicTrigger = new BasicTriggerCapability(); +const capID = BasicTriggerCapability.CAPABILITY_ID; const subscribeRequest = create(ExecuteRequestSchema, { - request: { case: 'subscribe', value: create(EmptySchema) }, + request: { case: "subscribe", value: create(EmptySchema) }, maxResponseSize: anyMaxResponseSize, config: anyConfig, -}) +}); const anyExecuteRequest = create(ExecuteRequestSchema, { request: { - case: 'trigger', + case: "trigger", value: create(TriggerSchema, { id: 0n, - payload: anyPack(OutputsSchema, create(OutputsSchema, { coolOutput: 'hi' })), + payload: anyPack( + OutputsSchema, + create(OutputsSchema, { coolOutput: "hi" }), + ), }), }, maxResponseSize: anyMaxResponseSize, config: anyConfig, -}) +}); type TestRunnerBindings = { - versionV2: () => void - sendResponse: (data: Uint8Array) => number - getWasiArgs: () => string - getSecrets: (data: Uint8Array | Uint8Array, maxresponse: number) => any + versionV2: () => void; + sendResponse: (data: Uint8Array) => number; + getWasiArgs: () => string; + getSecrets: ( + data: Uint8Array | Uint8Array, + maxresponse: number, + ) => any; awaitSecrets: ( data: Uint8Array | Uint8Array, maxresponse: number, - ) => Uint8Array | Uint8Array -} + ) => Uint8Array | Uint8Array; +}; const mockHostBindings: TestRunnerBindings = { sendResponse: mock(() => { - return 0 + return 0; }), versionV2: mock(() => {}), getWasiArgs: mock(() => { - throw new Error('override for tests') + throw new Error("override for tests"); }), getSecrets: mock((data, maxResponseSize) => { - throw new Error('override for tests') + throw new Error("override for tests"); }), awaitSecrets: mock((data, maxResponseSize) => { - throw new Error('override for tests') + throw new Error("override for tests"); }), -} +}; const proxyHostBindings = { sendResponse: (data: Uint8Array) => { - return mockHostBindings.sendResponse(data) + return mockHostBindings.sendResponse(data); }, versionV2: () => { - return mockHostBindings.versionV2() + return mockHostBindings.versionV2(); }, getWasiArgs: () => { - return mockHostBindings.getWasiArgs() + return mockHostBindings.getWasiArgs(); }, switchModes: mock(() => {}), log: (message: string) => { - throw new Error('log called unexpectedly in test') + throw new Error("log called unexpectedly in test"); }, callCapability: (data: Uint8Array) => { - throw new Error('callCapability called unexpectedly in test') + throw new Error("callCapability called unexpectedly in test"); }, awaitCapabilities: (data: Uint8Array, id: number) => { - throw new Error('awaitCapabilities called unexpectedly in test') + throw new Error("awaitCapabilities called unexpectedly in test"); }, getSecrets: (data: Uint8Array, id: number) => { - return mockHostBindings.getSecrets(data, id) + return mockHostBindings.getSecrets(data, id); }, awaitSecrets: (data: Uint8Array, id: number) => { - return mockHostBindings.awaitSecrets(data, id) + return mockHostBindings.awaitSecrets(data, id); }, now: () => { - throw new Error('now called unexpectedly in test') + throw new Error("now called unexpectedly in test"); }, sleep: () => { - throw new Error('sleep called unexpectedly in test') + throw new Error("sleep called unexpectedly in test"); }, -} +}; -Object.assign(globalThis, proxyHostBindings) +Object.assign(globalThis, proxyHostBindings); afterEach(() => { - mock.restore() -}) + mock.restore(); +}); -describe('runner', () => { - describe('run', () => { - test('gathers subscriptions', async () => { - var sentResponse: ExecutionResult | null = null +describe("runner", () => { + describe("run", () => { + test("gathers subscriptions", async () => { + var sentResponse: ExecutionResult | null = null; mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input) - return 0 - }) - const runner = await getTestRunner(subscribeRequest) + sentResponse = fromBinary(ExecutionResultSchema, input); + return 0; + }); + const runner = await getTestRunner(subscribeRequest); await runner.run(async (_: string, secretsProvider: SecretsProvider) => { return [ - cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), () => { - throw new Error('Must not be called during registration to tiggers') + cre.handler(basicTrigger.trigger({ name: "foo", number: 10 }), () => { + throw new Error( + "Must not be called during registration to tiggers", + ); }), - ] - }) - expect(sentResponse).toBeDefined() - expect(sentResponse!.result.case).toBe('triggerSubscriptions') - const responseValue = sentResponse!.result.value! as TriggerSubscriptionRequest - expect(responseValue.subscriptions.length).toBe(1) - expect(responseValue.subscriptions[0].id).toBe(capID) - expect(responseValue.subscriptions[0].method).toBe('Trigger') - expect(responseValue.subscriptions[0].payload).toBeDefined() - const actualConfig = anyUnpack(responseValue.subscriptions[0].payload!, ConfigSchema)! - expect(actualConfig.name).toBe('foo') - expect(actualConfig.number).toBe(10) - }) + ]; + }); + expect(sentResponse).toBeDefined(); + expect(sentResponse!.result.case).toBe("triggerSubscriptions"); + const responseValue = sentResponse!.result + .value! as TriggerSubscriptionRequest; + expect(responseValue.subscriptions.length).toBe(1); + expect(responseValue.subscriptions[0].id).toBe(capID); + expect(responseValue.subscriptions[0].method).toBe("Trigger"); + expect(responseValue.subscriptions[0].payload).toBeDefined(); + const actualConfig = anyUnpack( + responseValue.subscriptions[0].payload!, + ConfigSchema, + )!; + expect(actualConfig.name).toBe("foo"); + expect(actualConfig.number).toBe(10); + }); - test('executes workflow', async () => { - var sentResponse: ExecutionResult | null = null + test("executes workflow", async () => { + var sentResponse: ExecutionResult | null = null; mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input) - return 0 - }) - const runner = await getTestRunner(anyExecuteRequest) + sentResponse = fromBinary(ExecutionResultSchema, input); + return 0; + }); + const runner = await getTestRunner(anyExecuteRequest); await runner.run(async (_: string, secretsProvider: SecretsProvider) => { return [ - cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), (runtime, trigger) => { - expect(runtime.config).toBe(anyConfig.toString()) - expect(trigger.coolOutput).toBe('hi') - return 10 - }), - ] - }) - expect(sentResponse).toBeDefined() - expect(sentResponse!.result.case).toBe('value') + cre.handler( + basicTrigger.trigger({ name: "foo", number: 10 }), + (runtime, trigger) => { + expect(runtime.config).toBe(anyConfig.toString()); + expect(trigger.coolOutput).toBe("hi"); + return 10; + }, + ), + ]; + }); + expect(sentResponse).toBeDefined(); + expect(sentResponse!.result.case).toBe("value"); expect( Value.wrap(sentResponse!.result.value as ProtoValue).unwrapToType({ instance: 10, }), - ).toBe(10) - }) - }) + ).toBe(10); + }); + }); - test('executes subscribe error', async () => { - var sentResponse: ExecutionResult | null = null - const anyError = 'error' + test("executes subscribe error", async () => { + var sentResponse: ExecutionResult | null = null; + const anyError = "error"; mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input) - expect(sentResponse!.result.case).toBe('error') - expect(sentResponse!.result.value).toBe(anyError) - return 0 - }) - const runner = await getTestRunner(subscribeRequest) + sentResponse = fromBinary(ExecutionResultSchema, input); + expect(sentResponse!.result.case).toBe("error"); + expect(sentResponse!.result.value).toBe(anyError); + return 0; + }); + const runner = await getTestRunner(subscribeRequest); await runner.run((_: string, secretsProvider: SecretsProvider) => { - throw new Error(anyError) - }) - }) + throw new Error(anyError); + }); + }); - test('executes subscribe resolve error', async () => { - var sentResponse: ExecutionResult | null = null - const anyError = 'error' + test("executes subscribe resolve error", async () => { + var sentResponse: ExecutionResult | null = null; + const anyError = "error"; mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input) - expect(sentResponse!.result.case).toBe('error') - expect(sentResponse!.result.value).toBe(anyError) - return 0 - }) - const runner = await getTestRunner(subscribeRequest) + sentResponse = fromBinary(ExecutionResultSchema, input); + expect(sentResponse!.result.case).toBe("error"); + expect(sentResponse!.result.value).toBe(anyError); + return 0; + }); + const runner = await getTestRunner(subscribeRequest); await runner.run(async (_: string, secretsProvider: SecretsProvider) => { - return Promise.reject(new Error(anyError)) - }) - }) + return Promise.reject(new Error(anyError)); + }); + }); - test('executes trigger error', async () => { - var sentResponse: ExecutionResult | null = null - const anyError = 'error' + test("executes trigger error", async () => { + var sentResponse: ExecutionResult | null = null; + const anyError = "error"; mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input) - expect(sentResponse!.result.case).toBe('error') - expect(sentResponse!.result.value).toBe(anyError) - return 0 - }) - const runner = await getTestRunner(anyExecuteRequest) + sentResponse = fromBinary(ExecutionResultSchema, input); + expect(sentResponse!.result.case).toBe("error"); + expect(sentResponse!.result.value).toBe(anyError); + return 0; + }); + const runner = await getTestRunner(anyExecuteRequest); await runner.run(async (_: string, secretsProvider: SecretsProvider) => { - throw new Error(anyError) - }) - }) + throw new Error(anyError); + }); + }); - test('executes workflow with multiple triggers', async () => { - var sentResponse: ExecutionResult | null = null + test("executes workflow with multiple triggers", async () => { + var sentResponse: ExecutionResult | null = null; mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input) - return 0 - }) - const testRequest = structuredClone(anyExecuteRequest) - const trigger = testRequest.request.value as Trigger - trigger.id = 1n + sentResponse = fromBinary(ExecutionResultSchema, input); + return 0; + }); + const testRequest = structuredClone(anyExecuteRequest); + const trigger = testRequest.request.value as Trigger; + trigger.id = 1n; - const runner = await getTestRunner(testRequest) + const runner = await getTestRunner(testRequest); await runner.run(async (_: string, secretsProvider: SecretsProvider) => { return [ - cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), (runtime, trigger) => { - expect(runtime.config).toBe(anyConfig.toString()) - expect(trigger.coolOutput).toBe('hi') - return 10 - }), - cre.handler(basicTrigger.trigger({ name: 'bar', number: 20 }), (runtime, trigger) => { - expect(runtime.config).toBe(anyConfig.toString()) - expect(trigger.coolOutput).toBe('hi') - return 20 - }), - cre.handler(basicTrigger.trigger({ name: 'baz', number: 30 }), (runtime, trigger) => { - expect(runtime.config).toBe(anyConfig.toString()) - expect(trigger.coolOutput).toBe('hi') - return 30 - }), - ] - }) - expect(sentResponse).toBeDefined() - expect(sentResponse!.result.case).toBe('value') + cre.handler( + basicTrigger.trigger({ name: "foo", number: 10 }), + (runtime, trigger) => { + expect(runtime.config).toBe(anyConfig.toString()); + expect(trigger.coolOutput).toBe("hi"); + return 10; + }, + ), + cre.handler( + basicTrigger.trigger({ name: "bar", number: 20 }), + (runtime, trigger) => { + expect(runtime.config).toBe(anyConfig.toString()); + expect(trigger.coolOutput).toBe("hi"); + return 20; + }, + ), + cre.handler( + basicTrigger.trigger({ name: "baz", number: 30 }), + (runtime, trigger) => { + expect(runtime.config).toBe(anyConfig.toString()); + expect(trigger.coolOutput).toBe("hi"); + return 30; + }, + ), + ]; + }); + expect(sentResponse).toBeDefined(); + expect(sentResponse!.result.case).toBe("value"); expect( Value.wrap(sentResponse!.result.value as ProtoValue).unwrapToType({ instance: 10, }), - ).toBe(20) - }) + ).toBe(20); + }); - test('get secrets passes max response size', async () => { + test("get secrets passes max response size", async () => { const anySecretResponse = create(SecretResponseSchema, { response: { - case: 'secret', + case: "secret", value: create(SecretSchema, { - id: 'Bar', - namespace: 'Foo', - owner: 'Baz', - value: 'Qux', + id: "Bar", + namespace: "Foo", + owner: "Baz", + value: "Qux", }), }, - }) + }); const anySecretsResponse = create(SecretResponsesSchema, { responses: [anySecretResponse], - }) + }); mockHostBindings.getSecrets = (data, maxResponseSize) => { - const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data - const secretsRequest = fromBinary(GetSecretsRequestSchema, dataBytes) - expect(secretsRequest.requests.length).toBe(1) - expect(secretsRequest.requests[0].namespace).toBe('Foo') - expect(secretsRequest.requests[0].id).toBe('Bar') - expect(secretsRequest.callbackId).toBe(0) - expect(maxResponseSize).toBe(Number(anyMaxResponseSize)) - return 0 - } + const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data; + const secretsRequest = fromBinary(GetSecretsRequestSchema, dataBytes); + expect(secretsRequest.requests.length).toBe(1); + expect(secretsRequest.requests[0].namespace).toBe("Foo"); + expect(secretsRequest.requests[0].id).toBe("Bar"); + expect(secretsRequest.callbackId).toBe(0); + expect(maxResponseSize).toBe(Number(anyMaxResponseSize)); + return 0; + }; mockHostBindings.awaitSecrets = (data, maxResponseSize) => { - const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data - const awaitSecretsRequest = fromBinary(AwaitSecretsRequestSchema, dataBytes) - expect(awaitSecretsRequest.ids.length).toBe(1) - expect(awaitSecretsRequest.ids[0]).toBe(0) - expect(maxResponseSize).toBe(Number(anyMaxResponseSize)) + const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data; + const awaitSecretsRequest = fromBinary( + AwaitSecretsRequestSchema, + dataBytes, + ); + expect(awaitSecretsRequest.ids.length).toBe(1); + expect(awaitSecretsRequest.ids[0]).toBe(0); + expect(maxResponseSize).toBe(Number(anyMaxResponseSize)); // Create the proper AwaitSecretsResponse with a map const awaitSecretsResponse = create(AwaitSecretsResponseSchema, { responses: { 0: anySecretsResponse, }, - }) - return toBinary(AwaitSecretsResponseSchema, awaitSecretsResponse) - } + }); + return toBinary(AwaitSecretsResponseSchema, awaitSecretsResponse); + }; - const dr = getTestRunner(subscribeRequest) - await (await dr).run(async (_: string, secretsProvider: SecretsProvider) => { - const secret = await secretsProvider.getSecret({ namespace: 'Foo', id: 'Bar' }).result() - expect(secret.namespace).toBe('Foo') - expect(secret.id).toBe('Bar') - expect(secret.owner).toBe('Baz') - expect(secret.value).toBe('Qux') - return [cre.handler(basicTrigger.trigger({}), () => 10)] - }) - expect(true).toBe(true) - }) -}) + const dr = getTestRunner(subscribeRequest); + await (await dr).run( + async (_: string, secretsProvider: SecretsProvider) => { + const batched = await secretsProvider + .getSecrets([{ namespace: "Foo", id: "Bar" }]) + .result(); + expect(batched.length).toBe(1); + expect(batched[0].response.case).toBe("secret"); + if (batched[0].response.case === "secret") { + expect(batched[0].response.value.namespace).toBe("Foo"); + expect(batched[0].response.value.id).toBe("Bar"); + expect(batched[0].response.value.owner).toBe("Baz"); + expect(batched[0].response.value.value).toBe("Qux"); + } + + // Keep compatibility coverage for single-secret API. + const single = await secretsProvider + .getSecret({ namespace: "Foo", id: "Bar" }) + .result(); + expect(single.namespace).toBe("Foo"); + expect(single.id).toBe("Bar"); + expect(single.owner).toBe("Baz"); + expect(single.value).toBe("Qux"); + return [cre.handler(basicTrigger.trigger({}), () => 10)]; + }, + ); + expect(true).toBe(true); + }); +}); function getTestRunner(request: ExecuteRequest): Promise> { - const serialized = toBinary(ExecuteRequestSchema, request) - const encoded = Buffer.from(serialized).toString('base64') + const serialized = toBinary(ExecuteRequestSchema, request); + const encoded = Buffer.from(serialized).toString("base64"); // Update the mock to return the specific request - mockHostBindings.getWasiArgs = mock(() => JSON.stringify(['program', encoded])) + mockHostBindings.getWasiArgs = mock(() => + JSON.stringify(["program", encoded]), + ); return Runner.newRunner({ configParser: (b) => { - const stringConfig = Buffer.from(b).toString() - expect(stringConfig).toBe(anyConfig.toString()) - return stringConfig + const stringConfig = Buffer.from(b).toString(); + expect(stringConfig).toBe(anyConfig.toString()); + return stringConfig; }, - }) + }); } diff --git a/packages/cre-sdk/src/sdk/wasm/runner.ts b/packages/cre-sdk/src/sdk/wasm/runner.ts index 35bec786..985b83e7 100644 --- a/packages/cre-sdk/src/sdk/wasm/runner.ts +++ b/packages/cre-sdk/src/sdk/wasm/runner.ts @@ -1,16 +1,16 @@ -import { create, fromBinary, toBinary } from '@bufbuild/protobuf' +import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; import { type ExecuteRequest, ExecuteRequestSchema, type ExecutionResult, ExecutionResultSchema, TriggerSubscriptionRequestSchema, -} from '@cre/generated/sdk/v1alpha/sdk_pb' -import { type ConfigHandlerParams, configHandler } from '@cre/sdk/utils/config' -import type { SecretsProvider, Workflow } from '@cre/sdk/workflow' -import { Value } from '../utils' -import { hostBindings } from './host-bindings' -import { Runtime } from './runtime' +} from "@cre/generated/sdk/v1alpha/sdk_pb"; +import { type ConfigHandlerParams, configHandler } from "@cre/sdk/utils/config"; +import type { SecretsProvider, Workflow } from "@cre/sdk/workflow"; +import { Value } from "../utils"; +import { hostBindings } from "./host-bindings"; +import { Runtime } from "./runtime"; export class Runner { private constructor( @@ -21,21 +21,24 @@ export class Runner { static async newRunner( configHandlerParams?: ConfigHandlerParams, ): Promise> { - hostBindings.versionV2() - const request = Runner.getRequest() - const config = await configHandler(request, configHandlerParams) - return new Runner(config, request) + hostBindings.versionV2(); + const request = Runner.getRequest(); + const config = await configHandler( + request, + configHandlerParams, + ); + return new Runner(config, request); } private static getRequest(): ExecuteRequest { - const argsString = hostBindings.getWasiArgs() - let args + const argsString = hostBindings.getWasiArgs(); + let args: any; try { - args = JSON.parse(argsString) + args = JSON.parse(argsString); } catch (e) { throw new Error( - 'Invalid request: could not parse WASI arguments as JSON. Ensure the WASM runtime is passing valid arguments to the workflow', - ) + "Invalid request: could not parse WASI arguments as JSON. Ensure the WASM runtime is passing valid arguments to the workflow", + ); } // SDK expects exactly 2 args: @@ -44,13 +47,13 @@ export class Runner { if (args.length !== 2) { throw new Error( `Invalid request: expected exactly 2 WASI arguments (script name and base64-encoded request payload), but received ${args.length}`, - ) + ); } - const base64Request = args[1] + const base64Request = args[1]; - const bytes = Buffer.from(base64Request, 'base64') - return fromBinary(ExecuteRequestSchema, bytes) + const bytes = Buffer.from(base64Request, "base64"); + return fromBinary(ExecuteRequestSchema, bytes); } async run( @@ -59,35 +62,36 @@ export class Runner { secretsProvider: SecretsProvider, ) => Promise> | Workflow, ) { - const runtime = new Runtime(this.config, 0, this.request.maxResponseSize) + const runtime = new Runtime(this.config, 0, this.request.maxResponseSize); - let result: Promise | ExecutionResult + let result: Promise | ExecutionResult; try { const workflow = await initFn(this.config, { + getSecrets: runtime.getSecrets.bind(runtime), getSecret: runtime.getSecret.bind(runtime), - }) + }); switch (this.request.request.case) { - case 'subscribe': - result = this.handleSubscribePhase(this.request, workflow) - break - case 'trigger': - result = this.handleExecutionPhase(this.request, workflow, runtime) - break + case "subscribe": + result = this.handleSubscribePhase(this.request, workflow); + break; + case "trigger": + result = this.handleExecutionPhase(this.request, workflow, runtime); + break; default: throw new Error( `Unknown request type '${this.request.request.case}': expected 'subscribe' or 'trigger'. This may indicate a version mismatch between the SDK and the CRE runtime`, - ) + ); } } catch (e) { - const err = e instanceof Error ? e.message : String(e) + const err = e instanceof Error ? e.message : String(e); result = create(ExecutionResultSchema, { - result: { case: 'error', value: err }, - }) + result: { case: "error", value: err }, + }); } - const awaitedResult = await result! - hostBindings.sendResponse(toBinary(ExecutionResultSchema, awaitedResult)) + const awaitedResult = await result!; + hostBindings.sendResponse(toBinary(ExecutionResultSchema, awaitedResult)); } async handleExecutionPhase( @@ -95,37 +99,37 @@ export class Runner { workflow: Workflow, runtime: Runtime, ): Promise { - if (req.request.case !== 'trigger') { + if (req.request.case !== "trigger") { throw new Error( `cannot handle non-trigger request as a trigger: received request type '${req.request.case}' in handleExecutionPhase. This is an internal SDK error`, - ) + ); } - const triggerMsg = req.request.value + const triggerMsg = req.request.value; // We're about to cast bigint to number, so we need to check if it's safe - const id = BigInt(triggerMsg.id) + const id = BigInt(triggerMsg.id); if (id > BigInt(Number.MAX_SAFE_INTEGER)) { throw new Error( `Trigger ID ${id} exceeds JavaScript safe integer range (Number.MAX_SAFE_INTEGER = ${Number.MAX_SAFE_INTEGER}). This trigger ID cannot be safely represented as a number`, - ) + ); } - const index = Number(triggerMsg.id) + const index = Number(triggerMsg.id); if (Number.isFinite(index) && index >= 0 && index < workflow.length) { - const entry = workflow[index] - const schema = entry.trigger.outputSchema() + const entry = workflow[index]; + const schema = entry.trigger.outputSchema(); if (!triggerMsg.payload) { return create(ExecutionResultSchema, { result: { - case: 'error', + case: "error", value: `trigger payload is missing for handler at index ${index} (trigger ID ${triggerMsg.id}). The trigger event must include a payload`, }, - }) + }); } - const payloadAny = triggerMsg.payload + const payloadAny = triggerMsg.payload; /** * Note: do not hardcode method name; routing by id is authoritative. @@ -134,39 +138,42 @@ export class Runner { * * @see https://github.com/smartcontractkit/cre-sdk-go/blob/5a41d81e3e072008484e85dc96d746401aafcba2/cre/wasm/runner.go#L81 * */ - const decoded = fromBinary(schema, payloadAny.value) - const adapted = entry.trigger.adapt(decoded) + const decoded = fromBinary(schema, payloadAny.value); + const adapted = entry.trigger.adapt(decoded); try { - const result = await entry.fn(runtime, adapted) - const wrapped = Value.wrap(result) + const result = await entry.fn(runtime, adapted); + const wrapped = Value.wrap(result); return create(ExecutionResultSchema, { - result: { case: 'value', value: wrapped.proto() }, - }) + result: { case: "value", value: wrapped.proto() }, + }); } catch (e) { - const err = e instanceof Error ? e.message : String(e) + const err = e instanceof Error ? e.message : String(e); return create(ExecutionResultSchema, { - result: { case: 'error', value: err }, - }) + result: { case: "error", value: err }, + }); } } return create(ExecutionResultSchema, { result: { - case: 'error', + case: "error", value: `trigger not found: no workflow handler registered at index ${index} (trigger ID ${triggerMsg.id}). The workflow has ${workflow.length} handler(s) registered. Verify the trigger subscription matches a registered handler`, }, - }) + }); } - handleSubscribePhase(req: ExecuteRequest, workflow: Workflow): ExecutionResult { - if (req.request.case !== 'subscribe') { + handleSubscribePhase( + req: ExecuteRequest, + workflow: Workflow, + ): ExecutionResult { + if (req.request.case !== "subscribe") { return create(ExecutionResultSchema, { result: { - case: 'error', + case: "error", value: `subscribe request expected but received '${req.request.case}' in handleSubscribePhase. This is an internal SDK error`, }, - }) + }); } // Build TriggerSubscriptionRequest from the workflow entries @@ -174,14 +181,14 @@ export class Runner { id: entry.trigger.capabilityId(), method: entry.trigger.method(), payload: entry.trigger.configAsAny(), - })) + })); const subscriptionRequest = create(TriggerSubscriptionRequestSchema, { subscriptions, - }) + }); return create(ExecutionResultSchema, { - result: { case: 'triggerSubscriptions', value: subscriptionRequest }, - }) + result: { case: "triggerSubscriptions", value: subscriptionRequest }, + }); } } diff --git a/packages/cre-sdk/src/sdk/workflow.ts b/packages/cre-sdk/src/sdk/workflow.ts index f033a784..8b4b969b 100644 --- a/packages/cre-sdk/src/sdk/workflow.ts +++ b/packages/cre-sdk/src/sdk/workflow.ts @@ -1,19 +1,18 @@ -import type { Message } from '@bufbuild/protobuf' +import type { Message } from "@bufbuild/protobuf"; import type { - CapabilityResponse, Secret, SecretRequest, SecretRequestJson, -} from '@cre/generated/sdk/v1alpha/sdk_pb' -import { type Runtime } from '@cre/sdk/runtime' -import type { Trigger } from '@cre/sdk/utils/triggers/trigger-interface' -import type { SecretsError } from './errors' -import type { CreSerializable } from './utils' + SecretResponse, +} from "@cre/generated/sdk/v1alpha/sdk_pb"; +import type { Runtime } from "@cre/sdk/runtime"; +import type { Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; +import type { CreSerializable } from "./utils"; export type HandlerFn = ( runtime: Runtime, triggerOutput: TTriggerOutput, -) => Promise> | CreSerializable +) => Promise> | CreSerializable; export interface HandlerEntry< TConfig, @@ -21,11 +20,13 @@ export interface HandlerEntry< TTriggerOutput, TResult, > { - trigger: Trigger - fn: HandlerFn + trigger: Trigger; + fn: HandlerFn; } -export type Workflow = ReadonlyArray> +export type Workflow = ReadonlyArray< + HandlerEntry +>; export const handler = < TRawTriggerOutput extends Message, @@ -38,10 +39,13 @@ export const handler = < ): HandlerEntry => ({ trigger, fn, -}) +}); export type SecretsProvider = { + getSecrets(requests: Array): { + result: () => SecretResponse[]; + }; getSecret(request: SecretRequest | SecretRequestJson): { - result: () => Secret - } -} + result: () => Secret; + }; +}; From ccb025f7c94dcbe60dfbb6788bac346a33e231d5 Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Tue, 5 May 2026 15:47:29 -0400 Subject: [PATCH 02/20] Fixed linting --- packages/cre-sdk-examples/package.json | 2 +- .../src/workflows/secrets/index.ts | 73 +- .../cre-sdk/src/sdk/impl/runtime-impl.test.ts | 1396 ++++++++--------- packages/cre-sdk/src/sdk/impl/runtime-impl.ts | 350 ++--- .../src/sdk/testutils/test-runtime.test.ts | 504 +++--- packages/cre-sdk/src/sdk/wasm/runner.test.ts | 447 +++--- packages/cre-sdk/src/sdk/wasm/runner.ts | 136 +- packages/cre-sdk/src/sdk/workflow.ts | 32 +- 8 files changed, 1353 insertions(+), 1587 deletions(-) diff --git a/packages/cre-sdk-examples/package.json b/packages/cre-sdk-examples/package.json index 3e55227c..a051e179 100644 --- a/packages/cre-sdk-examples/package.json +++ b/packages/cre-sdk-examples/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@bufbuild/protobuf": "2.6.3", - "@chainlink/cre-sdk": "1.7.0-alpha.1", + "@chainlink/cre-sdk": "workspace:*", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/packages/cre-sdk-examples/src/workflows/secrets/index.ts b/packages/cre-sdk-examples/src/workflows/secrets/index.ts index b00c0efa..e6602f82 100644 --- a/packages/cre-sdk-examples/src/workflows/secrets/index.ts +++ b/packages/cre-sdk-examples/src/workflows/secrets/index.ts @@ -8,14 +8,14 @@ import { ok, Runner, type Runtime, -} from "@chainlink/cre-sdk"; -import { z } from "zod"; +} from '@chainlink/cre-sdk' +import { z } from 'zod' const configSchema = z.object({ url: z.string(), -}); +}) -type Config = z.infer; +type Config = z.infer const responseSchema = z.object({ name: z.string(), @@ -34,9 +34,9 @@ const responseSchema = z.object({ created: z.string().datetime(), edited: z.string().datetime(), url: z.string(), -}); +}) -type StarWarsCharacter = z.infer; +type StarWarsCharacter = z.infer const fetchStarWarsCharacter = ( sendRequester: HTTPSendRequester, @@ -44,65 +44,60 @@ const fetchStarWarsCharacter = ( url: string, characterId: string, ): StarWarsCharacter => { - url = config.url.replace("{characterId}", characterId); - const response = sendRequester.sendRequest({ url, method: "GET" }).result(); + url = config.url.replace('{characterId}', characterId) + const response = sendRequester.sendRequest({ url, method: 'GET' }).result() // Check if the response is successful using the helper function if (!ok(response)) { - throw new Error(`HTTP request failed with status: ${response.statusCode}`); + throw new Error(`HTTP request failed with status: ${response.statusCode}`) } - const character = responseSchema.parse(json(response)); + const character = responseSchema.parse(json(response)) - return character; -}; + return character +} const onHTTPTrigger = async (runtime: Runtime) => { - const httpCapability = new HTTPClient(); + const httpCapability = new HTTPClient() // Fetch a single secret - const secretUrlValue = runtime.getSecret({ id: "SECRET_URL" }).result().value; + const secretUrlValue = runtime.getSecret({ id: 'SECRET_URL' }).result().value // Fetch multiple secrets - const secretsToFetch = [ - { id: "CHARACTER_ID1" }, - { id: "CHARACTER_ID2" }, - { id: "CHARACTER_ID3" }, - ]; - const secretResponses = runtime.getSecrets(secretsToFetch).result(); + const secretsToFetch = [{ id: 'CHARACTER_ID1' }, { id: 'CHARACTER_ID2' }, { id: 'CHARACTER_ID3' }] + const secretResponses = runtime.getSecrets(secretsToFetch).result() const characterIds = secretResponses.flatMap((response) => - response.response.case === "secret" && response.response.value?.id + response.response.case === 'secret' && response.response.value?.id ? [response.response.value.value] : [], - ); + ) if (characterIds.length === 0) { - throw new Error("No character ID secrets available"); + throw new Error('No character ID secrets available') } // choose a random character id // Math.random() is safe to use in the workflow - const characterId = - characterIds[Math.floor(Math.random() * characterIds.length)]; + const characterId = characterIds[Math.floor(Math.random() * characterIds.length)] const result: StarWarsCharacter = httpCapability - .sendRequest( - runtime, - fetchStarWarsCharacter, - consensusIdenticalAggregation(), - )(runtime.config, secretUrlValue, characterId) - .result(); - - return result; -}; + .sendRequest(runtime, fetchStarWarsCharacter, consensusIdenticalAggregation())( + runtime.config, + secretUrlValue, + characterId, + ) + .result() + + return result +} const initWorkflow = () => { - const httpTrigger = new HTTPCapability(); + const httpTrigger = new HTTPCapability() - return [handler(httpTrigger.trigger({}), onHTTPTrigger)]; -}; + return [handler(httpTrigger.trigger({}), onHTTPTrigger)] +} export async function main() { const runner = await Runner.newRunner({ configSchema, - }); - await runner.run(initWorkflow); + }) + await runner.run(initWorkflow) } diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts index 8c8476f9..0e05b5d3 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts @@ -1,21 +1,21 @@ -import { afterEach, describe, expect, mock, test } from "bun:test"; -import { create } from "@bufbuild/protobuf"; -import { type Any, anyPack, anyUnpack } from "@bufbuild/protobuf/wkt"; +import { afterEach, describe, expect, mock, test } from 'bun:test' +import { create } from '@bufbuild/protobuf' +import { type Any, anyPack, anyUnpack } from '@bufbuild/protobuf/wkt' import { InputSchema, OutputSchema, -} from "@cre/generated/capabilities/internal/actionandtrigger/v1/action_and_trigger_pb"; +} from '@cre/generated/capabilities/internal/actionandtrigger/v1/action_and_trigger_pb' import { InputsSchema, OutputsSchema, -} from "@cre/generated/capabilities/internal/basicaction/v1/basic_action_pb"; +} from '@cre/generated/capabilities/internal/basicaction/v1/basic_action_pb' import { type NodeInputs, type NodeInputsJson, NodeInputsSchema, type NodeOutputs, NodeOutputsSchema, -} from "@cre/generated/capabilities/internal/nodeaction/v1/node_action_pb"; +} from '@cre/generated/capabilities/internal/nodeaction/v1/node_action_pb' import { AggregationType, type AwaitCapabilitiesRequest, @@ -31,13 +31,13 @@ import { SecretResponsesSchema, type SimpleConsensusInputs, type SimpleConsensusInputsJson, -} from "@cre/generated/sdk/v1alpha/sdk_pb"; -import type { Value as ProtoValue } from "@cre/generated/values/v1/values_pb"; -import { BasicCapability } from "@cre/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen"; -import { BasicActionCapability } from "@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen"; -import { ConsensusCapability } from "@cre/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen"; -import { BasicActionCapability as NodeActionCapability } from "@cre/generated-sdk/capabilities/internal/nodeaction/v1/basicaction_sdk_gen"; -import type { NodeRuntime, Runtime } from "@cre/sdk/cre"; +} from '@cre/generated/sdk/v1alpha/sdk_pb' +import type { Value as ProtoValue } from '@cre/generated/values/v1/values_pb' +import { BasicCapability } from '@cre/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen' +import { BasicActionCapability } from '@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen' +import { ConsensusCapability } from '@cre/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen' +import { BasicActionCapability as NodeActionCapability } from '@cre/generated-sdk/capabilities/internal/nodeaction/v1/basicaction_sdk_gen' +import type { NodeRuntime, Runtime } from '@cre/sdk/cre' import { ConsensusAggregationByFields, ConsensusFieldAggregation, @@ -46,162 +46,141 @@ import { ignore, median, Value, -} from "@cre/sdk/utils"; -import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import { - DonModeError, - NodeModeError, - SecretsBatchError, - SecretsError, -} from "../errors"; -import { RESPONSE_BUFFER_TOO_SMALL } from "../testutils/test-runtime"; -import { type RuntimeHelpers, RuntimeImpl } from "./runtime-impl"; +} from '@cre/sdk/utils' +import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' +import { DonModeError, NodeModeError, SecretsBatchError, SecretsError } from '../errors' +import { RESPONSE_BUFFER_TOO_SMALL } from '../testutils/test-runtime' +import { type RuntimeHelpers, RuntimeImpl } from './runtime-impl' // Helper function to create a RuntimeHelpers mock with error-throwing defaults -function createRuntimeHelpersMock( - overrides: Partial = {}, -): RuntimeHelpers { +function createRuntimeHelpersMock(overrides: Partial = {}): RuntimeHelpers { // Create default implementation that throws errors for all methods const defaultMock: RuntimeHelpers = { call: mock(() => { - throw new Error("Method not implemented: call"); + throw new Error('Method not implemented: call') }), await: mock(() => { - throw new Error("Method not implemented: await"); + throw new Error('Method not implemented: await') }), getSecrets: mock(() => { - throw new Error("Method not implemented: getSecrets"); + throw new Error('Method not implemented: getSecrets') }), awaitSecrets: mock(() => { - throw new Error("Method not implemented: awaitSecrets"); + throw new Error('Method not implemented: awaitSecrets') }), // switchModes is used in every test, most will ignore it, so it's safe to default to a no-op. switchModes: mock(() => {}), now: mock(() => { - throw new Error("Method not implemented: now"); + throw new Error('Method not implemented: now') }), sleep: mock(() => { throw new Error('Method not implemented: sleep') }), log: mock(() => {}), - }; + } // Return a merged object with overrides taking precedence - return { ...defaultMock, ...overrides }; + return { ...defaultMock, ...overrides } } -const anyMaxSize = 1024n * 1024n; +const anyMaxSize = 1024n * 1024n // Store original prototypes for manual restoration -const originalConsensusSimple = ConsensusCapability.prototype.simple; -const originalNodeActionPerformAction = - NodeActionCapability.prototype.performAction; +const originalConsensusSimple = ConsensusCapability.prototype.simple +const originalNodeActionPerformAction = NodeActionCapability.prototype.performAction afterEach(() => { // Restore all mocks after each test - mock.restore(); + mock.restore() // Manually restore prototype methods - ConsensusCapability.prototype.simple = originalConsensusSimple; - NodeActionCapability.prototype.performAction = - originalNodeActionPerformAction; -}); - -describe("test runtime", () => { - describe("test call capability", () => { - test("allows awaiting multiple capability results in different order than calls", () => { - const anyResult1 = "ok1"; - const anyResult2 = "ok2"; - var expectedCall = 1; - var expectedAwait = 2; - - const input1 = create(InputsSchema, { inputThing: true }); - const input2 = create(InputSchema, { name: "input" }); + ConsensusCapability.prototype.simple = originalConsensusSimple + NodeActionCapability.prototype.performAction = originalNodeActionPerformAction +}) + +describe('test runtime', () => { + describe('test call capability', () => { + test('allows awaiting multiple capability results in different order than calls', () => { + const anyResult1 = 'ok1' + const anyResult2 = 'ok2' + var expectedCall = 1 + var expectedAwait = 2 + + const input1 = create(InputsSchema, { inputThing: true }) + const input2 = create(InputSchema, { name: 'input' }) const helpers = createRuntimeHelpersMock({ call: mock((request: CapabilityRequest) => { switch (request.callbackId) { case 1: - expect(expectedCall).toEqual(1); - expectedCall++; - expect(request.id).toEqual(BasicActionCapability.CAPABILITY_ID); - expect(request.method).toEqual("PerformAction"); - expect(anyUnpack(request.payload as Any, InputsSchema)).toEqual( - input1, - ); - return true; + expect(expectedCall).toEqual(1) + expectedCall++ + expect(request.id).toEqual(BasicActionCapability.CAPABILITY_ID) + expect(request.method).toEqual('PerformAction') + expect(anyUnpack(request.payload as Any, InputsSchema)).toEqual(input1) + return true case 2: - expect(expectedCall).toEqual(2); - expectedCall++; - expect(request.id).toEqual(BasicCapability.CAPABILITY_ID); - expect(request.method).toEqual("Action"); - expect(anyUnpack(request.payload as Any, InputSchema)).toEqual( - input2, - ); - return true; + expect(expectedCall).toEqual(2) + expectedCall++ + expect(request.id).toEqual(BasicCapability.CAPABILITY_ID) + expect(request.method).toEqual('Action') + expect(anyUnpack(request.payload as Any, InputSchema)).toEqual(input2) + return true default: - throw new Error( - `Unexpected call with callbackId: ${request.callbackId}`, - ); + throw new Error(`Unexpected call with callbackId: ${request.callbackId}`) } }), await: mock((request: AwaitCapabilitiesRequest) => { - expect(request.ids.length).toEqual(1); - var payload: Any; - const id = request.ids[0]; + expect(request.ids.length).toEqual(1) + var payload: Any + const id = request.ids[0] switch (id) { case 1: - expect(1).toEqual(expectedAwait); - expectedAwait--; - payload = anyPack( - OutputsSchema, - create(OutputsSchema, { adaptedThing: anyResult1 }), - ); - break; + expect(1).toEqual(expectedAwait) + expectedAwait-- + payload = anyPack(OutputsSchema, create(OutputsSchema, { adaptedThing: anyResult1 })) + break case 2: - expect(2).toEqual(expectedAwait); - expectedAwait--; - payload = anyPack( - OutputSchema, - create(OutputSchema, { welcome: anyResult2 }), - ); - break; + expect(2).toEqual(expectedAwait) + expectedAwait-- + payload = anyPack(OutputSchema, create(OutputSchema, { welcome: anyResult2 })) + break default: - throw new Error(`Unexpected await with id: ${request.ids[0]}`); + throw new Error(`Unexpected await with id: ${request.ids[0]}`) } return create(AwaitCapabilitiesResponseSchema, { responses: { [id]: create(CapabilityResponseSchema, { - response: { case: "payload", value: payload }, + response: { case: 'payload', value: payload }, }), }, - }); + }) }), - }); - - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const workflowAction1 = new BasicActionCapability(); - const call1 = workflowAction1.performAction(runtime, input1); - const workflowAction2 = new BasicCapability(); - const call2 = workflowAction2.action(runtime, input2); - const result2 = call2.result(); - expect(result2.welcome).toEqual(anyResult2); - const result1 = call1.result(); - expect(result1.adaptedThing).toEqual(anyResult1); - }); - - test("call capability errors", () => { + }) + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const workflowAction1 = new BasicActionCapability() + const call1 = workflowAction1.performAction(runtime, input1) + const workflowAction2 = new BasicCapability() + const call2 = workflowAction2.action(runtime, input2) + const result2 = call2.result() + expect(result2.welcome).toEqual(anyResult2) + const result1 = call1.result() + expect(result1.adaptedThing).toEqual(anyResult1) + }) + + test('call capability errors', () => { const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => { - return false; + return false }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const workflowAction1 = new BasicActionCapability(); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const workflowAction1 = new BasicActionCapability() const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ); + ) expect(() => call1.result()).toThrow( new CapabilityError( @@ -209,36 +188,36 @@ describe("test runtime", () => { { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: "PerformAction", + method: 'PerformAction', }, ), - ); - }); + ) + }) - test("capability errors are returned to the caller", () => { - const anyError = "error"; + test('capability errors are returned to the caller', () => { + const anyError = 'error' const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => { - return true; + return true }), await: mock((request: AwaitCapabilitiesRequest) => { - expect(request.ids.length).toEqual(1); + expect(request.ids.length).toEqual(1) return create(AwaitCapabilitiesResponseSchema, { responses: { [request.ids[0]]: create(CapabilityResponseSchema, { - response: { case: "error", value: anyError }, + response: { case: 'error', value: anyError }, }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const workflowAction1 = new BasicActionCapability(); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const workflowAction1 = new BasicActionCapability() const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ); + ) expect(() => call1.result()).toThrow( new CapabilityError( @@ -246,55 +225,55 @@ describe("test runtime", () => { { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: "PerformAction", + method: 'PerformAction', }, ), - ); - }); + ) + }) - test("await errors", () => { - const anyError = "error"; + test('await errors', () => { + const anyError = 'error' const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => { - return true; + return true }), await: mock((_: AwaitCapabilitiesRequest) => { - throw new Error(anyError); + throw new Error(anyError) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const workflowAction1 = new BasicActionCapability(); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const workflowAction1 = new BasicActionCapability() const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ); + ) expect(() => call1.result()).toThrow( new CapabilityError(anyError, { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: "PerformAction", + method: 'PerformAction', }), - ); - }); + ) + }) - test("await missing response", () => { + test('await missing response', () => { const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => { - return true; + return true }), await: mock((_: AwaitCapabilitiesRequest) => { - return create(AwaitCapabilitiesResponseSchema, { responses: {} }); + return create(AwaitCapabilitiesResponseSchema, { responses: {} }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const workflowAction1 = new BasicActionCapability(); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const workflowAction1 = new BasicActionCapability() const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ); + ) expect(() => call1.result()).toThrow( new CapabilityError( @@ -302,61 +281,58 @@ describe("test runtime", () => { { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: "PerformAction", + method: 'PerformAction', }, ), - ); - }); + ) + }) - test("await throws RESPONSE_BUFFER_TOO_SMALL when response exceeds max size", () => { + test('await throws RESPONSE_BUFFER_TOO_SMALL when response exceeds max size', () => { const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => true), await: mock((_: AwaitCapabilitiesRequest) => { - throw new Error(RESPONSE_BUFFER_TOO_SMALL); + throw new Error(RESPONSE_BUFFER_TOO_SMALL) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const workflowAction1 = new BasicActionCapability(); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const workflowAction1 = new BasicActionCapability() const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ); + ) - expect(() => call1.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL); - }); + expect(() => call1.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL) + }) - test("await returns unparsable payload throws CapabilityError", () => { + test('await returns unparsable payload throws CapabilityError', () => { // Any with correct type_url but invalid value bytes so fromBinary throws - const validAny = anyPack( - OutputsSchema, - create(OutputsSchema, { adaptedThing: "x" }), - ); + const validAny = anyPack(OutputsSchema, create(OutputsSchema, { adaptedThing: 'x' })) const corruptPayload = { typeUrl: validAny.typeUrl, value: new Uint8Array([0xff, 0xff]), - }; + } const helpers = createRuntimeHelpersMock({ call: mock((_: CapabilityRequest) => true), await: mock((request: AwaitCapabilitiesRequest) => { - const id = request.ids[0]; + const id = request.ids[0] return create(AwaitCapabilitiesResponseSchema, { responses: { [id]: create(CapabilityResponseSchema, { - response: { case: "payload", value: corruptPayload as Any }, + response: { case: 'payload', value: corruptPayload as Any }, }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const workflowAction1 = new BasicActionCapability(); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const workflowAction1 = new BasicActionCapability() const call1 = workflowAction1.performAction( runtime, create(InputsSchema, { inputThing: true }), - ); + ) expect(() => call1.result()).toThrow( new CapabilityError( @@ -364,116 +340,116 @@ describe("test runtime", () => { { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, - method: "PerformAction", + method: 'PerformAction', }, ), - ); - }); - }); -}); + ) + }) + }) +}) -describe("test now converts to date", () => { - test("now converts to date", () => { +describe('test now converts to date', () => { + test('now converts to date', () => { const helpers = createRuntimeHelpersMock({ now: mock(() => 1716153600000), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const now = runtime.now(); - expect(now).toEqual(new Date(1716153600000)); - }); -}); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const now = runtime.now() + expect(now).toEqual(new Date(1716153600000)) + }) +}) -describe("test sleep delegates to helpers", () => { - test("sleep calls helpers.sleep with the provided milliseconds", () => { - const sleepMock = mock(() => {}); +describe('test sleep delegates to helpers', () => { + test('sleep calls helpers.sleep with the provided milliseconds', () => { + const sleepMock = mock(() => {}) const helpers = createRuntimeHelpersMock({ sleep: sleepMock, - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - runtime.sleep(500); - expect(sleepMock).toHaveBeenCalledTimes(1); - expect(sleepMock).toHaveBeenCalledWith(500); - }); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + runtime.sleep(500) + expect(sleepMock).toHaveBeenCalledTimes(1) + expect(sleepMock).toHaveBeenCalledWith(500) + }) - test("sleep passes zero milliseconds to helpers.sleep", () => { - const sleepMock = mock(() => {}); + test('sleep passes zero milliseconds to helpers.sleep', () => { + const sleepMock = mock(() => {}) const helpers = createRuntimeHelpersMock({ sleep: sleepMock, - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - runtime.sleep(0); - expect(sleepMock).toHaveBeenCalledWith(0); - }); -}); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + runtime.sleep(0) + expect(sleepMock).toHaveBeenCalledWith(0) + }) +}) -describe("test getSecret", () => { - test("getSecrets returns ordered batched responses", () => { +describe('test getSecret', () => { + test('getSecrets returns ordered batched responses', () => { const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - expect(request.callbackId).toEqual(1); - expect(request.requests.length).toEqual(2); - expect(request.requests[0].id).toEqual("secret-1"); - expect(request.requests[1].id).toEqual("secret-2"); + expect(request.callbackId).toEqual(1) + expect(request.requests.length).toEqual(2) + expect(request.requests[0].id).toEqual('secret-1') + expect(request.requests[1].id).toEqual('secret-2') }), awaitSecrets: mock((request) => { - expect(request.ids.length).toEqual(1); - expect(request.ids[0]).toEqual(1); + expect(request.ids.length).toEqual(1) + expect(request.ids[0]).toEqual(1) return create(AwaitSecretsResponseSchema, { responses: { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { response: { - case: "secret", + case: 'secret', value: { - id: "secret-1", - namespace: "ns", - owner: "owner-1", - value: "value-1", + id: 'secret-1', + namespace: 'ns', + owner: 'owner-1', + value: 'value-1', }, }, }), create(SecretResponseSchema, { response: { - case: "secret", + case: 'secret', value: { - id: "secret-2", - namespace: "ns", - owner: "owner-2", - value: "value-2", + id: 'secret-2', + namespace: 'ns', + owner: 'owner-2', + value: 'value-2', }, }, }), ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) const responses = runtime .getSecrets([ - { id: "secret-1", namespace: "ns" }, - { id: "secret-2", namespace: "ns" }, + { id: 'secret-1', namespace: 'ns' }, + { id: 'secret-2', namespace: 'ns' }, ]) - .result(); + .result() - expect(responses.length).toEqual(2); - expect(responses[0].response.case).toEqual("secret"); - expect(responses[1].response.case).toEqual("secret"); - if (responses[0].response.case === "secret") { - expect(responses[0].response.value.id).toEqual("secret-1"); + expect(responses.length).toEqual(2) + expect(responses[0].response.case).toEqual('secret') + expect(responses[1].response.case).toEqual('secret') + if (responses[0].response.case === 'secret') { + expect(responses[0].response.value.id).toEqual('secret-1') } - if (responses[1].response.case === "secret") { - expect(responses[1].response.value.id).toEqual("secret-2"); + if (responses[1].response.case === 'secret') { + expect(responses[1].response.value.id).toEqual('secret-2') } - }); + }) - test("getSecrets returns mixed success and error responses without throwing", () => { + test('getSecrets returns mixed success and error responses without throwing', () => { const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), awaitSecrets: mock(() => { @@ -483,105 +459,103 @@ describe("test getSecret", () => { responses: [ create(SecretResponseSchema, { response: { - case: "secret", + case: 'secret', value: { - id: "ok-secret", - namespace: "ns", - owner: "owner", - value: "ok-value", + id: 'ok-secret', + namespace: 'ns', + owner: 'owner', + value: 'ok-value', }, }, }), create(SecretResponseSchema, { response: { - case: "error", + case: 'error', value: { - id: "missing-secret", - namespace: "ns", - owner: "owner", - error: "secret not found", + id: 'missing-secret', + namespace: 'ns', + owner: 'owner', + error: 'secret not found', }, }, }), ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) const responses = runtime .getSecrets([ - { id: "ok-secret", namespace: "ns" }, - { id: "missing-secret", namespace: "ns" }, + { id: 'ok-secret', namespace: 'ns' }, + { id: 'missing-secret', namespace: 'ns' }, ]) - .result(); + .result() - expect(responses.length).toEqual(2); - expect(responses[0].response.case).toEqual("secret"); - expect(responses[1].response.case).toEqual("error"); - }); + expect(responses.length).toEqual(2) + expect(responses[0].response.case).toEqual('secret') + expect(responses[1].response.case).toEqual('error') + }) - test("normalizes missing secret namespace to default for JSON and protobuf requests", () => { - const observedNamespaces: string[] = []; + test('normalizes missing secret namespace to default for JSON and protobuf requests', () => { + const observedNamespaces: string[] = [] const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - expect(request.requests.length).toEqual(1); - observedNamespaces.push(request.requests[0].namespace); + expect(request.requests.length).toEqual(1) + observedNamespaces.push(request.requests[0].namespace) }), awaitSecrets: mock((request) => { - const id = request.ids[0]; + const id = request.ids[0] return create(AwaitSecretsResponseSchema, { responses: { [id]: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { response: { - case: "secret", + case: 'secret', value: { - id: "secret", - namespace: "main", - owner: "owner", - value: "value", + id: 'secret', + namespace: 'main', + owner: 'owner', + value: 'value', }, }, }), ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - runtime.getSecret({ id: "json-secret" }).result(); - runtime - .getSecret(create(SecretRequestSchema, { id: "proto-secret" })) - .result(); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + runtime.getSecret({ id: 'json-secret' }).result() + runtime.getSecret(create(SecretRequestSchema, { id: 'proto-secret' })).result() - expect(observedNamespaces).toEqual(["main", "main"]); - }); + expect(observedNamespaces).toEqual(['main', 'main']) + }) - test("getSecrets throws SecretsBatchError when host getSecrets call fails", () => { + test('getSecrets throws SecretsBatchError when host getSecrets call fails', () => { const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => { - throw new Error("vault: signer unreachable"); + throw new Error('vault: signer unreachable') }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime .getSecrets([ - { id: "secret-a", namespace: "ns" }, - { id: "secret-b", namespace: "ns" }, + { id: 'secret-a', namespace: 'ns' }, + { id: 'secret-b', namespace: 'ns' }, ]) .result(), - ).toThrow(SecretsBatchError); - }); + ).toThrow(SecretsBatchError) + }) - test("getSecrets throws SecretsBatchError for malformed batched response envelope", () => { + test('getSecrets throws SecretsBatchError for malformed batched response envelope', () => { const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), awaitSecrets: mock(() => @@ -589,165 +563,165 @@ describe("test getSecret", () => { responses: {}, }), ), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime .getSecrets([ - { id: "secret-a", namespace: "ns" }, - { id: "secret-b", namespace: "ns" }, + { id: 'secret-a', namespace: 'ns' }, + { id: 'secret-b', namespace: 'ns' }, ]) .result(), - ).toThrow(SecretsBatchError); - }); + ).toThrow(SecretsBatchError) + }) - test("successfully gets secret with SecretRequest (proto message)", () => { + test('successfully gets secret with SecretRequest (proto message)', () => { const secretRequest = create(SecretRequestSchema, { - id: "my-secret", - namespace: "test-ns", - }); + id: 'my-secret', + namespace: 'test-ns', + }) const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - expect(request.callbackId).toEqual(1); - expect(request.requests.length).toEqual(1); + expect(request.callbackId).toEqual(1) + expect(request.requests.length).toEqual(1) }), awaitSecrets: mock((request) => { - expect(request.ids.length).toEqual(1); - expect(request.ids[0]).toEqual(1); + expect(request.ids.length).toEqual(1) + expect(request.ids[0]).toEqual(1) return create(AwaitSecretsResponseSchema, { responses: { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { response: { - case: "secret", + case: 'secret', value: { - id: "my-secret", - namespace: "test-ns", - owner: "test-owner", - value: "secret-value-123", + id: 'my-secret', + namespace: 'test-ns', + owner: 'test-owner', + value: 'secret-value-123', }, }, }), ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const result = runtime.getSecret(secretRequest).result(); - expect(result.id).toEqual("my-secret"); - expect(result.namespace).toEqual("test-ns"); - expect(result.value).toEqual("secret-value-123"); - }); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const result = runtime.getSecret(secretRequest).result() + expect(result.id).toEqual('my-secret') + expect(result.namespace).toEqual('test-ns') + expect(result.value).toEqual('secret-value-123') + }) - test("successfully gets secret with SecretRequestJson (plain JSON)", () => { - const secretRequestJson = { id: "another-secret", namespace: "another-ns" }; + test('successfully gets secret with SecretRequestJson (plain JSON)', () => { + const secretRequestJson = { id: 'another-secret', namespace: 'another-ns' } const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - expect(request.callbackId).toEqual(1); - expect(request.requests.length).toEqual(1); + expect(request.callbackId).toEqual(1) + expect(request.requests.length).toEqual(1) }), awaitSecrets: mock((request) => { - expect(request.ids.length).toEqual(1); - expect(request.ids[0]).toEqual(1); + expect(request.ids.length).toEqual(1) + expect(request.ids[0]).toEqual(1) return create(AwaitSecretsResponseSchema, { responses: { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { response: { - case: "secret", + case: 'secret', value: { - id: "another-secret", - namespace: "another-ns", - owner: "another-owner", - value: "value-456", + id: 'another-secret', + namespace: 'another-ns', + owner: 'another-owner', + value: 'value-456', }, }, }), ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - const result = runtime.getSecret(secretRequestJson).result(); - expect(result.id).toEqual("another-secret"); - expect(result.namespace).toEqual("another-ns"); - expect(result.value).toEqual("value-456"); - }); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const result = runtime.getSecret(secretRequestJson).result() + expect(result.id).toEqual('another-secret') + expect(result.namespace).toEqual('another-ns') + expect(result.value).toEqual('value-456') + }) - test("getSecrets throws → wrapped as SecretsError", () => { + test('getSecrets throws → wrapped as SecretsError', () => { const secretRequest = create(SecretRequestSchema, { - id: "test-secret", - namespace: "test-ns", - }); + id: 'test-secret', + namespace: 'test-ns', + }) const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => { - throw new Error("vault: signer unreachable"); + throw new Error('vault: signer unreachable') }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, "vault: signer unreachable"), - ); - }); + new SecretsError(secretRequest, 'vault: signer unreachable'), + ) + }) - test("awaitSecrets throws → wrapped as SecretsError", () => { + test('awaitSecrets throws → wrapped as SecretsError', () => { const secretRequest = create(SecretRequestSchema, { - id: "test-secret", - namespace: "test-ns", - }); + id: 'test-secret', + namespace: 'test-ns', + }) const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), awaitSecrets: mock(() => { - throw new Error("vault: timeout fetching secret"); + throw new Error('vault: timeout fetching secret') }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, "vault: timeout fetching secret"), - ); - }); + new SecretsError(secretRequest, 'vault: timeout fetching secret'), + ) + }) - test("awaitSecrets returns no response for callback ID", () => { + test('awaitSecrets returns no response for callback ID', () => { const secretRequest = create(SecretRequestSchema, { - id: "test-secret", - namespace: "test-ns", - }); + id: 'test-secret', + namespace: 'test-ns', + }) const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), awaitSecrets: mock(() => { return create(AwaitSecretsResponseSchema, { responses: {}, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, "no response"), - ); - }); + new SecretsError(secretRequest, 'no response'), + ) + }) - test("awaitSecrets returns invalid number of responses", () => { + test('awaitSecrets returns invalid number of responses', () => { const secretRequest = create(SecretRequestSchema, { - id: "test-secret", - namespace: "test-ns", - }); + id: 'test-secret', + namespace: 'test-ns', + }) const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), @@ -758,27 +732,27 @@ describe("test getSecret", () => { responses: [], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, "invalid value returned from host"), - ); - }); + new SecretsError(secretRequest, 'invalid value returned from host'), + ) + }) - test("awaitSecrets returns too many responses", () => { + test('awaitSecrets returns too many responses', () => { const secretRequest = create(SecretRequestSchema, { - id: "test-secret", - namespace: "test-ns", - }); + id: 'test-secret', + namespace: 'test-ns', + }) const secretValue = { - id: "secret1", - namespace: "test-ns", - owner: "test-owner", - value: "value1", - }; + id: 'secret1', + namespace: 'test-ns', + owner: 'test-owner', + value: 'value1', + } const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), @@ -788,30 +762,30 @@ describe("test getSecret", () => { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { - response: { case: "secret", value: secretValue }, + response: { case: 'secret', value: secretValue }, }), create(SecretResponseSchema, { - response: { case: "secret", value: secretValue }, + response: { case: 'secret', value: secretValue }, }), ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, "invalid value returned from host"), - ); - }); + new SecretsError(secretRequest, 'invalid value returned from host'), + ) + }) - test("awaitSecrets returns error response", () => { + test('awaitSecrets returns error response', () => { const secretRequest = create(SecretRequestSchema, { - id: "test-secret", - namespace: "test-ns", - }); - const errorMessage = "secret not found"; + id: 'test-secret', + namespace: 'test-ns', + }) + const errorMessage = 'secret not found' const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), @@ -821,26 +795,26 @@ describe("test getSecret", () => { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { - response: { case: "error", value: { error: errorMessage } }, + response: { case: 'error', value: { error: errorMessage } }, }), ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime.getSecret(secretRequest).result()).toThrow( new SecretsError(secretRequest, errorMessage), - ); - }); + ) + }) - test("awaitSecrets returns unknown response case", () => { + test('awaitSecrets returns unknown response case', () => { const secretRequest = create(SecretRequestSchema, { - id: "test-secret", - namespace: "test-ns", - }); + id: 'test-secret', + namespace: 'test-ns', + }) const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), @@ -855,582 +829,504 @@ describe("test getSecret", () => { ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError( - secretRequest, - "cannot unmarshal returned value from host", - ), - ); - }); + new SecretsError(secretRequest, 'cannot unmarshal returned value from host'), + ) + }) - test("getSecret increments callback ID correctly", () => { - const callbackIds: number[] = []; + test('getSecret increments callback ID correctly', () => { + const callbackIds: number[] = [] const secretValue = { - id: "secret", - namespace: "test-ns", - owner: "test-owner", - value: "value", - }; + id: 'secret', + namespace: 'test-ns', + owner: 'test-owner', + value: 'value', + } const helpers = createRuntimeHelpersMock({ getSecrets: mock((request) => { - callbackIds.push(request.callbackId); + callbackIds.push(request.callbackId) }), awaitSecrets: mock((request) => { - const id = request.ids[0]; + const id = request.ids[0] return create(AwaitSecretsResponseSchema, { responses: { [id]: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { - response: { case: "secret", value: secretValue }, + response: { case: 'secret', value: secretValue }, }), ], }), }, - }); + }) }), - }); + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - runtime.getSecret({ id: "secret1", namespace: "ns1" }).result(); - runtime.getSecret({ id: "secret2", namespace: "ns2" }).result(); - runtime.getSecret({ id: "secret3", namespace: "ns3" }).result(); + runtime.getSecret({ id: 'secret1', namespace: 'ns1' }).result() + runtime.getSecret({ id: 'secret2', namespace: 'ns2' }).result() + runtime.getSecret({ id: 'secret3', namespace: 'ns3' }).result() - expect(callbackIds).toEqual([1, 2, 3]); - }); + expect(callbackIds).toEqual([1, 2, 3]) + }) - test("getSecret in node mode throws DonModeError", () => { - const helpers = createRuntimeHelpersMock(); + test('getSecret in node mode throws DonModeError', () => { + const helpers = createRuntimeHelpersMock() - (ConsensusCapability.prototype as any).simple = mock(() => { - return { result: () => Value.from(0).proto() }; - }); + ;(ConsensusCapability.prototype as any).simple = mock(() => { + return { result: () => Value.from(0).proto() } + }) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - let capturedError: Error | undefined; + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + let capturedError: Error | undefined runtime.runInNodeMode((_nodeRuntime: NodeRuntime) => { // Try to call getSecret from within node mode (should fail) try { - runtime.getSecret({ id: "test", namespace: "test-ns" }).result(); + runtime.getSecret({ id: 'test', namespace: 'test-ns' }).result() } catch (e) { - capturedError = e as Error; + capturedError = e as Error } - return 0; - }, consensusMedianAggregation())(); - - expect(capturedError).toBeDefined(); - expect(capturedError).toBeInstanceOf(DonModeError); - }); -}); - -describe("test run in node mode", () => { - test("successful consensus", () => { - const anyObservation = 10; - const anyMedian = 11; - const modes: Mode[] = []; + return 0 + }, consensusMedianAggregation())() + + expect(capturedError).toBeDefined() + expect(capturedError).toBeInstanceOf(DonModeError) + }) +}) + +describe('test run in node mode', () => { + test('successful consensus', () => { + const anyObservation = 10 + const anyMedian = 11 + const modes: Mode[] = [] const helpers = createRuntimeHelpersMock({ switchModes: mock((mode: Mode) => { - modes.push(mode); + modes.push(mode) }), - }); - - (ConsensusCapability.prototype as any).simple = mock( - ( - _: Runtime, - inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, - ) => { - expect(modes).toEqual([Mode.DON, Mode.NODE, Mode.DON]); - expect(inputs.default).toBeUndefined(); + }) + + ;(ConsensusCapability.prototype as any).simple = mock( + (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { + expect(modes).toEqual([Mode.DON, Mode.NODE, Mode.DON]) + expect(inputs.default).toBeUndefined() const consensusDescriptor = create(ConsensusDescriptorSchema, { descriptor: { - case: "fieldsMap", + case: 'fieldsMap', value: create(FieldsMapSchema, { fields: { outputThing: create(ConsensusDescriptorSchema, { descriptor: { - case: "aggregation", + case: 'aggregation', value: AggregationType.MEDIAN, }, }), }, }), }, - }); - expect(inputs.descriptors).toEqual(consensusDescriptor); - expect( - (inputs as { $typeName?: string }).$typeName, - ).not.toBeUndefined(); - const inputsProto = inputs as SimpleConsensusInputs; - expect(inputsProto.observation.case).toEqual("value"); + }) + expect(inputs.descriptors).toEqual(consensusDescriptor) + expect((inputs as { $typeName?: string }).$typeName).not.toBeUndefined() + const inputsProto = inputs as SimpleConsensusInputs + expect(inputsProto.observation.case).toEqual('value') expect( Value.wrap(inputsProto.observation.value as ProtoValue).unwrapToType({ factory: () => create(NodeOutputsSchema), }).outputThing, - ).toEqual(anyObservation); + ).toEqual(anyObservation) return { - result: () => - Value.from( - create(NodeOutputsSchema, { outputThing: anyMedian }), - ).proto(), - }; + result: () => Value.from(create(NodeOutputsSchema, { outputThing: anyMedian })).proto(), + } }, - ); + ) // Create a mock that handles both overloads properly - const performActionMock = function ( - this: NodeActionCapability, - ...args: unknown[] - ): unknown { + const performActionMock = function (this: NodeActionCapability, ...args: unknown[]): unknown { // Check if this is the sugar syntax overload (has function parameter) - if (typeof args[0] === "function") { + if (typeof args[0] === 'function') { // This test doesn't expect sugar syntax to be used - throw new Error("Sugar syntax should not be used in this test"); + throw new Error('Sugar syntax should not be used in this test') } // Otherwise, this is the basic call overload - const [_, __] = args as [ - NodeRuntime, - NodeInputs | NodeInputsJson, - ]; - expect(modes).toEqual([Mode.DON, Mode.NODE]); + const [_, __] = args as [NodeRuntime, NodeInputs | NodeInputsJson] + expect(modes).toEqual([Mode.DON, Mode.NODE]) return { - result: () => - create(NodeOutputsSchema, { outputThing: anyObservation }), - }; - }; + result: () => create(NodeOutputsSchema, { outputThing: anyObservation }), + } + } // Apply the mock with proper typing // biome-ignore lint/suspicious/noExplicitAny: Mock assignment requires any due to overloaded function signature - (NodeActionCapability.prototype as any).performAction = - mock(performActionMock); + ;(NodeActionCapability.prototype as any).performAction = mock(performActionMock) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) const result = runtime .runInNodeMode( (nodeRuntime: NodeRuntime) => { - const capability = new NodeActionCapability(); + const capability = new NodeActionCapability() return capability - .performAction( - nodeRuntime, - create(NodeInputsSchema, { inputThing: true }), - ) - .result(); + .performAction(nodeRuntime, create(NodeInputsSchema, { inputThing: true })) + .result() }, ConsensusAggregationByFields({ outputThing: median }), )() - .result(); + .result() - expect(result.outputThing).toEqual(anyMedian); - }); + expect(result.outputThing).toEqual(anyMedian) + }) - test("failed consensus", () => { - const anyError = "error"; + test('failed consensus', () => { + const anyError = 'error' const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }); - - (ConsensusCapability.prototype as any).simple = mock( - ( - _: Runtime, - inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, - ) => { - expect(inputs.default).toBeUndefined(); + }) + + ;(ConsensusCapability.prototype as any).simple = mock( + (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { + expect(inputs.default).toBeUndefined() expect(inputs.descriptors).toEqual( create(ConsensusDescriptorSchema, { - descriptor: { case: "aggregation", value: AggregationType.MEDIAN }, + descriptor: { case: 'aggregation', value: AggregationType.MEDIAN }, }), - ); - expect( - (inputs as { $typeName?: string }).$typeName, - ).not.toBeUndefined(); - const inputsProto = inputs as SimpleConsensusInputs; - expect(inputsProto.observation.case).toEqual("error"); - expect(inputsProto.observation.value).toEqual(anyError); + ) + expect((inputs as { $typeName?: string }).$typeName).not.toBeUndefined() + const inputsProto = inputs as SimpleConsensusInputs + expect(inputsProto.observation.case).toEqual('error') + expect(inputsProto.observation.value).toEqual(anyError) return { result: () => { - throw new Error(anyError); + throw new Error(anyError) }, - }; + } }, - ); + ) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) const result = runtime.runInNodeMode((_: NodeRuntime) => { - throw new Error(anyError); - }, consensusMedianAggregation())(); - expect(() => result.result()).toThrow(new Error(anyError)); - }); - - test("primitive consensus with unused default returns observation value", () => { - const observationValue = 99; - const defaultValue = 100; + throw new Error(anyError) + }, consensusMedianAggregation())() + expect(() => result.result()).toThrow(new Error(anyError)) + }) + + test('primitive consensus with unused default returns observation value', () => { + const observationValue = 99 + const defaultValue = 100 const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }); - - (ConsensusCapability.prototype as any).simple = mock( - ( - _: Runtime, - inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, - ) => { - const inputsProto = inputs as SimpleConsensusInputs; - expect(inputsProto.observation.case).toEqual("value"); - expect( - Value.wrap(inputsProto.observation.value as ProtoValue).unwrap(), - ).toEqual(observationValue); - expect(inputsProto.default).toBeDefined(); - expect(Value.wrap(inputsProto.default as ProtoValue).unwrap()).toEqual( - defaultValue, - ); + }) + + ;(ConsensusCapability.prototype as any).simple = mock( + (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { + const inputsProto = inputs as SimpleConsensusInputs + expect(inputsProto.observation.case).toEqual('value') + expect(Value.wrap(inputsProto.observation.value as ProtoValue).unwrap()).toEqual( + observationValue, + ) + expect(inputsProto.default).toBeDefined() + expect(Value.wrap(inputsProto.default as ProtoValue).unwrap()).toEqual(defaultValue) return { result: () => Value.from(observationValue).proto(), - }; + } }, - ); + ) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) const result = runtime .runInNodeMode( (_: NodeRuntime) => observationValue, consensusMedianAggregation().withDefault(defaultValue), )() - .result(); + .result() - expect(result).toEqual(observationValue); - }); + expect(result).toEqual(observationValue) + }) - test("primitive consensus with used default returns default when function errors", () => { - const defaultVal = 100; - const anyError = "error"; + test('primitive consensus with used default returns default when function errors', () => { + const defaultVal = 100 + const anyError = 'error' const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }); - - (ConsensusCapability.prototype as any).simple = mock( - ( - _: Runtime, - inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, - ) => { - const inputsProto = inputs as SimpleConsensusInputs; - expect(inputsProto.observation.case).toEqual("error"); - expect(inputsProto.observation.value).toEqual(anyError); - expect(inputsProto.default).toBeDefined(); - expect(Value.wrap(inputsProto.default as ProtoValue).unwrap()).toEqual( - defaultVal, - ); + }) + + ;(ConsensusCapability.prototype as any).simple = mock( + (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { + const inputsProto = inputs as SimpleConsensusInputs + expect(inputsProto.observation.case).toEqual('error') + expect(inputsProto.observation.value).toEqual(anyError) + expect(inputsProto.default).toBeDefined() + expect(Value.wrap(inputsProto.default as ProtoValue).unwrap()).toEqual(defaultVal) return { result: () => Value.from(defaultVal).proto(), - }; + } }, - ); + ) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) const result = runtime .runInNodeMode((_: NodeRuntime) => { - throw new Error(anyError); + throw new Error(anyError) }, consensusMedianAggregation().withDefault(defaultVal))() - .result(); + .result() - expect(result).toEqual(defaultVal); - }); + expect(result).toEqual(defaultVal) + }) - test("node runtime in don mode fails", () => { + test('node runtime in don mode fails', () => { const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), call: mock((_: CapabilityRequest) => { - expect(false).toBe(true); - return false; + expect(false).toBe(true) + return false }), - }); - - (ConsensusCapability.prototype as any).simple = mock( - ( - _: Runtime, - __: SimpleConsensusInputs | SimpleConsensusInputsJson, - ) => { - return { result: () => Value.from(0).proto() }; + }) + + ;(ConsensusCapability.prototype as any).simple = mock( + (_: Runtime, __: SimpleConsensusInputs | SimpleConsensusInputsJson) => { + return { result: () => Value.from(0).proto() } }, - ); + ) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); - var nrt: NodeRuntime | undefined; + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + var nrt: NodeRuntime | undefined runtime.runInNodeMode((nodeRuntime: NodeRuntime) => { - nrt = nodeRuntime; - return 0; - }, consensusMedianAggregation())(); + nrt = nodeRuntime + return 0 + }, consensusMedianAggregation())() - const capability = new NodeActionCapability(); - expect(nrt).toBeDefined(); + const capability = new NodeActionCapability() + expect(nrt).toBeDefined() expect(() => capability - .performAction( - nrt as NodeRuntime, - create(NodeInputsSchema, { inputThing: true }), - ) + .performAction(nrt as NodeRuntime, create(NodeInputsSchema, { inputThing: true })) .result(), - ).toThrow(new NodeModeError()); - }); + ).toThrow(new NodeModeError()) + }) - test("don runtime in node mode fails", () => { + test('don runtime in node mode fails', () => { const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }); - - (ConsensusCapability.prototype as any).simple = mock( - ( - _: Runtime, - inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, - ) => { - expect(inputs.default).toBeUndefined(); + }) + + ;(ConsensusCapability.prototype as any).simple = mock( + (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { + expect(inputs.default).toBeUndefined() expect(inputs.descriptors).toEqual( create(ConsensusDescriptorSchema, { - descriptor: { case: "aggregation", value: AggregationType.MEDIAN }, + descriptor: { case: 'aggregation', value: AggregationType.MEDIAN }, }), - ); - expect( - (inputs as { $typeName?: string }).$typeName, - ).not.toBeUndefined(); - const inputsProto = inputs as SimpleConsensusInputs; - expect(inputsProto.observation.case).toEqual("error"); - expect(inputsProto.observation.value).toEqual( - new DonModeError().message, - ); + ) + expect((inputs as { $typeName?: string }).$typeName).not.toBeUndefined() + const inputsProto = inputs as SimpleConsensusInputs + expect(inputsProto.observation.case).toEqual('error') + expect(inputsProto.observation.value).toEqual(new DonModeError().message) return { result: () => { - throw new DonModeError(); + throw new DonModeError() }, - }; + } }, - ); + ) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) const result = runtime.runInNodeMode((_: NodeRuntime) => { - const capability = new BasicActionCapability(); - capability - .performAction(runtime, create(InputsSchema, { inputThing: true })) - .result(); - return 0; - }, consensusMedianAggregation())(); - expect(() => result.result()).toThrow(new DonModeError()); - }); - - test("multiple runInNodeMode calls have unique callback IDs", () => { - const callbackIds: number[] = []; + const capability = new BasicActionCapability() + capability.performAction(runtime, create(InputsSchema, { inputThing: true })).result() + return 0 + }, consensusMedianAggregation())() + expect(() => result.result()).toThrow(new DonModeError()) + }) + + test('multiple runInNodeMode calls have unique callback IDs', () => { + const callbackIds: number[] = [] const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), call: mock((request: CapabilityRequest) => { - callbackIds.push(request.callbackId); - return true; + callbackIds.push(request.callbackId) + return true }), await: mock((request: AwaitCapabilitiesRequest) => { - const id = request.ids[0]; + const id = request.ids[0] return create(AwaitCapabilitiesResponseSchema, { responses: { [id]: create(CapabilityResponseSchema, { response: { - case: "payload", - value: anyPack( - NodeOutputsSchema, - create(NodeOutputsSchema, { outputThing: 42 }), - ), + case: 'payload', + value: anyPack(NodeOutputsSchema, create(NodeOutputsSchema, { outputThing: 42 })), }, }), }, - }); + }) }), - }); + }) - (ConsensusCapability.prototype as any).simple = mock( - ( - _: Runtime, - __: SimpleConsensusInputs | SimpleConsensusInputsJson, - ) => { + ;(ConsensusCapability.prototype as any).simple = mock( + (_: Runtime, __: SimpleConsensusInputs | SimpleConsensusInputsJson) => { return { - result: () => - Value.from(create(NodeOutputsSchema, { outputThing: 42 })).proto(), - }; + result: () => Value.from(create(NodeOutputsSchema, { outputThing: 42 })).proto(), + } }, - ); + ) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) // First runInNodeMode call with capability inside const call1 = runtime.runInNodeMode( (nodeRuntime: NodeRuntime) => { - const capability = new NodeActionCapability(); + const capability = new NodeActionCapability() return capability - .performAction( - nodeRuntime, - create(NodeInputsSchema, { inputThing: true }), - ) - .result(); + .performAction(nodeRuntime, create(NodeInputsSchema, { inputThing: true })) + .result() }, ConsensusAggregationByFields({ outputThing: median }), - ); + ) - call1().result(); + call1().result() // Second runInNodeMode call with capability inside const call2 = runtime.runInNodeMode( (nodeRuntime: NodeRuntime) => { - const capability = new NodeActionCapability(); + const capability = new NodeActionCapability() return capability - .performAction( - nodeRuntime, - create(NodeInputsSchema, { inputThing: false }), - ) - .result(); + .performAction(nodeRuntime, create(NodeInputsSchema, { inputThing: false })) + .result() }, ConsensusAggregationByFields({ outputThing: median }), - ); + ) - call2().result(); + call2().result() // Verify that we have two distinct callback IDs - expect(callbackIds.length).toEqual(2); - expect(callbackIds[0]).toEqual(-1); // First node mode call - expect(callbackIds[1]).toEqual(-2); // Second node mode call + expect(callbackIds.length).toEqual(2) + expect(callbackIds[0]).toEqual(-1) // First node mode call + expect(callbackIds[1]).toEqual(-2) // Second node mode call // Ensure they are different (no reuse/collision) - expect(callbackIds[0]).not.toEqual(callbackIds[1]); - }); + expect(callbackIds[0]).not.toEqual(callbackIds[1]) + }) - test("clears ignored fields from default and response values", () => { + test('clears ignored fields from default and response values', () => { type NestedStruct = { - nestedIncluded: string; - nestedIgnored: string; - }; + nestedIncluded: string + nestedIgnored: string + } type TestStruct = { - includedField: string; - ignoredField: string; - nested: NestedStruct; - }; + includedField: string + ignoredField: string + nested: NestedStruct + } const defaultVal: TestStruct = { - includedField: "default_included", - ignoredField: "default_ignored", + includedField: 'default_included', + ignoredField: 'default_ignored', nested: { - nestedIncluded: "default_nested_included", - nestedIgnored: "default_nested_ignored", + nestedIncluded: 'default_nested_included', + nestedIgnored: 'default_nested_ignored', }, - }; + } const responseVal: TestStruct = { - includedField: "response_included", - ignoredField: "response_ignored", + includedField: 'response_included', + ignoredField: 'response_ignored', nested: { - nestedIncluded: "response_nested_included", - nestedIgnored: "response_nested_ignored", + nestedIncluded: 'response_nested_included', + nestedIgnored: 'response_nested_ignored', }, - }; + } const helpers = createRuntimeHelpersMock({ switchModes: mock((_: Mode) => {}), - }); - - (ConsensusCapability.prototype as any).simple = mock( - ( - _: Runtime, - inputs: SimpleConsensusInputs | SimpleConsensusInputsJson, - ) => { - const inputsProto = inputs as SimpleConsensusInputs; - if (inputsProto.observation.case === "value") { + }) + + ;(ConsensusCapability.prototype as any).simple = mock( + (_: Runtime, inputs: SimpleConsensusInputs | SimpleConsensusInputsJson) => { + const inputsProto = inputs as SimpleConsensusInputs + if (inputsProto.observation.case === 'value') { const unwrapped = Value.wrap( inputsProto.observation.value as ProtoValue, - ).unwrap() as TestStruct; - expect(unwrapped.includedField).toEqual("response_included"); - expect(unwrapped.ignoredField).toBeUndefined(); - expect(unwrapped.nested.nestedIncluded).toEqual( - "response_nested_included", - ); - expect(unwrapped.nested.nestedIgnored).toBeUndefined(); + ).unwrap() as TestStruct + expect(unwrapped.includedField).toEqual('response_included') + expect(unwrapped.ignoredField).toBeUndefined() + expect(unwrapped.nested.nestedIncluded).toEqual('response_nested_included') + expect(unwrapped.nested.nestedIgnored).toBeUndefined() return { result: () => inputsProto.observation.value as ProtoValue, - }; + } } if (inputsProto.default) { - const unwrapped = Value.wrap( - inputsProto.default as ProtoValue, - ).unwrap() as TestStruct; - expect(unwrapped.includedField).toEqual("default_included"); - expect(unwrapped.ignoredField).toBeUndefined(); - expect(unwrapped.nested.nestedIncluded).toEqual( - "default_nested_included", - ); - expect(unwrapped.nested.nestedIgnored).toBeUndefined(); + const unwrapped = Value.wrap(inputsProto.default as ProtoValue).unwrap() as TestStruct + expect(unwrapped.includedField).toEqual('default_included') + expect(unwrapped.ignoredField).toBeUndefined() + expect(unwrapped.nested.nestedIncluded).toEqual('default_nested_included') + expect(unwrapped.nested.nestedIgnored).toBeUndefined() return { result: () => inputsProto.default as ProtoValue, - }; + } } - if (inputsProto.observation.case === "error") { + if (inputsProto.observation.case === 'error') { return { result: () => { - throw new Error(inputsProto.observation.value as string); + throw new Error(inputsProto.observation.value as string) }, - }; + } } return { result: () => { - throw new Error("unexpected case"); + throw new Error('unexpected case') }, - }; + } }, - ); + ) - const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize); + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) const nestedAggregation = ConsensusAggregationByFields({ nestedIncluded: identical, nestedIgnored: ignore, - }); + }) const result = runtime .runInNodeMode( (_nodeRuntime: NodeRuntime) => { - return responseVal; + return responseVal }, ConsensusAggregationByFields({ includedField: identical, ignoredField: ignore, nested: () => - new ConsensusFieldAggregation( - nestedAggregation.descriptor, - ), + new ConsensusFieldAggregation(nestedAggregation.descriptor), }).withDefault(defaultVal), )() - .result(); + .result() - expect(result.includedField).toEqual("response_included"); - expect(result.ignoredField).toBeUndefined(); - expect(result.nested.nestedIncluded).toEqual("response_nested_included"); - expect(result.nested.nestedIgnored).toBeUndefined(); + expect(result.includedField).toEqual('response_included') + expect(result.ignoredField).toBeUndefined() + expect(result.nested.nestedIncluded).toEqual('response_nested_included') + expect(result.nested.nestedIgnored).toBeUndefined() const result2 = runtime .runInNodeMode( (_nodeRuntime: NodeRuntime) => { - throw new Error("error"); + throw new Error('error') }, ConsensusAggregationByFields({ includedField: identical, ignoredField: ignore, nested: () => - new ConsensusFieldAggregation( - nestedAggregation.descriptor, - ), + new ConsensusFieldAggregation(nestedAggregation.descriptor), }).withDefault(defaultVal), )() - .result(); - - expect(result2.includedField).toEqual("default_included"); - expect(result2.ignoredField).toBeUndefined(); - expect(result2.nested.nestedIncluded).toEqual("default_nested_included"); - expect(result2.nested.nestedIgnored).toBeUndefined(); - }); -}); + .result() + + expect(result2.includedField).toEqual('default_included') + expect(result2.ignoredField).toBeUndefined() + expect(result2.nested.nestedIncluded).toEqual('default_nested_included') + expect(result2.nested.nestedIgnored).toBeUndefined() + }) +}) diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.ts index c0477344..e4e048eb 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.ts @@ -1,6 +1,6 @@ -import { create, type Message } from "@bufbuild/protobuf"; -import type { GenMessage } from "@bufbuild/protobuf/codegenv2"; -import { type Any, anyPack, anyUnpack } from "@bufbuild/protobuf/wkt"; +import { create, type Message } from '@bufbuild/protobuf' +import type { GenMessage } from '@bufbuild/protobuf/codegenv2' +import { type Any, anyPack, anyUnpack } from '@bufbuild/protobuf/wkt' import { type AwaitCapabilitiesRequest, AwaitCapabilitiesRequestSchema, @@ -20,9 +20,9 @@ import { SecretRequestSchema, type SecretResponse, SimpleConsensusInputsSchema, -} from "@cre/generated/sdk/v1alpha/sdk_pb"; -import type { Value as ProtoValue } from "@cre/generated/values/v1/values_pb"; -import { ConsensusCapability } from "@cre/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen"; +} from '@cre/generated/sdk/v1alpha/sdk_pb' +import type { Value as ProtoValue } from '@cre/generated/values/v1/values_pb' +import { ConsensusCapability } from '@cre/generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen' import type { BaseRuntime, CallCapabilityParams, @@ -30,22 +30,17 @@ import type { ReportRequest, ReportRequestJson, Runtime, -} from "@cre/sdk"; -import type { Report } from "@cre/sdk/report"; +} from '@cre/sdk' +import type { Report } from '@cre/sdk/report' import { type ConsensusAggregation, type CreSerializable, type PrimitiveTypes, type UnwrapOptions, Value, -} from "@cre/sdk/utils"; -import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import { - DonModeError, - NodeModeError, - SecretsBatchError, - SecretsError, -} from "../errors"; +} from '@cre/sdk/utils' +import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' +import { DonModeError, NodeModeError, SecretsBatchError, SecretsError } from '../errors' const DEFAULT_SECRET_NAMESPACE = 'main' @@ -63,7 +58,7 @@ export class BaseRuntimeImpl implements BaseRuntime { * - Set in DON mode when code tries to use NodeRuntime * - Set in Node mode when code tries to use Runtime */ - public modeError?: Error; + public modeError?: Error constructor( public config: C, @@ -88,22 +83,22 @@ export class BaseRuntimeImpl implements BaseRuntime { if (this.modeError) { return { result: () => { - throw this.modeError; + throw this.modeError }, - }; + } } // Allocate unique callback ID for this request - const callbackId = this.allocateCallbackId(); + const callbackId = this.allocateCallbackId() // Send request to WASM host - const anyPayload = anyPack(inputSchema, payload); + const anyPayload = anyPack(inputSchema, payload) const req = create(CapabilityRequestSchema, { id: capabilityId, method, payload: anyPayload, callbackId, - }); + }) if (!this.helpers.call(req)) { return { @@ -115,21 +110,16 @@ export class BaseRuntimeImpl implements BaseRuntime { method, capabilityId, }, - ); + ) }, - }; + } } // Return lazy result - await and unwrap when .result() is called return { result: () => - this.awaitAndUnwrapCapabilityResponse( - callbackId, - capabilityId, - method, - outputSchema, - ), - }; + this.awaitAndUnwrapCapabilityResponse(callbackId, capabilityId, method, outputSchema), + } } /** @@ -137,13 +127,13 @@ export class BaseRuntimeImpl implements BaseRuntime { * DON mode increments, Node mode decrements (prevents collisions). */ private allocateCallbackId(): number { - const callbackId = this.nextCallId; + const callbackId = this.nextCallId if (this.mode === Mode.DON) { - this.nextCallId++; + this.nextCallId++ } else { - this.nextCallId--; + this.nextCallId-- } - return callbackId; + return callbackId } /** @@ -157,12 +147,9 @@ export class BaseRuntimeImpl implements BaseRuntime { ): O { const awaitRequest = create(AwaitCapabilitiesRequestSchema, { ids: [callbackId], - }); - const awaitResponse = this.helpers.await( - awaitRequest, - this.maxResponseSize, - ); - const capabilityResponse = awaitResponse.responses[callbackId]; + }) + const awaitResponse = this.helpers.await(awaitRequest, this.maxResponseSize) + const capabilityResponse = awaitResponse.responses[callbackId] if (!capabilityResponse) { throw new CapabilityError( @@ -172,14 +159,14 @@ export class BaseRuntimeImpl implements BaseRuntime { method, callbackId, }, - ); + ) } - const response = capabilityResponse.response; + const response = capabilityResponse.response switch (response.case) { - case "payload": { + case 'payload': { try { - return anyUnpack(response.value as Any, outputSchema) as O; + return anyUnpack(response.value as Any, outputSchema) as O } catch { throw new CapabilityError( `Failed to deserialize response payload for capability '${capabilityId}' method '${method}': the response could not be unpacked into the expected output schema`, @@ -188,10 +175,10 @@ export class BaseRuntimeImpl implements BaseRuntime { method, callbackId, }, - ); + ) } } - case "error": + case 'error': throw new CapabilityError( `Capability '${capabilityId}' method '${method}' returned an error: ${response.value}`, { @@ -199,7 +186,7 @@ export class BaseRuntimeImpl implements BaseRuntime { method, callbackId, }, - ); + ) default: throw new CapabilityError( `Unexpected response type '${response.case}' for capability '${capabilityId}' method '${method}': expected 'payload' or 'error'`, @@ -208,17 +195,17 @@ export class BaseRuntimeImpl implements BaseRuntime { method, callbackId, }, - ); + ) } } getNextCallId(): number { - return this.nextCallId; + return this.nextCallId } now(): Date { // date is already in milliseconds - return new Date(this.helpers.now()); + return new Date(this.helpers.now()) } sleep(ms: number): void { @@ -226,7 +213,7 @@ export class BaseRuntimeImpl implements BaseRuntime { } log(message: string): void { - this.helpers.log(message); + this.helpers.log(message) } } @@ -238,20 +225,12 @@ export class BaseRuntimeImpl implements BaseRuntime { * Useful in situation where you already expect non-determinism (e.g., inherently variable HTTP responses). * Switching from Node Mode back to DON mode requires workflow authors to handle consensus themselves. */ -export class NodeRuntimeImpl - extends BaseRuntimeImpl - implements NodeRuntime -{ - _isNodeRuntime: true = true; +export class NodeRuntimeImpl extends BaseRuntimeImpl implements NodeRuntime { + _isNodeRuntime: true = true - constructor( - config: C, - nextCallId: number, - helpers: RuntimeHelpers, - maxResponseSize: bigint, - ) { - helpers.switchModes(Mode.NODE); - super(config, nextCallId, helpers, maxResponseSize, Mode.NODE); + constructor(config: C, nextCallId: number, helpers: RuntimeHelpers, maxResponseSize: bigint) { + helpers.switchModes(Mode.NODE) + super(config, nextCallId, helpers, maxResponseSize, Mode.NODE) } } @@ -260,16 +239,11 @@ export class NodeRuntimeImpl * You ask the network to execute something, and CRE handles the underlying complexity to ensure you get back one final, secure, and trustworthy result. */ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { - private nextNodeCallId: number = -1; + private nextNodeCallId: number = -1 - constructor( - config: C, - nextCallId: number, - helpers: RuntimeHelpers, - maxResponseSize: bigint, - ) { - helpers.switchModes(Mode.DON); - super(config, nextCallId, helpers, maxResponseSize, Mode.DON); + constructor(config: C, nextCallId: number, helpers: RuntimeHelpers, maxResponseSize: bigint) { + helpers.switchModes(Mode.DON) + super(config, nextCallId, helpers, maxResponseSize, Mode.DON) } /** @@ -286,41 +260,35 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { runInNodeMode( fn: (nodeRuntime: NodeRuntime, ...args: TArgs) => TOutput, consensusAggregation: ConsensusAggregation, - unwrapOptions?: TOutput extends PrimitiveTypes - ? never - : UnwrapOptions, + unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions, ): (...args: TArgs) => { result: () => TOutput } { return (...args: TArgs): { result: () => TOutput } => { // Step 1: Create node runtime and prevent DON operations - this.modeError = new DonModeError(); + this.modeError = new DonModeError() const nodeRuntime = new NodeRuntimeImpl( this.config, this.nextNodeCallId, this.helpers, this.maxResponseSize, - ); + ) // Step 2: Prepare consensus input with config - const consensusInput = this.prepareConsensusInput(consensusAggregation); + const consensusInput = this.prepareConsensusInput(consensusAggregation) // Step 3: Execute node function and capture result/error try { - const observation = fn(nodeRuntime, ...args); - this.captureObservation( - consensusInput, - observation, - consensusAggregation.descriptor, - ); + const observation = fn(nodeRuntime, ...args) + this.captureObservation(consensusInput, observation, consensusAggregation.descriptor) } catch (e: unknown) { - this.captureError(consensusInput, e); + this.captureError(consensusInput, e) } finally { // Step 4: Always restore DON mode - this.restoreDonMode(nodeRuntime); + this.restoreDonMode(nodeRuntime) } // Step 5: Run consensus and return lazy result - return this.runConsensusAndWrap(consensusInput, unwrapOptions); - }; + return this.runConsensusAndWrap(consensusInput, unwrapOptions) + } } private prepareConsensusInput( @@ -328,18 +296,18 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { ) { const consensusInput = create(SimpleConsensusInputsSchema, { descriptors: consensusAggregation.descriptor, - }); + }) if (consensusAggregation.defaultValue) { // Safe cast: ConsensusAggregation implies T extends CreSerializable const defaultValue = Value.from( consensusAggregation.defaultValue as CreSerializable, - ).proto(); - clearIgnoredFields(defaultValue, consensusAggregation.descriptor); - consensusInput.default = defaultValue; + ).proto() + clearIgnoredFields(defaultValue, consensusAggregation.descriptor) + consensusInput.default = defaultValue } - return consensusInput; + return consensusInput } private captureObservation( @@ -348,61 +316,57 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { descriptor: ConsensusDescriptor, ) { // Safe cast: ConsensusAggregation implies T extends CreSerializable - const observationValue = Value.from( - observation as CreSerializable, - ).proto(); - clearIgnoredFields(observationValue, descriptor); + const observationValue = Value.from(observation as CreSerializable).proto() + clearIgnoredFields(observationValue, descriptor) consensusInput.observation = { - case: "value", + case: 'value', value: observationValue, - }; + } } private captureError(consensusInput: any, e: unknown) { consensusInput.observation = { - case: "error", + case: 'error', value: (e instanceof Error && e.message) || String(e), - }; + } } private restoreDonMode(nodeRuntime: NodeRuntimeImpl) { - this.modeError = undefined; - this.nextNodeCallId = nodeRuntime.nextCallId; - nodeRuntime.modeError = new NodeModeError(); - this.helpers.switchModes(Mode.DON); + this.modeError = undefined + this.nextNodeCallId = nodeRuntime.nextCallId + nodeRuntime.modeError = new NodeModeError() + this.helpers.switchModes(Mode.DON) } private runConsensusAndWrap( consensusInput: any, - unwrapOptions?: TOutput extends PrimitiveTypes - ? never - : UnwrapOptions, + unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions, ): { result: () => TOutput } { - const consensus = new ConsensusCapability(); - const call = consensus.simple(this, consensusInput); + const consensus = new ConsensusCapability() + const call = consensus.simple(this, consensusInput) return { result: () => { - const result = call.result(); - const wrappedValue = Value.wrap(result); + const result = call.result() + const wrappedValue = Value.wrap(result) return unwrapOptions ? wrappedValue.unwrapToType(unwrapOptions) - : (wrappedValue.unwrap() as TOutput); + : (wrappedValue.unwrap() as TOutput) }, - }; + } } getSecrets(requests: Array): { - result: () => SecretResponse[]; + result: () => SecretResponse[] } { // Enforce mode restrictions if (this.modeError) { return { result: () => { - throw this.modeError; + throw this.modeError }, - }; + } } // Normalize requests (accept both protobuf and JSON formats) @@ -418,111 +382,98 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { id: request.id, namespace: request.namespace || DEFAULT_SECRET_NAMESPACE, }), - ); + ) if (normalizedRequests.length === 0) { return { result: () => [], - }; + } } // Allocate callback ID and send request - const id = this.nextCallId; - this.nextCallId++; + const id = this.nextCallId + this.nextCallId++ const secretsReq = create(GetSecretsRequestSchema, { callbackId: id, requests: normalizedRequests, - }); + }) try { - this.helpers.getSecrets(secretsReq, this.maxResponseSize); + this.helpers.getSecrets(secretsReq, this.maxResponseSize) } catch (err) { - const message = err instanceof Error ? err.message : String(err); + const message = err instanceof Error ? err.message : String(err) return { result: () => { - throw new SecretsBatchError(normalizedRequests, message); + throw new SecretsBatchError(normalizedRequests, message) }, - }; + } } // Return lazy result return { result: () => this.awaitAndUnwrapSecrets(id, normalizedRequests), - }; + } } getSecret(request: SecretRequest | SecretRequestJson): { - result: () => Secret; + result: () => Secret } { - const secretRequest = (request as unknown as { $typeName?: string }) - .$typeName + const secretRequest = (request as unknown as { $typeName?: string }).$typeName ? (request as SecretRequest) - : create(SecretRequestSchema, request); + : create(SecretRequestSchema, request) - const getSecretsCall = this.getSecrets([secretRequest]); + const getSecretsCall = this.getSecrets([secretRequest]) return { result: () => { - let responseList: SecretResponse[]; + let responseList: SecretResponse[] try { - responseList = getSecretsCall.result(); + responseList = getSecretsCall.result() } catch (err) { if (err instanceof SecretsBatchError) { - throw new SecretsError(secretRequest, err.error); + throw new SecretsError(secretRequest, err.error) } - throw err; + throw err } - return this.unwrapSingleSecretResult(responseList, secretRequest); + return this.unwrapSingleSecretResult(responseList, secretRequest) }, - }; + } } - private awaitAndUnwrapSecrets( - id: number, - requests: SecretRequest[], - ): SecretResponse[] { - const awaitRequest = create(AwaitSecretsRequestSchema, { ids: [id] }); - let awaitResponse: AwaitSecretsResponse; + private awaitAndUnwrapSecrets(id: number, requests: SecretRequest[]): SecretResponse[] { + const awaitRequest = create(AwaitSecretsRequestSchema, { ids: [id] }) + let awaitResponse: AwaitSecretsResponse try { - awaitResponse = this.helpers.awaitSecrets( - awaitRequest, - this.maxResponseSize, - ); + awaitResponse = this.helpers.awaitSecrets(awaitRequest, this.maxResponseSize) } catch (err) { - const message = err instanceof Error ? err.message : String(err); - throw new SecretsBatchError(requests, message); + const message = err instanceof Error ? err.message : String(err) + throw new SecretsBatchError(requests, message) } - const secretsResponse = awaitResponse.responses[id]; + const secretsResponse = awaitResponse.responses[id] if (!secretsResponse) { - throw new SecretsBatchError(requests, "no response"); + throw new SecretsBatchError(requests, 'no response') } if (secretsResponse.responses.length !== requests.length) { - throw new SecretsBatchError(requests, "invalid value returned from host"); + throw new SecretsBatchError(requests, 'invalid value returned from host') } - return secretsResponse.responses; + return secretsResponse.responses } - private unwrapSingleSecretResult( - responseList: SecretResponse[], - request: SecretRequest, - ): Secret { + private unwrapSingleSecretResult(responseList: SecretResponse[], request: SecretRequest): Secret { if (responseList.length !== 1) { - throw new SecretsError(request, "invalid value returned from host"); + throw new SecretsError(request, 'invalid value returned from host') } - const response = responseList[0].response; + const response = responseList[0].response switch (response.case) { - case "secret": - return response.value; - case "error": - throw new SecretsError(request, response.value.error); + case 'secret': + return response.value + case 'error': + throw new SecretsError(request, response.value.error) default: - throw new SecretsError( - request, - "cannot unmarshal returned value from host", - ); + throw new SecretsError(request, 'cannot unmarshal returned value from host') } } @@ -530,12 +481,12 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { * Generates a report via consensus mechanism. */ report(input: ReportRequest | ReportRequestJson): { result: () => Report } { - const consensus = new ConsensusCapability(); + const consensus = new ConsensusCapability() // Cast to native overload signature - the impl dispatches on $typeName. - const call = consensus.report(this, input as ReportRequest); + const call = consensus.report(this, input as ReportRequest) return { result: () => call.result(), - }; + } } } @@ -545,71 +496,60 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { */ export interface RuntimeHelpers { /** Initiates a capability call. Returns false if capability not found. */ - call(request: CapabilityRequest): boolean; + call(request: CapabilityRequest): boolean /** Awaits capability responses. Blocks until responses are ready. */ - await( - request: AwaitCapabilitiesRequest, - maxResponseSize: bigint, - ): AwaitCapabilitiesResponse; + await(request: AwaitCapabilitiesRequest, maxResponseSize: bigint): AwaitCapabilitiesResponse /** Requests secrets from host. Throws if host rejects the request. */ - getSecrets(request: GetSecretsRequest, maxResponseSize: bigint): void; + getSecrets(request: GetSecretsRequest, maxResponseSize: bigint): void /** Awaits secret responses. Blocks until secrets are ready. */ - awaitSecrets( - request: AwaitSecretsRequest, - maxResponseSize: bigint, - ): AwaitSecretsResponse; + awaitSecrets(request: AwaitSecretsRequest, maxResponseSize: bigint): AwaitSecretsResponse /** Switches execution mode (DON vs Node). Affects available operations. */ - switchModes(mode: Mode): void; + switchModes(mode: Mode): void /** Returns current time in milliseconds since Unix epoch. */ - now(): number; + now(): number /** Sleeps for the specified duration. */ sleep(ms: number): void /** Logs a message to the host environment. */ - log(message: string): void; + log(message: string): void } -function clearIgnoredFields( - value: ProtoValue, - descriptor: ConsensusDescriptor, -): void { +function clearIgnoredFields(value: ProtoValue, descriptor: ConsensusDescriptor): void { if (!descriptor || !value) { - return; + return } const fieldsMap = - descriptor.descriptor?.case === "fieldsMap" - ? descriptor.descriptor.value - : undefined; + descriptor.descriptor?.case === 'fieldsMap' ? descriptor.descriptor.value : undefined if (!fieldsMap) { - return; + return } - if (value.value?.case === "mapValue") { - const mapValue = value.value.value; + if (value.value?.case === 'mapValue') { + const mapValue = value.value.value if (!mapValue || !mapValue.fields) { - return; + return } for (const [key, val] of Object.entries(mapValue.fields)) { - const nestedDescriptor = fieldsMap.fields[key]; + const nestedDescriptor = fieldsMap.fields[key] if (!nestedDescriptor) { - delete mapValue.fields[key]; - continue; + delete mapValue.fields[key] + continue } const nestedFieldsMap = - nestedDescriptor.descriptor?.case === "fieldsMap" + nestedDescriptor.descriptor?.case === 'fieldsMap' ? nestedDescriptor.descriptor.value - : undefined; - if (nestedFieldsMap && val.value?.case === "mapValue") { - clearIgnoredFields(val, nestedDescriptor); + : undefined + if (nestedFieldsMap && val.value?.case === 'mapValue') { + clearIgnoredFields(val, nestedDescriptor) } } } diff --git a/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts b/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts index eadf0505..f16457d8 100644 --- a/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts +++ b/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts @@ -3,14 +3,14 @@ * createTestRuntimeHelpers, default consensus handler, and TestRuntime getLogs/setTimeProvider. * Does not re-test RuntimeImpl behaviour covered in runtime-impl.test.ts. */ -import { test as bunTest, describe, expect } from "bun:test"; -import { create } from "@bufbuild/protobuf"; -import { AnySchema } from "@bufbuild/protobuf/wkt"; -import { BasicActionCapability } from "@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen"; -import { consensusMedianAggregation } from "@cre/sdk/utils"; -import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import { SecretsError } from "../errors"; -import { BasicTestActionMock } from "../test/generated/capabilities/internal/basicaction/v1/basic_test_action_mock_gen"; +import { test as bunTest, describe, expect } from 'bun:test' +import { create } from '@bufbuild/protobuf' +import { AnySchema } from '@bufbuild/protobuf/wkt' +import { BasicActionCapability } from '@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen' +import { consensusMedianAggregation } from '@cre/sdk/utils' +import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' +import { SecretsError } from '../errors' +import { BasicTestActionMock } from '../test/generated/capabilities/internal/basicaction/v1/basic_test_action_mock_gen' import { __testOnlyRegistryStore, __testOnlyRunWithRegistry, @@ -20,300 +20,278 @@ import { RESPONSE_BUFFER_TOO_SMALL, registerTestCapability, test, -} from "./test-runtime"; +} from './test-runtime' -describe("Registry (via test)", () => { - test("get returns undefined for unregistered id", async () => { - expect(getTestCapabilityHandler("missing")).toBeUndefined(); - }); +describe('Registry (via test)', () => { + test('get returns undefined for unregistered id', async () => { + expect(getTestCapabilityHandler('missing')).toBeUndefined() + }) - test("register and get return handler", async () => { + test('register and get return handler', async () => { const handler = () => ({ - response: { case: "error" as const, value: "x" }, - }); - registerTestCapability("my-cap", handler); - expect(getTestCapabilityHandler("my-cap")).toBe(handler); - }); - - test("register throws when id already exists", async () => { - registerTestCapability("dup", () => ({ - response: { case: "error" as const, value: "" }, - })); + response: { case: 'error' as const, value: 'x' }, + }) + registerTestCapability('my-cap', handler) + expect(getTestCapabilityHandler('my-cap')).toBe(handler) + }) + + test('register throws when id already exists', async () => { + registerTestCapability('dup', () => ({ + response: { case: 'error' as const, value: '' }, + })) expect(() => - registerTestCapability("dup", () => ({ - response: { case: "error" as const, value: "" }, + registerTestCapability('dup', () => ({ + response: { case: 'error' as const, value: '' }, })), - ).toThrow("capability already exists: dup"); - }); -}); - -describe("TestRuntime / helper layer", () => { - test("getLogs returns messages written via helper log()", () => { - const rt = newTestRuntime(); - rt.log("msg1"); - rt.log("msg2"); - expect(rt.getLogs()).toEqual(["msg1", "msg2"]); - }); - - test("now() uses Date.now() when setTimeProvider not set", () => { - const rt = newTestRuntime(); - const before = Date.now(); - const t = rt.now().getTime(); - const after = Date.now(); - expect(t).toBeGreaterThanOrEqual(before); - expect(t).toBeLessThanOrEqual(after); - }); - - test("setTimeProvider causes helper now() to return provided value", () => { - const rt = newTestRuntime(); - const fixed = 999888777666; - rt.setTimeProvider(() => fixed); - expect(rt.now().getTime()).toBe(fixed); - }); - - test("sleep() completes without throwing", () => { - const rt = newTestRuntime(); - expect(() => rt.sleep(100)).not.toThrow(); - }); - - test("sleep() with zero milliseconds completes without throwing", () => { - const rt = newTestRuntime(); - expect(() => rt.sleep(0)).not.toThrow(); - }); - - test("sleep() returns undefined (no-op in test runtime)", () => { - const rt = newTestRuntime(); - const result = rt.sleep(250); - expect(result).toBeUndefined(); - }); - - test("helper call returns false for unregistered capability", () => { - const rt = newTestRuntime(); - const cap = new BasicActionCapability(); - const call = cap.performAction(rt, { inputThing: true }); - expect(() => call.result()).toThrow(CapabilityError); - expect(() => call.result()).toThrow(/not found/); - }); - - test("registered capability: callCapability and await path both route to handler and return result", () => { - const expectedResult = "result-from-registered-handler"; - const mock = BasicTestActionMock.testInstance(); - mock.performAction = () => ({ adaptedThing: expectedResult }); - const rt = newTestRuntime(); + ).toThrow('capability already exists: dup') + }) +}) + +describe('TestRuntime / helper layer', () => { + test('getLogs returns messages written via helper log()', () => { + const rt = newTestRuntime() + rt.log('msg1') + rt.log('msg2') + expect(rt.getLogs()).toEqual(['msg1', 'msg2']) + }) + + test('now() uses Date.now() when setTimeProvider not set', () => { + const rt = newTestRuntime() + const before = Date.now() + const t = rt.now().getTime() + const after = Date.now() + expect(t).toBeGreaterThanOrEqual(before) + expect(t).toBeLessThanOrEqual(after) + }) + + test('setTimeProvider causes helper now() to return provided value', () => { + const rt = newTestRuntime() + const fixed = 999888777666 + rt.setTimeProvider(() => fixed) + expect(rt.now().getTime()).toBe(fixed) + }) + + test('sleep() completes without throwing', () => { + const rt = newTestRuntime() + expect(() => rt.sleep(100)).not.toThrow() + }) + + test('sleep() with zero milliseconds completes without throwing', () => { + const rt = newTestRuntime() + expect(() => rt.sleep(0)).not.toThrow() + }) + + test('sleep() returns undefined (no-op in test runtime)', () => { + const rt = newTestRuntime() + const result = rt.sleep(250) + expect(result).toBeUndefined() + }) + + test('helper call returns false for unregistered capability', () => { + const rt = newTestRuntime() + const cap = new BasicActionCapability() + const call = cap.performAction(rt, { inputThing: true }) + expect(() => call.result()).toThrow(CapabilityError) + expect(() => call.result()).toThrow(/not found/) + }) + + test('registered capability: callCapability and await path both route to handler and return result', () => { + const expectedResult = 'result-from-registered-handler' + const mock = BasicTestActionMock.testInstance() + mock.performAction = () => ({ adaptedThing: expectedResult }) + const rt = newTestRuntime() // Sync path: callCapability (via performAction) then .result() triggers internal await const call1 = new BasicActionCapability().performAction(rt, { inputThing: true, - }); - expect(call1.result().adaptedThing).toBe(expectedResult); + }) + expect(call1.result().adaptedThing).toBe(expectedResult) // Async path: two in-flight calls, then both .result() — helper routes by callbackId const call2 = new BasicActionCapability().performAction(rt, { inputThing: false, - }); + }) const call3 = new BasicActionCapability().performAction(rt, { inputThing: true, - }); - expect(call2.result().adaptedThing).toBe(expectedResult); - expect(call3.result().adaptedThing).toBe(expectedResult); - }); - - test("helper call catches handler throw and await returns error response", () => { - const rt = newTestRuntime(); - const errMsg = "node function error"; + }) + expect(call2.result().adaptedThing).toBe(expectedResult) + expect(call3.result().adaptedThing).toBe(expectedResult) + }) + + test('helper call catches handler throw and await returns error response', () => { + const rt = newTestRuntime() + const errMsg = 'node function error' const p = rt.runInNodeMode(() => { - throw new Error(errMsg); - }, consensusMedianAggregation())(); - expect(() => p.result()).toThrow(errMsg); - }); - - test("helper await throws RESPONSE_BUFFER_TOO_SMALL when serialized response exceeds maxResponseSize", () => { - const rt = newTestRuntime(null, { maxResponseSize: 1 }); - const payload = new Uint8Array(new ArrayBuffer(3)); - payload.set([1, 2, 3]); + throw new Error(errMsg) + }, consensusMedianAggregation())() + expect(() => p.result()).toThrow(errMsg) + }) + + test('helper await throws RESPONSE_BUFFER_TOO_SMALL when serialized response exceeds maxResponseSize', () => { + const rt = newTestRuntime(null, { maxResponseSize: 1 }) + const payload = new Uint8Array(new ArrayBuffer(3)) + payload.set([1, 2, 3]) const reportCall = rt.report({ - encodedPayload: Buffer.from(payload).toString("base64"), - }); - expect(() => reportCall.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL); - }); - - test("default Report handler: defaultReport metadata + payload + sigs", () => { - const rt = newTestRuntime(); - const payloadBytes = new TextEncoder().encode("some_encoded_report_data"); - const payload = new Uint8Array(new ArrayBuffer(payloadBytes.length)); - payload.set(payloadBytes); - const result = rt - .report({ encodedPayload: Buffer.from(payload).toString("base64") }) - .result(); - const unwrapped = result.x_generatedCodeOnly_unwrap(); - expect(unwrapped.rawReport.length).toBe( - REPORT_METADATA_HEADER_LENGTH + payload.length, - ); - const expectedMetadata = new Uint8Array(REPORT_METADATA_HEADER_LENGTH); + encodedPayload: Buffer.from(payload).toString('base64'), + }) + expect(() => reportCall.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL) + }) + + test('default Report handler: defaultReport metadata + payload + sigs', () => { + const rt = newTestRuntime() + const payloadBytes = new TextEncoder().encode('some_encoded_report_data') + const payload = new Uint8Array(new ArrayBuffer(payloadBytes.length)) + payload.set(payloadBytes) + const result = rt.report({ encodedPayload: Buffer.from(payload).toString('base64') }).result() + const unwrapped = result.x_generatedCodeOnly_unwrap() + expect(unwrapped.rawReport.length).toBe(REPORT_METADATA_HEADER_LENGTH + payload.length) + const expectedMetadata = new Uint8Array(REPORT_METADATA_HEADER_LENGTH) for (let i = 0; i < REPORT_METADATA_HEADER_LENGTH; i++) { - expectedMetadata[i] = i % 256; + expectedMetadata[i] = i % 256 } - expect(unwrapped.rawReport.slice(0, REPORT_METADATA_HEADER_LENGTH)).toEqual( - expectedMetadata, - ); - expect(unwrapped.rawReport.slice(REPORT_METADATA_HEADER_LENGTH)).toEqual( - payload, - ); - expect(unwrapped.sigs).toHaveLength(2); - expect(new TextDecoder().decode(unwrapped.sigs[0].signature)).toBe( - "default_signature_1", - ); - expect(new TextDecoder().decode(unwrapped.sigs[1].signature)).toBe( - "default_signature_2", - ); - }); - - test("default Simple handler: observation value branch returns value", () => { - const rt = newTestRuntime(); - const p = rt.runInNodeMode(() => 42, consensusMedianAggregation())(); - expect(p.result()).toBe(42); - }); - - test("default Simple handler: observation error with default returns default", () => { - const rt = newTestRuntime(); + expect(unwrapped.rawReport.slice(0, REPORT_METADATA_HEADER_LENGTH)).toEqual(expectedMetadata) + expect(unwrapped.rawReport.slice(REPORT_METADATA_HEADER_LENGTH)).toEqual(payload) + expect(unwrapped.sigs).toHaveLength(2) + expect(new TextDecoder().decode(unwrapped.sigs[0].signature)).toBe('default_signature_1') + expect(new TextDecoder().decode(unwrapped.sigs[1].signature)).toBe('default_signature_2') + }) + + test('default Simple handler: observation value branch returns value', () => { + const rt = newTestRuntime() + const p = rt.runInNodeMode(() => 42, consensusMedianAggregation())() + expect(p.result()).toBe(42) + }) + + test('default Simple handler: observation error with default returns default', () => { + const rt = newTestRuntime() const p = rt.runInNodeMode(() => { - throw new Error("fail"); - }, consensusMedianAggregation().withDefault(100))(); - expect(p.result()).toBe(100); - }); + throw new Error('fail') + }, consensusMedianAggregation().withDefault(100))() + expect(p.result()).toBe(100) + }) - test("default Simple handler: observation error without default throws", () => { - const rt = newTestRuntime(); + test('default Simple handler: observation error without default throws', () => { + const rt = newTestRuntime() const p = rt.runInNodeMode(() => { - throw new Error("no default"); - }, consensusMedianAggregation())(); - expect(() => p.result()).toThrow("no default"); - }); - - test("default consensus handler returns error for unknown method", async () => { - newTestRuntime(); // registers consensus handler on current registry - const handler = getTestCapabilityHandler("consensus@1.0.0-alpha"); - if (!handler) throw new Error("expected handler"); + throw new Error('no default') + }, consensusMedianAggregation())() + expect(() => p.result()).toThrow('no default') + }) + + test('default consensus handler returns error for unknown method', async () => { + newTestRuntime() // registers consensus handler on current registry + const handler = getTestCapabilityHandler('consensus@1.0.0-alpha') + if (!handler) throw new Error('expected handler') const res = handler({ - id: "consensus@1.0.0-alpha", - method: "Other", + id: 'consensus@1.0.0-alpha', + method: 'Other', payload: create(AnySchema, { value: new Uint8Array(0) }), - }); - expect(res.response.case).toBe("error"); - if (res.response.case === "error") { - expect(res.response.value).toBe("unknown method Other"); + }) + expect(res.response.case).toBe('error') + if (res.response.case === 'error') { + expect(res.response.value).toBe('unknown method Other') } - }); - - test("helper getSecrets: secret found returns value", () => { - const secrets = new Map>(); - secrets.set("ns1", new Map([["id1", "val1"]])); - const rt = newTestRuntime(secrets); - const result = rt.getSecret({ id: "id1", namespace: "ns1" }).result(); - expect(result.value).toBe("val1"); - expect(result.id).toBe("id1"); - expect(result.namespace).toBe("ns1"); - }); - - test("helper getSecrets: batched call returns mixed secret/error responses", () => { - const secrets = new Map>(); - secrets.set("ns1", new Map([["id1", "val1"]])); - const rt = newTestRuntime(secrets); + }) + + test('helper getSecrets: secret found returns value', () => { + const secrets = new Map>() + secrets.set('ns1', new Map([['id1', 'val1']])) + const rt = newTestRuntime(secrets) + const result = rt.getSecret({ id: 'id1', namespace: 'ns1' }).result() + expect(result.value).toBe('val1') + expect(result.id).toBe('id1') + expect(result.namespace).toBe('ns1') + }) + + test('helper getSecrets: batched call returns mixed secret/error responses', () => { + const secrets = new Map>() + secrets.set('ns1', new Map([['id1', 'val1']])) + const rt = newTestRuntime(secrets) const responses = rt .getSecrets([ - { id: "id1", namespace: "ns1" }, - { id: "missing", namespace: "ns1" }, + { id: 'id1', namespace: 'ns1' }, + { id: 'missing', namespace: 'ns1' }, ]) - .result(); - - expect(responses.length).toBe(2); - expect(responses[0].response.case).toBe("secret"); - expect(responses[1].response.case).toBe("error"); - }); - - test("helper getSecrets: secret not found returns error response", () => { - const rt = newTestRuntime(); + .result() + + expect(responses.length).toBe(2) + expect(responses[0].response.case).toBe('secret') + expect(responses[1].response.case).toBe('error') + }) + + test('helper getSecrets: secret not found returns error response', () => { + const rt = newTestRuntime() + expect(() => rt.getSecret({ id: 'missing', namespace: 'ns' }).result()).toThrow(SecretsError) + }) + + test('newTestRuntime uses options.maxResponseSize', () => { + const rt = newTestRuntime(null, { maxResponseSize: 1 }) + const payload = new Uint8Array(new ArrayBuffer(2)) + payload.set([1, 2]) expect(() => - rt.getSecret({ id: "missing", namespace: "ns" }).result(), - ).toThrow(SecretsError); - }); + rt.report({ encodedPayload: Buffer.from(payload).toString('base64') }).result(), + ).toThrow(RESPONSE_BUFFER_TOO_SMALL) + }) - test("newTestRuntime rejects unsafe maxResponseSize values", () => { - expect(() => - newTestRuntime(null, { - maxResponseSize: Number.MAX_SAFE_INTEGER + 1, - }), - ).toThrow( - "newTestRuntime maxResponseSize must be a non-negative safe integer number", - ); - }); - - test("newTestRuntime uses options.maxResponseSize", () => { - const rt = newTestRuntime(null, { maxResponseSize: 1 }); - const payload = new Uint8Array(new ArrayBuffer(2)); - payload.set([1, 2]); + test('newTestRuntime rejects unsafe maxResponseSize values', () => { expect(() => - rt - .report({ encodedPayload: Buffer.from(payload).toString("base64") }) - .result(), - ).toThrow(RESPONSE_BUFFER_TOO_SMALL); - }); - - test("newTestRuntime with null/undefined secrets uses empty map", () => { - const rt = newTestRuntime(null); - expect(() => rt.getSecret({ id: "x", namespace: "y" }).result()).toThrow( - SecretsError, - ); - const rt2 = newTestRuntime(); - expect(rt2.getLogs()).toEqual([]); - }); -}); - -describe("test wrapper", () => { - test("registry is available inside test body (set/read without passing registry)", async () => { - registerTestCapability("cap-a", () => ({ - response: { case: "error" as const, value: "a" }, - })); - const handler = getTestCapabilityHandler("cap-a"); - if (!handler) throw new Error("expected handler"); + newTestRuntime(null, { maxResponseSize: Number.MAX_SAFE_INTEGER + 1 }), + ).toThrow('newTestRuntime maxResponseSize must be a non-negative safe integer number') + }) + + test('newTestRuntime with null/undefined secrets uses empty map', () => { + const rt = newTestRuntime(null) + expect(() => rt.getSecret({ id: 'x', namespace: 'y' }).result()).toThrow(SecretsError) + const rt2 = newTestRuntime() + expect(rt2.getLogs()).toEqual([]) + }) +}) + +describe('test wrapper', () => { + test('registry is available inside test body (set/read without passing registry)', async () => { + registerTestCapability('cap-a', () => ({ + response: { case: 'error' as const, value: 'a' }, + })) + const handler = getTestCapabilityHandler('cap-a') + if (!handler) throw new Error('expected handler') expect( handler({ - id: "cap-a", - method: "M", + id: 'cap-a', + method: 'M', payload: create(AnySchema, { value: new Uint8Array(0) }), }).response, ).toEqual({ - case: "error", - value: "a", - }); - }); - - test("isolation: test A registers only-a", async () => { - registerTestCapability("only-a", () => ({ - response: { case: "error" as const, value: "a" }, - })); - expect(getTestCapabilityHandler("only-a")).toBeDefined(); - }); - - test("isolation: test B does not see test A registry", async () => { - expect(getTestCapabilityHandler("only-a")).toBeUndefined(); - }); - - bunTest("cleanup: after test finishes, store is undefined", async () => { + case: 'error', + value: 'a', + }) + }) + + test('isolation: test A registers only-a', async () => { + registerTestCapability('only-a', () => ({ + response: { case: 'error' as const, value: 'a' }, + })) + expect(getTestCapabilityHandler('only-a')).toBeDefined() + }) + + test('isolation: test B does not see test A registry', async () => { + expect(getTestCapabilityHandler('only-a')).toBeUndefined() + }) + + bunTest('cleanup: after test finishes, store is undefined', async () => { await __testOnlyRunWithRegistry(async () => { - expect(__testOnlyRegistryStore()).toBeDefined(); - }); - expect(__testOnlyRegistryStore()).toBeUndefined(); - }); + expect(__testOnlyRegistryStore()).toBeDefined() + }) + expect(__testOnlyRegistryStore()).toBeUndefined() + }) - bunTest("failure path: cleanup happens when test body throws", async () => { + bunTest('failure path: cleanup happens when test body throws', async () => { await expect( __testOnlyRunWithRegistry(async () => { - expect(__testOnlyRegistryStore()).toBeDefined(); - throw new Error("intentional failure"); + expect(__testOnlyRegistryStore()).toBeDefined() + throw new Error('intentional failure') }), - ).rejects.toThrow("intentional failure"); - expect(__testOnlyRegistryStore()).toBeUndefined(); - }); -}); + ).rejects.toThrow('intentional failure') + expect(__testOnlyRegistryStore()).toBeUndefined() + }) +}) diff --git a/packages/cre-sdk/src/sdk/wasm/runner.test.ts b/packages/cre-sdk/src/sdk/wasm/runner.test.ts index b1cb5df3..893ff99e 100644 --- a/packages/cre-sdk/src/sdk/wasm/runner.test.ts +++ b/packages/cre-sdk/src/sdk/wasm/runner.test.ts @@ -1,10 +1,10 @@ -import { afterEach, describe, expect, mock, test } from "bun:test"; -import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; -import { anyPack, anyUnpack, EmptySchema } from "@bufbuild/protobuf/wkt"; +import { afterEach, describe, expect, mock, test } from 'bun:test' +import { create, fromBinary, toBinary } from '@bufbuild/protobuf' +import { anyPack, anyUnpack, EmptySchema } from '@bufbuild/protobuf/wkt' import { ConfigSchema, OutputsSchema, -} from "@cre/generated/capabilities/internal/basictrigger/v1/basic_trigger_pb"; +} from '@cre/generated/capabilities/internal/basictrigger/v1/basic_trigger_pb' import { AwaitSecretsRequestSchema, AwaitSecretsResponseSchema, @@ -19,354 +19,319 @@ import { type Trigger, TriggerSchema, type TriggerSubscriptionRequest, -} from "@cre/generated/sdk/v1alpha/sdk_pb"; -import type { Value as ProtoValue } from "@cre/generated/values/v1/values_pb"; -import { BasicCapability as BasicTriggerCapability } from "@cre/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen"; -import { cre } from "@cre/sdk/cre"; -import { Value } from "../utils"; -import type { SecretsProvider } from "../workflow"; -import { Runner } from "./runner"; +} from '@cre/generated/sdk/v1alpha/sdk_pb' +import type { Value as ProtoValue } from '@cre/generated/values/v1/values_pb' +import { BasicCapability as BasicTriggerCapability } from '@cre/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen' +import { cre } from '@cre/sdk/cre' +import { Value } from '../utils' +import type { SecretsProvider } from '../workflow' +import { Runner } from './runner' -const anyConfig = Buffer.from("config"); -const anyMaxResponseSize = 2048n; -const basicTrigger = new BasicTriggerCapability(); -const capID = BasicTriggerCapability.CAPABILITY_ID; +const anyConfig = Buffer.from('config') +const anyMaxResponseSize = 2048n +const basicTrigger = new BasicTriggerCapability() +const capID = BasicTriggerCapability.CAPABILITY_ID const subscribeRequest = create(ExecuteRequestSchema, { - request: { case: "subscribe", value: create(EmptySchema) }, + request: { case: 'subscribe', value: create(EmptySchema) }, maxResponseSize: anyMaxResponseSize, config: anyConfig, -}); +}) const anyExecuteRequest = create(ExecuteRequestSchema, { request: { - case: "trigger", + case: 'trigger', value: create(TriggerSchema, { id: 0n, - payload: anyPack( - OutputsSchema, - create(OutputsSchema, { coolOutput: "hi" }), - ), + payload: anyPack(OutputsSchema, create(OutputsSchema, { coolOutput: 'hi' })), }), }, maxResponseSize: anyMaxResponseSize, config: anyConfig, -}); +}) type TestRunnerBindings = { - versionV2: () => void; - sendResponse: (data: Uint8Array) => number; - getWasiArgs: () => string; - getSecrets: ( - data: Uint8Array | Uint8Array, - maxresponse: number, - ) => any; + versionV2: () => void + sendResponse: (data: Uint8Array) => number + getWasiArgs: () => string + getSecrets: (data: Uint8Array | Uint8Array, maxresponse: number) => any awaitSecrets: ( data: Uint8Array | Uint8Array, maxresponse: number, - ) => Uint8Array | Uint8Array; -}; + ) => Uint8Array | Uint8Array +} const mockHostBindings: TestRunnerBindings = { sendResponse: mock(() => { - return 0; + return 0 }), versionV2: mock(() => {}), getWasiArgs: mock(() => { - throw new Error("override for tests"); + throw new Error('override for tests') }), getSecrets: mock((data, maxResponseSize) => { - throw new Error("override for tests"); + throw new Error('override for tests') }), awaitSecrets: mock((data, maxResponseSize) => { - throw new Error("override for tests"); + throw new Error('override for tests') }), -}; +} const proxyHostBindings = { sendResponse: (data: Uint8Array) => { - return mockHostBindings.sendResponse(data); + return mockHostBindings.sendResponse(data) }, versionV2: () => { - return mockHostBindings.versionV2(); + return mockHostBindings.versionV2() }, getWasiArgs: () => { - return mockHostBindings.getWasiArgs(); + return mockHostBindings.getWasiArgs() }, switchModes: mock(() => {}), log: (message: string) => { - throw new Error("log called unexpectedly in test"); + throw new Error('log called unexpectedly in test') }, callCapability: (data: Uint8Array) => { - throw new Error("callCapability called unexpectedly in test"); + throw new Error('callCapability called unexpectedly in test') }, awaitCapabilities: (data: Uint8Array, id: number) => { - throw new Error("awaitCapabilities called unexpectedly in test"); + throw new Error('awaitCapabilities called unexpectedly in test') }, getSecrets: (data: Uint8Array, id: number) => { - return mockHostBindings.getSecrets(data, id); + return mockHostBindings.getSecrets(data, id) }, awaitSecrets: (data: Uint8Array, id: number) => { - return mockHostBindings.awaitSecrets(data, id); + return mockHostBindings.awaitSecrets(data, id) }, now: () => { - throw new Error("now called unexpectedly in test"); + throw new Error('now called unexpectedly in test') }, sleep: () => { - throw new Error("sleep called unexpectedly in test"); + throw new Error('sleep called unexpectedly in test') }, -}; +} -Object.assign(globalThis, proxyHostBindings); +Object.assign(globalThis, proxyHostBindings) afterEach(() => { - mock.restore(); -}); + mock.restore() +}) -describe("runner", () => { - describe("run", () => { - test("gathers subscriptions", async () => { - var sentResponse: ExecutionResult | null = null; +describe('runner', () => { + describe('run', () => { + test('gathers subscriptions', async () => { + var sentResponse: ExecutionResult | null = null mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input); - return 0; - }); - const runner = await getTestRunner(subscribeRequest); + sentResponse = fromBinary(ExecutionResultSchema, input) + return 0 + }) + const runner = await getTestRunner(subscribeRequest) await runner.run(async (_: string, secretsProvider: SecretsProvider) => { return [ - cre.handler(basicTrigger.trigger({ name: "foo", number: 10 }), () => { - throw new Error( - "Must not be called during registration to tiggers", - ); + cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), () => { + throw new Error('Must not be called during registration to tiggers') }), - ]; - }); - expect(sentResponse).toBeDefined(); - expect(sentResponse!.result.case).toBe("triggerSubscriptions"); - const responseValue = sentResponse!.result - .value! as TriggerSubscriptionRequest; - expect(responseValue.subscriptions.length).toBe(1); - expect(responseValue.subscriptions[0].id).toBe(capID); - expect(responseValue.subscriptions[0].method).toBe("Trigger"); - expect(responseValue.subscriptions[0].payload).toBeDefined(); - const actualConfig = anyUnpack( - responseValue.subscriptions[0].payload!, - ConfigSchema, - )!; - expect(actualConfig.name).toBe("foo"); - expect(actualConfig.number).toBe(10); - }); + ] + }) + expect(sentResponse).toBeDefined() + expect(sentResponse!.result.case).toBe('triggerSubscriptions') + const responseValue = sentResponse!.result.value! as TriggerSubscriptionRequest + expect(responseValue.subscriptions.length).toBe(1) + expect(responseValue.subscriptions[0].id).toBe(capID) + expect(responseValue.subscriptions[0].method).toBe('Trigger') + expect(responseValue.subscriptions[0].payload).toBeDefined() + const actualConfig = anyUnpack(responseValue.subscriptions[0].payload!, ConfigSchema)! + expect(actualConfig.name).toBe('foo') + expect(actualConfig.number).toBe(10) + }) - test("executes workflow", async () => { - var sentResponse: ExecutionResult | null = null; + test('executes workflow', async () => { + var sentResponse: ExecutionResult | null = null mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input); - return 0; - }); - const runner = await getTestRunner(anyExecuteRequest); + sentResponse = fromBinary(ExecutionResultSchema, input) + return 0 + }) + const runner = await getTestRunner(anyExecuteRequest) await runner.run(async (_: string, secretsProvider: SecretsProvider) => { return [ - cre.handler( - basicTrigger.trigger({ name: "foo", number: 10 }), - (runtime, trigger) => { - expect(runtime.config).toBe(anyConfig.toString()); - expect(trigger.coolOutput).toBe("hi"); - return 10; - }, - ), - ]; - }); - expect(sentResponse).toBeDefined(); - expect(sentResponse!.result.case).toBe("value"); + cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), (runtime, trigger) => { + expect(runtime.config).toBe(anyConfig.toString()) + expect(trigger.coolOutput).toBe('hi') + return 10 + }), + ] + }) + expect(sentResponse).toBeDefined() + expect(sentResponse!.result.case).toBe('value') expect( Value.wrap(sentResponse!.result.value as ProtoValue).unwrapToType({ instance: 10, }), - ).toBe(10); - }); - }); + ).toBe(10) + }) + }) - test("executes subscribe error", async () => { - var sentResponse: ExecutionResult | null = null; - const anyError = "error"; + test('executes subscribe error', async () => { + var sentResponse: ExecutionResult | null = null + const anyError = 'error' mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input); - expect(sentResponse!.result.case).toBe("error"); - expect(sentResponse!.result.value).toBe(anyError); - return 0; - }); - const runner = await getTestRunner(subscribeRequest); + sentResponse = fromBinary(ExecutionResultSchema, input) + expect(sentResponse!.result.case).toBe('error') + expect(sentResponse!.result.value).toBe(anyError) + return 0 + }) + const runner = await getTestRunner(subscribeRequest) await runner.run((_: string, secretsProvider: SecretsProvider) => { - throw new Error(anyError); - }); - }); + throw new Error(anyError) + }) + }) - test("executes subscribe resolve error", async () => { - var sentResponse: ExecutionResult | null = null; - const anyError = "error"; + test('executes subscribe resolve error', async () => { + var sentResponse: ExecutionResult | null = null + const anyError = 'error' mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input); - expect(sentResponse!.result.case).toBe("error"); - expect(sentResponse!.result.value).toBe(anyError); - return 0; - }); - const runner = await getTestRunner(subscribeRequest); + sentResponse = fromBinary(ExecutionResultSchema, input) + expect(sentResponse!.result.case).toBe('error') + expect(sentResponse!.result.value).toBe(anyError) + return 0 + }) + const runner = await getTestRunner(subscribeRequest) await runner.run(async (_: string, secretsProvider: SecretsProvider) => { - return Promise.reject(new Error(anyError)); - }); - }); + return Promise.reject(new Error(anyError)) + }) + }) - test("executes trigger error", async () => { - var sentResponse: ExecutionResult | null = null; - const anyError = "error"; + test('executes trigger error', async () => { + var sentResponse: ExecutionResult | null = null + const anyError = 'error' mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input); - expect(sentResponse!.result.case).toBe("error"); - expect(sentResponse!.result.value).toBe(anyError); - return 0; - }); - const runner = await getTestRunner(anyExecuteRequest); + sentResponse = fromBinary(ExecutionResultSchema, input) + expect(sentResponse!.result.case).toBe('error') + expect(sentResponse!.result.value).toBe(anyError) + return 0 + }) + const runner = await getTestRunner(anyExecuteRequest) await runner.run(async (_: string, secretsProvider: SecretsProvider) => { - throw new Error(anyError); - }); - }); + throw new Error(anyError) + }) + }) - test("executes workflow with multiple triggers", async () => { - var sentResponse: ExecutionResult | null = null; + test('executes workflow with multiple triggers', async () => { + var sentResponse: ExecutionResult | null = null mockHostBindings.sendResponse = mock((input) => { - sentResponse = fromBinary(ExecutionResultSchema, input); - return 0; - }); - const testRequest = structuredClone(anyExecuteRequest); - const trigger = testRequest.request.value as Trigger; - trigger.id = 1n; + sentResponse = fromBinary(ExecutionResultSchema, input) + return 0 + }) + const testRequest = structuredClone(anyExecuteRequest) + const trigger = testRequest.request.value as Trigger + trigger.id = 1n - const runner = await getTestRunner(testRequest); + const runner = await getTestRunner(testRequest) await runner.run(async (_: string, secretsProvider: SecretsProvider) => { return [ - cre.handler( - basicTrigger.trigger({ name: "foo", number: 10 }), - (runtime, trigger) => { - expect(runtime.config).toBe(anyConfig.toString()); - expect(trigger.coolOutput).toBe("hi"); - return 10; - }, - ), - cre.handler( - basicTrigger.trigger({ name: "bar", number: 20 }), - (runtime, trigger) => { - expect(runtime.config).toBe(anyConfig.toString()); - expect(trigger.coolOutput).toBe("hi"); - return 20; - }, - ), - cre.handler( - basicTrigger.trigger({ name: "baz", number: 30 }), - (runtime, trigger) => { - expect(runtime.config).toBe(anyConfig.toString()); - expect(trigger.coolOutput).toBe("hi"); - return 30; - }, - ), - ]; - }); - expect(sentResponse).toBeDefined(); - expect(sentResponse!.result.case).toBe("value"); + cre.handler(basicTrigger.trigger({ name: 'foo', number: 10 }), (runtime, trigger) => { + expect(runtime.config).toBe(anyConfig.toString()) + expect(trigger.coolOutput).toBe('hi') + return 10 + }), + cre.handler(basicTrigger.trigger({ name: 'bar', number: 20 }), (runtime, trigger) => { + expect(runtime.config).toBe(anyConfig.toString()) + expect(trigger.coolOutput).toBe('hi') + return 20 + }), + cre.handler(basicTrigger.trigger({ name: 'baz', number: 30 }), (runtime, trigger) => { + expect(runtime.config).toBe(anyConfig.toString()) + expect(trigger.coolOutput).toBe('hi') + return 30 + }), + ] + }) + expect(sentResponse).toBeDefined() + expect(sentResponse!.result.case).toBe('value') expect( Value.wrap(sentResponse!.result.value as ProtoValue).unwrapToType({ instance: 10, }), - ).toBe(20); - }); + ).toBe(20) + }) - test("get secrets passes max response size", async () => { + test('get secrets passes max response size', async () => { const anySecretResponse = create(SecretResponseSchema, { response: { - case: "secret", + case: 'secret', value: create(SecretSchema, { - id: "Bar", - namespace: "Foo", - owner: "Baz", - value: "Qux", + id: 'Bar', + namespace: 'Foo', + owner: 'Baz', + value: 'Qux', }), }, - }); + }) const anySecretsResponse = create(SecretResponsesSchema, { responses: [anySecretResponse], - }); + }) mockHostBindings.getSecrets = (data, maxResponseSize) => { - const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data; - const secretsRequest = fromBinary(GetSecretsRequestSchema, dataBytes); - expect(secretsRequest.requests.length).toBe(1); - expect(secretsRequest.requests[0].namespace).toBe("Foo"); - expect(secretsRequest.requests[0].id).toBe("Bar"); - expect(secretsRequest.callbackId).toBe(0); - expect(maxResponseSize).toBe(Number(anyMaxResponseSize)); - return 0; - }; + const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data + const secretsRequest = fromBinary(GetSecretsRequestSchema, dataBytes) + expect(secretsRequest.requests.length).toBe(1) + expect(secretsRequest.requests[0].namespace).toBe('Foo') + expect(secretsRequest.requests[0].id).toBe('Bar') + expect(secretsRequest.callbackId).toBe(0) + expect(maxResponseSize).toBe(Number(anyMaxResponseSize)) + return 0 + } mockHostBindings.awaitSecrets = (data, maxResponseSize) => { - const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data; - const awaitSecretsRequest = fromBinary( - AwaitSecretsRequestSchema, - dataBytes, - ); - expect(awaitSecretsRequest.ids.length).toBe(1); - expect(awaitSecretsRequest.ids[0]).toBe(0); - expect(maxResponseSize).toBe(Number(anyMaxResponseSize)); + const dataBytes = Array.isArray(data) ? new Uint8Array(data) : data + const awaitSecretsRequest = fromBinary(AwaitSecretsRequestSchema, dataBytes) + expect(awaitSecretsRequest.ids.length).toBe(1) + expect(awaitSecretsRequest.ids[0]).toBe(0) + expect(maxResponseSize).toBe(Number(anyMaxResponseSize)) // Create the proper AwaitSecretsResponse with a map const awaitSecretsResponse = create(AwaitSecretsResponseSchema, { responses: { 0: anySecretsResponse, }, - }); - return toBinary(AwaitSecretsResponseSchema, awaitSecretsResponse); - }; + }) + return toBinary(AwaitSecretsResponseSchema, awaitSecretsResponse) + } - const dr = getTestRunner(subscribeRequest); - await (await dr).run( - async (_: string, secretsProvider: SecretsProvider) => { - const batched = await secretsProvider - .getSecrets([{ namespace: "Foo", id: "Bar" }]) - .result(); - expect(batched.length).toBe(1); - expect(batched[0].response.case).toBe("secret"); - if (batched[0].response.case === "secret") { - expect(batched[0].response.value.namespace).toBe("Foo"); - expect(batched[0].response.value.id).toBe("Bar"); - expect(batched[0].response.value.owner).toBe("Baz"); - expect(batched[0].response.value.value).toBe("Qux"); - } + const dr = getTestRunner(subscribeRequest) + await (await dr).run(async (_: string, secretsProvider: SecretsProvider) => { + const batched = await secretsProvider.getSecrets([{ namespace: 'Foo', id: 'Bar' }]).result() + expect(batched.length).toBe(1) + expect(batched[0].response.case).toBe('secret') + if (batched[0].response.case === 'secret') { + expect(batched[0].response.value.namespace).toBe('Foo') + expect(batched[0].response.value.id).toBe('Bar') + expect(batched[0].response.value.owner).toBe('Baz') + expect(batched[0].response.value.value).toBe('Qux') + } - // Keep compatibility coverage for single-secret API. - const single = await secretsProvider - .getSecret({ namespace: "Foo", id: "Bar" }) - .result(); - expect(single.namespace).toBe("Foo"); - expect(single.id).toBe("Bar"); - expect(single.owner).toBe("Baz"); - expect(single.value).toBe("Qux"); - return [cre.handler(basicTrigger.trigger({}), () => 10)]; - }, - ); - expect(true).toBe(true); - }); -}); + // Keep compatibility coverage for single-secret API. + const single = await secretsProvider.getSecret({ namespace: 'Foo', id: 'Bar' }).result() + expect(single.namespace).toBe('Foo') + expect(single.id).toBe('Bar') + expect(single.owner).toBe('Baz') + expect(single.value).toBe('Qux') + return [cre.handler(basicTrigger.trigger({}), () => 10)] + }) + expect(true).toBe(true) + }) +}) function getTestRunner(request: ExecuteRequest): Promise> { - const serialized = toBinary(ExecuteRequestSchema, request); - const encoded = Buffer.from(serialized).toString("base64"); + const serialized = toBinary(ExecuteRequestSchema, request) + const encoded = Buffer.from(serialized).toString('base64') // Update the mock to return the specific request - mockHostBindings.getWasiArgs = mock(() => - JSON.stringify(["program", encoded]), - ); + mockHostBindings.getWasiArgs = mock(() => JSON.stringify(['program', encoded])) return Runner.newRunner({ configParser: (b) => { - const stringConfig = Buffer.from(b).toString(); - expect(stringConfig).toBe(anyConfig.toString()); - return stringConfig; + const stringConfig = Buffer.from(b).toString() + expect(stringConfig).toBe(anyConfig.toString()) + return stringConfig }, - }); + }) } diff --git a/packages/cre-sdk/src/sdk/wasm/runner.ts b/packages/cre-sdk/src/sdk/wasm/runner.ts index 985b83e7..80b0767a 100644 --- a/packages/cre-sdk/src/sdk/wasm/runner.ts +++ b/packages/cre-sdk/src/sdk/wasm/runner.ts @@ -1,16 +1,16 @@ -import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; +import { create, fromBinary, toBinary } from '@bufbuild/protobuf' import { type ExecuteRequest, ExecuteRequestSchema, type ExecutionResult, ExecutionResultSchema, TriggerSubscriptionRequestSchema, -} from "@cre/generated/sdk/v1alpha/sdk_pb"; -import { type ConfigHandlerParams, configHandler } from "@cre/sdk/utils/config"; -import type { SecretsProvider, Workflow } from "@cre/sdk/workflow"; -import { Value } from "../utils"; -import { hostBindings } from "./host-bindings"; -import { Runtime } from "./runtime"; +} from '@cre/generated/sdk/v1alpha/sdk_pb' +import { type ConfigHandlerParams, configHandler } from '@cre/sdk/utils/config' +import type { SecretsProvider, Workflow } from '@cre/sdk/workflow' +import { Value } from '../utils' +import { hostBindings } from './host-bindings' +import { Runtime } from './runtime' export class Runner { private constructor( @@ -21,24 +21,21 @@ export class Runner { static async newRunner( configHandlerParams?: ConfigHandlerParams, ): Promise> { - hostBindings.versionV2(); - const request = Runner.getRequest(); - const config = await configHandler( - request, - configHandlerParams, - ); - return new Runner(config, request); + hostBindings.versionV2() + const request = Runner.getRequest() + const config = await configHandler(request, configHandlerParams) + return new Runner(config, request) } private static getRequest(): ExecuteRequest { - const argsString = hostBindings.getWasiArgs(); - let args: any; + const argsString = hostBindings.getWasiArgs() + let args: any try { - args = JSON.parse(argsString); + args = JSON.parse(argsString) } catch (e) { throw new Error( - "Invalid request: could not parse WASI arguments as JSON. Ensure the WASM runtime is passing valid arguments to the workflow", - ); + 'Invalid request: could not parse WASI arguments as JSON. Ensure the WASM runtime is passing valid arguments to the workflow', + ) } // SDK expects exactly 2 args: @@ -47,13 +44,13 @@ export class Runner { if (args.length !== 2) { throw new Error( `Invalid request: expected exactly 2 WASI arguments (script name and base64-encoded request payload), but received ${args.length}`, - ); + ) } - const base64Request = args[1]; + const base64Request = args[1] - const bytes = Buffer.from(base64Request, "base64"); - return fromBinary(ExecuteRequestSchema, bytes); + const bytes = Buffer.from(base64Request, 'base64') + return fromBinary(ExecuteRequestSchema, bytes) } async run( @@ -62,36 +59,36 @@ export class Runner { secretsProvider: SecretsProvider, ) => Promise> | Workflow, ) { - const runtime = new Runtime(this.config, 0, this.request.maxResponseSize); + const runtime = new Runtime(this.config, 0, this.request.maxResponseSize) - let result: Promise | ExecutionResult; + let result: Promise | ExecutionResult try { const workflow = await initFn(this.config, { getSecrets: runtime.getSecrets.bind(runtime), getSecret: runtime.getSecret.bind(runtime), - }); + }) switch (this.request.request.case) { - case "subscribe": - result = this.handleSubscribePhase(this.request, workflow); - break; - case "trigger": - result = this.handleExecutionPhase(this.request, workflow, runtime); - break; + case 'subscribe': + result = this.handleSubscribePhase(this.request, workflow) + break + case 'trigger': + result = this.handleExecutionPhase(this.request, workflow, runtime) + break default: throw new Error( `Unknown request type '${this.request.request.case}': expected 'subscribe' or 'trigger'. This may indicate a version mismatch between the SDK and the CRE runtime`, - ); + ) } } catch (e) { - const err = e instanceof Error ? e.message : String(e); + const err = e instanceof Error ? e.message : String(e) result = create(ExecutionResultSchema, { - result: { case: "error", value: err }, - }); + result: { case: 'error', value: err }, + }) } - const awaitedResult = await result!; - hostBindings.sendResponse(toBinary(ExecutionResultSchema, awaitedResult)); + const awaitedResult = await result! + hostBindings.sendResponse(toBinary(ExecutionResultSchema, awaitedResult)) } async handleExecutionPhase( @@ -99,37 +96,37 @@ export class Runner { workflow: Workflow, runtime: Runtime, ): Promise { - if (req.request.case !== "trigger") { + if (req.request.case !== 'trigger') { throw new Error( `cannot handle non-trigger request as a trigger: received request type '${req.request.case}' in handleExecutionPhase. This is an internal SDK error`, - ); + ) } - const triggerMsg = req.request.value; + const triggerMsg = req.request.value // We're about to cast bigint to number, so we need to check if it's safe - const id = BigInt(triggerMsg.id); + const id = BigInt(triggerMsg.id) if (id > BigInt(Number.MAX_SAFE_INTEGER)) { throw new Error( `Trigger ID ${id} exceeds JavaScript safe integer range (Number.MAX_SAFE_INTEGER = ${Number.MAX_SAFE_INTEGER}). This trigger ID cannot be safely represented as a number`, - ); + ) } - const index = Number(triggerMsg.id); + const index = Number(triggerMsg.id) if (Number.isFinite(index) && index >= 0 && index < workflow.length) { - const entry = workflow[index]; - const schema = entry.trigger.outputSchema(); + const entry = workflow[index] + const schema = entry.trigger.outputSchema() if (!triggerMsg.payload) { return create(ExecutionResultSchema, { result: { - case: "error", + case: 'error', value: `trigger payload is missing for handler at index ${index} (trigger ID ${triggerMsg.id}). The trigger event must include a payload`, }, - }); + }) } - const payloadAny = triggerMsg.payload; + const payloadAny = triggerMsg.payload /** * Note: do not hardcode method name; routing by id is authoritative. @@ -138,42 +135,39 @@ export class Runner { * * @see https://github.com/smartcontractkit/cre-sdk-go/blob/5a41d81e3e072008484e85dc96d746401aafcba2/cre/wasm/runner.go#L81 * */ - const decoded = fromBinary(schema, payloadAny.value); - const adapted = entry.trigger.adapt(decoded); + const decoded = fromBinary(schema, payloadAny.value) + const adapted = entry.trigger.adapt(decoded) try { - const result = await entry.fn(runtime, adapted); - const wrapped = Value.wrap(result); + const result = await entry.fn(runtime, adapted) + const wrapped = Value.wrap(result) return create(ExecutionResultSchema, { - result: { case: "value", value: wrapped.proto() }, - }); + result: { case: 'value', value: wrapped.proto() }, + }) } catch (e) { - const err = e instanceof Error ? e.message : String(e); + const err = e instanceof Error ? e.message : String(e) return create(ExecutionResultSchema, { - result: { case: "error", value: err }, - }); + result: { case: 'error', value: err }, + }) } } return create(ExecutionResultSchema, { result: { - case: "error", + case: 'error', value: `trigger not found: no workflow handler registered at index ${index} (trigger ID ${triggerMsg.id}). The workflow has ${workflow.length} handler(s) registered. Verify the trigger subscription matches a registered handler`, }, - }); + }) } - handleSubscribePhase( - req: ExecuteRequest, - workflow: Workflow, - ): ExecutionResult { - if (req.request.case !== "subscribe") { + handleSubscribePhase(req: ExecuteRequest, workflow: Workflow): ExecutionResult { + if (req.request.case !== 'subscribe') { return create(ExecutionResultSchema, { result: { - case: "error", + case: 'error', value: `subscribe request expected but received '${req.request.case}' in handleSubscribePhase. This is an internal SDK error`, }, - }); + }) } // Build TriggerSubscriptionRequest from the workflow entries @@ -181,14 +175,14 @@ export class Runner { id: entry.trigger.capabilityId(), method: entry.trigger.method(), payload: entry.trigger.configAsAny(), - })); + })) const subscriptionRequest = create(TriggerSubscriptionRequestSchema, { subscriptions, - }); + }) return create(ExecutionResultSchema, { - result: { case: "triggerSubscriptions", value: subscriptionRequest }, - }); + result: { case: 'triggerSubscriptions', value: subscriptionRequest }, + }) } } diff --git a/packages/cre-sdk/src/sdk/workflow.ts b/packages/cre-sdk/src/sdk/workflow.ts index 8b4b969b..bfb87293 100644 --- a/packages/cre-sdk/src/sdk/workflow.ts +++ b/packages/cre-sdk/src/sdk/workflow.ts @@ -1,18 +1,18 @@ -import type { Message } from "@bufbuild/protobuf"; +import type { Message } from '@bufbuild/protobuf' import type { Secret, SecretRequest, SecretRequestJson, SecretResponse, -} from "@cre/generated/sdk/v1alpha/sdk_pb"; -import type { Runtime } from "@cre/sdk/runtime"; -import type { Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; -import type { CreSerializable } from "./utils"; +} from '@cre/generated/sdk/v1alpha/sdk_pb' +import type { Runtime } from '@cre/sdk/runtime' +import type { Trigger } from '@cre/sdk/utils/triggers/trigger-interface' +import type { CreSerializable } from './utils' export type HandlerFn = ( runtime: Runtime, triggerOutput: TTriggerOutput, -) => Promise> | CreSerializable; +) => Promise> | CreSerializable export interface HandlerEntry< TConfig, @@ -20,13 +20,11 @@ export interface HandlerEntry< TTriggerOutput, TResult, > { - trigger: Trigger; - fn: HandlerFn; + trigger: Trigger + fn: HandlerFn } -export type Workflow = ReadonlyArray< - HandlerEntry ->; +export type Workflow = ReadonlyArray> export const handler = < TRawTriggerOutput extends Message, @@ -39,13 +37,13 @@ export const handler = < ): HandlerEntry => ({ trigger, fn, -}); +}) export type SecretsProvider = { getSecrets(requests: Array): { - result: () => SecretResponse[]; - }; + result: () => SecretResponse[] + } getSecret(request: SecretRequest | SecretRequestJson): { - result: () => Secret; - }; -}; + result: () => Secret + } +} From e3ea6ae2d8c7e151336a46a7647e0048da59dab2 Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Mon, 8 Jun 2026 16:40:18 -0400 Subject: [PATCH 03/20] Switched to throwing an error on any failed fetch --- .../src/workflows/secrets/index.ts | 18 ++-- packages/cre-sdk/src/sdk/errors.ts | 2 +- .../cre-sdk/src/sdk/impl/runtime-impl.test.ts | 82 ++++++++++++++++--- packages/cre-sdk/src/sdk/impl/runtime-impl.ts | 14 ++++ .../src/sdk/testutils/test-runtime.test.ts | 30 ++++--- 5 files changed, 112 insertions(+), 34 deletions(-) diff --git a/packages/cre-sdk-examples/src/workflows/secrets/index.ts b/packages/cre-sdk-examples/src/workflows/secrets/index.ts index e6602f82..eebafcfa 100644 --- a/packages/cre-sdk-examples/src/workflows/secrets/index.ts +++ b/packages/cre-sdk-examples/src/workflows/secrets/index.ts @@ -62,16 +62,16 @@ const onHTTPTrigger = async (runtime: Runtime) => { // Fetch a single secret const secretUrlValue = runtime.getSecret({ id: 'SECRET_URL' }).result().value - // Fetch multiple secrets + // Fetch multiple secrets — throws if any secret fails const secretsToFetch = [{ id: 'CHARACTER_ID1' }, { id: 'CHARACTER_ID2' }, { id: 'CHARACTER_ID3' }] - const secretResponses = runtime.getSecrets(secretsToFetch).result() - const characterIds = secretResponses.flatMap((response) => - response.response.case === 'secret' && response.response.value?.id - ? [response.response.value.value] - : [], - ) - if (characterIds.length === 0) { - throw new Error('No character ID secrets available') + let characterIds: string[] + try { + characterIds = runtime + .getSecrets(secretsToFetch) + .result() + .map((response: any) => response.response.value.value) + } catch (err: any) { + throw new Error(`Failed to fetch character ID secrets: ${err.message}`) } // choose a random character id diff --git a/packages/cre-sdk/src/sdk/errors.ts b/packages/cre-sdk/src/sdk/errors.ts index b3681532..333a4c7c 100644 --- a/packages/cre-sdk/src/sdk/errors.ts +++ b/packages/cre-sdk/src/sdk/errors.ts @@ -32,7 +32,7 @@ export class SecretsBatchError extends Error { public readonly error: string, ) { super( - `batch secret retrieval failed for ${secretRequests.length} request(s): ${error}. Verify the host response is complete and that the workflow has access to the requested secrets`, + `batch secret retrieval failed for ${secretRequests.length} request(s):\n${error}\nVerify the host response is complete and that the workflow has access to the requested secrets`, ) this.name = 'SecretsBatchError' } diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts index 0e05b5d3..c7830567 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts @@ -449,7 +449,7 @@ describe('test getSecret', () => { } }) - test('getSecrets returns mixed success and error responses without throwing', () => { + test('getSecrets throws SecretsBatchError when any secret fails', () => { const helpers = createRuntimeHelpersMock({ getSecrets: mock(() => undefined), awaitSecrets: mock(() => { @@ -487,16 +487,71 @@ describe('test getSecret', () => { }) const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) - const responses = runtime - .getSecrets([ - { id: 'ok-secret', namespace: 'ns' }, - { id: 'missing-secret', namespace: 'ns' }, - ]) - .result() + try { + runtime + .getSecrets([ + { id: 'ok-secret', namespace: 'ns' }, + { id: 'missing-secret', namespace: 'ns' }, + ]) + .result() + throw new Error('expected getSecrets to throw') + } catch (err) { + expect(err).toBeInstanceOf(SecretsBatchError) + expect((err as SecretsBatchError).message).toContain('missing-secret: secret not found') + } + }) - expect(responses.length).toEqual(2) - expect(responses[0].response.case).toEqual('secret') - expect(responses[1].response.case).toEqual('error') + test('getSecrets throws SecretsBatchError with all error messages when multiple secrets fail', () => { + const helpers = createRuntimeHelpersMock({ + getSecrets: mock(() => undefined), + awaitSecrets: mock(() => { + return create(AwaitSecretsResponseSchema, { + responses: { + 1: create(SecretResponsesSchema, { + responses: [ + create(SecretResponseSchema, { + response: { + case: 'error', + value: { + id: 'missing-a', + namespace: 'ns', + owner: 'owner', + error: 'not found', + }, + }, + }), + create(SecretResponseSchema, { + response: { + case: 'error', + value: { + id: 'missing-b', + namespace: 'ns', + owner: 'owner', + error: 'access denied', + }, + }, + }), + ], + }), + }, + }) + }), + }) + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + try { + runtime + .getSecrets([ + { id: 'missing-a', namespace: 'ns' }, + { id: 'missing-b', namespace: 'ns' }, + ]) + .result() + throw new Error('expected getSecrets to throw') + } catch (err) { + expect(err).toBeInstanceOf(SecretsBatchError) + expect((err as SecretsBatchError).message).toContain('missing-a: not found') + expect((err as SecretsBatchError).message).toContain('missing-b: access denied') + } }) test('normalizes missing secret namespace to default for JSON and protobuf requests', () => { @@ -795,7 +850,10 @@ describe('test getSecret', () => { 1: create(SecretResponsesSchema, { responses: [ create(SecretResponseSchema, { - response: { case: 'error', value: { error: errorMessage } }, + response: { + case: 'error', + value: { id: 'test-secret', error: errorMessage }, + }, }), ], }), @@ -806,7 +864,7 @@ describe('test getSecret', () => { const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) expect(() => runtime.getSecret(secretRequest).result()).toThrow( - new SecretsError(secretRequest, errorMessage), + new SecretsError(secretRequest, 'test-secret: secret not found'), ) }) diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.ts index e4e048eb..4932d2ac 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.ts @@ -458,6 +458,20 @@ export class RuntimeImpl extends BaseRuntimeImpl implements Runtime { throw new SecretsBatchError(requests, 'invalid value returned from host') } + const failedResponses = secretsResponse.responses.filter( + (response) => response.response.case === 'error', + ) + if (failedResponses.length > 0) { + const errorMessages = failedResponses.map((response) => { + if (response.response.case !== 'error') { + return 'unknown: unknown error' + } + const { id, error } = response.response.value + return `${id || 'unknown'}: ${error || 'unknown error'}` + }) + throw new SecretsBatchError(requests, errorMessages.join('\n')) + } + return secretsResponse.responses } diff --git a/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts b/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts index f16457d8..5e36b721 100644 --- a/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts +++ b/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts @@ -9,7 +9,7 @@ import { AnySchema } from '@bufbuild/protobuf/wkt' import { BasicActionCapability } from '@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen' import { consensusMedianAggregation } from '@cre/sdk/utils' import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' -import { SecretsError } from '../errors' +import { SecretsBatchError, SecretsError } from '../errors' import { BasicTestActionMock } from '../test/generated/capabilities/internal/basicaction/v1/basic_test_action_mock_gen' import { __testOnlyRegistryStore, @@ -203,21 +203,27 @@ describe('TestRuntime / helper layer', () => { expect(result.namespace).toBe('ns1') }) - test('helper getSecrets: batched call returns mixed secret/error responses', () => { + test('helper getSecrets: batched call throws SecretsBatchError when any secret fails', () => { const secrets = new Map>() secrets.set('ns1', new Map([['id1', 'val1']])) const rt = newTestRuntime(secrets) - const responses = rt - .getSecrets([ - { id: 'id1', namespace: 'ns1' }, - { id: 'missing', namespace: 'ns1' }, - ]) - .result() - - expect(responses.length).toBe(2) - expect(responses[0].response.case).toBe('secret') - expect(responses[1].response.case).toBe('error') + expect(() => + rt + .getSecrets([ + { id: 'id1', namespace: 'ns1' }, + { id: 'missing', namespace: 'ns1' }, + ]) + .result(), + ).toThrow(SecretsBatchError) + expect(() => + rt + .getSecrets([ + { id: 'id1', namespace: 'ns1' }, + { id: 'missing', namespace: 'ns1' }, + ]) + .result(), + ).toThrow('missing:') }) test('helper getSecrets: secret not found returns error response', () => { From 1629d43f8f13dc8a69d43fea3cd19404c5c4757b Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Wed, 10 Jun 2026 23:26:31 -0400 Subject: [PATCH 04/20] add solana mainnet --- .../capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts | 4 ++++ .../capabilities/blockchain/solana/v1alpha/client_sdk_gen.ts | 1 + .../capabilities/blockchain/evm/v1alpha/client_pb.ts | 2 +- .../capabilities/blockchain/solana/v1alpha/client_pb.ts | 2 +- submodules/chainlink-protos | 2 +- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts b/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts index 0aedc42d..d9e6e69f 100644 --- a/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts +++ b/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts @@ -123,6 +123,8 @@ export class ClientCapability { /** Available ChainSelector values */ static readonly SUPPORTED_CHAIN_SELECTORS = { + 'adi-mainnet': 4059281736450291836n, + 'adi-testnet': 9418205736192840573n, 'apechain-testnet-curtis': 9900119385908781505n, 'arc-testnet': 3034092155422581607n, 'avalanche-mainnet': 6433500567565415381n, @@ -130,6 +132,7 @@ export class ClientCapability { 'binance_smart_chain-mainnet': 11344663589394136015n, 'binance_smart_chain-testnet': 13264668187771770619n, 'celo-mainnet': 1346049177634351622n, + 'celo-sepolia': 3761762704474186180n, 'cronos-testnet': 2995292832068775165n, 'dtcc-testnet-andesite': 15513093881969820114n, 'ethereum-mainnet': 5009297550715157269n, @@ -169,6 +172,7 @@ export class ClientCapability { 'polygon-mainnet': 4051577828743386545n, 'polygon-testnet-amoy': 16281711391670634445n, 'private-testnet-andesite': 6915682381028791124n, + 'private-testnet-rhyolite': 604447335222770945n, 'sonic-mainnet': 1673871237479749969n, 'sonic-testnet': 1763698235108410440n, 'tac-testnet': 9488606126177218005n, diff --git a/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/solana/v1alpha/client_sdk_gen.ts b/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/solana/v1alpha/client_sdk_gen.ts index 2da065f7..612bd21a 100644 --- a/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/solana/v1alpha/client_sdk_gen.ts +++ b/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/solana/v1alpha/client_sdk_gen.ts @@ -92,6 +92,7 @@ export class ClientCapability { /** Available ChainSelector values */ static readonly SUPPORTED_CHAIN_SELECTORS = { 'solana-devnet': 16423721717087811551n, + 'solana-mainnet': 124615329519749607n, } as const constructor(private readonly ChainSelector: bigint) {} diff --git a/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts b/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts index 366e7d9a..3900c04e 100644 --- a/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts +++ b/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts @@ -17,7 +17,7 @@ import { file_values_v1_values } from '../../../../values/v1/values_pb' export const file_capabilities_blockchain_evm_v1alpha_client: GenFile = /*@__PURE__*/ fileDesc( - 'CjBjYXBhYmlsaXRpZXMvYmxvY2tjaGFpbi9ldm0vdjFhbHBoYS9jbGllbnQucHJvdG8SI2NhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhIh0KC1RvcGljVmFsdWVzEg4KBnZhbHVlcxgBIAMoDCK4AQoXRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QSEQoJYWRkcmVzc2VzGAEgAygMEkAKBnRvcGljcxgCIAMoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRvcGljVmFsdWVzEkgKCmNvbmZpZGVuY2UYAyABKA4yNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Db25maWRlbmNlTGV2ZWwiegoTQ2FsbENvbnRyYWN0UmVxdWVzdBI6CgRjYWxsGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZxInCgxibG9ja19udW1iZXIYAiABKAsyES52YWx1ZXMudjEuQmlnSW50IiEKEUNhbGxDb250cmFjdFJlcGx5EgwKBGRhdGEYASABKAwiWwoRRmlsdGVyTG9nc1JlcXVlc3QSRgoMZmlsdGVyX3F1ZXJ5GAEgASgLMjAuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyUXVlcnkiSQoPRmlsdGVyTG9nc1JlcGx5EjYKBGxvZ3MYASADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cixwEKA0xvZxIPCgdhZGRyZXNzGAEgASgMEg4KBnRvcGljcxgCIAMoDBIPCgd0eF9oYXNoGAMgASgMEhIKCmJsb2NrX2hhc2gYBCABKAwSDAoEZGF0YRgFIAEoDBIRCglldmVudF9zaWcYBiABKAwSJwoMYmxvY2tfbnVtYmVyGAcgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIQCgh0eF9pbmRleBgIIAEoDRINCgVpbmRleBgJIAEoDRIPCgdyZW1vdmVkGAogASgIIjEKB0NhbGxNc2cSDAoEZnJvbRgBIAEoDBIKCgJ0bxgCIAEoDBIMCgRkYXRhGAMgASgMIr0BCgtGaWx0ZXJRdWVyeRISCgpibG9ja19oYXNoGAEgASgMEiUKCmZyb21fYmxvY2sYAiABKAsyES52YWx1ZXMudjEuQmlnSW50EiMKCHRvX2Jsb2NrGAMgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIRCglhZGRyZXNzZXMYBCADKAwSOwoGdG9waWNzGAUgAygLMisuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuVG9waWNzIhcKBlRvcGljcxINCgV0b3BpYxgBIAMoDCJMChBCYWxhbmNlQXRSZXF1ZXN0Eg8KB2FjY291bnQYASABKAwSJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludCI0Cg5CYWxhbmNlQXRSZXBseRIiCgdiYWxhbmNlGAEgASgLMhEudmFsdWVzLnYxLkJpZ0ludCJPChJFc3RpbWF0ZUdhc1JlcXVlc3QSOQoDbXNnGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZyIjChBFc3RpbWF0ZUdhc1JlcGx5Eg8KA2dhcxgBIAEoBEICMAAiKwobR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXF1ZXN0EgwKBGhhc2gYASABKAwiYgoZR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXBseRJFCgt0cmFuc2FjdGlvbhgBIAEoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRyYW5zYWN0aW9uIqEBCgtUcmFuc2FjdGlvbhIRCgVub25jZRgBIAEoBEICMAASDwoDZ2FzGAIgASgEQgIwABIKCgJ0bxgDIAEoDBIMCgRkYXRhGAQgASgMEgwKBGhhc2gYBSABKAwSIAoFdmFsdWUYBiABKAsyES52YWx1ZXMudjEuQmlnSW50EiQKCWdhc19wcmljZRgHIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiLAocR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVxdWVzdBIMCgRoYXNoGAEgASgMIlsKGkdldFRyYW5zYWN0aW9uUmVjZWlwdFJlcGx5Ej0KB3JlY2VpcHQYASABKAsyLC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXB0IpkCCgdSZWNlaXB0EhIKBnN0YXR1cxgBIAEoBEICMAASFAoIZ2FzX3VzZWQYAiABKARCAjAAEhQKCHR4X2luZGV4GAMgASgEQgIwABISCgpibG9ja19oYXNoGAQgASgMEjYKBGxvZ3MYBiADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cSDwoHdHhfaGFzaBgHIAEoDBIuChNlZmZlY3RpdmVfZ2FzX3ByaWNlGAggASgLMhEudmFsdWVzLnYxLkJpZ0ludBInCgxibG9ja19udW1iZXIYCSABKAsyES52YWx1ZXMudjEuQmlnSW50EhgKEGNvbnRyYWN0X2FkZHJlc3MYCiABKAwiQAoVSGVhZGVyQnlOdW1iZXJSZXF1ZXN0EicKDGJsb2NrX251bWJlchgBIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiUgoTSGVhZGVyQnlOdW1iZXJSZXBseRI7CgZoZWFkZXIYASABKAsyKy5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5IZWFkZXIiawoGSGVhZGVyEhUKCXRpbWVzdGFtcBgBIAEoBEICMAASJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIMCgRoYXNoGAMgASgMEhMKC3BhcmVudF9oYXNoGAQgASgMIqsBChJXcml0ZVJlcG9ydFJlcXVlc3QSEAoIcmVjZWl2ZXIYASABKAwSKwoGcmVwb3J0GAIgASgLMhsuc2RrLnYxYWxwaGEuUmVwb3J0UmVzcG9uc2USRwoKZ2FzX2NvbmZpZxgDIAEoCzIuLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkdhc0NvbmZpZ0gAiAEBQg0KC19nYXNfY29uZmlnIiIKCUdhc0NvbmZpZxIVCglnYXNfbGltaXQYASABKARCAjAAIocDChBXcml0ZVJlcG9ydFJlcGx5EkAKCXR4X3N0YXR1cxgBIAEoDjItLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlR4U3RhdHVzEnUKInJlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXMYAiABKA4yRC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzSACIAQESFAoHdHhfaGFzaBgDIAEoDEgBiAEBEi8KD3RyYW5zYWN0aW9uX2ZlZRgEIAEoCzIRLnZhbHVlcy52MS5CaWdJbnRIAogBARIaCg1lcnJvcl9tZXNzYWdlGAUgASgJSAOIAQFCJQojX3JlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXNCCgoIX3R4X2hhc2hCEgoQX3RyYW5zYWN0aW9uX2ZlZUIQCg5fZXJyb3JfbWVzc2FnZSppCg9Db25maWRlbmNlTGV2ZWwSGQoVQ09ORklERU5DRV9MRVZFTF9TQUZFEAASGwoXQ09ORklERU5DRV9MRVZFTF9MQVRFU1QQARIeChpDT05GSURFTkNFX0xFVkVMX0ZJTkFMSVpFRBACKoIBCh9SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzEi4KKlJFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfU1VDQ0VTUxAAEi8KK1JFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfUkVWRVJURUQQASpOCghUeFN0YXR1cxITCg9UWF9TVEFUVVNfRkFUQUwQABIWChJUWF9TVEFUVVNfUkVWRVJURUQQARIVChFUWF9TVEFUVVNfU1VDQ0VTUxACMoUYCgZDbGllbnQSgAEKDENhbGxDb250cmFjdBI4LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkNhbGxDb250cmFjdFJlcXVlc3QaNi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5DYWxsQ29udHJhY3RSZXBseRJ6CgpGaWx0ZXJMb2dzEjYuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nc1JlcXVlc3QaNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5GaWx0ZXJMb2dzUmVwbHkSdwoJQmFsYW5jZUF0EjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQmFsYW5jZUF0UmVxdWVzdBozLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkJhbGFuY2VBdFJlcGx5En0KC0VzdGltYXRlR2FzEjcuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXF1ZXN0GjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXBseRKYAQoUR2V0VHJhbnNhY3Rpb25CeUhhc2gSQC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcXVlc3QaPi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcGx5EpsBChVHZXRUcmFuc2FjdGlvblJlY2VpcHQSQS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvblJlY2VpcHRSZXF1ZXN0Gj8uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVwbHkShgEKDkhlYWRlckJ5TnVtYmVyEjouY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXF1ZXN0GjguY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXBseRJ2CgpMb2dUcmlnZ2VyEjwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QaKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cwARJ9CgtXcml0ZVJlcG9ydBI3LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVxdWVzdBo1LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVwbHkayg6CtRjFDggBEglldm1AMS4wLjAatQ4KDUNoYWluU2VsZWN0b3ISow4SoA4KJAoXYXBlY2hhaW4tdGVzdG5ldC1jdXJ0aXMQwcO0+I3EkrKJAQoXCgthcmMtdGVzdG5ldBDnxoye19fQjSoKHQoRYXZhbGFuY2hlLW1haW5uZXQQ1eeKwOHVmKRZCiMKFmF2YWxhbmNoZS10ZXN0bmV0LWZ1amkQm/n8kKLjqPjMAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLW1haW5uZXQQz/eU8djtlbidAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLXRlc3RuZXQQ+62+nICu5Iq4AQoYCgxjZWxvLW1haW5uZXQQhtTo2IaTiNcSChoKDmNyb25vcy10ZXN0bmV0EP3Z7q3g3trIKQoiChVkdGNjLXRlc3RuZXQtYW5kZXNpdGUQ0oPj0JmW5aTXAQocChBldGhlcmV1bS1tYWlubmV0EJX28eTPsqbCRQonChtldGhlcmV1bS1tYWlubmV0LWFyYml0cnVtLTEQxOiNzY6boddECiQKF2V0aGVyZXVtLW1haW5uZXQtYmFzZS0xEIL/q6L+uZDT3QEKIgoWZXRoZXJldW0tbWFpbm5ldC1pbmstMRCgsKbpt+aqhDAKJAoYZXRoZXJldW0tbWFpbm5ldC1saW5lYS0xELa66ZjLvbCbQAolChlldGhlcmV1bS1tYWlubmV0LW1hbnRsZS0xEIrntJXnsIPMFQonChtldGhlcmV1bS1tYWlubmV0LW9wdGltaXNtLTEQuJWPw/f+0OkzCiYKGWV0aGVyZXVtLW1haW5uZXQtc2Nyb2xsLTEQuLzk68S+yJ+3AQopCh1ldGhlcmV1bS1tYWlubmV0LXdvcmxkY2hhaW4tMRCH77q3xbbCuBwKJQoZZXRoZXJldW0tbWFpbm5ldC14bGF5ZXItMRCWpfycpqjv7SkKJQoZZXRoZXJldW0tbWFpbm5ldC16a3N5bmMtMRCU7pfZ7bSx1xUKJQoYZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhENm15M78ye6g3gEKLwojZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLWFyYml0cnVtLTEQ6s7u/+q2hKMwCiwKH2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1iYXNlLTEQuMq57/aQrsiPAQosCiBldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtbGluZWEtMRDrqtT+gvnmr08KLQohZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLW1hbnRsZS0xENXGuO7N9vKmcgovCiNldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtb3B0aW1pc20tMRCfhsWhvtjDwEgKLQohZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXNjcm9sbC0xEIvptL7buu3RHwowCiNldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtdW5pY2hhaW4tMRC03v7g7JeplsQBCjEKJWV0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS13b3JsZGNoYWluLTEQut/gxcep88VJCi0KIWV0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS16a3N5bmMtMRC3wfz98sSA3l8KIAoUZ25vc2lzX2NoYWluLW1haW5uZXQQ9JKt2vKirroGCicKG2dub3Npc19jaGFpbi10ZXN0bmV0LWNoaWFkbxCzsYLQm6WPj3sKHwoTaHlwZXJsaXF1aWQtbWFpbm5ldBCns/jdztHp8iEKHwoTaHlwZXJsaXF1aWQtdGVzdG5ldBCIzt3Il+DJvTsKIAoTaW5rLXRlc3RuZXQtc2Vwb2xpYRDo9Kel8+aWwIcBChkKDWpvdmF5LW1haW5uZXQQtcPEmqGA35IVChkKDWpvdmF5LXRlc3RuZXQQ5M+KhN6y3o4NChsKD21lZ2FldGgtbWFpbm5ldBDqlbbIvOSmyFQKHgoRbWVnYWV0aC10ZXN0bmV0LTIQ443eiLGP/ZP9AQokChdwaGFyb3MtYXRsYW50aWMtdGVzdG5ldBDMme3gzryvtN8BChoKDnBoYXJvcy1tYWlubmV0EMjBh571782hbAobCg5wbGFzbWEtbWFpbm5ldBD4m/HR2snVxoEBChoKDnBsYXNtYS10ZXN0bmV0ENWbv6XDtJmHNwobCg9wb2x5Z29uLW1haW5uZXQQsavk8JqShp04CiEKFHBvbHlnb24tdGVzdG5ldC1hbW95EM2P1t/xx5D64QEKJAoYcHJpdmF0ZS10ZXN0bmV0LWFuZGVzaXRlENSmmKXBj9z8XwoZCg1zb25pYy1tYWlubmV0ENGy5e3ZoLKdFwoZCg1zb25pYy10ZXN0bmV0EMiI+9S0xvq8GAoYCgt0YWMtdGVzdG5ldBDV243j+5+T14MBChsKDnhsYXllci10ZXN0bmV0EMm+obStzLzdjQFC5QEKJ2NvbS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYUILQ2xpZW50UHJvdG9QAaICA0NCRaoCI0NhcGFiaWxpdGllcy5CbG9ja2NoYWluLkV2bS5WMWFscGhhygIjQ2FwYWJpbGl0aWVzXEJsb2NrY2hhaW5cRXZtXFYxYWxwaGHiAi9DYXBhYmlsaXRpZXNcQmxvY2tjaGFpblxFdm1cVjFhbHBoYVxHUEJNZXRhZGF0YeoCJkNhcGFiaWxpdGllczo6QmxvY2tjaGFpbjo6RXZtOjpWMWFscGhhYgZwcm90bzM', + 'CjBjYXBhYmlsaXRpZXMvYmxvY2tjaGFpbi9ldm0vdjFhbHBoYS9jbGllbnQucHJvdG8SI2NhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhIh0KC1RvcGljVmFsdWVzEg4KBnZhbHVlcxgBIAMoDCK4AQoXRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QSEQoJYWRkcmVzc2VzGAEgAygMEkAKBnRvcGljcxgCIAMoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRvcGljVmFsdWVzEkgKCmNvbmZpZGVuY2UYAyABKA4yNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Db25maWRlbmNlTGV2ZWwiegoTQ2FsbENvbnRyYWN0UmVxdWVzdBI6CgRjYWxsGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZxInCgxibG9ja19udW1iZXIYAiABKAsyES52YWx1ZXMudjEuQmlnSW50IiEKEUNhbGxDb250cmFjdFJlcGx5EgwKBGRhdGEYASABKAwiWwoRRmlsdGVyTG9nc1JlcXVlc3QSRgoMZmlsdGVyX3F1ZXJ5GAEgASgLMjAuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyUXVlcnkiSQoPRmlsdGVyTG9nc1JlcGx5EjYKBGxvZ3MYASADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cixwEKA0xvZxIPCgdhZGRyZXNzGAEgASgMEg4KBnRvcGljcxgCIAMoDBIPCgd0eF9oYXNoGAMgASgMEhIKCmJsb2NrX2hhc2gYBCABKAwSDAoEZGF0YRgFIAEoDBIRCglldmVudF9zaWcYBiABKAwSJwoMYmxvY2tfbnVtYmVyGAcgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIQCgh0eF9pbmRleBgIIAEoDRINCgVpbmRleBgJIAEoDRIPCgdyZW1vdmVkGAogASgIIjEKB0NhbGxNc2cSDAoEZnJvbRgBIAEoDBIKCgJ0bxgCIAEoDBIMCgRkYXRhGAMgASgMIr0BCgtGaWx0ZXJRdWVyeRISCgpibG9ja19oYXNoGAEgASgMEiUKCmZyb21fYmxvY2sYAiABKAsyES52YWx1ZXMudjEuQmlnSW50EiMKCHRvX2Jsb2NrGAMgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIRCglhZGRyZXNzZXMYBCADKAwSOwoGdG9waWNzGAUgAygLMisuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuVG9waWNzIhcKBlRvcGljcxINCgV0b3BpYxgBIAMoDCJMChBCYWxhbmNlQXRSZXF1ZXN0Eg8KB2FjY291bnQYASABKAwSJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludCI0Cg5CYWxhbmNlQXRSZXBseRIiCgdiYWxhbmNlGAEgASgLMhEudmFsdWVzLnYxLkJpZ0ludCJPChJFc3RpbWF0ZUdhc1JlcXVlc3QSOQoDbXNnGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZyIjChBFc3RpbWF0ZUdhc1JlcGx5Eg8KA2dhcxgBIAEoBEICMAAiKwobR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXF1ZXN0EgwKBGhhc2gYASABKAwiYgoZR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXBseRJFCgt0cmFuc2FjdGlvbhgBIAEoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRyYW5zYWN0aW9uIqEBCgtUcmFuc2FjdGlvbhIRCgVub25jZRgBIAEoBEICMAASDwoDZ2FzGAIgASgEQgIwABIKCgJ0bxgDIAEoDBIMCgRkYXRhGAQgASgMEgwKBGhhc2gYBSABKAwSIAoFdmFsdWUYBiABKAsyES52YWx1ZXMudjEuQmlnSW50EiQKCWdhc19wcmljZRgHIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiLAocR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVxdWVzdBIMCgRoYXNoGAEgASgMIlsKGkdldFRyYW5zYWN0aW9uUmVjZWlwdFJlcGx5Ej0KB3JlY2VpcHQYASABKAsyLC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXB0IpkCCgdSZWNlaXB0EhIKBnN0YXR1cxgBIAEoBEICMAASFAoIZ2FzX3VzZWQYAiABKARCAjAAEhQKCHR4X2luZGV4GAMgASgEQgIwABISCgpibG9ja19oYXNoGAQgASgMEjYKBGxvZ3MYBiADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cSDwoHdHhfaGFzaBgHIAEoDBIuChNlZmZlY3RpdmVfZ2FzX3ByaWNlGAggASgLMhEudmFsdWVzLnYxLkJpZ0ludBInCgxibG9ja19udW1iZXIYCSABKAsyES52YWx1ZXMudjEuQmlnSW50EhgKEGNvbnRyYWN0X2FkZHJlc3MYCiABKAwiQAoVSGVhZGVyQnlOdW1iZXJSZXF1ZXN0EicKDGJsb2NrX251bWJlchgBIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiUgoTSGVhZGVyQnlOdW1iZXJSZXBseRI7CgZoZWFkZXIYASABKAsyKy5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5IZWFkZXIiawoGSGVhZGVyEhUKCXRpbWVzdGFtcBgBIAEoBEICMAASJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIMCgRoYXNoGAMgASgMEhMKC3BhcmVudF9oYXNoGAQgASgMIqsBChJXcml0ZVJlcG9ydFJlcXVlc3QSEAoIcmVjZWl2ZXIYASABKAwSKwoGcmVwb3J0GAIgASgLMhsuc2RrLnYxYWxwaGEuUmVwb3J0UmVzcG9uc2USRwoKZ2FzX2NvbmZpZxgDIAEoCzIuLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkdhc0NvbmZpZ0gAiAEBQg0KC19nYXNfY29uZmlnIiIKCUdhc0NvbmZpZxIVCglnYXNfbGltaXQYASABKARCAjAAIocDChBXcml0ZVJlcG9ydFJlcGx5EkAKCXR4X3N0YXR1cxgBIAEoDjItLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlR4U3RhdHVzEnUKInJlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXMYAiABKA4yRC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzSACIAQESFAoHdHhfaGFzaBgDIAEoDEgBiAEBEi8KD3RyYW5zYWN0aW9uX2ZlZRgEIAEoCzIRLnZhbHVlcy52MS5CaWdJbnRIAogBARIaCg1lcnJvcl9tZXNzYWdlGAUgASgJSAOIAQFCJQojX3JlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXNCCgoIX3R4X2hhc2hCEgoQX3RyYW5zYWN0aW9uX2ZlZUIQCg5fZXJyb3JfbWVzc2FnZSppCg9Db25maWRlbmNlTGV2ZWwSGQoVQ09ORklERU5DRV9MRVZFTF9TQUZFEAASGwoXQ09ORklERU5DRV9MRVZFTF9MQVRFU1QQARIeChpDT05GSURFTkNFX0xFVkVMX0ZJTkFMSVpFRBACKoIBCh9SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzEi4KKlJFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfU1VDQ0VTUxAAEi8KK1JFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfUkVWRVJURUQQASpOCghUeFN0YXR1cxITCg9UWF9TVEFUVVNfRkFUQUwQABIWChJUWF9TVEFUVVNfUkVWRVJURUQQARIVChFUWF9TVEFUVVNfU1VDQ0VTUxACMvgYCgZDbGllbnQSgAEKDENhbGxDb250cmFjdBI4LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkNhbGxDb250cmFjdFJlcXVlc3QaNi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5DYWxsQ29udHJhY3RSZXBseRJ6CgpGaWx0ZXJMb2dzEjYuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nc1JlcXVlc3QaNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5GaWx0ZXJMb2dzUmVwbHkSdwoJQmFsYW5jZUF0EjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQmFsYW5jZUF0UmVxdWVzdBozLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkJhbGFuY2VBdFJlcGx5En0KC0VzdGltYXRlR2FzEjcuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXF1ZXN0GjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXBseRKYAQoUR2V0VHJhbnNhY3Rpb25CeUhhc2gSQC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcXVlc3QaPi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcGx5EpsBChVHZXRUcmFuc2FjdGlvblJlY2VpcHQSQS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvblJlY2VpcHRSZXF1ZXN0Gj8uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVwbHkShgEKDkhlYWRlckJ5TnVtYmVyEjouY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXF1ZXN0GjguY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXBseRJ2CgpMb2dUcmlnZ2VyEjwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QaKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cwARJ9CgtXcml0ZVJlcG9ydBI3LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVxdWVzdBo1LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVwbHkavQ+CtRi4DwgBEglldm1AMS4wLjAaqA8KDUNoYWluU2VsZWN0b3ISlg8Skw8KFwoLYWRpLW1haW5uZXQQ/PDmwrfn3ao4ChgKC2FkaS10ZXN0bmV0EP2myPr5hozaggEKJAoXYXBlY2hhaW4tdGVzdG5ldC1jdXJ0aXMQwcO0+I3EkrKJAQoXCgthcmMtdGVzdG5ldBDnxoye19fQjSoKHQoRYXZhbGFuY2hlLW1haW5uZXQQ1eeKwOHVmKRZCiMKFmF2YWxhbmNoZS10ZXN0bmV0LWZ1amkQm/n8kKLjqPjMAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLW1haW5uZXQQz/eU8djtlbidAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLXRlc3RuZXQQ+62+nICu5Iq4AQoYCgxjZWxvLW1haW5uZXQQhtTo2IaTiNcSChgKDGNlbG8tc2Vwb2xpYRDEw/yL/OedmjQKGgoOY3Jvbm9zLXRlc3RuZXQQ/dnureDe2sgpCiIKFWR0Y2MtdGVzdG5ldC1hbmRlc2l0ZRDSg+PQmZblpNcBChwKEGV0aGVyZXVtLW1haW5uZXQQlfbx5M+ypsJFCicKG2V0aGVyZXVtLW1haW5uZXQtYXJiaXRydW0tMRDE6I3Njpuh10QKJAoXZXRoZXJldW0tbWFpbm5ldC1iYXNlLTEQgv+rov65kNPdAQoiChZldGhlcmV1bS1tYWlubmV0LWluay0xEKCwpum35qqEMAokChhldGhlcmV1bS1tYWlubmV0LWxpbmVhLTEQtrrpmMu9sJtACiUKGWV0aGVyZXVtLW1haW5uZXQtbWFudGxlLTEQiue0leewg8wVCicKG2V0aGVyZXVtLW1haW5uZXQtb3B0aW1pc20tMRC4lY/D9/7Q6TMKJgoZZXRoZXJldW0tbWFpbm5ldC1zY3JvbGwtMRC4vOTrxL7In7cBCikKHWV0aGVyZXVtLW1haW5uZXQtd29ybGRjaGFpbi0xEIfvurfFtsK4HAolChlldGhlcmV1bS1tYWlubmV0LXhsYXllci0xEJal/JymqO/tKQolChlldGhlcmV1bS1tYWlubmV0LXprc3luYy0xEJTul9nttLHXFQolChhldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEQ2bXkzvzJ7qDeAQovCiNldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtYXJiaXRydW0tMRDqzu7/6raEozAKLAofZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLWJhc2UtMRC4yrnv9pCuyI8BCiwKIGV0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1saW5lYS0xEOuq1P6C+eavTwotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtbWFudGxlLTEQ1ca47s328qZyCi8KI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1vcHRpbWlzbS0xEJ+GxaG+2MPASAotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtc2Nyb2xsLTEQi+m0vtu67dEfCjAKI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS11bmljaGFpbi0xELTe/uDsl6mWxAEKMQolZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXdvcmxkY2hhaW4tMRC63+DFx6nzxUkKLQohZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXprc3luYy0xELfB/P3yxIDeXwogChRnbm9zaXNfY2hhaW4tbWFpbm5ldBD0kq3a8qKuugYKJwobZ25vc2lzX2NoYWluLXRlc3RuZXQtY2hpYWRvELOxgtCbpY+PewofChNoeXBlcmxpcXVpZC1tYWlubmV0EKez+N3O0enyIQofChNoeXBlcmxpcXVpZC10ZXN0bmV0EIjO3ciX4Mm9OwogChNpbmstdGVzdG5ldC1zZXBvbGlhEOj0p6Xz5pbAhwEKGQoNam92YXktbWFpbm5ldBC1w8SaoYDfkhUKGQoNam92YXktdGVzdG5ldBDkz4qE3rLejg0KGwoPbWVnYWV0aC1tYWlubmV0EOqVtsi85KbIVAoeChFtZWdhZXRoLXRlc3RuZXQtMhDjjd6IsY/9k/0BCiQKF3BoYXJvcy1hdGxhbnRpYy10ZXN0bmV0EMyZ7eDOvK+03wEKGgoOcGhhcm9zLW1haW5uZXQQyMGHnvXvzaFsChsKDnBsYXNtYS1tYWlubmV0EPib8dHaydXGgQEKGgoOcGxhc21hLXRlc3RuZXQQ1Zu/pcO0mYc3ChsKD3BvbHlnb24tbWFpbm5ldBCxq+TwmpKGnTgKIQoUcG9seWdvbi10ZXN0bmV0LWFtb3kQzY/W3/HHkPrhAQokChhwcml2YXRlLXRlc3RuZXQtYW5kZXNpdGUQ1KaYpcGP3PxfCiQKGHByaXZhdGUtdGVzdG5ldC1yaHlvbGl0ZRCB+onr4bTbsQgKGQoNc29uaWMtbWFpbm5ldBDRsuXt2aCynRcKGQoNc29uaWMtdGVzdG5ldBDIiPvUtMb6vBgKGAoLdGFjLXRlc3RuZXQQ1duN4/ufk9eDAQobCg54bGF5ZXItdGVzdG5ldBDJvqG0rcy83Y0BQuUBCidjb20uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGFCC0NsaWVudFByb3RvUAGiAgNDQkWqAiNDYXBhYmlsaXRpZXMuQmxvY2tjaGFpbi5Fdm0uVjFhbHBoYcoCI0NhcGFiaWxpdGllc1xCbG9ja2NoYWluXEV2bVxWMWFscGhh4gIvQ2FwYWJpbGl0aWVzXEJsb2NrY2hhaW5cRXZtXFYxYWxwaGFcR1BCTWV0YWRhdGHqAiZDYXBhYmlsaXRpZXM6OkJsb2NrY2hhaW46OkV2bTo6VjFhbHBoYWIGcHJvdG8z', [file_sdk_v1alpha_sdk, file_tools_generator_v1alpha_cre_metadata, file_values_v1_values], ) diff --git a/packages/cre-sdk/src/generated/capabilities/blockchain/solana/v1alpha/client_pb.ts b/packages/cre-sdk/src/generated/capabilities/blockchain/solana/v1alpha/client_pb.ts index f687ae02..fe57fc57 100644 --- a/packages/cre-sdk/src/generated/capabilities/blockchain/solana/v1alpha/client_pb.ts +++ b/packages/cre-sdk/src/generated/capabilities/blockchain/solana/v1alpha/client_pb.ts @@ -15,7 +15,7 @@ import { file_tools_generator_v1alpha_cre_metadata } from '../../../../tools/gen export const file_capabilities_blockchain_solana_v1alpha_client: GenFile = /*@__PURE__*/ fileDesc( - 'CjNjYXBhYmlsaXRpZXMvYmxvY2tjaGFpbi9zb2xhbmEvdjFhbHBoYS9jbGllbnQucHJvdG8SJmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhIiYKDUNvbXB1dGVDb25maWcSFQoNY29tcHV0ZV9saW1pdBgBIAEoDSI2CgtBY2NvdW50TWV0YRISCgpwdWJsaWNfa2V5GAEgASgMEhMKC2lzX3dyaXRhYmxlGAIgASgIIosCChJXcml0ZVJlcG9ydFJlcXVlc3QSTwoScmVtYWluaW5nX2FjY291bnRzGAEgAygLMjMuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uc29sYW5hLnYxYWxwaGEuQWNjb3VudE1ldGESEAoIcmVjZWl2ZXIYAiABKAwSUgoOY29tcHV0ZV9jb25maWcYAyABKAsyNS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5zb2xhbmEudjFhbHBoYS5Db21wdXRlQ29uZmlnSACIAQESKwoGcmVwb3J0GAQgASgLMhsuc2RrLnYxYWxwaGEuUmVwb3J0UmVzcG9uc2VCEQoPX2NvbXB1dGVfY29uZmlnIogDChBXcml0ZVJlcG9ydFJlcGx5EkMKCXR4X3N0YXR1cxgBIAEoDjIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhLlR4U3RhdHVzEngKInJlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXMYAiABKA4yRy5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5zb2xhbmEudjFhbHBoYS5SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzSACIAQESGQoMdHhfc2lnbmF0dXJlGAMgASgMSAGIAQESIAoPdHJhbnNhY3Rpb25fZmVlGAQgASgEQgIwAEgCiAEBEhoKDWVycm9yX21lc3NhZ2UYBSABKAlIA4gBAUIlCiNfcmVjZWl2ZXJfY29udHJhY3RfZXhlY3V0aW9uX3N0YXR1c0IPCg1fdHhfc2lnbmF0dXJlQhIKEF90cmFuc2FjdGlvbl9mZWVCEAoOX2Vycm9yX21lc3NhZ2UqTQoIVHhTdGF0dXMSEwoPVFhfU1RBVFVTX0ZBVEFMEAASFQoRVFhfU1RBVFVTX0FCT1JURUQQARIVChFUWF9TVEFUVVNfU1VDQ0VTUxACKoIBCh9SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzEi4KKlJFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfU1VDQ0VTUxAAEi8KK1JFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfUkVWRVJURUQQATLVAQoGQ2xpZW50EoMBCgtXcml0ZVJlcG9ydBI6LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhLldyaXRlUmVwb3J0UmVxdWVzdBo4LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhLldyaXRlUmVwb3J0UmVwbHkaRYK1GEEIARIMc29sYW5hQDEuMC4wGi8KDUNoYWluU2VsZWN0b3ISHhIcChoKDXNvbGFuYS1kZXZuZXQQ3++Mp6n8sfbjAUL0AQoqY29tLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhQgtDbGllbnRQcm90b1ABogIDQ0JTqgImQ2FwYWJpbGl0aWVzLkJsb2NrY2hhaW4uU29sYW5hLlYxYWxwaGHKAiZDYXBhYmlsaXRpZXNcQmxvY2tjaGFpblxTb2xhbmFcVjFhbHBoYeICMkNhcGFiaWxpdGllc1xCbG9ja2NoYWluXFNvbGFuYVxWMWFscGhhXEdQQk1ldGFkYXRh6gIpQ2FwYWJpbGl0aWVzOjpCbG9ja2NoYWluOjpTb2xhbmE6OlYxYWxwaGFiBnByb3RvMw', + 'CjNjYXBhYmlsaXRpZXMvYmxvY2tjaGFpbi9zb2xhbmEvdjFhbHBoYS9jbGllbnQucHJvdG8SJmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhIiYKDUNvbXB1dGVDb25maWcSFQoNY29tcHV0ZV9saW1pdBgBIAEoDSI2CgtBY2NvdW50TWV0YRISCgpwdWJsaWNfa2V5GAEgASgMEhMKC2lzX3dyaXRhYmxlGAIgASgIIosCChJXcml0ZVJlcG9ydFJlcXVlc3QSTwoScmVtYWluaW5nX2FjY291bnRzGAEgAygLMjMuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uc29sYW5hLnYxYWxwaGEuQWNjb3VudE1ldGESEAoIcmVjZWl2ZXIYAiABKAwSUgoOY29tcHV0ZV9jb25maWcYAyABKAsyNS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5zb2xhbmEudjFhbHBoYS5Db21wdXRlQ29uZmlnSACIAQESKwoGcmVwb3J0GAQgASgLMhsuc2RrLnYxYWxwaGEuUmVwb3J0UmVzcG9uc2VCEQoPX2NvbXB1dGVfY29uZmlnIogDChBXcml0ZVJlcG9ydFJlcGx5EkMKCXR4X3N0YXR1cxgBIAEoDjIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhLlR4U3RhdHVzEngKInJlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXMYAiABKA4yRy5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5zb2xhbmEudjFhbHBoYS5SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzSACIAQESGQoMdHhfc2lnbmF0dXJlGAMgASgMSAGIAQESIAoPdHJhbnNhY3Rpb25fZmVlGAQgASgEQgIwAEgCiAEBEhoKDWVycm9yX21lc3NhZ2UYBSABKAlIA4gBAUIlCiNfcmVjZWl2ZXJfY29udHJhY3RfZXhlY3V0aW9uX3N0YXR1c0IPCg1fdHhfc2lnbmF0dXJlQhIKEF90cmFuc2FjdGlvbl9mZWVCEAoOX2Vycm9yX21lc3NhZ2UqTQoIVHhTdGF0dXMSEwoPVFhfU1RBVFVTX0ZBVEFMEAASFQoRVFhfU1RBVFVTX0FCT1JURUQQARIVChFUWF9TVEFUVVNfU1VDQ0VTUxACKoIBCh9SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzEi4KKlJFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfU1VDQ0VTUxAAEi8KK1JFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfUkVWRVJURUQQATLxAQoGQ2xpZW50EoMBCgtXcml0ZVJlcG9ydBI6LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhLldyaXRlUmVwb3J0UmVxdWVzdBo4LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLnNvbGFuYS52MWFscGhhLldyaXRlUmVwb3J0UmVwbHkaYYK1GF0IARIMc29sYW5hQDEuMC4wGksKDUNoYWluU2VsZWN0b3ISOhI4ChoKDXNvbGFuYS1kZXZuZXQQ3++Mp6n8sfbjAQoaCg5zb2xhbmEtbWFpbm5ldBDnk9+Mtp+u3QFC9AEKKmNvbS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5zb2xhbmEudjFhbHBoYUILQ2xpZW50UHJvdG9QAaICA0NCU6oCJkNhcGFiaWxpdGllcy5CbG9ja2NoYWluLlNvbGFuYS5WMWFscGhhygImQ2FwYWJpbGl0aWVzXEJsb2NrY2hhaW5cU29sYW5hXFYxYWxwaGHiAjJDYXBhYmlsaXRpZXNcQmxvY2tjaGFpblxTb2xhbmFcVjFhbHBoYVxHUEJNZXRhZGF0YeoCKUNhcGFiaWxpdGllczo6QmxvY2tjaGFpbjo6U29sYW5hOjpWMWFscGhhYgZwcm90bzM', [file_sdk_v1alpha_sdk, file_tools_generator_v1alpha_cre_metadata], ) diff --git a/submodules/chainlink-protos b/submodules/chainlink-protos index faea187e..5f00275c 160000 --- a/submodules/chainlink-protos +++ b/submodules/chainlink-protos @@ -1 +1 @@ -Subproject commit faea187e6997e00852cad8046c09b7b2d0f1e128 +Subproject commit 5f00275cf10d7d590cad6caa74988d90630d7ca6 From 2f367a7aa6756c48001559e8263032d6cdb4b86e Mon Sep 17 00:00:00 2001 From: ernest-nowacki Date: Thu, 11 Jun 2026 14:29:00 +0200 Subject: [PATCH 05/20] Non code change --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 80466b76..efa55eec 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Chainlink CRE SDK for TypeScript +# Chainlink CRE SDK for TypeScript. Build decentralized workflows that combine off-chain computation with on-chain execution using the Chainlink Runtime Environment (CRE) SDK. @@ -270,23 +270,23 @@ bun generate:sdk # Generate all SDK types and code The [`breaking-changes`](./.github/workflows/breaking-changes.yml) workflow blocks PRs that alter any of the three protected contracts. If your change is intentional, update the relevant baseline before pushing: -| Contract | What triggers the failure | How to update | -|---|---|---| -| Proto fields | Field deleted, renamed, renumbered, or type changed | No baseline file — CI runs `buf breaking` on `submodules/chainlink-protos` (`cre` module) against the submodule commit pinned on `main` | -| TypeScript public API | An exported type/interface was removed or changed | Run `bun run update-api-baseline` inside `packages/cre-sdk` and commit `api-baseline.d.ts` | -| JS host binding names | A binding was added, removed, or renamed in `host-bindings.ts` | Run `bun test --update-snapshots` inside `packages/cre-sdk` and commit the updated `__snapshots__` file | -| Rust host imports | An `extern "C"` import was added or removed in `lib.rs` | Re-run the sed extraction (see `breaking-changes.yml`) and commit `host-imports-baseline.txt` | +| Contract | What triggers the failure | How to update | +| --------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| Proto fields | Field deleted, renamed, renumbered, or type changed | No baseline file — CI runs `buf breaking` on `submodules/chainlink-protos` (`cre` module) against the submodule commit pinned on `main` | +| TypeScript public API | An exported type/interface was removed or changed | Run `bun run update-api-baseline` inside `packages/cre-sdk` and commit `api-baseline.d.ts` | +| JS host binding names | A binding was added, removed, or renamed in `host-bindings.ts` | Run `bun test --update-snapshots` inside `packages/cre-sdk` and commit the updated `__snapshots__` file | +| Rust host imports | An `extern "C"` import was added or removed in `lib.rs` | Re-run the sed extraction (see `breaking-changes.yml`) and commit `host-imports-baseline.txt` | #### CI override labels When a change is **intentionally** breaking and cannot be fixed by updating a baseline (e.g. a coordinated proto break before `main` catches up), a **maintainer** adds the matching label on the PR (this re-runs the workflow): -| Label | Skips | -|-------|--------| -| `breaking-change:proto` | `proto-breaking` (`buf breaking`) | -| `breaking-change:typescript-api` | `ts-api-surface` | -| `breaking-change:host-bindings` | `host-bindings` | -| `breaking-change:approved` | All three jobs | +| Label | Skips | +| -------------------------------- | --------------------------------- | +| `breaking-change:proto` | `proto-breaking` (`buf breaking`) | +| `breaking-change:typescript-api` | `ts-api-surface` | +| `breaking-change:host-bindings` | `host-bindings` | +| `breaking-change:approved` | All three jobs | Labels are an audit trail in the PR timeline, not a substitute for review. Prefer updating baselines (`api-baseline.d.ts`, snapshots, `host-imports-baseline.txt`) when the new contract is the new source of truth. For proto breaks, coordinate the `chainlink-protos` submodule bump and document migration notes in the PR. From 7d8a46432b32df454d783f5eddf987ae849b4e87 Mon Sep 17 00:00:00 2001 From: ernest-nowacki Date: Thu, 11 Jun 2026 15:13:58 +0200 Subject: [PATCH 06/20] Improve test templates script --- scripts/test-templates.sh | 53 ++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/scripts/test-templates.sh b/scripts/test-templates.sh index 26f91fc3..c5929406 100755 --- a/scripts/test-templates.sh +++ b/scripts/test-templates.sh @@ -209,6 +209,49 @@ TOTAL=${#ALL_PKGS[@]} info "Found $TOTAL TypeScript templates to test." info "" +# -------------------------------------------------------------------------- +# 3b. Pre-install dependencies for every package +# +# Some templates typecheck files imported from sibling packages (e.g. +# por/workflow.ts imports ../contracts/evm/ts/generated/*). Those imports +# only resolve if the sibling package's dependencies are installed too. +# Install every package up front — including packages that aren't templates +# themselves — so results don't depend on the order templates are processed. + +info "Pre-installing dependencies for all packages..." + +while IFS= read -r pkg; do + PKG_DIR=$(dirname "$pkg") + cd "$PKG_DIR" + + # Track lock files for restoration + for lockfile in package-lock.json bun.lock; do + if [ -f "$lockfile" ]; then + cp "$lockfile" "$lockfile.bak" + LOCKFILE_BACKUPS+=("$PKG_DIR/$lockfile.bak:$PKG_DIR/$lockfile") + else + GENERATED_FILES+=("$PKG_DIR/$lockfile") + fi + done + + _out="" + if ! run_captured _out bun install; then + # Failures in actual templates are caught and reported in the main loop. + vlog " ⚠️ bun install failed in $PKG_DIR" + continue + fi + # Inject the local SDK tarball so sibling files typecheck against the + # SDK under test rather than the published version. + if grep -q '"@chainlink/cre-sdk"' package.json; then + run_captured _out bun install --no-save "@chainlink/cre-sdk@file:$TARBALL_PATH" || \ + vlog " ⚠️ SDK tarball install failed in $PKG_DIR" + fi +done < <(/usr/bin/find "$TEMPLATES_ABS_PATH" -name "package.json" -not -path "*/node_modules/*") + +cd "$MONOREPO_ROOT" +info "✅ Dependencies pre-installed." +info "" + # -------------------------------------------------------------------------- # 4. Test each template @@ -233,15 +276,7 @@ for pkg in "${ALL_PKGS[@]}"; do cd "$WORKFLOW_DIR" - # Track lock files for restoration - for lockfile in package-lock.json bun.lock; do - if [ -f "$lockfile" ]; then - cp "$lockfile" "$lockfile.bak" - LOCKFILE_BACKUPS+=("$WORKFLOW_DIR/$lockfile.bak:$WORKFLOW_DIR/$lockfile") - else - GENERATED_FILES+=("$WORKFLOW_DIR/$lockfile") - fi - done + # Lock file backups are handled by the pre-install pass above. # Install dependencies vlog " Installing dependencies..." From 0e58503cdedde2e0a182b521dc20efcdde9158f3 Mon Sep 17 00:00:00 2001 From: ernest-nowacki Date: Thu, 11 Jun 2026 15:21:55 +0200 Subject: [PATCH 07/20] Remove dummy change from readme - no longer needed --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index efa55eec..fc6ec490 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Chainlink CRE SDK for TypeScript. +# Chainlink CRE SDK for TypeScript Build decentralized workflows that combine off-chain computation with on-chain execution using the Chainlink Runtime Environment (CRE) SDK. From 5df76f4e518cd2cd4dbcfa65353f47d57210d9be Mon Sep 17 00:00:00 2001 From: Ernest Nowacki <124677192+ernest-nowacki@users.noreply.github.com> Date: Thu, 11 Jun 2026 17:25:56 +0200 Subject: [PATCH 08/20] Set new versions for the release (#270) --- packages/cre-sdk-examples/package.json | 2 +- packages/cre-sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cre-sdk-examples/package.json b/packages/cre-sdk-examples/package.json index a051e179..973e0da8 100644 --- a/packages/cre-sdk-examples/package.json +++ b/packages/cre-sdk-examples/package.json @@ -1,7 +1,7 @@ { "name": "@chainlink/cre-sdk-examples", "private": true, - "version": "1.10.0", + "version": "1.11.0", "type": "module", "author": "Ernest Nowacki", "license": "BUSL-1.1", diff --git a/packages/cre-sdk/package.json b/packages/cre-sdk/package.json index 7ab00202..8ecf5f95 100644 --- a/packages/cre-sdk/package.json +++ b/packages/cre-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/cre-sdk", - "version": "1.10.0", + "version": "1.11.0", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", From dad0dc703c0984b77bb27ba3f6fbe7a245a7dd57 Mon Sep 17 00:00:00 2001 From: Matthew Pendrey Date: Wed, 10 Jun 2026 11:37:44 +0100 Subject: [PATCH 09/20] updates to support typed errors --- packages/cre-sdk/Makefile | 2 +- .../src/capabilities/errors/error-codes.ts | 106 +++++++++++++ .../errors/error-serialization.test.ts | 144 ++++++++++++++++++ .../errors/error-serialization.ts | 23 +++ .../cre-sdk/src/capabilities/errors/error.ts | 92 +++++++++++ .../cre-sdk/src/capabilities/errors/index.ts | 3 + packages/cre-sdk/src/sdk/errors.ts | 3 + .../cre-sdk/src/sdk/impl/runtime-impl.test.ts | 66 ++++++-- packages/cre-sdk/src/sdk/impl/runtime-impl.ts | 21 +-- packages/cre-sdk/src/sdk/index.ts | 1 + .../src/sdk/testutils/test-runtime.test.ts | 5 +- .../utils/capabilities/capability-error.ts | 4 + .../standard_tests/capability_errors/test.ts | 44 ++++++ packages/cre-sdk/tsconfig.build.json | 2 +- 14 files changed, 481 insertions(+), 35 deletions(-) create mode 100644 packages/cre-sdk/src/capabilities/errors/error-codes.ts create mode 100644 packages/cre-sdk/src/capabilities/errors/error-serialization.test.ts create mode 100644 packages/cre-sdk/src/capabilities/errors/error-serialization.ts create mode 100644 packages/cre-sdk/src/capabilities/errors/error.ts create mode 100644 packages/cre-sdk/src/capabilities/errors/index.ts create mode 100644 packages/cre-sdk/src/standard_tests/capability_errors/test.ts diff --git a/packages/cre-sdk/Makefile b/packages/cre-sdk/Makefile index c166c42b..a47f8dfa 100644 --- a/packages/cre-sdk/Makefile +++ b/packages/cre-sdk/Makefile @@ -1,4 +1,4 @@ -COMMON_VERSION ?= cre-std-tests@0.6.0 +COMMON_VERSION ?= cre-std-tests@0.8.0 MODULE := github.com/smartcontractkit/chainlink-common TEST_PATTERN ?= ^TestStandard diff --git a/packages/cre-sdk/src/capabilities/errors/error-codes.ts b/packages/cre-sdk/src/capabilities/errors/error-codes.ts new file mode 100644 index 00000000..39a9c9dc --- /dev/null +++ b/packages/cre-sdk/src/capabilities/errors/error-codes.ts @@ -0,0 +1,106 @@ +/** + * Capability error codes are primarily based on gRPC error codes: + * https://grpc.github.io/grpc/core/md_doc_statuscodes.html + * Custom error codes specific to this project should start from 100 to avoid + * conflicts with future gRPC codes. Note: 0 (OK) is intentionally excluded + * because capability errors must always indicate a failure condition. + */ +export type ErrorCode = number + +/** Sentinel for error code strings that are not recognised. */ +export const UnrecognisedErrorCode = -1 + +/** Indicates the operation was canceled (typically by the caller). */ +export const Canceled = 1 + +/** Unknown error. */ +export const Unknown = 2 + +/** Client specified an invalid argument. */ +export const InvalidArgument = 3 + +/** Operation expired before completion. */ +export const DeadlineExceeded = 4 + +/** Some requested entity was not found. */ +export const NotFound = 5 + +/** An attempt to create an entity failed because one already exists. */ +export const AlreadyExists = 6 + +/** The caller does not have permission to execute the specified operation. */ +export const PermissionDenied = 7 + +/** Some resource has been exhausted. */ +export const ResourceExhausted = 8 + +/** Operation was rejected because the system is not in a state required for execution. */ +export const FailedPrecondition = 9 + +/** The operation was aborted, typically due to a concurrency issue. */ +export const Aborted = 10 + +/** Operation was attempted past the valid range. */ +export const OutOfRange = 11 + +/** Operation is not implemented or not supported/enabled in this service. */ +export const Unimplemented = 12 + +/** Some invariants expected by the underlying system have been broken. */ +export const Internal = 13 + +/** The service is currently unavailable. */ +export const Unavailable = 14 + +/** Unrecoverable data loss or corruption. */ +export const DataLoss = 15 + +/** The request does not have valid authentication credentials. */ +export const Unauthenticated = 16 + +/** Custom error codes not defined in the gRPC error space. */ + +/** Failure to reach consensus. */ +export const ConsensusFailed = 100 + +/** A CRE limit breach has occurred. */ +export const LimitExceeded = 101 + +/** Not enough observations to enable the operation to complete. */ +export const InsufficientObservations = 102 + +const errorCodeToString = new Map([ + [Canceled, 'Canceled'], + [Unknown, 'Unknown'], + [InvalidArgument, 'InvalidArgument'], + [DeadlineExceeded, 'DeadlineExceeded'], + [NotFound, 'NotFound'], + [AlreadyExists, 'AlreadyExists'], + [PermissionDenied, 'PermissionDenied'], + [ResourceExhausted, 'ResourceExhausted'], + [FailedPrecondition, 'FailedPrecondition'], + [Aborted, 'Aborted'], + [OutOfRange, 'OutOfRange'], + [Unimplemented, 'Unimplemented'], + [Internal, 'Internal'], + [Unavailable, 'Unavailable'], + [DataLoss, 'DataLoss'], + [Unauthenticated, 'Unauthenticated'], + [ConsensusFailed, 'ConsensusFailed'], + [LimitExceeded, 'LimitExceeded'], + [InsufficientObservations, 'InsufficientObservations'], +]) + +const stringToErrorCode = new Map( + Array.from(errorCodeToString.entries()).map(([code, name]) => [name, code]), +) + +/** Returns the string representation of the error code. */ +export function errorCodeToStringValue(code: ErrorCode): string { + return errorCodeToString.get(code) ?? 'UnrecognisedErrorCode' +} + +/** Converts a string to an error code. */ +export function fromErrorCodeString(str: string): ErrorCode { + return stringToErrorCode.get(str) ?? UnrecognisedErrorCode +} diff --git a/packages/cre-sdk/src/capabilities/errors/error-serialization.test.ts b/packages/cre-sdk/src/capabilities/errors/error-serialization.test.ts new file mode 100644 index 00000000..f472b6fb --- /dev/null +++ b/packages/cre-sdk/src/capabilities/errors/error-serialization.test.ts @@ -0,0 +1,144 @@ +import { describe, expect, test } from 'bun:test' +import { + CapabilityExecutionError, + fromOriginString, + fromVisibilityString, + isCapabilityExecutionError, + OriginUser, + VisibilityPublic, +} from './error' +import { + DeadlineExceeded, + fromErrorCodeString, + Unknown, + UnrecognisedErrorCode, +} from './error-codes' +import { deserializeErrorFromString } from './error-serialization' + +function requireCapabilityError(err: unknown): CapabilityExecutionError { + expect(isCapabilityExecutionError(err)).toBe(true) + return err as CapabilityExecutionError +} + +function capabilityErrorsEqual(a: CapabilityExecutionError, b: CapabilityExecutionError): boolean { + return ( + a.code === b.code && + a.origin === b.origin && + a.visibility === b.visibility && + a.toString() === b.toString() + ) +} + +describe('deserializeErrorFromString invalid fields', () => { + test('InvalidVisibility', () => { + const serializedError = 'InvalidVisibility:User:Unknown:some error occurred' + const deserializedErr = requireCapabilityError(deserializeErrorFromString(serializedError)) + const expectedErr = new CapabilityExecutionError( + 'some error occurred', + fromVisibilityString('InvalidVisibility'), + OriginUser, + Unknown, + ) + expect(capabilityErrorsEqual(deserializedErr, expectedErr)).toBe(true) + expect(fromVisibilityString('InvalidVisibility')).toBe(-1) + }) + + test('InvalidOrigin', () => { + const serializedError = 'Public:InvalidOrigin:Unknown:some error occurred' + const deserializedErr = requireCapabilityError(deserializeErrorFromString(serializedError)) + const expectedErr = new CapabilityExecutionError( + 'some error occurred', + VisibilityPublic, + fromOriginString('InvalidOrigin'), + Unknown, + ) + expect(capabilityErrorsEqual(deserializedErr, expectedErr)).toBe(true) + expect(fromOriginString('InvalidOrigin')).toBe(-1) + }) + + test('UnrecognisedErrorCode', () => { + const serializedError = 'Public:System:NewUnknownErrorCode:some error occurred' + const deserializedErr = requireCapabilityError(deserializeErrorFromString(serializedError)) + const expectedErr = new CapabilityExecutionError( + 'some error occurred', + VisibilityPublic, + 0, + UnrecognisedErrorCode, + ) + expect(capabilityErrorsEqual(deserializedErr, expectedErr)).toBe(true) + expect(fromErrorCodeString('NewUnknownErrorCode')).toBe(UnrecognisedErrorCode) + expect(deserializedErr.toString()).toBe('some error occurred') + }) + + test('ColonRichPlainMessageMatchingUnknownCode', () => { + const msg = 'failed:attempt:Unknown: details here' + const deserializedErr = requireCapabilityError(deserializeErrorFromString(msg)) + const expectedErr = new CapabilityExecutionError( + ' details here', + fromVisibilityString('failed'), + fromOriginString('attempt'), + Unknown, + ) + expect(capabilityErrorsEqual(deserializedErr, expectedErr)).toBe(true) + }) +}) + +describe('deserializeErrorFromString non wire format', () => { + test('InsufficientFields', () => { + const msg = 'Public:System:Unknown' + const err = deserializeErrorFromString(msg) + expect(err.message).toBe(msg) + expect(isCapabilityExecutionError(err)).toBe(false) + }) + + test('PlainMessage', () => { + const msg = 'some error has occurred that is not in the serialized capability error format' + const err = deserializeErrorFromString(msg) + expect(err.message).toBe(msg) + expect(isCapabilityExecutionError(err)).toBe(false) + }) +}) + +describe('deserializeErrorFromString that is not serialised capability error', () => { + test('plain message returns stdlib error', () => { + const msg = 'some plain failure' + const err = deserializeErrorFromString(msg) + expect(err.message).toBe(msg) + expect(isCapabilityExecutionError(err)).toBe(false) + }) + + test('valid serialized still returns capability error', () => { + const serialized = 'Public:User:DeadlineExceeded:detail' + const expected = new CapabilityExecutionError('detail', VisibilityPublic, OriginUser, DeadlineExceeded) + const err = deserializeErrorFromString(serialized) + const deserialized = requireCapabilityError(err) + expect(capabilityErrorsEqual(expected, deserialized)).toBe(true) + }) + + test('detail with colons preserves colons', () => { + const detail = 'failed at step: 1: connection refused' + const serialized = `Public:User:DeadlineExceeded:${detail}` + const expected = new CapabilityExecutionError(detail, VisibilityPublic, OriginUser, DeadlineExceeded) + const err = deserializeErrorFromString(serialized) + const deserialized = requireCapabilityError(err) + expect(capabilityErrorsEqual(expected, deserialized)).toBe(true) + expect(deserialized.detail).toBe(detail) + }) +}) + +describe('CapabilityExecutionError formatting', () => { + test('recognised code includes code prefix', () => { + const err = new CapabilityExecutionError('detail', VisibilityPublic, OriginUser, DeadlineExceeded) + expect(err.toString()).toBe('[4]DeadlineExceeded: detail') + }) + + test('unrecognised code returns detail only', () => { + const err = new CapabilityExecutionError( + 'detail', + VisibilityPublic, + OriginUser, + UnrecognisedErrorCode, + ) + expect(err.toString()).toBe('detail') + }) +}) diff --git a/packages/cre-sdk/src/capabilities/errors/error-serialization.ts b/packages/cre-sdk/src/capabilities/errors/error-serialization.ts new file mode 100644 index 00000000..2c078c58 --- /dev/null +++ b/packages/cre-sdk/src/capabilities/errors/error-serialization.ts @@ -0,0 +1,23 @@ +import { CapabilityExecutionError, fromOriginString, fromVisibilityString } from './error' +import { fromErrorCodeString } from './error-codes' + +const errorMessageSeparator = ':' + +/** + * Parses errorMsg in the capability error wire format. + * If errorMsg is not a serialized capability error, a plain Error is returned. + */ +export function deserializeErrorFromString(errorMsg: string): Error { + const parts = errorMsg.split(errorMessageSeparator) + + if (parts.length < 4) { + return new Error(errorMsg) + } + + const visibility = fromVisibilityString(parts[0]) + const origin = fromOriginString(parts[1]) + const errorCode = fromErrorCodeString(parts[2]) + const detail = parts.slice(3).join(errorMessageSeparator) + + return new CapabilityExecutionError(detail, visibility, origin, errorCode) +} diff --git a/packages/cre-sdk/src/capabilities/errors/error.ts b/packages/cre-sdk/src/capabilities/errors/error.ts new file mode 100644 index 00000000..28688d3d --- /dev/null +++ b/packages/cre-sdk/src/capabilities/errors/error.ts @@ -0,0 +1,92 @@ +/** + * CRE capability errors and wire format shared across CRE components. + * Aligns with github.com/smartcontractkit/chainlink-common/pkg/capabilities/errors. + */ +import { type ErrorCode, errorCodeToStringValue, UnrecognisedErrorCode } from './error-codes' + +export type Origin = number + +/** The error originated from a system issue. */ +export const OriginSystem = 0 + +/** The error originated from user input or action. */ +export const OriginUser = 1 + +export function originToString(origin: Origin): string { + switch (origin) { + case OriginSystem: + return 'System' + case OriginUser: + return 'User' + default: + return 'UnknownOrigin' + } +} + +/** Converts a string to an Origin value. */ +export function fromOriginString(s: string): Origin { + switch (s) { + case 'System': + return OriginSystem + case 'User': + return OriginUser + default: + return -1 + } +} + +export type Visibility = number + +/** The full details of the error can be shared across all nodes in the network. */ +export const VisibilityPublic = 0 + +/** The error contains sensitive information visible only to the local node. */ +export const VisibilityPrivate = 1 + +export function visibilityToString(visibility: Visibility): string { + switch (visibility) { + case VisibilityPublic: + return 'Public' + case VisibilityPrivate: + return 'Private' + default: + return 'UnknownVisibility' + } +} + +/** Converts a string to a Visibility value. */ +export function fromVisibilityString(s: string): Visibility { + switch (s) { + case 'Public': + return VisibilityPublic + case 'Private': + return VisibilityPrivate + default: + return -1 + } +} + +export class CapabilityExecutionError extends Error { + public readonly name = 'CapabilityExecutionError' + + constructor( + public readonly detail: string, + public readonly visibility: Visibility, + public readonly origin: Origin, + public readonly code: ErrorCode, + ) { + super( + code === UnrecognisedErrorCode + ? detail + : `[${code}]${errorCodeToStringValue(code)}: ${detail}`, + ) + } + + override toString(): string { + return this.message + } +} + +export function isCapabilityExecutionError(err: unknown): err is CapabilityExecutionError { + return err instanceof CapabilityExecutionError +} diff --git a/packages/cre-sdk/src/capabilities/errors/index.ts b/packages/cre-sdk/src/capabilities/errors/index.ts new file mode 100644 index 00000000..0442e2ad --- /dev/null +++ b/packages/cre-sdk/src/capabilities/errors/index.ts @@ -0,0 +1,3 @@ +export * from './error' +export * from './error-codes' +export * from './error-serialization' diff --git a/packages/cre-sdk/src/sdk/errors.ts b/packages/cre-sdk/src/sdk/errors.ts index 02fa2186..e51db0d7 100644 --- a/packages/cre-sdk/src/sdk/errors.ts +++ b/packages/cre-sdk/src/sdk/errors.ts @@ -1,5 +1,8 @@ import type { SecretRequest } from '@cre/generated/sdk/v1alpha/sdk_pb' +// TODO: on the next major version, rename this to CapabilityRuntimeError +export { CapabilityError as CapabilityRuntimeError } from './utils/capabilities/capability-error' + export class DonModeError extends Error { constructor() { super('cannot use Runtime inside RunInNodeMode') diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts index 154c8d68..2bf8f0f5 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts @@ -47,8 +47,14 @@ import { median, Value, } from '@cre/sdk/utils' -import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' -import { DonModeError, NodeModeError, SecretsError } from '../errors' +import { + CapabilityExecutionError, + DeadlineExceeded, + isCapabilityExecutionError, + OriginUser, + VisibilityPublic, +} from '@cre/capabilities/errors' +import { CapabilityRuntimeError, DonModeError, NodeModeError, SecretsError } from '../errors' import { RESPONSE_BUFFER_TOO_SMALL } from '../testutils/test-runtime' import { type RuntimeHelpers, RuntimeImpl } from './runtime-impl' @@ -183,7 +189,7 @@ describe('test runtime', () => { ) expect(() => call1.result()).toThrow( - new CapabilityError( + new CapabilityRuntimeError( `Capability '${BasicActionCapability.CAPABILITY_ID}' not found: the host rejected the call to method 'PerformAction'. Verify the capability ID is correct and the capability is available in this CRE environment`, { callbackId: 1, @@ -219,16 +225,44 @@ describe('test runtime', () => { create(InputsSchema, { inputThing: true }), ) - expect(() => call1.result()).toThrow( - new CapabilityError( - `Capability '${BasicActionCapability.CAPABILITY_ID}' method 'PerformAction' returned an error: ${anyError}`, - { - callbackId: 1, - capabilityId: BasicActionCapability.CAPABILITY_ID, - method: 'PerformAction', - }, - ), + expect(() => call1.result()).toThrow(new Error(anyError)) + }) + + test('serialized capability errors are deserialized for the caller', () => { + const serializedError = 'Public:User:DeadlineExceeded:capability failed' + const helpers = createRuntimeHelpersMock({ + call: mock((_: CapabilityRequest) => { + return true + }), + await: mock((request: AwaitCapabilitiesRequest) => { + expect(request.ids.length).toEqual(1) + return create(AwaitCapabilitiesResponseSchema, { + responses: { + [request.ids[0]]: create(CapabilityResponseSchema, { + response: { case: 'error', value: serializedError }, + }), + }, + }) + }), + }) + + const runtime = new RuntimeImpl({}, 1, helpers, anyMaxSize) + const workflowAction1 = new BasicActionCapability() + const call1 = workflowAction1.performAction( + runtime, + create(InputsSchema, { inputThing: true }), ) + + try { + call1.result() + expect(false).toBe(true) + } catch (err) { + expect(isCapabilityExecutionError(err)).toBe(true) + const capErr = err as CapabilityExecutionError + expect(capErr.code).toBe(DeadlineExceeded) + expect(capErr.detail).toBe('capability failed') + expect(capErr.message).toBe('[4]DeadlineExceeded: capability failed') + } }) test('await errors', () => { @@ -250,7 +284,7 @@ describe('test runtime', () => { ) expect(() => call1.result()).toThrow( - new CapabilityError(anyError, { + new CapabilityRuntimeError(anyError, { callbackId: 1, capabilityId: BasicActionCapability.CAPABILITY_ID, method: 'PerformAction', @@ -276,7 +310,7 @@ describe('test runtime', () => { ) expect(() => call1.result()).toThrow( - new CapabilityError( + new CapabilityRuntimeError( `No response found for capability '${BasicActionCapability.CAPABILITY_ID}' method 'PerformAction' (callback ID 1): the host returned a response map that does not contain an entry for this call`, { callbackId: 1, @@ -305,7 +339,7 @@ describe('test runtime', () => { expect(() => call1.result()).toThrow(RESPONSE_BUFFER_TOO_SMALL) }) - test('await returns unparsable payload throws CapabilityError', () => { + test('await returns unparsable payload throws CapabilityRuntimeError', () => { // Any with correct type_url but invalid value bytes so fromBinary throws const validAny = anyPack(OutputsSchema, create(OutputsSchema, { adaptedThing: 'x' })) const corruptPayload = { @@ -335,7 +369,7 @@ describe('test runtime', () => { ) expect(() => call1.result()).toThrow( - new CapabilityError( + new CapabilityRuntimeError( `Failed to deserialize response payload for capability '${BasicActionCapability.CAPABILITY_ID}' method 'PerformAction': the response could not be unpacked into the expected output schema`, { callbackId: 1, diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.ts index 82f340cd..8198e416 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.ts @@ -1,6 +1,7 @@ import { create, type Message } from '@bufbuild/protobuf' import type { GenMessage } from '@bufbuild/protobuf/codegenv2' import { type Any, anyPack, anyUnpack } from '@bufbuild/protobuf/wkt' +import { deserializeErrorFromString } from '@cre/capabilities/errors' import { type AwaitCapabilitiesRequest, AwaitCapabilitiesRequestSchema, @@ -38,8 +39,7 @@ import { type UnwrapOptions, Value, } from '@cre/sdk/utils' -import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' -import { DonModeError, NodeModeError, SecretsError } from '../errors' +import { CapabilityRuntimeError, DonModeError, NodeModeError, SecretsError } from '../errors' const DEFAULT_SECRET_NAMESPACE = 'main' @@ -102,7 +102,7 @@ export class BaseRuntimeImpl implements BaseRuntime { if (!this.helpers.call(req)) { return { result: () => { - throw new CapabilityError( + throw new CapabilityRuntimeError( `Capability '${capabilityId}' not found: the host rejected the call to method '${method}'. Verify the capability ID is correct and the capability is available in this CRE environment`, { callbackId, @@ -151,7 +151,7 @@ export class BaseRuntimeImpl implements BaseRuntime { const capabilityResponse = awaitResponse.responses[callbackId] if (!capabilityResponse) { - throw new CapabilityError( + throw new CapabilityRuntimeError( `No response found for capability '${capabilityId}' method '${method}' (callback ID ${callbackId}): the host returned a response map that does not contain an entry for this call`, { capabilityId, @@ -167,7 +167,7 @@ export class BaseRuntimeImpl implements BaseRuntime { try { return anyUnpack(response.value as Any, outputSchema) as O } catch { - throw new CapabilityError( + throw new CapabilityRuntimeError( `Failed to deserialize response payload for capability '${capabilityId}' method '${method}': the response could not be unpacked into the expected output schema`, { capabilityId, @@ -178,16 +178,9 @@ export class BaseRuntimeImpl implements BaseRuntime { } } case 'error': - throw new CapabilityError( - `Capability '${capabilityId}' method '${method}' returned an error: ${response.value}`, - { - capabilityId, - method, - callbackId, - }, - ) + throw deserializeErrorFromString(response.value) default: - throw new CapabilityError( + throw new CapabilityRuntimeError( `Unexpected response type '${response.case}' for capability '${capabilityId}' method '${method}': expected 'payload' or 'error'`, { capabilityId, diff --git a/packages/cre-sdk/src/sdk/index.ts b/packages/cre-sdk/src/sdk/index.ts index 66ee7b37..7af65861 100644 --- a/packages/cre-sdk/src/sdk/index.ts +++ b/packages/cre-sdk/src/sdk/index.ts @@ -1,3 +1,4 @@ +export * from '../capabilities/errors' export * from './cre' export * from './don-info' export * from './errors' diff --git a/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts b/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts index 63c0959d..3fbc6eb6 100644 --- a/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts +++ b/packages/cre-sdk/src/sdk/testutils/test-runtime.test.ts @@ -8,8 +8,7 @@ import { create } from '@bufbuild/protobuf' import { AnySchema } from '@bufbuild/protobuf/wkt' import { BasicActionCapability } from '@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen' import { consensusMedianAggregation } from '@cre/sdk/utils' -import { CapabilityError } from '@cre/sdk/utils/capabilities/capability-error' -import { SecretsError } from '../errors' +import { CapabilityRuntimeError, SecretsError } from '../errors' import { BasicTestActionMock } from '../test/generated/capabilities/internal/basicaction/v1/basic_test_action_mock_gen' import { __testOnlyRegistryStore, @@ -91,7 +90,7 @@ describe('TestRuntime / helper layer', () => { const rt = newTestRuntime() const cap = new BasicActionCapability() const call = cap.performAction(rt, { inputThing: true }) - expect(() => call.result()).toThrow(CapabilityError) + expect(() => call.result()).toThrow(CapabilityRuntimeError) expect(() => call.result()).toThrow(/not found/) }) diff --git a/packages/cre-sdk/src/sdk/utils/capabilities/capability-error.ts b/packages/cre-sdk/src/sdk/utils/capabilities/capability-error.ts index 7aa0c4fa..7ea3652e 100644 --- a/packages/cre-sdk/src/sdk/utils/capabilities/capability-error.ts +++ b/packages/cre-sdk/src/sdk/utils/capabilities/capability-error.ts @@ -1,3 +1,7 @@ +/** + * @deprecated Use {@link CapabilityRuntimeError} from `@chainlink/cre-sdk` instead. + * Will be removed in the next major version. + */ export class CapabilityError extends Error { public name: string public capabilityId?: string diff --git a/packages/cre-sdk/src/standard_tests/capability_errors/test.ts b/packages/cre-sdk/src/standard_tests/capability_errors/test.ts new file mode 100644 index 00000000..b441ee73 --- /dev/null +++ b/packages/cre-sdk/src/standard_tests/capability_errors/test.ts @@ -0,0 +1,44 @@ +import { isCapabilityExecutionError, UnrecognisedErrorCode } from '@cre/capabilities/errors' +import { BasicActionCapability } from '@cre/generated-sdk/capabilities/internal/basicaction/v1/basicaction_sdk_gen' +import { BasicCapability as BasicTriggerCapability } from '@cre/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen' +import { cre, type Runtime } from '@cre/sdk/cre' +import { Runner } from '@cre/sdk/wasm' + +const checkCapabilityErrors = (runtime: Runtime) => { + const basicAction = new BasicActionCapability() + const input = { inputThing: true } + + while (true) { + try { + const output = basicAction.performAction(runtime, input).result() + if (output.adaptedThing !== 'Done') { + throw new Error(`expected Done response, got ${output.adaptedThing}`) + } + return 'Done' + } catch (err) { + if (!isCapabilityExecutionError(err)) { + throw new Error(`expected capability error, got ${String(err)}`) + } + if (err.code === UnrecognisedErrorCode) { + throw new Error('expected recognised error code, got UnrecognisedErrorCode') + } + } + } +} + +const initWorkflow = () => { + const basicTrigger = new BasicTriggerCapability() + + return [cre.handler(basicTrigger.trigger({}), checkCapabilityErrors)] +} + +export async function main() { + console.log(`TS workflow: standard test: capability errors [${new Date().toISOString()}]`) + + const runner = await Runner.newRunner({ + configParser: (c) => c, + }) + await runner.run(initWorkflow) +} + +await main() diff --git a/packages/cre-sdk/tsconfig.build.json b/packages/cre-sdk/tsconfig.build.json index 2dcd1ec8..22923a0c 100644 --- a/packages/cre-sdk/tsconfig.build.json +++ b/packages/cre-sdk/tsconfig.build.json @@ -36,6 +36,6 @@ "emitDecoratorMetadata": true }, - "include": ["src/index.ts", "src/sdk/**/*", "src/pb.ts"], + "include": ["src/index.ts", "src/capabilities/**/*", "src/sdk/**/*", "src/pb.ts"], "exclude": ["cli/**/*", "**/*.test.ts", "**/*.test.js"] } From ee771b35c319ab2e834b419f014090a8d9220035 Mon Sep 17 00:00:00 2001 From: Matthew Pendrey Date: Thu, 11 Jun 2026 16:35:43 +0100 Subject: [PATCH 10/20] lint --- .../errors/error-serialization.test.ts | 21 ++++++++++++++++--- .../cre-sdk/src/sdk/impl/runtime-impl.test.ts | 14 ++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/cre-sdk/src/capabilities/errors/error-serialization.test.ts b/packages/cre-sdk/src/capabilities/errors/error-serialization.test.ts index f472b6fb..548a5b79 100644 --- a/packages/cre-sdk/src/capabilities/errors/error-serialization.test.ts +++ b/packages/cre-sdk/src/capabilities/errors/error-serialization.test.ts @@ -109,7 +109,12 @@ describe('deserializeErrorFromString that is not serialised capability error', ( test('valid serialized still returns capability error', () => { const serialized = 'Public:User:DeadlineExceeded:detail' - const expected = new CapabilityExecutionError('detail', VisibilityPublic, OriginUser, DeadlineExceeded) + const expected = new CapabilityExecutionError( + 'detail', + VisibilityPublic, + OriginUser, + DeadlineExceeded, + ) const err = deserializeErrorFromString(serialized) const deserialized = requireCapabilityError(err) expect(capabilityErrorsEqual(expected, deserialized)).toBe(true) @@ -118,7 +123,12 @@ describe('deserializeErrorFromString that is not serialised capability error', ( test('detail with colons preserves colons', () => { const detail = 'failed at step: 1: connection refused' const serialized = `Public:User:DeadlineExceeded:${detail}` - const expected = new CapabilityExecutionError(detail, VisibilityPublic, OriginUser, DeadlineExceeded) + const expected = new CapabilityExecutionError( + detail, + VisibilityPublic, + OriginUser, + DeadlineExceeded, + ) const err = deserializeErrorFromString(serialized) const deserialized = requireCapabilityError(err) expect(capabilityErrorsEqual(expected, deserialized)).toBe(true) @@ -128,7 +138,12 @@ describe('deserializeErrorFromString that is not serialised capability error', ( describe('CapabilityExecutionError formatting', () => { test('recognised code includes code prefix', () => { - const err = new CapabilityExecutionError('detail', VisibilityPublic, OriginUser, DeadlineExceeded) + const err = new CapabilityExecutionError( + 'detail', + VisibilityPublic, + OriginUser, + DeadlineExceeded, + ) expect(err.toString()).toBe('[4]DeadlineExceeded: detail') }) diff --git a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts index 2bf8f0f5..52fac1eb 100644 --- a/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts +++ b/packages/cre-sdk/src/sdk/impl/runtime-impl.test.ts @@ -1,6 +1,13 @@ import { afterEach, describe, expect, mock, test } from 'bun:test' import { create } from '@bufbuild/protobuf' import { type Any, anyPack, anyUnpack } from '@bufbuild/protobuf/wkt' +import { + CapabilityExecutionError, + DeadlineExceeded, + isCapabilityExecutionError, + OriginUser, + VisibilityPublic, +} from '@cre/capabilities/errors' import { InputSchema, OutputSchema, @@ -47,13 +54,6 @@ import { median, Value, } from '@cre/sdk/utils' -import { - CapabilityExecutionError, - DeadlineExceeded, - isCapabilityExecutionError, - OriginUser, - VisibilityPublic, -} from '@cre/capabilities/errors' import { CapabilityRuntimeError, DonModeError, NodeModeError, SecretsError } from '../errors' import { RESPONSE_BUFFER_TOO_SMALL } from '../testutils/test-runtime' import { type RuntimeHelpers, RuntimeImpl } from './runtime-impl' From 629b3967895bcfd1f82f264e8add39101bdb4b60 Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Wed, 17 Jun 2026 09:53:08 -0500 Subject: [PATCH 11/20] Add support for private-testnet-pumice (#271) * Added support for private-testnet-pumice * Regenerated api-baseline --- bun.lock | 10 ++-- packages/cre-sdk/api-baseline.d.ts | 2 + packages/cre-sdk/package.json | 2 +- .../blockchain/evm/v1alpha/client_sdk_gen.ts | 1 + .../blockchain/evm/v1alpha/client_pb.ts | 2 +- .../networking/http/v1alpha/client_pb.ts | 58 ++++++++++++++++++- .../mainnet/evm/arc-mainnet.ts | 16 +++++ .../testnet/evm/glamsterdam-devnet-5.ts | 16 +++++ .../testnet/evm/private-testnet-pumice.ts | 16 +++++ packages/cre-sdk/src/generated/networks.ts | 21 +++++++ submodules/chainlink-protos | 2 +- 11 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 packages/cre-sdk/src/generated/chain-selectors/mainnet/evm/arc-mainnet.ts create mode 100644 packages/cre-sdk/src/generated/chain-selectors/testnet/evm/glamsterdam-devnet-5.ts create mode 100644 packages/cre-sdk/src/generated/chain-selectors/testnet/evm/private-testnet-pumice.ts diff --git a/bun.lock b/bun.lock index 2718d346..837d3c53 100644 --- a/bun.lock +++ b/bun.lock @@ -63,7 +63,7 @@ }, "packages/cre-sdk": { "name": "@chainlink/cre-sdk", - "version": "1.8.0", + "version": "1.11.0", "bin": { "cre-compile": "bin/cre-compile.ts", }, @@ -79,7 +79,7 @@ "@biomejs/biome": "2.3.14", "@bufbuild/buf": "1.56.0", "@types/bun": "1.3.8", - "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#116ebbce88b36d4e76a03c12b3437ed33a5f9860", + "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#c6379b1272aa41e6977f60799c82a49465a14b3f", "fast-glob": "3.3.3", "ts-proto": "2.7.5", "typescript": "5.9.3", @@ -88,7 +88,7 @@ }, "packages/cre-sdk-examples": { "name": "@chainlink/cre-sdk-examples", - "version": "1.8.0", + "version": "1.11.0", "dependencies": { "@bufbuild/protobuf": "2.6.3", "@chainlink/cre-sdk": "workspace:*", @@ -98,7 +98,7 @@ }, "packages/cre-sdk-javy-plugin": { "name": "@chainlink/cre-sdk-javy-plugin", - "version": "1.6.0", + "version": "1.7.0", "bin": { "cre-setup": "bin/setup.ts", "cre-compile-workflow": "bin/compile-workflow.ts", @@ -210,7 +210,7 @@ "case-anything": ["case-anything@2.1.13", "", {}, "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng=="], - "chain-selectors": ["chain-selectors@github:smartcontractkit/chain-selectors#116ebbc", {}, "smartcontractkit-chain-selectors-116ebbc"], + "chain-selectors": ["chain-selectors@github:smartcontractkit/chain-selectors#c6379b1", {}, "smartcontractkit-chain-selectors-c6379b1"], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], diff --git a/packages/cre-sdk/api-baseline.d.ts b/packages/cre-sdk/api-baseline.d.ts index 0d516db4..3593f72d 100644 --- a/packages/cre-sdk/api-baseline.d.ts +++ b/packages/cre-sdk/api-baseline.d.ts @@ -8,6 +8,7 @@ export * as CRON_TRIGGER_PB from '@cre/generated/capabilities/scheduler/cron/v1/ export * as SDK_PB from '@cre/generated/sdk/v1alpha/sdk_pb'; export * as VALUES_PB from '@cre/generated/values/v1/values_pb'; export * as BUFBUILD_TYPES from '@cre/sdk/types/bufbuild-types'; +export * from '../capabilities/errors'; export * from './cre'; export * from './don-info'; export * from './errors'; @@ -89,6 +90,7 @@ export type SecretsProvider = { }; }; import type { SecretRequest } from '@cre/generated/sdk/v1alpha/sdk_pb'; +export { CapabilityError as CapabilityRuntimeError } from './utils/capabilities/capability-error'; export declare class DonModeError extends Error { constructor(); } diff --git a/packages/cre-sdk/package.json b/packages/cre-sdk/package.json index 8ecf5f95..257c4c1a 100644 --- a/packages/cre-sdk/package.json +++ b/packages/cre-sdk/package.json @@ -70,7 +70,7 @@ "@biomejs/biome": "2.3.14", "@bufbuild/buf": "1.56.0", "@types/bun": "1.3.8", - "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#116ebbce88b36d4e76a03c12b3437ed33a5f9860", + "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#c6379b1272aa41e6977f60799c82a49465a14b3f", "fast-glob": "3.3.3", "ts-proto": "2.7.5", "typescript": "5.9.3", diff --git a/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts b/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts index d9e6e69f..5e2c7286 100644 --- a/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts +++ b/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts @@ -172,6 +172,7 @@ export class ClientCapability { 'polygon-mainnet': 4051577828743386545n, 'polygon-testnet-amoy': 16281711391670634445n, 'private-testnet-andesite': 6915682381028791124n, + 'private-testnet-pumice': 1564738277398880633n, 'private-testnet-rhyolite': 604447335222770945n, 'sonic-mainnet': 1673871237479749969n, 'sonic-testnet': 1763698235108410440n, diff --git a/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts b/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts index 3900c04e..edb623a7 100644 --- a/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts +++ b/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts @@ -17,7 +17,7 @@ import { file_values_v1_values } from '../../../../values/v1/values_pb' export const file_capabilities_blockchain_evm_v1alpha_client: GenFile = /*@__PURE__*/ fileDesc( - 'CjBjYXBhYmlsaXRpZXMvYmxvY2tjaGFpbi9ldm0vdjFhbHBoYS9jbGllbnQucHJvdG8SI2NhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhIh0KC1RvcGljVmFsdWVzEg4KBnZhbHVlcxgBIAMoDCK4AQoXRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QSEQoJYWRkcmVzc2VzGAEgAygMEkAKBnRvcGljcxgCIAMoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRvcGljVmFsdWVzEkgKCmNvbmZpZGVuY2UYAyABKA4yNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Db25maWRlbmNlTGV2ZWwiegoTQ2FsbENvbnRyYWN0UmVxdWVzdBI6CgRjYWxsGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZxInCgxibG9ja19udW1iZXIYAiABKAsyES52YWx1ZXMudjEuQmlnSW50IiEKEUNhbGxDb250cmFjdFJlcGx5EgwKBGRhdGEYASABKAwiWwoRRmlsdGVyTG9nc1JlcXVlc3QSRgoMZmlsdGVyX3F1ZXJ5GAEgASgLMjAuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyUXVlcnkiSQoPRmlsdGVyTG9nc1JlcGx5EjYKBGxvZ3MYASADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cixwEKA0xvZxIPCgdhZGRyZXNzGAEgASgMEg4KBnRvcGljcxgCIAMoDBIPCgd0eF9oYXNoGAMgASgMEhIKCmJsb2NrX2hhc2gYBCABKAwSDAoEZGF0YRgFIAEoDBIRCglldmVudF9zaWcYBiABKAwSJwoMYmxvY2tfbnVtYmVyGAcgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIQCgh0eF9pbmRleBgIIAEoDRINCgVpbmRleBgJIAEoDRIPCgdyZW1vdmVkGAogASgIIjEKB0NhbGxNc2cSDAoEZnJvbRgBIAEoDBIKCgJ0bxgCIAEoDBIMCgRkYXRhGAMgASgMIr0BCgtGaWx0ZXJRdWVyeRISCgpibG9ja19oYXNoGAEgASgMEiUKCmZyb21fYmxvY2sYAiABKAsyES52YWx1ZXMudjEuQmlnSW50EiMKCHRvX2Jsb2NrGAMgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIRCglhZGRyZXNzZXMYBCADKAwSOwoGdG9waWNzGAUgAygLMisuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuVG9waWNzIhcKBlRvcGljcxINCgV0b3BpYxgBIAMoDCJMChBCYWxhbmNlQXRSZXF1ZXN0Eg8KB2FjY291bnQYASABKAwSJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludCI0Cg5CYWxhbmNlQXRSZXBseRIiCgdiYWxhbmNlGAEgASgLMhEudmFsdWVzLnYxLkJpZ0ludCJPChJFc3RpbWF0ZUdhc1JlcXVlc3QSOQoDbXNnGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZyIjChBFc3RpbWF0ZUdhc1JlcGx5Eg8KA2dhcxgBIAEoBEICMAAiKwobR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXF1ZXN0EgwKBGhhc2gYASABKAwiYgoZR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXBseRJFCgt0cmFuc2FjdGlvbhgBIAEoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRyYW5zYWN0aW9uIqEBCgtUcmFuc2FjdGlvbhIRCgVub25jZRgBIAEoBEICMAASDwoDZ2FzGAIgASgEQgIwABIKCgJ0bxgDIAEoDBIMCgRkYXRhGAQgASgMEgwKBGhhc2gYBSABKAwSIAoFdmFsdWUYBiABKAsyES52YWx1ZXMudjEuQmlnSW50EiQKCWdhc19wcmljZRgHIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiLAocR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVxdWVzdBIMCgRoYXNoGAEgASgMIlsKGkdldFRyYW5zYWN0aW9uUmVjZWlwdFJlcGx5Ej0KB3JlY2VpcHQYASABKAsyLC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXB0IpkCCgdSZWNlaXB0EhIKBnN0YXR1cxgBIAEoBEICMAASFAoIZ2FzX3VzZWQYAiABKARCAjAAEhQKCHR4X2luZGV4GAMgASgEQgIwABISCgpibG9ja19oYXNoGAQgASgMEjYKBGxvZ3MYBiADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cSDwoHdHhfaGFzaBgHIAEoDBIuChNlZmZlY3RpdmVfZ2FzX3ByaWNlGAggASgLMhEudmFsdWVzLnYxLkJpZ0ludBInCgxibG9ja19udW1iZXIYCSABKAsyES52YWx1ZXMudjEuQmlnSW50EhgKEGNvbnRyYWN0X2FkZHJlc3MYCiABKAwiQAoVSGVhZGVyQnlOdW1iZXJSZXF1ZXN0EicKDGJsb2NrX251bWJlchgBIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiUgoTSGVhZGVyQnlOdW1iZXJSZXBseRI7CgZoZWFkZXIYASABKAsyKy5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5IZWFkZXIiawoGSGVhZGVyEhUKCXRpbWVzdGFtcBgBIAEoBEICMAASJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIMCgRoYXNoGAMgASgMEhMKC3BhcmVudF9oYXNoGAQgASgMIqsBChJXcml0ZVJlcG9ydFJlcXVlc3QSEAoIcmVjZWl2ZXIYASABKAwSKwoGcmVwb3J0GAIgASgLMhsuc2RrLnYxYWxwaGEuUmVwb3J0UmVzcG9uc2USRwoKZ2FzX2NvbmZpZxgDIAEoCzIuLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkdhc0NvbmZpZ0gAiAEBQg0KC19nYXNfY29uZmlnIiIKCUdhc0NvbmZpZxIVCglnYXNfbGltaXQYASABKARCAjAAIocDChBXcml0ZVJlcG9ydFJlcGx5EkAKCXR4X3N0YXR1cxgBIAEoDjItLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlR4U3RhdHVzEnUKInJlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXMYAiABKA4yRC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzSACIAQESFAoHdHhfaGFzaBgDIAEoDEgBiAEBEi8KD3RyYW5zYWN0aW9uX2ZlZRgEIAEoCzIRLnZhbHVlcy52MS5CaWdJbnRIAogBARIaCg1lcnJvcl9tZXNzYWdlGAUgASgJSAOIAQFCJQojX3JlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXNCCgoIX3R4X2hhc2hCEgoQX3RyYW5zYWN0aW9uX2ZlZUIQCg5fZXJyb3JfbWVzc2FnZSppCg9Db25maWRlbmNlTGV2ZWwSGQoVQ09ORklERU5DRV9MRVZFTF9TQUZFEAASGwoXQ09ORklERU5DRV9MRVZFTF9MQVRFU1QQARIeChpDT05GSURFTkNFX0xFVkVMX0ZJTkFMSVpFRBACKoIBCh9SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzEi4KKlJFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfU1VDQ0VTUxAAEi8KK1JFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfUkVWRVJURUQQASpOCghUeFN0YXR1cxITCg9UWF9TVEFUVVNfRkFUQUwQABIWChJUWF9TVEFUVVNfUkVWRVJURUQQARIVChFUWF9TVEFUVVNfU1VDQ0VTUxACMvgYCgZDbGllbnQSgAEKDENhbGxDb250cmFjdBI4LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkNhbGxDb250cmFjdFJlcXVlc3QaNi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5DYWxsQ29udHJhY3RSZXBseRJ6CgpGaWx0ZXJMb2dzEjYuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nc1JlcXVlc3QaNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5GaWx0ZXJMb2dzUmVwbHkSdwoJQmFsYW5jZUF0EjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQmFsYW5jZUF0UmVxdWVzdBozLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkJhbGFuY2VBdFJlcGx5En0KC0VzdGltYXRlR2FzEjcuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXF1ZXN0GjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXBseRKYAQoUR2V0VHJhbnNhY3Rpb25CeUhhc2gSQC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcXVlc3QaPi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcGx5EpsBChVHZXRUcmFuc2FjdGlvblJlY2VpcHQSQS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvblJlY2VpcHRSZXF1ZXN0Gj8uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVwbHkShgEKDkhlYWRlckJ5TnVtYmVyEjouY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXF1ZXN0GjguY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXBseRJ2CgpMb2dUcmlnZ2VyEjwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QaKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cwARJ9CgtXcml0ZVJlcG9ydBI3LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVxdWVzdBo1LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVwbHkavQ+CtRi4DwgBEglldm1AMS4wLjAaqA8KDUNoYWluU2VsZWN0b3ISlg8Skw8KFwoLYWRpLW1haW5uZXQQ/PDmwrfn3ao4ChgKC2FkaS10ZXN0bmV0EP2myPr5hozaggEKJAoXYXBlY2hhaW4tdGVzdG5ldC1jdXJ0aXMQwcO0+I3EkrKJAQoXCgthcmMtdGVzdG5ldBDnxoye19fQjSoKHQoRYXZhbGFuY2hlLW1haW5uZXQQ1eeKwOHVmKRZCiMKFmF2YWxhbmNoZS10ZXN0bmV0LWZ1amkQm/n8kKLjqPjMAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLW1haW5uZXQQz/eU8djtlbidAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLXRlc3RuZXQQ+62+nICu5Iq4AQoYCgxjZWxvLW1haW5uZXQQhtTo2IaTiNcSChgKDGNlbG8tc2Vwb2xpYRDEw/yL/OedmjQKGgoOY3Jvbm9zLXRlc3RuZXQQ/dnureDe2sgpCiIKFWR0Y2MtdGVzdG5ldC1hbmRlc2l0ZRDSg+PQmZblpNcBChwKEGV0aGVyZXVtLW1haW5uZXQQlfbx5M+ypsJFCicKG2V0aGVyZXVtLW1haW5uZXQtYXJiaXRydW0tMRDE6I3Njpuh10QKJAoXZXRoZXJldW0tbWFpbm5ldC1iYXNlLTEQgv+rov65kNPdAQoiChZldGhlcmV1bS1tYWlubmV0LWluay0xEKCwpum35qqEMAokChhldGhlcmV1bS1tYWlubmV0LWxpbmVhLTEQtrrpmMu9sJtACiUKGWV0aGVyZXVtLW1haW5uZXQtbWFudGxlLTEQiue0leewg8wVCicKG2V0aGVyZXVtLW1haW5uZXQtb3B0aW1pc20tMRC4lY/D9/7Q6TMKJgoZZXRoZXJldW0tbWFpbm5ldC1zY3JvbGwtMRC4vOTrxL7In7cBCikKHWV0aGVyZXVtLW1haW5uZXQtd29ybGRjaGFpbi0xEIfvurfFtsK4HAolChlldGhlcmV1bS1tYWlubmV0LXhsYXllci0xEJal/JymqO/tKQolChlldGhlcmV1bS1tYWlubmV0LXprc3luYy0xEJTul9nttLHXFQolChhldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEQ2bXkzvzJ7qDeAQovCiNldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtYXJiaXRydW0tMRDqzu7/6raEozAKLAofZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLWJhc2UtMRC4yrnv9pCuyI8BCiwKIGV0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1saW5lYS0xEOuq1P6C+eavTwotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtbWFudGxlLTEQ1ca47s328qZyCi8KI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1vcHRpbWlzbS0xEJ+GxaG+2MPASAotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtc2Nyb2xsLTEQi+m0vtu67dEfCjAKI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS11bmljaGFpbi0xELTe/uDsl6mWxAEKMQolZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXdvcmxkY2hhaW4tMRC63+DFx6nzxUkKLQohZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXprc3luYy0xELfB/P3yxIDeXwogChRnbm9zaXNfY2hhaW4tbWFpbm5ldBD0kq3a8qKuugYKJwobZ25vc2lzX2NoYWluLXRlc3RuZXQtY2hpYWRvELOxgtCbpY+PewofChNoeXBlcmxpcXVpZC1tYWlubmV0EKez+N3O0enyIQofChNoeXBlcmxpcXVpZC10ZXN0bmV0EIjO3ciX4Mm9OwogChNpbmstdGVzdG5ldC1zZXBvbGlhEOj0p6Xz5pbAhwEKGQoNam92YXktbWFpbm5ldBC1w8SaoYDfkhUKGQoNam92YXktdGVzdG5ldBDkz4qE3rLejg0KGwoPbWVnYWV0aC1tYWlubmV0EOqVtsi85KbIVAoeChFtZWdhZXRoLXRlc3RuZXQtMhDjjd6IsY/9k/0BCiQKF3BoYXJvcy1hdGxhbnRpYy10ZXN0bmV0EMyZ7eDOvK+03wEKGgoOcGhhcm9zLW1haW5uZXQQyMGHnvXvzaFsChsKDnBsYXNtYS1tYWlubmV0EPib8dHaydXGgQEKGgoOcGxhc21hLXRlc3RuZXQQ1Zu/pcO0mYc3ChsKD3BvbHlnb24tbWFpbm5ldBCxq+TwmpKGnTgKIQoUcG9seWdvbi10ZXN0bmV0LWFtb3kQzY/W3/HHkPrhAQokChhwcml2YXRlLXRlc3RuZXQtYW5kZXNpdGUQ1KaYpcGP3PxfCiQKGHByaXZhdGUtdGVzdG5ldC1yaHlvbGl0ZRCB+onr4bTbsQgKGQoNc29uaWMtbWFpbm5ldBDRsuXt2aCynRcKGQoNc29uaWMtdGVzdG5ldBDIiPvUtMb6vBgKGAoLdGFjLXRlc3RuZXQQ1duN4/ufk9eDAQobCg54bGF5ZXItdGVzdG5ldBDJvqG0rcy83Y0BQuUBCidjb20uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGFCC0NsaWVudFByb3RvUAGiAgNDQkWqAiNDYXBhYmlsaXRpZXMuQmxvY2tjaGFpbi5Fdm0uVjFhbHBoYcoCI0NhcGFiaWxpdGllc1xCbG9ja2NoYWluXEV2bVxWMWFscGhh4gIvQ2FwYWJpbGl0aWVzXEJsb2NrY2hhaW5cRXZtXFYxYWxwaGFcR1BCTWV0YWRhdGHqAiZDYXBhYmlsaXRpZXM6OkJsb2NrY2hhaW46OkV2bTo6VjFhbHBoYWIGcHJvdG8z', + 'CjBjYXBhYmlsaXRpZXMvYmxvY2tjaGFpbi9ldm0vdjFhbHBoYS9jbGllbnQucHJvdG8SI2NhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhIh0KC1RvcGljVmFsdWVzEg4KBnZhbHVlcxgBIAMoDCK4AQoXRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QSEQoJYWRkcmVzc2VzGAEgAygMEkAKBnRvcGljcxgCIAMoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRvcGljVmFsdWVzEkgKCmNvbmZpZGVuY2UYAyABKA4yNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Db25maWRlbmNlTGV2ZWwiegoTQ2FsbENvbnRyYWN0UmVxdWVzdBI6CgRjYWxsGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZxInCgxibG9ja19udW1iZXIYAiABKAsyES52YWx1ZXMudjEuQmlnSW50IiEKEUNhbGxDb250cmFjdFJlcGx5EgwKBGRhdGEYASABKAwiWwoRRmlsdGVyTG9nc1JlcXVlc3QSRgoMZmlsdGVyX3F1ZXJ5GAEgASgLMjAuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyUXVlcnkiSQoPRmlsdGVyTG9nc1JlcGx5EjYKBGxvZ3MYASADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cixwEKA0xvZxIPCgdhZGRyZXNzGAEgASgMEg4KBnRvcGljcxgCIAMoDBIPCgd0eF9oYXNoGAMgASgMEhIKCmJsb2NrX2hhc2gYBCABKAwSDAoEZGF0YRgFIAEoDBIRCglldmVudF9zaWcYBiABKAwSJwoMYmxvY2tfbnVtYmVyGAcgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIQCgh0eF9pbmRleBgIIAEoDRINCgVpbmRleBgJIAEoDRIPCgdyZW1vdmVkGAogASgIIjEKB0NhbGxNc2cSDAoEZnJvbRgBIAEoDBIKCgJ0bxgCIAEoDBIMCgRkYXRhGAMgASgMIr0BCgtGaWx0ZXJRdWVyeRISCgpibG9ja19oYXNoGAEgASgMEiUKCmZyb21fYmxvY2sYAiABKAsyES52YWx1ZXMudjEuQmlnSW50EiMKCHRvX2Jsb2NrGAMgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIRCglhZGRyZXNzZXMYBCADKAwSOwoGdG9waWNzGAUgAygLMisuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuVG9waWNzIhcKBlRvcGljcxINCgV0b3BpYxgBIAMoDCJMChBCYWxhbmNlQXRSZXF1ZXN0Eg8KB2FjY291bnQYASABKAwSJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludCI0Cg5CYWxhbmNlQXRSZXBseRIiCgdiYWxhbmNlGAEgASgLMhEudmFsdWVzLnYxLkJpZ0ludCJPChJFc3RpbWF0ZUdhc1JlcXVlc3QSOQoDbXNnGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZyIjChBFc3RpbWF0ZUdhc1JlcGx5Eg8KA2dhcxgBIAEoBEICMAAiKwobR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXF1ZXN0EgwKBGhhc2gYASABKAwiYgoZR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXBseRJFCgt0cmFuc2FjdGlvbhgBIAEoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRyYW5zYWN0aW9uIqEBCgtUcmFuc2FjdGlvbhIRCgVub25jZRgBIAEoBEICMAASDwoDZ2FzGAIgASgEQgIwABIKCgJ0bxgDIAEoDBIMCgRkYXRhGAQgASgMEgwKBGhhc2gYBSABKAwSIAoFdmFsdWUYBiABKAsyES52YWx1ZXMudjEuQmlnSW50EiQKCWdhc19wcmljZRgHIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiLAocR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVxdWVzdBIMCgRoYXNoGAEgASgMIlsKGkdldFRyYW5zYWN0aW9uUmVjZWlwdFJlcGx5Ej0KB3JlY2VpcHQYASABKAsyLC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXB0IpkCCgdSZWNlaXB0EhIKBnN0YXR1cxgBIAEoBEICMAASFAoIZ2FzX3VzZWQYAiABKARCAjAAEhQKCHR4X2luZGV4GAMgASgEQgIwABISCgpibG9ja19oYXNoGAQgASgMEjYKBGxvZ3MYBiADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cSDwoHdHhfaGFzaBgHIAEoDBIuChNlZmZlY3RpdmVfZ2FzX3ByaWNlGAggASgLMhEudmFsdWVzLnYxLkJpZ0ludBInCgxibG9ja19udW1iZXIYCSABKAsyES52YWx1ZXMudjEuQmlnSW50EhgKEGNvbnRyYWN0X2FkZHJlc3MYCiABKAwiQAoVSGVhZGVyQnlOdW1iZXJSZXF1ZXN0EicKDGJsb2NrX251bWJlchgBIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiUgoTSGVhZGVyQnlOdW1iZXJSZXBseRI7CgZoZWFkZXIYASABKAsyKy5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5IZWFkZXIiawoGSGVhZGVyEhUKCXRpbWVzdGFtcBgBIAEoBEICMAASJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIMCgRoYXNoGAMgASgMEhMKC3BhcmVudF9oYXNoGAQgASgMIqsBChJXcml0ZVJlcG9ydFJlcXVlc3QSEAoIcmVjZWl2ZXIYASABKAwSKwoGcmVwb3J0GAIgASgLMhsuc2RrLnYxYWxwaGEuUmVwb3J0UmVzcG9uc2USRwoKZ2FzX2NvbmZpZxgDIAEoCzIuLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkdhc0NvbmZpZ0gAiAEBQg0KC19nYXNfY29uZmlnIiIKCUdhc0NvbmZpZxIVCglnYXNfbGltaXQYASABKARCAjAAIocDChBXcml0ZVJlcG9ydFJlcGx5EkAKCXR4X3N0YXR1cxgBIAEoDjItLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlR4U3RhdHVzEnUKInJlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXMYAiABKA4yRC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzSACIAQESFAoHdHhfaGFzaBgDIAEoDEgBiAEBEi8KD3RyYW5zYWN0aW9uX2ZlZRgEIAEoCzIRLnZhbHVlcy52MS5CaWdJbnRIAogBARIaCg1lcnJvcl9tZXNzYWdlGAUgASgJSAOIAQFCJQojX3JlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXNCCgoIX3R4X2hhc2hCEgoQX3RyYW5zYWN0aW9uX2ZlZUIQCg5fZXJyb3JfbWVzc2FnZSppCg9Db25maWRlbmNlTGV2ZWwSGQoVQ09ORklERU5DRV9MRVZFTF9TQUZFEAASGwoXQ09ORklERU5DRV9MRVZFTF9MQVRFU1QQARIeChpDT05GSURFTkNFX0xFVkVMX0ZJTkFMSVpFRBACKoIBCh9SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzEi4KKlJFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfU1VDQ0VTUxAAEi8KK1JFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfUkVWRVJURUQQASpOCghUeFN0YXR1cxITCg9UWF9TVEFUVVNfRkFUQUwQABIWChJUWF9TVEFUVVNfUkVWRVJURUQQARIVChFUWF9TVEFUVVNfU1VDQ0VTUxACMpwZCgZDbGllbnQSgAEKDENhbGxDb250cmFjdBI4LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkNhbGxDb250cmFjdFJlcXVlc3QaNi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5DYWxsQ29udHJhY3RSZXBseRJ6CgpGaWx0ZXJMb2dzEjYuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nc1JlcXVlc3QaNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5GaWx0ZXJMb2dzUmVwbHkSdwoJQmFsYW5jZUF0EjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQmFsYW5jZUF0UmVxdWVzdBozLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkJhbGFuY2VBdFJlcGx5En0KC0VzdGltYXRlR2FzEjcuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXF1ZXN0GjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXBseRKYAQoUR2V0VHJhbnNhY3Rpb25CeUhhc2gSQC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcXVlc3QaPi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcGx5EpsBChVHZXRUcmFuc2FjdGlvblJlY2VpcHQSQS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvblJlY2VpcHRSZXF1ZXN0Gj8uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVwbHkShgEKDkhlYWRlckJ5TnVtYmVyEjouY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXF1ZXN0GjguY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXBseRJ2CgpMb2dUcmlnZ2VyEjwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QaKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cwARJ9CgtXcml0ZVJlcG9ydBI3LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVxdWVzdBo1LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVwbHka4Q+CtRjcDwgBEglldm1AMS4wLjAazA8KDUNoYWluU2VsZWN0b3ISug8Stw8KFwoLYWRpLW1haW5uZXQQ/PDmwrfn3ao4ChgKC2FkaS10ZXN0bmV0EP2myPr5hozaggEKJAoXYXBlY2hhaW4tdGVzdG5ldC1jdXJ0aXMQwcO0+I3EkrKJAQoXCgthcmMtdGVzdG5ldBDnxoye19fQjSoKHQoRYXZhbGFuY2hlLW1haW5uZXQQ1eeKwOHVmKRZCiMKFmF2YWxhbmNoZS10ZXN0bmV0LWZ1amkQm/n8kKLjqPjMAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLW1haW5uZXQQz/eU8djtlbidAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLXRlc3RuZXQQ+62+nICu5Iq4AQoYCgxjZWxvLW1haW5uZXQQhtTo2IaTiNcSChgKDGNlbG8tc2Vwb2xpYRDEw/yL/OedmjQKGgoOY3Jvbm9zLXRlc3RuZXQQ/dnureDe2sgpCiIKFWR0Y2MtdGVzdG5ldC1hbmRlc2l0ZRDSg+PQmZblpNcBChwKEGV0aGVyZXVtLW1haW5uZXQQlfbx5M+ypsJFCicKG2V0aGVyZXVtLW1haW5uZXQtYXJiaXRydW0tMRDE6I3Njpuh10QKJAoXZXRoZXJldW0tbWFpbm5ldC1iYXNlLTEQgv+rov65kNPdAQoiChZldGhlcmV1bS1tYWlubmV0LWluay0xEKCwpum35qqEMAokChhldGhlcmV1bS1tYWlubmV0LWxpbmVhLTEQtrrpmMu9sJtACiUKGWV0aGVyZXVtLW1haW5uZXQtbWFudGxlLTEQiue0leewg8wVCicKG2V0aGVyZXVtLW1haW5uZXQtb3B0aW1pc20tMRC4lY/D9/7Q6TMKJgoZZXRoZXJldW0tbWFpbm5ldC1zY3JvbGwtMRC4vOTrxL7In7cBCikKHWV0aGVyZXVtLW1haW5uZXQtd29ybGRjaGFpbi0xEIfvurfFtsK4HAolChlldGhlcmV1bS1tYWlubmV0LXhsYXllci0xEJal/JymqO/tKQolChlldGhlcmV1bS1tYWlubmV0LXprc3luYy0xEJTul9nttLHXFQolChhldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEQ2bXkzvzJ7qDeAQovCiNldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtYXJiaXRydW0tMRDqzu7/6raEozAKLAofZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLWJhc2UtMRC4yrnv9pCuyI8BCiwKIGV0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1saW5lYS0xEOuq1P6C+eavTwotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtbWFudGxlLTEQ1ca47s328qZyCi8KI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1vcHRpbWlzbS0xEJ+GxaG+2MPASAotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtc2Nyb2xsLTEQi+m0vtu67dEfCjAKI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS11bmljaGFpbi0xELTe/uDsl6mWxAEKMQolZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXdvcmxkY2hhaW4tMRC63+DFx6nzxUkKLQohZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXprc3luYy0xELfB/P3yxIDeXwogChRnbm9zaXNfY2hhaW4tbWFpbm5ldBD0kq3a8qKuugYKJwobZ25vc2lzX2NoYWluLXRlc3RuZXQtY2hpYWRvELOxgtCbpY+PewofChNoeXBlcmxpcXVpZC1tYWlubmV0EKez+N3O0enyIQofChNoeXBlcmxpcXVpZC10ZXN0bmV0EIjO3ciX4Mm9OwogChNpbmstdGVzdG5ldC1zZXBvbGlhEOj0p6Xz5pbAhwEKGQoNam92YXktbWFpbm5ldBC1w8SaoYDfkhUKGQoNam92YXktdGVzdG5ldBDkz4qE3rLejg0KGwoPbWVnYWV0aC1tYWlubmV0EOqVtsi85KbIVAoeChFtZWdhZXRoLXRlc3RuZXQtMhDjjd6IsY/9k/0BCiQKF3BoYXJvcy1hdGxhbnRpYy10ZXN0bmV0EMyZ7eDOvK+03wEKGgoOcGhhcm9zLW1haW5uZXQQyMGHnvXvzaFsChsKDnBsYXNtYS1tYWlubmV0EPib8dHaydXGgQEKGgoOcGxhc21hLXRlc3RuZXQQ1Zu/pcO0mYc3ChsKD3BvbHlnb24tbWFpbm5ldBCxq+TwmpKGnTgKIQoUcG9seWdvbi10ZXN0bmV0LWFtb3kQzY/W3/HHkPrhAQokChhwcml2YXRlLXRlc3RuZXQtYW5kZXNpdGUQ1KaYpcGP3PxfCiIKFnByaXZhdGUtdGVzdG5ldC1wdW1pY2UQ+cLEtsSlxNsVCiQKGHByaXZhdGUtdGVzdG5ldC1yaHlvbGl0ZRCB+onr4bTbsQgKGQoNc29uaWMtbWFpbm5ldBDRsuXt2aCynRcKGQoNc29uaWMtdGVzdG5ldBDIiPvUtMb6vBgKGAoLdGFjLXRlc3RuZXQQ1duN4/ufk9eDAQobCg54bGF5ZXItdGVzdG5ldBDJvqG0rcy83Y0BQuUBCidjb20uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGFCC0NsaWVudFByb3RvUAGiAgNDQkWqAiNDYXBhYmlsaXRpZXMuQmxvY2tjaGFpbi5Fdm0uVjFhbHBoYcoCI0NhcGFiaWxpdGllc1xCbG9ja2NoYWluXEV2bVxWMWFscGhh4gIvQ2FwYWJpbGl0aWVzXEJsb2NrY2hhaW5cRXZtXFYxYWxwaGFcR1BCTWV0YWRhdGHqAiZDYXBhYmlsaXRpZXM6OkJsb2NrY2hhaW46OkV2bTo6VjFhbHBoYWIGcHJvdG8z', [file_sdk_v1alpha_sdk, file_tools_generator_v1alpha_cre_metadata, file_values_v1_values], ) diff --git a/packages/cre-sdk/src/generated/capabilities/networking/http/v1alpha/client_pb.ts b/packages/cre-sdk/src/generated/capabilities/networking/http/v1alpha/client_pb.ts index aaa1f748..22bb6338 100644 --- a/packages/cre-sdk/src/generated/capabilities/networking/http/v1alpha/client_pb.ts +++ b/packages/cre-sdk/src/generated/capabilities/networking/http/v1alpha/client_pb.ts @@ -15,7 +15,7 @@ import { file_tools_generator_v1alpha_cre_metadata } from '../../../../tools/gen export const file_capabilities_networking_http_v1alpha_client: GenFile = /*@__PURE__*/ fileDesc( - 'CjFjYXBhYmlsaXRpZXMvbmV0d29ya2luZy9odHRwL3YxYWxwaGEvY2xpZW50LnByb3RvEiRjYXBhYmlsaXRpZXMubmV0d29ya2luZy5odHRwLnYxYWxwaGEiSgoNQ2FjaGVTZXR0aW5ncxINCgVzdG9yZRgBIAEoCBIqCgdtYXhfYWdlGAIgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uIh4KDEhlYWRlclZhbHVlcxIOCgZ2YWx1ZXMYASADKAki7wMKB1JlcXVlc3QSCwoDdXJsGAEgASgJEg4KBm1ldGhvZBgCIAEoCRJPCgdoZWFkZXJzGAMgAygLMjouY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLlJlcXVlc3QuSGVhZGVyc0VudHJ5QgIYARIMCgRib2R5GAQgASgMEioKB3RpbWVvdXQYBSABKAsyGS5nb29nbGUucHJvdG9idWYuRHVyYXRpb24SSwoOY2FjaGVfc2V0dGluZ3MYBiABKAsyMy5jYXBhYmlsaXRpZXMubmV0d29ya2luZy5odHRwLnYxYWxwaGEuQ2FjaGVTZXR0aW5ncxJWCg1tdWx0aV9oZWFkZXJzGAcgAygLMj8uY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLlJlcXVlc3QuTXVsdGlIZWFkZXJzRW50cnkaLgoMSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEaZwoRTXVsdGlIZWFkZXJzRW50cnkSCwoDa2V5GAEgASgJEkEKBXZhbHVlGAIgASgLMjIuY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLkhlYWRlclZhbHVlczoCOAEi8QIKCFJlc3BvbnNlEhMKC3N0YXR1c19jb2RlGAEgASgNElAKB2hlYWRlcnMYAiADKAsyOy5jYXBhYmlsaXRpZXMubmV0d29ya2luZy5odHRwLnYxYWxwaGEuUmVzcG9uc2UuSGVhZGVyc0VudHJ5QgIYARIMCgRib2R5GAMgASgMElcKDW11bHRpX2hlYWRlcnMYBCADKAsyQC5jYXBhYmlsaXRpZXMubmV0d29ya2luZy5odHRwLnYxYWxwaGEuUmVzcG9uc2UuTXVsdGlIZWFkZXJzRW50cnkaLgoMSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEaZwoRTXVsdGlIZWFkZXJzRW50cnkSCwoDa2V5GAEgASgJEkEKBXZhbHVlGAIgASgLMjIuY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLkhlYWRlclZhbHVlczoCOAEymAEKBkNsaWVudBJsCgtTZW5kUmVxdWVzdBItLmNhcGFiaWxpdGllcy5uZXR3b3JraW5nLmh0dHAudjFhbHBoYS5SZXF1ZXN0Gi4uY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLlJlc3BvbnNlGiCCtRgcCAISGGh0dHAtYWN0aW9uc0AxLjAuMC1hbHBoYULqAQooY29tLmNhcGFiaWxpdGllcy5uZXR3b3JraW5nLmh0dHAudjFhbHBoYUILQ2xpZW50UHJvdG9QAaICA0NOSKoCJENhcGFiaWxpdGllcy5OZXR3b3JraW5nLkh0dHAuVjFhbHBoYcoCJENhcGFiaWxpdGllc1xOZXR3b3JraW5nXEh0dHBcVjFhbHBoYeICMENhcGFiaWxpdGllc1xOZXR3b3JraW5nXEh0dHBcVjFhbHBoYVxHUEJNZXRhZGF0YeoCJ0NhcGFiaWxpdGllczo6TmV0d29ya2luZzo6SHR0cDo6VjFhbHBoYWIGcHJvdG8z', + 'CjFjYXBhYmlsaXRpZXMvbmV0d29ya2luZy9odHRwL3YxYWxwaGEvY2xpZW50LnByb3RvEiRjYXBhYmlsaXRpZXMubmV0d29ya2luZy5odHRwLnYxYWxwaGEiSgoNQ2FjaGVTZXR0aW5ncxINCgVzdG9yZRgBIAEoCBIqCgdtYXhfYWdlGAIgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uIh4KDEhlYWRlclZhbHVlcxIOCgZ2YWx1ZXMYASADKAkiNAoITXRsc0F1dGgSEwoLcHJpdmF0ZV9rZXkYASABKAwSEwoLY2VydGlmaWNhdGUYAiABKAwiuwQKB1JlcXVlc3QSCwoDdXJsGAEgASgJEg4KBm1ldGhvZBgCIAEoCRJPCgdoZWFkZXJzGAMgAygLMjouY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLlJlcXVlc3QuSGVhZGVyc0VudHJ5QgIYARIMCgRib2R5GAQgASgMEioKB3RpbWVvdXQYBSABKAsyGS5nb29nbGUucHJvdG9idWYuRHVyYXRpb24SSwoOY2FjaGVfc2V0dGluZ3MYBiABKAsyMy5jYXBhYmlsaXRpZXMubmV0d29ya2luZy5odHRwLnYxYWxwaGEuQ2FjaGVTZXR0aW5ncxJWCg1tdWx0aV9oZWFkZXJzGAcgAygLMj8uY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLlJlcXVlc3QuTXVsdGlIZWFkZXJzRW50cnkSQQoEbXRscxgIIAEoCzIuLmNhcGFiaWxpdGllcy5uZXR3b3JraW5nLmh0dHAudjFhbHBoYS5NdGxzQXV0aEgAiAEBGi4KDEhlYWRlcnNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBGmcKEU11bHRpSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRJBCgV2YWx1ZRgCIAEoCzIyLmNhcGFiaWxpdGllcy5uZXR3b3JraW5nLmh0dHAudjFhbHBoYS5IZWFkZXJWYWx1ZXM6AjgBQgcKBV9tdGxzIvECCghSZXNwb25zZRITCgtzdGF0dXNfY29kZRgBIAEoDRJQCgdoZWFkZXJzGAIgAygLMjsuY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLlJlc3BvbnNlLkhlYWRlcnNFbnRyeUICGAESDAoEYm9keRgDIAEoDBJXCg1tdWx0aV9oZWFkZXJzGAQgAygLMkAuY2FwYWJpbGl0aWVzLm5ldHdvcmtpbmcuaHR0cC52MWFscGhhLlJlc3BvbnNlLk11bHRpSGVhZGVyc0VudHJ5Gi4KDEhlYWRlcnNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBGmcKEU11bHRpSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRJBCgV2YWx1ZRgCIAEoCzIyLmNhcGFiaWxpdGllcy5uZXR3b3JraW5nLmh0dHAudjFhbHBoYS5IZWFkZXJWYWx1ZXM6AjgBMpgBCgZDbGllbnQSbAoLU2VuZFJlcXVlc3QSLS5jYXBhYmlsaXRpZXMubmV0d29ya2luZy5odHRwLnYxYWxwaGEuUmVxdWVzdBouLmNhcGFiaWxpdGllcy5uZXR3b3JraW5nLmh0dHAudjFhbHBoYS5SZXNwb25zZRoggrUYHAgCEhhodHRwLWFjdGlvbnNAMS4wLjAtYWxwaGFC6gEKKGNvbS5jYXBhYmlsaXRpZXMubmV0d29ya2luZy5odHRwLnYxYWxwaGFCC0NsaWVudFByb3RvUAGiAgNDTkiqAiRDYXBhYmlsaXRpZXMuTmV0d29ya2luZy5IdHRwLlYxYWxwaGHKAiRDYXBhYmlsaXRpZXNcTmV0d29ya2luZ1xIdHRwXFYxYWxwaGHiAjBDYXBhYmlsaXRpZXNcTmV0d29ya2luZ1xIdHRwXFYxYWxwaGFcR1BCTWV0YWRhdGHqAidDYXBhYmlsaXRpZXM6Ok5ldHdvcmtpbmc6Okh0dHA6OlYxYWxwaGFiBnByb3RvMw', [file_google_protobuf_duration, file_tools_generator_v1alpha_cre_metadata], ) @@ -101,6 +101,48 @@ export const HeaderValuesSchema: GenMessage & { + /** + * @generated from field: bytes private_key = 1; + */ + privateKey: Uint8Array + + /** + * @generated from field: bytes certificate = 2; + */ + certificate: Uint8Array +} + +/** + * MtlsAuth represents the private-key/cert pair for mtls auth. + * + * @generated from message capabilities.networking.http.v1alpha.MtlsAuth + */ +export type MtlsAuthJson = { + /** + * @generated from field: bytes private_key = 1; + */ + privateKey?: string + + /** + * @generated from field: bytes certificate = 2; + */ + certificate?: string +} + +/** + * Describes the message capabilities.networking.http.v1alpha.MtlsAuth. + * Use `create(MtlsAuthSchema)` to create a new message. + */ +export const MtlsAuthSchema: GenMessage = + /*@__PURE__*/ + messageDesc(file_capabilities_networking_http_v1alpha_client, 2) + /** * @generated from message capabilities.networking.http.v1alpha.Request */ @@ -144,6 +186,11 @@ export type Request = Message<'capabilities.networking.http.v1alpha.Request'> & * @generated from field: map multi_headers = 7; */ multiHeaders: { [key: string]: HeaderValues } + + /** + * @generated from field: optional capabilities.networking.http.v1alpha.MtlsAuth mtls = 8; + */ + mtls?: MtlsAuth } /** @@ -189,6 +236,11 @@ export type RequestJson = { * @generated from field: map multi_headers = 7; */ multiHeaders?: { [key: string]: HeaderValuesJson } + + /** + * @generated from field: optional capabilities.networking.http.v1alpha.MtlsAuth mtls = 8; + */ + mtls?: MtlsAuthJson } /** @@ -197,7 +249,7 @@ export type RequestJson = { */ export const RequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_capabilities_networking_http_v1alpha_client, 2) + messageDesc(file_capabilities_networking_http_v1alpha_client, 3) /** * @generated from message capabilities.networking.http.v1alpha.Response @@ -261,7 +313,7 @@ export type ResponseJson = { */ export const ResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_capabilities_networking_http_v1alpha_client, 3) + messageDesc(file_capabilities_networking_http_v1alpha_client, 4) /** * @generated from service capabilities.networking.http.v1alpha.Client diff --git a/packages/cre-sdk/src/generated/chain-selectors/mainnet/evm/arc-mainnet.ts b/packages/cre-sdk/src/generated/chain-selectors/mainnet/evm/arc-mainnet.ts new file mode 100644 index 00000000..28bbc4d5 --- /dev/null +++ b/packages/cre-sdk/src/generated/chain-selectors/mainnet/evm/arc-mainnet.ts @@ -0,0 +1,16 @@ +// This file is auto-generated. Do not edit manually. +// Generated from: https://github.com/smartcontractkit/chain-selectors + +import type { NetworkInfo } from '@cre/sdk/utils/chain-selectors/types' + +const network: NetworkInfo = { + chainId: '5042', + chainSelector: { + name: 'arc-mainnet', + selector: 6370580034781731079n, + }, + chainFamily: 'evm', + networkType: 'mainnet', +} as const + +export default network diff --git a/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/glamsterdam-devnet-5.ts b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/glamsterdam-devnet-5.ts new file mode 100644 index 00000000..484833b7 --- /dev/null +++ b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/glamsterdam-devnet-5.ts @@ -0,0 +1,16 @@ +// This file is auto-generated. Do not edit manually. +// Generated from: https://github.com/smartcontractkit/chain-selectors + +import type { NetworkInfo } from '@cre/sdk/utils/chain-selectors/types' + +const network: NetworkInfo = { + chainId: '7095321190', + chainSelector: { + name: 'glamsterdam-devnet-5', + selector: 10073034426865795585n, + }, + chainFamily: 'evm', + networkType: 'testnet', +} as const + +export default network diff --git a/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/private-testnet-pumice.ts b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/private-testnet-pumice.ts new file mode 100644 index 00000000..7d181bbe --- /dev/null +++ b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/private-testnet-pumice.ts @@ -0,0 +1,16 @@ +// This file is auto-generated. Do not edit manually. +// Generated from: https://github.com/smartcontractkit/chain-selectors + +import type { NetworkInfo } from '@cre/sdk/utils/chain-selectors/types' + +const network: NetworkInfo = { + chainId: '2026041004', + chainSelector: { + name: 'private-testnet-pumice', + selector: 1564738277398880633n, + }, + chainFamily: 'evm', + networkType: 'testnet', +} as const + +export default network diff --git a/packages/cre-sdk/src/generated/networks.ts b/packages/cre-sdk/src/generated/networks.ts index be160f6e..f972f8e8 100644 --- a/packages/cre-sdk/src/generated/networks.ts +++ b/packages/cre-sdk/src/generated/networks.ts @@ -8,6 +8,7 @@ import mainnet_evm_ab_mainnet from './chain-selectors/mainnet/evm/ab-mainnet' import mainnet_evm_abstract_mainnet from './chain-selectors/mainnet/evm/abstract-mainnet' import mainnet_evm_adi_mainnet from './chain-selectors/mainnet/evm/adi-mainnet' import mainnet_evm_apechain_mainnet from './chain-selectors/mainnet/evm/apechain-mainnet' +import mainnet_evm_arc_mainnet from './chain-selectors/mainnet/evm/arc-mainnet' import mainnet_evm_areon_mainnet from './chain-selectors/mainnet/evm/areon-mainnet' import mainnet_evm_avalanche_mainnet from './chain-selectors/mainnet/evm/avalanche-mainnet' import mainnet_evm_avalanche_subnet_dexalot_mainnet from './chain-selectors/mainnet/evm/avalanche-subnet-dexalot-mainnet' @@ -213,6 +214,7 @@ import testnet_evm_filecoin_testnet from './chain-selectors/testnet/evm/filecoin import testnet_evm_gate_chain_testnet_meteora from './chain-selectors/testnet/evm/gate-chain-testnet-meteora' import testnet_evm_gate_layer_testnet from './chain-selectors/testnet/evm/gate-layer-testnet' import testnet_evm_geth_testnet from './chain-selectors/testnet/evm/geth-testnet' +import testnet_evm_glamsterdam_devnet_5 from './chain-selectors/testnet/evm/glamsterdam-devnet-5' import testnet_evm_gnosis_chain_testnet_chiado from './chain-selectors/testnet/evm/gnosis.chain-testnet-chiado' import testnet_evm_hedera_testnet from './chain-selectors/testnet/evm/hedera-testnet' import testnet_evm_hemi_testnet_sepolia from './chain-selectors/testnet/evm/hemi-testnet-sepolia' @@ -253,6 +255,7 @@ import testnet_evm_private_testnet_granite from './chain-selectors/testnet/evm/p import testnet_evm_private_testnet_mica from './chain-selectors/testnet/evm/private-testnet-mica' import testnet_evm_private_testnet_obsidian from './chain-selectors/testnet/evm/private-testnet-obsidian' import testnet_evm_private_testnet_opala from './chain-selectors/testnet/evm/private-testnet-opala' +import testnet_evm_private_testnet_pumice from './chain-selectors/testnet/evm/private-testnet-pumice' import testnet_evm_private_testnet_rhyolite from './chain-selectors/testnet/evm/private-testnet-rhyolite' import testnet_evm_robinhood_testnet from './chain-selectors/testnet/evm/robinhood-testnet' import testnet_evm_ronin_testnet_saigon from './chain-selectors/testnet/evm/ronin-testnet-saigon' @@ -420,6 +423,7 @@ export const allNetworks: NetworkInfo[] = [ mainnet_evm_ethereum_mainnet_mantle_1, testnet_evm_ethereum_testnet_goerli_mantle_1, testnet_evm_ethereum_testnet_sepolia_mantle_1, + mainnet_evm_arc_mainnet, mainnet_evm_superseed_mainnet, testnet_evm_binance_smart_chain_testnet_opbnb_1, testnet_evm_nexon_dev, @@ -556,9 +560,11 @@ export const allNetworks: NetworkInfo[] = [ mainnet_evm_tron_mainnet_evm, testnet_evm_zora_testnet, testnet_evm_private_testnet_rhyolite, + testnet_evm_private_testnet_pumice, testnet_evm_tron_testnet_shasta_evm, testnet_evm_tron_devnet_evm, testnet_evm_tron_testnet_nile_evm, + testnet_evm_glamsterdam_devnet_5, mainnet_solana_solana_mainnet, testnet_solana_solana_testnet, testnet_solana_solana_devnet, @@ -647,6 +653,7 @@ export const mainnet = { mainnet_evm_megaeth_mainnet, mainnet_evm_robinhood_mainnet, mainnet_evm_ethereum_mainnet_mantle_1, + mainnet_evm_arc_mainnet, mainnet_evm_superseed_mainnet, mainnet_evm_nibiru_mainnet, mainnet_evm_zetachain_mainnet, @@ -855,9 +862,11 @@ export const testnet = { testnet_evm_ethereum_testnet_sepolia_blast_1, testnet_evm_zora_testnet, testnet_evm_private_testnet_rhyolite, + testnet_evm_private_testnet_pumice, testnet_evm_tron_testnet_shasta_evm, testnet_evm_tron_devnet_evm, testnet_evm_tron_testnet_nile_evm, + testnet_evm_glamsterdam_devnet_5, ] as const, solana: [testnet_solana_solana_testnet, testnet_solana_solana_devnet] as const, aptos: [testnet_aptos_aptos_testnet, testnet_aptos_aptos_localnet] as const, @@ -940,6 +949,7 @@ export const mainnetBySelector = new Map([ [6093540873831549674n, mainnet_evm_megaeth_mainnet], [6180753054346818345n, mainnet_evm_robinhood_mainnet], [1556008542357238666n, mainnet_evm_ethereum_mainnet_mantle_1], + [6370580034781731079n, mainnet_evm_arc_mainnet], [470401360549526817n, mainnet_evm_superseed_mainnet], [17349189558768828726n, mainnet_evm_nibiru_mainnet], [10817664450262215148n, mainnet_evm_zetachain_mainnet], @@ -1146,9 +1156,11 @@ export const testnetBySelector = new Map([ [2027362563942762617n, testnet_evm_ethereum_testnet_sepolia_blast_1], [16244020411108056671n, testnet_evm_zora_testnet], [604447335222770945n, testnet_evm_private_testnet_rhyolite], + [1564738277398880633n, testnet_evm_private_testnet_pumice], [13231703482326770598n, testnet_evm_tron_testnet_shasta_evm], [13231703482326770600n, testnet_evm_tron_devnet_evm], [2052925811360307749n, testnet_evm_tron_testnet_nile_evm], + [10073034426865795585n, testnet_evm_glamsterdam_devnet_5], [6302590918974934319n, testnet_solana_solana_testnet], [16423721717087811551n, testnet_solana_solana_devnet], [743186221051783445n, testnet_aptos_aptos_testnet], @@ -1232,6 +1244,7 @@ export const mainnetByName = new Map([ ['megaeth-mainnet', mainnet_evm_megaeth_mainnet], ['robinhood-mainnet', mainnet_evm_robinhood_mainnet], ['ethereum-mainnet-mantle-1', mainnet_evm_ethereum_mainnet_mantle_1], + ['arc-mainnet', mainnet_evm_arc_mainnet], ['superseed-mainnet', mainnet_evm_superseed_mainnet], ['nibiru-mainnet', mainnet_evm_nibiru_mainnet], ['zetachain-mainnet', mainnet_evm_zetachain_mainnet], @@ -1453,9 +1466,11 @@ export const testnetByName = new Map([ ['ethereum-testnet-sepolia-blast-1', testnet_evm_ethereum_testnet_sepolia_blast_1], ['zora-testnet', testnet_evm_zora_testnet], ['private-testnet-rhyolite', testnet_evm_private_testnet_rhyolite], + ['private-testnet-pumice', testnet_evm_private_testnet_pumice], ['tron-testnet-shasta-evm', testnet_evm_tron_testnet_shasta_evm], ['tron-devnet-evm', testnet_evm_tron_devnet_evm], ['tron-testnet-nile-evm', testnet_evm_tron_testnet_nile_evm], + ['glamsterdam-devnet-5', testnet_evm_glamsterdam_devnet_5], ['solana-testnet', testnet_solana_solana_testnet], ['solana-devnet', testnet_solana_solana_devnet], ['aptos-testnet', testnet_aptos_aptos_testnet], @@ -1540,6 +1555,7 @@ export const mainnetBySelectorByFamily = { [6093540873831549674n, mainnet_evm_megaeth_mainnet], [6180753054346818345n, mainnet_evm_robinhood_mainnet], [1556008542357238666n, mainnet_evm_ethereum_mainnet_mantle_1], + [6370580034781731079n, mainnet_evm_arc_mainnet], [470401360549526817n, mainnet_evm_superseed_mainnet], [17349189558768828726n, mainnet_evm_nibiru_mainnet], [10817664450262215148n, mainnet_evm_zetachain_mainnet], @@ -1748,9 +1764,11 @@ export const testnetBySelectorByFamily = { [2027362563942762617n, testnet_evm_ethereum_testnet_sepolia_blast_1], [16244020411108056671n, testnet_evm_zora_testnet], [604447335222770945n, testnet_evm_private_testnet_rhyolite], + [1564738277398880633n, testnet_evm_private_testnet_pumice], [13231703482326770598n, testnet_evm_tron_testnet_shasta_evm], [13231703482326770600n, testnet_evm_tron_devnet_evm], [2052925811360307749n, testnet_evm_tron_testnet_nile_evm], + [10073034426865795585n, testnet_evm_glamsterdam_devnet_5], ]), solana: new Map([ [6302590918974934319n, testnet_solana_solana_testnet], @@ -1846,6 +1864,7 @@ export const mainnetByNameByFamily = { ['megaeth-mainnet', mainnet_evm_megaeth_mainnet], ['robinhood-mainnet', mainnet_evm_robinhood_mainnet], ['ethereum-mainnet-mantle-1', mainnet_evm_ethereum_mainnet_mantle_1], + ['arc-mainnet', mainnet_evm_arc_mainnet], ['superseed-mainnet', mainnet_evm_superseed_mainnet], ['nibiru-mainnet', mainnet_evm_nibiru_mainnet], ['zetachain-mainnet', mainnet_evm_zetachain_mainnet], @@ -2072,9 +2091,11 @@ export const testnetByNameByFamily = { ['ethereum-testnet-sepolia-blast-1', testnet_evm_ethereum_testnet_sepolia_blast_1], ['zora-testnet', testnet_evm_zora_testnet], ['private-testnet-rhyolite', testnet_evm_private_testnet_rhyolite], + ['private-testnet-pumice', testnet_evm_private_testnet_pumice], ['tron-testnet-shasta-evm', testnet_evm_tron_testnet_shasta_evm], ['tron-devnet-evm', testnet_evm_tron_devnet_evm], ['tron-testnet-nile-evm', testnet_evm_tron_testnet_nile_evm], + ['glamsterdam-devnet-5', testnet_evm_glamsterdam_devnet_5], ]), solana: new Map([ ['solana-testnet', testnet_solana_solana_testnet], diff --git a/submodules/chainlink-protos b/submodules/chainlink-protos index 5f00275c..e7de09d4 160000 --- a/submodules/chainlink-protos +++ b/submodules/chainlink-protos @@ -1 +1 @@ -Subproject commit 5f00275cf10d7d590cad6caa74988d90630d7ca6 +Subproject commit e7de09d4f5538cd787960b02010a6cc988d0c4c6 From ebb07e917ff2795df5e9115a87f33d8e5d6559f0 Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:14:30 -0500 Subject: [PATCH 12/20] Set new versions for the release (#272) --- packages/cre-sdk-examples/package.json | 2 +- packages/cre-sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cre-sdk-examples/package.json b/packages/cre-sdk-examples/package.json index 973e0da8..85606927 100644 --- a/packages/cre-sdk-examples/package.json +++ b/packages/cre-sdk-examples/package.json @@ -1,7 +1,7 @@ { "name": "@chainlink/cre-sdk-examples", "private": true, - "version": "1.11.0", + "version": "1.12.0", "type": "module", "author": "Ernest Nowacki", "license": "BUSL-1.1", diff --git a/packages/cre-sdk/package.json b/packages/cre-sdk/package.json index 257c4c1a..5b97e993 100644 --- a/packages/cre-sdk/package.json +++ b/packages/cre-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/cre-sdk", - "version": "1.11.0", + "version": "1.12.0", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", From 7aabf2e7d5cf665b205fa9ecc66beab2b480c9ce Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:15:50 -0500 Subject: [PATCH 13/20] Set new versions for the release (#273) --- packages/cre-sdk-examples/package.json | 2 +- packages/cre-sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cre-sdk-examples/package.json b/packages/cre-sdk-examples/package.json index 85606927..9e7d8549 100644 --- a/packages/cre-sdk-examples/package.json +++ b/packages/cre-sdk-examples/package.json @@ -1,7 +1,7 @@ { "name": "@chainlink/cre-sdk-examples", "private": true, - "version": "1.12.0", + "version": "1.13.0", "type": "module", "author": "Ernest Nowacki", "license": "BUSL-1.1", diff --git a/packages/cre-sdk/package.json b/packages/cre-sdk/package.json index 5b97e993..d945654b 100644 --- a/packages/cre-sdk/package.json +++ b/packages/cre-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/cre-sdk", - "version": "1.12.0", + "version": "1.13.0", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", From 8a0b15831308bcccc01e813db160cd6f26b4c36f Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:36:52 -0500 Subject: [PATCH 14/20] Bumped chain-selectors (#274) --- bun.lock | 8 ++++---- packages/cre-sdk-examples/package.json | 2 +- packages/cre-sdk/package.json | 4 ++-- .../blockchain/evm/v1alpha/client_sdk_gen.ts | 1 + .../blockchain/evm/v1alpha/client_pb.ts | 2 +- .../mainnet/evm/dtcc-mainnet-appchain.ts | 16 ++++++++++++++++ .../testnet/evm/private-testnet-quartzite.ts | 16 ++++++++++++++++ packages/cre-sdk/src/generated/networks.ts | 14 ++++++++++++++ submodules/chainlink-protos | 2 +- 9 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 packages/cre-sdk/src/generated/chain-selectors/mainnet/evm/dtcc-mainnet-appchain.ts create mode 100644 packages/cre-sdk/src/generated/chain-selectors/testnet/evm/private-testnet-quartzite.ts diff --git a/bun.lock b/bun.lock index 837d3c53..c2ee1031 100644 --- a/bun.lock +++ b/bun.lock @@ -63,7 +63,7 @@ }, "packages/cre-sdk": { "name": "@chainlink/cre-sdk", - "version": "1.11.0", + "version": "1.13.0", "bin": { "cre-compile": "bin/cre-compile.ts", }, @@ -79,7 +79,7 @@ "@biomejs/biome": "2.3.14", "@bufbuild/buf": "1.56.0", "@types/bun": "1.3.8", - "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#c6379b1272aa41e6977f60799c82a49465a14b3f", + "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#3e718d1c9910b2952c50befe378b0fe8707e6647", "fast-glob": "3.3.3", "ts-proto": "2.7.5", "typescript": "5.9.3", @@ -88,7 +88,7 @@ }, "packages/cre-sdk-examples": { "name": "@chainlink/cre-sdk-examples", - "version": "1.11.0", + "version": "1.13.0", "dependencies": { "@bufbuild/protobuf": "2.6.3", "@chainlink/cre-sdk": "workspace:*", @@ -210,7 +210,7 @@ "case-anything": ["case-anything@2.1.13", "", {}, "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng=="], - "chain-selectors": ["chain-selectors@github:smartcontractkit/chain-selectors#c6379b1", {}, "smartcontractkit-chain-selectors-c6379b1"], + "chain-selectors": ["chain-selectors@github:smartcontractkit/chain-selectors#3e718d1", {}, "smartcontractkit-chain-selectors-3e718d1"], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], diff --git a/packages/cre-sdk-examples/package.json b/packages/cre-sdk-examples/package.json index 9e7d8549..bd89d419 100644 --- a/packages/cre-sdk-examples/package.json +++ b/packages/cre-sdk-examples/package.json @@ -1,7 +1,7 @@ { "name": "@chainlink/cre-sdk-examples", "private": true, - "version": "1.13.0", + "version": "1.14.0", "type": "module", "author": "Ernest Nowacki", "license": "BUSL-1.1", diff --git a/packages/cre-sdk/package.json b/packages/cre-sdk/package.json index d945654b..29696c1f 100644 --- a/packages/cre-sdk/package.json +++ b/packages/cre-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/cre-sdk", - "version": "1.13.0", + "version": "1.14.0", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -70,7 +70,7 @@ "@biomejs/biome": "2.3.14", "@bufbuild/buf": "1.56.0", "@types/bun": "1.3.8", - "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#c6379b1272aa41e6977f60799c82a49465a14b3f", + "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#3e718d1c9910b2952c50befe378b0fe8707e6647", "fast-glob": "3.3.3", "ts-proto": "2.7.5", "typescript": "5.9.3", diff --git a/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts b/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts index 5e2c7286..4e87ecae 100644 --- a/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts +++ b/packages/cre-sdk/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts @@ -173,6 +173,7 @@ export class ClientCapability { 'polygon-testnet-amoy': 16281711391670634445n, 'private-testnet-andesite': 6915682381028791124n, 'private-testnet-pumice': 1564738277398880633n, + 'private-testnet-quartzite': 4175996748267305081n, 'private-testnet-rhyolite': 604447335222770945n, 'sonic-mainnet': 1673871237479749969n, 'sonic-testnet': 1763698235108410440n, diff --git a/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts b/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts index edb623a7..938a6513 100644 --- a/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts +++ b/packages/cre-sdk/src/generated/capabilities/blockchain/evm/v1alpha/client_pb.ts @@ -17,7 +17,7 @@ import { file_values_v1_values } from '../../../../values/v1/values_pb' export const file_capabilities_blockchain_evm_v1alpha_client: GenFile = /*@__PURE__*/ fileDesc( - 'CjBjYXBhYmlsaXRpZXMvYmxvY2tjaGFpbi9ldm0vdjFhbHBoYS9jbGllbnQucHJvdG8SI2NhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhIh0KC1RvcGljVmFsdWVzEg4KBnZhbHVlcxgBIAMoDCK4AQoXRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QSEQoJYWRkcmVzc2VzGAEgAygMEkAKBnRvcGljcxgCIAMoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRvcGljVmFsdWVzEkgKCmNvbmZpZGVuY2UYAyABKA4yNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Db25maWRlbmNlTGV2ZWwiegoTQ2FsbENvbnRyYWN0UmVxdWVzdBI6CgRjYWxsGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZxInCgxibG9ja19udW1iZXIYAiABKAsyES52YWx1ZXMudjEuQmlnSW50IiEKEUNhbGxDb250cmFjdFJlcGx5EgwKBGRhdGEYASABKAwiWwoRRmlsdGVyTG9nc1JlcXVlc3QSRgoMZmlsdGVyX3F1ZXJ5GAEgASgLMjAuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyUXVlcnkiSQoPRmlsdGVyTG9nc1JlcGx5EjYKBGxvZ3MYASADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cixwEKA0xvZxIPCgdhZGRyZXNzGAEgASgMEg4KBnRvcGljcxgCIAMoDBIPCgd0eF9oYXNoGAMgASgMEhIKCmJsb2NrX2hhc2gYBCABKAwSDAoEZGF0YRgFIAEoDBIRCglldmVudF9zaWcYBiABKAwSJwoMYmxvY2tfbnVtYmVyGAcgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIQCgh0eF9pbmRleBgIIAEoDRINCgVpbmRleBgJIAEoDRIPCgdyZW1vdmVkGAogASgIIjEKB0NhbGxNc2cSDAoEZnJvbRgBIAEoDBIKCgJ0bxgCIAEoDBIMCgRkYXRhGAMgASgMIr0BCgtGaWx0ZXJRdWVyeRISCgpibG9ja19oYXNoGAEgASgMEiUKCmZyb21fYmxvY2sYAiABKAsyES52YWx1ZXMudjEuQmlnSW50EiMKCHRvX2Jsb2NrGAMgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIRCglhZGRyZXNzZXMYBCADKAwSOwoGdG9waWNzGAUgAygLMisuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuVG9waWNzIhcKBlRvcGljcxINCgV0b3BpYxgBIAMoDCJMChBCYWxhbmNlQXRSZXF1ZXN0Eg8KB2FjY291bnQYASABKAwSJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludCI0Cg5CYWxhbmNlQXRSZXBseRIiCgdiYWxhbmNlGAEgASgLMhEudmFsdWVzLnYxLkJpZ0ludCJPChJFc3RpbWF0ZUdhc1JlcXVlc3QSOQoDbXNnGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZyIjChBFc3RpbWF0ZUdhc1JlcGx5Eg8KA2dhcxgBIAEoBEICMAAiKwobR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXF1ZXN0EgwKBGhhc2gYASABKAwiYgoZR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXBseRJFCgt0cmFuc2FjdGlvbhgBIAEoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRyYW5zYWN0aW9uIqEBCgtUcmFuc2FjdGlvbhIRCgVub25jZRgBIAEoBEICMAASDwoDZ2FzGAIgASgEQgIwABIKCgJ0bxgDIAEoDBIMCgRkYXRhGAQgASgMEgwKBGhhc2gYBSABKAwSIAoFdmFsdWUYBiABKAsyES52YWx1ZXMudjEuQmlnSW50EiQKCWdhc19wcmljZRgHIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiLAocR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVxdWVzdBIMCgRoYXNoGAEgASgMIlsKGkdldFRyYW5zYWN0aW9uUmVjZWlwdFJlcGx5Ej0KB3JlY2VpcHQYASABKAsyLC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXB0IpkCCgdSZWNlaXB0EhIKBnN0YXR1cxgBIAEoBEICMAASFAoIZ2FzX3VzZWQYAiABKARCAjAAEhQKCHR4X2luZGV4GAMgASgEQgIwABISCgpibG9ja19oYXNoGAQgASgMEjYKBGxvZ3MYBiADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cSDwoHdHhfaGFzaBgHIAEoDBIuChNlZmZlY3RpdmVfZ2FzX3ByaWNlGAggASgLMhEudmFsdWVzLnYxLkJpZ0ludBInCgxibG9ja19udW1iZXIYCSABKAsyES52YWx1ZXMudjEuQmlnSW50EhgKEGNvbnRyYWN0X2FkZHJlc3MYCiABKAwiQAoVSGVhZGVyQnlOdW1iZXJSZXF1ZXN0EicKDGJsb2NrX251bWJlchgBIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiUgoTSGVhZGVyQnlOdW1iZXJSZXBseRI7CgZoZWFkZXIYASABKAsyKy5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5IZWFkZXIiawoGSGVhZGVyEhUKCXRpbWVzdGFtcBgBIAEoBEICMAASJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIMCgRoYXNoGAMgASgMEhMKC3BhcmVudF9oYXNoGAQgASgMIqsBChJXcml0ZVJlcG9ydFJlcXVlc3QSEAoIcmVjZWl2ZXIYASABKAwSKwoGcmVwb3J0GAIgASgLMhsuc2RrLnYxYWxwaGEuUmVwb3J0UmVzcG9uc2USRwoKZ2FzX2NvbmZpZxgDIAEoCzIuLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkdhc0NvbmZpZ0gAiAEBQg0KC19nYXNfY29uZmlnIiIKCUdhc0NvbmZpZxIVCglnYXNfbGltaXQYASABKARCAjAAIocDChBXcml0ZVJlcG9ydFJlcGx5EkAKCXR4X3N0YXR1cxgBIAEoDjItLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlR4U3RhdHVzEnUKInJlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXMYAiABKA4yRC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzSACIAQESFAoHdHhfaGFzaBgDIAEoDEgBiAEBEi8KD3RyYW5zYWN0aW9uX2ZlZRgEIAEoCzIRLnZhbHVlcy52MS5CaWdJbnRIAogBARIaCg1lcnJvcl9tZXNzYWdlGAUgASgJSAOIAQFCJQojX3JlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXNCCgoIX3R4X2hhc2hCEgoQX3RyYW5zYWN0aW9uX2ZlZUIQCg5fZXJyb3JfbWVzc2FnZSppCg9Db25maWRlbmNlTGV2ZWwSGQoVQ09ORklERU5DRV9MRVZFTF9TQUZFEAASGwoXQ09ORklERU5DRV9MRVZFTF9MQVRFU1QQARIeChpDT05GSURFTkNFX0xFVkVMX0ZJTkFMSVpFRBACKoIBCh9SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzEi4KKlJFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfU1VDQ0VTUxAAEi8KK1JFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfUkVWRVJURUQQASpOCghUeFN0YXR1cxITCg9UWF9TVEFUVVNfRkFUQUwQABIWChJUWF9TVEFUVVNfUkVWRVJURUQQARIVChFUWF9TVEFUVVNfU1VDQ0VTUxACMpwZCgZDbGllbnQSgAEKDENhbGxDb250cmFjdBI4LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkNhbGxDb250cmFjdFJlcXVlc3QaNi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5DYWxsQ29udHJhY3RSZXBseRJ6CgpGaWx0ZXJMb2dzEjYuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nc1JlcXVlc3QaNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5GaWx0ZXJMb2dzUmVwbHkSdwoJQmFsYW5jZUF0EjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQmFsYW5jZUF0UmVxdWVzdBozLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkJhbGFuY2VBdFJlcGx5En0KC0VzdGltYXRlR2FzEjcuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXF1ZXN0GjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXBseRKYAQoUR2V0VHJhbnNhY3Rpb25CeUhhc2gSQC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcXVlc3QaPi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcGx5EpsBChVHZXRUcmFuc2FjdGlvblJlY2VpcHQSQS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvblJlY2VpcHRSZXF1ZXN0Gj8uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVwbHkShgEKDkhlYWRlckJ5TnVtYmVyEjouY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXF1ZXN0GjguY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXBseRJ2CgpMb2dUcmlnZ2VyEjwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QaKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cwARJ9CgtXcml0ZVJlcG9ydBI3LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVxdWVzdBo1LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVwbHka4Q+CtRjcDwgBEglldm1AMS4wLjAazA8KDUNoYWluU2VsZWN0b3ISug8Stw8KFwoLYWRpLW1haW5uZXQQ/PDmwrfn3ao4ChgKC2FkaS10ZXN0bmV0EP2myPr5hozaggEKJAoXYXBlY2hhaW4tdGVzdG5ldC1jdXJ0aXMQwcO0+I3EkrKJAQoXCgthcmMtdGVzdG5ldBDnxoye19fQjSoKHQoRYXZhbGFuY2hlLW1haW5uZXQQ1eeKwOHVmKRZCiMKFmF2YWxhbmNoZS10ZXN0bmV0LWZ1amkQm/n8kKLjqPjMAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLW1haW5uZXQQz/eU8djtlbidAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLXRlc3RuZXQQ+62+nICu5Iq4AQoYCgxjZWxvLW1haW5uZXQQhtTo2IaTiNcSChgKDGNlbG8tc2Vwb2xpYRDEw/yL/OedmjQKGgoOY3Jvbm9zLXRlc3RuZXQQ/dnureDe2sgpCiIKFWR0Y2MtdGVzdG5ldC1hbmRlc2l0ZRDSg+PQmZblpNcBChwKEGV0aGVyZXVtLW1haW5uZXQQlfbx5M+ypsJFCicKG2V0aGVyZXVtLW1haW5uZXQtYXJiaXRydW0tMRDE6I3Njpuh10QKJAoXZXRoZXJldW0tbWFpbm5ldC1iYXNlLTEQgv+rov65kNPdAQoiChZldGhlcmV1bS1tYWlubmV0LWluay0xEKCwpum35qqEMAokChhldGhlcmV1bS1tYWlubmV0LWxpbmVhLTEQtrrpmMu9sJtACiUKGWV0aGVyZXVtLW1haW5uZXQtbWFudGxlLTEQiue0leewg8wVCicKG2V0aGVyZXVtLW1haW5uZXQtb3B0aW1pc20tMRC4lY/D9/7Q6TMKJgoZZXRoZXJldW0tbWFpbm5ldC1zY3JvbGwtMRC4vOTrxL7In7cBCikKHWV0aGVyZXVtLW1haW5uZXQtd29ybGRjaGFpbi0xEIfvurfFtsK4HAolChlldGhlcmV1bS1tYWlubmV0LXhsYXllci0xEJal/JymqO/tKQolChlldGhlcmV1bS1tYWlubmV0LXprc3luYy0xEJTul9nttLHXFQolChhldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEQ2bXkzvzJ7qDeAQovCiNldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtYXJiaXRydW0tMRDqzu7/6raEozAKLAofZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLWJhc2UtMRC4yrnv9pCuyI8BCiwKIGV0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1saW5lYS0xEOuq1P6C+eavTwotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtbWFudGxlLTEQ1ca47s328qZyCi8KI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1vcHRpbWlzbS0xEJ+GxaG+2MPASAotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtc2Nyb2xsLTEQi+m0vtu67dEfCjAKI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS11bmljaGFpbi0xELTe/uDsl6mWxAEKMQolZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXdvcmxkY2hhaW4tMRC63+DFx6nzxUkKLQohZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXprc3luYy0xELfB/P3yxIDeXwogChRnbm9zaXNfY2hhaW4tbWFpbm5ldBD0kq3a8qKuugYKJwobZ25vc2lzX2NoYWluLXRlc3RuZXQtY2hpYWRvELOxgtCbpY+PewofChNoeXBlcmxpcXVpZC1tYWlubmV0EKez+N3O0enyIQofChNoeXBlcmxpcXVpZC10ZXN0bmV0EIjO3ciX4Mm9OwogChNpbmstdGVzdG5ldC1zZXBvbGlhEOj0p6Xz5pbAhwEKGQoNam92YXktbWFpbm5ldBC1w8SaoYDfkhUKGQoNam92YXktdGVzdG5ldBDkz4qE3rLejg0KGwoPbWVnYWV0aC1tYWlubmV0EOqVtsi85KbIVAoeChFtZWdhZXRoLXRlc3RuZXQtMhDjjd6IsY/9k/0BCiQKF3BoYXJvcy1hdGxhbnRpYy10ZXN0bmV0EMyZ7eDOvK+03wEKGgoOcGhhcm9zLW1haW5uZXQQyMGHnvXvzaFsChsKDnBsYXNtYS1tYWlubmV0EPib8dHaydXGgQEKGgoOcGxhc21hLXRlc3RuZXQQ1Zu/pcO0mYc3ChsKD3BvbHlnb24tbWFpbm5ldBCxq+TwmpKGnTgKIQoUcG9seWdvbi10ZXN0bmV0LWFtb3kQzY/W3/HHkPrhAQokChhwcml2YXRlLXRlc3RuZXQtYW5kZXNpdGUQ1KaYpcGP3PxfCiIKFnByaXZhdGUtdGVzdG5ldC1wdW1pY2UQ+cLEtsSlxNsVCiQKGHByaXZhdGUtdGVzdG5ldC1yaHlvbGl0ZRCB+onr4bTbsQgKGQoNc29uaWMtbWFpbm5ldBDRsuXt2aCynRcKGQoNc29uaWMtdGVzdG5ldBDIiPvUtMb6vBgKGAoLdGFjLXRlc3RuZXQQ1duN4/ufk9eDAQobCg54bGF5ZXItdGVzdG5ldBDJvqG0rcy83Y0BQuUBCidjb20uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGFCC0NsaWVudFByb3RvUAGiAgNDQkWqAiNDYXBhYmlsaXRpZXMuQmxvY2tjaGFpbi5Fdm0uVjFhbHBoYcoCI0NhcGFiaWxpdGllc1xCbG9ja2NoYWluXEV2bVxWMWFscGhh4gIvQ2FwYWJpbGl0aWVzXEJsb2NrY2hhaW5cRXZtXFYxYWxwaGFcR1BCTWV0YWRhdGHqAiZDYXBhYmlsaXRpZXM6OkJsb2NrY2hhaW46OkV2bTo6VjFhbHBoYWIGcHJvdG8z', + 'CjBjYXBhYmlsaXRpZXMvYmxvY2tjaGFpbi9ldm0vdjFhbHBoYS9jbGllbnQucHJvdG8SI2NhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhIh0KC1RvcGljVmFsdWVzEg4KBnZhbHVlcxgBIAMoDCK4AQoXRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QSEQoJYWRkcmVzc2VzGAEgAygMEkAKBnRvcGljcxgCIAMoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRvcGljVmFsdWVzEkgKCmNvbmZpZGVuY2UYAyABKA4yNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Db25maWRlbmNlTGV2ZWwiegoTQ2FsbENvbnRyYWN0UmVxdWVzdBI6CgRjYWxsGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZxInCgxibG9ja19udW1iZXIYAiABKAsyES52YWx1ZXMudjEuQmlnSW50IiEKEUNhbGxDb250cmFjdFJlcGx5EgwKBGRhdGEYASABKAwiWwoRRmlsdGVyTG9nc1JlcXVlc3QSRgoMZmlsdGVyX3F1ZXJ5GAEgASgLMjAuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyUXVlcnkiSQoPRmlsdGVyTG9nc1JlcGx5EjYKBGxvZ3MYASADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cixwEKA0xvZxIPCgdhZGRyZXNzGAEgASgMEg4KBnRvcGljcxgCIAMoDBIPCgd0eF9oYXNoGAMgASgMEhIKCmJsb2NrX2hhc2gYBCABKAwSDAoEZGF0YRgFIAEoDBIRCglldmVudF9zaWcYBiABKAwSJwoMYmxvY2tfbnVtYmVyGAcgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIQCgh0eF9pbmRleBgIIAEoDRINCgVpbmRleBgJIAEoDRIPCgdyZW1vdmVkGAogASgIIjEKB0NhbGxNc2cSDAoEZnJvbRgBIAEoDBIKCgJ0bxgCIAEoDBIMCgRkYXRhGAMgASgMIr0BCgtGaWx0ZXJRdWVyeRISCgpibG9ja19oYXNoGAEgASgMEiUKCmZyb21fYmxvY2sYAiABKAsyES52YWx1ZXMudjEuQmlnSW50EiMKCHRvX2Jsb2NrGAMgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIRCglhZGRyZXNzZXMYBCADKAwSOwoGdG9waWNzGAUgAygLMisuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuVG9waWNzIhcKBlRvcGljcxINCgV0b3BpYxgBIAMoDCJMChBCYWxhbmNlQXRSZXF1ZXN0Eg8KB2FjY291bnQYASABKAwSJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludCI0Cg5CYWxhbmNlQXRSZXBseRIiCgdiYWxhbmNlGAEgASgLMhEudmFsdWVzLnYxLkJpZ0ludCJPChJFc3RpbWF0ZUdhc1JlcXVlc3QSOQoDbXNnGAEgASgLMiwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQ2FsbE1zZyIjChBFc3RpbWF0ZUdhc1JlcGx5Eg8KA2dhcxgBIAEoBEICMAAiKwobR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXF1ZXN0EgwKBGhhc2gYASABKAwiYgoZR2V0VHJhbnNhY3Rpb25CeUhhc2hSZXBseRJFCgt0cmFuc2FjdGlvbhgBIAEoCzIwLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlRyYW5zYWN0aW9uIqEBCgtUcmFuc2FjdGlvbhIRCgVub25jZRgBIAEoBEICMAASDwoDZ2FzGAIgASgEQgIwABIKCgJ0bxgDIAEoDBIMCgRkYXRhGAQgASgMEgwKBGhhc2gYBSABKAwSIAoFdmFsdWUYBiABKAsyES52YWx1ZXMudjEuQmlnSW50EiQKCWdhc19wcmljZRgHIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiLAocR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVxdWVzdBIMCgRoYXNoGAEgASgMIlsKGkdldFRyYW5zYWN0aW9uUmVjZWlwdFJlcGx5Ej0KB3JlY2VpcHQYASABKAsyLC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXB0IpkCCgdSZWNlaXB0EhIKBnN0YXR1cxgBIAEoBEICMAASFAoIZ2FzX3VzZWQYAiABKARCAjAAEhQKCHR4X2luZGV4GAMgASgEQgIwABISCgpibG9ja19oYXNoGAQgASgMEjYKBGxvZ3MYBiADKAsyKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cSDwoHdHhfaGFzaBgHIAEoDBIuChNlZmZlY3RpdmVfZ2FzX3ByaWNlGAggASgLMhEudmFsdWVzLnYxLkJpZ0ludBInCgxibG9ja19udW1iZXIYCSABKAsyES52YWx1ZXMudjEuQmlnSW50EhgKEGNvbnRyYWN0X2FkZHJlc3MYCiABKAwiQAoVSGVhZGVyQnlOdW1iZXJSZXF1ZXN0EicKDGJsb2NrX251bWJlchgBIAEoCzIRLnZhbHVlcy52MS5CaWdJbnQiUgoTSGVhZGVyQnlOdW1iZXJSZXBseRI7CgZoZWFkZXIYASABKAsyKy5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5IZWFkZXIiawoGSGVhZGVyEhUKCXRpbWVzdGFtcBgBIAEoBEICMAASJwoMYmxvY2tfbnVtYmVyGAIgASgLMhEudmFsdWVzLnYxLkJpZ0ludBIMCgRoYXNoGAMgASgMEhMKC3BhcmVudF9oYXNoGAQgASgMIqsBChJXcml0ZVJlcG9ydFJlcXVlc3QSEAoIcmVjZWl2ZXIYASABKAwSKwoGcmVwb3J0GAIgASgLMhsuc2RrLnYxYWxwaGEuUmVwb3J0UmVzcG9uc2USRwoKZ2FzX2NvbmZpZxgDIAEoCzIuLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkdhc0NvbmZpZ0gAiAEBQg0KC19nYXNfY29uZmlnIiIKCUdhc0NvbmZpZxIVCglnYXNfbGltaXQYASABKARCAjAAIocDChBXcml0ZVJlcG9ydFJlcGx5EkAKCXR4X3N0YXR1cxgBIAEoDjItLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLlR4U3RhdHVzEnUKInJlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXMYAiABKA4yRC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzSACIAQESFAoHdHhfaGFzaBgDIAEoDEgBiAEBEi8KD3RyYW5zYWN0aW9uX2ZlZRgEIAEoCzIRLnZhbHVlcy52MS5CaWdJbnRIAogBARIaCg1lcnJvcl9tZXNzYWdlGAUgASgJSAOIAQFCJQojX3JlY2VpdmVyX2NvbnRyYWN0X2V4ZWN1dGlvbl9zdGF0dXNCCgoIX3R4X2hhc2hCEgoQX3RyYW5zYWN0aW9uX2ZlZUIQCg5fZXJyb3JfbWVzc2FnZSppCg9Db25maWRlbmNlTGV2ZWwSGQoVQ09ORklERU5DRV9MRVZFTF9TQUZFEAASGwoXQ09ORklERU5DRV9MRVZFTF9MQVRFU1QQARIeChpDT05GSURFTkNFX0xFVkVMX0ZJTkFMSVpFRBACKoIBCh9SZWNlaXZlckNvbnRyYWN0RXhlY3V0aW9uU3RhdHVzEi4KKlJFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfU1VDQ0VTUxAAEi8KK1JFQ0VJVkVSX0NPTlRSQUNUX0VYRUNVVElPTl9TVEFUVVNfUkVWRVJURUQQASpOCghUeFN0YXR1cxITCg9UWF9TVEFUVVNfRkFUQUwQABIWChJUWF9TVEFUVVNfUkVWRVJURUQQARIVChFUWF9TVEFUVVNfU1VDQ0VTUxACMsMZCgZDbGllbnQSgAEKDENhbGxDb250cmFjdBI4LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkNhbGxDb250cmFjdFJlcXVlc3QaNi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5DYWxsQ29udHJhY3RSZXBseRJ6CgpGaWx0ZXJMb2dzEjYuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nc1JlcXVlc3QaNC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5GaWx0ZXJMb2dzUmVwbHkSdwoJQmFsYW5jZUF0EjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuQmFsYW5jZUF0UmVxdWVzdBozLmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLkJhbGFuY2VBdFJlcGx5En0KC0VzdGltYXRlR2FzEjcuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXF1ZXN0GjUuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRXN0aW1hdGVHYXNSZXBseRKYAQoUR2V0VHJhbnNhY3Rpb25CeUhhc2gSQC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcXVlc3QaPi5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvbkJ5SGFzaFJlcGx5EpsBChVHZXRUcmFuc2FjdGlvblJlY2VpcHQSQS5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5HZXRUcmFuc2FjdGlvblJlY2VpcHRSZXF1ZXN0Gj8uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuR2V0VHJhbnNhY3Rpb25SZWNlaXB0UmVwbHkShgEKDkhlYWRlckJ5TnVtYmVyEjouY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXF1ZXN0GjguY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuSGVhZGVyQnlOdW1iZXJSZXBseRJ2CgpMb2dUcmlnZ2VyEjwuY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGEuRmlsdGVyTG9nVHJpZ2dlclJlcXVlc3QaKC5jYXBhYmlsaXRpZXMuYmxvY2tjaGFpbi5ldm0udjFhbHBoYS5Mb2cwARJ9CgtXcml0ZVJlcG9ydBI3LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVxdWVzdBo1LmNhcGFiaWxpdGllcy5ibG9ja2NoYWluLmV2bS52MWFscGhhLldyaXRlUmVwb3J0UmVwbHkaiBCCtRiDEAgBEglldm1AMS4wLjAa8w8KDUNoYWluU2VsZWN0b3IS4Q8S3g8KFwoLYWRpLW1haW5uZXQQ/PDmwrfn3ao4ChgKC2FkaS10ZXN0bmV0EP2myPr5hozaggEKJAoXYXBlY2hhaW4tdGVzdG5ldC1jdXJ0aXMQwcO0+I3EkrKJAQoXCgthcmMtdGVzdG5ldBDnxoye19fQjSoKHQoRYXZhbGFuY2hlLW1haW5uZXQQ1eeKwOHVmKRZCiMKFmF2YWxhbmNoZS10ZXN0bmV0LWZ1amkQm/n8kKLjqPjMAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLW1haW5uZXQQz/eU8djtlbidAQooChtiaW5hbmNlX3NtYXJ0X2NoYWluLXRlc3RuZXQQ+62+nICu5Iq4AQoYCgxjZWxvLW1haW5uZXQQhtTo2IaTiNcSChgKDGNlbG8tc2Vwb2xpYRDEw/yL/OedmjQKGgoOY3Jvbm9zLXRlc3RuZXQQ/dnureDe2sgpCiIKFWR0Y2MtdGVzdG5ldC1hbmRlc2l0ZRDSg+PQmZblpNcBChwKEGV0aGVyZXVtLW1haW5uZXQQlfbx5M+ypsJFCicKG2V0aGVyZXVtLW1haW5uZXQtYXJiaXRydW0tMRDE6I3Njpuh10QKJAoXZXRoZXJldW0tbWFpbm5ldC1iYXNlLTEQgv+rov65kNPdAQoiChZldGhlcmV1bS1tYWlubmV0LWluay0xEKCwpum35qqEMAokChhldGhlcmV1bS1tYWlubmV0LWxpbmVhLTEQtrrpmMu9sJtACiUKGWV0aGVyZXVtLW1haW5uZXQtbWFudGxlLTEQiue0leewg8wVCicKG2V0aGVyZXVtLW1haW5uZXQtb3B0aW1pc20tMRC4lY/D9/7Q6TMKJgoZZXRoZXJldW0tbWFpbm5ldC1zY3JvbGwtMRC4vOTrxL7In7cBCikKHWV0aGVyZXVtLW1haW5uZXQtd29ybGRjaGFpbi0xEIfvurfFtsK4HAolChlldGhlcmV1bS1tYWlubmV0LXhsYXllci0xEJal/JymqO/tKQolChlldGhlcmV1bS1tYWlubmV0LXprc3luYy0xEJTul9nttLHXFQolChhldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEQ2bXkzvzJ7qDeAQovCiNldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtYXJiaXRydW0tMRDqzu7/6raEozAKLAofZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLWJhc2UtMRC4yrnv9pCuyI8BCiwKIGV0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1saW5lYS0xEOuq1P6C+eavTwotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtbWFudGxlLTEQ1ca47s328qZyCi8KI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS1vcHRpbWlzbS0xEJ+GxaG+2MPASAotCiFldGhlcmV1bS10ZXN0bmV0LXNlcG9saWEtc2Nyb2xsLTEQi+m0vtu67dEfCjAKI2V0aGVyZXVtLXRlc3RuZXQtc2Vwb2xpYS11bmljaGFpbi0xELTe/uDsl6mWxAEKMQolZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXdvcmxkY2hhaW4tMRC63+DFx6nzxUkKLQohZXRoZXJldW0tdGVzdG5ldC1zZXBvbGlhLXprc3luYy0xELfB/P3yxIDeXwogChRnbm9zaXNfY2hhaW4tbWFpbm5ldBD0kq3a8qKuugYKJwobZ25vc2lzX2NoYWluLXRlc3RuZXQtY2hpYWRvELOxgtCbpY+PewofChNoeXBlcmxpcXVpZC1tYWlubmV0EKez+N3O0enyIQofChNoeXBlcmxpcXVpZC10ZXN0bmV0EIjO3ciX4Mm9OwogChNpbmstdGVzdG5ldC1zZXBvbGlhEOj0p6Xz5pbAhwEKGQoNam92YXktbWFpbm5ldBC1w8SaoYDfkhUKGQoNam92YXktdGVzdG5ldBDkz4qE3rLejg0KGwoPbWVnYWV0aC1tYWlubmV0EOqVtsi85KbIVAoeChFtZWdhZXRoLXRlc3RuZXQtMhDjjd6IsY/9k/0BCiQKF3BoYXJvcy1hdGxhbnRpYy10ZXN0bmV0EMyZ7eDOvK+03wEKGgoOcGhhcm9zLW1haW5uZXQQyMGHnvXvzaFsChsKDnBsYXNtYS1tYWlubmV0EPib8dHaydXGgQEKGgoOcGxhc21hLXRlc3RuZXQQ1Zu/pcO0mYc3ChsKD3BvbHlnb24tbWFpbm5ldBCxq+TwmpKGnTgKIQoUcG9seWdvbi10ZXN0bmV0LWFtb3kQzY/W3/HHkPrhAQokChhwcml2YXRlLXRlc3RuZXQtYW5kZXNpdGUQ1KaYpcGP3PxfCiIKFnByaXZhdGUtdGVzdG5ldC1wdW1pY2UQ+cLEtsSlxNsVCiUKGXByaXZhdGUtdGVzdG5ldC1xdWFydHppdGUQ+fCi3azdh/o5CiQKGHByaXZhdGUtdGVzdG5ldC1yaHlvbGl0ZRCB+onr4bTbsQgKGQoNc29uaWMtbWFpbm5ldBDRsuXt2aCynRcKGQoNc29uaWMtdGVzdG5ldBDIiPvUtMb6vBgKGAoLdGFjLXRlc3RuZXQQ1duN4/ufk9eDAQobCg54bGF5ZXItdGVzdG5ldBDJvqG0rcy83Y0BQuUBCidjb20uY2FwYWJpbGl0aWVzLmJsb2NrY2hhaW4uZXZtLnYxYWxwaGFCC0NsaWVudFByb3RvUAGiAgNDQkWqAiNDYXBhYmlsaXRpZXMuQmxvY2tjaGFpbi5Fdm0uVjFhbHBoYcoCI0NhcGFiaWxpdGllc1xCbG9ja2NoYWluXEV2bVxWMWFscGhh4gIvQ2FwYWJpbGl0aWVzXEJsb2NrY2hhaW5cRXZtXFYxYWxwaGFcR1BCTWV0YWRhdGHqAiZDYXBhYmlsaXRpZXM6OkJsb2NrY2hhaW46OkV2bTo6VjFhbHBoYWIGcHJvdG8z', [file_sdk_v1alpha_sdk, file_tools_generator_v1alpha_cre_metadata, file_values_v1_values], ) diff --git a/packages/cre-sdk/src/generated/chain-selectors/mainnet/evm/dtcc-mainnet-appchain.ts b/packages/cre-sdk/src/generated/chain-selectors/mainnet/evm/dtcc-mainnet-appchain.ts new file mode 100644 index 00000000..81fc9444 --- /dev/null +++ b/packages/cre-sdk/src/generated/chain-selectors/mainnet/evm/dtcc-mainnet-appchain.ts @@ -0,0 +1,16 @@ +// This file is auto-generated. Do not edit manually. +// Generated from: https://github.com/smartcontractkit/chain-selectors + +import type { NetworkInfo } from '@cre/sdk/utils/chain-selectors/types' + +const network: NetworkInfo = { + chainId: '2026041005', + chainSelector: { + name: 'dtcc-mainnet-appchain', + selector: 13879014182901017172n, + }, + chainFamily: 'evm', + networkType: 'mainnet', +} as const + +export default network diff --git a/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/private-testnet-quartzite.ts b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/private-testnet-quartzite.ts new file mode 100644 index 00000000..20793c16 --- /dev/null +++ b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/private-testnet-quartzite.ts @@ -0,0 +1,16 @@ +// This file is auto-generated. Do not edit manually. +// Generated from: https://github.com/smartcontractkit/chain-selectors + +import type { NetworkInfo } from '@cre/sdk/utils/chain-selectors/types' + +const network: NetworkInfo = { + chainId: '2026041002', + chainSelector: { + name: 'private-testnet-quartzite', + selector: 4175996748267305081n, + }, + chainFamily: 'evm', + networkType: 'testnet', +} as const + +export default network diff --git a/packages/cre-sdk/src/generated/networks.ts b/packages/cre-sdk/src/generated/networks.ts index f972f8e8..d5f19dc8 100644 --- a/packages/cre-sdk/src/generated/networks.ts +++ b/packages/cre-sdk/src/generated/networks.ts @@ -32,6 +32,7 @@ import mainnet_evm_corn_mainnet from './chain-selectors/mainnet/evm/corn-mainnet import mainnet_evm_creditcoin_mainnet from './chain-selectors/mainnet/evm/creditcoin-mainnet' import mainnet_evm_cronos_mainnet from './chain-selectors/mainnet/evm/cronos-mainnet' import mainnet_evm_cronos_zkevm_mainnet from './chain-selectors/mainnet/evm/cronos-zkevm-mainnet' +import mainnet_evm_dtcc_mainnet_appchain from './chain-selectors/mainnet/evm/dtcc-mainnet-appchain' import mainnet_evm_edge_mainnet from './chain-selectors/mainnet/evm/edge-mainnet' // Import all individual network files import mainnet_evm_ethereum_mainnet from './chain-selectors/mainnet/evm/ethereum-mainnet' @@ -256,6 +257,7 @@ import testnet_evm_private_testnet_mica from './chain-selectors/testnet/evm/priv import testnet_evm_private_testnet_obsidian from './chain-selectors/testnet/evm/private-testnet-obsidian' import testnet_evm_private_testnet_opala from './chain-selectors/testnet/evm/private-testnet-opala' import testnet_evm_private_testnet_pumice from './chain-selectors/testnet/evm/private-testnet-pumice' +import testnet_evm_private_testnet_quartzite from './chain-selectors/testnet/evm/private-testnet-quartzite' import testnet_evm_private_testnet_rhyolite from './chain-selectors/testnet/evm/private-testnet-rhyolite' import testnet_evm_robinhood_testnet from './chain-selectors/testnet/evm/robinhood-testnet' import testnet_evm_ronin_testnet_saigon from './chain-selectors/testnet/evm/ronin-testnet-saigon' @@ -559,8 +561,10 @@ export const allNetworks: NetworkInfo[] = [ testnet_evm_ethereum_testnet_sepolia_blast_1, mainnet_evm_tron_mainnet_evm, testnet_evm_zora_testnet, + testnet_evm_private_testnet_quartzite, testnet_evm_private_testnet_rhyolite, testnet_evm_private_testnet_pumice, + mainnet_evm_dtcc_mainnet_appchain, testnet_evm_tron_testnet_shasta_evm, testnet_evm_tron_devnet_evm, testnet_evm_tron_testnet_nile_evm, @@ -701,6 +705,7 @@ export const mainnet = { mainnet_evm_zora_mainnet, mainnet_evm_corn_mainnet, mainnet_evm_tron_mainnet_evm, + mainnet_evm_dtcc_mainnet_appchain, ] as const, solana: [mainnet_solana_solana_mainnet] as const, aptos: [mainnet_aptos_aptos_mainnet] as const, @@ -861,6 +866,7 @@ export const testnet = { testnet_evm_plume_testnet, testnet_evm_ethereum_testnet_sepolia_blast_1, testnet_evm_zora_testnet, + testnet_evm_private_testnet_quartzite, testnet_evm_private_testnet_rhyolite, testnet_evm_private_testnet_pumice, testnet_evm_tron_testnet_shasta_evm, @@ -997,6 +1003,7 @@ export const mainnetBySelector = new Map([ [3555797439612589184n, mainnet_evm_zora_mainnet], [9043146809313071210n, mainnet_evm_corn_mainnet], [1546563616611573946n, mainnet_evm_tron_mainnet_evm], + [13879014182901017172n, mainnet_evm_dtcc_mainnet_appchain], [124615329519749607n, mainnet_solana_solana_mainnet], [4741433654826277614n, mainnet_aptos_aptos_mainnet], [17529533435026248318n, mainnet_sui_sui_mainnet], @@ -1155,6 +1162,7 @@ export const testnetBySelector = new Map([ [14684575664602284776n, testnet_evm_plume_testnet], [2027362563942762617n, testnet_evm_ethereum_testnet_sepolia_blast_1], [16244020411108056671n, testnet_evm_zora_testnet], + [4175996748267305081n, testnet_evm_private_testnet_quartzite], [604447335222770945n, testnet_evm_private_testnet_rhyolite], [1564738277398880633n, testnet_evm_private_testnet_pumice], [13231703482326770598n, testnet_evm_tron_testnet_shasta_evm], @@ -1292,6 +1300,7 @@ export const mainnetByName = new Map([ ['zora-mainnet', mainnet_evm_zora_mainnet], ['corn-mainnet', mainnet_evm_corn_mainnet], ['tron-mainnet-evm', mainnet_evm_tron_mainnet_evm], + ['dtcc-mainnet-appchain', mainnet_evm_dtcc_mainnet_appchain], ['solana-mainnet', mainnet_solana_solana_mainnet], ['aptos-mainnet', mainnet_aptos_aptos_mainnet], ['sui-mainnet', mainnet_sui_sui_mainnet], @@ -1465,6 +1474,7 @@ export const testnetByName = new Map([ ['plume-testnet', testnet_evm_plume_testnet], ['ethereum-testnet-sepolia-blast-1', testnet_evm_ethereum_testnet_sepolia_blast_1], ['zora-testnet', testnet_evm_zora_testnet], + ['private-testnet-quartzite', testnet_evm_private_testnet_quartzite], ['private-testnet-rhyolite', testnet_evm_private_testnet_rhyolite], ['private-testnet-pumice', testnet_evm_private_testnet_pumice], ['tron-testnet-shasta-evm', testnet_evm_tron_testnet_shasta_evm], @@ -1603,6 +1613,7 @@ export const mainnetBySelectorByFamily = { [3555797439612589184n, mainnet_evm_zora_mainnet], [9043146809313071210n, mainnet_evm_corn_mainnet], [1546563616611573946n, mainnet_evm_tron_mainnet_evm], + [13879014182901017172n, mainnet_evm_dtcc_mainnet_appchain], ]), solana: new Map([[124615329519749607n, mainnet_solana_solana_mainnet]]), aptos: new Map([[4741433654826277614n, mainnet_aptos_aptos_mainnet]]), @@ -1763,6 +1774,7 @@ export const testnetBySelectorByFamily = { [14684575664602284776n, testnet_evm_plume_testnet], [2027362563942762617n, testnet_evm_ethereum_testnet_sepolia_blast_1], [16244020411108056671n, testnet_evm_zora_testnet], + [4175996748267305081n, testnet_evm_private_testnet_quartzite], [604447335222770945n, testnet_evm_private_testnet_rhyolite], [1564738277398880633n, testnet_evm_private_testnet_pumice], [13231703482326770598n, testnet_evm_tron_testnet_shasta_evm], @@ -1912,6 +1924,7 @@ export const mainnetByNameByFamily = { ['zora-mainnet', mainnet_evm_zora_mainnet], ['corn-mainnet', mainnet_evm_corn_mainnet], ['tron-mainnet-evm', mainnet_evm_tron_mainnet_evm], + ['dtcc-mainnet-appchain', mainnet_evm_dtcc_mainnet_appchain], ]), solana: new Map([['solana-mainnet', mainnet_solana_solana_mainnet]]), aptos: new Map([['aptos-mainnet', mainnet_aptos_aptos_mainnet]]), @@ -2090,6 +2103,7 @@ export const testnetByNameByFamily = { ['plume-testnet', testnet_evm_plume_testnet], ['ethereum-testnet-sepolia-blast-1', testnet_evm_ethereum_testnet_sepolia_blast_1], ['zora-testnet', testnet_evm_zora_testnet], + ['private-testnet-quartzite', testnet_evm_private_testnet_quartzite], ['private-testnet-rhyolite', testnet_evm_private_testnet_rhyolite], ['private-testnet-pumice', testnet_evm_private_testnet_pumice], ['tron-testnet-shasta-evm', testnet_evm_tron_testnet_shasta_evm], diff --git a/submodules/chainlink-protos b/submodules/chainlink-protos index e7de09d4..1e006a21 160000 --- a/submodules/chainlink-protos +++ b/submodules/chainlink-protos @@ -1 +1 @@ -Subproject commit e7de09d4f5538cd787960b02010a6cc988d0c4c6 +Subproject commit 1e006a211c8041e4e98b3c8cc9d8d76c2332c42a From c381f8474fe8daccf4d2d89897aac7ed2b3d56f1 Mon Sep 17 00:00:00 2001 From: Ernest Nowacki <124677192+ernest-nowacki@users.noreply.github.com> Date: Thu, 2 Jul 2026 08:30:09 +0200 Subject: [PATCH 15/20] Solana SDK (#278) * Solana progress * Run formatting * Prepare sdk for binding generator * restore files * Fix lint issue * Lock the newer versions * Rework structure of cre-examples so it support modern format (matching what cre cli delivers on init) and deploying solana workflow for testing * Fix imports * Fix test * Update the baseline * Update from local simulation to staging settings --- bun.lock | 65 +- bunfig.toml | 6 + .../project.yaml | 11 +- .../workflow.yaml | 16 + .../project.yaml | 11 +- .../workflow.yaml | 16 + packages/cre-sdk-examples/.env.example | 4 +- packages/cre-sdk-examples/README.md | 20 +- packages/cre-sdk-examples/package.json | 2 + packages/cre-sdk-examples/project.yaml | 69 +- .../{config.json => config.production.json} | 0 .../config.staging.json} | 0 .../{index.ts => main.ts} | 0 .../confidential-http-with-body/workflow.yaml | 41 +- .../{config.json => config.production.json} | 0 .../workflows/hello-world/config.staging.json | 3 + .../hello-world/{index.ts => main.ts} | 0 .../src/workflows/hello-world/workflow.yaml | 41 +- .../{config.json => config.production.json} | 0 .../config.staging.json | 5 + .../{index.ts => main.ts} | 0 .../workflow.yaml | 41 +- .../config.production.json | 4 + .../config.staging.json | 4 + .../{index.ts => main.ts} | 0 .../http-confidential-fetch/workflow.yaml | 41 +- .../{config.json => config.production.json} | 0 .../config.staging.json} | 0 .../http-fetch-no-sugar/{index.ts => main.ts} | 0 .../http-fetch-no-sugar/workflow.yaml | 41 +- .../http-fetch/config.production.json | 4 + .../workflows/http-fetch/config.staging.json | 4 + .../http-fetch/{index.ts => main.ts} | 0 .../src/workflows/http-fetch/workflow.yaml | 41 +- .../{config.json => config.production.json} | 0 .../workflows/log-trigger/config.staging.json | 8 + .../log-trigger/{index.ts => main.ts} | 0 .../src/workflows/log-trigger/workflow.yaml | 40 +- .../{config.json => config.production.json} | 0 .../config.staging.json} | 0 .../on-chain-write/{index.ts => main.ts} | 0 .../workflows/on-chain-write/workflow.yaml | 42 +- .../workflows/on-chain/config.production.json | 12 + .../workflows/on-chain/config.staging.json | 12 + .../workflows/on-chain/{index.ts => main.ts} | 0 .../src/workflows/on-chain/workflow.yaml | 41 +- .../{config.json => config.production.json} | 0 .../proof-of-reserve/config.staging.json | 15 + .../proof-of-reserve/{index.ts => main.ts} | 0 .../workflows/proof-of-reserve/workflow.yaml | 40 +- .../{config.json => config.production.json} | 0 .../config.staging.json} | 0 .../workflows/secrets/{index.ts => main.ts} | 0 .../src/workflows/secrets/workflow.yaml | 40 +- .../solana-onchain-write/DataStorage.ts | 595 ++++++++++++++++++ .../workflows/solana-onchain-write/README.md | 41 ++ .../config.production.json | 23 + .../solana-onchain-write/config.staging.json | 23 + .../workflows/solana-onchain-write/main.ts | 118 ++++ .../solana-onchain-write/workflow.yaml | 33 + .../star-wars/config.production.json | 3 + .../workflows/star-wars/config.staging.json | 3 + .../workflows/star-wars/{index.ts => main.ts} | 0 .../src/workflows/star-wars/workflow.yaml | 41 +- packages/cre-sdk/README.md | 64 +- packages/cre-sdk/api-baseline.d.ts | 1 + packages/cre-sdk/package.json | 5 +- .../src/sdk/test/contract-mock-core.ts | 25 + .../cre-sdk/src/sdk/test/evm-contract-mock.ts | 174 ++--- packages/cre-sdk/src/sdk/test/index.ts | 6 + .../solana-binding-fixture/DataStorage.ts | 576 +++++++++++++++++ .../DataStorage_mock.ts | 19 + .../sdk/test/solana-binding-fixture/README.md | 22 + .../solana-binding-e2e.test.ts | 139 ++++ .../workflow-wasm-check.ts | 42 ++ .../src/sdk/test/solana-contract-mock.test.ts | 100 +++ .../src/sdk/test/solana-contract-mock.ts | 117 ++++ .../evm-helpers.test.ts} | 4 +- .../evm-helpers.ts} | 10 +- .../capabilities/blockchain/report-helpers.ts | 28 + .../blockchain/solana/solana-helpers.test.ts | 175 ++++++ .../blockchain/solana/solana-helpers.ts | 163 +++++ packages/cre-sdk/src/sdk/utils/hex-utils.ts | 9 + packages/cre-sdk/src/sdk/utils/index.ts | 5 +- packages/cre-sdk/tsconfig.build.json | 11 +- 85 files changed, 2884 insertions(+), 431 deletions(-) create mode 100644 bunfig.toml rename packages/cre-sdk-examples/src/workflows/confidential-http-with-body/{config.json => config.production.json} (100%) rename packages/cre-sdk-examples/src/workflows/{http-confidential-fetch/config.json => confidential-http-with-body/config.staging.json} (100%) rename packages/cre-sdk-examples/src/workflows/confidential-http-with-body/{index.ts => main.ts} (100%) rename packages/cre-sdk-examples/src/workflows/hello-world/{config.json => config.production.json} (100%) create mode 100644 packages/cre-sdk-examples/src/workflows/hello-world/config.staging.json rename packages/cre-sdk-examples/src/workflows/hello-world/{index.ts => main.ts} (100%) rename packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/{config.json => config.production.json} (100%) create mode 100644 packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/config.staging.json rename packages/cre-sdk-examples/src/workflows/http-confidential-fetch-with-secrets/{index.ts => main.ts} (100%) create mode 100644 packages/cre-sdk-examples/src/workflows/http-confidential-fetch/config.production.json create mode 100644 packages/cre-sdk-examples/src/workflows/http-confidential-fetch/config.staging.json rename packages/cre-sdk-examples/src/workflows/http-confidential-fetch/{index.ts => main.ts} (100%) rename packages/cre-sdk-examples/src/workflows/http-fetch-no-sugar/{config.json => config.production.json} (100%) rename packages/cre-sdk-examples/src/workflows/{http-fetch/config.json => http-fetch-no-sugar/config.staging.json} (100%) rename packages/cre-sdk-examples/src/workflows/http-fetch-no-sugar/{index.ts => main.ts} (100%) create mode 100644 packages/cre-sdk-examples/src/workflows/http-fetch/config.production.json create mode 100644 packages/cre-sdk-examples/src/workflows/http-fetch/config.staging.json rename packages/cre-sdk-examples/src/workflows/http-fetch/{index.ts => main.ts} (100%) rename packages/cre-sdk-examples/src/workflows/log-trigger/{config.json => config.production.json} (100%) create mode 100644 packages/cre-sdk-examples/src/workflows/log-trigger/config.staging.json rename packages/cre-sdk-examples/src/workflows/log-trigger/{index.ts => main.ts} (100%) rename packages/cre-sdk-examples/src/workflows/on-chain-write/{config.json => config.production.json} (100%) rename packages/cre-sdk-examples/src/workflows/{on-chain/config.json => on-chain-write/config.staging.json} (100%) rename packages/cre-sdk-examples/src/workflows/on-chain-write/{index.ts => main.ts} (100%) create mode 100644 packages/cre-sdk-examples/src/workflows/on-chain/config.production.json create mode 100644 packages/cre-sdk-examples/src/workflows/on-chain/config.staging.json rename packages/cre-sdk-examples/src/workflows/on-chain/{index.ts => main.ts} (100%) rename packages/cre-sdk-examples/src/workflows/proof-of-reserve/{config.json => config.production.json} (100%) create mode 100644 packages/cre-sdk-examples/src/workflows/proof-of-reserve/config.staging.json rename packages/cre-sdk-examples/src/workflows/proof-of-reserve/{index.ts => main.ts} (100%) rename packages/cre-sdk-examples/src/workflows/secrets/{config.json => config.production.json} (100%) rename packages/cre-sdk-examples/src/workflows/{star-wars/config.json => secrets/config.staging.json} (100%) rename packages/cre-sdk-examples/src/workflows/secrets/{index.ts => main.ts} (100%) create mode 100644 packages/cre-sdk-examples/src/workflows/solana-onchain-write/DataStorage.ts create mode 100644 packages/cre-sdk-examples/src/workflows/solana-onchain-write/README.md create mode 100644 packages/cre-sdk-examples/src/workflows/solana-onchain-write/config.production.json create mode 100644 packages/cre-sdk-examples/src/workflows/solana-onchain-write/config.staging.json create mode 100644 packages/cre-sdk-examples/src/workflows/solana-onchain-write/main.ts create mode 100644 packages/cre-sdk-examples/src/workflows/solana-onchain-write/workflow.yaml create mode 100644 packages/cre-sdk-examples/src/workflows/star-wars/config.production.json create mode 100644 packages/cre-sdk-examples/src/workflows/star-wars/config.staging.json rename packages/cre-sdk-examples/src/workflows/star-wars/{index.ts => main.ts} (100%) create mode 100644 packages/cre-sdk/src/sdk/test/contract-mock-core.ts create mode 100644 packages/cre-sdk/src/sdk/test/solana-binding-fixture/DataStorage.ts create mode 100644 packages/cre-sdk/src/sdk/test/solana-binding-fixture/DataStorage_mock.ts create mode 100644 packages/cre-sdk/src/sdk/test/solana-binding-fixture/README.md create mode 100644 packages/cre-sdk/src/sdk/test/solana-binding-fixture/solana-binding-e2e.test.ts create mode 100644 packages/cre-sdk/src/sdk/test/solana-binding-fixture/workflow-wasm-check.ts create mode 100644 packages/cre-sdk/src/sdk/test/solana-contract-mock.test.ts create mode 100644 packages/cre-sdk/src/sdk/test/solana-contract-mock.ts rename packages/cre-sdk/src/sdk/utils/capabilities/blockchain/{blockchain-helpers.test.ts => evm/evm-helpers.test.ts} (99%) rename packages/cre-sdk/src/sdk/utils/capabilities/blockchain/{blockchain-helpers.ts => evm/evm-helpers.ts} (96%) create mode 100644 packages/cre-sdk/src/sdk/utils/capabilities/blockchain/report-helpers.ts create mode 100644 packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.test.ts create mode 100644 packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.ts diff --git a/bun.lock b/bun.lock index c2ee1031..5ddb7b0d 100644 --- a/bun.lock +++ b/bun.lock @@ -63,7 +63,7 @@ }, "packages/cre-sdk": { "name": "@chainlink/cre-sdk", - "version": "1.13.0", + "version": "1.14.0", "bin": { "cre-compile": "bin/cre-compile.ts", }, @@ -71,6 +71,9 @@ "@bufbuild/protobuf": "2.6.3", "@bufbuild/protoc-gen-es": "2.6.3", "@chainlink/cre-sdk-javy-plugin": "workspace:*", + "@noble/hashes": "2.2.0", + "@solana/addresses": "6.10.0", + "@solana/codecs": "6.10.0", "@standard-schema/spec": "1.0.0", "viem": "2.34.0", "zod": "3.25.76", @@ -83,15 +86,17 @@ "fast-glob": "3.3.3", "ts-proto": "2.7.5", "typescript": "5.9.3", - "yaml": "2.8.1", + "yaml": "2.9.0", }, }, "packages/cre-sdk-examples": { "name": "@chainlink/cre-sdk-examples", - "version": "1.13.0", + "version": "1.14.0", "dependencies": { "@bufbuild/protobuf": "2.6.3", "@chainlink/cre-sdk": "workspace:*", + "@solana/addresses": "6.10.0", + "@solana/codecs": "6.10.0", "viem": "2.34.0", "zod": "3.25.76", }, @@ -174,7 +179,7 @@ "@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], - "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -188,11 +193,33 @@ "@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], + "@solana/addresses": ["@solana/addresses@6.10.0", "", { "dependencies": { "@solana/assertions": "6.10.0", "@solana/codecs-core": "6.10.0", "@solana/codecs-strings": "6.10.0", "@solana/errors": "6.10.0", "@solana/nominal-types": "6.10.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-vEoCGBTxG0HCERAn84KXkrJjl+pDaNzOpZ0qbgcPS98fYxP5yzbKB8SNOY2bzrbkRUmmw5Q3hqTRERemUN2Gcw=="], + + "@solana/assertions": ["@solana/assertions@6.10.0", "", { "dependencies": { "@solana/errors": "6.10.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-lKSAdVo+P/6Lp4vs6shstXmFOpvxrABwn4o1462tb7sKkNapk6o9pPFVPGw4DUgPS3WqWRs1j2tmpuVjhQRntg=="], + + "@solana/codecs": ["@solana/codecs@6.10.0", "", { "dependencies": { "@solana/codecs-core": "6.10.0", "@solana/codecs-data-structures": "6.10.0", "@solana/codecs-numbers": "6.10.0", "@solana/codecs-strings": "6.10.0", "@solana/fixed-points": "6.10.0", "@solana/options": "6.10.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-lLVuxod4ChWp9i7OvpgIykYG8Q9OGPVXKnHM9VlzDDLylsx7Y1FoQL00sHa7PqFkJVmkBufaA6dcGbQ7FU+lAQ=="], + + "@solana/codecs-core": ["@solana/codecs-core@6.10.0", "", { "dependencies": { "@solana/errors": "6.10.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-nfAl9OMGo4HanIMxGsQoVB7BxMoqBCYEUxl8oEAZZ09pDxnaXQZkTRXEwPPccag37XfW1ciPd1vWPKwB2b0HHQ=="], + + "@solana/codecs-data-structures": ["@solana/codecs-data-structures@6.10.0", "", { "dependencies": { "@solana/codecs-core": "6.10.0", "@solana/codecs-numbers": "6.10.0", "@solana/errors": "6.10.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-CNasJW3bq5u+632Zt5aJ8rOjAjv2HyenpV8o9kAIqdmV4CBpjCCoBnKn8LkuR/sbeREZxJYfhKTXO/9ruAkw7A=="], + + "@solana/codecs-numbers": ["@solana/codecs-numbers@6.10.0", "", { "dependencies": { "@solana/codecs-core": "6.10.0", "@solana/errors": "6.10.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-CcM+wX4zOiA9zkh8A7t1787A0Ehgmu5+6Z2tKoHew6cNw/dkaUTPa8JnNHbvfsLC8dfHC1BhAEJl86sKmRsfkQ=="], + + "@solana/codecs-strings": ["@solana/codecs-strings@6.10.0", "", { "dependencies": { "@solana/codecs-core": "6.10.0", "@solana/codecs-numbers": "6.10.0", "@solana/errors": "6.10.0" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.4.0" }, "optionalPeers": ["fastestsmallesttextencoderdecoder", "typescript"] }, "sha512-zlaqkg7K6F6IN4V/Ec8TWkTn054gxv7ZLagvGkuEyAdPQ6BzzsehOm2TqCuyXgJJTCGPLY1bEk6yH9NxANe0kA=="], + + "@solana/errors": ["@solana/errors@6.10.0", "", { "dependencies": { "chalk": "5.6.2", "commander": "15.0.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"], "bin": { "errors": "bin/cli.mjs" } }, "sha512-KBLAxCtAXr357JNhCyIDQXWbuSj5vN6w+28FSfcYY6OOSiphmXLAV3V58jgV0C6iNbIzFJFi6yatFyDTdeJsNg=="], + + "@solana/fixed-points": ["@solana/fixed-points@6.10.0", "", { "dependencies": { "@solana/codecs-core": "6.10.0", "@solana/errors": "6.10.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-ZkKL0alXH3L7/wMiVG8YUuG8qBKunlM810+YBD7nUPRhifiGsX1zwADViHLYNqLr/jUk0mTYFUcKznTpB/K+Gg=="], + + "@solana/nominal-types": ["@solana/nominal-types@6.10.0", "", { "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-9ykyBBvnkInH7fCacjJi7zu2PJyd+OCt+VTjIISv070fHzKIMFqZqJJ/dJ0SRH2aHwfB3n86iVsmtBtuxi4KKA=="], + + "@solana/options": ["@solana/options@6.10.0", "", { "dependencies": { "@solana/codecs-core": "6.10.0", "@solana/codecs-data-structures": "6.10.0", "@solana/codecs-numbers": "6.10.0", "@solana/codecs-strings": "6.10.0", "@solana/errors": "6.10.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-RO9UT3UYD8/Cu2uM6ZXbKvLeMnVD42+g9JRds7Pfs4AhiOyg4R4TJrQUAppTgavPTO3PBRlWtWOC05ZH/yAIbg=="], + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], "@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="], - "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], + "@types/node": ["@types/node@26.0.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA=="], "@typescript/vfs": ["@typescript/vfs@1.6.4", "", { "dependencies": { "debug": "^4.4.3" }, "peerDependencies": { "typescript": "*" } }, "sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ=="], @@ -202,7 +229,7 @@ "bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -212,6 +239,10 @@ "chain-selectors": ["chain-selectors@github:smartcontractkit/chain-selectors#3e718d1", {}, "smartcontractkit-chain-selectors-3e718d1"], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "commander": ["commander@15.0.0", "", {}, "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], @@ -226,7 +257,7 @@ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], @@ -250,7 +281,7 @@ "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], @@ -312,7 +343,7 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], @@ -320,7 +351,7 @@ "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -330,16 +361,30 @@ "@chainlink/cre-sdk-examples/viem": ["viem@2.34.0", "", { "dependencies": { "@noble/curves": "1.9.6", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.0.8", "isows": "1.0.7", "ox": "0.8.7", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-HJZG9Wt0DLX042MG0PK17tpataxtdAEhpta9/Q44FqKwy3xZMI5Lx4jF+zZPuXFuYjZ68R0PXqRwlswHs6r4gA=="], + "@noble/curves/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@scure/bip32/@noble/curves": ["@noble/curves@1.9.6", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="], + "@scure/bip32/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "@scure/bip39/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "viem/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@chainlink/cre-sdk-examples/viem/@noble/curves": ["@noble/curves@1.9.6", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="], + "@chainlink/cre-sdk-examples/viem/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@chainlink/cre-sdk-examples/viem/abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], "@chainlink/cre-sdk-examples/viem/ox": ["ox@0.8.7", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.0.8", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-W1f0FiMf9NZqtHPEDEAEkyzZDwbIKfmH2qmQx8NNiQ/9JhxrSblmtLJsSfTtQG5YKowLOnBlLVguCyxm/7ztxw=="], "@chainlink/cre-sdk/viem/@noble/curves": ["@noble/curves@1.9.6", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="], + "@chainlink/cre-sdk/viem/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@chainlink/cre-sdk/viem/abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], "@chainlink/cre-sdk/viem/ox": ["ox@0.8.7", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.0.8", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-W1f0FiMf9NZqtHPEDEAEkyzZDwbIKfmH2qmQx8NNiQ/9JhxrSblmtLJsSfTtQG5YKowLOnBlLVguCyxm/7ztxw=="], diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 00000000..fceb5b9a --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,6 @@ +[install] +# Without this, newer bun versions install workspace packages in a way that +# breaks TypeScript's ability to find the types for built-in globals like +# Response and Request. Setting "hoisted" keeps all packages in one shared +# node_modules folder (the classic layout), which fixes the lookup. +linker = "hoisted" \ No newline at end of file diff --git a/packages/cre-rust-prebuilt-plugin-example/project.yaml b/packages/cre-rust-prebuilt-plugin-example/project.yaml index 7752bff3..cb02d563 100644 --- a/packages/cre-rust-prebuilt-plugin-example/project.yaml +++ b/packages/cre-rust-prebuilt-plugin-example/project.yaml @@ -1,5 +1,14 @@ -# CRE project settings — only local-simulation is needed for this example. local-simulation: rpcs: - chain-name: ethereum-testnet-sepolia url: https://por.bcy-p.metalhosts.com/cre-alpha/MvqtrdftrbxcP3ZgGBJb3bK5/ethereum/sepolia + +staging-settings: + rpcs: + - chain-name: ethereum-testnet-sepolia + url: https://ethereum-sepolia-rpc.publicnode.com + +production-settings: + rpcs: + - chain-name: ethereum-testnet-sepolia + url: https://ethereum-sepolia-rpc.publicnode.com diff --git a/packages/cre-rust-prebuilt-plugin-example/workflow.yaml b/packages/cre-rust-prebuilt-plugin-example/workflow.yaml index f89c9eb7..c72567c7 100644 --- a/packages/cre-rust-prebuilt-plugin-example/workflow.yaml +++ b/packages/cre-rust-prebuilt-plugin-example/workflow.yaml @@ -6,3 +6,19 @@ local-simulation: workflow-artifacts: workflow-path: "./wasm/workflow.wasm" config-path: "./config.json" + +staging-settings: + user-workflow: + workflow-name: "rust-inject-prebuilt-plugin" + workflow-artifacts: + workflow-path: "./wasm/workflow.wasm" + config-path: "./config.json" + secrets-path: "" + +production-settings: + user-workflow: + workflow-name: "rust-inject-prebuilt-plugin" + workflow-artifacts: + workflow-path: "./wasm/workflow.wasm" + config-path: "./config.json" + secrets-path: "" diff --git a/packages/cre-rust-source-extensions-example/project.yaml b/packages/cre-rust-source-extensions-example/project.yaml index 7752bff3..cb02d563 100644 --- a/packages/cre-rust-source-extensions-example/project.yaml +++ b/packages/cre-rust-source-extensions-example/project.yaml @@ -1,5 +1,14 @@ -# CRE project settings — only local-simulation is needed for this example. local-simulation: rpcs: - chain-name: ethereum-testnet-sepolia url: https://por.bcy-p.metalhosts.com/cre-alpha/MvqtrdftrbxcP3ZgGBJb3bK5/ethereum/sepolia + +staging-settings: + rpcs: + - chain-name: ethereum-testnet-sepolia + url: https://ethereum-sepolia-rpc.publicnode.com + +production-settings: + rpcs: + - chain-name: ethereum-testnet-sepolia + url: https://ethereum-sepolia-rpc.publicnode.com diff --git a/packages/cre-rust-source-extensions-example/workflow.yaml b/packages/cre-rust-source-extensions-example/workflow.yaml index 3293c01f..03bd9040 100644 --- a/packages/cre-rust-source-extensions-example/workflow.yaml +++ b/packages/cre-rust-source-extensions-example/workflow.yaml @@ -6,3 +6,19 @@ local-simulation: workflow-artifacts: workflow-path: "./wasm/workflow.wasm" config-path: "./config.json" + +staging-settings: + user-workflow: + workflow-name: "rust-inject-source-extensions" + workflow-artifacts: + workflow-path: "./wasm/workflow.wasm" + config-path: "./config.json" + secrets-path: "" + +production-settings: + user-workflow: + workflow-name: "rust-inject-source-extensions" + workflow-artifacts: + workflow-path: "./wasm/workflow.wasm" + config-path: "./config.json" + secrets-path: "" diff --git a/packages/cre-sdk-examples/.env.example b/packages/cre-sdk-examples/.env.example index e10aed2a..64d40946 100644 --- a/packages/cre-sdk-examples/.env.example +++ b/packages/cre-sdk-examples/.env.example @@ -9,8 +9,8 @@ ############################################################################### # Ethereum private key or 1Password reference (e.g. op://vault/item/field) CRE_ETH_PRIVATE_KEY=0000000000000000000000000000000000000000000000000000000000000001 -# Profile to use for this environment (e.g. local-simulation, production, production-testnet) -CRE_TARGET=local-simulation +# Profile to use for this environment (e.g. staging-settins, production-settings) +CRE_TARGET=staging-settings # This one will be used in PoR workflow SECRET_ADDRESS_ALL=0x4700A50d858Cb281847ca4Ee0938F80DEfB3F1dd # This one will be used in secrets workflow diff --git a/packages/cre-sdk-examples/README.md b/packages/cre-sdk-examples/README.md index c6a66cad..fe5169b8 100644 --- a/packages/cre-sdk-examples/README.md +++ b/packages/cre-sdk-examples/README.md @@ -28,37 +28,43 @@ The setup is done in a way that treats `cre-sdk-examples` root directory as a CR **Examples**: -[Hello World workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/hello-world/index.ts): +[Hello World workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/hello-world/main.ts): ```zsh cre workflow simulate ./src/workflows/hello-world ``` -[Http Fetch workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/http-fetch/index.ts): +[Http Fetch workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/http-fetch/main.ts): ```zsh cre workflow simulate ./src/workflows/http-fetch ``` -[On Chain Read workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/on-chain/index.ts): +[On Chain Read workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/on-chain/main.ts): ```zsh cre workflow simulate ./src/workflows/on-chain ``` -[On Chain Write workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/on-chain-write/index.ts): +[On Chain Write workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/on-chain-write/main.ts): ```zsh cre workflow simulate ./src/workflows/on-chain-write ``` -[Proof of Reserve workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/proof-of-reserve/index.ts): +[Solana On-Chain Write workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/solana-onchain-write/main.ts): + +```zsh +cre workflow simulate ./src/workflows/solana-onchain-write +``` + +[Proof of Reserve workflow](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/proof-of-reserve/main.ts): ```zsh cre workflow simulate ./src/workflows/proof-of-reserve ``` -[Star Wars character example](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/star-wars/index.ts): +[Star Wars character example](https://github.com/smartcontractkit/cre-sdk-typescript/blob/main/packages/cre-sdk-examples/src/workflows/star-wars/main.ts): ```zsh cre workflow simulate ./src/workflows/star-wars @@ -75,5 +81,5 @@ bun x cre-compile Example: ```bash -bun cre-compile src/workflows/hello-world/index.ts dist/hello-world.wasm +bun cre-compile src/workflows/hello-world/main.ts dist/hello-world.wasm ``` diff --git a/packages/cre-sdk-examples/package.json b/packages/cre-sdk-examples/package.json index bd89d419..42072b87 100644 --- a/packages/cre-sdk-examples/package.json +++ b/packages/cre-sdk-examples/package.json @@ -17,6 +17,8 @@ "dependencies": { "@bufbuild/protobuf": "2.6.3", "@chainlink/cre-sdk": "workspace:*", + "@solana/addresses": "6.10.0", + "@solana/codecs": "6.10.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/packages/cre-sdk-examples/project.yaml b/packages/cre-sdk-examples/project.yaml index 076f5d2e..f9a5de6b 100644 --- a/packages/cre-sdk-examples/project.yaml +++ b/packages/cre-sdk-examples/project.yaml @@ -1,50 +1,47 @@ # ========================================================================== # CRE PROJECT SETTINGS FILE # ========================================================================== -# This file defines environment-specific targets used by the CRE CLI. -# Each top-level key is a target (e.g., `production`, `production-testnet`, etc.). -# -# You can define your own custom target names, such as `my-target`, and point -# the CLI to it via an environment variable. -# -# Below is an example `my-target`: +# Project-specific settings for CRE CLI targets. +# Each target defines cre-cli, account, and rpcs groups. # +# Example custom target: # my-target: -# cre-cli: -# # Required: Workflow DON ID used for registering and operating workflows. -# don-family: "small" -# logging: -# # Optional: Path to the seth configuration file (TOML). Used for logging configuration. -# seth-config-path: "/path/to/seth-config.toml" +# account: +# workflow-owner-address: "0x123..." # Optional: Owner wallet/MSIG address (used for --unsigned transactions) # rpcs: -# # Required: Map each used chain selector to a corresponding RPC URL (HTTPS) -# - chain-name: ethereum-mainnet -# url: "https://sepolia.infura.io/v3/YOUR_API_KEY" +# - chain-name: ethereum-testnet-sepolia # Required if your workflow interacts with this chain +# url: "" +# +# RPC URLs support ${VAR_NAME} syntax to reference environment variables. +# This keeps secrets out of project.yaml (which is committed to git). +# Variables are resolved from your .env file or exported shell variables. +# Example: +# - chain-name: ethereum-testnet-sepolia +# url: https://rpc.example.com/${CRE_SECRET_RPC_SEPOLIA} +# +# Experimental chains (automatically used by the simulator when present): +# Use this for chains not yet in official chain-selectors (e.g., hackathons, new chain integrations). +# In your workflow, reference the chain as evm:ChainSelector:@1.0.0 +# +# experimental-chains: +# - chain-selector: 12345 # The chain selector value +# rpc-url: "https://rpc.example.com" # RPC endpoint URL +# forwarder: "0x..." # Forwarder contract address on the chain # ========================================================================== -local-simulation: +staging-settings: rpcs: - chain-name: ethereum-testnet-sepolia - url: https://por.bcy-p.metalhosts.com/cre-alpha/MvqtrdftrbxcP3ZgGBJb3bK5/ethereum/sepolia + url: https://ethereum-sepolia-rpc.publicnode.com + - chain-name: solana-devnet + url: https://api.devnet.solana.com # ========================================================================== -production-testnet: - cre-cli: - don-family: "zone-a" - logging: - seth-config-path: "" +production-settings: rpcs: - chain-name: ethereum-testnet-sepolia - url: https://por.bcy-p.metalhosts.com/cre-alpha/MvqtrdftrbxcP3ZgGBJb3bK5/ethereum/sepolia - - chain-name: ethereum-testnet-sepolia-base-1 - url: