@chainlink/cre-sdk 1.6.0-alpha.3 → 1.6.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -516,6 +516,15 @@ This SDK uses [@bufbuild/protobuf](https://www.npmjs.com/package/@bufbuild/proto
516
516
  - `buf.gen.yaml` - Code generation configuration using ts-proto
517
517
  - Generated files are placed in `src/generated/`
518
518
 
519
+ **Opt-in proto allowlist:**
520
+
521
+ [`buf.gen.yaml`](./buf.gen.yaml) uses an explicit `paths:` allowlist under its `inputs`. Protos added to the `chainlink-protos` submodule are **not** picked up automatically — a new capability must be added to both:
522
+
523
+ 1. [`packages/cre-sdk/buf.gen.yaml`](./buf.gen.yaml) — to generate the raw `*_pb.ts` types.
524
+ 2. [`packages/cre-sdk/scripts/src/generate-sdks.ts`](./scripts/src/generate-sdks.ts) — to generate the SDK wrapper classes and mocks.
525
+
526
+ This prevents leaking in-progress or internal capabilities into the public SDK surface before they are ready.
527
+
519
528
  ### Chain Selectors Generation
520
529
 
521
530
  Auto-generated TypeScript files for 200+ blockchain networks from the official [Chainlink chain-selectors repository](https://github.com/smartcontractkit/chain-selectors).
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  import { main as compileWorkflow } from "../scripts/src/compile-workflow";
4
+ import { parseCompileFlags } from "@chainlink/cre-sdk-javy-plugin/scripts/parse-compile-flags";
4
5
  import {
5
6
  parseCompileCliArgs,
6
7
  skipTypeChecksFlag,
@@ -9,26 +10,34 @@ import { WorkflowTypecheckError } from "../scripts/src/typecheck-workflow";
9
10
  import { WorkflowRuntimeCompatibilityError } from "../scripts/src/validate-workflow-runtime-compat";
10
11
 
11
12
  const main = async () => {
13
+ const cliArgs = process.argv.slice(2);
14
+ const { creExports, plugin, rest } = parseCompileFlags(cliArgs);
15
+
12
16
  let inputPath: string | undefined;
13
17
  let outputPathArg: string | undefined;
14
18
  let skipTypeChecks = false;
15
19
 
16
20
  try {
17
- const parsed = parseCompileCliArgs(process.argv.slice(2));
21
+ const parsed = parseCompileCliArgs(rest);
18
22
  inputPath = parsed.inputPath;
19
23
  outputPathArg = parsed.outputPath;
20
24
  skipTypeChecks = parsed.skipTypeChecks;
21
25
  } catch (error) {
22
26
  console.error(error instanceof Error ? error.message : error);
23
27
  console.error(
24
- `Usage: cre-compile <path/to/workflow.ts> [path/to/output.wasm] [${skipTypeChecksFlag}]`,
28
+ `Usage: cre-compile [--plugin <path>] [--cre-exports <crate-dir>]... <path/to/workflow.ts> [path/to/output.wasm] [${skipTypeChecksFlag}]`,
25
29
  );
26
30
  process.exit(1);
27
31
  }
28
32
 
33
+ if (plugin !== null && creExports.length > 0) {
34
+ console.error("❌ Error: --plugin and --cre-exports are mutually exclusive.");
35
+ process.exit(1);
36
+ }
37
+
29
38
  if (!inputPath) {
30
39
  console.error(
31
- `Usage: cre-compile <path/to/workflow.ts> [path/to/output.wasm] [${skipTypeChecksFlag}]`,
40
+ `Usage: cre-compile [--plugin <path>] [--cre-exports <crate-dir>]... <path/to/workflow.ts> [path/to/output.wasm] [${skipTypeChecksFlag}]`,
32
41
  );
33
42
  console.error("Examples:");
34
43
  console.error(" cre-compile src/standard_tests/secrets/test.ts");
@@ -41,10 +50,13 @@ const main = async () => {
41
50
  process.exit(1);
42
51
  }
43
52
 
44
- await compileWorkflow(inputPath, outputPathArg, { skipTypeChecks });
53
+ await compileWorkflow(inputPath, outputPathArg, {
54
+ skipTypeChecks,
55
+ creExports: creExports.length > 0 ? creExports : undefined,
56
+ plugin,
57
+ });
45
58
  };
46
59
 
47
- // CLI entry point
48
60
  main().catch((e) => {
49
61
  if (
50
62
  e instanceof WorkflowRuntimeCompatibilityError ||
@@ -0,0 +1,123 @@
1
+ import type { Message } from '@bufbuild/protobuf';
2
+ import type { GenFile, GenMessage } from '@bufbuild/protobuf/codegenv2';
3
+ /**
4
+ * Describes the file workflows/v2/cre_info.proto.
5
+ */
6
+ export declare const file_workflows_v2_cre_info: GenFile;
7
+ /**
8
+ * @generated from message workflows.v2.CreInfo
9
+ */
10
+ export type CreInfo = Message<'workflows.v2.CreInfo'> & {
11
+ /**
12
+ * @generated from field: int32 donID = 1;
13
+ */
14
+ donID: number;
15
+ /**
16
+ * @generated from field: int32 donF = 2;
17
+ */
18
+ donF: number;
19
+ /**
20
+ * @generated from field: int32 donN = 3;
21
+ */
22
+ donN: number;
23
+ /**
24
+ * @generated from field: string p2pID = 4;
25
+ */
26
+ p2pID: string;
27
+ /**
28
+ * @generated from field: string workflowRegistryAddress = 5;
29
+ */
30
+ workflowRegistryAddress: string;
31
+ /**
32
+ * @generated from field: string workflowRegistryVersion = 6;
33
+ */
34
+ workflowRegistryVersion: string;
35
+ /**
36
+ * @generated from field: string workflowRegistryChain = 7;
37
+ */
38
+ workflowRegistryChain: string;
39
+ /**
40
+ * @generated from field: string engineVersion = 8;
41
+ */
42
+ engineVersion: string;
43
+ /**
44
+ * @generated from field: string capabilitiesRegistryVersion = 9;
45
+ */
46
+ capabilitiesRegistryVersion: string;
47
+ /**
48
+ * @generated from field: string donVersion = 10;
49
+ */
50
+ donVersion: string;
51
+ /**
52
+ * workflowSource identifies where the workflow was deployed from.
53
+ * Format varies by source type:
54
+ * - Onchain contract: "contract:{chain_selector}:{contract_address}"
55
+ * - GRPC source: "grpc:{source_name}:v1"
56
+ * - File source: "file:{source_name}:v1"
57
+ *
58
+ * @generated from field: string workflowSource = 11;
59
+ */
60
+ workflowSource: string;
61
+ };
62
+ /**
63
+ * @generated from message workflows.v2.CreInfo
64
+ */
65
+ export type CreInfoJson = {
66
+ /**
67
+ * @generated from field: int32 donID = 1;
68
+ */
69
+ donID?: number;
70
+ /**
71
+ * @generated from field: int32 donF = 2;
72
+ */
73
+ donF?: number;
74
+ /**
75
+ * @generated from field: int32 donN = 3;
76
+ */
77
+ donN?: number;
78
+ /**
79
+ * @generated from field: string p2pID = 4;
80
+ */
81
+ p2pID?: string;
82
+ /**
83
+ * @generated from field: string workflowRegistryAddress = 5;
84
+ */
85
+ workflowRegistryAddress?: string;
86
+ /**
87
+ * @generated from field: string workflowRegistryVersion = 6;
88
+ */
89
+ workflowRegistryVersion?: string;
90
+ /**
91
+ * @generated from field: string workflowRegistryChain = 7;
92
+ */
93
+ workflowRegistryChain?: string;
94
+ /**
95
+ * @generated from field: string engineVersion = 8;
96
+ */
97
+ engineVersion?: string;
98
+ /**
99
+ * @generated from field: string capabilitiesRegistryVersion = 9;
100
+ */
101
+ capabilitiesRegistryVersion?: string;
102
+ /**
103
+ * @generated from field: string donVersion = 10;
104
+ */
105
+ donVersion?: string;
106
+ /**
107
+ * workflowSource identifies where the workflow was deployed from.
108
+ * Format varies by source type:
109
+ * - Onchain contract: "contract:{chain_selector}:{contract_address}"
110
+ * - GRPC source: "grpc:{source_name}:v1"
111
+ * - File source: "file:{source_name}:v1"
112
+ *
113
+ * @generated from field: string workflowSource = 11;
114
+ */
115
+ workflowSource?: string;
116
+ };
117
+ /**
118
+ * Describes the message workflows.v2.CreInfo.
119
+ * Use `create(CreInfoSchema)` to create a new message.
120
+ */
121
+ export declare const CreInfoSchema: GenMessage<CreInfo, {
122
+ jsonType: CreInfoJson;
123
+ }>;
@@ -0,0 +1,17 @@
1
+ // @generated by protoc-gen-es v2.6.3 with parameter "target=ts,import_extension=none,json_types=true,keep_empty_files=false"
2
+ // @generated from file workflows/v2/cre_info.proto (package workflows.v2, syntax proto3)
3
+ /* eslint-disable */
4
+ import { fileDesc, messageDesc } from '@bufbuild/protobuf/codegenv2';
5
+ /**
6
+ * Describes the file workflows/v2/cre_info.proto.
7
+ */
8
+ export const file_workflows_v2_cre_info =
9
+ /*@__PURE__*/
10
+ fileDesc('Cht3b3JrZmxvd3MvdjIvY3JlX2luZm8ucHJvdG8SDHdvcmtmbG93cy52MiKMAgoHQ3JlSW5mbxINCgVkb25JRBgBIAEoBRIMCgRkb25GGAIgASgFEgwKBGRvbk4YAyABKAUSDQoFcDJwSUQYBCABKAkSHwoXd29ya2Zsb3dSZWdpc3RyeUFkZHJlc3MYBSABKAkSHwoXd29ya2Zsb3dSZWdpc3RyeVZlcnNpb24YBiABKAkSHQoVd29ya2Zsb3dSZWdpc3RyeUNoYWluGAcgASgJEhUKDWVuZ2luZVZlcnNpb24YCCABKAkSIwobY2FwYWJpbGl0aWVzUmVnaXN0cnlWZXJzaW9uGAkgASgJEhIKCmRvblZlcnNpb24YCiABKAkSFgoOd29ya2Zsb3dTb3VyY2UYCyABKAlCrwEKEGNvbS53b3JrZmxvd3MudjJCDENyZUluZm9Qcm90b1ABWjxnaXRodWIuY29tL3NtYXJ0Y29udHJhY3RraXQvY2hhaW5saW5rLXByb3Rvcy93b3JrZmxvd3MvZ28vdjKiAgNXWFiqAgxXb3JrZmxvd3MuVjLKAgxXb3JrZmxvd3NcVjLiAhhXb3JrZmxvd3NcVjJcR1BCTWV0YWRhdGHqAg1Xb3JrZmxvd3M6OlYyYgZwcm90bzM');
11
+ /**
12
+ * Describes the message workflows.v2.CreInfo.
13
+ * Use `create(CreInfoSchema)` to create a new message.
14
+ */
15
+ export const CreInfoSchema =
16
+ /*@__PURE__*/
17
+ messageDesc(file_workflows_v2_cre_info, 0);
@@ -0,0 +1,55 @@
1
+ import type { Message } from '@bufbuild/protobuf';
2
+ import type { GenFile, GenMessage } from '@bufbuild/protobuf/codegenv2';
3
+ /**
4
+ * Describes the file workflows/v2/workflow_key.proto.
5
+ */
6
+ export declare const file_workflows_v2_workflow_key: GenFile;
7
+ /**
8
+ * @generated from message workflows.v2.WorkflowKey
9
+ */
10
+ export type WorkflowKey = Message<'workflows.v2.WorkflowKey'> & {
11
+ /**
12
+ * @generated from field: string workflowOwner = 1;
13
+ */
14
+ workflowOwner: string;
15
+ /**
16
+ * @generated from field: string workflowName = 2;
17
+ */
18
+ workflowName: string;
19
+ /**
20
+ * @generated from field: string workflowID = 3;
21
+ */
22
+ workflowID: string;
23
+ /**
24
+ * @generated from field: string organizationID = 4;
25
+ */
26
+ organizationID: string;
27
+ };
28
+ /**
29
+ * @generated from message workflows.v2.WorkflowKey
30
+ */
31
+ export type WorkflowKeyJson = {
32
+ /**
33
+ * @generated from field: string workflowOwner = 1;
34
+ */
35
+ workflowOwner?: string;
36
+ /**
37
+ * @generated from field: string workflowName = 2;
38
+ */
39
+ workflowName?: string;
40
+ /**
41
+ * @generated from field: string workflowID = 3;
42
+ */
43
+ workflowID?: string;
44
+ /**
45
+ * @generated from field: string organizationID = 4;
46
+ */
47
+ organizationID?: string;
48
+ };
49
+ /**
50
+ * Describes the message workflows.v2.WorkflowKey.
51
+ * Use `create(WorkflowKeySchema)` to create a new message.
52
+ */
53
+ export declare const WorkflowKeySchema: GenMessage<WorkflowKey, {
54
+ jsonType: WorkflowKeyJson;
55
+ }>;
@@ -0,0 +1,17 @@
1
+ // @generated by protoc-gen-es v2.6.3 with parameter "target=ts,import_extension=none,json_types=true,keep_empty_files=false"
2
+ // @generated from file workflows/v2/workflow_key.proto (package workflows.v2, syntax proto3)
3
+ /* eslint-disable */
4
+ import { fileDesc, messageDesc } from '@bufbuild/protobuf/codegenv2';
5
+ /**
6
+ * Describes the file workflows/v2/workflow_key.proto.
7
+ */
8
+ export const file_workflows_v2_workflow_key =
9
+ /*@__PURE__*/
10
+ fileDesc('Ch93b3JrZmxvd3MvdjIvd29ya2Zsb3dfa2V5LnByb3RvEgx3b3JrZmxvd3MudjIiZgoLV29ya2Zsb3dLZXkSFQoNd29ya2Zsb3dPd25lchgBIAEoCRIUCgx3b3JrZmxvd05hbWUYAiABKAkSEgoKd29ya2Zsb3dJRBgDIAEoCRIWCg5vcmdhbml6YXRpb25JRBgEIAEoCUKzAQoQY29tLndvcmtmbG93cy52MkIQV29ya2Zsb3dLZXlQcm90b1ABWjxnaXRodWIuY29tL3NtYXJ0Y29udHJhY3RraXQvY2hhaW5saW5rLXByb3Rvcy93b3JrZmxvd3MvZ28vdjKiAgNXWFiqAgxXb3JrZmxvd3MuVjLKAgxXb3JrZmxvd3NcVjLiAhhXb3JrZmxvd3NcVjJcR1BCTWV0YWRhdGHqAg1Xb3JrZmxvd3M6OlYyYgZwcm90bzM');
11
+ /**
12
+ * Describes the message workflows.v2.WorkflowKey.
13
+ * Use `create(WorkflowKeySchema)` to create a new message.
14
+ */
15
+ export const WorkflowKeySchema =
16
+ /*@__PURE__*/
17
+ messageDesc(file_workflows_v2_workflow_key, 0);
@@ -0,0 +1,118 @@
1
+ import type { Message } from '@bufbuild/protobuf';
2
+ import type { GenEnum, GenFile, GenMessage } from '@bufbuild/protobuf/codegenv2';
3
+ import type { CreInfo, CreInfoJson } from './cre_info_pb';
4
+ import type { WorkflowKey, WorkflowKeyJson } from './workflow_key_pb';
5
+ /**
6
+ * Describes the file workflows/v2/workflow_user_metric.proto.
7
+ */
8
+ export declare const file_workflows_v2_workflow_user_metric: GenFile;
9
+ /**
10
+ * @generated from message workflows.v2.WorkflowUserMetric
11
+ */
12
+ export type WorkflowUserMetric = Message<'workflows.v2.WorkflowUserMetric'> & {
13
+ /**
14
+ * @generated from field: workflows.v2.CreInfo creInfo = 1;
15
+ */
16
+ creInfo?: CreInfo;
17
+ /**
18
+ * @generated from field: workflows.v2.WorkflowKey workflow = 2;
19
+ */
20
+ workflow?: WorkflowKey;
21
+ /**
22
+ * @generated from field: string workflowExecutionID = 3;
23
+ */
24
+ workflowExecutionID: string;
25
+ /**
26
+ * @generated from field: string timestamp = 4;
27
+ */
28
+ timestamp: string;
29
+ /**
30
+ * @generated from field: string name = 5;
31
+ */
32
+ name: string;
33
+ /**
34
+ * @generated from field: double value = 6;
35
+ */
36
+ value: number;
37
+ /**
38
+ * @generated from field: workflows.v2.UserMetricType type = 7;
39
+ */
40
+ type: UserMetricType;
41
+ /**
42
+ * @generated from field: map<string, string> labels = 8;
43
+ */
44
+ labels: {
45
+ [key: string]: string;
46
+ };
47
+ };
48
+ /**
49
+ * @generated from message workflows.v2.WorkflowUserMetric
50
+ */
51
+ export type WorkflowUserMetricJson = {
52
+ /**
53
+ * @generated from field: workflows.v2.CreInfo creInfo = 1;
54
+ */
55
+ creInfo?: CreInfoJson;
56
+ /**
57
+ * @generated from field: workflows.v2.WorkflowKey workflow = 2;
58
+ */
59
+ workflow?: WorkflowKeyJson;
60
+ /**
61
+ * @generated from field: string workflowExecutionID = 3;
62
+ */
63
+ workflowExecutionID?: string;
64
+ /**
65
+ * @generated from field: string timestamp = 4;
66
+ */
67
+ timestamp?: string;
68
+ /**
69
+ * @generated from field: string name = 5;
70
+ */
71
+ name?: string;
72
+ /**
73
+ * @generated from field: double value = 6;
74
+ */
75
+ value?: number | 'NaN' | 'Infinity' | '-Infinity';
76
+ /**
77
+ * @generated from field: workflows.v2.UserMetricType type = 7;
78
+ */
79
+ type?: UserMetricTypeJson;
80
+ /**
81
+ * @generated from field: map<string, string> labels = 8;
82
+ */
83
+ labels?: {
84
+ [key: string]: string;
85
+ };
86
+ };
87
+ /**
88
+ * Describes the message workflows.v2.WorkflowUserMetric.
89
+ * Use `create(WorkflowUserMetricSchema)` to create a new message.
90
+ */
91
+ export declare const WorkflowUserMetricSchema: GenMessage<WorkflowUserMetric, {
92
+ jsonType: WorkflowUserMetricJson;
93
+ }>;
94
+ /**
95
+ * @generated from enum workflows.v2.UserMetricType
96
+ */
97
+ export declare enum UserMetricType {
98
+ /**
99
+ * @generated from enum value: USER_METRIC_TYPE_UNSPECIFIED = 0;
100
+ */
101
+ UNSPECIFIED = 0,
102
+ /**
103
+ * @generated from enum value: USER_METRIC_TYPE_COUNTER = 1;
104
+ */
105
+ COUNTER = 1,
106
+ /**
107
+ * @generated from enum value: USER_METRIC_TYPE_GAUGE = 2;
108
+ */
109
+ GAUGE = 2
110
+ }
111
+ /**
112
+ * @generated from enum workflows.v2.UserMetricType
113
+ */
114
+ export type UserMetricTypeJson = 'USER_METRIC_TYPE_UNSPECIFIED' | 'USER_METRIC_TYPE_COUNTER' | 'USER_METRIC_TYPE_GAUGE';
115
+ /**
116
+ * Describes the enum workflows.v2.UserMetricType.
117
+ */
118
+ export declare const UserMetricTypeSchema: GenEnum<UserMetricType, UserMetricTypeJson>;
@@ -0,0 +1,41 @@
1
+ // @generated by protoc-gen-es v2.6.3 with parameter "target=ts,import_extension=none,json_types=true,keep_empty_files=false"
2
+ // @generated from file workflows/v2/workflow_user_metric.proto (package workflows.v2, syntax proto3)
3
+ /* eslint-disable */
4
+ import { enumDesc, fileDesc, messageDesc } from '@bufbuild/protobuf/codegenv2';
5
+ import { file_workflows_v2_cre_info } from './cre_info_pb';
6
+ import { file_workflows_v2_workflow_key } from './workflow_key_pb';
7
+ /**
8
+ * Describes the file workflows/v2/workflow_user_metric.proto.
9
+ */
10
+ export const file_workflows_v2_workflow_user_metric =
11
+ /*@__PURE__*/
12
+ fileDesc('Cid3b3JrZmxvd3MvdjIvd29ya2Zsb3dfdXNlcl9tZXRyaWMucHJvdG8SDHdvcmtmbG93cy52MiLPAgoSV29ya2Zsb3dVc2VyTWV0cmljEiYKB2NyZUluZm8YASABKAsyFS53b3JrZmxvd3MudjIuQ3JlSW5mbxIrCgh3b3JrZmxvdxgCIAEoCzIZLndvcmtmbG93cy52Mi5Xb3JrZmxvd0tleRIbChN3b3JrZmxvd0V4ZWN1dGlvbklEGAMgASgJEhEKCXRpbWVzdGFtcBgEIAEoCRIMCgRuYW1lGAUgASgJEg0KBXZhbHVlGAYgASgBEioKBHR5cGUYByABKA4yHC53b3JrZmxvd3MudjIuVXNlck1ldHJpY1R5cGUSPAoGbGFiZWxzGAggAygLMiwud29ya2Zsb3dzLnYyLldvcmtmbG93VXNlck1ldHJpYy5MYWJlbHNFbnRyeRotCgtMYWJlbHNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBKmwKDlVzZXJNZXRyaWNUeXBlEiAKHFVTRVJfTUVUUklDX1RZUEVfVU5TUEVDSUZJRUQQABIcChhVU0VSX01FVFJJQ19UWVBFX0NPVU5URVIQARIaChZVU0VSX01FVFJJQ19UWVBFX0dBVUdFEAJCugEKEGNvbS53b3JrZmxvd3MudjJCF1dvcmtmbG93VXNlck1ldHJpY1Byb3RvUAFaPGdpdGh1Yi5jb20vc21hcnRjb250cmFjdGtpdC9jaGFpbmxpbmstcHJvdG9zL3dvcmtmbG93cy9nby92MqICA1dYWKoCDFdvcmtmbG93cy5WMsoCDFdvcmtmbG93c1xWMuICGFdvcmtmbG93c1xWMlxHUEJNZXRhZGF0YeoCDVdvcmtmbG93czo6VjJiBnByb3RvMw', [file_workflows_v2_cre_info, file_workflows_v2_workflow_key]);
13
+ /**
14
+ * Describes the message workflows.v2.WorkflowUserMetric.
15
+ * Use `create(WorkflowUserMetricSchema)` to create a new message.
16
+ */
17
+ export const WorkflowUserMetricSchema = /*@__PURE__*/ messageDesc(file_workflows_v2_workflow_user_metric, 0);
18
+ /**
19
+ * @generated from enum workflows.v2.UserMetricType
20
+ */
21
+ export var UserMetricType;
22
+ (function (UserMetricType) {
23
+ /**
24
+ * @generated from enum value: USER_METRIC_TYPE_UNSPECIFIED = 0;
25
+ */
26
+ UserMetricType[UserMetricType["UNSPECIFIED"] = 0] = "UNSPECIFIED";
27
+ /**
28
+ * @generated from enum value: USER_METRIC_TYPE_COUNTER = 1;
29
+ */
30
+ UserMetricType[UserMetricType["COUNTER"] = 1] = "COUNTER";
31
+ /**
32
+ * @generated from enum value: USER_METRIC_TYPE_GAUGE = 2;
33
+ */
34
+ UserMetricType[UserMetricType["GAUGE"] = 2] = "GAUGE";
35
+ })(UserMetricType || (UserMetricType = {}));
36
+ /**
37
+ * Describes the enum workflows.v2.UserMetricType.
38
+ */
39
+ export const UserMetricTypeSchema =
40
+ /*@__PURE__*/
41
+ enumDesc(file_workflows_v2_workflow_user_metric, 0);
@@ -43,7 +43,10 @@ export declare class BaseRuntimeImpl<C> implements BaseRuntime<C> {
43
43
  getNextCallId(): number;
44
44
  now(): Date;
45
45
  log(message: string): void;
46
+ emitMetric(name: string, value: number, type: MetricType, labels?: Record<string, string>): boolean;
46
47
  }
48
+ /** Ergonomic union for {@link BaseRuntimeImpl.emitMetric}. */
49
+ export type MetricType = 'counter' | 'gauge';
47
50
  /**
48
51
  * It is used when a BFT guarantee cannot be provided automatically (e.g. calling a standard API).
49
52
  * You tell each node to perform a task on its own.
@@ -112,4 +115,9 @@ export interface RuntimeHelpers {
112
115
  now(): number;
113
116
  /** Logs a message to the host environment. */
114
117
  log(message: string): void;
118
+ /**
119
+ * Emits a user metric to the host. Payload is a protobuf-encoded
120
+ * `workflows.v2.WorkflowUserMetric`. Returns false if the host rejected it.
121
+ */
122
+ emitMetric(payload: Uint8Array): boolean;
115
123
  }
@@ -1,6 +1,7 @@
1
- import { create } from '@bufbuild/protobuf';
1
+ import { create, toBinary } from '@bufbuild/protobuf';
2
2
  import { anyPack, anyUnpack } from '@bufbuild/protobuf/wkt';
3
3
  import { AwaitCapabilitiesRequestSchema, AwaitSecretsRequestSchema, CapabilityRequestSchema, GetSecretsRequestSchema, Mode, SecretRequestSchema, SimpleConsensusInputsSchema, } from '../../generated/sdk/v1alpha/sdk_pb';
4
+ import { UserMetricType, WorkflowUserMetricSchema, } from '../../generated/workflows/v2/workflow_user_metric_pb';
4
5
  import { ConsensusCapability } from '../../generated-sdk/capabilities/internal/consensus/v1alpha/consensus_sdk_gen';
5
6
  import { Value, } from '../utils';
6
7
  import { CapabilityError } from '../utils/capabilities/capability-error';
@@ -139,7 +140,20 @@ export class BaseRuntimeImpl {
139
140
  log(message) {
140
141
  this.helpers.log(message);
141
142
  }
143
+ emitMetric(name, value, type, labels) {
144
+ const metric = create(WorkflowUserMetricSchema, {
145
+ name,
146
+ value,
147
+ type: METRIC_TYPE_TO_PROTO[type],
148
+ labels: labels ?? {},
149
+ });
150
+ return this.helpers.emitMetric(toBinary(WorkflowUserMetricSchema, metric));
151
+ }
142
152
  }
153
+ const METRIC_TYPE_TO_PROTO = {
154
+ counter: UserMetricType.COUNTER,
155
+ gauge: UserMetricType.GAUGE,
156
+ };
143
157
  /**
144
158
  * It is used when a BFT guarantee cannot be provided automatically (e.g. calling a standard API).
145
159
  * You tell each node to perform a task on its own.
@@ -1,10 +1,11 @@
1
1
  import type { Message } from '@bufbuild/protobuf';
2
2
  import type { GenMessage } from '@bufbuild/protobuf/codegenv2';
3
3
  import type { ReportRequest, ReportRequestJson } from '../generated/sdk/v1alpha/sdk_pb';
4
+ import type { MetricType } from './impl/runtime-impl';
4
5
  import type { Report } from './report';
5
6
  import type { ConsensusAggregation, PrimitiveTypes, UnwrapOptions } from './utils';
6
7
  import type { SecretsProvider } from '.';
7
- export type { ReportRequest, ReportRequestJson };
8
+ export type { ReportRequest, ReportRequestJson, MetricType };
8
9
  export type CallCapabilityParams<I extends Message, O extends Message> = {
9
10
  capabilityId: string;
10
11
  method: string;
@@ -23,6 +24,7 @@ export interface BaseRuntime<C> {
23
24
  };
24
25
  now(): Date;
25
26
  log(message: string): void;
27
+ emitMetric(name: string, value: number, type: MetricType, labels?: Record<string, string>): boolean;
26
28
  }
27
29
  /**
28
30
  * Runtime for Node mode execution.
@@ -3,6 +3,7 @@
3
3
  * Registry is scoped per test via AsyncLocalStorage; use testWithRuntime to run tests with a registry.
4
4
  */
5
5
  import type { Any } from '@bufbuild/protobuf/wkt';
6
+ import type { WorkflowUserMetric } from '../../generated/workflows/v2/workflow_user_metric_pb';
6
7
  import type { RuntimeHelpers } from '../impl/runtime-impl';
7
8
  import { RuntimeImpl } from '../impl/runtime-impl';
8
9
  import { TestWriter } from './test-writer';
@@ -80,5 +81,6 @@ export declare class TestRuntime extends RuntimeImpl<unknown> {
80
81
  private readonly state;
81
82
  constructor(helpers: RuntimeHelpers, maxResponseSize: bigint, testWriter: TestWriter, state: TestRuntimeState);
82
83
  getLogs(): string[];
84
+ getMetrics(): WorkflowUserMetric[];
83
85
  setTimeProvider(timeProvider: () => number): void;
84
86
  }
@@ -236,6 +236,10 @@ function createTestRuntimeHelpers(registry, secrets, testWriter, state, _maxResp
236
236
  log(message) {
237
237
  testWriter.log(message);
238
238
  },
239
+ emitMetric(payload) {
240
+ testWriter.emitMetric(payload);
241
+ return true;
242
+ },
239
243
  };
240
244
  }
241
245
  /**
@@ -307,6 +311,9 @@ export class TestRuntime extends RuntimeImpl {
307
311
  getLogs() {
308
312
  return this.testWriter.getLogs();
309
313
  }
314
+ getMetrics() {
315
+ return this.testWriter.getMetrics();
316
+ }
310
317
  setTimeProvider(timeProvider) {
311
318
  this.state.timeProvider = timeProvider;
312
319
  }
@@ -1,13 +1,19 @@
1
+ import { type WorkflowUserMetric } from '../../generated/workflows/v2/workflow_user_metric_pb';
1
2
  /**
2
3
  * In-memory log sink for tests. Captures messages so tests can assert on log output.
3
4
  * Equivalent to Go's cre/testutils/test_writer.go.
4
5
  */
5
6
  export declare class TestWriter {
6
7
  private logs;
8
+ private metrics;
7
9
  /** Appends a message to the captured log buffer. */
8
10
  log(message: string): void;
9
11
  /** Returns a copy of all captured log messages in order. */
10
12
  getLogs(): string[];
11
- /** Clears the captured log buffer. */
13
+ /** Captures a serialized WorkflowUserMetric payload. */
14
+ emitMetric(payload: Uint8Array): void;
15
+ /** Returns captured metric payloads decoded as `WorkflowUserMetric` protos. */
16
+ getMetrics(): WorkflowUserMetric[];
17
+ /** Clears captured logs and metrics. */
12
18
  clear(): void;
13
19
  }
@@ -1,9 +1,12 @@
1
+ import { fromBinary } from '@bufbuild/protobuf';
2
+ import { WorkflowUserMetricSchema, } from '../../generated/workflows/v2/workflow_user_metric_pb';
1
3
  /**
2
4
  * In-memory log sink for tests. Captures messages so tests can assert on log output.
3
5
  * Equivalent to Go's cre/testutils/test_writer.go.
4
6
  */
5
7
  export class TestWriter {
6
8
  logs = [];
9
+ metrics = [];
7
10
  /** Appends a message to the captured log buffer. */
8
11
  log(message) {
9
12
  this.logs.push(message);
@@ -12,8 +15,17 @@ export class TestWriter {
12
15
  getLogs() {
13
16
  return [...this.logs];
14
17
  }
15
- /** Clears the captured log buffer. */
18
+ /** Captures a serialized WorkflowUserMetric payload. */
19
+ emitMetric(payload) {
20
+ this.metrics.push(payload);
21
+ }
22
+ /** Returns captured metric payloads decoded as `WorkflowUserMetric` protos. */
23
+ getMetrics() {
24
+ return this.metrics.map((bytes) => fromBinary(WorkflowUserMetricSchema, bytes));
25
+ }
26
+ /** Clears captured logs and metrics. */
16
27
  clear() {
17
28
  this.logs = [];
29
+ this.metrics = [];
18
30
  }
19
31
  }
@@ -45,6 +45,14 @@ declare global {
45
45
  */
46
46
  function log(message: string): void
47
47
 
48
+ /**
49
+ * Emits a user metric to the host. The payload is a protobuf-encoded
50
+ * `workflows.v2.WorkflowUserMetric` message.
51
+ * @param payload - protobuf-encoded WorkflowUserMetric bytes
52
+ * @returns 0 on success, negative on error (rate-limited, oversized, invalid name, etc.)
53
+ */
54
+ function emitMetric(payload: Uint8Array): number
55
+
48
56
  /**
49
57
  * Sends a response back to the host
50
58
  * @param response - bytes response
@@ -5,6 +5,7 @@ export declare const hostBindings: {
5
5
  getSecrets: (args_0: Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>, args_1: number, ...args: unknown[]) => any;
6
6
  awaitSecrets: (args_0: Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>, args_1: number, ...args: unknown[]) => Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>;
7
7
  log: (args_0: string, ...args: unknown[]) => void;
8
+ emitMetric: (args_0: Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>, ...args: unknown[]) => number;
8
9
  sendResponse: (args_0: Uint8Array<ArrayBufferLike> | Uint8Array<ArrayBuffer>, ...args: unknown[]) => number;
9
10
  switchModes: (args_0: Mode, ...args: unknown[]) => void;
10
11
  versionV2: (...args: unknown[]) => void;
@@ -27,6 +27,10 @@ const globalHostBindingsSchema = z.object({
27
27
  .returns(z.union([z.instanceof(Uint8Array), z.custom()])),
28
28
  getWasiArgs: z.function().args().returns(z.string()),
29
29
  now: z.function().args().returns(z.number()),
30
+ emitMetric: z
31
+ .function()
32
+ .args(z.union([z.instanceof(Uint8Array), z.custom()]))
33
+ .returns(z.number()),
30
34
  });
31
35
  // Validate global host functions at runtime
32
36
  const validateGlobalHostBindings = () => {
@@ -56,4 +56,7 @@ class WasmRuntimeHelpers {
56
56
  log(message) {
57
57
  hostBindings.log(message);
58
58
  }
59
+ emitMetric(payload) {
60
+ return hostBindings.emitMetric(payload) >= 0;
61
+ }
59
62
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chainlink/cre-sdk",
3
- "version": "1.6.0-alpha.3",
3
+ "version": "1.6.0-alpha.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -60,7 +60,7 @@
60
60
  "dependencies": {
61
61
  "@bufbuild/protobuf": "2.6.3",
62
62
  "@bufbuild/protoc-gen-es": "2.6.3",
63
- "@chainlink/cre-sdk-javy-plugin": "1.5.0",
63
+ "@chainlink/cre-sdk-javy-plugin": "1.6.0-alpha.5",
64
64
  "@standard-schema/spec": "1.0.0",
65
65
  "viem": "2.34.0",
66
66
  "zod": "3.25.76"
@@ -8,9 +8,9 @@ set -e
8
8
  # Create dist test workflow folder
9
9
  mkdir -p ./dist/workflows/standard_tests
10
10
 
11
- # Build javy wasm
12
- if [ ! -f ../cre-sdk-javy-plugin/dist/javy_chainlink_sdk.wasm ]; then
13
- echo "Error: javy_chainlink_sdk.wasm not found"
11
+ # Plugin package must be built (initialized plugin is what we ship).
12
+ if [ ! -f ../cre-sdk-javy-plugin/dist/javy-chainlink-sdk.plugin.wasm ]; then
13
+ echo "Error: javy-chainlink-sdk.plugin.wasm not found (run cre-sdk-javy-plugin build first)"
14
14
  exit 1
15
15
  fi
16
16
 
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process'
2
2
  import { existsSync } from 'node:fs'
3
3
  import { mkdir } from 'node:fs/promises'
4
4
  import path from 'node:path'
5
+ import { parseCompileFlags } from '@chainlink/cre-sdk-javy-plugin/scripts/parse-compile-flags'
5
6
 
6
7
  function runBun(args: string[]): Promise<void> {
7
8
  return new Promise((resolve, reject) => {
@@ -19,20 +20,29 @@ function runBun(args: string[]): Promise<void> {
19
20
 
20
21
  const isJsFile = (p: string) => ['.js', '.mjs', '.cjs'].includes(path.extname(p).toLowerCase())
21
22
 
22
- export const main = async (inputFile?: string, outputFile?: string) => {
23
+ export const main = async (
24
+ inputFile?: string,
25
+ outputFile?: string,
26
+ creExportsPaths?: string[],
27
+ pluginPath?: string | null,
28
+ ) => {
23
29
  const cliArgs = process.argv.slice(3)
30
+ const { creExports: cliCreExports, plugin: cliPlugin, rest: cliRest } = parseCompileFlags(cliArgs)
24
31
 
25
- // Resolve input/output from params or CLI
26
- const inputPath = inputFile ?? cliArgs[0]
27
- const outputPathArg = outputFile ?? cliArgs[1]
32
+ const inputPath = inputFile ?? cliRest[0]
33
+ const outputPathArg = outputFile ?? cliRest[1]
34
+ const creExports = creExportsPaths ?? cliCreExports
35
+ const plugin = pluginPath !== undefined ? pluginPath : cliPlugin
36
+
37
+ if (plugin !== null && plugin !== undefined && creExports.length > 0) {
38
+ console.error('❌ Error: --plugin and --cre-exports are mutually exclusive.')
39
+ process.exit(1)
40
+ }
28
41
 
29
42
  if (!inputPath) {
30
43
  console.error(
31
44
  'Usage: bun compile:js-to-wasm <path/to/input.(js|mjs|cjs)> [path/to/output.wasm]',
32
45
  )
33
- console.error('Examples:')
34
- console.error(' bun compile:js-to-wasm ./build/workflows/test.js')
35
- console.error(' bun compile:js-to-wasm ./build/workflows/test.mjs ./artifacts/test.wasm')
36
46
  process.exit(1)
37
47
  }
38
48
 
@@ -47,31 +57,41 @@ export const main = async (inputFile?: string, outputFile?: string) => {
47
57
  process.exit(1)
48
58
  }
49
59
 
50
- // Default output = same dir, same basename, .wasm extension
51
60
  const defaultOut = path.join(
52
61
  path.dirname(resolvedInput),
53
62
  path.basename(resolvedInput).replace(/\.(m|c)?js$/i, '.wasm'),
54
63
  )
55
64
  const resolvedOutput = outputPathArg ? path.resolve(outputPathArg) : defaultOut
56
65
 
57
- // Ensure output directory exists
58
66
  await mkdir(path.dirname(resolvedOutput), { recursive: true })
59
67
 
60
- console.info(`🔨 Compiling to WASM`)
68
+ console.info('🔨 Compiling to WASM')
61
69
  console.info(`📁 Input: ${resolvedInput}`)
62
70
  console.info(`🎯 Output: ${resolvedOutput}`)
63
71
 
64
- // Prefer the sibling @chainlink/cre-sdk-javy-plugin install (same as monorepo layout).
65
- // Bun's shell `$` template can throw EINVAL on some Linux/arm64 Docker setups; use spawn.
66
- const scriptDir = import.meta.dir
67
- const compilerPath = path.resolve(
68
- scriptDir,
69
- '../../../cre-sdk-javy-plugin/bin/compile-workflow.ts',
70
- )
72
+ const compileArgs: string[] = []
73
+ if (plugin != null && plugin !== '') {
74
+ compileArgs.push('--plugin', path.resolve(plugin))
75
+ } else {
76
+ compileArgs.push(...creExports.flatMap((p) => ['--cre-exports', path.resolve(p)]))
77
+ }
78
+ compileArgs.push(resolvedInput, resolvedOutput)
79
+
80
+ let javyPluginRoot: string
81
+ if (process.env.CRE_SDK_JAVY_PLUGIN_HOME) {
82
+ javyPluginRoot = path.resolve(process.env.CRE_SDK_JAVY_PLUGIN_HOME)
83
+ } else {
84
+ try {
85
+ javyPluginRoot = path.dirname(require.resolve('@chainlink/cre-sdk-javy-plugin/package.json'))
86
+ } catch {
87
+ javyPluginRoot = path.resolve(import.meta.dir, '../../../cre-sdk-javy-plugin')
88
+ }
89
+ }
90
+ const compilerPath = path.join(javyPluginRoot, 'bin/compile-workflow.ts')
71
91
  if (existsSync(compilerPath)) {
72
- await runBun(['--bun', compilerPath, resolvedInput, resolvedOutput])
92
+ await runBun(['--bun', compilerPath, ...compileArgs])
73
93
  } else {
74
- await runBun(['x', 'cre-compile-workflow', resolvedInput, resolvedOutput])
94
+ await runBun(['x', 'cre-compile-workflow', ...compileArgs])
75
95
  }
76
96
 
77
97
  console.info(`✅ Compiled: ${resolvedOutput}`)
@@ -1,17 +1,20 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { mkdir } from 'node:fs/promises'
3
3
  import path from 'node:path'
4
+ import { parseCompileFlags } from '@chainlink/cre-sdk-javy-plugin/scripts/parse-compile-flags'
4
5
  import { parseCompileCliArgs, skipTypeChecksFlag } from './compile-cli-args'
5
6
  import { main as compileToJs } from './compile-to-js'
6
7
  import { main as compileToWasm } from './compile-to-wasm'
7
8
 
8
9
  type CompileWorkflowOptions = {
9
10
  skipTypeChecks?: boolean
11
+ creExports?: string[]
12
+ plugin?: string | null
10
13
  }
11
14
 
12
15
  const printUsage = () => {
13
16
  console.error(
14
- `Usage: bun compile:workflow <path/to/workflow.ts> [path/to/output.wasm] [${skipTypeChecksFlag}]`,
17
+ `Usage: bun compile:workflow [--plugin <path>] [--cre-exports <crate-dir>]... <path/to/workflow.ts> [path/to/output.wasm] [${skipTypeChecksFlag}]`,
15
18
  )
16
19
  console.error('Examples:')
17
20
  console.error(' bun compile:workflow src/standard_tests/secrets/test.ts')
@@ -31,17 +34,25 @@ export const main = async (
31
34
  let parsedInputPath: string | undefined
32
35
  let parsedOutputPath: string | undefined
33
36
  let parsedSkipTypeChecks = false
37
+ let parsedCreExports: string[] = []
38
+ let parsedPlugin: string | null = null
34
39
 
35
- if (inputFile != null || outputWasmFile != null || options?.skipTypeChecks != null) {
40
+ if (inputFile != null || outputWasmFile != null || options != null) {
36
41
  parsedInputPath = inputFile
37
42
  parsedOutputPath = outputWasmFile
38
43
  parsedSkipTypeChecks = options?.skipTypeChecks ?? false
44
+ parsedCreExports = options?.creExports ?? []
45
+ parsedPlugin = options?.plugin !== undefined ? options.plugin : null
39
46
  } else {
40
47
  try {
41
- const parsed = parseCompileCliArgs(process.argv.slice(3))
48
+ const cliArgs = process.argv.slice(3)
49
+ const { creExports, plugin, rest } = parseCompileFlags(cliArgs)
50
+ const parsed = parseCompileCliArgs(rest)
42
51
  parsedInputPath = parsed.inputPath
43
52
  parsedOutputPath = parsed.outputPath
44
53
  parsedSkipTypeChecks = parsed.skipTypeChecks
54
+ parsedCreExports = creExports
55
+ parsedPlugin = plugin
45
56
  } catch (error) {
46
57
  console.error(error instanceof Error ? error.message : error)
47
58
  printUsage()
@@ -52,6 +63,11 @@ export const main = async (
52
63
  const inputPath = parsedInputPath
53
64
  const outputPathArg = parsedOutputPath
54
65
 
66
+ if (parsedPlugin != null && parsedPlugin !== '' && parsedCreExports.length > 0) {
67
+ console.error('❌ Error: --plugin and --cre-exports are mutually exclusive.')
68
+ process.exit(1)
69
+ }
70
+
55
71
  if (!inputPath) {
56
72
  printUsage()
57
73
  process.exit(1)
@@ -63,33 +79,36 @@ export const main = async (
63
79
  process.exit(1)
64
80
  }
65
81
 
66
- // Default final output = same dir, same basename, .wasm
67
82
  const defaultWasmOut = path.join(
68
83
  path.dirname(resolvedInput),
69
84
  path.basename(resolvedInput).replace(/\.[^.]+$/, '.wasm'),
70
85
  )
71
86
  const resolvedWasmOutput = outputPathArg ? path.resolve(outputPathArg) : defaultWasmOut
72
-
73
- // Put the intermediate JS next to the final wasm (so custom outputs stay together)
74
87
  const resolvedJsOutput = resolvedWasmOutput.replace(/\.wasm$/i, '.js')
75
88
 
76
- // Ensure directories exist (handles both intermediate JS dir and wasm dir)
77
89
  await mkdir(path.dirname(resolvedJsOutput), { recursive: true })
78
90
 
79
- console.info(`🚀 Compiling workflow`)
80
- console.info(`📁 Input: ${resolvedInput}\n`)
91
+ console.info('🚀 Compiling workflow')
92
+ console.info(`📁 Input: ${resolvedInput}`)
93
+ console.info(`🧪 JS out: ${resolvedJsOutput}`)
94
+ console.info(`🎯 WASM out:${resolvedWasmOutput}\n`)
81
95
  if (parsedSkipTypeChecks) {
82
96
  console.info(`⚠️ Skipping TypeScript checks (${skipTypeChecksFlag})`)
83
97
  }
84
98
 
85
- // Step 1: TS/JS → JS (bundled)
86
99
  console.info('📦 Step 1: Compiling JS...')
87
100
  await compileToJs(resolvedInput, resolvedJsOutput, { skipTypeChecks: parsedSkipTypeChecks })
88
101
 
89
- // Step 2: JS → WASM
90
102
  console.info('\n🔨 Step 2: Compiling to WASM...')
91
- await compileToWasm(resolvedJsOutput, resolvedWasmOutput)
103
+ await compileToWasm(resolvedJsOutput, resolvedWasmOutput, parsedCreExports, parsedPlugin)
92
104
 
93
105
  console.info(`\n✅ Workflow built: ${resolvedWasmOutput}`)
94
106
  return resolvedWasmOutput
95
107
  }
108
+
109
+ if (import.meta.main) {
110
+ main().catch((e) => {
111
+ console.error(e)
112
+ process.exit(1)
113
+ })
114
+ }