@contractspec/example.workflow-system 1.46.0 → 1.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/approval/approval.enum.d.ts +3 -3
- package/dist/approval/approval.enum.d.ts.map +1 -1
- package/dist/approval/approval.event.d.ts +32 -32
- package/dist/approval/approval.event.d.ts.map +1 -1
- package/dist/approval/approval.event.js +5 -5
- package/dist/approval/approval.event.js.map +1 -1
- package/dist/approval/approval.handler.js.map +1 -1
- package/dist/approval/approval.operations.d.ts +134 -134
- package/dist/approval/approval.schema.d.ts +24 -24
- package/dist/entities/approval.d.ts +36 -36
- package/dist/entities/approval.d.ts.map +1 -1
- package/dist/entities/index.d.ts +127 -127
- package/dist/entities/index.js.map +1 -1
- package/dist/entities/instance.d.ts +47 -47
- package/dist/entities/step.d.ts +32 -32
- package/dist/entities/workflow.d.ts +23 -23
- package/dist/example.d.ts +2 -2
- package/dist/example.d.ts.map +1 -1
- package/dist/example.js +4 -2
- package/dist/example.js.map +1 -1
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +3 -0
- package/dist/handlers/workflow.handlers.d.ts +122 -0
- package/dist/handlers/workflow.handlers.d.ts.map +1 -0
- package/dist/handlers/workflow.handlers.js +263 -0
- package/dist/handlers/workflow.handlers.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/instance/instance.enum.d.ts +2 -2
- package/dist/instance/instance.event.d.ts +87 -87
- package/dist/instance/instance.event.d.ts.map +1 -1
- package/dist/instance/instance.event.js +4 -4
- package/dist/instance/instance.event.js.map +1 -1
- package/dist/instance/instance.handler.js.map +1 -1
- package/dist/instance/instance.operations.d.ts +256 -256
- package/dist/instance/instance.schema.d.ts +54 -54
- package/dist/presentations/index.d.ts +23 -24
- package/dist/presentations/index.d.ts.map +1 -1
- package/dist/presentations/index.js +26 -23
- package/dist/presentations/index.js.map +1 -1
- package/dist/seeders/index.d.ts +10 -0
- package/dist/seeders/index.d.ts.map +1 -0
- package/dist/seeders/index.js +19 -0
- package/dist/seeders/index.js.map +1 -0
- package/dist/state-machine/index.js.map +1 -1
- package/dist/tests/operations.test-spec.d.ts +10 -0
- package/dist/tests/operations.test-spec.d.ts.map +1 -0
- package/dist/tests/operations.test-spec.js +123 -0
- package/dist/tests/operations.test-spec.js.map +1 -0
- package/dist/ui/WorkflowDashboard.d.ts +7 -0
- package/dist/ui/WorkflowDashboard.d.ts.map +1 -0
- package/dist/ui/WorkflowDashboard.js +223 -0
- package/dist/ui/WorkflowDashboard.js.map +1 -0
- package/dist/ui/hooks/index.d.ts +2 -0
- package/dist/ui/hooks/index.js +5 -0
- package/dist/ui/hooks/useWorkflowList.d.ts +22 -0
- package/dist/ui/hooks/useWorkflowList.d.ts.map +1 -0
- package/dist/ui/hooks/useWorkflowList.js +55 -0
- package/dist/ui/hooks/useWorkflowList.js.map +1 -0
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/renderers/index.d.ts +2 -0
- package/dist/ui/renderers/index.js +3 -0
- package/dist/ui/renderers/workflow.markdown.d.ts +28 -0
- package/dist/ui/renderers/workflow.markdown.d.ts.map +1 -0
- package/dist/ui/renderers/workflow.markdown.js +234 -0
- package/dist/ui/renderers/workflow.markdown.js.map +1 -0
- package/dist/workflow/workflow.enum.d.ts +5 -5
- package/dist/workflow/workflow.enum.d.ts.map +1 -1
- package/dist/workflow/workflow.event.d.ts +33 -33
- package/dist/workflow/workflow.event.js +3 -3
- package/dist/workflow/workflow.event.js.map +1 -1
- package/dist/workflow/workflow.handler.js.map +1 -1
- package/dist/workflow/workflow.operations.d.ts +246 -246
- package/dist/workflow/workflow.schema.d.ts +63 -63
- package/dist/workflow/workflow.schema.d.ts.map +1 -1
- package/dist/workflow-system.capability.d.ts +9 -0
- package/dist/workflow-system.capability.d.ts.map +1 -0
- package/dist/workflow-system.capability.js +34 -0
- package/dist/workflow-system.capability.js.map +1 -0
- package/dist/workflow-system.feature.d.ts +2 -2
- package/dist/workflow-system.feature.d.ts.map +1 -1
- package/dist/workflow-system.feature.js +9 -2
- package/dist/workflow-system.feature.js.map +1 -1
- package/package.json +24 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["newStatus: StateMachineState['status']","stepMap: Record<string, StateMachineStep>"],"sources":["../../src/state-machine/index.ts"],"sourcesContent":["/**\n * Workflow State Machine Engine\n *\n * Provides state machine logic for workflow transitions.\n * This is a spec-level abstraction that defines the interface\n * for workflow state transitions.\n */\n\n// ============ Types ============\n\n/**\n * Represents a transition definition in a workflow step.\n */\nexport interface TransitionDefinition {\n /** The action that triggers this transition (e.g., \"approve\", \"reject\") */\n action: string;\n /** The target step key to transition to */\n targetStepKey: string;\n /** Optional condition expression for conditional transitions */\n condition?: string;\n /** Optional guard roles - only users with these roles can take this action */\n allowedRoles?: string[];\n}\n\n/**\n * Step definition for state machine.\n */\nexport interface StateMachineStep {\n key: string;\n name: string;\n type:\n | 'START'\n | 'APPROVAL'\n | 'TASK'\n | 'CONDITION'\n | 'PARALLEL'\n | 'WAIT'\n | 'ACTION'\n | 'END';\n /** Map of action -> transition definition */\n transitions: Record<string, string | TransitionDefinition>;\n /** For approval steps: how approvals are handled */\n approvalMode?: 'ANY' | 'ALL' | 'MAJORITY' | 'SEQUENTIAL';\n /** Roles that can approve/act on this step */\n allowedRoles?: string[];\n /** Timeout in seconds */\n timeoutSeconds?: number;\n /** Condition expression for CONDITION type */\n conditionExpression?: string;\n}\n\n/**\n * Workflow state machine definition.\n */\nexport interface StateMachineDefinition {\n key: string;\n name: string;\n version: string;\n initialStepKey: string;\n steps: Record<string, StateMachineStep>;\n}\n\n/**\n * Current state of an instance.\n */\nexport interface StateMachineState {\n currentStepKey: string;\n status:\n | 'PENDING'\n | 'RUNNING'\n | 'WAITING'\n | 'PAUSED'\n | 'COMPLETED'\n | 'CANCELLED'\n | 'FAILED'\n | 'TIMEOUT';\n contextData: Record<string, unknown>;\n history: {\n stepKey: string;\n action: string;\n timestamp: Date;\n executedBy: string;\n }[];\n}\n\n/**\n * Result of a transition attempt.\n */\nexport interface TransitionResult {\n success: boolean;\n previousStepKey: string;\n currentStepKey: string | null;\n status: StateMachineState['status'];\n error?: string;\n}\n\n/**\n * Context for transition validation.\n */\nexport interface TransitionContext {\n userId: string;\n userRoles: string[];\n data?: Record<string, unknown>;\n}\n\n// ============ State Machine Engine ============\n\n/**\n * State machine engine interface.\n * Implementation should be provided at runtime.\n */\nexport interface IStateMachineEngine {\n /**\n * Validate that a transition is allowed.\n */\n canTransition(\n definition: StateMachineDefinition,\n state: StateMachineState,\n action: string,\n context: TransitionContext\n ): { allowed: boolean; reason?: string };\n\n /**\n * Get available actions for the current step.\n */\n getAvailableActions(\n definition: StateMachineDefinition,\n state: StateMachineState,\n context: TransitionContext\n ): string[];\n\n /**\n * Execute a transition.\n */\n transition(\n definition: StateMachineDefinition,\n state: StateMachineState,\n action: string,\n context: TransitionContext\n ): TransitionResult;\n\n /**\n * Evaluate a condition expression.\n */\n evaluateCondition(\n expression: string,\n contextData: Record<string, unknown>\n ): boolean;\n}\n\n/**\n * Basic state machine engine implementation.\n * This is a reference implementation for spec validation.\n */\nexport class BasicStateMachineEngine implements IStateMachineEngine {\n canTransition(\n definition: StateMachineDefinition,\n state: StateMachineState,\n action: string,\n context: TransitionContext\n ): { allowed: boolean; reason?: string } {\n // Check if state allows transitions\n if (state.status !== 'RUNNING' && state.status !== 'WAITING') {\n return {\n allowed: false,\n reason: `Workflow is ${state.status}, cannot transition`,\n };\n }\n\n const currentStep = definition.steps[state.currentStepKey];\n if (!currentStep) {\n return {\n allowed: false,\n reason: `Step ${state.currentStepKey} not found`,\n };\n }\n\n // Check if action exists for this step\n const transition = currentStep.transitions[action];\n if (!transition) {\n return {\n allowed: false,\n reason: `Action ${action} not available in step ${state.currentStepKey}`,\n };\n }\n\n // Check role-based access\n if (currentStep.allowedRoles && currentStep.allowedRoles.length > 0) {\n const hasRole = currentStep.allowedRoles.some((role) =>\n context.userRoles.includes(role)\n );\n if (!hasRole) {\n return {\n allowed: false,\n reason: `User lacks required role for this action`,\n };\n }\n }\n\n // Check transition-specific roles if defined\n if (\n typeof transition === 'object' &&\n transition.allowedRoles &&\n transition.allowedRoles.length > 0\n ) {\n const hasRole = transition.allowedRoles.some((role) =>\n context.userRoles.includes(role)\n );\n if (!hasRole) {\n return {\n allowed: false,\n reason: `User lacks required role for action ${action}`,\n };\n }\n }\n\n return { allowed: true };\n }\n\n getAvailableActions(\n definition: StateMachineDefinition,\n state: StateMachineState,\n context: TransitionContext\n ): string[] {\n if (state.status !== 'RUNNING' && state.status !== 'WAITING') {\n return [];\n }\n\n const currentStep = definition.steps[state.currentStepKey];\n if (!currentStep) {\n return [];\n }\n\n return Object.keys(currentStep.transitions).filter((action) => {\n const result = this.canTransition(definition, state, action, context);\n return result.allowed;\n });\n }\n\n transition(\n definition: StateMachineDefinition,\n state: StateMachineState,\n action: string,\n context: TransitionContext\n ): TransitionResult {\n const validation = this.canTransition(definition, state, action, context);\n if (!validation.allowed) {\n return {\n success: false,\n previousStepKey: state.currentStepKey,\n currentStepKey: state.currentStepKey,\n status: state.status,\n error: validation.reason,\n };\n }\n\n const currentStep = definition.steps[state.currentStepKey];\n if (!currentStep) {\n return {\n success: false,\n previousStepKey: state.currentStepKey,\n currentStepKey: state.currentStepKey,\n status: state.status,\n error: `Current step ${state.currentStepKey} not found`,\n };\n }\n const transition = currentStep.transitions[action];\n if (!transition) {\n return {\n success: false,\n previousStepKey: state.currentStepKey,\n currentStepKey: state.currentStepKey,\n status: state.status,\n error: `Transition for action ${action} not found`,\n };\n }\n const targetStepKey =\n typeof transition === 'string' ? transition : transition.targetStepKey;\n const targetStep = definition.steps[targetStepKey];\n\n if (!targetStep) {\n return {\n success: false,\n previousStepKey: state.currentStepKey,\n currentStepKey: state.currentStepKey,\n status: state.status,\n error: `Target step ${targetStepKey} not found`,\n };\n }\n\n // Determine new status\n let newStatus: StateMachineState['status'] = 'RUNNING';\n if (targetStep.type === 'END') {\n newStatus = 'COMPLETED';\n } else if (targetStep.type === 'APPROVAL' || targetStep.type === 'WAIT') {\n newStatus = 'WAITING';\n }\n\n return {\n success: true,\n previousStepKey: state.currentStepKey,\n currentStepKey: targetStepKey,\n status: newStatus,\n };\n }\n\n evaluateCondition(\n expression: string,\n contextData: Record<string, unknown>\n ): boolean {\n // Basic condition evaluation\n // In production, this should use a proper expression language\n try {\n // Simple property checks like \"amount > 1000\"\n const match = expression.match(\n /^(\\w+)\\s*(>=|<=|>|<|===|!==|==|!=)\\s*(.+)$/\n );\n if (match) {\n const [, prop, operator, value] = match;\n if (!prop || !operator || value === undefined) {\n return false;\n }\n const propValue = contextData[prop];\n const compareValue = JSON.parse(value);\n\n switch (operator) {\n case '>':\n return Number(propValue) > Number(compareValue);\n case '<':\n return Number(propValue) < Number(compareValue);\n case '>=':\n return Number(propValue) >= Number(compareValue);\n case '<=':\n return Number(propValue) <= Number(compareValue);\n case '===':\n case '==':\n return propValue === compareValue;\n case '!==':\n case '!=':\n return propValue !== compareValue;\n }\n }\n\n // Check for boolean property\n if (expression in contextData) {\n return Boolean(contextData[expression]);\n }\n\n return false;\n } catch {\n return false;\n }\n }\n}\n\n/**\n * Create a new state machine engine instance.\n */\nexport function createStateMachineEngine(): IStateMachineEngine {\n return new BasicStateMachineEngine();\n}\n\n// ============ Utility Functions ============\n\n/**\n * Build a state machine definition from workflow entities.\n */\nexport function buildStateMachineDefinition(\n workflow: {\n key: string;\n name: string;\n version: string;\n initialStepId: string | null;\n },\n steps: {\n key: string;\n name: string;\n type: string;\n transitions: Record<string, string | TransitionDefinition>;\n approvalMode?: string;\n approverRoles?: string[];\n timeoutSeconds?: number;\n conditionExpression?: string;\n }[]\n): StateMachineDefinition {\n const stepMap: Record<string, StateMachineStep> = {};\n\n for (const step of steps) {\n stepMap[step.key] = {\n key: step.key,\n name: step.name,\n type: step.type as StateMachineStep['type'],\n transitions: step.transitions,\n approvalMode: step.approvalMode as StateMachineStep['approvalMode'],\n allowedRoles: step.approverRoles,\n timeoutSeconds: step.timeoutSeconds,\n conditionExpression: step.conditionExpression,\n };\n }\n\n // Find initial step\n const startStep = steps.find((s) => s.type === 'START');\n const initialStepKey = startStep?.key ?? steps[0]?.key ?? '';\n\n return {\n key: workflow.key,\n name: workflow.name,\n version: workflow.version,\n initialStepKey,\n steps: stepMap,\n };\n}\n\n/**\n * Create initial state for a new workflow instance.\n */\nexport function createInitialState(\n definition: StateMachineDefinition,\n contextData: Record<string, unknown> = {}\n): StateMachineState {\n return {\n currentStepKey: definition.initialStepKey,\n status: 'RUNNING',\n contextData,\n history: [],\n };\n}\n"],"mappings":";;;;;AA0JA,IAAa,0BAAb,MAAoE;CAClE,cACE,YACA,OACA,QACA,SACuC;AAEvC,MAAI,MAAM,WAAW,aAAa,MAAM,WAAW,UACjD,QAAO;GACL,SAAS;GACT,QAAQ,eAAe,MAAM,OAAO;GACrC;EAGH,MAAM,cAAc,WAAW,MAAM,MAAM;AAC3C,MAAI,CAAC,YACH,QAAO;GACL,SAAS;GACT,QAAQ,QAAQ,MAAM,eAAe;GACtC;EAIH,MAAM,aAAa,YAAY,YAAY;AAC3C,MAAI,CAAC,WACH,QAAO;GACL,SAAS;GACT,QAAQ,UAAU,OAAO,yBAAyB,MAAM;GACzD;AAIH,MAAI,YAAY,gBAAgB,YAAY,aAAa,SAAS,GAIhE;OAAI,CAHY,YAAY,aAAa,MAAM,SAC7C,QAAQ,UAAU,SAAS,KAAK,CACjC,CAEC,QAAO;IACL,SAAS;IACT,QAAQ;IACT;;AAKL,MACE,OAAO,eAAe,YACtB,WAAW,gBACX,WAAW,aAAa,SAAS,GAKjC;OAAI,CAHY,WAAW,aAAa,MAAM,SAC5C,QAAQ,UAAU,SAAS,KAAK,CACjC,CAEC,QAAO;IACL,SAAS;IACT,QAAQ,uCAAuC;IAChD;;AAIL,SAAO,EAAE,SAAS,MAAM;;CAG1B,oBACE,YACA,OACA,SACU;AACV,MAAI,MAAM,WAAW,aAAa,MAAM,WAAW,UACjD,QAAO,EAAE;EAGX,MAAM,cAAc,WAAW,MAAM,MAAM;AAC3C,MAAI,CAAC,YACH,QAAO,EAAE;AAGX,SAAO,OAAO,KAAK,YAAY,YAAY,CAAC,QAAQ,WAAW;AAE7D,UADe,KAAK,cAAc,YAAY,OAAO,QAAQ,QAAQ,CACvD;IACd;;CAGJ,WACE,YACA,OACA,QACA,SACkB;EAClB,MAAM,aAAa,KAAK,cAAc,YAAY,OAAO,QAAQ,QAAQ;AACzE,MAAI,CAAC,WAAW,QACd,QAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACd,OAAO,WAAW;GACnB;EAGH,MAAM,cAAc,WAAW,MAAM,MAAM;AAC3C,MAAI,CAAC,YACH,QAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACd,OAAO,gBAAgB,MAAM,eAAe;GAC7C;EAEH,MAAM,aAAa,YAAY,YAAY;AAC3C,MAAI,CAAC,WACH,QAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACd,OAAO,yBAAyB,OAAO;GACxC;EAEH,MAAM,gBACJ,OAAO,eAAe,WAAW,aAAa,WAAW;EAC3D,MAAM,aAAa,WAAW,MAAM;AAEpC,MAAI,CAAC,WACH,QAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACd,OAAO,eAAe,cAAc;GACrC;EAIH,IAAIA,YAAyC;AAC7C,MAAI,WAAW,SAAS,MACtB,aAAY;WACH,WAAW,SAAS,cAAc,WAAW,SAAS,OAC/D,aAAY;AAGd,SAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB;GAChB,QAAQ;GACT;;CAGH,kBACE,YACA,aACS;AAGT,MAAI;GAEF,MAAM,QAAQ,WAAW,MACvB,6CACD;AACD,OAAI,OAAO;IACT,MAAM,GAAG,MAAM,UAAU,SAAS;AAClC,QAAI,CAAC,QAAQ,CAAC,YAAY,UAAU,OAClC,QAAO;IAET,MAAM,YAAY,YAAY;IAC9B,MAAM,eAAe,KAAK,MAAM,MAAM;AAEtC,YAAQ,UAAR;KACE,KAAK,IACH,QAAO,OAAO,UAAU,GAAG,OAAO,aAAa;KACjD,KAAK,IACH,QAAO,OAAO,UAAU,GAAG,OAAO,aAAa;KACjD,KAAK,KACH,QAAO,OAAO,UAAU,IAAI,OAAO,aAAa;KAClD,KAAK,KACH,QAAO,OAAO,UAAU,IAAI,OAAO,aAAa;KAClD,KAAK;KACL,KAAK,KACH,QAAO,cAAc;KACvB,KAAK;KACL,KAAK,KACH,QAAO,cAAc;;;AAK3B,OAAI,cAAc,YAChB,QAAO,QAAQ,YAAY,YAAY;AAGzC,UAAO;UACD;AACN,UAAO;;;;;;;AAQb,SAAgB,2BAAgD;AAC9D,QAAO,IAAI,yBAAyB;;;;;AAQtC,SAAgB,4BACd,UAMA,OAUwB;CACxB,MAAMC,UAA4C,EAAE;AAEpD,MAAK,MAAM,QAAQ,MACjB,SAAQ,KAAK,OAAO;EAClB,KAAK,KAAK;EACV,MAAM,KAAK;EACX,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,cAAc,KAAK;EACnB,cAAc,KAAK;EACnB,gBAAgB,KAAK;EACrB,qBAAqB,KAAK;EAC3B;CAKH,MAAM,iBADY,MAAM,MAAM,MAAM,EAAE,SAAS,QAAQ,EACrB,OAAO,MAAM,IAAI,OAAO;AAE1D,QAAO;EACL,KAAK,SAAS;EACd,MAAM,SAAS;EACf,SAAS,SAAS;EAClB;EACA,OAAO;EACR;;;;;AAMH,SAAgB,mBACd,YACA,cAAuC,EAAE,EACtB;AACnB,QAAO;EACL,gBAAgB,WAAW;EAC3B,QAAQ;EACR;EACA,SAAS,EAAE;EACZ"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/state-machine/index.ts"],"sourcesContent":["/**\n * Workflow State Machine Engine\n *\n * Provides state machine logic for workflow transitions.\n * This is a spec-level abstraction that defines the interface\n * for workflow state transitions.\n */\n\n// ============ Types ============\n\n/**\n * Represents a transition definition in a workflow step.\n */\nexport interface TransitionDefinition {\n /** The action that triggers this transition (e.g., \"approve\", \"reject\") */\n action: string;\n /** The target step key to transition to */\n targetStepKey: string;\n /** Optional condition expression for conditional transitions */\n condition?: string;\n /** Optional guard roles - only users with these roles can take this action */\n allowedRoles?: string[];\n}\n\n/**\n * Step definition for state machine.\n */\nexport interface StateMachineStep {\n key: string;\n name: string;\n type:\n | 'START'\n | 'APPROVAL'\n | 'TASK'\n | 'CONDITION'\n | 'PARALLEL'\n | 'WAIT'\n | 'ACTION'\n | 'END';\n /** Map of action -> transition definition */\n transitions: Record<string, string | TransitionDefinition>;\n /** For approval steps: how approvals are handled */\n approvalMode?: 'ANY' | 'ALL' | 'MAJORITY' | 'SEQUENTIAL';\n /** Roles that can approve/act on this step */\n allowedRoles?: string[];\n /** Timeout in seconds */\n timeoutSeconds?: number;\n /** Condition expression for CONDITION type */\n conditionExpression?: string;\n}\n\n/**\n * Workflow state machine definition.\n */\nexport interface StateMachineDefinition {\n key: string;\n name: string;\n version: string;\n initialStepKey: string;\n steps: Record<string, StateMachineStep>;\n}\n\n/**\n * Current state of an instance.\n */\nexport interface StateMachineState {\n currentStepKey: string;\n status:\n | 'PENDING'\n | 'RUNNING'\n | 'WAITING'\n | 'PAUSED'\n | 'COMPLETED'\n | 'CANCELLED'\n | 'FAILED'\n | 'TIMEOUT';\n contextData: Record<string, unknown>;\n history: {\n stepKey: string;\n action: string;\n timestamp: Date;\n executedBy: string;\n }[];\n}\n\n/**\n * Result of a transition attempt.\n */\nexport interface TransitionResult {\n success: boolean;\n previousStepKey: string;\n currentStepKey: string | null;\n status: StateMachineState['status'];\n error?: string;\n}\n\n/**\n * Context for transition validation.\n */\nexport interface TransitionContext {\n userId: string;\n userRoles: string[];\n data?: Record<string, unknown>;\n}\n\n// ============ State Machine Engine ============\n\n/**\n * State machine engine interface.\n * Implementation should be provided at runtime.\n */\nexport interface IStateMachineEngine {\n /**\n * Validate that a transition is allowed.\n */\n canTransition(\n definition: StateMachineDefinition,\n state: StateMachineState,\n action: string,\n context: TransitionContext\n ): { allowed: boolean; reason?: string };\n\n /**\n * Get available actions for the current step.\n */\n getAvailableActions(\n definition: StateMachineDefinition,\n state: StateMachineState,\n context: TransitionContext\n ): string[];\n\n /**\n * Execute a transition.\n */\n transition(\n definition: StateMachineDefinition,\n state: StateMachineState,\n action: string,\n context: TransitionContext\n ): TransitionResult;\n\n /**\n * Evaluate a condition expression.\n */\n evaluateCondition(\n expression: string,\n contextData: Record<string, unknown>\n ): boolean;\n}\n\n/**\n * Basic state machine engine implementation.\n * This is a reference implementation for spec validation.\n */\nexport class BasicStateMachineEngine implements IStateMachineEngine {\n canTransition(\n definition: StateMachineDefinition,\n state: StateMachineState,\n action: string,\n context: TransitionContext\n ): { allowed: boolean; reason?: string } {\n // Check if state allows transitions\n if (state.status !== 'RUNNING' && state.status !== 'WAITING') {\n return {\n allowed: false,\n reason: `Workflow is ${state.status}, cannot transition`,\n };\n }\n\n const currentStep = definition.steps[state.currentStepKey];\n if (!currentStep) {\n return {\n allowed: false,\n reason: `Step ${state.currentStepKey} not found`,\n };\n }\n\n // Check if action exists for this step\n const transition = currentStep.transitions[action];\n if (!transition) {\n return {\n allowed: false,\n reason: `Action ${action} not available in step ${state.currentStepKey}`,\n };\n }\n\n // Check role-based access\n if (currentStep.allowedRoles && currentStep.allowedRoles.length > 0) {\n const hasRole = currentStep.allowedRoles.some((role) =>\n context.userRoles.includes(role)\n );\n if (!hasRole) {\n return {\n allowed: false,\n reason: `User lacks required role for this action`,\n };\n }\n }\n\n // Check transition-specific roles if defined\n if (\n typeof transition === 'object' &&\n transition.allowedRoles &&\n transition.allowedRoles.length > 0\n ) {\n const hasRole = transition.allowedRoles.some((role) =>\n context.userRoles.includes(role)\n );\n if (!hasRole) {\n return {\n allowed: false,\n reason: `User lacks required role for action ${action}`,\n };\n }\n }\n\n return { allowed: true };\n }\n\n getAvailableActions(\n definition: StateMachineDefinition,\n state: StateMachineState,\n context: TransitionContext\n ): string[] {\n if (state.status !== 'RUNNING' && state.status !== 'WAITING') {\n return [];\n }\n\n const currentStep = definition.steps[state.currentStepKey];\n if (!currentStep) {\n return [];\n }\n\n return Object.keys(currentStep.transitions).filter((action) => {\n const result = this.canTransition(definition, state, action, context);\n return result.allowed;\n });\n }\n\n transition(\n definition: StateMachineDefinition,\n state: StateMachineState,\n action: string,\n context: TransitionContext\n ): TransitionResult {\n const validation = this.canTransition(definition, state, action, context);\n if (!validation.allowed) {\n return {\n success: false,\n previousStepKey: state.currentStepKey,\n currentStepKey: state.currentStepKey,\n status: state.status,\n error: validation.reason,\n };\n }\n\n const currentStep = definition.steps[state.currentStepKey];\n if (!currentStep) {\n return {\n success: false,\n previousStepKey: state.currentStepKey,\n currentStepKey: state.currentStepKey,\n status: state.status,\n error: `Current step ${state.currentStepKey} not found`,\n };\n }\n const transition = currentStep.transitions[action];\n if (!transition) {\n return {\n success: false,\n previousStepKey: state.currentStepKey,\n currentStepKey: state.currentStepKey,\n status: state.status,\n error: `Transition for action ${action} not found`,\n };\n }\n const targetStepKey =\n typeof transition === 'string' ? transition : transition.targetStepKey;\n const targetStep = definition.steps[targetStepKey];\n\n if (!targetStep) {\n return {\n success: false,\n previousStepKey: state.currentStepKey,\n currentStepKey: state.currentStepKey,\n status: state.status,\n error: `Target step ${targetStepKey} not found`,\n };\n }\n\n // Determine new status\n let newStatus: StateMachineState['status'] = 'RUNNING';\n if (targetStep.type === 'END') {\n newStatus = 'COMPLETED';\n } else if (targetStep.type === 'APPROVAL' || targetStep.type === 'WAIT') {\n newStatus = 'WAITING';\n }\n\n return {\n success: true,\n previousStepKey: state.currentStepKey,\n currentStepKey: targetStepKey,\n status: newStatus,\n };\n }\n\n evaluateCondition(\n expression: string,\n contextData: Record<string, unknown>\n ): boolean {\n // Basic condition evaluation\n // In production, this should use a proper expression language\n try {\n // Simple property checks like \"amount > 1000\"\n const match = expression.match(\n /^(\\w+)\\s*(>=|<=|>|<|===|!==|==|!=)\\s*(.+)$/\n );\n if (match) {\n const [, prop, operator, value] = match;\n if (!prop || !operator || value === undefined) {\n return false;\n }\n const propValue = contextData[prop];\n const compareValue = JSON.parse(value);\n\n switch (operator) {\n case '>':\n return Number(propValue) > Number(compareValue);\n case '<':\n return Number(propValue) < Number(compareValue);\n case '>=':\n return Number(propValue) >= Number(compareValue);\n case '<=':\n return Number(propValue) <= Number(compareValue);\n case '===':\n case '==':\n return propValue === compareValue;\n case '!==':\n case '!=':\n return propValue !== compareValue;\n }\n }\n\n // Check for boolean property\n if (expression in contextData) {\n return Boolean(contextData[expression]);\n }\n\n return false;\n } catch {\n return false;\n }\n }\n}\n\n/**\n * Create a new state machine engine instance.\n */\nexport function createStateMachineEngine(): IStateMachineEngine {\n return new BasicStateMachineEngine();\n}\n\n// ============ Utility Functions ============\n\n/**\n * Build a state machine definition from workflow entities.\n */\nexport function buildStateMachineDefinition(\n workflow: {\n key: string;\n name: string;\n version: string;\n initialStepId: string | null;\n },\n steps: {\n key: string;\n name: string;\n type: string;\n transitions: Record<string, string | TransitionDefinition>;\n approvalMode?: string;\n approverRoles?: string[];\n timeoutSeconds?: number;\n conditionExpression?: string;\n }[]\n): StateMachineDefinition {\n const stepMap: Record<string, StateMachineStep> = {};\n\n for (const step of steps) {\n stepMap[step.key] = {\n key: step.key,\n name: step.name,\n type: step.type as StateMachineStep['type'],\n transitions: step.transitions,\n approvalMode: step.approvalMode as StateMachineStep['approvalMode'],\n allowedRoles: step.approverRoles,\n timeoutSeconds: step.timeoutSeconds,\n conditionExpression: step.conditionExpression,\n };\n }\n\n // Find initial step\n const startStep = steps.find((s) => s.type === 'START');\n const initialStepKey = startStep?.key ?? steps[0]?.key ?? '';\n\n return {\n key: workflow.key,\n name: workflow.name,\n version: workflow.version,\n initialStepKey,\n steps: stepMap,\n };\n}\n\n/**\n * Create initial state for a new workflow instance.\n */\nexport function createInitialState(\n definition: StateMachineDefinition,\n contextData: Record<string, unknown> = {}\n): StateMachineState {\n return {\n currentStepKey: definition.initialStepKey,\n status: 'RUNNING',\n contextData,\n history: [],\n };\n}\n"],"mappings":";;;;;AA0JA,IAAa,0BAAb,MAAoE;CAClE,cACE,YACA,OACA,QACA,SACuC;AAEvC,MAAI,MAAM,WAAW,aAAa,MAAM,WAAW,UACjD,QAAO;GACL,SAAS;GACT,QAAQ,eAAe,MAAM,OAAO;GACrC;EAGH,MAAM,cAAc,WAAW,MAAM,MAAM;AAC3C,MAAI,CAAC,YACH,QAAO;GACL,SAAS;GACT,QAAQ,QAAQ,MAAM,eAAe;GACtC;EAIH,MAAM,aAAa,YAAY,YAAY;AAC3C,MAAI,CAAC,WACH,QAAO;GACL,SAAS;GACT,QAAQ,UAAU,OAAO,yBAAyB,MAAM;GACzD;AAIH,MAAI,YAAY,gBAAgB,YAAY,aAAa,SAAS,GAIhE;OAAI,CAHY,YAAY,aAAa,MAAM,SAC7C,QAAQ,UAAU,SAAS,KAAK,CACjC,CAEC,QAAO;IACL,SAAS;IACT,QAAQ;IACT;;AAKL,MACE,OAAO,eAAe,YACtB,WAAW,gBACX,WAAW,aAAa,SAAS,GAKjC;OAAI,CAHY,WAAW,aAAa,MAAM,SAC5C,QAAQ,UAAU,SAAS,KAAK,CACjC,CAEC,QAAO;IACL,SAAS;IACT,QAAQ,uCAAuC;IAChD;;AAIL,SAAO,EAAE,SAAS,MAAM;;CAG1B,oBACE,YACA,OACA,SACU;AACV,MAAI,MAAM,WAAW,aAAa,MAAM,WAAW,UACjD,QAAO,EAAE;EAGX,MAAM,cAAc,WAAW,MAAM,MAAM;AAC3C,MAAI,CAAC,YACH,QAAO,EAAE;AAGX,SAAO,OAAO,KAAK,YAAY,YAAY,CAAC,QAAQ,WAAW;AAE7D,UADe,KAAK,cAAc,YAAY,OAAO,QAAQ,QAAQ,CACvD;IACd;;CAGJ,WACE,YACA,OACA,QACA,SACkB;EAClB,MAAM,aAAa,KAAK,cAAc,YAAY,OAAO,QAAQ,QAAQ;AACzE,MAAI,CAAC,WAAW,QACd,QAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACd,OAAO,WAAW;GACnB;EAGH,MAAM,cAAc,WAAW,MAAM,MAAM;AAC3C,MAAI,CAAC,YACH,QAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACd,OAAO,gBAAgB,MAAM,eAAe;GAC7C;EAEH,MAAM,aAAa,YAAY,YAAY;AAC3C,MAAI,CAAC,WACH,QAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACd,OAAO,yBAAyB,OAAO;GACxC;EAEH,MAAM,gBACJ,OAAO,eAAe,WAAW,aAAa,WAAW;EAC3D,MAAM,aAAa,WAAW,MAAM;AAEpC,MAAI,CAAC,WACH,QAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACd,OAAO,eAAe,cAAc;GACrC;EAIH,IAAI,YAAyC;AAC7C,MAAI,WAAW,SAAS,MACtB,aAAY;WACH,WAAW,SAAS,cAAc,WAAW,SAAS,OAC/D,aAAY;AAGd,SAAO;GACL,SAAS;GACT,iBAAiB,MAAM;GACvB,gBAAgB;GAChB,QAAQ;GACT;;CAGH,kBACE,YACA,aACS;AAGT,MAAI;GAEF,MAAM,QAAQ,WAAW,MACvB,6CACD;AACD,OAAI,OAAO;IACT,MAAM,GAAG,MAAM,UAAU,SAAS;AAClC,QAAI,CAAC,QAAQ,CAAC,YAAY,UAAU,OAClC,QAAO;IAET,MAAM,YAAY,YAAY;IAC9B,MAAM,eAAe,KAAK,MAAM,MAAM;AAEtC,YAAQ,UAAR;KACE,KAAK,IACH,QAAO,OAAO,UAAU,GAAG,OAAO,aAAa;KACjD,KAAK,IACH,QAAO,OAAO,UAAU,GAAG,OAAO,aAAa;KACjD,KAAK,KACH,QAAO,OAAO,UAAU,IAAI,OAAO,aAAa;KAClD,KAAK,KACH,QAAO,OAAO,UAAU,IAAI,OAAO,aAAa;KAClD,KAAK;KACL,KAAK,KACH,QAAO,cAAc;KACvB,KAAK;KACL,KAAK,KACH,QAAO,cAAc;;;AAK3B,OAAI,cAAc,YAChB,QAAO,QAAQ,YAAY,YAAY;AAGzC,UAAO;UACD;AACN,UAAO;;;;;;;AAQb,SAAgB,2BAAgD;AAC9D,QAAO,IAAI,yBAAyB;;;;;AAQtC,SAAgB,4BACd,UAMA,OAUwB;CACxB,MAAM,UAA4C,EAAE;AAEpD,MAAK,MAAM,QAAQ,MACjB,SAAQ,KAAK,OAAO;EAClB,KAAK,KAAK;EACV,MAAM,KAAK;EACX,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,cAAc,KAAK;EACnB,cAAc,KAAK;EACnB,gBAAgB,KAAK;EACrB,qBAAqB,KAAK;EAC3B;CAKH,MAAM,iBADY,MAAM,MAAM,MAAM,EAAE,SAAS,QAAQ,EACrB,OAAO,MAAM,IAAI,OAAO;AAE1D,QAAO;EACL,KAAK,SAAS;EACd,MAAM,SAAS;EACf,SAAS,SAAS;EAClB;EACA,OAAO;EACR;;;;;AAMH,SAAgB,mBACd,YACA,cAAuC,EAAE,EACtB;AACnB,QAAO;EACL,gBAAgB,WAAW;EAC3B,QAAQ;EACR;EACA,SAAS,EAAE;EACZ"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as _contractspec_lib_contracts51 from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/tests/operations.test-spec.d.ts
|
|
4
|
+
declare const DefinitionListTest: _contractspec_lib_contracts51.TestSpec;
|
|
5
|
+
declare const InstanceListTest: _contractspec_lib_contracts51.TestSpec;
|
|
6
|
+
declare const ApprovalListMineTest: _contractspec_lib_contracts51.TestSpec;
|
|
7
|
+
declare const ApprovalDecideTest: _contractspec_lib_contracts51.TestSpec;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { ApprovalDecideTest, ApprovalListMineTest, DefinitionListTest, InstanceListTest };
|
|
10
|
+
//# sourceMappingURL=operations.test-spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.test-spec.d.ts","names":[],"sources":["../../src/tests/operations.test-spec.ts"],"sourcesContent":[],"mappings":";;;cAEa,oBAyBX,6BAAA,CAzB6B;cA2BlB,kBAyBX,6BAAA,CAzB2B;cA2BhB,sBAyBX,6BAAA,CAzB+B;AAtDpB,cAiFA,kBAxDX,EAiFA,6BAAA,CAzB6B,QAxD7B"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { defineTestSpec } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/tests/operations.test-spec.ts
|
|
4
|
+
const DefinitionListTest = defineTestSpec({
|
|
5
|
+
meta: {
|
|
6
|
+
key: "workflow.definition.list.test",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
stability: "experimental",
|
|
9
|
+
owners: ["@example.workflow-system"],
|
|
10
|
+
description: "Test for listing workflow definitions",
|
|
11
|
+
tags: ["test"]
|
|
12
|
+
},
|
|
13
|
+
target: {
|
|
14
|
+
type: "operation",
|
|
15
|
+
operation: {
|
|
16
|
+
key: "workflow.definition.list",
|
|
17
|
+
version: "1.0.0"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
scenarios: [{
|
|
21
|
+
key: "success",
|
|
22
|
+
when: { operation: { key: "workflow.definition.list" } },
|
|
23
|
+
then: [{
|
|
24
|
+
type: "expectOutput",
|
|
25
|
+
match: {}
|
|
26
|
+
}]
|
|
27
|
+
}, {
|
|
28
|
+
key: "error",
|
|
29
|
+
when: { operation: { key: "workflow.definition.list" } },
|
|
30
|
+
then: [{ type: "expectError" }]
|
|
31
|
+
}]
|
|
32
|
+
});
|
|
33
|
+
const InstanceListTest = defineTestSpec({
|
|
34
|
+
meta: {
|
|
35
|
+
key: "workflow.instance.list.test",
|
|
36
|
+
version: "1.0.0",
|
|
37
|
+
stability: "experimental",
|
|
38
|
+
owners: ["@example.workflow-system"],
|
|
39
|
+
description: "Test for listing workflow instances",
|
|
40
|
+
tags: ["test"]
|
|
41
|
+
},
|
|
42
|
+
target: {
|
|
43
|
+
type: "operation",
|
|
44
|
+
operation: {
|
|
45
|
+
key: "workflow.instance.list",
|
|
46
|
+
version: "1.0.0"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
scenarios: [{
|
|
50
|
+
key: "success",
|
|
51
|
+
when: { operation: { key: "workflow.instance.list" } },
|
|
52
|
+
then: [{
|
|
53
|
+
type: "expectOutput",
|
|
54
|
+
match: {}
|
|
55
|
+
}]
|
|
56
|
+
}, {
|
|
57
|
+
key: "error",
|
|
58
|
+
when: { operation: { key: "workflow.instance.list" } },
|
|
59
|
+
then: [{ type: "expectError" }]
|
|
60
|
+
}]
|
|
61
|
+
});
|
|
62
|
+
const ApprovalListMineTest = defineTestSpec({
|
|
63
|
+
meta: {
|
|
64
|
+
key: "workflow.approval.list.mine.test",
|
|
65
|
+
version: "1.0.0",
|
|
66
|
+
stability: "experimental",
|
|
67
|
+
owners: ["@example.workflow-system"],
|
|
68
|
+
description: "Test for listing my approvals",
|
|
69
|
+
tags: ["test"]
|
|
70
|
+
},
|
|
71
|
+
target: {
|
|
72
|
+
type: "operation",
|
|
73
|
+
operation: {
|
|
74
|
+
key: "workflow.approval.list.mine",
|
|
75
|
+
version: "1.0.0"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
scenarios: [{
|
|
79
|
+
key: "success",
|
|
80
|
+
when: { operation: { key: "workflow.approval.list.mine" } },
|
|
81
|
+
then: [{
|
|
82
|
+
type: "expectOutput",
|
|
83
|
+
match: {}
|
|
84
|
+
}]
|
|
85
|
+
}, {
|
|
86
|
+
key: "error",
|
|
87
|
+
when: { operation: { key: "workflow.approval.list.mine" } },
|
|
88
|
+
then: [{ type: "expectError" }]
|
|
89
|
+
}]
|
|
90
|
+
});
|
|
91
|
+
const ApprovalDecideTest = defineTestSpec({
|
|
92
|
+
meta: {
|
|
93
|
+
key: "workflow.approval.decide.test",
|
|
94
|
+
version: "1.0.0",
|
|
95
|
+
stability: "experimental",
|
|
96
|
+
owners: ["@example.workflow-system"],
|
|
97
|
+
description: "Test for deciding on approval",
|
|
98
|
+
tags: ["test"]
|
|
99
|
+
},
|
|
100
|
+
target: {
|
|
101
|
+
type: "operation",
|
|
102
|
+
operation: {
|
|
103
|
+
key: "workflow.approval.decide",
|
|
104
|
+
version: "1.0.0"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
scenarios: [{
|
|
108
|
+
key: "success",
|
|
109
|
+
when: { operation: { key: "workflow.approval.decide" } },
|
|
110
|
+
then: [{
|
|
111
|
+
type: "expectOutput",
|
|
112
|
+
match: {}
|
|
113
|
+
}]
|
|
114
|
+
}, {
|
|
115
|
+
key: "error",
|
|
116
|
+
when: { operation: { key: "workflow.approval.decide" } },
|
|
117
|
+
then: [{ type: "expectError" }]
|
|
118
|
+
}]
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
//#endregion
|
|
122
|
+
export { ApprovalDecideTest, ApprovalListMineTest, DefinitionListTest, InstanceListTest };
|
|
123
|
+
//# sourceMappingURL=operations.test-spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.test-spec.js","names":[],"sources":["../../src/tests/operations.test-spec.ts"],"sourcesContent":["import { defineTestSpec } from '@contractspec/lib.contracts';\n\nexport const DefinitionListTest = defineTestSpec({\n meta: {\n key: 'workflow.definition.list.test',\n version: '1.0.0',\n stability: 'experimental',\n owners: ['@example.workflow-system'],\n description: 'Test for listing workflow definitions',\n tags: ['test'],\n },\n target: {\n type: 'operation',\n operation: { key: 'workflow.definition.list', version: '1.0.0' },\n },\n scenarios: [\n {\n key: 'success',\n when: { operation: { key: 'workflow.definition.list' } },\n then: [{ type: 'expectOutput', match: {} }],\n },\n {\n key: 'error',\n when: { operation: { key: 'workflow.definition.list' } },\n then: [{ type: 'expectError' }],\n },\n ],\n});\n\nexport const InstanceListTest = defineTestSpec({\n meta: {\n key: 'workflow.instance.list.test',\n version: '1.0.0',\n stability: 'experimental',\n owners: ['@example.workflow-system'],\n description: 'Test for listing workflow instances',\n tags: ['test'],\n },\n target: {\n type: 'operation',\n operation: { key: 'workflow.instance.list', version: '1.0.0' },\n },\n scenarios: [\n {\n key: 'success',\n when: { operation: { key: 'workflow.instance.list' } },\n then: [{ type: 'expectOutput', match: {} }],\n },\n {\n key: 'error',\n when: { operation: { key: 'workflow.instance.list' } },\n then: [{ type: 'expectError' }],\n },\n ],\n});\n\nexport const ApprovalListMineTest = defineTestSpec({\n meta: {\n key: 'workflow.approval.list.mine.test',\n version: '1.0.0',\n stability: 'experimental',\n owners: ['@example.workflow-system'],\n description: 'Test for listing my approvals',\n tags: ['test'],\n },\n target: {\n type: 'operation',\n operation: { key: 'workflow.approval.list.mine', version: '1.0.0' },\n },\n scenarios: [\n {\n key: 'success',\n when: { operation: { key: 'workflow.approval.list.mine' } },\n then: [{ type: 'expectOutput', match: {} }],\n },\n {\n key: 'error',\n when: { operation: { key: 'workflow.approval.list.mine' } },\n then: [{ type: 'expectError' }],\n },\n ],\n});\n\nexport const ApprovalDecideTest = defineTestSpec({\n meta: {\n key: 'workflow.approval.decide.test',\n version: '1.0.0',\n stability: 'experimental',\n owners: ['@example.workflow-system'],\n description: 'Test for deciding on approval',\n tags: ['test'],\n },\n target: {\n type: 'operation',\n operation: { key: 'workflow.approval.decide', version: '1.0.0' },\n },\n scenarios: [\n {\n key: 'success',\n when: { operation: { key: 'workflow.approval.decide' } },\n then: [{ type: 'expectOutput', match: {} }],\n },\n {\n key: 'error',\n when: { operation: { key: 'workflow.approval.decide' } },\n then: [{ type: 'expectError' }],\n },\n ],\n});\n"],"mappings":";;;AAEA,MAAa,qBAAqB,eAAe;CAC/C,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,2BAA2B;EACpC,aAAa;EACb,MAAM,CAAC,OAAO;EACf;CACD,QAAQ;EACN,MAAM;EACN,WAAW;GAAE,KAAK;GAA4B,SAAS;GAAS;EACjE;CACD,WAAW,CACT;EACE,KAAK;EACL,MAAM,EAAE,WAAW,EAAE,KAAK,4BAA4B,EAAE;EACxD,MAAM,CAAC;GAAE,MAAM;GAAgB,OAAO,EAAE;GAAE,CAAC;EAC5C,EACD;EACE,KAAK;EACL,MAAM,EAAE,WAAW,EAAE,KAAK,4BAA4B,EAAE;EACxD,MAAM,CAAC,EAAE,MAAM,eAAe,CAAC;EAChC,CACF;CACF,CAAC;AAEF,MAAa,mBAAmB,eAAe;CAC7C,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,2BAA2B;EACpC,aAAa;EACb,MAAM,CAAC,OAAO;EACf;CACD,QAAQ;EACN,MAAM;EACN,WAAW;GAAE,KAAK;GAA0B,SAAS;GAAS;EAC/D;CACD,WAAW,CACT;EACE,KAAK;EACL,MAAM,EAAE,WAAW,EAAE,KAAK,0BAA0B,EAAE;EACtD,MAAM,CAAC;GAAE,MAAM;GAAgB,OAAO,EAAE;GAAE,CAAC;EAC5C,EACD;EACE,KAAK;EACL,MAAM,EAAE,WAAW,EAAE,KAAK,0BAA0B,EAAE;EACtD,MAAM,CAAC,EAAE,MAAM,eAAe,CAAC;EAChC,CACF;CACF,CAAC;AAEF,MAAa,uBAAuB,eAAe;CACjD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,2BAA2B;EACpC,aAAa;EACb,MAAM,CAAC,OAAO;EACf;CACD,QAAQ;EACN,MAAM;EACN,WAAW;GAAE,KAAK;GAA+B,SAAS;GAAS;EACpE;CACD,WAAW,CACT;EACE,KAAK;EACL,MAAM,EAAE,WAAW,EAAE,KAAK,+BAA+B,EAAE;EAC3D,MAAM,CAAC;GAAE,MAAM;GAAgB,OAAO,EAAE;GAAE,CAAC;EAC5C,EACD;EACE,KAAK;EACL,MAAM,EAAE,WAAW,EAAE,KAAK,+BAA+B,EAAE;EAC3D,MAAM,CAAC,EAAE,MAAM,eAAe,CAAC;EAChC,CACF;CACF,CAAC;AAEF,MAAa,qBAAqB,eAAe;CAC/C,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,2BAA2B;EACpC,aAAa;EACb,MAAM,CAAC,OAAO;EACf;CACD,QAAQ;EACN,MAAM;EACN,WAAW;GAAE,KAAK;GAA4B,SAAS;GAAS;EACjE;CACD,WAAW,CACT;EACE,KAAK;EACL,MAAM,EAAE,WAAW,EAAE,KAAK,4BAA4B,EAAE;EACxD,MAAM,CAAC;GAAE,MAAM;GAAgB,OAAO,EAAE;GAAE,CAAC;EAC5C,EACD;EACE,KAAK;EACL,MAAM,EAAE,WAAW,EAAE,KAAK,4BAA4B,EAAE;EACxD,MAAM,CAAC,EAAE,MAAM,eAAe,CAAC;EAChC,CACF;CACF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/WorkflowDashboard.d.ts
|
|
4
|
+
declare function WorkflowDashboard(): react_jsx_runtime0.JSX.Element;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { WorkflowDashboard };
|
|
7
|
+
//# sourceMappingURL=WorkflowDashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkflowDashboard.d.ts","names":[],"sources":["../../src/ui/WorkflowDashboard.tsx"],"sourcesContent":[],"mappings":";;;iBAmCgB,iBAAA,CAAA,GAAiB,kBAAA,CAAA,GAAA,CAAA"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useWorkflowList } from "./hooks/useWorkflowList.js";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { Button, ErrorState, LoaderBlock, StatCard, StatCardGroup } from "@contractspec/lib.design-system";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
|
|
8
|
+
//#region src/ui/WorkflowDashboard.tsx
|
|
9
|
+
/**
|
|
10
|
+
* Workflow Dashboard
|
|
11
|
+
*
|
|
12
|
+
* Interactive dashboard for the workflow-system template.
|
|
13
|
+
* Displays workflow definitions and instances with stats.
|
|
14
|
+
*/
|
|
15
|
+
const STATUS_COLORS = {
|
|
16
|
+
ACTIVE: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
|
|
17
|
+
DRAFT: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400",
|
|
18
|
+
ARCHIVED: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400",
|
|
19
|
+
PENDING: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
|
|
20
|
+
IN_PROGRESS: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400",
|
|
21
|
+
COMPLETED: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
|
|
22
|
+
REJECTED: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400",
|
|
23
|
+
CANCELLED: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400"
|
|
24
|
+
};
|
|
25
|
+
function WorkflowDashboard() {
|
|
26
|
+
const [activeTab, setActiveTab] = useState("definitions");
|
|
27
|
+
const { definitions, instances, loading, error, stats, refetch } = useWorkflowList();
|
|
28
|
+
const tabs = [{
|
|
29
|
+
id: "definitions",
|
|
30
|
+
label: "Definitions",
|
|
31
|
+
icon: "📋"
|
|
32
|
+
}, {
|
|
33
|
+
id: "instances",
|
|
34
|
+
label: "Instances",
|
|
35
|
+
icon: "🔄"
|
|
36
|
+
}];
|
|
37
|
+
if (loading) return /* @__PURE__ */ jsx(LoaderBlock, { label: "Loading Workflows..." });
|
|
38
|
+
if (error) return /* @__PURE__ */ jsx(ErrorState, {
|
|
39
|
+
title: "Failed to load Workflows",
|
|
40
|
+
description: error.message,
|
|
41
|
+
onRetry: refetch,
|
|
42
|
+
retryLabel: "Retry"
|
|
43
|
+
});
|
|
44
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
45
|
+
className: "space-y-6",
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ jsxs("div", {
|
|
48
|
+
className: "flex items-center justify-between",
|
|
49
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
50
|
+
className: "text-2xl font-bold",
|
|
51
|
+
children: "Workflow System"
|
|
52
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
53
|
+
onClick: () => alert("Create workflow modal"),
|
|
54
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
55
|
+
className: "mr-2",
|
|
56
|
+
children: "+"
|
|
57
|
+
}), " New Workflow"]
|
|
58
|
+
})]
|
|
59
|
+
}),
|
|
60
|
+
/* @__PURE__ */ jsxs(StatCardGroup, { children: [
|
|
61
|
+
/* @__PURE__ */ jsx(StatCard, {
|
|
62
|
+
label: "Workflows",
|
|
63
|
+
value: stats.totalDefinitions,
|
|
64
|
+
hint: `${stats.activeDefinitions} active`
|
|
65
|
+
}),
|
|
66
|
+
/* @__PURE__ */ jsx(StatCard, {
|
|
67
|
+
label: "Instances",
|
|
68
|
+
value: stats.totalInstances,
|
|
69
|
+
hint: "total runs"
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ jsx(StatCard, {
|
|
72
|
+
label: "Pending",
|
|
73
|
+
value: stats.pendingInstances,
|
|
74
|
+
hint: "awaiting action"
|
|
75
|
+
}),
|
|
76
|
+
/* @__PURE__ */ jsx(StatCard, {
|
|
77
|
+
label: "Completed",
|
|
78
|
+
value: stats.completedInstances,
|
|
79
|
+
hint: "finished"
|
|
80
|
+
})
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ jsx("nav", {
|
|
83
|
+
className: "bg-muted flex gap-1 rounded-lg p-1",
|
|
84
|
+
role: "tablist",
|
|
85
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxs(Button, {
|
|
86
|
+
type: "button",
|
|
87
|
+
role: "tab",
|
|
88
|
+
"aria-selected": activeTab === tab.id,
|
|
89
|
+
onClick: () => setActiveTab(tab.id),
|
|
90
|
+
className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
|
|
91
|
+
children: [/* @__PURE__ */ jsx("span", { children: tab.icon }), tab.label]
|
|
92
|
+
}, tab.id))
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ jsxs("div", {
|
|
95
|
+
className: "min-h-[400px]",
|
|
96
|
+
role: "tabpanel",
|
|
97
|
+
children: [activeTab === "definitions" && /* @__PURE__ */ jsx("div", {
|
|
98
|
+
className: "border-border rounded-lg border",
|
|
99
|
+
children: /* @__PURE__ */ jsxs("table", {
|
|
100
|
+
className: "w-full",
|
|
101
|
+
children: [/* @__PURE__ */ jsx("thead", {
|
|
102
|
+
className: "border-border bg-muted/30 border-b",
|
|
103
|
+
children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
104
|
+
/* @__PURE__ */ jsx("th", {
|
|
105
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
106
|
+
children: "Name"
|
|
107
|
+
}),
|
|
108
|
+
/* @__PURE__ */ jsx("th", {
|
|
109
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
110
|
+
children: "Type"
|
|
111
|
+
}),
|
|
112
|
+
/* @__PURE__ */ jsx("th", {
|
|
113
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
114
|
+
children: "Status"
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ jsx("th", {
|
|
117
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
118
|
+
children: "Created"
|
|
119
|
+
})
|
|
120
|
+
] })
|
|
121
|
+
}), /* @__PURE__ */ jsxs("tbody", {
|
|
122
|
+
className: "divide-border divide-y",
|
|
123
|
+
children: [definitions.map((def) => /* @__PURE__ */ jsxs("tr", {
|
|
124
|
+
className: "hover:bg-muted/50",
|
|
125
|
+
children: [
|
|
126
|
+
/* @__PURE__ */ jsxs("td", {
|
|
127
|
+
className: "px-4 py-3",
|
|
128
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
129
|
+
className: "font-medium",
|
|
130
|
+
children: def.name
|
|
131
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
132
|
+
className: "text-muted-foreground text-sm",
|
|
133
|
+
children: def.description
|
|
134
|
+
})]
|
|
135
|
+
}),
|
|
136
|
+
/* @__PURE__ */ jsx("td", {
|
|
137
|
+
className: "px-4 py-3 font-mono text-sm",
|
|
138
|
+
children: def.type
|
|
139
|
+
}),
|
|
140
|
+
/* @__PURE__ */ jsx("td", {
|
|
141
|
+
className: "px-4 py-3",
|
|
142
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
143
|
+
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[def.status] ?? ""}`,
|
|
144
|
+
children: def.status
|
|
145
|
+
})
|
|
146
|
+
}),
|
|
147
|
+
/* @__PURE__ */ jsx("td", {
|
|
148
|
+
className: "text-muted-foreground px-4 py-3 text-sm",
|
|
149
|
+
children: def.createdAt.toLocaleDateString()
|
|
150
|
+
})
|
|
151
|
+
]
|
|
152
|
+
}, def.id)), definitions.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", {
|
|
153
|
+
colSpan: 4,
|
|
154
|
+
className: "text-muted-foreground px-4 py-8 text-center",
|
|
155
|
+
children: "No workflow definitions found"
|
|
156
|
+
}) })]
|
|
157
|
+
})]
|
|
158
|
+
})
|
|
159
|
+
}), activeTab === "instances" && /* @__PURE__ */ jsx("div", {
|
|
160
|
+
className: "border-border rounded-lg border",
|
|
161
|
+
children: /* @__PURE__ */ jsxs("table", {
|
|
162
|
+
className: "w-full",
|
|
163
|
+
children: [/* @__PURE__ */ jsx("thead", {
|
|
164
|
+
className: "border-border bg-muted/30 border-b",
|
|
165
|
+
children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
166
|
+
/* @__PURE__ */ jsx("th", {
|
|
167
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
168
|
+
children: "Instance ID"
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ jsx("th", {
|
|
171
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
172
|
+
children: "Status"
|
|
173
|
+
}),
|
|
174
|
+
/* @__PURE__ */ jsx("th", {
|
|
175
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
176
|
+
children: "Requested By"
|
|
177
|
+
}),
|
|
178
|
+
/* @__PURE__ */ jsx("th", {
|
|
179
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
180
|
+
children: "Started"
|
|
181
|
+
})
|
|
182
|
+
] })
|
|
183
|
+
}), /* @__PURE__ */ jsxs("tbody", {
|
|
184
|
+
className: "divide-border divide-y",
|
|
185
|
+
children: [instances.map((inst) => /* @__PURE__ */ jsxs("tr", {
|
|
186
|
+
className: "hover:bg-muted/50",
|
|
187
|
+
children: [
|
|
188
|
+
/* @__PURE__ */ jsx("td", {
|
|
189
|
+
className: "px-4 py-3 font-mono text-sm",
|
|
190
|
+
children: inst.id
|
|
191
|
+
}),
|
|
192
|
+
/* @__PURE__ */ jsx("td", {
|
|
193
|
+
className: "px-4 py-3",
|
|
194
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
195
|
+
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[inst.status] ?? ""}`,
|
|
196
|
+
children: inst.status
|
|
197
|
+
})
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ jsx("td", {
|
|
200
|
+
className: "px-4 py-3 text-sm",
|
|
201
|
+
children: inst.requestedBy
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ jsx("td", {
|
|
204
|
+
className: "text-muted-foreground px-4 py-3 text-sm",
|
|
205
|
+
children: inst.startedAt.toLocaleDateString()
|
|
206
|
+
})
|
|
207
|
+
]
|
|
208
|
+
}, inst.id)), instances.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", {
|
|
209
|
+
colSpan: 4,
|
|
210
|
+
className: "text-muted-foreground px-4 py-8 text-center",
|
|
211
|
+
children: "No workflow instances found"
|
|
212
|
+
}) })]
|
|
213
|
+
})]
|
|
214
|
+
})
|
|
215
|
+
})]
|
|
216
|
+
})
|
|
217
|
+
]
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
//#endregion
|
|
222
|
+
export { WorkflowDashboard };
|
|
223
|
+
//# sourceMappingURL=WorkflowDashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkflowDashboard.js","names":[],"sources":["../../src/ui/WorkflowDashboard.tsx"],"sourcesContent":["'use client';\n\n/**\n * Workflow Dashboard\n *\n * Interactive dashboard for the workflow-system template.\n * Displays workflow definitions and instances with stats.\n */\nimport { useState } from 'react';\nimport {\n Button,\n ErrorState,\n LoaderBlock,\n StatCard,\n StatCardGroup,\n} from '@contractspec/lib.design-system';\nimport { useWorkflowList } from './hooks/useWorkflowList';\n\ntype Tab = 'definitions' | 'instances';\n\nconst STATUS_COLORS: Record<string, string> = {\n ACTIVE:\n 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',\n DRAFT: 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400',\n ARCHIVED:\n 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400',\n PENDING: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',\n IN_PROGRESS:\n 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400',\n COMPLETED:\n 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',\n REJECTED: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',\n CANCELLED: 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400',\n};\n\nexport function WorkflowDashboard() {\n const [activeTab, setActiveTab] = useState<Tab>('definitions');\n const { definitions, instances, loading, error, stats, refetch } =\n useWorkflowList();\n\n const tabs: { id: Tab; label: string; icon: string }[] = [\n { id: 'definitions', label: 'Definitions', icon: '📋' },\n { id: 'instances', label: 'Instances', icon: '🔄' },\n ];\n\n if (loading) {\n return <LoaderBlock label=\"Loading Workflows...\" />;\n }\n\n if (error) {\n return (\n <ErrorState\n title=\"Failed to load Workflows\"\n description={error.message}\n onRetry={refetch}\n retryLabel=\"Retry\"\n />\n );\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-2xl font-bold\">Workflow System</h2>\n <Button onClick={() => alert('Create workflow modal')}>\n <span className=\"mr-2\">+</span> New Workflow\n </Button>\n </div>\n\n {/* Stats Row */}\n <StatCardGroup>\n <StatCard\n label=\"Workflows\"\n value={stats.totalDefinitions}\n hint={`${stats.activeDefinitions} active`}\n />\n <StatCard\n label=\"Instances\"\n value={stats.totalInstances}\n hint=\"total runs\"\n />\n <StatCard\n label=\"Pending\"\n value={stats.pendingInstances}\n hint=\"awaiting action\"\n />\n <StatCard\n label=\"Completed\"\n value={stats.completedInstances}\n hint=\"finished\"\n />\n </StatCardGroup>\n\n {/* Navigation Tabs */}\n <nav className=\"bg-muted flex gap-1 rounded-lg p-1\" role=\"tablist\">\n {tabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === tab.id}\n onClick={() => setActiveTab(tab.id)}\n className={`flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n <span>{tab.icon}</span>\n {tab.label}\n </Button>\n ))}\n </nav>\n\n {/* Tab Content */}\n <div className=\"min-h-[400px]\" role=\"tabpanel\">\n {activeTab === 'definitions' && (\n <div className=\"border-border rounded-lg border\">\n <table className=\"w-full\">\n <thead className=\"border-border bg-muted/30 border-b\">\n <tr>\n <th className=\"px-4 py-3 text-left text-sm font-medium\">\n Name\n </th>\n <th className=\"px-4 py-3 text-left text-sm font-medium\">\n Type\n </th>\n <th className=\"px-4 py-3 text-left text-sm font-medium\">\n Status\n </th>\n <th className=\"px-4 py-3 text-left text-sm font-medium\">\n Created\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-border divide-y\">\n {definitions.map((def) => (\n <tr key={def.id} className=\"hover:bg-muted/50\">\n <td className=\"px-4 py-3\">\n <div className=\"font-medium\">{def.name}</div>\n <div className=\"text-muted-foreground text-sm\">\n {def.description}\n </div>\n </td>\n <td className=\"px-4 py-3 font-mono text-sm\">{def.type}</td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[def.status] ?? ''}`}\n >\n {def.status}\n </span>\n </td>\n <td className=\"text-muted-foreground px-4 py-3 text-sm\">\n {def.createdAt.toLocaleDateString()}\n </td>\n </tr>\n ))}\n {definitions.length === 0 && (\n <tr>\n <td\n colSpan={4}\n className=\"text-muted-foreground px-4 py-8 text-center\"\n >\n No workflow definitions found\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n )}\n\n {activeTab === 'instances' && (\n <div className=\"border-border rounded-lg border\">\n <table className=\"w-full\">\n <thead className=\"border-border bg-muted/30 border-b\">\n <tr>\n <th className=\"px-4 py-3 text-left text-sm font-medium\">\n Instance ID\n </th>\n <th className=\"px-4 py-3 text-left text-sm font-medium\">\n Status\n </th>\n <th className=\"px-4 py-3 text-left text-sm font-medium\">\n Requested By\n </th>\n <th className=\"px-4 py-3 text-left text-sm font-medium\">\n Started\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-border divide-y\">\n {instances.map((inst) => (\n <tr key={inst.id} className=\"hover:bg-muted/50\">\n <td className=\"px-4 py-3 font-mono text-sm\">{inst.id}</td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[inst.status] ?? ''}`}\n >\n {inst.status}\n </span>\n </td>\n <td className=\"px-4 py-3 text-sm\">{inst.requestedBy}</td>\n <td className=\"text-muted-foreground px-4 py-3 text-sm\">\n {inst.startedAt.toLocaleDateString()}\n </td>\n </tr>\n ))}\n {instances.length === 0 && (\n <tr>\n <td\n colSpan={4}\n className=\"text-muted-foreground px-4 py-8 text-center\"\n >\n No workflow instances found\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { WorkflowDefinition, WorkflowInstance } from "../../handlers/workflow.handlers.js";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/hooks/useWorkflowList.d.ts
|
|
4
|
+
interface WorkflowStats {
|
|
5
|
+
totalDefinitions: number;
|
|
6
|
+
activeDefinitions: number;
|
|
7
|
+
totalInstances: number;
|
|
8
|
+
pendingInstances: number;
|
|
9
|
+
completedInstances: number;
|
|
10
|
+
rejectedInstances: number;
|
|
11
|
+
}
|
|
12
|
+
declare function useWorkflowList(projectId?: string): {
|
|
13
|
+
definitions: WorkflowDefinition[];
|
|
14
|
+
instances: WorkflowInstance[];
|
|
15
|
+
loading: boolean;
|
|
16
|
+
error: Error | null;
|
|
17
|
+
stats: WorkflowStats;
|
|
18
|
+
refetch: () => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { WorkflowStats, useWorkflowList };
|
|
22
|
+
//# sourceMappingURL=useWorkflowList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWorkflowList.d.ts","names":[],"sources":["../../../src/ui/hooks/useWorkflowList.ts"],"sourcesContent":[],"mappings":";;;UAUiB,aAAA;;EAAA,iBAAa,EAAA,MAAA;EASd,cAAA,EAAA,MAAe;;;;;iBAAf,eAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
5
|
+
|
|
6
|
+
//#region src/ui/hooks/useWorkflowList.ts
|
|
7
|
+
function useWorkflowList(projectId = "local-project") {
|
|
8
|
+
const { handlers } = useTemplateRuntime();
|
|
9
|
+
const workflow = handlers.workflow;
|
|
10
|
+
const [definitions, setDefinitions] = useState([]);
|
|
11
|
+
const [instances, setInstances] = useState([]);
|
|
12
|
+
const [loading, setLoading] = useState(true);
|
|
13
|
+
const [error, setError] = useState(null);
|
|
14
|
+
const fetchData = useCallback(async () => {
|
|
15
|
+
try {
|
|
16
|
+
setLoading(true);
|
|
17
|
+
setError(null);
|
|
18
|
+
const [defResult, instResult] = await Promise.all([workflow.listDefinitions({
|
|
19
|
+
projectId,
|
|
20
|
+
limit: 100
|
|
21
|
+
}), workflow.listInstances({
|
|
22
|
+
projectId,
|
|
23
|
+
limit: 100
|
|
24
|
+
})]);
|
|
25
|
+
setDefinitions(defResult.definitions);
|
|
26
|
+
setInstances(instResult.instances);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
setError(err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to load workflows"));
|
|
29
|
+
} finally {
|
|
30
|
+
setLoading(false);
|
|
31
|
+
}
|
|
32
|
+
}, [handlers, projectId]);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
fetchData();
|
|
35
|
+
}, [fetchData]);
|
|
36
|
+
return {
|
|
37
|
+
definitions,
|
|
38
|
+
instances,
|
|
39
|
+
loading,
|
|
40
|
+
error,
|
|
41
|
+
stats: {
|
|
42
|
+
totalDefinitions: definitions.length,
|
|
43
|
+
activeDefinitions: definitions.filter((d) => d.status === "ACTIVE").length,
|
|
44
|
+
totalInstances: instances.length,
|
|
45
|
+
pendingInstances: instances.filter((i) => i.status === "PENDING").length,
|
|
46
|
+
completedInstances: instances.filter((i) => i.status === "COMPLETED").length,
|
|
47
|
+
rejectedInstances: instances.filter((i) => i.status === "REJECTED").length
|
|
48
|
+
},
|
|
49
|
+
refetch: fetchData
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
export { useWorkflowList };
|
|
55
|
+
//# sourceMappingURL=useWorkflowList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWorkflowList.js","names":[],"sources":["../../../src/ui/hooks/useWorkflowList.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\nimport type {\n WorkflowDefinition,\n WorkflowInstance,\n WorkflowHandlers,\n} from '../../handlers/workflow.handlers';\nimport { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';\n\nexport interface WorkflowStats {\n totalDefinitions: number;\n activeDefinitions: number;\n totalInstances: number;\n pendingInstances: number;\n completedInstances: number;\n rejectedInstances: number;\n}\n\nexport function useWorkflowList(projectId = 'local-project') {\n const { handlers } = useTemplateRuntime<{ workflow: WorkflowHandlers }>();\n const workflow = handlers.workflow;\n const [definitions, setDefinitions] = useState<WorkflowDefinition[]>([]);\n const [instances, setInstances] = useState<WorkflowInstance[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchData = useCallback(async () => {\n try {\n setLoading(true);\n setError(null);\n\n const [defResult, instResult] = await Promise.all([\n workflow.listDefinitions({ projectId, limit: 100 }),\n workflow.listInstances({ projectId, limit: 100 }),\n ]);\n\n setDefinitions(defResult.definitions);\n setInstances(instResult.instances);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Failed to load workflows')\n );\n } finally {\n setLoading(false);\n }\n }, [handlers, projectId]);\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n const stats: WorkflowStats = {\n totalDefinitions: definitions.length,\n activeDefinitions: definitions.filter((d) => d.status === 'ACTIVE').length,\n totalInstances: instances.length,\n pendingInstances: instances.filter((i) => i.status === 'PENDING').length,\n completedInstances: instances.filter((i) => i.status === 'COMPLETED')\n .length,\n rejectedInstances: instances.filter((i) => i.status === 'REJECTED').length,\n };\n\n return {\n definitions,\n instances,\n loading,\n error,\n stats,\n refetch: fetchData,\n };\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,gBAAgB,YAAY,iBAAiB;CAC3D,MAAM,EAAE,aAAa,oBAAoD;CACzE,MAAM,WAAW,SAAS;CAC1B,MAAM,CAAC,aAAa,kBAAkB,SAA+B,EAAE,CAAC;CACxE,MAAM,CAAC,WAAW,gBAAgB,SAA6B,EAAE,CAAC;CAClE,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CAEtD,MAAM,YAAY,YAAY,YAAY;AACxC,MAAI;AACF,cAAW,KAAK;AAChB,YAAS,KAAK;GAEd,MAAM,CAAC,WAAW,cAAc,MAAM,QAAQ,IAAI,CAChD,SAAS,gBAAgB;IAAE;IAAW,OAAO;IAAK,CAAC,EACnD,SAAS,cAAc;IAAE;IAAW,OAAO;IAAK,CAAC,CAClD,CAAC;AAEF,kBAAe,UAAU,YAAY;AACrC,gBAAa,WAAW,UAAU;WAC3B,KAAK;AACZ,YACE,eAAe,QAAQ,sBAAM,IAAI,MAAM,2BAA2B,CACnE;YACO;AACR,cAAW,MAAM;;IAElB,CAAC,UAAU,UAAU,CAAC;AAEzB,iBAAgB;AACd,aAAW;IACV,CAAC,UAAU,CAAC;AAYf,QAAO;EACL;EACA;EACA;EACA;EACA,OAf2B;GAC3B,kBAAkB,YAAY;GAC9B,mBAAmB,YAAY,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC;GACpE,gBAAgB,UAAU;GAC1B,kBAAkB,UAAU,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;GAClE,oBAAoB,UAAU,QAAQ,MAAM,EAAE,WAAW,YAAY,CAClE;GACH,mBAAmB,UAAU,QAAQ,MAAM,EAAE,WAAW,WAAW,CAAC;GACrE;EAQC,SAAS;EACV"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer } from "./renderers/workflow.markdown.js";
|
|
2
|
+
import "./renderers/index.js";
|
|
3
|
+
import { WorkflowDashboard } from "./WorkflowDashboard.js";
|
|
4
|
+
import { WorkflowStats, useWorkflowList } from "./hooks/useWorkflowList.js";
|
|
5
|
+
import "./hooks/index.js";
|
|
6
|
+
export { WorkflowDashboard, WorkflowStats, useWorkflowList, workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer };
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer } from "./renderers/workflow.markdown.js";
|
|
2
|
+
import { useWorkflowList } from "./hooks/useWorkflowList.js";
|
|
3
|
+
import { WorkflowDashboard } from "./WorkflowDashboard.js";
|
|
4
|
+
import "./hooks/index.js";
|
|
5
|
+
|
|
6
|
+
export { WorkflowDashboard, useWorkflowList, workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer } from "./workflow.markdown.js";
|
|
2
|
+
export { workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer } from "./workflow.markdown.js";
|
|
2
|
+
|
|
3
|
+
export { workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PresentationRenderer } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/renderers/workflow.markdown.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Markdown renderer for Workflow Dashboard
|
|
7
|
+
*/
|
|
8
|
+
declare const workflowDashboardMarkdownRenderer: PresentationRenderer<{
|
|
9
|
+
mimeType: string;
|
|
10
|
+
body: string;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Markdown renderer for Workflow Definition List
|
|
14
|
+
*/
|
|
15
|
+
declare const workflowDefinitionListMarkdownRenderer: PresentationRenderer<{
|
|
16
|
+
mimeType: string;
|
|
17
|
+
body: string;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Markdown renderer for Workflow Instance Detail
|
|
21
|
+
*/
|
|
22
|
+
declare const workflowInstanceDetailMarkdownRenderer: PresentationRenderer<{
|
|
23
|
+
mimeType: string;
|
|
24
|
+
body: string;
|
|
25
|
+
}>;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer };
|
|
28
|
+
//# sourceMappingURL=workflow.markdown.d.ts.map
|