Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bee4825
chore(workflow): scaffold package configuration
vinaysingh8866 Sep 27, 2025
6529d75
feat(workflow): add module entry, config, and events
vinaysingh8866 Sep 27, 2025
14286a4
feat(workflow): add models and schema validation
vinaysingh8866 Sep 27, 2025
428d1bd
feat(workflow): add engine utilities (AttributePlanner, GuardEvaluator)
vinaysingh8866 Sep 27, 2025
216bcda
feat(workflow): add repositories for templates and instances
vinaysingh8866 Sep 27, 2025
1264434
feat(workflow): add protocol message types and constructors
vinaysingh8866 Sep 27, 2025
423a235
feat(workflow): add protocol handlers (publish, start, status, etc.)
vinaysingh8866 Sep 27, 2025
d4a2b48
feat(workflow): add actions, service and API
vinaysingh8866 Sep 27, 2025
a254522
test(workflow): add unit and integration tests
vinaysingh8866 Sep 27, 2025
93e1e53
test(e2e): add workflow anoncreds end-to-end test
vinaysingh8866 Sep 27, 2025
6d9c26e
chore: update pnpm-lock.yaml
vinaysingh8866 Sep 27, 2025
aa05856
chore(workflow): force guard evaluation to JMESPath
vinaysingh8866 Sep 27, 2025
044a515
feat(workflow): compute expressions use JMESPath
vinaysingh8866 Sep 27, 2025
e8da346
test(workflow): fix test imports for src/tests layout
vinaysingh8866 Sep 27, 2025
75ef9e0
test(e2e): refine workflow anoncreds
vinaysingh8866 Sep 27, 2025
fd2b14a
test(workflow): type-safe mocks,
vinaysingh8866 Sep 27, 2025
d572d4c
chore(workflow): satisfy Biome static-
vinaysingh8866 Sep 27, 2025
a3c71e3
fix(workflow): validation for
vinaysingh8866 Sep 27, 2025
4e93f29
feat(workflow): add Workflow 1.0
vinaysingh8866 Sep 27, 2025
dc6339a
fix(workflow): removed tests from build
vinaysingh8866 Sep 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/workflow/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Config } from '@jest/types'

const config: Config.InitialOptions = {
preset: 'ts-jest',
testEnvironment: 'node',
transform: {
'\\.(t|j)sx?$': [
'ts-jest',
{
tsconfig: {
isolatedModules: true,
},
},
],
},
displayName: '@credo-ts/workflow',
testMatch: ['**/?(*.)test.ts', '**/?(*.)spec.ts'],
setupFilesAfterEnv: ['./src/tests/setup.ts'],
}

export default config
43 changes: 43 additions & 0 deletions packages/workflow/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@credo-ts/workflow",
"main": "src/index",
"types": "src/index",
"version": "0.5.13",
"files": ["build"],
"license": "Apache-2.0",
"publishConfig": {
"main": "build/index",
"types": "build/index",
"access": "public"
},
"homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/workflow",
"repository": {
"type": "git",
"url": "https://github.com/openwallet-foundation/credo-ts",
"directory": "packages/workflow"
},
"scripts": {
"build": "pnpm run clean && pnpm run compile",
"clean": "rimraf ./build",
"compile": "tsc -p tsconfig.build.json",
"prepublishOnly": "pnpm run build",
"test": "jest",
"test:cov": "jest --coverage --runInBand"
},
"dependencies": {
"@credo-ts/core": "workspace:*",
"@credo-ts/didcomm": "workspace:*",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"jmespath": "^0.16.0"
},
"devDependencies": {
"@credo-ts/askar": "workspace:*",
"@credo-ts/node": "workspace:*",
"@openwallet-foundation/askar-nodejs": "catalog:",
"@types/jmespath": "^0.15.2",
"reflect-metadata": "catalog:",
"rimraf": "catalog:",
"typescript": "catalog:"
}
}
39 changes: 39 additions & 0 deletions packages/workflow/src/WorkflowEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { BaseEvent } from '@credo-ts/core'
import type { WorkflowInstanceRecord, WorkflowInstanceStatus } from './repository/WorkflowInstanceRecord'

export enum WorkflowEventTypes {
WorkflowInstanceStateChanged = 'WorkflowInstanceStateChanged',
WorkflowInstanceStatusChanged = 'WorkflowInstanceStatusChanged',
WorkflowInstanceCompleted = 'WorkflowInstanceCompleted',
}

export interface WorkflowInstanceStateChangedEvent extends BaseEvent {
type: WorkflowEventTypes.WorkflowInstanceStateChanged
payload: {
instanceRecord: WorkflowInstanceRecord
previousState: string | null
newState: string
event: string
actionKey?: string
msgId?: string
}
}

export interface WorkflowInstanceStatusChangedEvent extends BaseEvent {
type: WorkflowEventTypes.WorkflowInstanceStatusChanged
payload: {
instanceRecord: WorkflowInstanceRecord
previousStatus: WorkflowInstanceStatus | null
newStatus: WorkflowInstanceStatus
reason?: string
}
}

export interface WorkflowInstanceCompletedEvent extends BaseEvent {
type: WorkflowEventTypes.WorkflowInstanceCompleted
payload: {
instanceRecord: WorkflowInstanceRecord
state: string
section?: string
}
}
105 changes: 105 additions & 0 deletions packages/workflow/src/WorkflowModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type { AgentContext, DependencyManager, Module } from '@credo-ts/core'
import { AgentConfig, EventEmitter } from '@credo-ts/core'
import {
DidCommCredentialEventTypes,
DidCommCredentialState,
DidCommCredentialStateChangedEvent,
DidCommFeatureRegistry,
DidCommMessageHandlerRegistry,
DidCommProofEventTypes,
DidCommProofState,
DidCommProofStateChangedEvent,
DidCommProtocol,
} from '@credo-ts/didcomm'
import { WorkflowModuleConfig, WorkflowModuleConfigOptions } from './WorkflowModuleConfig'
import { WorkflowApi } from './api/WorkflowApi'
import { AdvanceHandler } from './protocol/handlers/AdvanceHandler'
import { CancelHandler } from './protocol/handlers/CancelHandler'
import { CompleteHandler } from './protocol/handlers/CompleteHandler'
import { PauseHandler } from './protocol/handlers/PauseHandler'
import { ProblemReportHandler } from './protocol/handlers/ProblemReportHandler'
import { PublishTemplateHandler } from './protocol/handlers/PublishTemplateHandler'
import { ResumeHandler } from './protocol/handlers/ResumeHandler'
import { StartHandler } from './protocol/handlers/StartHandler'
import { StatusHandler } from './protocol/handlers/StatusHandler'
import { WorkflowInstanceRepository } from './repository/WorkflowInstanceRepository'
import { WorkflowTemplateRepository } from './repository/WorkflowTemplateRepository'
import { WorkflowService } from './services/WorkflowService'

export const WORKFLOW_PROTOCOL_URI = 'https://didcomm.org/workflow/1.0'
export const WORKFLOW_ROLES = ['processor', 'coordinator'] as const

export class WorkflowModule implements Module {
public readonly api = WorkflowApi
public readonly config: WorkflowModuleConfig

public constructor(options?: WorkflowModuleConfigOptions) {
this.config = new WorkflowModuleConfig(options)
}

public register(dependencyManager: DependencyManager) {
dependencyManager.resolve(AgentConfig).logger.info('Registering WorkflowModule')
dependencyManager.registerInstance(WorkflowModuleConfig, this.config)

dependencyManager.registerSingleton(WorkflowTemplateRepository)
dependencyManager.registerSingleton(WorkflowInstanceRepository)
dependencyManager.registerSingleton(WorkflowService)
dependencyManager.registerSingleton(WorkflowApi)

dependencyManager.registerSingleton(PublishTemplateHandler)
dependencyManager.registerSingleton(StartHandler)
dependencyManager.registerSingleton(AdvanceHandler)
dependencyManager.registerSingleton(StatusHandler)
dependencyManager.registerSingleton(ProblemReportHandler)
dependencyManager.registerSingleton(PauseHandler)
dependencyManager.registerSingleton(ResumeHandler)
dependencyManager.registerSingleton(CancelHandler)
dependencyManager.registerSingleton(CompleteHandler)
}

public async initialize(agentContext: AgentContext): Promise<void> {
const dm = agentContext.dependencyManager
const logger = dm.resolve(AgentConfig).logger
const features = dm.resolve(DidCommFeatureRegistry)
const handlers = dm.resolve(DidCommMessageHandlerRegistry)
logger.info('Initializing WorkflowModule - registering workflow/1.0 protocol')
try {
features.register(new DidCommProtocol({ id: WORKFLOW_PROTOCOL_URI, roles: [...WORKFLOW_ROLES] }))
} catch {}
handlers.registerMessageHandler(dm.resolve(PublishTemplateHandler))
handlers.registerMessageHandler(dm.resolve(StartHandler))
handlers.registerMessageHandler(dm.resolve(AdvanceHandler))
handlers.registerMessageHandler(dm.resolve(StatusHandler))
handlers.registerMessageHandler(dm.resolve(ProblemReportHandler))
handlers.registerMessageHandler(dm.resolve(PauseHandler))
handlers.registerMessageHandler(dm.resolve(ResumeHandler))
handlers.registerMessageHandler(dm.resolve(CancelHandler))
handlers.registerMessageHandler(dm.resolve(CompleteHandler))
// Inbound mapping: credentials/proofs → workflow events
const events = dm.resolve(EventEmitter)
const service = dm.resolve(WorkflowService)
events.on<DidCommCredentialStateChangedEvent>(
DidCommCredentialEventTypes.DidCommCredentialStateChanged,
async (e) => {
const rec = e.payload.credentialExchangeRecord
const connId = rec.connectionId
if (!connId) return
if (rec.state === DidCommCredentialState.RequestReceived) {
await service.autoAdvanceByConnection(agentContext, connId, 'request_received')
} else if (rec.state === DidCommCredentialState.Done) {
await service.autoAdvanceByConnection(agentContext, connId, 'issued_ack')
}
}
)
events.on<DidCommProofStateChangedEvent>(DidCommProofEventTypes.ProofStateChanged, async (e) => {
const rec = e.payload.proofRecord
const connId = rec.connectionId
if (!connId) return
if (rec.state === DidCommProofState.PresentationReceived) {
await service.autoAdvanceByConnection(agentContext, connId, 'presentation_received')
} else if (rec.state === DidCommProofState.Done) {
await service.autoAdvanceByConnection(agentContext, connId, 'verified_ack')
}
})
}
}
22 changes: 22 additions & 0 deletions packages/workflow/src/WorkflowModuleConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export type GuardEngine = 'jmespath' | 'cel' | 'js'

export interface WorkflowModuleConfigOptions {
guardEngine?: GuardEngine
autoReturnExistingOnSingleton?: boolean
actionTimeoutMs?: number
enableProblemReport?: boolean
}

export class WorkflowModuleConfig {
public readonly guardEngine: GuardEngine
public readonly autoReturnExistingOnSingleton: boolean
public readonly actionTimeoutMs: number
public readonly enableProblemReport: boolean

public constructor(options?: WorkflowModuleConfigOptions) {
this.guardEngine = options?.guardEngine ?? 'jmespath'
this.autoReturnExistingOnSingleton = options?.autoReturnExistingOnSingleton ?? true
this.actionTimeoutMs = options?.actionTimeoutMs ?? 15000
this.enableProblemReport = options?.enableProblemReport ?? true
}
}
Loading
Loading