improvement(monorepo): added tsconfig package, resolved type errors in testing package (#2613)

This commit is contained in:
Waleed
2025-12-28 00:36:48 -08:00
committed by GitHub
parent 7c0a3c15ac
commit 71130c8b0a
42 changed files with 706 additions and 914 deletions

View File

@@ -1,249 +1,18 @@
# Sim SDKs
# Packages
This directory contains the official SDKs for [Sim](https://sim.ai), allowing developers to execute workflows programmatically from their applications.
## Internal
## Available SDKs
| Package | Description |
|---------|-------------|
| [@sim/tsconfig](./tsconfig) | Shared TypeScript configs (base, nextjs, library, library-build) |
| [@sim/db](./db) | Database schema and Drizzle ORM utilities |
| [@sim/logger](./logger) | Structured logging with colored output |
| [@sim/testing](./testing) | Test factories, builders, and assertions |
### Package Installation Commands
## Published
- **TypeScript/JavaScript**: `npm install simstudio-ts-sdk`
- **Python**: `pip install simstudio-sdk`
### 🟢 TypeScript/JavaScript SDK (`simstudio-ts-sdk`)
**Directory:** `ts-sdk/`
The TypeScript SDK provides type-safe workflow execution for Node.js and browser environments.
**Installation:**
```bash
npm install simstudio-ts-sdk
# or
yarn add simstudio-ts-sdk
# or
bun add simstudio-ts-sdk
```
**Quick Start:**
```typescript
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: 'your-api-key-here'
});
const result = await client.executeWorkflow('workflow-id', {
input: { message: 'Hello, world!' }
});
```
### 🐍 Python SDK (`simstudio-sdk`)
**Directory:** `python-sdk/`
The Python SDK provides Pythonic workflow execution with comprehensive error handling and data classes.
**Installation:**
```bash
pip install simstudio-sdk
```
**Quick Start:**
```python
from simstudio import SimStudioClient
client = SimStudioClient(api_key='your-api-key-here')
result = client.execute_workflow('workflow-id',
input_data={'message': 'Hello, world!'})
```
## Core Features
Both SDKs provide the same core functionality:
**Workflow Execution** - Execute deployed workflows with optional input data
**Status Checking** - Check deployment status and workflow readiness
**Error Handling** - Comprehensive error handling with specific error codes
**Timeout Support** - Configurable timeouts for workflow execution
**Input Validation** - Validate workflows before execution
**Type Safety** - Full type definitions (TypeScript) and data classes (Python)
## API Compatibility
Both SDKs are built on top of the same REST API endpoints:
- `POST /api/workflows/{id}/execute` - Execute workflow (with or without input)
- `GET /api/workflows/{id}/status` - Get workflow status
## Authentication
Both SDKs use API key authentication via the `X-API-Key` header. You can obtain an API key by:
1. Logging in to your [Sim](https://sim.ai) account
2. Navigating to your workflow
3. Clicking "Deploy" to deploy your workflow
4. Creating or selecting an API key during deployment
## Environment Variables
Both SDKs support environment variable configuration:
```bash
# Required
SIM_API_KEY=your-api-key-here
# Optional
SIM_BASE_URL=https://sim.ai # or your custom domain
```
## Error Handling
Both SDKs provide consistent error handling with these error codes:
| Code | Description |
|------|-------------|
| `UNAUTHORIZED` | Invalid API key |
| `TIMEOUT` | Request timed out |
| `USAGE_LIMIT_EXCEEDED` | Account usage limit exceeded |
| `INVALID_JSON` | Invalid JSON in request body |
| `EXECUTION_ERROR` | General execution error |
| `STATUS_ERROR` | Error getting workflow status |
## Examples
### TypeScript Example
```typescript
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
try {
// Check if workflow is ready
const isReady = await client.validateWorkflow('workflow-id');
if (!isReady) {
throw new Error('Workflow not deployed');
}
// Execute workflow
const result = await client.executeWorkflow('workflow-id', {
input: { data: 'example' },
timeout: 30000
});
if (result.success) {
console.log('Output:', result.output);
}
} catch (error) {
if (error instanceof SimStudioError) {
console.error(`Error ${error.code}: ${error.message}`);
}
}
```
### Python Example
```python
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv('SIM_API_KEY'))
try:
# Check if workflow is ready
is_ready = client.validate_workflow('workflow-id')
if not is_ready:
raise Exception('Workflow not deployed')
# Execute workflow
result = client.execute_workflow('workflow-id',
input_data={'data': 'example'},
timeout=30.0)
if result.success:
print(f'Output: {result.output}')
except SimStudioError as error:
print(f'Error {error.code}: {error}')
```
## Development
### Building the SDKs
**TypeScript SDK:**
```bash
cd packages/ts-sdk
bun install
bun run build
```
**Python SDK:**
```bash
cd packages/python-sdk
pip install -e ".[dev]"
python -m build
```
### Running Examples
**TypeScript:**
```bash
cd packages/ts-sdk
SIM_API_KEY=your-key bun run examples/basic-usage.ts
```
**Python:**
```bash
cd packages/python-sdk
SIM_API_KEY=your-key python examples/basic_usage.py
```
### Testing
**TypeScript:**
```bash
cd packages/ts-sdk
bun run test
```
**Python:**
```bash
cd packages/python-sdk
pytest
```
## Publishing
The SDKs are automatically published to npm and PyPI when changes are pushed to the main branch. See [Publishing Setup](../.github/PUBLISHING.md) for details on:
- Setting up GitHub secrets for automated publishing
- Manual publishing instructions
- Version management and semantic versioning
- Troubleshooting common issues
## Contributing
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/amazing-feature`
3. Make your changes
4. Add tests for your changes
5. Run the test suite: `bun run test` (TypeScript) or `pytest` (Python)
6. Update version numbers if needed
7. Commit your changes: `git commit -m 'Add amazing feature'`
8. Push to the branch: `git push origin feature/amazing-feature`
9. Open a Pull Request
## License
Both SDKs are licensed under the Apache-2.0 License. See the [LICENSE](../LICENSE) file for details.
## Support
- 📖 [Documentation](https://docs.sim.ai)
- 💬 [Discord Community](https://discord.gg/simstudio)
- 🐛 [Issue Tracker](https://github.com/simstudioai/sim/issues)
- 📧 [Email Support](mailto:support@sim.ai)
| Package | npm | Description |
|---------|-----|-------------|
| [cli](./cli) | `simstudio` | Run Sim locally via Docker |
| [ts-sdk](./ts-sdk) | `simstudio-ts-sdk` | TypeScript SDK for workflow execution |
| [python-sdk](./python-sdk) | `simstudio-sdk` | Python SDK for workflow execution |

View File

@@ -2,14 +2,19 @@
"name": "simstudio",
"version": "0.1.19",
"description": "Sim CLI - Run Sim with a single command",
"main": "dist/index.js",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"bin": {
"simstudio": "dist/index.js"
},
"scripts": {
"build": "bun run build:tsc",
"build:tsc": "tsc",
"build": "tsc",
"type-check": "tsc --noEmit",
"prepublishOnly": "bun run build"
},
"files": [
@@ -19,17 +24,16 @@
"simstudio",
"ai",
"workflow",
"ui",
"cli",
"sim",
"sim-studio",
"agent",
"agents",
"automation",
"docker"
],
"author": "Sim",
"license": "Apache-2.0",
"engines": {
"node": ">=16"
},
"dependencies": {
"chalk": "^4.1.2",
"commander": "^11.1.0",
@@ -38,20 +42,9 @@
"listr2": "^6.6.1"
},
"devDependencies": {
"@sim/tsconfig": "workspace:*",
"@types/inquirer": "^8.2.6",
"@types/node": "^20.5.1",
"typescript": "^5.1.6"
},
"engines": {
"node": ">=16"
},
"turbo": {
"tasks": {
"build": {
"outputs": [
"dist/**"
]
}
}
"typescript": "^5.7.3"
}
}

View File

@@ -1,15 +1,10 @@
{
"extends": "@sim/tsconfig/library-build.json",
"compilerOptions": {
"target": "es2020",
"module": "ESNext",
"target": "ES2020",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"forceConsistentCasingInFileNames": true
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]

View File

@@ -24,15 +24,13 @@
"db:studio": "bunx drizzle-kit studio --config=./drizzle.config.ts",
"type-check": "tsc --noEmit"
},
"peerDependencies": {
"dependencies": {
"drizzle-orm": "^0.44.5",
"postgres": "^3.4.5"
"postgres": "^3.4.5",
"zod": "^3.24.2"
},
"devDependencies": {
"@sim/tsconfig": "workspace:*",
"typescript": "^5.7.3"
},
"overrides": {
"drizzle-orm": "^0.44.5",
"postgres": "^3.4.5"
}
}

View File

@@ -1,21 +1,12 @@
{
"extends": "@sim/tsconfig/library.json",
"compilerOptions": {
"target": "es2022",
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["es2022"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"@sim/db": ["./index.ts"],
"@sim/db/*": ["./*"]
},
"resolveJsonModule": true,
"noEmit": true
}
},
"include": ["**/*.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "scripts"]
}

View File

@@ -23,6 +23,7 @@
"chalk": "5.6.2"
},
"devDependencies": {
"@sim/tsconfig": "workspace:*",
"typescript": "^5.7.3",
"vitest": "^3.0.8"
}

View File

@@ -1,19 +1,5 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"noEmit": true,
"isolatedModules": true,
"resolveJsonModule": true
},
"extends": "@sim/tsconfig/library.json",
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -41,7 +41,11 @@
"vitest": "^3.0.0"
},
"devDependencies": {
"@sim/tsconfig": "workspace:*",
"typescript": "^5.7.3",
"vitest": "^3.0.8"
},
"dependencies": {
"nanoid": "5.1.6"
}
}

View File

@@ -1,5 +1,6 @@
import { expect } from 'vitest'
import type { BlockState, Edge, WorkflowState } from '../types'
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Asserts that a block exists in the workflow.
@@ -12,7 +13,7 @@ import type { BlockState, Edge, WorkflowState } from '../types'
* ```
*/
export function expectBlockExists(
blocks: Record<string, BlockState>,
blocks: Record<string, any>,
blockId: string,
expectedType?: string
): void {
@@ -33,7 +34,7 @@ export function expectBlockExists(
* expectBlockNotExists(workflow.blocks, 'deleted-block')
* ```
*/
export function expectBlockNotExists(blocks: Record<string, BlockState>, blockId: string): void {
export function expectBlockNotExists(blocks: Record<string, any>, blockId: string): void {
expect(blocks[blockId], `Block "${blockId}" should not exist`).toBeUndefined()
}
@@ -45,7 +46,7 @@ export function expectBlockNotExists(blocks: Record<string, BlockState>, blockId
* expectEdgeConnects(workflow.edges, 'block-0', 'block-1')
* ```
*/
export function expectEdgeConnects(edges: Edge[], sourceId: string, targetId: string): void {
export function expectEdgeConnects(edges: any[], sourceId: string, targetId: string): void {
const edge = edges.find((e) => e.source === sourceId && e.target === targetId)
expect(edge, `Edge from "${sourceId}" to "${targetId}" should exist`).toBeDefined()
}
@@ -58,7 +59,7 @@ export function expectEdgeConnects(edges: Edge[], sourceId: string, targetId: st
* expectNoEdgeBetween(workflow.edges, 'block-1', 'block-0') // No reverse edge
* ```
*/
export function expectNoEdgeBetween(edges: Edge[], sourceId: string, targetId: string): void {
export function expectNoEdgeBetween(edges: any[], sourceId: string, targetId: string): void {
const edge = edges.find((e) => e.source === sourceId && e.target === targetId)
expect(edge, `Edge from "${sourceId}" to "${targetId}" should not exist`).toBeUndefined()
}
@@ -72,7 +73,7 @@ export function expectNoEdgeBetween(edges: Edge[], sourceId: string, targetId: s
* ```
*/
export function expectBlockHasParent(
blocks: Record<string, BlockState>,
blocks: Record<string, any>,
childId: string,
expectedParentId: string
): void {
@@ -91,7 +92,7 @@ export function expectBlockHasParent(
* expectBlockCount(workflow, 5)
* ```
*/
export function expectBlockCount(workflow: WorkflowState, expectedCount: number): void {
export function expectBlockCount(workflow: any, expectedCount: number): void {
const actualCount = Object.keys(workflow.blocks).length
expect(actualCount, `Workflow should have ${expectedCount} blocks`).toBe(expectedCount)
}
@@ -104,7 +105,7 @@ export function expectBlockCount(workflow: WorkflowState, expectedCount: number)
* expectEdgeCount(workflow, 4)
* ```
*/
export function expectEdgeCount(workflow: WorkflowState, expectedCount: number): void {
export function expectEdgeCount(workflow: any, expectedCount: number): void {
expect(workflow.edges.length, `Workflow should have ${expectedCount} edges`).toBe(expectedCount)
}
@@ -117,7 +118,7 @@ export function expectEdgeCount(workflow: WorkflowState, expectedCount: number):
* ```
*/
export function expectBlockPosition(
blocks: Record<string, BlockState>,
blocks: Record<string, any>,
blockId: string,
expectedPosition: { x: number; y: number }
): void {
@@ -135,7 +136,7 @@ export function expectBlockPosition(
* expectBlockEnabled(workflow.blocks, 'block-1')
* ```
*/
export function expectBlockEnabled(blocks: Record<string, BlockState>, blockId: string): void {
export function expectBlockEnabled(blocks: Record<string, any>, blockId: string): void {
const block = blocks[blockId]
expect(block, `Block "${blockId}" should exist`).toBeDefined()
expect(block.enabled, `Block "${blockId}" should be enabled`).toBe(true)
@@ -149,7 +150,7 @@ export function expectBlockEnabled(blocks: Record<string, BlockState>, blockId:
* expectBlockDisabled(workflow.blocks, 'disabled-block')
* ```
*/
export function expectBlockDisabled(blocks: Record<string, BlockState>, blockId: string): void {
export function expectBlockDisabled(blocks: Record<string, any>, blockId: string): void {
const block = blocks[blockId]
expect(block, `Block "${blockId}" should exist`).toBeDefined()
expect(block.enabled, `Block "${blockId}" should be disabled`).toBe(false)
@@ -164,7 +165,7 @@ export function expectBlockDisabled(blocks: Record<string, BlockState>, blockId:
* ```
*/
export function expectLoopExists(
workflow: WorkflowState,
workflow: any,
loopId: string,
expectedConfig?: { iterations?: number; loopType?: string; nodes?: string[] }
): void {
@@ -193,7 +194,7 @@ export function expectLoopExists(
* ```
*/
export function expectParallelExists(
workflow: WorkflowState,
workflow: any,
parallelId: string,
expectedConfig?: { count?: number; parallelType?: string; nodes?: string[] }
): void {
@@ -222,7 +223,7 @@ export function expectParallelExists(
* expectEmptyWorkflow(workflow)
* ```
*/
export function expectEmptyWorkflow(workflow: WorkflowState): void {
export function expectEmptyWorkflow(workflow: any): void {
expect(Object.keys(workflow.blocks).length, 'Workflow should have no blocks').toBe(0)
expect(workflow.edges.length, 'Workflow should have no edges').toBe(0)
expect(Object.keys(workflow.loops).length, 'Workflow should have no loops').toBe(0)
@@ -237,7 +238,7 @@ export function expectEmptyWorkflow(workflow: WorkflowState): void {
* expectLinearChain(workflow.edges, ['start', 'step1', 'step2', 'end'])
* ```
*/
export function expectLinearChain(edges: Edge[], blockIds: string[]): void {
export function expectLinearChain(edges: any[], blockIds: string[]): void {
for (let i = 0; i < blockIds.length - 1; i++) {
expectEdgeConnects(edges, blockIds[i], blockIds[i + 1])
}

View File

@@ -4,7 +4,9 @@ import {
createFunctionBlock,
createStarterBlock,
} from '../factories/block.factory'
import type { BlockState, Edge, Loop, Parallel, Position, WorkflowState } from '../types'
import type { Position } from '../types'
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Fluent builder for creating complex workflow states.
@@ -29,11 +31,11 @@ import type { BlockState, Edge, Loop, Parallel, Position, WorkflowState } from '
* ```
*/
export class WorkflowBuilder {
private blocks: Record<string, BlockState> = {}
private edges: Edge[] = []
private loops: Record<string, Loop> = {}
private parallels: Record<string, Parallel> = {}
private variables: WorkflowState['variables'] = []
private blocks: Record<string, any> = {}
private edges: any[] = []
private loops: Record<string, any> = {}
private parallels: Record<string, any> = {}
private variables: any[] = []
private isDeployed = false
/**
@@ -245,8 +247,9 @@ export class WorkflowBuilder {
/**
* Builds and returns the workflow state.
* Returns `any` to be assignable to any app's workflow type.
*/
build(): WorkflowState {
build(): any {
return {
blocks: this.blocks,
edges: this.edges,

View File

@@ -1,15 +1,18 @@
import type { BlockData, BlockOutput, BlockState, Position, SubBlockState } from '../types'
import type { BlockData, BlockOutput, Position } from '../types'
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Options for creating a mock block.
* All fields are optional - sensible defaults are provided.
* Uses `any` for subBlocks to accept any app type without conflicts.
*/
export interface BlockFactoryOptions {
id?: string
type?: string
name?: string
position?: Position
subBlocks?: Record<string, SubBlockState>
subBlocks?: Record<string, any>
outputs?: Record<string, BlockOutput>
enabled?: boolean
horizontalHandles?: boolean
@@ -43,7 +46,7 @@ function generateBlockId(prefix = 'block'): string {
* const block = createBlock({ type: 'function', parentId: 'loop-1' })
* ```
*/
export function createBlock(options: BlockFactoryOptions = {}): BlockState {
export function createBlock(options: BlockFactoryOptions = {}): any {
const id = options.id ?? generateBlockId(options.type ?? 'block')
const data: BlockData = options.data ?? {}
@@ -72,7 +75,7 @@ export function createBlock(options: BlockFactoryOptions = {}): BlockState {
/**
* Creates a starter block (workflow entry point).
*/
export function createStarterBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createStarterBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'starter',
@@ -83,7 +86,7 @@ export function createStarterBlock(options: Omit<BlockFactoryOptions, 'type'> =
/**
* Creates an agent block (AI agent execution).
*/
export function createAgentBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createAgentBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'agent',
@@ -94,7 +97,7 @@ export function createAgentBlock(options: Omit<BlockFactoryOptions, 'type'> = {}
/**
* Creates a function block (code execution).
*/
export function createFunctionBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createFunctionBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'function',
@@ -105,7 +108,7 @@ export function createFunctionBlock(options: Omit<BlockFactoryOptions, 'type'> =
/**
* Creates a condition block (branching logic).
*/
export function createConditionBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createConditionBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'condition',
@@ -121,7 +124,7 @@ export function createLoopBlock(
loopType?: 'for' | 'forEach' | 'while' | 'doWhile'
count?: number
} = {}
): BlockState {
): any {
const data: BlockData = {
...options.data,
loopType: options.loopType ?? 'for',
@@ -145,7 +148,7 @@ export function createParallelBlock(
parallelType?: 'count' | 'collection'
count?: number
} = {}
): BlockState {
): any {
const data: BlockData = {
...options.data,
parallelType: options.parallelType ?? 'count',
@@ -164,7 +167,7 @@ export function createParallelBlock(
/**
* Creates a router block (output routing).
*/
export function createRouterBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createRouterBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'router',
@@ -175,7 +178,7 @@ export function createRouterBlock(options: Omit<BlockFactoryOptions, 'type'> = {
/**
* Creates an API block (HTTP requests).
*/
export function createApiBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createApiBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'api',
@@ -186,7 +189,7 @@ export function createApiBlock(options: Omit<BlockFactoryOptions, 'type'> = {}):
/**
* Creates a response block (workflow output).
*/
export function createResponseBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createResponseBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'response',
@@ -197,7 +200,7 @@ export function createResponseBlock(options: Omit<BlockFactoryOptions, 'type'> =
/**
* Creates a webhook trigger block.
*/
export function createWebhookBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createWebhookBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'webhook',
@@ -208,7 +211,7 @@ export function createWebhookBlock(options: Omit<BlockFactoryOptions, 'type'> =
/**
* Creates a knowledge block (vector search).
*/
export function createKnowledgeBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState {
export function createKnowledgeBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({
...options,
type: 'knowledge',

View File

@@ -1,4 +1,4 @@
import type { Edge } from '../types'
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Options for creating a mock edge.
@@ -36,7 +36,7 @@ function generateEdgeId(source: string, target: string): string {
* })
* ```
*/
export function createEdge(options: EdgeFactoryOptions): Edge {
export function createEdge(options: EdgeFactoryOptions): any {
return {
id: options.id ?? generateEdgeId(options.source, options.target),
source: options.source,
@@ -66,7 +66,7 @@ export function createEdges(
sourceHandle?: string
targetHandle?: string
}>
): Edge[] {
): any[] {
return connections.map((conn) => createEdge(conn))
}
@@ -79,8 +79,8 @@ export function createEdges(
* const edges = createLinearEdges(['a', 'b', 'c', 'd'])
* ```
*/
export function createLinearEdges(blockIds: string[]): Edge[] {
const edges: Edge[] = []
export function createLinearEdges(blockIds: string[]): any[] {
const edges: any[] = []
for (let i = 0; i < blockIds.length - 1; i++) {
edges.push(createEdge({ source: blockIds[i], target: blockIds[i + 1] }))
}

View File

@@ -1,5 +1,6 @@
import { nanoid } from 'nanoid'
import type { BlockState, Edge } from '../types'
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Operation types supported by the undo/redo store.
@@ -51,8 +52,8 @@ export interface RemoveBlockOperation extends BaseOperation {
type: 'remove-block'
data: {
blockId: string
blockSnapshot: BlockState | null
edgeSnapshots?: Edge[]
blockSnapshot: any
edgeSnapshots?: any[]
}
}
@@ -69,7 +70,7 @@ export interface AddEdgeOperation extends BaseOperation {
*/
export interface RemoveEdgeOperation extends BaseOperation {
type: 'remove-edge'
data: { edgeId: string; edgeSnapshot: Edge | null }
data: { edgeId: string; edgeSnapshot: any }
}
/**
@@ -80,7 +81,7 @@ export interface DuplicateBlockOperation extends BaseOperation {
data: {
sourceBlockId: string
duplicatedBlockId: string
duplicatedBlockSnapshot: BlockState
duplicatedBlockSnapshot: any
}
}
@@ -127,10 +128,7 @@ interface OperationEntryOptions {
/**
* Creates a mock add-block operation entry.
*/
export function createAddBlockEntry(
blockId: string,
options: OperationEntryOptions = {}
): OperationEntry {
export function createAddBlockEntry(blockId: string, options: OperationEntryOptions = {}): any {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now()
@@ -161,9 +159,9 @@ export function createAddBlockEntry(
*/
export function createRemoveBlockEntry(
blockId: string,
blockSnapshot: BlockState | null = null,
blockSnapshot: any = null,
options: OperationEntryOptions = {}
): OperationEntry {
): any {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now()
@@ -192,10 +190,7 @@ export function createRemoveBlockEntry(
/**
* Creates a mock add-edge operation entry.
*/
export function createAddEdgeEntry(
edgeId: string,
options: OperationEntryOptions = {}
): OperationEntry {
export function createAddEdgeEntry(edgeId: string, options: OperationEntryOptions = {}): any {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now()
@@ -226,9 +221,9 @@ export function createAddEdgeEntry(
*/
export function createRemoveEdgeEntry(
edgeId: string,
edgeSnapshot: Edge | null = null,
edgeSnapshot: any = null,
options: OperationEntryOptions = {}
): OperationEntry {
): any {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now()
@@ -262,10 +257,7 @@ interface MoveBlockOptions extends OperationEntryOptions {
/**
* Creates a mock move-block operation entry.
*/
export function createMoveBlockEntry(
blockId: string,
options: MoveBlockOptions = {}
): OperationEntry {
export function createMoveBlockEntry(blockId: string, options: MoveBlockOptions = {}): any {
const {
id = nanoid(8),
workflowId = 'wf-1',
@@ -304,9 +296,9 @@ export function createMoveBlockEntry(
export function createDuplicateBlockEntry(
sourceBlockId: string,
duplicatedBlockId: string,
duplicatedBlockSnapshot: BlockState,
duplicatedBlockSnapshot: any,
options: OperationEntryOptions = {}
): OperationEntry {
): any {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now()
@@ -343,7 +335,7 @@ export function createUpdateParentEntry(
oldPosition?: { x: number; y: number }
newPosition?: { x: number; y: number }
} = {}
): OperationEntry {
): any {
const {
id = nanoid(8),
workflowId = 'wf-1',

View File

@@ -1,18 +1,20 @@
import type { BlockState, Edge, Loop, Parallel, WorkflowState } from '../types'
import { createBlock, createFunctionBlock, createStarterBlock } from './block.factory'
import { createLinearEdges } from './edge.factory'
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Options for creating a mock workflow state.
* Uses `any` for complex types to avoid conflicts with app types.
*/
export interface WorkflowFactoryOptions {
blocks?: Record<string, BlockState>
edges?: Edge[]
loops?: Record<string, Loop>
parallels?: Record<string, Parallel>
blocks?: Record<string, any>
edges?: any[]
loops?: Record<string, any>
parallels?: Record<string, any>
lastSaved?: number
isDeployed?: boolean
variables?: WorkflowState['variables']
variables?: any[]
}
/**
@@ -23,7 +25,7 @@ export interface WorkflowFactoryOptions {
* const workflow = createWorkflowState()
* ```
*/
export function createWorkflowState(options: WorkflowFactoryOptions = {}): WorkflowState {
export function createWorkflowState(options: WorkflowFactoryOptions = {}): any {
return {
blocks: options.blocks ?? {},
edges: options.edges ?? [],
@@ -45,12 +47,12 @@ export function createWorkflowState(options: WorkflowFactoryOptions = {}): Workf
* const workflow = createLinearWorkflow(3)
* ```
*/
export function createLinearWorkflow(blockCount: number, spacing = 200): WorkflowState {
export function createLinearWorkflow(blockCount: number, spacing = 200): any {
if (blockCount < 1) {
return createWorkflowState()
}
const blocks: Record<string, BlockState> = {}
const blocks: Record<string, any> = {}
const blockIds: string[] = []
for (let i = 0; i < blockCount; i++) {
@@ -87,8 +89,8 @@ export function createLinearWorkflow(blockCount: number, spacing = 200): Workflo
* └─→ false-branch ┘
* ```
*/
export function createBranchingWorkflow(): WorkflowState {
const blocks: Record<string, BlockState> = {
export function createBranchingWorkflow(): any {
const blocks: Record<string, any> = {
start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }),
condition: createBlock({
id: 'condition',
@@ -109,7 +111,7 @@ export function createBranchingWorkflow(): WorkflowState {
end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 600, y: 0 } }),
}
const edges: Edge[] = [
const edges: any[] = [
{ id: 'e1', source: 'start', target: 'condition' },
{ id: 'e2', source: 'condition', target: 'true-branch', sourceHandle: 'condition-if' },
{ id: 'e3', source: 'condition', target: 'false-branch', sourceHandle: 'condition-else' },
@@ -128,8 +130,8 @@ export function createBranchingWorkflow(): WorkflowState {
* start ─→ loop[loop-body] ─→ end
* ```
*/
export function createLoopWorkflow(iterations = 3): WorkflowState {
const blocks: Record<string, BlockState> = {
export function createLoopWorkflow(iterations = 3): any {
const blocks: Record<string, any> = {
start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }),
loop: createBlock({
id: 'loop',
@@ -147,12 +149,12 @@ export function createLoopWorkflow(iterations = 3): WorkflowState {
end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 500, y: 0 } }),
}
const edges: Edge[] = [
const edges: any[] = [
{ id: 'e1', source: 'start', target: 'loop' },
{ id: 'e2', source: 'loop', target: 'end' },
]
const loops: Record<string, Loop> = {
const loops: Record<string, any> = {
loop: {
id: 'loop',
nodes: ['loop-body'],
@@ -172,8 +174,8 @@ export function createLoopWorkflow(iterations = 3): WorkflowState {
* start ─→ parallel[parallel-task] ─→ end
* ```
*/
export function createParallelWorkflow(count = 2): WorkflowState {
const blocks: Record<string, BlockState> = {
export function createParallelWorkflow(count = 2): any {
const blocks: Record<string, any> = {
start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }),
parallel: createBlock({
id: 'parallel',
@@ -191,12 +193,12 @@ export function createParallelWorkflow(count = 2): WorkflowState {
end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 500, y: 0 } }),
}
const edges: Edge[] = [
const edges: any[] = [
{ id: 'e1', source: 'start', target: 'parallel' },
{ id: 'e2', source: 'parallel', target: 'end' },
]
const parallels: Record<string, Parallel> = {
const parallels: Record<string, any> = {
parallel: {
id: 'parallel',
nodes: ['parallel-task'],

View File

@@ -1,9 +1,15 @@
/**
* Core types for the testing package.
* These are simplified versions of the actual types used in apps/sim,
* designed for test scenarios without requiring all dependencies.
*
* These are intentionally loose/permissive types that accept any shape of data
* from the app. The testing package should not try to mirror app types exactly -
* that creates maintenance burden and type drift issues.
*
* Tests themselves provide type safety through their actual usage of app types.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface Position {
x: number
y: number
@@ -11,72 +17,26 @@ export interface Position {
export interface BlockData {
parentId?: string
extent?: 'parent'
extent?: string
width?: number
height?: number
count?: number
loopType?: 'for' | 'forEach' | 'while' | 'doWhile'
parallelType?: 'count' | 'collection'
loopType?: string
parallelType?: string
collection?: any
whileCondition?: string
doWhileCondition?: string
type?: string
[key: string]: any
}
/**
* SubBlockType union for testing.
* Matches the SubBlockType values from the app (apps/sim/blocks/types.ts).
*/
export type SubBlockType =
| 'short-input'
| 'long-input'
| 'dropdown'
| 'combobox'
| 'slider'
| 'table'
| 'code'
| 'switch'
| 'tool-input'
| 'checkbox-list'
| 'grouped-checkbox-list'
| 'condition-input'
| 'eval-input'
| 'time-input'
| 'oauth-input'
| 'webhook-config'
| 'schedule-info'
| 'file-selector'
| 'project-selector'
| 'channel-selector'
| 'user-selector'
| 'folder-selector'
| 'knowledge-base-selector'
| 'knowledge-tag-filters'
| 'document-selector'
| 'document-tag-entry'
| 'mcp-server-selector'
| 'mcp-tool-selector'
| 'mcp-dynamic-args'
| 'input-format'
export interface SubBlockState {
id: string
type: SubBlockType
value: string | number | string[][] | null
type: string
value: any
}
/**
* Primitive value types for block outputs.
*/
export type PrimitiveValueType = 'string' | 'number' | 'boolean'
/**
* BlockOutput type matching the app's structure.
* Can be a primitive type or an object with string keys.
*/
export type BlockOutput =
| PrimitiveValueType
| { [key: string]: PrimitiveValueType | Record<string, any> }
export type BlockOutput = any
export interface BlockState {
id: string
@@ -91,38 +51,39 @@ export interface BlockState {
advancedMode?: boolean
triggerMode?: boolean
data?: BlockData
layout?: {
measuredWidth?: number
measuredHeight?: number
}
layout?: Record<string, any>
[key: string]: any
}
export interface Edge {
id: string
source: string
target: string
sourceHandle?: string
targetHandle?: string
sourceHandle?: string | null
targetHandle?: string | null
type?: string
data?: Record<string, any>
[key: string]: any
}
export interface Loop {
id: string
nodes: string[]
iterations: number
loopType: 'for' | 'forEach' | 'while' | 'doWhile'
forEachItems?: any[] | Record<string, any> | string
loopType: string
forEachItems?: any
whileCondition?: string
doWhileCondition?: string
[key: string]: any
}
export interface Parallel {
id: string
nodes: string[]
distribution?: any[] | Record<string, any> | string
distribution?: any
count?: number
parallelType?: 'count' | 'collection'
parallelType?: string
[key: string]: any
}
export interface WorkflowState {
@@ -135,12 +96,8 @@ export interface WorkflowState {
isDeployed?: boolean
deployedAt?: Date
needsRedeployment?: boolean
variables?: Array<{
id: string
name: string
type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'plain'
value: any
}>
variables?: any[]
[key: string]: any
}
export interface ExecutionContext {
@@ -164,6 +121,7 @@ export interface ExecutionContext {
completedLoops: Set<string>
activeExecutionPath: Set<string>
abortSignal?: AbortSignal
[key: string]: any
}
export interface User {
@@ -171,6 +129,7 @@ export interface User {
email: string
name?: string
image?: string
[key: string]: any
}
export interface Workspace {
@@ -179,6 +138,7 @@ export interface Workspace {
ownerId: string
createdAt: Date
updatedAt: Date
[key: string]: any
}
export interface Workflow {
@@ -189,4 +149,5 @@ export interface Workflow {
createdAt: Date
updatedAt: Date
isDeployed?: boolean
[key: string]: any
}

View File

@@ -1,20 +1,11 @@
{
"extends": "@sim/tsconfig/library.json",
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": ".",
"paths": {
"@sim/testing/*": ["./src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
"exclude": ["node_modules"]
}

View File

@@ -3,9 +3,6 @@
"version": "0.1.1",
"description": "Sim SDK - Execute workflows programmatically",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
@@ -13,8 +10,8 @@
}
},
"scripts": {
"build": "bun run build:tsc",
"build:tsc": "tsc",
"build": "tsc",
"type-check": "tsc --noEmit",
"dev:watch": "tsc --watch",
"prepublishOnly": "bun run build",
"test": "vitest run",
@@ -38,10 +35,11 @@
"node-fetch": "^3.3.2"
},
"devDependencies": {
"@sim/tsconfig": "workspace:*",
"@types/node": "^20.5.1",
"typescript": "^5.1.6",
"vitest": "^3.0.8",
"@vitest/coverage-v8": "^3.0.8"
"@vitest/coverage-v8": "^3.0.8",
"typescript": "^5.7.3",
"vitest": "^3.0.8"
},
"engines": {
"node": ">=16"

View File

@@ -1,19 +1,12 @@
{
"extends": "@sim/tsconfig/library-build.json",
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"moduleResolution": "node",
"resolveJsonModule": true
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]

View File

@@ -0,0 +1,24 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Base",
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"verbatimModuleSyntax": false,
"noUncheckedIndexedAccess": false,
"noEmit": true
},
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,11 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Library (Build)",
"extends": "./base.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noEmit": false
}
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Library (No Emit)",
"extends": "./base.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"noEmit": true
}
}

View File

@@ -0,0 +1,17 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Next.js",
"extends": "./base.json",
"compilerOptions": {
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"allowJs": true,
"incremental": true,
"allowImportingTsExtensions": true,
"plugins": [
{
"name": "next"
}
]
}
}

View File

@@ -0,0 +1,13 @@
{
"name": "@sim/tsconfig",
"version": "0.0.0",
"private": true,
"license": "Apache-2.0",
"description": "Shared TypeScript configurations for Sim monorepo",
"exports": {
"./base.json": "./base.json",
"./nextjs.json": "./nextjs.json",
"./library.json": "./library.json",
"./library-build.json": "./library-build.json"
}
}