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

@@ -29,6 +29,7 @@
"tailwind-merge": "^3.0.2" "tailwind-merge": "^3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@sim/tsconfig": "workspace:*",
"@tailwindcss/postcss": "^4.0.12", "@tailwindcss/postcss": "^4.0.12",
"@types/mdx": "^2.0.13", "@types/mdx": "^2.0.13",
"@types/node": "^22.14.1", "@types/node": "^22.14.1",

View File

@@ -1,29 +1,11 @@
{ {
"extends": "@sim/tsconfig/nextjs.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"target": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"paths": { "paths": {
"@/.source/*": ["./.source/*"], "@/.source/*": ["./.source/*"],
"@/*": ["./*"] "@/*": ["./*"]
}, }
"plugins": [
{
"name": "next"
}
]
}, },
"include": [ "include": [
"next-env.d.ts", "next-env.d.ts",

View File

@@ -14,7 +14,7 @@ export async function GET() {
headers: { headers: {
Accept: 'application/vnd.github+json', Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28', 'X-GitHub-Api-Version': '2022-11-28',
'User-Agent': 'SimStudio/1.0', 'User-Agent': 'Sim/1.0',
...(token ? { Authorization: `Bearer ${token}` } : {}), ...(token ? { Authorization: `Bearer ${token}` } : {}),
}, },
next: { revalidate: 3600 }, next: { revalidate: 3600 },

View File

@@ -138,7 +138,10 @@ describe('OAuth Token Refresh', () => {
}) })
) )
const [, requestOptions] = mockFetch.mock.calls[0] const [, requestOptions] = mockFetch.mock.calls[0] as [
string,
{ headers: Record<string, string>; body: string },
]
const authHeader = requestOptions.headers.Authorization const authHeader = requestOptions.headers.Authorization
expect(authHeader).toMatch(/^Basic /) expect(authHeader).toMatch(/^Basic /)
@@ -251,7 +254,10 @@ describe('OAuth Token Refresh', () => {
}) })
) )
const [, requestOptions] = mockFetch.mock.calls[0] const [, requestOptions] = mockFetch.mock.calls[0] as [
string,
{ headers: Record<string, string>; body: string },
]
expect(requestOptions.headers.Authorization).toBeUndefined() expect(requestOptions.headers.Authorization).toBeUndefined()
@@ -279,7 +285,10 @@ describe('OAuth Token Refresh', () => {
await withMockFetch(mockFetch, () => refreshOAuthToken('github', refreshToken)) await withMockFetch(mockFetch, () => refreshOAuthToken('github', refreshToken))
const [, requestOptions] = mockFetch.mock.calls[0] const [, requestOptions] = mockFetch.mock.calls[0] as [
string,
{ headers: Record<string, string>; body: string },
]
expect(requestOptions.headers.Accept).toBe('application/json') expect(requestOptions.headers.Accept).toBe('application/json')
}) })
@@ -289,7 +298,10 @@ describe('OAuth Token Refresh', () => {
await withMockFetch(mockFetch, () => refreshOAuthToken('reddit', refreshToken)) await withMockFetch(mockFetch, () => refreshOAuthToken('reddit', refreshToken))
const [, requestOptions] = mockFetch.mock.calls[0] const [, requestOptions] = mockFetch.mock.calls[0] as [
string,
{ headers: Record<string, string>; body: string },
]
expect(requestOptions.headers['User-Agent']).toBe( expect(requestOptions.headers['User-Agent']).toBe(
'sim-studio/1.0 (https://github.com/simstudioai/sim)' 'sim-studio/1.0 (https://github.com/simstudioai/sim)'
) )

View File

@@ -652,7 +652,7 @@ async function processEmails(
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-Webhook-Secret': webhookData.secret || '', 'X-Webhook-Secret': webhookData.secret || '',
'User-Agent': 'SimStudio/1.0', 'User-Agent': 'Sim/1.0',
}, },
body: JSON.stringify(payload), body: JSON.stringify(payload),
}) })

View File

@@ -465,7 +465,7 @@ async function processOutlookEmails(
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-Webhook-Secret': webhookData.secret || '', 'X-Webhook-Secret': webhookData.secret || '',
'User-Agent': 'SimStudio/1.0', 'User-Agent': 'Sim/1.0',
}, },
body: JSON.stringify(payload), body: JSON.stringify(payload),
}) })

View File

@@ -60,7 +60,7 @@ export interface RssWebhookPayload {
const parser = new Parser({ const parser = new Parser({
timeout: 30000, timeout: 30000,
headers: { headers: {
'User-Agent': 'SimStudio/1.0 RSS Poller', 'User-Agent': 'Sim/1.0 RSS Poller',
}, },
}) })
@@ -255,7 +255,7 @@ async function fetchNewRssItems(
const response = await fetch(pinnedUrl, { const response = await fetch(pinnedUrl, {
headers: { headers: {
Host: urlValidation.originalHostname!, Host: urlValidation.originalHostname!,
'User-Agent': 'SimStudio/1.0 RSS Poller', 'User-Agent': 'Sim/1.0 RSS Poller',
Accept: 'application/rss+xml, application/xml, text/xml, */*', Accept: 'application/rss+xml, application/xml, text/xml, */*',
}, },
signal: AbortSignal.timeout(30000), signal: AbortSignal.timeout(30000),
@@ -362,7 +362,7 @@ async function processRssItems(
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-Webhook-Secret': webhookData.secret || '', 'X-Webhook-Secret': webhookData.secret || '',
'User-Agent': 'SimStudio/1.0', 'User-Agent': 'Sim/1.0',
}, },
body: JSON.stringify(payload), body: JSON.stringify(payload),
}) })

View File

@@ -46,10 +46,9 @@ function asAppBlocks<T>(blocks: T): Record<string, AppBlockState> {
* These tests intentionally use old SubBlockTypes (textarea, select, messages-input, input) * These tests intentionally use old SubBlockTypes (textarea, select, messages-input, input)
* to verify the migration logic converts them to new types. * to verify the migration logic converts them to new types.
*/ */
function legacySubBlocks<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any
subBlocks: T function legacySubBlocks(subBlocks: Record<string, any>): any {
): Record<string, { id: string; type: string; value: any }> { return subBlocks
return subBlocks as Record<string, { id: string; type: string; value: any }>
} }
const { mockDb, mockWorkflowBlocks, mockWorkflowEdges, mockWorkflowSubflows } = vi.hoisted(() => { const { mockDb, mockWorkflowBlocks, mockWorkflowEdges, mockWorkflowSubflows } = vi.hoisted(() => {
@@ -1022,7 +1021,7 @@ describe('Database Helpers', () => {
position: { x: 100, y: 100 }, position: { x: 100, y: 100 },
height: 150, height: 150,
advancedMode: false, advancedMode: false,
subBlocks: { model: { id: 'model', type: 'select', value: 'gpt-4o' } }, subBlocks: legacySubBlocks({ model: { id: 'model', type: 'select', value: 'gpt-4o' } }),
}), }),
mockWorkflowId mockWorkflowId
) )
@@ -1034,11 +1033,11 @@ describe('Database Helpers', () => {
position: { x: 200, y: 100 }, position: { x: 200, y: 100 },
height: 200, height: 200,
advancedMode: true, advancedMode: true,
subBlocks: { subBlocks: legacySubBlocks({
systemPrompt: { id: 'systemPrompt', type: 'textarea', value: 'System prompt' }, systemPrompt: { id: 'systemPrompt', type: 'textarea', value: 'System prompt' },
userPrompt: { id: 'userPrompt', type: 'textarea', value: 'User prompt' }, userPrompt: { id: 'userPrompt', type: 'textarea', value: 'User prompt' },
model: { id: 'model', type: 'select', value: 'gpt-4o' }, model: { id: 'model', type: 'select', value: 'gpt-4o' },
}, }),
}), }),
mockWorkflowId mockWorkflowId
) )
@@ -1153,7 +1152,7 @@ describe('Database Helpers', () => {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
name: 'Test Agent', name: 'Test Agent',
subBlocks: { subBlocks: legacySubBlocks({
systemPrompt: { systemPrompt: {
id: 'systemPrompt', id: 'systemPrompt',
type: 'textarea', type: 'textarea',
@@ -1164,7 +1163,7 @@ describe('Database Helpers', () => {
type: 'textarea', type: 'textarea',
value: 'Hello world', value: 'Hello world',
}, },
}, }),
}), }),
} }
@@ -1183,13 +1182,13 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
systemPrompt: { systemPrompt: {
id: 'systemPrompt', id: 'systemPrompt',
type: 'textarea', type: 'textarea',
value: 'You are helpful', value: 'You are helpful',
}, },
}, }),
}), }),
} }
@@ -1204,13 +1203,13 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
userPrompt: { userPrompt: {
id: 'userPrompt', id: 'userPrompt',
type: 'textarea', type: 'textarea',
value: 'Hello', value: 'Hello',
}, },
}, }),
}), }),
} }
@@ -1225,13 +1224,13 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
userPrompt: { userPrompt: {
id: 'userPrompt', id: 'userPrompt',
type: 'textarea', type: 'textarea',
value: { input: 'Hello from object' }, value: { input: 'Hello from object' },
}, },
}, }),
}), }),
} }
@@ -1246,13 +1245,13 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
userPrompt: { userPrompt: {
id: 'userPrompt', id: 'userPrompt',
type: 'textarea', type: 'textarea',
value: { foo: 'bar', baz: 123 }, value: { foo: 'bar', baz: 123 },
}, },
}, }),
}), }),
} }
@@ -1268,7 +1267,7 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
systemPrompt: { systemPrompt: {
id: 'systemPrompt', id: 'systemPrompt',
type: 'textarea', type: 'textarea',
@@ -1284,7 +1283,7 @@ describe('Database Helpers', () => {
type: 'messages-input', type: 'messages-input',
value: existingMessages, value: existingMessages,
}, },
}, }),
}), }),
} }
@@ -1297,13 +1296,13 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
model: { model: {
id: 'model', id: 'model',
type: 'select', type: 'select',
value: 'gpt-4o', value: 'gpt-4o',
}, },
}, }),
}), }),
} }
@@ -1316,13 +1315,13 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'api-1': createApiBlock({ 'api-1': createApiBlock({
id: 'api-1', id: 'api-1',
subBlocks: { subBlocks: legacySubBlocks({
url: { url: {
id: 'url', id: 'url',
type: 'input', type: 'input',
value: 'https://example.com', value: 'https://example.com',
}, },
}, }),
}), }),
} }
@@ -1336,18 +1335,18 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
systemPrompt: { id: 'systemPrompt', type: 'textarea', value: 'System 1' }, systemPrompt: { id: 'systemPrompt', type: 'textarea', value: 'System 1' },
}, }),
}), }),
'api-1': createApiBlock({ 'api-1': createApiBlock({
id: 'api-1', id: 'api-1',
}), }),
'agent-2': createAgentBlock({ 'agent-2': createAgentBlock({
id: 'agent-2', id: 'agent-2',
subBlocks: { subBlocks: legacySubBlocks({
userPrompt: { id: 'userPrompt', type: 'textarea', value: 'User 2' }, userPrompt: { id: 'userPrompt', type: 'textarea', value: 'User 2' },
}, }),
}), }),
} }
@@ -1368,10 +1367,10 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
systemPrompt: { id: 'systemPrompt', type: 'textarea', value: '' }, systemPrompt: { id: 'systemPrompt', type: 'textarea', value: '' },
userPrompt: { id: 'userPrompt', type: 'textarea', value: '' }, userPrompt: { id: 'userPrompt', type: 'textarea', value: '' },
}, }),
}), }),
} }
@@ -1384,9 +1383,9 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
systemPrompt: { id: 'systemPrompt', type: 'textarea', value: 123 }, systemPrompt: { id: 'systemPrompt', type: 'textarea', value: 123 },
}, }),
}), }),
} }
@@ -1401,9 +1400,9 @@ describe('Database Helpers', () => {
const blocks = { const blocks = {
'agent-1': createAgentBlock({ 'agent-1': createAgentBlock({
id: 'agent-1', id: 'agent-1',
subBlocks: { subBlocks: legacySubBlocks({
systemPrompt: { id: 'systemPrompt', type: 'textarea', value: 'System' }, systemPrompt: { id: 'systemPrompt', type: 'textarea', value: 'System' },
}, }),
}), }),
} }

View File

@@ -23,7 +23,6 @@
"generate-docs": "bun run ../../scripts/generate-docs.ts" "generate-docs": "bun run ../../scripts/generate-docs.ts"
}, },
"dependencies": { "dependencies": {
"@sim/logger": "workspace:*",
"@anthropic-ai/sdk": "^0.39.0", "@anthropic-ai/sdk": "^0.39.0",
"@aws-sdk/client-dynamodb": "3.940.0", "@aws-sdk/client-dynamodb": "3.940.0",
"@aws-sdk/client-rds-data": "3.940.0", "@aws-sdk/client-rds-data": "3.940.0",
@@ -40,6 +39,8 @@
"@e2b/code-interpreter": "^2.0.0", "@e2b/code-interpreter": "^2.0.0",
"@google/genai": "1.34.0", "@google/genai": "1.34.0",
"@hookform/resolvers": "^4.1.3", "@hookform/resolvers": "^4.1.3",
"@linear/sdk": "40.0.0",
"@modelcontextprotocol/sdk": "1.20.2",
"@opentelemetry/api": "^1.9.0", "@opentelemetry/api": "^1.9.0",
"@opentelemetry/exporter-jaeger": "2.1.0", "@opentelemetry/exporter-jaeger": "2.1.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.200.0", "@opentelemetry/exporter-trace-otlp-http": "^0.200.0",
@@ -70,6 +71,10 @@
"@radix-ui/react-visually-hidden": "1.2.4", "@radix-ui/react-visually-hidden": "1.2.4",
"@react-email/components": "^0.0.34", "@react-email/components": "^0.0.34",
"@react-email/render": "2.0.0", "@react-email/render": "2.0.0",
"@sim/logger": "workspace:*",
"@t3-oss/env-nextjs": "0.13.4",
"@tanstack/react-query": "5.90.8",
"@tanstack/react-query-devtools": "5.90.2",
"@trigger.dev/sdk": "4.1.2", "@trigger.dev/sdk": "4.1.2",
"@types/react-window": "2.0.0", "@types/react-window": "2.0.0",
"@types/three": "0.177.0", "@types/three": "0.177.0",
@@ -82,18 +87,25 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"croner": "^9.0.0", "croner": "^9.0.0",
"cronstrue": "3.3.0",
"csv-parse": "6.1.0", "csv-parse": "6.1.0",
"date-fns": "4.1.0", "date-fns": "4.1.0",
"decimal.js": "10.6.0",
"drizzle-orm": "^0.44.5",
"encoding": "0.1.13", "encoding": "0.1.13",
"entities": "6.0.1", "entities": "6.0.1",
"ffmpeg-static": "5.3.0",
"fluent-ffmpeg": "2.1.3",
"framer-motion": "^12.5.0", "framer-motion": "^12.5.0",
"fuse.js": "7.1.0", "fuse.js": "7.1.0",
"google-auth-library": "10.5.0",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"groq-sdk": "^0.15.0", "groq-sdk": "^0.15.0",
"html-to-image": "1.11.13", "html-to-image": "1.11.13",
"html-to-text": "^9.0.5", "html-to-text": "^9.0.5",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"ioredis": "^5.6.0", "ioredis": "^5.6.0",
"isolated-vm": "6.0.2",
"jose": "6.0.11", "jose": "6.0.11",
"js-tiktoken": "1.0.21", "js-tiktoken": "1.0.21",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
@@ -102,17 +114,22 @@
"lodash": "4.17.21", "lodash": "4.17.21",
"lucide-react": "^0.479.0", "lucide-react": "^0.479.0",
"mammoth": "^1.9.0", "mammoth": "^1.9.0",
"mongodb": "6.19.0",
"mysql2": "3.14.3", "mysql2": "3.14.3",
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"neo4j-driver": "6.0.1",
"next": "16.1.0-canary.21", "next": "16.1.0-canary.21",
"next-mdx-remote": "^5.0.0", "next-mdx-remote": "^5.0.0",
"next-runtime-env": "3.3.0", "next-runtime-env": "3.3.0",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"nodemailer": "7.0.11",
"officeparser": "^5.2.0", "officeparser": "^5.2.0",
"onedollarstats": "0.0.10",
"openai": "^4.91.1", "openai": "^4.91.1",
"papaparse": "5.5.3", "papaparse": "5.5.3",
"posthog-js": "1.268.9", "posthog-js": "1.268.9",
"posthog-node": "5.9.2", "posthog-node": "5.9.2",
"postgres": "^3.4.5",
"prismjs": "^1.30.0", "prismjs": "^1.30.0",
"react": "19.2.1", "react": "19.2.1",
"react-colorful": "5.6.1", "react-colorful": "5.6.1",
@@ -126,14 +143,17 @@
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"remark-gfm": "4.0.1", "remark-gfm": "4.0.1",
"resend": "^4.1.2", "resend": "^4.1.2",
"rss-parser": "3.13.0",
"sharp": "0.34.3", "sharp": "0.34.3",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"socket.io-client": "4.8.1",
"ssh2": "^1.17.0", "ssh2": "^1.17.0",
"stripe": "18.5.0", "stripe": "18.5.0",
"tailwind-merge": "^2.6.0", "tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"thread-stream": "4.0.0", "thread-stream": "4.0.0",
"three": "0.177.0", "three": "0.177.0",
"twilio": "5.9.0",
"unpdf": "1.4.0", "unpdf": "1.4.0",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"xlsx": "0.18.5", "xlsx": "0.18.5",
@@ -142,13 +162,16 @@
}, },
"devDependencies": { "devDependencies": {
"@sim/testing": "workspace:*", "@sim/testing": "workspace:*",
"@sim/tsconfig": "workspace:*",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@trigger.dev/build": "4.1.2", "@trigger.dev/build": "4.1.2",
"@types/fluent-ffmpeg": "2.1.28",
"@types/html-to-text": "9.0.4", "@types/html-to-text": "9.0.4",
"@types/js-yaml": "4.0.9", "@types/js-yaml": "4.0.9",
"@types/jsdom": "21.1.7", "@types/jsdom": "21.1.7",
"@types/lodash": "^4.17.16", "@types/lodash": "^4.17.16",
"@types/node": "24.2.1", "@types/node": "24.2.1",
"@types/nodemailer": "7.0.4",
"@types/papaparse": "5.3.16", "@types/papaparse": "5.3.16",
"@types/prismjs": "^1.26.5", "@types/prismjs": "^1.26.5",
"@types/react": "^19", "@types/react": "^19",
@@ -171,6 +194,8 @@
"trustedDependencies": [ "trustedDependencies": [
"canvas", "canvas",
"better-sqlite3", "better-sqlite3",
"ffmpeg-static",
"isolated-vm",
"sharp" "sharp"
], ],
"overrides": { "overrides": {

View File

@@ -23,6 +23,7 @@ import {
} from '@sim/testing' } from '@sim/testing'
import { beforeEach, describe, expect, it } from 'vitest' import { beforeEach, describe, expect, it } from 'vitest'
import { runWithUndoRedoRecordingSuspended, useUndoRedoStore } from '@/stores/undo-redo/store' import { runWithUndoRedoRecordingSuspended, useUndoRedoStore } from '@/stores/undo-redo/store'
import type { DuplicateBlockOperation, UpdateParentOperation } from '@/stores/undo-redo/types'
describe('useUndoRedoStore', () => { describe('useUndoRedoStore', () => {
const workflowId = 'wf-test' const workflowId = 'wf-test'
@@ -663,7 +664,8 @@ describe('useUndoRedoStore', () => {
) )
const entry = undo(workflowId, userId) const entry = undo(workflowId, userId)
expect(entry?.operation.data.duplicatedBlockSnapshot).toMatchObject({ const operation = entry?.operation as DuplicateBlockOperation
expect(operation.data.duplicatedBlockSnapshot).toMatchObject({
id: 'duplicated-block', id: 'duplicated-block',
name: 'Duplicated Agent', name: 'Duplicated Agent',
type: 'agent', type: 'agent',
@@ -716,10 +718,11 @@ describe('useUndoRedoStore', () => {
) )
const entry = undo(workflowId, userId) const entry = undo(workflowId, userId)
expect(entry?.inverse.data.oldParentId).toBe('loop-2') const inverse = entry?.inverse as UpdateParentOperation
expect(entry?.inverse.data.newParentId).toBe('loop-1') expect(inverse.data.oldParentId).toBe('loop-2')
expect(entry?.inverse.data.oldPosition).toEqual({ x: 100, y: 100 }) expect(inverse.data.newParentId).toBe('loop-1')
expect(entry?.inverse.data.newPosition).toEqual({ x: 0, y: 0 }) expect(inverse.data.oldPosition).toEqual({ x: 100, y: 100 })
expect(inverse.data.newPosition).toEqual({ x: 0, y: 0 })
}) })
}) })

View File

@@ -27,7 +27,7 @@ export const pageContentTool: ToolConfig<WikipediaPageContentParams, WikipediaPa
}, },
method: 'GET', method: 'GET',
headers: () => ({ headers: () => ({
'User-Agent': 'SimStudio/1.0 (https://sim.ai)', 'User-Agent': 'Sim/1.0 (https://sim.ai)',
Accept: Accept:
'text/html; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/HTML/2.1.0"', 'text/html; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/HTML/2.1.0"',
}), }),

View File

@@ -15,7 +15,7 @@ export const randomPageTool: ToolConfig<Record<string, never>, WikipediaRandomPa
}, },
method: 'GET', method: 'GET',
headers: () => ({ headers: () => ({
'User-Agent': 'SimStudio/1.0 (https://sim.ai)', 'User-Agent': 'Sim/1.0 (https://sim.ai)',
Accept: 'application/json', Accept: 'application/json',
}), }),
}, },

View File

@@ -41,7 +41,7 @@ export const searchTool: ToolConfig<WikipediaSearchParams, WikipediaSearchRespon
}, },
method: 'GET', method: 'GET',
headers: () => ({ headers: () => ({
'User-Agent': 'SimStudio/1.0 (https://sim.ai)', 'User-Agent': 'Sim/1.0 (https://sim.ai)',
Accept: 'application/json', Accept: 'application/json',
}), }),
}, },

View File

@@ -27,7 +27,7 @@ export const pageSummaryTool: ToolConfig<WikipediaPageSummaryParams, WikipediaPa
}, },
method: 'GET', method: 'GET',
headers: () => ({ headers: () => ({
'User-Agent': 'SimStudio/1.0 (https://sim.ai)', 'User-Agent': 'Sim/1.0 (https://sim.ai)',
Accept: 'application/json', Accept: 'application/json',
}), }),
}, },

View File

@@ -1,13 +1,6 @@
{ {
"extends": "@sim/tsconfig/nextjs.json",
"compilerOptions": { "compilerOptions": {
"target": "es2022",
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["es2022", "dom", "dom.iterable"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["./*"], "@/*": ["./*"],
@@ -28,19 +21,7 @@
"@sim/db/*": ["../../packages/db/*"], "@sim/db/*": ["../../packages/db/*"],
"@/executor": ["./executor"], "@/executor": ["./executor"],
"@/executor/*": ["./executor/*"] "@/executor/*": ["./executor/*"]
}, }
"allowJs": true,
"noEmit": true,
"incremental": true,
"resolveJsonModule": true,
"isolatedModules": true,
"allowImportingTsExtensions": true,
"jsx": "react-jsx",
"plugins": [
{
"name": "next"
}
]
}, },
"include": [ "include": [
"**/*.ts", "**/*.ts",

653
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -18,18 +18,19 @@ FROM base AS deps
WORKDIR /app WORKDIR /app
COPY package.json bun.lock turbo.json ./ COPY package.json bun.lock turbo.json ./
RUN mkdir -p apps packages/db packages/testing packages/logger RUN mkdir -p apps packages/db packages/testing packages/logger packages/tsconfig
COPY apps/sim/package.json ./apps/sim/package.json COPY apps/sim/package.json ./apps/sim/package.json
COPY packages/db/package.json ./packages/db/package.json COPY packages/db/package.json ./packages/db/package.json
COPY packages/testing/package.json ./packages/testing/package.json COPY packages/testing/package.json ./packages/testing/package.json
COPY packages/logger/package.json ./packages/logger/package.json COPY packages/logger/package.json ./packages/logger/package.json
COPY packages/tsconfig/package.json ./packages/tsconfig/package.json
# Install turbo globally, then dependencies, then rebuild isolated-vm for Node.js # Install turbo globally, then dependencies, then rebuild isolated-vm for Node.js
RUN --mount=type=cache,id=bun-cache,target=/root/.bun/install/cache \ RUN --mount=type=cache,id=bun-cache,target=/root/.bun/install/cache \
--mount=type=cache,id=npm-cache,target=/root/.npm \ --mount=type=cache,id=npm-cache,target=/root/.npm \
bun install -g turbo && \ bun install -g turbo && \
HUSKY=0 bun install --omit=dev --ignore-scripts && \ HUSKY=0 bun install --omit=dev --ignore-scripts && \
cd $(readlink -f node_modules/isolated-vm) && npx node-gyp rebuild --release && cd /app cd node_modules/.bun/isolated-vm@*/node_modules/isolated-vm && npx node-gyp rebuild --release && cd /app
# ======================================== # ========================================
# Builder Stage: Build the Application # Builder Stage: Build the Application
@@ -104,7 +105,7 @@ COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/.next/static ./apps/sim/.next/static COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/.next/static ./apps/sim/.next/static
# Copy isolated-vm native module (compiled for Node.js in deps stage) # Copy isolated-vm native module (compiled for Node.js in deps stage)
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/isolated-vm ./node_modules/isolated-vm COPY --from=deps --chown=nextjs:nodejs /app/node_modules/.bun/isolated-vm@6.0.2/node_modules/isolated-vm ./node_modules/isolated-vm
# Copy the isolated-vm worker script # Copy the isolated-vm worker script
COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/execution/isolated-vm-worker.cjs ./apps/sim/lib/execution/isolated-vm-worker.cjs COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/execution/isolated-vm-worker.cjs ./apps/sim/lib/execution/isolated-vm-worker.cjs

View File

@@ -11,11 +11,12 @@ RUN apk add --no-cache libc6-compat
WORKDIR /app WORKDIR /app
COPY package.json bun.lock turbo.json ./ COPY package.json bun.lock turbo.json ./
RUN mkdir -p apps packages/db packages/testing packages/logger RUN mkdir -p apps packages/db packages/testing packages/logger packages/tsconfig
COPY apps/sim/package.json ./apps/sim/package.json COPY apps/sim/package.json ./apps/sim/package.json
COPY packages/db/package.json ./packages/db/package.json COPY packages/db/package.json ./packages/db/package.json
COPY packages/testing/package.json ./packages/testing/package.json COPY packages/testing/package.json ./packages/testing/package.json
COPY packages/logger/package.json ./packages/logger/package.json COPY packages/logger/package.json ./packages/logger/package.json
COPY packages/tsconfig/package.json ./packages/tsconfig/package.json
# Install dependencies with cache mount for faster builds # Install dependencies with cache mount for faster builds
RUN --mount=type=cache,id=bun-cache,target=/root/.bun/install/cache \ RUN --mount=type=cache,id=bun-cache,target=/root/.bun/install/cache \

View File

@@ -33,37 +33,11 @@
"drizzle-orm": "^0.44.5", "drizzle-orm": "^0.44.5",
"postgres": "^3.4.5" "postgres": "^3.4.5"
}, },
"dependencies": {
"@linear/sdk": "40.0.0",
"@modelcontextprotocol/sdk": "1.20.2",
"@t3-oss/env-nextjs": "0.13.4",
"@tanstack/react-query": "5.90.8",
"@tanstack/react-query-devtools": "5.90.2",
"@types/fluent-ffmpeg": "2.1.28",
"cronstrue": "3.3.0",
"decimal.js": "10.6.0",
"drizzle-orm": "^0.44.5",
"ffmpeg-static": "5.3.0",
"fluent-ffmpeg": "2.1.3",
"isolated-vm": "6.0.2",
"mongodb": "6.19.0",
"neo4j-driver": "6.0.1",
"next-runtime-env": "3.3.0",
"nodemailer": "7.0.11",
"onedollarstats": "0.0.10",
"postgres": "^3.4.5",
"remark-gfm": "4.0.1",
"rss-parser": "3.13.0",
"socket.io-client": "4.8.1",
"twilio": "5.9.0",
"zod": "^3.24.2"
},
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.0.0-beta.5", "@biomejs/biome": "2.0.0-beta.5",
"@next/env": "16.1.0-canary.21", "@next/env": "16.1.0-canary.21",
"@octokit/rest": "^21.0.0", "@octokit/rest": "^21.0.0",
"@tailwindcss/typography": "0.5.19", "@tailwindcss/typography": "0.5.19",
"@types/nodemailer": "7.0.4",
"drizzle-kit": "^0.31.4", "drizzle-kit": "^0.31.4",
"husky": "9.1.7", "husky": "9.1.7",
"lint-staged": "16.0.0", "lint-staged": "16.0.0",
@@ -75,8 +49,6 @@
] ]
}, },
"trustedDependencies": [ "trustedDependencies": [
"ffmpeg-static",
"isolated-vm",
"sharp" "sharp"
] ]
} }

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` | Package | npm | Description |
- **Python**: `pip install simstudio-sdk` |---------|-----|-------------|
| [cli](./cli) | `simstudio` | Run Sim locally via Docker |
### 🟢 TypeScript/JavaScript SDK (`simstudio-ts-sdk`) | [ts-sdk](./ts-sdk) | `simstudio-ts-sdk` | TypeScript SDK for workflow execution |
| [python-sdk](./python-sdk) | `simstudio-sdk` | Python SDK for workflow execution |
**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)

View File

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

View File

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

View File

@@ -24,15 +24,13 @@
"db:studio": "bunx drizzle-kit studio --config=./drizzle.config.ts", "db:studio": "bunx drizzle-kit studio --config=./drizzle.config.ts",
"type-check": "tsc --noEmit" "type-check": "tsc --noEmit"
}, },
"peerDependencies": { "dependencies": {
"drizzle-orm": "^0.44.5", "drizzle-orm": "^0.44.5",
"postgres": "^3.4.5" "postgres": "^3.4.5",
"zod": "^3.24.2"
}, },
"devDependencies": { "devDependencies": {
"@sim/tsconfig": "workspace:*",
"typescript": "^5.7.3" "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": { "compilerOptions": {
"target": "es2022",
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["es2022"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@sim/db": ["./index.ts"], "@sim/db": ["./index.ts"],
"@sim/db/*": ["./*"] "@sim/db/*": ["./*"]
}, }
"resolveJsonModule": true,
"noEmit": true
}, },
"include": ["**/*.ts"], "include": ["**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules", "scripts"]
} }

View File

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

View File

@@ -1,19 +1,5 @@
{ {
"compilerOptions": { "extends": "@sim/tsconfig/library.json",
"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
},
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]
} }

View File

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

View File

@@ -1,5 +1,6 @@
import { expect } from 'vitest' 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. * Asserts that a block exists in the workflow.
@@ -12,7 +13,7 @@ import type { BlockState, Edge, WorkflowState } from '../types'
* ``` * ```
*/ */
export function expectBlockExists( export function expectBlockExists(
blocks: Record<string, BlockState>, blocks: Record<string, any>,
blockId: string, blockId: string,
expectedType?: string expectedType?: string
): void { ): void {
@@ -33,7 +34,7 @@ export function expectBlockExists(
* expectBlockNotExists(workflow.blocks, 'deleted-block') * 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() 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') * 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) const edge = edges.find((e) => e.source === sourceId && e.target === targetId)
expect(edge, `Edge from "${sourceId}" to "${targetId}" should exist`).toBeDefined() 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 * 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) const edge = edges.find((e) => e.source === sourceId && e.target === targetId)
expect(edge, `Edge from "${sourceId}" to "${targetId}" should not exist`).toBeUndefined() 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( export function expectBlockHasParent(
blocks: Record<string, BlockState>, blocks: Record<string, any>,
childId: string, childId: string,
expectedParentId: string expectedParentId: string
): void { ): void {
@@ -91,7 +92,7 @@ export function expectBlockHasParent(
* expectBlockCount(workflow, 5) * 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 const actualCount = Object.keys(workflow.blocks).length
expect(actualCount, `Workflow should have ${expectedCount} blocks`).toBe(expectedCount) expect(actualCount, `Workflow should have ${expectedCount} blocks`).toBe(expectedCount)
} }
@@ -104,7 +105,7 @@ export function expectBlockCount(workflow: WorkflowState, expectedCount: number)
* expectEdgeCount(workflow, 4) * 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) 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( export function expectBlockPosition(
blocks: Record<string, BlockState>, blocks: Record<string, any>,
blockId: string, blockId: string,
expectedPosition: { x: number; y: number } expectedPosition: { x: number; y: number }
): void { ): void {
@@ -135,7 +136,7 @@ export function expectBlockPosition(
* expectBlockEnabled(workflow.blocks, 'block-1') * 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] const block = blocks[blockId]
expect(block, `Block "${blockId}" should exist`).toBeDefined() expect(block, `Block "${blockId}" should exist`).toBeDefined()
expect(block.enabled, `Block "${blockId}" should be enabled`).toBe(true) 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') * 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] const block = blocks[blockId]
expect(block, `Block "${blockId}" should exist`).toBeDefined() expect(block, `Block "${blockId}" should exist`).toBeDefined()
expect(block.enabled, `Block "${blockId}" should be disabled`).toBe(false) 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( export function expectLoopExists(
workflow: WorkflowState, workflow: any,
loopId: string, loopId: string,
expectedConfig?: { iterations?: number; loopType?: string; nodes?: string[] } expectedConfig?: { iterations?: number; loopType?: string; nodes?: string[] }
): void { ): void {
@@ -193,7 +194,7 @@ export function expectLoopExists(
* ``` * ```
*/ */
export function expectParallelExists( export function expectParallelExists(
workflow: WorkflowState, workflow: any,
parallelId: string, parallelId: string,
expectedConfig?: { count?: number; parallelType?: string; nodes?: string[] } expectedConfig?: { count?: number; parallelType?: string; nodes?: string[] }
): void { ): void {
@@ -222,7 +223,7 @@ export function expectParallelExists(
* expectEmptyWorkflow(workflow) * 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(Object.keys(workflow.blocks).length, 'Workflow should have no blocks').toBe(0)
expect(workflow.edges.length, 'Workflow should have no edges').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) 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']) * 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++) { for (let i = 0; i < blockIds.length - 1; i++) {
expectEdgeConnects(edges, blockIds[i], blockIds[i + 1]) expectEdgeConnects(edges, blockIds[i], blockIds[i + 1])
} }

View File

@@ -4,7 +4,9 @@ import {
createFunctionBlock, createFunctionBlock,
createStarterBlock, createStarterBlock,
} from '../factories/block.factory' } 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. * Fluent builder for creating complex workflow states.
@@ -29,11 +31,11 @@ import type { BlockState, Edge, Loop, Parallel, Position, WorkflowState } from '
* ``` * ```
*/ */
export class WorkflowBuilder { export class WorkflowBuilder {
private blocks: Record<string, BlockState> = {} private blocks: Record<string, any> = {}
private edges: Edge[] = [] private edges: any[] = []
private loops: Record<string, Loop> = {} private loops: Record<string, any> = {}
private parallels: Record<string, Parallel> = {} private parallels: Record<string, any> = {}
private variables: WorkflowState['variables'] = [] private variables: any[] = []
private isDeployed = false private isDeployed = false
/** /**
@@ -245,8 +247,9 @@ export class WorkflowBuilder {
/** /**
* Builds and returns the workflow state. * Builds and returns the workflow state.
* Returns `any` to be assignable to any app's workflow type.
*/ */
build(): WorkflowState { build(): any {
return { return {
blocks: this.blocks, blocks: this.blocks,
edges: this.edges, 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. * Options for creating a mock block.
* All fields are optional - sensible defaults are provided. * All fields are optional - sensible defaults are provided.
* Uses `any` for subBlocks to accept any app type without conflicts.
*/ */
export interface BlockFactoryOptions { export interface BlockFactoryOptions {
id?: string id?: string
type?: string type?: string
name?: string name?: string
position?: Position position?: Position
subBlocks?: Record<string, SubBlockState> subBlocks?: Record<string, any>
outputs?: Record<string, BlockOutput> outputs?: Record<string, BlockOutput>
enabled?: boolean enabled?: boolean
horizontalHandles?: boolean horizontalHandles?: boolean
@@ -43,7 +46,7 @@ function generateBlockId(prefix = 'block'): string {
* const block = createBlock({ type: 'function', parentId: 'loop-1' }) * 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 id = options.id ?? generateBlockId(options.type ?? 'block')
const data: BlockData = options.data ?? {} const data: BlockData = options.data ?? {}
@@ -72,7 +75,7 @@ export function createBlock(options: BlockFactoryOptions = {}): BlockState {
/** /**
* Creates a starter block (workflow entry point). * 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({ return createBlock({
...options, ...options,
type: 'starter', type: 'starter',
@@ -83,7 +86,7 @@ export function createStarterBlock(options: Omit<BlockFactoryOptions, 'type'> =
/** /**
* Creates an agent block (AI agent execution). * 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({ return createBlock({
...options, ...options,
type: 'agent', type: 'agent',
@@ -94,7 +97,7 @@ export function createAgentBlock(options: Omit<BlockFactoryOptions, 'type'> = {}
/** /**
* Creates a function block (code execution). * Creates a function block (code execution).
*/ */
export function createFunctionBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState { export function createFunctionBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({ return createBlock({
...options, ...options,
type: 'function', type: 'function',
@@ -105,7 +108,7 @@ export function createFunctionBlock(options: Omit<BlockFactoryOptions, 'type'> =
/** /**
* Creates a condition block (branching logic). * Creates a condition block (branching logic).
*/ */
export function createConditionBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState { export function createConditionBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({ return createBlock({
...options, ...options,
type: 'condition', type: 'condition',
@@ -121,7 +124,7 @@ export function createLoopBlock(
loopType?: 'for' | 'forEach' | 'while' | 'doWhile' loopType?: 'for' | 'forEach' | 'while' | 'doWhile'
count?: number count?: number
} = {} } = {}
): BlockState { ): any {
const data: BlockData = { const data: BlockData = {
...options.data, ...options.data,
loopType: options.loopType ?? 'for', loopType: options.loopType ?? 'for',
@@ -145,7 +148,7 @@ export function createParallelBlock(
parallelType?: 'count' | 'collection' parallelType?: 'count' | 'collection'
count?: number count?: number
} = {} } = {}
): BlockState { ): any {
const data: BlockData = { const data: BlockData = {
...options.data, ...options.data,
parallelType: options.parallelType ?? 'count', parallelType: options.parallelType ?? 'count',
@@ -164,7 +167,7 @@ export function createParallelBlock(
/** /**
* Creates a router block (output routing). * Creates a router block (output routing).
*/ */
export function createRouterBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState { export function createRouterBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({ return createBlock({
...options, ...options,
type: 'router', type: 'router',
@@ -175,7 +178,7 @@ export function createRouterBlock(options: Omit<BlockFactoryOptions, 'type'> = {
/** /**
* Creates an API block (HTTP requests). * Creates an API block (HTTP requests).
*/ */
export function createApiBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState { export function createApiBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({ return createBlock({
...options, ...options,
type: 'api', type: 'api',
@@ -186,7 +189,7 @@ export function createApiBlock(options: Omit<BlockFactoryOptions, 'type'> = {}):
/** /**
* Creates a response block (workflow output). * Creates a response block (workflow output).
*/ */
export function createResponseBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState { export function createResponseBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({ return createBlock({
...options, ...options,
type: 'response', type: 'response',
@@ -197,7 +200,7 @@ export function createResponseBlock(options: Omit<BlockFactoryOptions, 'type'> =
/** /**
* Creates a webhook trigger block. * Creates a webhook trigger block.
*/ */
export function createWebhookBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState { export function createWebhookBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({ return createBlock({
...options, ...options,
type: 'webhook', type: 'webhook',
@@ -208,7 +211,7 @@ export function createWebhookBlock(options: Omit<BlockFactoryOptions, 'type'> =
/** /**
* Creates a knowledge block (vector search). * Creates a knowledge block (vector search).
*/ */
export function createKnowledgeBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): BlockState { export function createKnowledgeBlock(options: Omit<BlockFactoryOptions, 'type'> = {}): any {
return createBlock({ return createBlock({
...options, ...options,
type: 'knowledge', 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. * 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 { return {
id: options.id ?? generateEdgeId(options.source, options.target), id: options.id ?? generateEdgeId(options.source, options.target),
source: options.source, source: options.source,
@@ -66,7 +66,7 @@ export function createEdges(
sourceHandle?: string sourceHandle?: string
targetHandle?: string targetHandle?: string
}> }>
): Edge[] { ): any[] {
return connections.map((conn) => createEdge(conn)) return connections.map((conn) => createEdge(conn))
} }
@@ -79,8 +79,8 @@ export function createEdges(
* const edges = createLinearEdges(['a', 'b', 'c', 'd']) * const edges = createLinearEdges(['a', 'b', 'c', 'd'])
* ``` * ```
*/ */
export function createLinearEdges(blockIds: string[]): Edge[] { export function createLinearEdges(blockIds: string[]): any[] {
const edges: Edge[] = [] const edges: any[] = []
for (let i = 0; i < blockIds.length - 1; i++) { for (let i = 0; i < blockIds.length - 1; i++) {
edges.push(createEdge({ source: blockIds[i], target: blockIds[i + 1] })) edges.push(createEdge({ source: blockIds[i], target: blockIds[i + 1] }))
} }

View File

@@ -1,5 +1,6 @@
import { nanoid } from 'nanoid' 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. * Operation types supported by the undo/redo store.
@@ -51,8 +52,8 @@ export interface RemoveBlockOperation extends BaseOperation {
type: 'remove-block' type: 'remove-block'
data: { data: {
blockId: string blockId: string
blockSnapshot: BlockState | null blockSnapshot: any
edgeSnapshots?: Edge[] edgeSnapshots?: any[]
} }
} }
@@ -69,7 +70,7 @@ export interface AddEdgeOperation extends BaseOperation {
*/ */
export interface RemoveEdgeOperation extends BaseOperation { export interface RemoveEdgeOperation extends BaseOperation {
type: 'remove-edge' type: 'remove-edge'
data: { edgeId: string; edgeSnapshot: Edge | null } data: { edgeId: string; edgeSnapshot: any }
} }
/** /**
@@ -80,7 +81,7 @@ export interface DuplicateBlockOperation extends BaseOperation {
data: { data: {
sourceBlockId: string sourceBlockId: string
duplicatedBlockId: string duplicatedBlockId: string
duplicatedBlockSnapshot: BlockState duplicatedBlockSnapshot: any
} }
} }
@@ -127,10 +128,7 @@ interface OperationEntryOptions {
/** /**
* Creates a mock add-block operation entry. * Creates a mock add-block operation entry.
*/ */
export function createAddBlockEntry( export function createAddBlockEntry(blockId: string, options: OperationEntryOptions = {}): any {
blockId: string,
options: OperationEntryOptions = {}
): OperationEntry {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now() const timestamp = Date.now()
@@ -161,9 +159,9 @@ export function createAddBlockEntry(
*/ */
export function createRemoveBlockEntry( export function createRemoveBlockEntry(
blockId: string, blockId: string,
blockSnapshot: BlockState | null = null, blockSnapshot: any = null,
options: OperationEntryOptions = {} options: OperationEntryOptions = {}
): OperationEntry { ): any {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now() const timestamp = Date.now()
@@ -192,10 +190,7 @@ export function createRemoveBlockEntry(
/** /**
* Creates a mock add-edge operation entry. * Creates a mock add-edge operation entry.
*/ */
export function createAddEdgeEntry( export function createAddEdgeEntry(edgeId: string, options: OperationEntryOptions = {}): any {
edgeId: string,
options: OperationEntryOptions = {}
): OperationEntry {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now() const timestamp = Date.now()
@@ -226,9 +221,9 @@ export function createAddEdgeEntry(
*/ */
export function createRemoveEdgeEntry( export function createRemoveEdgeEntry(
edgeId: string, edgeId: string,
edgeSnapshot: Edge | null = null, edgeSnapshot: any = null,
options: OperationEntryOptions = {} options: OperationEntryOptions = {}
): OperationEntry { ): any {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now() const timestamp = Date.now()
@@ -262,10 +257,7 @@ interface MoveBlockOptions extends OperationEntryOptions {
/** /**
* Creates a mock move-block operation entry. * Creates a mock move-block operation entry.
*/ */
export function createMoveBlockEntry( export function createMoveBlockEntry(blockId: string, options: MoveBlockOptions = {}): any {
blockId: string,
options: MoveBlockOptions = {}
): OperationEntry {
const { const {
id = nanoid(8), id = nanoid(8),
workflowId = 'wf-1', workflowId = 'wf-1',
@@ -304,9 +296,9 @@ export function createMoveBlockEntry(
export function createDuplicateBlockEntry( export function createDuplicateBlockEntry(
sourceBlockId: string, sourceBlockId: string,
duplicatedBlockId: string, duplicatedBlockId: string,
duplicatedBlockSnapshot: BlockState, duplicatedBlockSnapshot: any,
options: OperationEntryOptions = {} options: OperationEntryOptions = {}
): OperationEntry { ): any {
const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options const { id = nanoid(8), workflowId = 'wf-1', userId = 'user-1', createdAt = Date.now() } = options
const timestamp = Date.now() const timestamp = Date.now()
@@ -343,7 +335,7 @@ export function createUpdateParentEntry(
oldPosition?: { x: number; y: number } oldPosition?: { x: number; y: number }
newPosition?: { x: number; y: number } newPosition?: { x: number; y: number }
} = {} } = {}
): OperationEntry { ): any {
const { const {
id = nanoid(8), id = nanoid(8),
workflowId = 'wf-1', 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 { createBlock, createFunctionBlock, createStarterBlock } from './block.factory'
import { createLinearEdges } from './edge.factory' import { createLinearEdges } from './edge.factory'
/* eslint-disable @typescript-eslint/no-explicit-any */
/** /**
* Options for creating a mock workflow state. * Options for creating a mock workflow state.
* Uses `any` for complex types to avoid conflicts with app types.
*/ */
export interface WorkflowFactoryOptions { export interface WorkflowFactoryOptions {
blocks?: Record<string, BlockState> blocks?: Record<string, any>
edges?: Edge[] edges?: any[]
loops?: Record<string, Loop> loops?: Record<string, any>
parallels?: Record<string, Parallel> parallels?: Record<string, any>
lastSaved?: number lastSaved?: number
isDeployed?: boolean isDeployed?: boolean
variables?: WorkflowState['variables'] variables?: any[]
} }
/** /**
@@ -23,7 +25,7 @@ export interface WorkflowFactoryOptions {
* const workflow = createWorkflowState() * const workflow = createWorkflowState()
* ``` * ```
*/ */
export function createWorkflowState(options: WorkflowFactoryOptions = {}): WorkflowState { export function createWorkflowState(options: WorkflowFactoryOptions = {}): any {
return { return {
blocks: options.blocks ?? {}, blocks: options.blocks ?? {},
edges: options.edges ?? [], edges: options.edges ?? [],
@@ -45,12 +47,12 @@ export function createWorkflowState(options: WorkflowFactoryOptions = {}): Workf
* const workflow = createLinearWorkflow(3) * const workflow = createLinearWorkflow(3)
* ``` * ```
*/ */
export function createLinearWorkflow(blockCount: number, spacing = 200): WorkflowState { export function createLinearWorkflow(blockCount: number, spacing = 200): any {
if (blockCount < 1) { if (blockCount < 1) {
return createWorkflowState() return createWorkflowState()
} }
const blocks: Record<string, BlockState> = {} const blocks: Record<string, any> = {}
const blockIds: string[] = [] const blockIds: string[] = []
for (let i = 0; i < blockCount; i++) { for (let i = 0; i < blockCount; i++) {
@@ -87,8 +89,8 @@ export function createLinearWorkflow(blockCount: number, spacing = 200): Workflo
* └─→ false-branch ┘ * └─→ false-branch ┘
* ``` * ```
*/ */
export function createBranchingWorkflow(): WorkflowState { export function createBranchingWorkflow(): any {
const blocks: Record<string, BlockState> = { const blocks: Record<string, any> = {
start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }), start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }),
condition: createBlock({ condition: createBlock({
id: 'condition', id: 'condition',
@@ -109,7 +111,7 @@ export function createBranchingWorkflow(): WorkflowState {
end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 600, y: 0 } }), end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 600, y: 0 } }),
} }
const edges: Edge[] = [ const edges: any[] = [
{ id: 'e1', source: 'start', target: 'condition' }, { id: 'e1', source: 'start', target: 'condition' },
{ id: 'e2', source: 'condition', target: 'true-branch', sourceHandle: 'condition-if' }, { id: 'e2', source: 'condition', target: 'true-branch', sourceHandle: 'condition-if' },
{ id: 'e3', source: 'condition', target: 'false-branch', sourceHandle: 'condition-else' }, { id: 'e3', source: 'condition', target: 'false-branch', sourceHandle: 'condition-else' },
@@ -128,8 +130,8 @@ export function createBranchingWorkflow(): WorkflowState {
* start ─→ loop[loop-body] ─→ end * start ─→ loop[loop-body] ─→ end
* ``` * ```
*/ */
export function createLoopWorkflow(iterations = 3): WorkflowState { export function createLoopWorkflow(iterations = 3): any {
const blocks: Record<string, BlockState> = { const blocks: Record<string, any> = {
start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }), start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }),
loop: createBlock({ loop: createBlock({
id: 'loop', id: 'loop',
@@ -147,12 +149,12 @@ export function createLoopWorkflow(iterations = 3): WorkflowState {
end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 500, y: 0 } }), end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 500, y: 0 } }),
} }
const edges: Edge[] = [ const edges: any[] = [
{ id: 'e1', source: 'start', target: 'loop' }, { id: 'e1', source: 'start', target: 'loop' },
{ id: 'e2', source: 'loop', target: 'end' }, { id: 'e2', source: 'loop', target: 'end' },
] ]
const loops: Record<string, Loop> = { const loops: Record<string, any> = {
loop: { loop: {
id: 'loop', id: 'loop',
nodes: ['loop-body'], nodes: ['loop-body'],
@@ -172,8 +174,8 @@ export function createLoopWorkflow(iterations = 3): WorkflowState {
* start ─→ parallel[parallel-task] ─→ end * start ─→ parallel[parallel-task] ─→ end
* ``` * ```
*/ */
export function createParallelWorkflow(count = 2): WorkflowState { export function createParallelWorkflow(count = 2): any {
const blocks: Record<string, BlockState> = { const blocks: Record<string, any> = {
start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }), start: createStarterBlock({ id: 'start', position: { x: 0, y: 0 } }),
parallel: createBlock({ parallel: createBlock({
id: 'parallel', id: 'parallel',
@@ -191,12 +193,12 @@ export function createParallelWorkflow(count = 2): WorkflowState {
end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 500, y: 0 } }), end: createFunctionBlock({ id: 'end', name: 'End', position: { x: 500, y: 0 } }),
} }
const edges: Edge[] = [ const edges: any[] = [
{ id: 'e1', source: 'start', target: 'parallel' }, { id: 'e1', source: 'start', target: 'parallel' },
{ id: 'e2', source: 'parallel', target: 'end' }, { id: 'e2', source: 'parallel', target: 'end' },
] ]
const parallels: Record<string, Parallel> = { const parallels: Record<string, any> = {
parallel: { parallel: {
id: 'parallel', id: 'parallel',
nodes: ['parallel-task'], nodes: ['parallel-task'],

View File

@@ -1,9 +1,15 @@
/** /**
* Core types for the testing package. * 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 { export interface Position {
x: number x: number
y: number y: number
@@ -11,72 +17,26 @@ export interface Position {
export interface BlockData { export interface BlockData {
parentId?: string parentId?: string
extent?: 'parent' extent?: string
width?: number width?: number
height?: number height?: number
count?: number count?: number
loopType?: 'for' | 'forEach' | 'while' | 'doWhile' loopType?: string
parallelType?: 'count' | 'collection' parallelType?: string
collection?: any collection?: any
whileCondition?: string whileCondition?: string
doWhileCondition?: string doWhileCondition?: string
type?: 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 { export interface SubBlockState {
id: string id: string
type: SubBlockType type: string
value: string | number | string[][] | null value: any
} }
/** export type BlockOutput = 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 interface BlockState { export interface BlockState {
id: string id: string
@@ -91,38 +51,39 @@ export interface BlockState {
advancedMode?: boolean advancedMode?: boolean
triggerMode?: boolean triggerMode?: boolean
data?: BlockData data?: BlockData
layout?: { layout?: Record<string, any>
measuredWidth?: number [key: string]: any
measuredHeight?: number
}
} }
export interface Edge { export interface Edge {
id: string id: string
source: string source: string
target: string target: string
sourceHandle?: string sourceHandle?: string | null
targetHandle?: string targetHandle?: string | null
type?: string type?: string
data?: Record<string, any> data?: Record<string, any>
[key: string]: any
} }
export interface Loop { export interface Loop {
id: string id: string
nodes: string[] nodes: string[]
iterations: number iterations: number
loopType: 'for' | 'forEach' | 'while' | 'doWhile' loopType: string
forEachItems?: any[] | Record<string, any> | string forEachItems?: any
whileCondition?: string whileCondition?: string
doWhileCondition?: string doWhileCondition?: string
[key: string]: any
} }
export interface Parallel { export interface Parallel {
id: string id: string
nodes: string[] nodes: string[]
distribution?: any[] | Record<string, any> | string distribution?: any
count?: number count?: number
parallelType?: 'count' | 'collection' parallelType?: string
[key: string]: any
} }
export interface WorkflowState { export interface WorkflowState {
@@ -135,12 +96,8 @@ export interface WorkflowState {
isDeployed?: boolean isDeployed?: boolean
deployedAt?: Date deployedAt?: Date
needsRedeployment?: boolean needsRedeployment?: boolean
variables?: Array<{ variables?: any[]
id: string [key: string]: any
name: string
type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'plain'
value: any
}>
} }
export interface ExecutionContext { export interface ExecutionContext {
@@ -164,6 +121,7 @@ export interface ExecutionContext {
completedLoops: Set<string> completedLoops: Set<string>
activeExecutionPath: Set<string> activeExecutionPath: Set<string>
abortSignal?: AbortSignal abortSignal?: AbortSignal
[key: string]: any
} }
export interface User { export interface User {
@@ -171,6 +129,7 @@ export interface User {
email: string email: string
name?: string name?: string
image?: string image?: string
[key: string]: any
} }
export interface Workspace { export interface Workspace {
@@ -179,6 +138,7 @@ export interface Workspace {
ownerId: string ownerId: string
createdAt: Date createdAt: Date
updatedAt: Date updatedAt: Date
[key: string]: any
} }
export interface Workflow { export interface Workflow {
@@ -189,4 +149,5 @@ export interface Workflow {
createdAt: Date createdAt: Date
updatedAt: Date updatedAt: Date
isDeployed?: boolean isDeployed?: boolean
[key: string]: any
} }

View File

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

View File

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

View File

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