From d01b6b30cd8bc63c3306dd18ebe314cc481af271 Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Fri, 5 Sep 2025 11:19:34 -0400 Subject: [PATCH 1/2] Simplify trigger interfaces --- .../blockchain/evm/v1alpha/client_sdk_gen.ts | 16 ++--- .../actionandtrigger/v1/basic_sdk_gen.ts | 16 ++--- .../internal/basictrigger/v1/basic_sdk_gen.ts | 16 ++--- .../networking/http/v1alpha/http_sdk_gen.ts | 16 ++--- .../scheduler/cron/v1/cron_sdk_gen.ts | 64 ++----------------- src/generator/generate-sdk.ts | 26 ++++---- src/generator/generate-trigger.ts | 14 ++-- src/sdk/utils/triggers/trigger-interface.ts | 59 ++--------------- 8 files changed, 48 insertions(+), 179 deletions(-) diff --git a/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts b/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts index 768ae774..00758f0a 100644 --- a/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts +++ b/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts @@ -5,7 +5,7 @@ import { } from "@cre/generated/sdk/v1alpha/sdk_pb"; import { callCapability } from "@cre/sdk/utils/capabilities/call-capability"; import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import { BaseTriggerImpl } from "@cre/sdk/utils/triggers/trigger-interface"; +import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; import { type Any, AnySchema } from "@bufbuild/protobuf/wkt"; import { getTypeUrl } from "@cre/sdk/utils/typeurl"; import { @@ -463,15 +463,13 @@ export class ClientCapability { /** * Trigger implementation for LogTrigger */ -class ClientLogTrigger extends BaseTriggerImpl { +class ClientLogTrigger implements Trigger { constructor( - mode: Mode, - config: FilterLogTriggerRequestJson, + public readonly mode: Mode, + public readonly config: FilterLogTriggerRequestJson, private readonly _capabilityId: string, private readonly _method: string - ) { - super(mode, config); - } + ) {} capabilityId(): string { return this._capabilityId; @@ -481,10 +479,6 @@ class ClientLogTrigger extends BaseTriggerImpl { +class BasicTrigger implements Trigger { constructor( - mode: Mode, - config: ConfigJson, + public readonly mode: Mode, + public readonly config: ConfigJson, private readonly _capabilityId: string, private readonly _method: string - ) { - super(mode, config); - } + ) {} capabilityId(): string { return this._capabilityId; @@ -101,10 +99,6 @@ class BasicTrigger extends BaseTriggerImpl { +class BasicTrigger implements Trigger { constructor( - mode: Mode, - config: ConfigJson, + public readonly mode: Mode, + public readonly config: ConfigJson, private readonly _capabilityId: string, private readonly _method: string - ) { - super(mode, config); - } + ) {} capabilityId(): string { return this._capabilityId; @@ -64,10 +62,6 @@ class BasicTrigger extends BaseTriggerImpl { return this._method; } - newOutput(): Outputs { - return create(OutputsSchema); - } - outputSchema() { return OutputsSchema; } diff --git a/src/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.ts b/src/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.ts index 22f781e5..8a1e4650 100644 --- a/src/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.ts +++ b/src/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.ts @@ -5,7 +5,7 @@ import { } from "@cre/generated/sdk/v1alpha/sdk_pb"; import { callCapability } from "@cre/sdk/utils/capabilities/call-capability"; import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import { BaseTriggerImpl } from "@cre/sdk/utils/triggers/trigger-interface"; +import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; import { type Any, AnySchema } from "@bufbuild/protobuf/wkt"; import { getTypeUrl } from "@cre/sdk/utils/typeurl"; import { @@ -46,15 +46,13 @@ export class HTTPCapability { /** * Trigger implementation for Trigger */ -class HTTPTrigger extends BaseTriggerImpl { +class HTTPTrigger implements Trigger { constructor( - mode: Mode, - config: ConfigJson, + public readonly mode: Mode, + public readonly config: ConfigJson, private readonly _capabilityId: string, private readonly _method: string - ) { - super(mode, config); - } + ) {} capabilityId(): string { return this._capabilityId; @@ -64,10 +62,6 @@ class HTTPTrigger extends BaseTriggerImpl { return this._method; } - newOutput(): Payload { - return create(PayloadSchema); - } - outputSchema() { return PayloadSchema; } diff --git a/src/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.ts b/src/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.ts index 9239e722..9f13b1db 100644 --- a/src/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.ts +++ b/src/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.ts @@ -5,7 +5,7 @@ import { } from "@cre/generated/sdk/v1alpha/sdk_pb"; import { callCapability } from "@cre/sdk/utils/capabilities/call-capability"; import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import { BaseTriggerImpl } from "@cre/sdk/utils/triggers/trigger-interface"; +import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; import { type Any, AnySchema } from "@bufbuild/protobuf/wkt"; import { getTypeUrl } from "@cre/sdk/utils/typeurl"; import { @@ -43,22 +43,18 @@ export class CronCapability { trigger(config: ConfigJson): CronTrigger { return new CronTrigger(this.mode, config, CronCapability.CAPABILITY_ID, "Trigger"); } - - // Method legacyTrigger is mapped to untyped API } /** * Trigger implementation for Trigger */ -class CronTrigger extends BaseTriggerImpl { +class CronTrigger implements Trigger { constructor( - mode: Mode, - config: ConfigJson, + public readonly mode: Mode, + public readonly config: ConfigJson, private readonly _capabilityId: string, private readonly _method: string - ) { - super(mode, config); - } + ) {} capabilityId(): string { return this._capabilityId; @@ -68,10 +64,6 @@ class CronTrigger extends BaseTriggerImpl { return this._method; } - newOutput(): Payload { - return create(PayloadSchema); - } - outputSchema() { return PayloadSchema; } @@ -91,50 +83,4 @@ class CronTrigger extends BaseTriggerImpl { adapt(rawOutput: Payload): Payload { return rawOutput; } -} - -/** - * Trigger implementation for LegacyTrigger - */ -class CronLegacyTrigger extends BaseTriggerImpl { - constructor( - mode: Mode, - config: ConfigJson, - private readonly _capabilityId: string, - private readonly _method: string - ) { - super(mode, config); - } - - capabilityId(): string { - return this._capabilityId; - } - - method(): string { - return this._method; - } - - newOutput(): LegacyPayload { - return create(LegacyPayloadSchema); - } - - outputSchema() { - return LegacyPayloadSchema; - } - - configAsAny(): Any { - const configMessage = fromJson(ConfigSchema, this.config); - return create(AnySchema, { - typeUrl: getTypeUrl(ConfigSchema), - value: toBinary(ConfigSchema, configMessage), - }); - } - - /** - * Transform the raw trigger output - override this method if needed - * Default implementation returns the raw output unchanged - */ - adapt(rawOutput: LegacyPayload): LegacyPayload { - return rawOutput; - } } \ No newline at end of file diff --git a/src/generator/generate-sdk.ts b/src/generator/generate-sdk.ts index e74aa370..301327bf 100644 --- a/src/generator/generate-sdk.ts +++ b/src/generator/generate-sdk.ts @@ -93,7 +93,7 @@ export function generateSdk(file: GenFile, outputDir: string) { // Add trigger imports if needed if (hasTriggers) { - imports.add(`import { BaseTriggerImpl } from "@cre/sdk/utils/triggers/trigger-interface";`) + imports.add(`import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface";`) imports.add(`import { type Any, AnySchema } from "@bufbuild/protobuf/wkt";`) } @@ -114,22 +114,20 @@ export function generateSdk(file: GenFile, outputDir: string) { const chainSelectorLabel = capOption.labels?.ChainSelector as any const hasChainSelector = chainSelectorLabel?.kind?.case === 'uint64Label' + // Skip legacy methods + const serviceMethods = service.methods.filter((method) => { + const methodMeta = method.proto.options + ? getExtension(method.proto.options, methodOption) + : null + + return !methodMeta?.mapToUntypedApi + }) + // Generate methods - const methods = service.methods + const methods = serviceMethods .map((method) => { const methodName = lowerCaseFirstLetter(method.name) - // Check for method-specific options - const methodMeta = method.proto.options - ? getExtension(method.proto.options, methodOption) - : null - - // Skip methods that map to untyped API if configured - if (methodMeta?.mapToUntypedApi) { - return ` - // Method ${methodName} is mapped to untyped API` - } - // Check if this is a streaming method (trigger) if (method.methodKind === 'server_streaming') { return generateTriggerMethod(method, methodName, capabilityClassName, service.name) @@ -141,7 +139,7 @@ export function generateSdk(file: GenFile, outputDir: string) { .join('\n') // Generate trigger classes - const triggerClasses = service.methods + const triggerClasses = serviceMethods .filter((method) => method.methodKind === 'server_streaming') .map((method) => generateTriggerClass(method, service.name)) .join('\n') diff --git a/src/generator/generate-trigger.ts b/src/generator/generate-trigger.ts index 169df569..2f090015 100644 --- a/src/generator/generate-trigger.ts +++ b/src/generator/generate-trigger.ts @@ -38,15 +38,13 @@ export function generateTriggerClass(method: DescMethod, className: string): str /** * Trigger implementation for ${method.name} */ -class ${triggerClassName} extends BaseTriggerImpl<${method.input.name}Json, ${method.output.name}, ${method.output.name}> { +class ${triggerClassName} implements Trigger<${method.output.name}, ${method.output.name}> { constructor( - mode: Mode, - config: ${method.input.name}Json, + public readonly mode: Mode, + public readonly config: ${method.input.name}Json, private readonly _capabilityId: string, private readonly _method: string - ) { - super(mode, config); - } + ) {} capabilityId(): string { return this._capabilityId; @@ -56,10 +54,6 @@ class ${triggerClassName} extends BaseTriggerImpl<${method.input.name}Json, ${me return this._method; } - newOutput(): ${method.output.name} { - return create(${method.output.name}Schema); - } - outputSchema() { return ${method.output.name}Schema; } diff --git a/src/sdk/utils/triggers/trigger-interface.ts b/src/sdk/utils/triggers/trigger-interface.ts index 6f8843cc..b82b16ec 100644 --- a/src/sdk/utils/triggers/trigger-interface.ts +++ b/src/sdk/utils/triggers/trigger-interface.ts @@ -1,74 +1,29 @@ -import type { Mode } from '@cre/generated/sdk/v1alpha/sdk_pb' import type { Any } from '@bufbuild/protobuf/wkt' import type { Message } from '@bufbuild/protobuf' import type { GenMessage } from '@bufbuild/protobuf/codegenv2' /** - * Base interface for trigger capabilities in the CRE SDK + * Full trigger interface with adapt method * - * Triggers are server-streaming RPC methods that emit events - * to the workflow runtime. + * The adapt method allows transformation of the raw protobuf output + * to a more convenient type for the workflow. */ -export interface BaseTrigger> { +export interface Trigger< + TRawTriggerOutput extends Message, + TTriggerOutput = TRawTriggerOutput, +> { /** The capability ID for this trigger */ capabilityId(): string /** The method name for this trigger */ method(): string - /** Create a new instance of the raw output type */ - newOutput(): TRawTriggerOutput - /** Access the raw output schema for decoding */ outputSchema(): GenMessage /** Get the configuration as an Any type for protobuf serialization */ configAsAny(): Any -} -/** - * Full trigger interface with adapt method - * - * The adapt method allows transformation of the raw protobuf output - * to a more convenient type for the workflow. - */ -export interface Trigger< - TRawTriggerOutput extends Message, - TTriggerOutput = TRawTriggerOutput, -> extends BaseTrigger { /** Transform the raw trigger output to the adapted type */ adapt(rawOutput: TRawTriggerOutput): TTriggerOutput | Promise } - -/** - * Base class for trigger implementations - */ -export abstract class BaseTriggerImpl< - TConfig, - TRawTriggerOutput extends Message, - TTriggerOutput = TRawTriggerOutput, -> implements Trigger -{ - constructor( - public readonly mode: Mode, - public readonly config: TConfig, - ) {} - - abstract capabilityId(): string - abstract method(): string - abstract newOutput(): TRawTriggerOutput - abstract outputSchema(): GenMessage - - /** Go naming parity */ - abstract configAsAny(): Any - - /** - * Default adapt implementation - returns raw output unchanged - * Override this method to transform the raw output to your desired type - */ - adapt(rawOutput: TRawTriggerOutput): TTriggerOutput | Promise { - // Type assertion is safe here as TTriggerOutput defaults to TRawTriggerOutput - // when not explicitly specified - return rawOutput as unknown as TTriggerOutput - } -} From 1649468645debed6fa86cc6ca91dee809f0b3e4c Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Fri, 5 Sep 2025 13:38:19 -0400 Subject: [PATCH 2/2] Fix spacing issue --- .../blockchain/evm/v1alpha/client_sdk_gen.ts | 2 +- .../actionandtrigger/v1/basic_sdk_gen.ts | 2 +- .../internal/basictrigger/v1/basic_sdk_gen.ts | 2 +- .../networking/http/v1alpha/http_sdk_gen.ts | 2 +- .../scheduler/cron/v1/cron_sdk_gen.ts | 2 +- src/generator/generate-sdk.ts | 364 ++++++++++-------- 6 files changed, 202 insertions(+), 172 deletions(-) diff --git a/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts b/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts index 00758f0a..5d237743 100644 --- a/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts +++ b/src/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.ts @@ -5,7 +5,7 @@ import { } from "@cre/generated/sdk/v1alpha/sdk_pb"; import { callCapability } from "@cre/sdk/utils/capabilities/call-capability"; import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; +import { type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; import { type Any, AnySchema } from "@bufbuild/protobuf/wkt"; import { getTypeUrl } from "@cre/sdk/utils/typeurl"; import { diff --git a/src/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen.ts b/src/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen.ts index a378457d..33b17125 100644 --- a/src/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen.ts +++ b/src/generated-sdk/capabilities/internal/actionandtrigger/v1/basic_sdk_gen.ts @@ -5,7 +5,7 @@ import { } from "@cre/generated/sdk/v1alpha/sdk_pb"; import { callCapability } from "@cre/sdk/utils/capabilities/call-capability"; import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; +import { type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; import { type Any, AnySchema } from "@bufbuild/protobuf/wkt"; import { getTypeUrl } from "@cre/sdk/utils/typeurl"; import { diff --git a/src/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen.ts b/src/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen.ts index 02fcfe99..29d271d3 100644 --- a/src/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen.ts +++ b/src/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen.ts @@ -5,7 +5,7 @@ import { } from "@cre/generated/sdk/v1alpha/sdk_pb"; import { callCapability } from "@cre/sdk/utils/capabilities/call-capability"; import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; +import { type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; import { type Any, AnySchema } from "@bufbuild/protobuf/wkt"; import { getTypeUrl } from "@cre/sdk/utils/typeurl"; import { diff --git a/src/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.ts b/src/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.ts index 8a1e4650..2691669f 100644 --- a/src/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.ts +++ b/src/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen.ts @@ -5,7 +5,7 @@ import { } from "@cre/generated/sdk/v1alpha/sdk_pb"; import { callCapability } from "@cre/sdk/utils/capabilities/call-capability"; import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; +import { type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; import { type Any, AnySchema } from "@bufbuild/protobuf/wkt"; import { getTypeUrl } from "@cre/sdk/utils/typeurl"; import { diff --git a/src/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.ts b/src/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.ts index 9f13b1db..aa18a441 100644 --- a/src/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.ts +++ b/src/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen.ts @@ -5,7 +5,7 @@ import { } from "@cre/generated/sdk/v1alpha/sdk_pb"; import { callCapability } from "@cre/sdk/utils/capabilities/call-capability"; import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error"; -import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; +import { type Trigger } from "@cre/sdk/utils/triggers/trigger-interface"; import { type Any, AnySchema } from "@bufbuild/protobuf/wkt"; import { getTypeUrl } from "@cre/sdk/utils/typeurl"; import { diff --git a/src/generator/generate-sdk.ts b/src/generator/generate-sdk.ts index 301327bf..a9476b19 100644 --- a/src/generator/generate-sdk.ts +++ b/src/generator/generate-sdk.ts @@ -1,24 +1,26 @@ -import { writeFileSync, mkdirSync } from 'node:fs' -import { join, dirname } from 'node:path' -import { getExtension, type DescService } from '@bufbuild/protobuf' +import { writeFileSync, mkdirSync } from "node:fs" +import { join, dirname } from "node:path" +import { getExtension, type DescService } from "@bufbuild/protobuf" import { - capability, - method as methodOption, -} from '@cre/generated/tools/generator/v1alpha/cre_metadata_pb' -import { Mode } from '@cre/generated/sdk/v1alpha/sdk_pb' -import type { GenFile } from '@bufbuild/protobuf/codegenv2' -import type { CapabilityMetadata } from '@cre/generated/tools/generator/v1alpha/cre_metadata_pb' -import { generateActionMethod } from './generate-action' -import { generateTriggerMethod, generateTriggerClass } from './generate-trigger' -import { lowerCaseFirstLetter, getImportPathForFile } from './utils' - -const getCapabilityServiceOptions = (service: DescService): CapabilityMetadata | false => { - if (!service.proto.options) { - return false - } - - const capabilityOption = getExtension(service.proto.options, capability) - return capabilityOption || false + capability, + method as methodOption, +} from "@cre/generated/tools/generator/v1alpha/cre_metadata_pb" +import { Mode } from "@cre/generated/sdk/v1alpha/sdk_pb" +import type { GenFile } from "@bufbuild/protobuf/codegenv2" +import type { CapabilityMetadata } from "@cre/generated/tools/generator/v1alpha/cre_metadata_pb" +import { generateActionMethod } from "./generate-action" +import { generateTriggerMethod, generateTriggerClass } from "./generate-trigger" +import { lowerCaseFirstLetter, getImportPathForFile } from "./utils" + +const getCapabilityServiceOptions = ( + service: DescService +): CapabilityMetadata | false => { + if (!service.proto.options) { + return false + } + + const capabilityOption = getExtension(service.proto.options, capability) + return capabilityOption || false } /** @@ -28,152 +30,175 @@ const getCapabilityServiceOptions = (service: DescService): CapabilityMetadata | * @param outputDir - The directory to output the SDK to. */ export function generateSdk(file: GenFile, outputDir: string) { - const capabilityServices = file.services.filter(getCapabilityServiceOptions) - if (capabilityServices.length === 0) { - console.warn(`No capability services found in ${file.name}. Skipping...`) - return - } - - // Process each service - capabilityServices.forEach((service) => { - const capOption = getCapabilityServiceOptions(service) - if (!capOption) { - // Shouldn't happen, we already filtered for services with capability metadata. - return - } - - // Generate imports - collect all unique types first - const typeImports = new Map>() - - // Process each method to collect types - service.methods.forEach((method) => { - // Handle input type - const inputFile = method.input.file - const inputPath = - inputFile.name === file.name - ? `@cre/generated/${file.name}_pb` - : getImportPathForFile(inputFile.name) - - if (!typeImports.has(inputPath)) { - typeImports.set(inputPath, new Set()) - } - - const inputPathTypes = typeImports.get(inputPath)! - inputPathTypes.add(`${method.input.name}Schema`) - inputPathTypes.add(`type ${method.input.name}Json`) - - // Handle output type - const outputFile = method.output.file - const outputPath = - outputFile.name === file.name - ? `@cre/generated/${file.name}_pb` - : getImportPathForFile(outputFile.name) - - if (!typeImports.has(outputPath)) { - typeImports.set(outputPath, new Set()) - } - - const outputPathTypes = typeImports.get(outputPath)! - outputPathTypes.add(`${method.output.name}Schema`) - outputPathTypes.add(`type ${method.output.name}`) - }) - - // Check if we have any triggers - const hasTriggers = service.methods.some((m) => m.methodKind === 'server_streaming') - - // Build import statements - const imports = new Set() - imports.add('import { fromBinary, toBinary, fromJson, create } from "@bufbuild/protobuf";') - imports.add(`import { + const capabilityServices = file.services.filter(getCapabilityServiceOptions) + if (capabilityServices.length === 0) { + console.warn(`No capability services found in ${file.name}. Skipping...`) + return + } + + // Process each service + capabilityServices.forEach((service) => { + const capOption = getCapabilityServiceOptions(service) + if (!capOption) { + // Shouldn't happen, we already filtered for services with capability metadata. + return + } + + // Generate imports - collect all unique types first + const typeImports = new Map>() + + // Process each method to collect types + service.methods.forEach((method) => { + // Handle input type + const inputFile = method.input.file + const inputPath = + inputFile.name === file.name + ? `@cre/generated/${file.name}_pb` + : getImportPathForFile(inputFile.name) + + if (!typeImports.has(inputPath)) { + typeImports.set(inputPath, new Set()) + } + + const inputPathTypes = typeImports.get(inputPath)! + inputPathTypes.add(`${method.input.name}Schema`) + inputPathTypes.add(`type ${method.input.name}Json`) + + // Handle output type + const outputFile = method.output.file + const outputPath = + outputFile.name === file.name + ? `@cre/generated/${file.name}_pb` + : getImportPathForFile(outputFile.name) + + if (!typeImports.has(outputPath)) { + typeImports.set(outputPath, new Set()) + } + + const outputPathTypes = typeImports.get(outputPath)! + outputPathTypes.add(`${method.output.name}Schema`) + outputPathTypes.add(`type ${method.output.name}`) + }) + + // Check if we have any triggers + const hasTriggers = service.methods.some( + (m) => m.methodKind === "server_streaming" + ) + + // Build import statements + const imports = new Set() + imports.add( + 'import { fromBinary, toBinary, fromJson, create } from "@bufbuild/protobuf";' + ) + imports.add(`import { Mode, type CapabilityResponse, } from "@cre/generated/sdk/v1alpha/sdk_pb";`) - imports.add(`import { callCapability } from "@cre/sdk/utils/capabilities/call-capability";`) - imports.add(`import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error";`) - - // Add trigger imports if needed - if (hasTriggers) { - imports.add(`import {type Trigger } from "@cre/sdk/utils/triggers/trigger-interface";`) - imports.add(`import { type Any, AnySchema } from "@bufbuild/protobuf/wkt";`) - } - - // Always import getTypeUrl when we generate Any payloads - imports.add(`import { getTypeUrl } from "@cre/sdk/utils/typeurl";`) - - // Generate deduplicated type imports - typeImports.forEach((types, path) => { - const sortedTypes = Array.from(types).sort() - imports.add(`import { - ${sortedTypes.join(',\n ')}, + imports.add( + `import { callCapability } from "@cre/sdk/utils/capabilities/call-capability";` + ) + imports.add( + `import { CapabilityError } from "@cre/sdk/utils/capabilities/capability-error";` + ) + + // Add trigger imports if needed + if (hasTriggers) { + imports.add( + `import { type Trigger } from "@cre/sdk/utils/triggers/trigger-interface";` + ) + imports.add( + `import { type Any, AnySchema } from "@bufbuild/protobuf/wkt";` + ) + } + + // Always import getTypeUrl when we generate Any payloads + imports.add(`import { getTypeUrl } from "@cre/sdk/utils/typeurl";`) + + // Generate deduplicated type imports + typeImports.forEach((types, path) => { + const sortedTypes = Array.from(types).sort() + imports.add(`import { + ${sortedTypes.join(",\n ")}, } from "${path}";`) - }) - - const capabilityClassName = `${service.name}Capability` - - // Check if this capability supports chainSelector via labels - const chainSelectorLabel = capOption.labels?.ChainSelector as any - const hasChainSelector = chainSelectorLabel?.kind?.case === 'uint64Label' - - // Skip legacy methods - const serviceMethods = service.methods.filter((method) => { - const methodMeta = method.proto.options - ? getExtension(method.proto.options, methodOption) - : null - - return !methodMeta?.mapToUntypedApi - }) - - // Generate methods - const methods = serviceMethods - .map((method) => { - const methodName = lowerCaseFirstLetter(method.name) - - // Check if this is a streaming method (trigger) - if (method.methodKind === 'server_streaming') { - return generateTriggerMethod(method, methodName, capabilityClassName, service.name) - } - - // Generate action method - return generateActionMethod(method, methodName, capabilityClassName, hasChainSelector) - }) - .join('\n') - - // Generate trigger classes - const triggerClasses = serviceMethods - .filter((method) => method.methodKind === 'server_streaming') - .map((method) => generateTriggerClass(method, service.name)) - .join('\n') - - // Determine default mode from metadata: NODE is specifically stated, DON otherwise. - const defaultMode = capOption.mode === Mode.NODE ? 'Mode.NODE' : 'Mode.DON' - - const [capabilityName, capabilityVersion] = capOption.capabilityId.split('@') - - // Extract chainSelector support - let chainSelectorSupport = '' - let constructorParams = `private readonly mode: Mode = ${service.name}Capability.DEFAULT_MODE` - - if (hasChainSelector && capOption.labels) { - const chainSelectorLabel = capOption.labels.ChainSelector as any - if ( - chainSelectorLabel?.kind?.case === 'uint64Label' && - chainSelectorLabel?.kind?.value?.defaults - ) { - const defaults = chainSelectorLabel.kind.value.defaults - chainSelectorSupport = ` + }) + + const capabilityClassName = `${service.name}Capability` + + // Check if this capability supports chainSelector via labels + const chainSelectorLabel = capOption.labels?.ChainSelector as any + const hasChainSelector = chainSelectorLabel?.kind?.case === "uint64Label" + + // Skip legacy methods + const serviceMethods = service.methods.filter((method) => { + const methodMeta = method.proto.options + ? getExtension(method.proto.options, methodOption) + : null + + return !methodMeta?.mapToUntypedApi + }) + + // Generate methods + const methods = serviceMethods + .map((method) => { + const methodName = lowerCaseFirstLetter(method.name) + + // Check if this is a streaming method (trigger) + if (method.methodKind === "server_streaming") { + return generateTriggerMethod( + method, + methodName, + capabilityClassName, + service.name + ) + } + + // Generate action method + return generateActionMethod( + method, + methodName, + capabilityClassName, + hasChainSelector + ) + }) + .join("\n") + + // Generate trigger classes + const triggerClasses = serviceMethods + .filter((method) => method.methodKind === "server_streaming") + .map((method) => generateTriggerClass(method, service.name)) + .join("\n") + + // Determine default mode from metadata: NODE is specifically stated, DON otherwise. + const defaultMode = capOption.mode === Mode.NODE ? "Mode.NODE" : "Mode.DON" + + const [capabilityName, capabilityVersion] = + capOption.capabilityId.split("@") + + // Extract chainSelector support + let chainSelectorSupport = "" + let constructorParams = `private readonly mode: Mode = ${service.name}Capability.DEFAULT_MODE` + + if (hasChainSelector && capOption.labels) { + const chainSelectorLabel = capOption.labels.ChainSelector as any + if ( + chainSelectorLabel?.kind?.case === "uint64Label" && + chainSelectorLabel?.kind?.value?.defaults + ) { + const defaults = chainSelectorLabel.kind.value.defaults + chainSelectorSupport = ` /** Available chain selectors */ static readonly SUPPORTED_CHAINS = { ${Object.entries(defaults) - .map(([key, value]) => ` "${key}": ${value}n`) - .join(',\n')} + .map(([key, value]) => ` "${key}": ${value}n`) + .join(",\n")} } as const;` - constructorParams = `${constructorParams},\n private readonly chainSelector?: bigint` - } - } + constructorParams = `${constructorParams},\n private readonly chainSelector?: bigint` + } + } - // Add JSDoc with metadata information - const classComment = ` + // Add JSDoc with metadata information + const classComment = ` /** * ${service.name} Capability * @@ -183,8 +208,8 @@ ${Object.entries(defaults) * Capability Version: ${capabilityVersion} */` - // Generate the complete file - const output = `${Array.from(imports).join('\n')} + // Generate the complete file + const output = `${Array.from(imports).join("\n")} ${classComment} export class ${capabilityClassName} { /** The capability ID for this service */ @@ -204,16 +229,21 @@ ${methods} } ${triggerClasses}` - // Determine output path - const serviceNameLowerCased: Lowercase = service.name.toLowerCase() as Lowercase - const outputPath = join(outputDir, dirname(file.name), `${serviceNameLowerCased}_sdk_gen.ts`) + // Determine output path + const serviceNameLowerCased: Lowercase = + service.name.toLowerCase() as Lowercase + const outputPath = join( + outputDir, + dirname(file.name), + `${serviceNameLowerCased}_sdk_gen.ts` + ) - // Create directory if needed - mkdirSync(dirname(outputPath), { recursive: true }) + // Create directory if needed + mkdirSync(dirname(outputPath), { recursive: true }) - // Write file - writeFileSync(outputPath, output) + // Write file + writeFileSync(outputPath, output) - console.log(`✅ Generated SDK for ${service.name} at: ${outputPath} .`) - }) + console.log(`✅ Generated SDK for ${service.name} at: ${outputPath} .`) + }) }