motia 0.17.1-beta.183-767430 → 0.17.2-beta.184-886580
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/create/index.mjs
CHANGED
|
@@ -31,7 +31,8 @@ const installRequiredDependencies = async (packageManager, rootDir, context) =>
|
|
|
31
31
|
const devDependencies = [
|
|
32
32
|
"ts-node@10.9.2",
|
|
33
33
|
"typescript@5.7.3",
|
|
34
|
-
"@types/react@19.1.1"
|
|
34
|
+
"@types/react@19.1.1",
|
|
35
|
+
`@motiadev/workbench@${version}`
|
|
35
36
|
].join(" ");
|
|
36
37
|
try {
|
|
37
38
|
await executeCommand(`${installCommand} ${dependencies}`, rootDir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["packageManager: string"],"sources":["../../src/create/index.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport pc from 'picocolors'\nimport { fileURLToPath } from 'url'\nimport type { CliContext, Message } from '../cloud/config-utils'\nimport { generateTypes } from '../generate-types'\nimport { pythonInstall } from '../install'\nimport { pluginDependencies } from '../plugins/plugin-dependencies'\nimport { executeCommand } from '../utils/execute-command'\nimport { getPackageManager, getPackageManagerFromEnv } from '../utils/get-package-manager'\nimport { version } from '../version'\nimport { pullRules } from './pull-rules'\nimport { setupTemplate } from './setup-template'\nimport { checkIfDirectoryExists, checkIfFileExists } from './utils'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\n\nconst installRequiredDependencies = async (packageManager: string, rootDir: string, context: CliContext) => {\n context.log('installing-dependencies', (message: Message) => message.tag('info').append('Installing dependencies...'))\n\n const installCommand = {\n npm: 'npm install --save',\n yarn: 'yarn add',\n pnpm: 'pnpm add',\n bun: 'bun add',\n }[packageManager]\n\n const dependencies = [\n `motia@${version}`,\n 'zod@4.1.12',\n `@motiadev/adapter-bullmq-events@${version}`,\n ...pluginDependencies.map((dep: string) => `${dep}@${version}`),\n ].join(' ')\n\n const devDependencies = ['ts-node@10.9.2', 'typescript@5.7.3', '@types/react@19.1.1'].join(' ')\n\n try {\n await executeCommand(`${installCommand} ${dependencies}`, rootDir)\n await executeCommand(`${installCommand} -D ${devDependencies}`, rootDir)\n\n context.log('dependencies-installed', (message: Message) => message.tag('success').append('Dependencies installed'))\n } catch (error) {\n console.error('❌ Failed to install dependencies:', error)\n }\n}\n\nconst preparePackageManager = async (rootDir: string, context: CliContext, detectFromParent = false) => {\n const detectionDir = detectFromParent ? process.cwd() : rootDir\n const envPackageManager = getPackageManagerFromEnv()\n const packageManager = getPackageManager(detectionDir)\n\n const isFallback =\n !envPackageManager && packageManager === 'npm' && !checkIfFileExists(detectionDir, 'package-lock.json')\n\n if (isFallback) {\n context.log('package-manager-using-default', (message: Message) =>\n message.tag('info').append('Using default package manager').append(packageManager, 'gray'),\n )\n } else {\n context.log('package-manager-detected', (message: Message) =>\n message.tag('info').append('Detected package manager').append(packageManager, 'gray'),\n )\n }\n\n return packageManager\n}\n\nconst installNodeDependencies = async (rootDir: string, context: CliContext) => {\n const packageManager = await preparePackageManager(rootDir, context)\n\n await installRequiredDependencies(packageManager, rootDir, context).catch((error: unknown) => {\n context.log('failed-to-install-dependencies', (message: Message) =>\n message.tag('failed').append('Failed to install dependencies'),\n )\n console.error(error)\n })\n\n return packageManager\n}\n\ntype Args = {\n projectName: string\n template: string\n cursorEnabled: boolean\n context: CliContext\n skipTutorialTemplates?: boolean\n skipRedis?: boolean\n}\n\nexport const create = async ({\n projectName,\n template,\n cursorEnabled,\n context,\n skipRedis = false,\n}: Args): Promise<void> => {\n console.log(\n '\\n\\n' +\n `\n _____ ______ ______ ______\n /'\\\\_/\\`\\\\/\\\\ __\\`\\\\/\\\\__ _\\\\/\\\\__ _\\\\ /\\\\ _ \\\\\n /\\\\ \\\\ \\\\ \\\\/\\\\ \\\\/_/\\\\ \\\\/\\\\/_/\\\\ \\\\/ \\\\ \\\\ \\\\L\\\\ \\\\\n \\\\ \\\\ \\\\__\\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ __ \\\\\n \\\\ \\\\ \\\\_/\\\\ \\\\ \\\\ \\\\_\\\\ \\\\ \\\\ \\\\ \\\\ \\\\_\\\\ \\\\__\\\\ \\\\ \\\\/\\\\ \\\\\n \\\\ \\\\_\\\\\\\\ \\\\_\\\\ \\\\_____\\\\ \\\\ \\\\_\\\\ /\\\\_____\\\\\\\\ \\\\_\\\\ \\\\_\\\\\n \\\\/_/ \\\\/_/\\\\/_____/ \\\\/_/ \\\\/_____/ \\\\/_/\\\\/_/\n ` +\n '\\n\\n',\n )\n\n const isCurrentDir = projectName === '.' || projectName === './' || projectName === '.\\\\'\n const rootDir = isCurrentDir ? process.cwd() : path.join(process.cwd(), projectName)\n const isPluginTemplate = template === 'plugin'\n\n process.env.REDISMS_DISABLE_POSTINSTALL = '1'\n if (!isCurrentDir && !checkIfDirectoryExists(rootDir)) {\n fs.mkdirSync(path.join(rootDir))\n context.log('directory-created', (message: Message) =>\n message.tag('success').append('Directory created ').append(projectName, 'gray'),\n )\n } else {\n context.log('directory-using', (message: Message) => message.tag('info').append('Using current directory'))\n }\n\n // Plugin template handles package.json differently (via template)\n if (!isPluginTemplate && !checkIfFileExists(rootDir, 'package.json')) {\n const finalProjectName =\n !projectName || projectName === '.' || projectName === './' || projectName === '.\\\\'\n ? path.basename(process.cwd())\n : projectName.trim()\n\n const packageJsonContent = {\n name: finalProjectName,\n description: '',\n type: 'module',\n scripts: {\n postinstall: 'motia install',\n dev: 'motia dev',\n start: 'motia start',\n 'generate-types': 'motia generate-types',\n build: 'motia build',\n clean: 'rm -rf dist node_modules python_modules .motia .mermaid',\n //'generate:config': 'motia get-config --output ./', TODO: doesnt work at the moment\n },\n keywords: ['motia'],\n }\n\n fs.writeFileSync(path.join(rootDir, 'package.json'), JSON.stringify(packageJsonContent, null, 2))\n\n context.log('package-json-created', (message: Message) =>\n message.tag('success').append('File').append('package.json', 'cyan').append('has been created.'),\n )\n } else if (!isPluginTemplate) {\n const packageJsonPath = path.join(rootDir, 'package.json')\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n if (!packageJson.scripts) {\n packageJson.scripts = {}\n }\n\n if (!packageJson.scripts.dev) {\n packageJson.scripts.dev = 'motia dev'\n } else {\n packageJson.scripts.olddev = packageJson.scripts.dev\n packageJson.scripts.dev = 'motia dev'\n context.log('dev-command-already-exists', (message: Message) =>\n message.tag('warning').append('dev command already exists in package.json'),\n )\n }\n\n fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))\n context.log('dev-command-updated', (message: Message) =>\n message\n .tag('success')\n .append('Updated')\n .append('dev', 'gray')\n .append('command to')\n .append('package.json', 'gray'),\n )\n }\n\n // Plugin template handles tsconfig.json via template\n if (!isPluginTemplate && !checkIfFileExists(rootDir, 'tsconfig.json')) {\n const tsconfigContent = {\n compilerOptions: {\n target: 'ES2020',\n module: 'ESNext',\n moduleResolution: 'bundler',\n allowImportingTsExtensions: true,\n noEmit: true,\n esModuleInterop: true,\n strict: true,\n skipLibCheck: true,\n forceConsistentCasingInFileNames: true,\n resolveJsonModule: true,\n allowJs: true,\n outDir: 'dist',\n rootDir: '.',\n baseUrl: '.',\n jsx: 'react-jsx',\n },\n include: ['**/*.ts', 'motia.config.ts', '**/*.tsx', 'types.d.ts', '**/*.jsx'],\n exclude: ['node_modules', 'dist', 'tests'],\n }\n\n fs.writeFileSync(path.join(rootDir, 'tsconfig.json'), JSON.stringify(tsconfigContent, null, 2))\n context.log('tsconfig-json-created', (message: Message) =>\n message.tag('success').append('File').append('tsconfig.json', 'cyan').append('has been created.'),\n )\n }\n\n // Plugin template handles .gitignore via template\n if (!isPluginTemplate && !checkIfFileExists(rootDir, '.gitignore')) {\n const gitignoreContent = [\n 'node_modules',\n 'python_modules',\n '.venv',\n 'venv',\n '.motia',\n '.mermaid',\n 'dist',\n '*.pyc',\n ].join('\\n')\n\n fs.writeFileSync(path.join(rootDir, '.gitignore'), gitignoreContent)\n context.log('gitignore-created', (message: Message) =>\n message.tag('success').append('File').append('.gitignore', 'cyan').append('has been created.'),\n )\n }\n\n // Skip cursor rules for plugin template\n if (!isPluginTemplate && cursorEnabled) {\n await pullRules({ force: true, rootDir }, context)\n }\n\n if (template) {\n await setupTemplate(template, rootDir, context)\n }\n\n if (!isPluginTemplate && skipRedis) {\n const motiaConfigPath = path.join(rootDir, 'motia.config.ts')\n\n const templatePath = path.join(__dirname, 'templates/motia.config.external-redis.ts.txt')\n const templateContent = fs.readFileSync(templatePath, 'utf-8')\n fs.writeFileSync(motiaConfigPath, templateContent)\n context.log('motia-config-created', (message: Message) =>\n message.tag('success').append('File').append('motia.config.ts', 'cyan').append('has been created.'),\n )\n }\n\n let packageManager: string\n if (!isPluginTemplate) {\n packageManager = await installNodeDependencies(rootDir, context)\n\n if (template.includes('python') || template.includes('multilang')) {\n await pythonInstall({ baseDir: rootDir })\n }\n\n await generateTypes(rootDir)\n } else {\n packageManager = await preparePackageManager(rootDir, context, true)\n\n context.log('installing-plugin-dependencies', (message: Message) =>\n message.tag('info').append('Installing plugin dependencies...'),\n )\n\n const installCommand = {\n npm: 'npm install',\n yarn: 'yarn',\n pnpm: 'pnpm install',\n bun: 'bun install',\n }[packageManager]\n\n try {\n await executeCommand(installCommand!, rootDir)\n context.log('plugin-dependencies-installed', (message: Message) =>\n message.tag('success').append('Plugin dependencies installed'),\n )\n } catch (error) {\n context.log('failed-to-install-plugin-dependencies', (message: Message) =>\n message.tag('failed').append('Failed to install plugin dependencies'),\n )\n console.error(error)\n }\n }\n\n const projectDirName = path.basename(rootDir)\n const devCommand = `${packageManager} run dev`\n const port = 3000\n const cdCommand = isCurrentDir ? '' : `${pc.cyan(`cd ${projectDirName}`)}\\n `\n\n context.log('success-blank', (message) => message.text(''))\n context.log('success-header', (message) =>\n message.text(`${pc.green('✨')} ${pc.bold('All set! Your project is ready to go.')}`),\n )\n context.log('success-blank-2', (message) => message.text(''))\n context.log('success-get-started', (message) => message.text('Get started:'))\n context.log('success-blank-3', (message) => message.text(''))\n context.log('success-commands', (message) => message.text(` ${cdCommand}${pc.cyan(devCommand)}`))\n context.log('success-blank-4', (message) => message.text(''))\n context.log('success-open', (message) => message.text(`Then open ${pc.cyan(`http://localhost:${port}`)}`))\n context.log('success-blank-5', (message: Message) => message.text(''))\n context.log('success-docs', (message) => message.text(`Docs: ${pc.cyan('https://www.motia.dev/docs')}`))\n context.log('success-blank-6', (message) => message.text(''))\n if (skipRedis) {\n context.log('redis-skip-warning', (message: Message) =>\n message\n .tag('warning')\n .append(\n '⚠️ You skipped Redis binary installation. Make sure to provide a Redis connection before running Motia.',\n ),\n )\n context.log('success-blank-7', (message) => message.text(''))\n }\n context.log('success-signoff', (message) => message.text('Happy coding! 🚀'))\n context.log('success-blank-8', (message) => message.text(''))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["packageManager: string"],"sources":["../../src/create/index.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport pc from 'picocolors'\nimport { fileURLToPath } from 'url'\nimport type { CliContext, Message } from '../cloud/config-utils'\nimport { generateTypes } from '../generate-types'\nimport { pythonInstall } from '../install'\nimport { pluginDependencies } from '../plugins/plugin-dependencies'\nimport { executeCommand } from '../utils/execute-command'\nimport { getPackageManager, getPackageManagerFromEnv } from '../utils/get-package-manager'\nimport { version } from '../version'\nimport { pullRules } from './pull-rules'\nimport { setupTemplate } from './setup-template'\nimport { checkIfDirectoryExists, checkIfFileExists } from './utils'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\n\nconst installRequiredDependencies = async (packageManager: string, rootDir: string, context: CliContext) => {\n context.log('installing-dependencies', (message: Message) => message.tag('info').append('Installing dependencies...'))\n\n const installCommand = {\n npm: 'npm install --save',\n yarn: 'yarn add',\n pnpm: 'pnpm add',\n bun: 'bun add',\n }[packageManager]\n\n const dependencies = [\n `motia@${version}`,\n 'zod@4.1.12',\n `@motiadev/adapter-bullmq-events@${version}`,\n ...pluginDependencies.map((dep: string) => `${dep}@${version}`),\n ].join(' ')\n\n const devDependencies = [\n 'ts-node@10.9.2',\n 'typescript@5.7.3',\n '@types/react@19.1.1',\n `@motiadev/workbench@${version}`,\n ].join(' ')\n\n try {\n await executeCommand(`${installCommand} ${dependencies}`, rootDir)\n await executeCommand(`${installCommand} -D ${devDependencies}`, rootDir)\n\n context.log('dependencies-installed', (message: Message) => message.tag('success').append('Dependencies installed'))\n } catch (error) {\n console.error('❌ Failed to install dependencies:', error)\n }\n}\n\nconst preparePackageManager = async (rootDir: string, context: CliContext, detectFromParent = false) => {\n const detectionDir = detectFromParent ? process.cwd() : rootDir\n const envPackageManager = getPackageManagerFromEnv()\n const packageManager = getPackageManager(detectionDir)\n\n const isFallback =\n !envPackageManager && packageManager === 'npm' && !checkIfFileExists(detectionDir, 'package-lock.json')\n\n if (isFallback) {\n context.log('package-manager-using-default', (message: Message) =>\n message.tag('info').append('Using default package manager').append(packageManager, 'gray'),\n )\n } else {\n context.log('package-manager-detected', (message: Message) =>\n message.tag('info').append('Detected package manager').append(packageManager, 'gray'),\n )\n }\n\n return packageManager\n}\n\nconst installNodeDependencies = async (rootDir: string, context: CliContext) => {\n const packageManager = await preparePackageManager(rootDir, context)\n\n await installRequiredDependencies(packageManager, rootDir, context).catch((error: unknown) => {\n context.log('failed-to-install-dependencies', (message: Message) =>\n message.tag('failed').append('Failed to install dependencies'),\n )\n console.error(error)\n })\n\n return packageManager\n}\n\ntype Args = {\n projectName: string\n template: string\n cursorEnabled: boolean\n context: CliContext\n skipTutorialTemplates?: boolean\n skipRedis?: boolean\n}\n\nexport const create = async ({\n projectName,\n template,\n cursorEnabled,\n context,\n skipRedis = false,\n}: Args): Promise<void> => {\n console.log(\n '\\n\\n' +\n `\n _____ ______ ______ ______\n /'\\\\_/\\`\\\\/\\\\ __\\`\\\\/\\\\__ _\\\\/\\\\__ _\\\\ /\\\\ _ \\\\\n /\\\\ \\\\ \\\\ \\\\/\\\\ \\\\/_/\\\\ \\\\/\\\\/_/\\\\ \\\\/ \\\\ \\\\ \\\\L\\\\ \\\\\n \\\\ \\\\ \\\\__\\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ __ \\\\\n \\\\ \\\\ \\\\_/\\\\ \\\\ \\\\ \\\\_\\\\ \\\\ \\\\ \\\\ \\\\ \\\\_\\\\ \\\\__\\\\ \\\\ \\\\/\\\\ \\\\\n \\\\ \\\\_\\\\\\\\ \\\\_\\\\ \\\\_____\\\\ \\\\ \\\\_\\\\ /\\\\_____\\\\\\\\ \\\\_\\\\ \\\\_\\\\\n \\\\/_/ \\\\/_/\\\\/_____/ \\\\/_/ \\\\/_____/ \\\\/_/\\\\/_/\n ` +\n '\\n\\n',\n )\n\n const isCurrentDir = projectName === '.' || projectName === './' || projectName === '.\\\\'\n const rootDir = isCurrentDir ? process.cwd() : path.join(process.cwd(), projectName)\n const isPluginTemplate = template === 'plugin'\n\n process.env.REDISMS_DISABLE_POSTINSTALL = '1'\n if (!isCurrentDir && !checkIfDirectoryExists(rootDir)) {\n fs.mkdirSync(path.join(rootDir))\n context.log('directory-created', (message: Message) =>\n message.tag('success').append('Directory created ').append(projectName, 'gray'),\n )\n } else {\n context.log('directory-using', (message: Message) => message.tag('info').append('Using current directory'))\n }\n\n // Plugin template handles package.json differently (via template)\n if (!isPluginTemplate && !checkIfFileExists(rootDir, 'package.json')) {\n const finalProjectName =\n !projectName || projectName === '.' || projectName === './' || projectName === '.\\\\'\n ? path.basename(process.cwd())\n : projectName.trim()\n\n const packageJsonContent = {\n name: finalProjectName,\n description: '',\n type: 'module',\n scripts: {\n postinstall: 'motia install',\n dev: 'motia dev',\n start: 'motia start',\n 'generate-types': 'motia generate-types',\n build: 'motia build',\n clean: 'rm -rf dist node_modules python_modules .motia .mermaid',\n //'generate:config': 'motia get-config --output ./', TODO: doesnt work at the moment\n },\n keywords: ['motia'],\n }\n\n fs.writeFileSync(path.join(rootDir, 'package.json'), JSON.stringify(packageJsonContent, null, 2))\n\n context.log('package-json-created', (message: Message) =>\n message.tag('success').append('File').append('package.json', 'cyan').append('has been created.'),\n )\n } else if (!isPluginTemplate) {\n const packageJsonPath = path.join(rootDir, 'package.json')\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n if (!packageJson.scripts) {\n packageJson.scripts = {}\n }\n\n if (!packageJson.scripts.dev) {\n packageJson.scripts.dev = 'motia dev'\n } else {\n packageJson.scripts.olddev = packageJson.scripts.dev\n packageJson.scripts.dev = 'motia dev'\n context.log('dev-command-already-exists', (message: Message) =>\n message.tag('warning').append('dev command already exists in package.json'),\n )\n }\n\n fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))\n context.log('dev-command-updated', (message: Message) =>\n message\n .tag('success')\n .append('Updated')\n .append('dev', 'gray')\n .append('command to')\n .append('package.json', 'gray'),\n )\n }\n\n // Plugin template handles tsconfig.json via template\n if (!isPluginTemplate && !checkIfFileExists(rootDir, 'tsconfig.json')) {\n const tsconfigContent = {\n compilerOptions: {\n target: 'ES2020',\n module: 'ESNext',\n moduleResolution: 'bundler',\n allowImportingTsExtensions: true,\n noEmit: true,\n esModuleInterop: true,\n strict: true,\n skipLibCheck: true,\n forceConsistentCasingInFileNames: true,\n resolveJsonModule: true,\n allowJs: true,\n outDir: 'dist',\n rootDir: '.',\n baseUrl: '.',\n jsx: 'react-jsx',\n },\n include: ['**/*.ts', 'motia.config.ts', '**/*.tsx', 'types.d.ts', '**/*.jsx'],\n exclude: ['node_modules', 'dist', 'tests'],\n }\n\n fs.writeFileSync(path.join(rootDir, 'tsconfig.json'), JSON.stringify(tsconfigContent, null, 2))\n context.log('tsconfig-json-created', (message: Message) =>\n message.tag('success').append('File').append('tsconfig.json', 'cyan').append('has been created.'),\n )\n }\n\n // Plugin template handles .gitignore via template\n if (!isPluginTemplate && !checkIfFileExists(rootDir, '.gitignore')) {\n const gitignoreContent = [\n 'node_modules',\n 'python_modules',\n '.venv',\n 'venv',\n '.motia',\n '.mermaid',\n 'dist',\n '*.pyc',\n ].join('\\n')\n\n fs.writeFileSync(path.join(rootDir, '.gitignore'), gitignoreContent)\n context.log('gitignore-created', (message: Message) =>\n message.tag('success').append('File').append('.gitignore', 'cyan').append('has been created.'),\n )\n }\n\n // Skip cursor rules for plugin template\n if (!isPluginTemplate && cursorEnabled) {\n await pullRules({ force: true, rootDir }, context)\n }\n\n if (template) {\n await setupTemplate(template, rootDir, context)\n }\n\n if (!isPluginTemplate && skipRedis) {\n const motiaConfigPath = path.join(rootDir, 'motia.config.ts')\n\n const templatePath = path.join(__dirname, 'templates/motia.config.external-redis.ts.txt')\n const templateContent = fs.readFileSync(templatePath, 'utf-8')\n fs.writeFileSync(motiaConfigPath, templateContent)\n context.log('motia-config-created', (message: Message) =>\n message.tag('success').append('File').append('motia.config.ts', 'cyan').append('has been created.'),\n )\n }\n\n let packageManager: string\n if (!isPluginTemplate) {\n packageManager = await installNodeDependencies(rootDir, context)\n\n if (template.includes('python') || template.includes('multilang')) {\n await pythonInstall({ baseDir: rootDir })\n }\n\n await generateTypes(rootDir)\n } else {\n packageManager = await preparePackageManager(rootDir, context, true)\n\n context.log('installing-plugin-dependencies', (message: Message) =>\n message.tag('info').append('Installing plugin dependencies...'),\n )\n\n const installCommand = {\n npm: 'npm install',\n yarn: 'yarn',\n pnpm: 'pnpm install',\n bun: 'bun install',\n }[packageManager]\n\n try {\n await executeCommand(installCommand!, rootDir)\n context.log('plugin-dependencies-installed', (message: Message) =>\n message.tag('success').append('Plugin dependencies installed'),\n )\n } catch (error) {\n context.log('failed-to-install-plugin-dependencies', (message: Message) =>\n message.tag('failed').append('Failed to install plugin dependencies'),\n )\n console.error(error)\n }\n }\n\n const projectDirName = path.basename(rootDir)\n const devCommand = `${packageManager} run dev`\n const port = 3000\n const cdCommand = isCurrentDir ? '' : `${pc.cyan(`cd ${projectDirName}`)}\\n `\n\n context.log('success-blank', (message) => message.text(''))\n context.log('success-header', (message) =>\n message.text(`${pc.green('✨')} ${pc.bold('All set! Your project is ready to go.')}`),\n )\n context.log('success-blank-2', (message) => message.text(''))\n context.log('success-get-started', (message) => message.text('Get started:'))\n context.log('success-blank-3', (message) => message.text(''))\n context.log('success-commands', (message) => message.text(` ${cdCommand}${pc.cyan(devCommand)}`))\n context.log('success-blank-4', (message) => message.text(''))\n context.log('success-open', (message) => message.text(`Then open ${pc.cyan(`http://localhost:${port}`)}`))\n context.log('success-blank-5', (message: Message) => message.text(''))\n context.log('success-docs', (message) => message.text(`Docs: ${pc.cyan('https://www.motia.dev/docs')}`))\n context.log('success-blank-6', (message) => message.text(''))\n if (skipRedis) {\n context.log('redis-skip-warning', (message: Message) =>\n message\n .tag('warning')\n .append(\n '⚠️ You skipped Redis binary installation. Make sure to provide a Redis connection before running Motia.',\n ),\n )\n context.log('success-blank-7', (message) => message.text(''))\n }\n context.log('success-signoff', (message) => message.text('Happy coding! 🚀'))\n context.log('success-blank-8', (message) => message.text(''))\n}\n"],"mappings":"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"}
|
|
@@ -44,12 +44,7 @@ export const steps: TutorialStep[] = [
|
|
|
44
44
|
{
|
|
45
45
|
elementXpath: workbenchXPath.flows.previewButton('apitrigger'),
|
|
46
46
|
title: 'Code Preview',
|
|
47
|
-
description: () =>
|
|
48
|
-
<p>
|
|
49
|
-
Clicking on this icon will allow you to visualize the source code for a given Step. This opens a code viewer
|
|
50
|
-
with interactive feature cards that explain different parts of the code.
|
|
51
|
-
</p>
|
|
52
|
-
),
|
|
47
|
+
description: () => <p>Clicking on this icon will allow you to visualize the source code for a given Step.</p>,
|
|
53
48
|
before: [
|
|
54
49
|
{
|
|
55
50
|
type: 'click',
|
|
@@ -60,46 +55,142 @@ export const steps: TutorialStep[] = [
|
|
|
60
55
|
},
|
|
61
56
|
{
|
|
62
57
|
elementXpath: workbenchXPath.sidebarContainer,
|
|
63
|
-
title: '
|
|
58
|
+
title: 'Step Config',
|
|
64
59
|
description: () => (
|
|
65
60
|
<div>
|
|
66
61
|
<p>
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
All Steps are defined by two main components, the <b>configuration</b> and the <b>handler</b>.
|
|
63
|
+
<br />
|
|
64
|
+
<br />
|
|
65
|
+
Let's start with the configuration, the common config attributes are
|
|
66
|
+
<i> type, name, description, and flows</i>.
|
|
67
|
+
<br />
|
|
68
|
+
<br />
|
|
69
69
|
</p>
|
|
70
|
-
<
|
|
71
|
-
<p>
|
|
72
|
-
<b>Click on the feature cards</b> to learn about:
|
|
73
|
-
</p>
|
|
74
|
-
<ul className="square-decoration">
|
|
75
|
-
<li>
|
|
76
|
-
<b>Step Configuration</b> - Common attributes like type, name, description, and flows
|
|
77
|
-
</li>
|
|
78
|
-
<li>
|
|
79
|
-
<b>API Step Configuration</b> - HTTP method and path attributes
|
|
80
|
-
</li>
|
|
70
|
+
<ul>
|
|
81
71
|
<li>
|
|
82
|
-
<b>
|
|
72
|
+
The <b>type</b> attribute is important since it declares the type of Step
|
|
83
73
|
</li>
|
|
84
74
|
<li>
|
|
85
|
-
<b>
|
|
75
|
+
The <b>flows</b> attribute will associate your Step with a given flow or set of flows.
|
|
86
76
|
</li>
|
|
87
77
|
<li>
|
|
88
|
-
<b>
|
|
89
|
-
|
|
90
|
-
<li>
|
|
91
|
-
<b>Logger</b> - Enhanced logging utilities for observability
|
|
92
|
-
</li>
|
|
93
|
-
<li>
|
|
94
|
-
<b>HTTP Response</b> - Returning responses that match your responseSchema
|
|
78
|
+
The <b>name</b> and <b>description</b> attributes will provide context in the visualization and
|
|
79
|
+
observability tools.
|
|
95
80
|
</li>
|
|
96
81
|
</ul>
|
|
97
|
-
<br />
|
|
98
|
-
<p>Take your time exploring these features. Click <b>Continue</b> when you're ready to move on.</p>
|
|
99
82
|
</div>
|
|
100
83
|
),
|
|
101
|
-
before: [
|
|
84
|
+
before: [
|
|
85
|
+
{ type: 'click', selector: workbenchXPath.flows.previewButton('apitrigger') },
|
|
86
|
+
{ type: 'click', selector: workbenchXPath.flows.feature('step-configuration') },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
91
|
+
title: 'API Step Configuration',
|
|
92
|
+
description: () => (
|
|
93
|
+
<p>
|
|
94
|
+
There are specific configuration attributes for an API Step. Let's start with the <b>method</b> attribute. This
|
|
95
|
+
will declare the type of HTTP method used to talk to your API Step.
|
|
96
|
+
<br />
|
|
97
|
+
<br />
|
|
98
|
+
Through the <b>path</b> attribute you'll declare the url path used to trigger your API Step
|
|
99
|
+
</p>
|
|
100
|
+
),
|
|
101
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('api-configuration') }],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
105
|
+
title: 'Request Body',
|
|
106
|
+
link: 'https://zod.dev/api',
|
|
107
|
+
description: () => (
|
|
108
|
+
<p>
|
|
109
|
+
The <b>bodySchema</b> attribute will define the shape of the request body.
|
|
110
|
+
<br />
|
|
111
|
+
<br />
|
|
112
|
+
<i>💡 Both the request body and response payload are defined by zod schemas</i>
|
|
113
|
+
</p>
|
|
114
|
+
),
|
|
115
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('request-body') }],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
119
|
+
title: 'Response Payload',
|
|
120
|
+
link: 'https://zod.dev/api',
|
|
121
|
+
description: () => (
|
|
122
|
+
<p>
|
|
123
|
+
Through the <b>responseSchema</b> attribute you can declare the different type of http responses based on the
|
|
124
|
+
http status code.
|
|
125
|
+
<br />
|
|
126
|
+
<br />
|
|
127
|
+
<i>💡 Both the request body and response payload are defined by zod schemas</i>
|
|
128
|
+
</p>
|
|
129
|
+
),
|
|
130
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('response-payload') }],
|
|
102
131
|
},
|
|
132
|
+
{
|
|
133
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
134
|
+
title: 'Event Driven Architecture',
|
|
135
|
+
description: () => (
|
|
136
|
+
<p>
|
|
137
|
+
Motia allows you to interact between Steps or flows through an event driven architecture.
|
|
138
|
+
<br />
|
|
139
|
+
<br />
|
|
140
|
+
In order to connect your Steps during runtime you will use the <b>emits</b> and <b>subscribes</b> attributes.
|
|
141
|
+
<br />
|
|
142
|
+
<br />
|
|
143
|
+
Through the <b>emits</b>, you can specify a list of topics that your Step emits for others to <i>subscribe</i>.
|
|
144
|
+
</p>
|
|
145
|
+
),
|
|
146
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('event-driven-architecture') }],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
150
|
+
title: 'Step Handler',
|
|
151
|
+
description: () => (
|
|
152
|
+
<p>
|
|
153
|
+
Now that we've covered how to declare a Step, let's dive into the <b>Step Handler</b>.<br />
|
|
154
|
+
<br />
|
|
155
|
+
Handlers are essential for the execution of your Step. For API Steps, the handler will receive the request
|
|
156
|
+
object as the first argument, followed by a second argument that provides access to the <b>logger</b>,{' '}
|
|
157
|
+
<b>event emitter</b>, <b>state manager</b>, and <b>trace id</b>.<br />
|
|
158
|
+
<br />
|
|
159
|
+
💡 We will cover these in depth further down the tutorial.
|
|
160
|
+
</p>
|
|
161
|
+
),
|
|
162
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
166
|
+
title: 'Logger',
|
|
167
|
+
description: () => (
|
|
168
|
+
<p>
|
|
169
|
+
We recommend using the provided <b>logger</b> util in order to guarantee observability through Motia's
|
|
170
|
+
ecosystem.
|
|
171
|
+
<br />
|
|
172
|
+
<br />
|
|
173
|
+
You can use logger similar to <i>console.log</i> for js or <i>print</i> for python, but with enhanced utilities,
|
|
174
|
+
such as being able to provide additional context.
|
|
175
|
+
<br />
|
|
176
|
+
<br />
|
|
177
|
+
Motia will take care of the rest to provide the best experience to visualize your logs and tie them through
|
|
178
|
+
tracing.
|
|
179
|
+
</p>
|
|
180
|
+
),
|
|
181
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('logger') }],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
185
|
+
title: 'HTTP Response',
|
|
186
|
+
description: () => (
|
|
187
|
+
<p>
|
|
188
|
+
Now let's wrap our API Step and return a response.
|
|
189
|
+
<br />
|
|
190
|
+
You simply need to return an object that complies with one of the <b>responseSchema</b> definitions declared in
|
|
191
|
+
your Step configuration.
|
|
192
|
+
</p>
|
|
193
|
+
),
|
|
103
194
|
before: [{ type: 'click', selector: workbenchXPath.flows.feature('http-response') }],
|
|
104
195
|
},
|
|
105
196
|
|
|
@@ -129,39 +220,77 @@ export const steps: TutorialStep[] = [
|
|
|
129
220
|
},
|
|
130
221
|
{
|
|
131
222
|
elementXpath: workbenchXPath.sidebarContainer,
|
|
132
|
-
title: '
|
|
223
|
+
title: 'Event Step',
|
|
133
224
|
link: 'https://www.motia.dev/docs/concepts/steps#triggers-event',
|
|
134
225
|
description: () => (
|
|
135
|
-
<
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
</
|
|
226
|
+
<p>
|
|
227
|
+
Now that we have an entry point in our flow, let's focus on subscribing to a <b>topic</b> and performing a
|
|
228
|
+
specific task.
|
|
229
|
+
<br /> <br />
|
|
230
|
+
For this we will look at the <b>Event</b> Step.
|
|
231
|
+
<br /> <br />
|
|
232
|
+
<b> Event</b> Steps are essential for Motia's event driven architecture. Let's dive deeper into the anatomy of
|
|
233
|
+
an Event Step by taking a look at the code visualization tool.
|
|
234
|
+
<br /> <br />
|
|
235
|
+
💡 <b>Event</b> Steps can only be triggered internally, through topic subscriptions.
|
|
236
|
+
</p>
|
|
237
|
+
),
|
|
238
|
+
before: [
|
|
239
|
+
{ type: 'click', selector: workbenchXPath.flows.previewButton('processfoodorder') },
|
|
240
|
+
{ type: 'click', selector: workbenchXPath.flows.feature('step-configuration') },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
245
|
+
title: 'Event Step Input',
|
|
246
|
+
description: () => (
|
|
247
|
+
<p>
|
|
248
|
+
<b>Event</b> Steps, like other Steps types, are composed of a configuration and a handler.
|
|
140
249
|
<br />
|
|
141
|
-
<p>
|
|
142
|
-
<b>Click on the feature cards</b> to learn about:
|
|
143
|
-
</p>
|
|
144
|
-
<ul className="square-decoration">
|
|
145
|
-
<li>
|
|
146
|
-
<b>Step Configuration</b> - Common attributes for Event Steps
|
|
147
|
-
</li>
|
|
148
|
-
<li>
|
|
149
|
-
<b>Input Schema</b> - The data structure provided by the topic (defined as a zod schema)
|
|
150
|
-
</li>
|
|
151
|
-
<li>
|
|
152
|
-
<b>Event Step Handler</b> - The handler receives topic data as the first argument
|
|
153
|
-
</li>
|
|
154
|
-
<li>
|
|
155
|
-
<b>State Management</b> - How to persist data in state with group IDs
|
|
156
|
-
</li>
|
|
157
|
-
</ul>
|
|
158
250
|
<br />
|
|
159
|
-
<
|
|
251
|
+
<b>Event</b> Steps have a specific attribute from their config, the <b>input</b> attribute, which declares the
|
|
252
|
+
data structure provided by the topic it is subscribed to.
|
|
160
253
|
<br />
|
|
161
|
-
<
|
|
162
|
-
|
|
254
|
+
<br />
|
|
255
|
+
The <b>input</b> attributes is defined as a zod schema, think of the <b>input</b> attributes as a contract for
|
|
256
|
+
other Steps that emit the topics that your Step subscribes to.
|
|
257
|
+
<br />
|
|
258
|
+
<br /> 💡 <b>Multiple Steps can subscribe to the same topic, but their input schema must be the same.</b>
|
|
259
|
+
</p>
|
|
163
260
|
),
|
|
164
|
-
before: [{ type: 'click', selector: workbenchXPath.flows.
|
|
261
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('input-schema') }],
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
265
|
+
title: 'Event Step Handler',
|
|
266
|
+
description: () => (
|
|
267
|
+
<p>
|
|
268
|
+
Let's take a look at the <b>Event</b> Step Handler.
|
|
269
|
+
<br />
|
|
270
|
+
<br />
|
|
271
|
+
The handler will seem familiar to other Step handlers, but notice that the first argument holds the data
|
|
272
|
+
provided for the topic or topics your Step subscribes to.
|
|
273
|
+
<br />
|
|
274
|
+
<br />
|
|
275
|
+
💡 The first argument will match the structure of your input schema, defined in the <b>Event</b> Step config.
|
|
276
|
+
</p>
|
|
277
|
+
),
|
|
278
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
282
|
+
title: 'Storing Data in State',
|
|
283
|
+
link: 'https://www.motia.dev/docs/development-guide/state-management',
|
|
284
|
+
description: () => (
|
|
285
|
+
<p>
|
|
286
|
+
Let's take a closer look at storing data in state.
|
|
287
|
+
<br />
|
|
288
|
+
<br />
|
|
289
|
+
In this example we are persisting the result of a third party HTTP request in <b>State</b>, scoping it to a
|
|
290
|
+
group id named "orders".
|
|
291
|
+
</p>
|
|
292
|
+
),
|
|
293
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('state') }],
|
|
165
294
|
},
|
|
166
295
|
|
|
167
296
|
// Cron Steps
|
|
@@ -184,34 +313,41 @@ export const steps: TutorialStep[] = [
|
|
|
184
313
|
},
|
|
185
314
|
{
|
|
186
315
|
elementXpath: workbenchXPath.sidebarContainer,
|
|
187
|
-
title: '
|
|
316
|
+
title: 'Cron Schedule',
|
|
188
317
|
link: 'https://www.motia.dev/docs/concepts/steps#triggers-cron',
|
|
189
318
|
description: () => (
|
|
190
|
-
<
|
|
191
|
-
<
|
|
192
|
-
<b>CRON</b> Steps are similar to other Step types - they have a configuration and a handler. The key difference
|
|
193
|
-
is the <b>cron</b> attribute that defines the schedule.
|
|
194
|
-
</p>
|
|
319
|
+
<p>
|
|
320
|
+
<b>CRON</b> Steps are similar to the other Step types, they are composed by a configuration and a handler.
|
|
195
321
|
<br />
|
|
196
|
-
<p>
|
|
197
|
-
<b>Click on the feature cards</b> to learn about:
|
|
198
|
-
</p>
|
|
199
|
-
<ul className="square-decoration">
|
|
200
|
-
<li>
|
|
201
|
-
<b>Cron Configuration</b> - How to define the cron schedule (e.g., every 5 minutes)
|
|
202
|
-
</li>
|
|
203
|
-
<li>
|
|
204
|
-
<b>Cron Step Handler</b> - Receives only the Motia context, giving access to emit topics, log, manage state,
|
|
205
|
-
and trace ID
|
|
206
|
-
</li>
|
|
207
|
-
</ul>
|
|
208
322
|
<br />
|
|
209
|
-
<
|
|
323
|
+
The <b>CRON</b> Step config has a distinct attribute, the <b>cron</b> attribute, through this attribute you will
|
|
324
|
+
define the cron schedule for your Step.
|
|
210
325
|
<br />
|
|
211
|
-
<
|
|
212
|
-
|
|
326
|
+
<br />
|
|
327
|
+
For instance, in this example the cron schedule is configured to execute the Step handler every 5 minutes. Let's
|
|
328
|
+
take a look at the handler definition.
|
|
329
|
+
</p>
|
|
330
|
+
),
|
|
331
|
+
before: [
|
|
332
|
+
{ type: 'click', selector: workbenchXPath.flows.previewButton('stateauditjob') },
|
|
333
|
+
{ type: 'click', selector: workbenchXPath.flows.feature('cron-configuration') },
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
338
|
+
title: 'Cron Step Handler',
|
|
339
|
+
description: () => (
|
|
340
|
+
<p>
|
|
341
|
+
The <b>CRON</b> Step handler only receives one argument, which is the Motia context, if you recall the Motia
|
|
342
|
+
context gives you access to utilities to emit <i>topics</i>, <i>log</i>, <i>manage state</i>, and it provides
|
|
343
|
+
the <i>trace id</i> associated to your Step's execution.
|
|
344
|
+
<br />
|
|
345
|
+
<br />
|
|
346
|
+
In this CRON Step example we are evaluating orders persisted in state, and emitting warnings through a topic for
|
|
347
|
+
each order that hasn't been processed and has a shipping date in the past.
|
|
348
|
+
</p>
|
|
213
349
|
),
|
|
214
|
-
before: [{ type: 'click', selector: workbenchXPath.flows.
|
|
350
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
|
|
215
351
|
},
|
|
216
352
|
|
|
217
353
|
// Endpoints
|
|
@@ -44,12 +44,7 @@ export const steps: TutorialStep[] = [
|
|
|
44
44
|
{
|
|
45
45
|
elementXpath: workbenchXPath.flows.previewButton('apitrigger'),
|
|
46
46
|
title: 'Code Preview',
|
|
47
|
-
description: () =>
|
|
48
|
-
<p>
|
|
49
|
-
Clicking on this icon will allow you to visualize the source code for a given Step. This opens a code viewer
|
|
50
|
-
with interactive feature cards that explain different parts of the code.
|
|
51
|
-
</p>
|
|
52
|
-
),
|
|
47
|
+
description: () => <p>Clicking on this icon will allow you to visualize the source code for a given Step.</p>,
|
|
53
48
|
before: [
|
|
54
49
|
{
|
|
55
50
|
type: 'click',
|
|
@@ -60,46 +55,142 @@ export const steps: TutorialStep[] = [
|
|
|
60
55
|
},
|
|
61
56
|
{
|
|
62
57
|
elementXpath: workbenchXPath.sidebarContainer,
|
|
63
|
-
title: '
|
|
58
|
+
title: 'Step Config',
|
|
64
59
|
description: () => (
|
|
65
60
|
<div>
|
|
66
61
|
<p>
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
All Steps are defined by two main components, the <b>configuration</b> and the <b>handler</b>.
|
|
63
|
+
<br />
|
|
64
|
+
<br />
|
|
65
|
+
Let's start with the configuration, the common config attributes are
|
|
66
|
+
<i> type, name, description, and flows</i>.
|
|
67
|
+
<br />
|
|
68
|
+
<br />
|
|
69
69
|
</p>
|
|
70
|
-
<
|
|
71
|
-
<p>
|
|
72
|
-
<b>Click on the feature cards</b> to learn about:
|
|
73
|
-
</p>
|
|
74
|
-
<ul className="square-decoration">
|
|
75
|
-
<li>
|
|
76
|
-
<b>Step Configuration</b> - Common attributes like type, name, description, and flows
|
|
77
|
-
</li>
|
|
78
|
-
<li>
|
|
79
|
-
<b>API Step Configuration</b> - HTTP method and path attributes
|
|
80
|
-
</li>
|
|
70
|
+
<ul>
|
|
81
71
|
<li>
|
|
82
|
-
<b>
|
|
72
|
+
The <b>type</b> attribute is important since it declares the type of Step
|
|
83
73
|
</li>
|
|
84
74
|
<li>
|
|
85
|
-
<b>
|
|
75
|
+
The <b>flows</b> attribute will associate your Step with a given flow or set of flows.
|
|
86
76
|
</li>
|
|
87
77
|
<li>
|
|
88
|
-
<b>
|
|
89
|
-
|
|
90
|
-
<li>
|
|
91
|
-
<b>Logger</b> - Enhanced logging utilities for observability
|
|
92
|
-
</li>
|
|
93
|
-
<li>
|
|
94
|
-
<b>HTTP Response</b> - Returning responses that match your responseSchema
|
|
78
|
+
The <b>name</b> and <b>description</b> attributes will provide context in the visualization and
|
|
79
|
+
observability tools.
|
|
95
80
|
</li>
|
|
96
81
|
</ul>
|
|
97
|
-
<br />
|
|
98
|
-
<p>Take your time exploring these features. Click <b>Continue</b> when you're ready to move on.</p>
|
|
99
82
|
</div>
|
|
100
83
|
),
|
|
101
|
-
before: [
|
|
84
|
+
before: [
|
|
85
|
+
{ type: 'click', selector: workbenchXPath.flows.previewButton('apitrigger') },
|
|
86
|
+
{ type: 'click', selector: workbenchXPath.flows.feature('step-configuration') },
|
|
87
|
+
],
|
|
102
88
|
},
|
|
89
|
+
{
|
|
90
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
91
|
+
title: 'API Step Configuration',
|
|
92
|
+
description: () => (
|
|
93
|
+
<p>
|
|
94
|
+
There are specific configuration attributes for an API Step. Let's start with the <b>method</b> attribute. This
|
|
95
|
+
will declare the type of HTTP method used to talk to your API Step.
|
|
96
|
+
<br />
|
|
97
|
+
<br />
|
|
98
|
+
Through the <b>path</b> attribute you'll declare the url path used to trigger your API Step
|
|
99
|
+
</p>
|
|
100
|
+
),
|
|
101
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('api-configuration') }],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
105
|
+
title: 'Request Body',
|
|
106
|
+
link: 'https://zod.dev/api',
|
|
107
|
+
description: () => (
|
|
108
|
+
<p>
|
|
109
|
+
The <b>bodySchema</b> attribute will define the shape of the request body.
|
|
110
|
+
<br />
|
|
111
|
+
<br />
|
|
112
|
+
<i>💡 Both the request body and response payload are defined by zod schemas</i>
|
|
113
|
+
</p>
|
|
114
|
+
),
|
|
115
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('request-body') }],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
119
|
+
title: 'Response Payload',
|
|
120
|
+
link: 'https://zod.dev/api',
|
|
121
|
+
description: () => (
|
|
122
|
+
<p>
|
|
123
|
+
Through the <b>responseSchema</b> attribute you can declare the different type of http responses based on the
|
|
124
|
+
http status code.
|
|
125
|
+
<br />
|
|
126
|
+
<br />
|
|
127
|
+
<i>💡 Both the request body and response payload are defined by zod schemas</i>
|
|
128
|
+
</p>
|
|
129
|
+
),
|
|
130
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('response-payload') }],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
134
|
+
title: 'Event Driven Architecture',
|
|
135
|
+
description: () => (
|
|
136
|
+
<p>
|
|
137
|
+
Motia allows you to interact between Steps or flows through an event driven architecture.
|
|
138
|
+
<br />
|
|
139
|
+
<br />
|
|
140
|
+
In order to connect your Steps during runtime you will use the <b>emits</b> and <b>subscribes</b> attributes.
|
|
141
|
+
<br />
|
|
142
|
+
<br />
|
|
143
|
+
Through the <b>emits</b>, you can specify a list of topics that your Step emits for others to <i>subscribe</i>.
|
|
144
|
+
</p>
|
|
145
|
+
),
|
|
146
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('event-driven-architecture') }],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
150
|
+
title: 'Step Handler',
|
|
151
|
+
description: () => (
|
|
152
|
+
<p>
|
|
153
|
+
Now that we've covered how to declare a Step, let's dive into the <b>Step Handler</b>.<br />
|
|
154
|
+
<br />
|
|
155
|
+
Handlers are essential for the execution of your Step. For API Steps, the handler will receive the request
|
|
156
|
+
object as the first argument, followed by a second argument that provides access to the <b>logger</b>,{' '}
|
|
157
|
+
<b>event emitter</b>, <b>state manager</b>, and <b>trace id</b>.<br />
|
|
158
|
+
<br />
|
|
159
|
+
💡 We will cover these in depth further down the tutorial.
|
|
160
|
+
</p>
|
|
161
|
+
),
|
|
162
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
166
|
+
title: 'Logger',
|
|
167
|
+
description: () => (
|
|
168
|
+
<p>
|
|
169
|
+
We recommend using the provided <b>logger</b> util in order to guarantee observability through Motia's
|
|
170
|
+
ecosystem.
|
|
171
|
+
<br />
|
|
172
|
+
<br />
|
|
173
|
+
You can use logger similar to <i>console.log</i> for js or <i>print</i> for python, but with enhanced utilities,
|
|
174
|
+
such as being able to provide additional context.
|
|
175
|
+
<br />
|
|
176
|
+
<br />
|
|
177
|
+
Motia will take care of the rest to provide the best experience to visualize your logs and tie them through
|
|
178
|
+
tracing.
|
|
179
|
+
</p>
|
|
180
|
+
),
|
|
181
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('logger') }],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
185
|
+
title: 'HTTP Response',
|
|
186
|
+
description: () => (
|
|
187
|
+
<p>
|
|
188
|
+
Now let's wrap our API Step and return a response.
|
|
189
|
+
<br />
|
|
190
|
+
You simply need to return an object that complies with one of the <b>responseSchema</b> definitions declared in
|
|
191
|
+
your Step configuration.
|
|
192
|
+
</p>
|
|
193
|
+
),
|
|
103
194
|
before: [{ type: 'click', selector: workbenchXPath.flows.feature('http-response') }],
|
|
104
195
|
},
|
|
105
196
|
|
|
@@ -129,39 +220,77 @@ export const steps: TutorialStep[] = [
|
|
|
129
220
|
},
|
|
130
221
|
{
|
|
131
222
|
elementXpath: workbenchXPath.sidebarContainer,
|
|
132
|
-
title: '
|
|
223
|
+
title: 'Event Step',
|
|
133
224
|
link: 'https://www.motia.dev/docs/concepts/steps#triggers-event',
|
|
134
225
|
description: () => (
|
|
135
|
-
<
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
</
|
|
226
|
+
<p>
|
|
227
|
+
Now that we have an entry point in our flow, let's focus on subscribing to a <b>topic</b> and performing a
|
|
228
|
+
specific task.
|
|
229
|
+
<br /> <br />
|
|
230
|
+
For this we will look at the <b>Event</b> Step.
|
|
231
|
+
<br /> <br />
|
|
232
|
+
<b> Event</b> Steps are essential for Motia's event driven architecture. Let's dive deeper into the
|
|
233
|
+
anatomy of an Event Step by taking a look at the code visualization tool.
|
|
234
|
+
<br /> <br />
|
|
235
|
+
💡 <b>Event</b> Steps can only be triggered internally, through topic subscriptions.
|
|
236
|
+
</p>
|
|
237
|
+
),
|
|
238
|
+
before: [
|
|
239
|
+
{ type: 'click', selector: workbenchXPath.flows.previewButton('processfoodorder') },
|
|
240
|
+
{ type: 'click', selector: workbenchXPath.flows.feature('step-configuration') },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
245
|
+
title: 'Event Step Input',
|
|
246
|
+
description: () => (
|
|
247
|
+
<p>
|
|
248
|
+
<b> Event</b> Steps, like other Step types, are composed of a configuration and a handler.
|
|
140
249
|
<br />
|
|
141
|
-
<p>
|
|
142
|
-
<b>Click on the feature cards</b> to learn about:
|
|
143
|
-
</p>
|
|
144
|
-
<ul className="square-decoration">
|
|
145
|
-
<li>
|
|
146
|
-
<b>Step Configuration</b> - Common attributes for Event Steps
|
|
147
|
-
</li>
|
|
148
|
-
<li>
|
|
149
|
-
<b>Input Schema</b> - The data structure provided by the topic (defined as a zod schema)
|
|
150
|
-
</li>
|
|
151
|
-
<li>
|
|
152
|
-
<b>Event Step Handler</b> - The handler receives topic data as the first argument
|
|
153
|
-
</li>
|
|
154
|
-
<li>
|
|
155
|
-
<b>State Management</b> - How to persist data in state with group IDs
|
|
156
|
-
</li>
|
|
157
|
-
</ul>
|
|
158
250
|
<br />
|
|
159
|
-
<
|
|
251
|
+
<b>Event</b> Steps have a specific attribute from their config, the <b>input</b> attribute, which declares the
|
|
252
|
+
data structure provided by the topic it is subscribed to.
|
|
160
253
|
<br />
|
|
161
|
-
<
|
|
162
|
-
|
|
254
|
+
<br />
|
|
255
|
+
The <b>input</b> attributes is defined as a zod schema, think of the <b>input</b> attributes as a contract for
|
|
256
|
+
other Steps that emit the topics that your Step subscribes to.
|
|
257
|
+
<br />
|
|
258
|
+
<br /> 💡 <b>Multiple Steps can subscribe to the same topic, but their input schema must be the same.</b>
|
|
259
|
+
</p>
|
|
260
|
+
),
|
|
261
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('input-schema') }],
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
265
|
+
title: 'Event Step Handler',
|
|
266
|
+
description: () => (
|
|
267
|
+
<p>
|
|
268
|
+
Let's take a look at the <b>Event</b> Step Handler.
|
|
269
|
+
<br />
|
|
270
|
+
<br />
|
|
271
|
+
The handler will seem familiar to other Step handlers, but notice that the first argument holds the data
|
|
272
|
+
provided for the topic or topics your Step subscribes to.
|
|
273
|
+
<br />
|
|
274
|
+
<br />
|
|
275
|
+
💡 The first argument will match the structure of your input schema, defined in the <b>Event</b> Step config.
|
|
276
|
+
</p>
|
|
277
|
+
),
|
|
278
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
282
|
+
title: 'Storing Data in State',
|
|
283
|
+
link: 'https://www.motia.dev/docs/development-guide/state-management',
|
|
284
|
+
description: () => (
|
|
285
|
+
<p>
|
|
286
|
+
Let's take a closer look at storing data in state.
|
|
287
|
+
<br />
|
|
288
|
+
<br />
|
|
289
|
+
In this example we are persisting the result of a third party HTTP request in <b>State</b>, scoping it to a
|
|
290
|
+
group id named "orders".
|
|
291
|
+
</p>
|
|
163
292
|
),
|
|
164
|
-
before: [{ type: 'click', selector: workbenchXPath.flows.
|
|
293
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('state') }],
|
|
165
294
|
},
|
|
166
295
|
|
|
167
296
|
// Cron Steps
|
|
@@ -184,34 +313,41 @@ export const steps: TutorialStep[] = [
|
|
|
184
313
|
},
|
|
185
314
|
{
|
|
186
315
|
elementXpath: workbenchXPath.sidebarContainer,
|
|
187
|
-
title: '
|
|
316
|
+
title: 'Cron Schedule',
|
|
188
317
|
link: 'https://www.motia.dev/docs/concepts/steps#triggers-cron',
|
|
189
318
|
description: () => (
|
|
190
|
-
<
|
|
191
|
-
<
|
|
192
|
-
<b>CRON</b> Steps are similar to other Step types - they have a configuration and a handler. The key difference
|
|
193
|
-
is the <b>cron</b> attribute that defines the schedule.
|
|
194
|
-
</p>
|
|
319
|
+
<p>
|
|
320
|
+
<b>CRON</b> Steps are similar to the other Step types, they are composed by a configuration and a handler.
|
|
195
321
|
<br />
|
|
196
|
-
<p>
|
|
197
|
-
<b>Click on the feature cards</b> to learn about:
|
|
198
|
-
</p>
|
|
199
|
-
<ul className="square-decoration">
|
|
200
|
-
<li>
|
|
201
|
-
<b>Cron Configuration</b> - How to define the cron schedule (e.g., every 5 minutes)
|
|
202
|
-
</li>
|
|
203
|
-
<li>
|
|
204
|
-
<b>Cron Step Handler</b> - Receives only the Motia context, giving access to emit topics, log, manage state,
|
|
205
|
-
and trace ID
|
|
206
|
-
</li>
|
|
207
|
-
</ul>
|
|
208
322
|
<br />
|
|
209
|
-
<
|
|
323
|
+
The <b>CRON</b> Step config has a distinct attribute, the <b>cron</b> attribute, through this attribute you will
|
|
324
|
+
define the cron schedule for your Step.
|
|
210
325
|
<br />
|
|
211
|
-
<
|
|
212
|
-
|
|
326
|
+
<br />
|
|
327
|
+
For instance, in this example the cron schedule is configured to execute the Step handler every 5 minutes. Let's
|
|
328
|
+
take a look at the handler definition.
|
|
329
|
+
</p>
|
|
330
|
+
),
|
|
331
|
+
before: [
|
|
332
|
+
{ type: 'click', selector: workbenchXPath.flows.previewButton('stateauditjob') },
|
|
333
|
+
{ type: 'click', selector: workbenchXPath.flows.feature('cron-configuration') },
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
338
|
+
title: 'Cron Step Handler',
|
|
339
|
+
description: () => (
|
|
340
|
+
<p>
|
|
341
|
+
The <b>CRON</b> Step handler only receives one argument, which is the Motia context, if you recall the Motia
|
|
342
|
+
context gives you access to utilities to emit <i>topics</i>, <i>log</i>, <i>manage state</i>, and it provides
|
|
343
|
+
the <i>trace id</i> associated to your Step's execution.
|
|
344
|
+
<br />
|
|
345
|
+
<br />
|
|
346
|
+
In this CRON Step example we are evaluating orders persisted in state, and emitting warnings through a topic for
|
|
347
|
+
each order that hasn't been processed and has a shipping date in the past.
|
|
348
|
+
</p>
|
|
213
349
|
),
|
|
214
|
-
before: [{ type: 'click', selector: workbenchXPath.flows.
|
|
350
|
+
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
|
|
215
351
|
},
|
|
216
352
|
|
|
217
353
|
// Endpoints
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "motia",
|
|
3
3
|
"description": "Build production-grade backends with a single primitive. APIs, background jobs, Queues, Workflows, and AI agents - unified in one system with built-in State management, Streaming, and Observability.",
|
|
4
|
-
"version": "0.17.
|
|
4
|
+
"version": "0.17.2-beta.184-886580",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"table": "^6.9.0",
|
|
47
47
|
"ts-node": "^10.9.2",
|
|
48
48
|
"zod": "^4.1.12",
|
|
49
|
-
"@motiadev/adapter-bullmq-events": "0.17.
|
|
50
|
-
"@motiadev/adapter-redis-cron": "0.17.
|
|
51
|
-
"@motiadev/adapter-redis-
|
|
52
|
-
"@motiadev/adapter-redis-
|
|
53
|
-
"@motiadev/core": "0.17.
|
|
54
|
-
"@motiadev/stream-client-node": "0.17.
|
|
55
|
-
"@motiadev/workbench": "0.17.
|
|
49
|
+
"@motiadev/adapter-bullmq-events": "0.17.2-beta.184-886580",
|
|
50
|
+
"@motiadev/adapter-redis-cron": "0.17.2-beta.184-886580",
|
|
51
|
+
"@motiadev/adapter-redis-streams": "0.17.2-beta.184-886580",
|
|
52
|
+
"@motiadev/adapter-redis-state": "0.17.2-beta.184-886580",
|
|
53
|
+
"@motiadev/core": "0.17.2-beta.184-886580",
|
|
54
|
+
"@motiadev/stream-client-node": "0.17.2-beta.184-886580",
|
|
55
|
+
"@motiadev/workbench": "0.17.2-beta.184-886580"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@amplitude/analytics-types": "^2.9.2",
|