motia 0.15.5-beta.174-650639 → 0.15.5-beta.174-096320
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 +26 -10
- package/dist/create/index.mjs.map +1 -1
- package/dist/cursor-rules/dot-files/.claude/agents/motia-developer.md +20 -11
- package/dist/cursor-rules/dot-files/.cursor/architecture/architecture.mdc +124 -28
- package/dist/cursor-rules/dot-files/.cursor/rules/motia/motia-config.mdc +66 -0
- package/dist/cursor-rules/dot-files/AGENTS.md +31 -16
- package/dist/cursor-rules/dot-files/CLAUDE.md +7 -2
- package/dist/cursor-rules/dot-files/opencode.json +1 -0
- package/dist/plugins/install-plugin-dependencies.mjs +2 -1
- package/dist/plugins/install-plugin-dependencies.mjs.map +1 -1
- package/dist/utils/get-package-manager.mjs +32 -1
- package/dist/utils/get-package-manager.mjs.map +1 -1
- package/dist/utils/validate-python-environment.mjs +1 -4
- package/dist/utils/validate-python-environment.mjs.map +1 -1
- package/package.json +8 -8
package/dist/create/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { version } from "../version.mjs";
|
|
2
2
|
import { executeCommand } from "../utils/execute-command.mjs";
|
|
3
3
|
import { checkIfDirectoryExists, checkIfFileExists } from "./utils.mjs";
|
|
4
|
-
import { getPackageManager } from "../utils/get-package-manager.mjs";
|
|
4
|
+
import { getPackageManager, getPackageManagerFromEnv } from "../utils/get-package-manager.mjs";
|
|
5
5
|
import { generateTypes } from "../generate-types.mjs";
|
|
6
6
|
import { pythonInstall } from "../install.mjs";
|
|
7
7
|
import { pluginDependencies } from "../plugins/plugin-dependencies.mjs";
|
|
@@ -19,7 +19,8 @@ const installRequiredDependencies = async (packageManager, rootDir, context) =>
|
|
|
19
19
|
const installCommand = {
|
|
20
20
|
npm: "npm install --save",
|
|
21
21
|
yarn: "yarn add",
|
|
22
|
-
pnpm: "pnpm add"
|
|
22
|
+
pnpm: "pnpm add",
|
|
23
|
+
bun: "bun add"
|
|
23
24
|
}[packageManager];
|
|
24
25
|
const dependencies = [
|
|
25
26
|
`motia@${version}`,
|
|
@@ -40,13 +41,12 @@ const installRequiredDependencies = async (packageManager, rootDir, context) =>
|
|
|
40
41
|
console.error("❌ Failed to install dependencies:", error);
|
|
41
42
|
}
|
|
42
43
|
};
|
|
43
|
-
const preparePackageManager = async (rootDir, context) => {
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} else context.log("package-manager-using-default", (message) => message.tag("info").append("Using default package manager").append(packageManager, "gray"));
|
|
44
|
+
const preparePackageManager = async (rootDir, context, detectFromParent = false) => {
|
|
45
|
+
const detectionDir = detectFromParent ? process.cwd() : rootDir;
|
|
46
|
+
const envPackageManager = getPackageManagerFromEnv();
|
|
47
|
+
const packageManager = getPackageManager(detectionDir);
|
|
48
|
+
if (!envPackageManager && packageManager === "npm" && !checkIfFileExists(detectionDir, "package-lock.json")) context.log("package-manager-using-default", (message) => message.tag("info").append("Using default package manager").append(packageManager, "gray"));
|
|
49
|
+
else context.log("package-manager-detected", (message) => message.tag("info").append("Detected package manager").append(packageManager, "gray"));
|
|
50
50
|
return packageManager;
|
|
51
51
|
};
|
|
52
52
|
const installNodeDependencies = async (rootDir, context) => {
|
|
@@ -162,7 +162,23 @@ const create = async ({ projectName, template, cursorEnabled, context, skipRedis
|
|
|
162
162
|
packageManager = await installNodeDependencies(rootDir, context);
|
|
163
163
|
if (template.includes("python") || template.includes("multilang")) await pythonInstall({ baseDir: rootDir });
|
|
164
164
|
await generateTypes(rootDir);
|
|
165
|
-
} else
|
|
165
|
+
} else {
|
|
166
|
+
packageManager = await preparePackageManager(rootDir, context, true);
|
|
167
|
+
context.log("installing-plugin-dependencies", (message) => message.tag("info").append("Installing plugin dependencies..."));
|
|
168
|
+
const installCommand = {
|
|
169
|
+
npm: "npm install",
|
|
170
|
+
yarn: "yarn",
|
|
171
|
+
pnpm: "pnpm install",
|
|
172
|
+
bun: "bun install"
|
|
173
|
+
}[packageManager];
|
|
174
|
+
try {
|
|
175
|
+
await executeCommand(installCommand, rootDir);
|
|
176
|
+
context.log("plugin-dependencies-installed", (message) => message.tag("success").append("Plugin dependencies installed"));
|
|
177
|
+
} catch (error) {
|
|
178
|
+
context.log("failed-to-install-plugin-dependencies", (message) => message.tag("failed").append("Failed to install plugin dependencies"));
|
|
179
|
+
console.error(error);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
166
182
|
const projectDirName = path.basename(rootDir);
|
|
167
183
|
const devCommand = `${packageManager} run dev`;
|
|
168
184
|
const port = 3e3;
|
|
@@ -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 } 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 }[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) => {\n let packageManager = 'npm'\n const detectedPackageManager = getPackageManager(rootDir)\n\n if (detectedPackageManager !== 'unknown') {\n context.log('package-manager-detected', (message: Message) =>\n message.tag('info').append('Detected package manager').append(detectedPackageManager, 'gray'),\n )\n packageManager = detectedPackageManager\n } else {\n context.log('package-manager-using-default', (message: Message) =>\n message.tag('info').append('Using default 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 // For plugin template, just detect the package manager\n packageManager = await preparePackageManager(rootDir, context)\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 = ['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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"}
|
|
@@ -11,56 +11,64 @@ You are an expert Motia developer with comprehensive knowledge of all Motia patt
|
|
|
11
11
|
|
|
12
12
|
Before writing ANY Motia code, you MUST read the relevant cursor rules from `.cursor/rules/`:
|
|
13
13
|
|
|
14
|
+
### Configuration Guide (in `.cursor/rules/motia/`)
|
|
15
|
+
|
|
16
|
+
1. **`motia-config.mdc`** - Project configuration
|
|
17
|
+
- Package.json requirements (`"type": "module"`)
|
|
18
|
+
- Plugin naming conventions and setup
|
|
19
|
+
- Adapter configuration, Redis setup
|
|
20
|
+
- Stream authentication patterns
|
|
21
|
+
|
|
14
22
|
### Step Type Guides (in `.cursor/rules/motia/`)
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
2. **`api-steps.mdc`** - HTTP endpoints
|
|
17
25
|
- Creating API Steps with TypeScript, JavaScript, or Python
|
|
18
26
|
- Request/response schemas, validation, middleware
|
|
19
27
|
- When to emit events vs process directly
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
3. **`event-steps.mdc`** - Background tasks
|
|
22
30
|
- Creating Event Steps with TypeScript, JavaScript, or Python
|
|
23
31
|
- Topic subscription, event chaining, retry mechanisms
|
|
24
32
|
- Asynchronous workflow patterns
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
4. **`cron-steps.mdc`** - Scheduled tasks
|
|
27
35
|
- Creating Cron Steps with TypeScript, JavaScript, or Python
|
|
28
36
|
- Cron expression syntax, idempotent patterns
|
|
29
37
|
- When to emit events from scheduled jobs
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
5. **`state-management.mdc`** - State/cache management
|
|
32
40
|
- Using state across steps with TypeScript, JavaScript, or Python
|
|
33
41
|
- When to use state vs database
|
|
34
42
|
- TTL configuration, caching strategies
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
6. **`middlewares.mdc`** - Request/response middleware
|
|
37
45
|
- Creating middleware with TypeScript, JavaScript, or Python
|
|
38
46
|
- Authentication, validation, error handling
|
|
39
47
|
- Middleware composition patterns
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
7. **`realtime-streaming.mdc`** - Real-time data
|
|
42
50
|
- Server-Sent Events (SSE) patterns
|
|
43
51
|
- WebSocket support
|
|
44
52
|
- Stream configuration and usage
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
8. **`virtual-steps.mdc`** - Visual flow connections
|
|
47
55
|
- Creating NOOP steps for Workbench
|
|
48
56
|
- Virtual emits/subscribes for documentation
|
|
49
57
|
- Workflow visualization
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
9. **`ui-steps.mdc`** - Custom Workbench components
|
|
52
60
|
- Creating custom visual components (TypeScript/React)
|
|
53
61
|
- EventNode, ApiNode, CronNode components
|
|
54
62
|
- Styling with Tailwind
|
|
55
63
|
|
|
56
64
|
### Architecture Guides (in `.cursor/architecture/`)
|
|
57
65
|
|
|
58
|
-
|
|
66
|
+
10. **`architecture.mdc`** - Project structure
|
|
59
67
|
- File organization, naming conventions
|
|
60
68
|
- Domain-Driven Design patterns
|
|
61
69
|
- Services, repositories, utilities structure
|
|
62
70
|
|
|
63
|
-
|
|
71
|
+
11. **`error-handling.mdc`** - Error handling
|
|
64
72
|
- Custom error classes
|
|
65
73
|
- Middleware error handling
|
|
66
74
|
- ZodError/Pydantic validation errors
|
|
@@ -78,6 +86,7 @@ Before writing ANY Motia code, you MUST read the relevant cursor rules from `.cu
|
|
|
78
86
|
## Key Principles
|
|
79
87
|
|
|
80
88
|
- **All guides have TypeScript, JavaScript, and Python examples**
|
|
89
|
+
- **Steps can live in `/src` or `/steps`** - Motia discovers both (use `/src` for modern structure)
|
|
81
90
|
- **Always export `config` and `handler`**
|
|
82
91
|
- **List all emits in config before using them**
|
|
83
92
|
- **Follow naming conventions**: `*.step.ts` (TS), `*.step.js` (JS), `*_step.py` (Python)
|
|
@@ -92,4 +101,4 @@ If you're unsure about any Motia pattern:
|
|
|
92
101
|
|
|
93
102
|
---
|
|
94
103
|
|
|
95
|
-
Remember: The
|
|
104
|
+
Remember: The 11 cursor rules in `.cursor/rules/` are your source of truth. Always read them first.
|
|
@@ -8,13 +8,61 @@ alwaysApply: true
|
|
|
8
8
|
|
|
9
9
|
## Overview
|
|
10
10
|
|
|
11
|
-
This guide covers the architecture
|
|
11
|
+
This guide covers the architecture and best practices for structuring Motia projects.
|
|
12
|
+
|
|
13
|
+
**Key Takeaway**: Motia automatically discovers steps from anywhere in your project. Modern projects use `/src` for a familiar structure that works seamlessly with Domain-Driven Design.
|
|
12
14
|
|
|
13
15
|
## File Structure
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
Motia automatically discovers step files from your project. You can organize steps in either:
|
|
18
|
+
|
|
19
|
+
- **`/src` folder** (recommended) - Familiar pattern for most developers
|
|
20
|
+
- **`/steps` folder** - Traditional Motia pattern
|
|
21
|
+
- Both folders simultaneously
|
|
22
|
+
|
|
23
|
+
### Recommended Structure (using `/src`)
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
project/
|
|
27
|
+
├── src/
|
|
28
|
+
│ ├── api/ # API endpoints
|
|
29
|
+
│ │ ├── users.step.ts
|
|
30
|
+
│ │ └── orders.step.ts
|
|
31
|
+
│ ├── events/ # Event handlers
|
|
32
|
+
│ │ ├── order-processing.step.ts
|
|
33
|
+
│ │ └── notifications.step.ts
|
|
34
|
+
│ ├── cron/ # Scheduled tasks
|
|
35
|
+
│ │ └── cleanup.step.ts
|
|
36
|
+
│ ├── services/ # Business logic
|
|
37
|
+
│ ├── repositories/ # Data access
|
|
38
|
+
│ └── utils/ # Utilities
|
|
39
|
+
└── motia.config.ts
|
|
40
|
+
```
|
|
16
41
|
|
|
17
|
-
|
|
42
|
+
### Alternative Structure (using `/steps`)
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
project/
|
|
46
|
+
├── steps/
|
|
47
|
+
│ ├── api/
|
|
48
|
+
│ │ └── users.step.ts
|
|
49
|
+
│ ├── events/
|
|
50
|
+
│ │ └── order-processing.step.ts
|
|
51
|
+
│ └── cron/
|
|
52
|
+
│ └── cleanup.step.ts
|
|
53
|
+
├── src/
|
|
54
|
+
│ ├── services/
|
|
55
|
+
│ └── utils/
|
|
56
|
+
└── motia.config.ts
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Create subfolders within your chosen directory to organize related steps into logical groups (domains, features, or flows).
|
|
60
|
+
|
|
61
|
+
**Why `/src` is recommended:**
|
|
62
|
+
- Familiar to developers from other frameworks (Next.js, NestJS, etc.)
|
|
63
|
+
- Natural co-location with services, repositories, and utilities
|
|
64
|
+
- Works seamlessly with Domain-Driven Design patterns
|
|
65
|
+
- Cleaner project root with fewer top-level folders
|
|
18
66
|
|
|
19
67
|
## Step Naming Conventions
|
|
20
68
|
|
|
@@ -40,49 +88,97 @@ Underneath the `steps/` folder, create subfolders for Flows. Flows are used to g
|
|
|
40
88
|
|
|
41
89
|
## Defining Middlewares
|
|
42
90
|
|
|
43
|
-
Middleware is a powerful feature in Motia
|
|
44
|
-
|
|
91
|
+
Middleware is a powerful feature in Motia for common validation, error handling, and shared logic.
|
|
92
|
+
|
|
93
|
+
### Middleware Organization
|
|
45
94
|
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
95
|
+
Store middlewares in a dedicated folder:
|
|
96
|
+
- `/middlewares` at project root (recommended)
|
|
97
|
+
- `/src/middlewares` if using `/src` structure
|
|
98
|
+
|
|
99
|
+
### Best Practices
|
|
100
|
+
|
|
101
|
+
- **One responsibility per middleware** - Follow SOLID principles
|
|
102
|
+
- **Descriptive naming** - Use names like `auth.middleware.ts`, `validation.middleware.ts`
|
|
103
|
+
- **Handle errors gracefully** - Use core middleware for ZodError (see [Error Handling Guide](./error-handling.mdc))
|
|
104
|
+
- **Avoid infrastructure concerns** - Rate limiting and CORS are handled by infrastructure, not middleware
|
|
51
105
|
|
|
52
106
|
## Domain Driven Design
|
|
53
107
|
|
|
54
|
-
|
|
108
|
+
Motia encourages Domain-Driven Design (DDD) principles for maintainable, scalable applications.
|
|
55
109
|
|
|
56
|
-
|
|
57
|
-
- Create `/src/repositories` folder to store your repositories, this is where it holds data access logic.
|
|
58
|
-
- Create `/src/utils` folder to store your utility functions.
|
|
59
|
-
- Models and DTOs are not quite necessary, we can rely on zod to create the models and DTOs from the steps.
|
|
60
|
-
- Controller layer is the Steps, it should have mostly logic around validation and calling services.
|
|
61
|
-
- Avoid having Service methods with just a call to the Repository, it should have some logic around it, if it doesn't have, then Steps can have access to repositories directly.
|
|
110
|
+
### Folder Structure for DDD
|
|
62
111
|
|
|
63
|
-
|
|
112
|
+
When using `/src` for steps (recommended), your structure naturally supports DDD:
|
|
64
113
|
|
|
65
|
-
|
|
114
|
+
```
|
|
115
|
+
src/
|
|
116
|
+
├── api/ # API Steps (Controllers)
|
|
117
|
+
├── events/ # Event Steps (Controllers)
|
|
118
|
+
├── cron/ # Cron Steps (Controllers)
|
|
119
|
+
├── services/ # Business logic layer
|
|
120
|
+
├── repositories/ # Data access layer
|
|
121
|
+
├── utils/ # Utility functions
|
|
122
|
+
└── types/ # Shared types (optional)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Layer Responsibilities
|
|
126
|
+
|
|
127
|
+
- **Steps (Controller Layer)**: Handle validation, call services, emit events
|
|
128
|
+
- **Services**: Contain business logic, orchestrate repositories
|
|
129
|
+
- **Repositories**: Direct data access (database, external APIs)
|
|
130
|
+
- **Utils**: Pure utility functions with no side effects
|
|
66
131
|
|
|
67
|
-
|
|
68
|
-
- Create a file inside the folder called `index.ts`.
|
|
69
|
-
- Inside `index.ts`, export a constant with the name of the service, with the methods as properties.
|
|
70
|
-
- Methods should be defined as separate files, use export named functions.
|
|
71
|
-
- Use the service in the Steps.
|
|
132
|
+
### Best Practices
|
|
72
133
|
|
|
73
|
-
|
|
134
|
+
- Models and DTOs are not necessary - use Zod schemas from step configs
|
|
135
|
+
- Steps should focus on validation and calling services
|
|
136
|
+
- Avoid service methods that only call repositories - Steps can access repositories directly
|
|
137
|
+
- Keep business logic in services, not in steps
|
|
74
138
|
|
|
139
|
+
### Services
|
|
140
|
+
|
|
141
|
+
Services contain your business logic and should be organized by domain.
|
|
142
|
+
|
|
143
|
+
**Structure:**
|
|
144
|
+
```
|
|
145
|
+
src/
|
|
146
|
+
├── services/
|
|
147
|
+
│ ├── auth/
|
|
148
|
+
│ │ ├── index.ts # Export service
|
|
149
|
+
│ │ ├── login.ts # Login method
|
|
150
|
+
│ │ └── register.ts # Register method
|
|
151
|
+
│ └── orders/
|
|
152
|
+
│ ├── index.ts
|
|
153
|
+
│ └── create-order.ts
|
|
154
|
+
└── api/
|
|
155
|
+
└── auth.step.ts # Uses authService
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Service Definition (`/src/services/auth/index.ts`):**
|
|
75
159
|
```typescript
|
|
76
160
|
/**
|
|
77
|
-
* Business logic
|
|
161
|
+
* Business logic methods imported from separate files
|
|
78
162
|
*/
|
|
79
163
|
import { login } from './login'
|
|
164
|
+
import { register } from './register'
|
|
80
165
|
|
|
81
166
|
/**
|
|
82
|
-
*
|
|
167
|
+
* Export service with methods as properties
|
|
83
168
|
*/
|
|
84
169
|
export const authService = {
|
|
85
|
-
login
|
|
170
|
+
login,
|
|
171
|
+
register
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Usage in Step (`/src/api/auth.step.ts`):**
|
|
176
|
+
```typescript
|
|
177
|
+
import { authService } from '../services/auth'
|
|
178
|
+
|
|
179
|
+
export const handler = async (req, ctx) => {
|
|
180
|
+
const user = await authService.login(req.body.email, req.body.password)
|
|
181
|
+
return { status: 200, body: { user } }
|
|
86
182
|
}
|
|
87
183
|
```
|
|
88
184
|
|
|
@@ -7,6 +7,43 @@ alwaysApply: false
|
|
|
7
7
|
|
|
8
8
|
The `motia.config.ts` file is the central configuration file for your Motia application. It allows you to customize plugins, adapters, stream authentication, and Express app settings.
|
|
9
9
|
|
|
10
|
+
## Critical Requirement: package.json Must Use ES Modules
|
|
11
|
+
|
|
12
|
+
**All Motia projects MUST have `"type": "module"` in their `package.json`.**
|
|
13
|
+
|
|
14
|
+
Motia uses ES modules internally and requires this setting to function correctly. Without it, you may encounter import/export errors during runtime.
|
|
15
|
+
|
|
16
|
+
### Correct package.json Setup
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"name": "my-motia-project",
|
|
21
|
+
"description": "My Motia application",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"postinstall": "motia install",
|
|
25
|
+
"dev": "motia dev",
|
|
26
|
+
"start": "motia start",
|
|
27
|
+
"build": "motia build",
|
|
28
|
+
"generate-types": "motia generate-types"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Migration from Existing Projects
|
|
34
|
+
|
|
35
|
+
If you have an existing Motia project, ensure you add `"type": "module"` to your `package.json`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"name": "my-project",
|
|
40
|
+
"type": "module", // ← Add this line
|
|
41
|
+
"scripts": {
|
|
42
|
+
"dev": "motia dev"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
10
47
|
## Creating the Configuration File
|
|
11
48
|
|
|
12
49
|
Create a `motia.config.ts` file in the root of your project:
|
|
@@ -157,6 +194,21 @@ export default config({
|
|
|
157
194
|
|
|
158
195
|
### Creating a Local Plugin
|
|
159
196
|
|
|
197
|
+
**Project structure:**
|
|
198
|
+
```
|
|
199
|
+
project/
|
|
200
|
+
├── src/ # Steps can be in /src or /steps
|
|
201
|
+
│ └── api/
|
|
202
|
+
│ └── example.step.ts
|
|
203
|
+
├── plugins/
|
|
204
|
+
│ └── my-plugin/
|
|
205
|
+
│ ├── components/
|
|
206
|
+
│ │ └── my-plugin-panel.tsx
|
|
207
|
+
│ └── index.ts
|
|
208
|
+
└── motia.config.ts
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Plugin implementation (`plugins/my-plugin/index.ts`):**
|
|
160
212
|
```typescript
|
|
161
213
|
import path from 'node:path'
|
|
162
214
|
import { config, type MotiaPlugin, type MotiaPluginContext } from 'motia'
|
|
@@ -195,6 +247,20 @@ export default config({
|
|
|
195
247
|
})
|
|
196
248
|
```
|
|
197
249
|
|
|
250
|
+
### Common Plugin Errors
|
|
251
|
+
|
|
252
|
+
**Error: Component not found**
|
|
253
|
+
- **Cause**: `packageName` doesn't match the actual folder structure
|
|
254
|
+
- **Solution**: Ensure `packageName: '~/plugins/my-plugin'` matches `plugins/my-plugin/` folder
|
|
255
|
+
|
|
256
|
+
**Error: Plugin not loading in workbench**
|
|
257
|
+
- **Cause**: Plugin function not exported correctly
|
|
258
|
+
- **Solution**: Use `export default function` in plugin's `index.ts`
|
|
259
|
+
|
|
260
|
+
**Error: Module resolution failed**
|
|
261
|
+
- **Cause**: Using incorrect casing in folder/file names
|
|
262
|
+
- **Solution**: Use `kebab-case` for folders/files, `PascalCase` for React components
|
|
263
|
+
|
|
198
264
|
## Adapters
|
|
199
265
|
|
|
200
266
|
Adapters allow you to customize the underlying infrastructure for state management, event handling, cron jobs, and streams. This is useful for horizontal scaling or using custom storage backends.
|
|
@@ -40,6 +40,7 @@ These guides are written in markdown and can be read by any AI coding tool. The
|
|
|
40
40
|
|
|
41
41
|
Read these files in `.cursor/rules/motia/` for detailed patterns:
|
|
42
42
|
|
|
43
|
+
- **`motia-config.mdc`** - Essential project setup, package.json requirements, plugin naming
|
|
43
44
|
- **`api-steps.mdc`** - Creating HTTP endpoints with schemas, validation, and middleware
|
|
44
45
|
- **`event-steps.mdc`** - Background task processing and event-driven workflows
|
|
45
46
|
- **`cron-steps.mdc`** - Scheduled tasks with cron expressions
|
|
@@ -62,31 +63,44 @@ Architecture guides in `.cursor/architecture/`:
|
|
|
62
63
|
|
|
63
64
|
### Project Structure
|
|
64
65
|
|
|
66
|
+
Motia discovers steps from both `/src` and `/steps` folders. Modern projects typically use `/src`:
|
|
67
|
+
|
|
68
|
+
**Recommended Structure (using `/src`):**
|
|
65
69
|
```
|
|
66
70
|
project/
|
|
67
71
|
├── .cursor/rules/ # DETAILED GUIDES - Read these first!
|
|
68
|
-
├── steps/ # All step definitions
|
|
69
|
-
│ ├── api/ # API endpoints
|
|
70
|
-
│ │ └── api.step.ts
|
|
71
|
-
│ │ └── api.step.js
|
|
72
|
-
│ │ └── api_step.py
|
|
73
|
-
│ ├── events/ # Events
|
|
74
|
-
│ │ └── events.step.ts
|
|
75
|
-
│ │ └── events.step.js
|
|
76
|
-
│ │ └── events_step.py
|
|
77
|
-
│ └── cron/ # Scheduled tasks
|
|
78
|
-
│ │ └── cron.step.ts
|
|
79
|
-
│ │ └── cron.step.js
|
|
80
|
-
│ │ └── cron_step.py
|
|
81
|
-
├── middlewares/ # Reusable middleware
|
|
82
|
-
│ └── middleware.middleware.ts
|
|
83
72
|
├── src/
|
|
73
|
+
│ ├── api/ # API endpoints
|
|
74
|
+
│ │ ├── users.step.ts
|
|
75
|
+
│ │ ├── orders.step.js
|
|
76
|
+
│ │ └── products_step.py
|
|
77
|
+
│ ├── events/ # Event handlers
|
|
78
|
+
│ │ ├── order-processing.step.ts
|
|
79
|
+
│ │ └── notifications_step.py
|
|
80
|
+
│ ├── cron/ # Scheduled tasks
|
|
81
|
+
│ │ └── cleanup.step.ts
|
|
84
82
|
│ ├── services/ # Business logic
|
|
83
|
+
│ ├── repositories/ # Data access
|
|
85
84
|
│ └── utils/ # Utilities
|
|
86
|
-
├──
|
|
85
|
+
├── middlewares/ # Reusable middleware
|
|
86
|
+
│ └── auth.middleware.ts
|
|
87
|
+
├── motia.config.ts # Motia configuration
|
|
87
88
|
└── types.d.ts # Auto-generated types
|
|
88
89
|
```
|
|
89
90
|
|
|
91
|
+
**Alternative Structure (using `/steps`):**
|
|
92
|
+
```
|
|
93
|
+
project/
|
|
94
|
+
├── steps/ # Step definitions
|
|
95
|
+
│ ├── api/
|
|
96
|
+
│ ├── events/
|
|
97
|
+
│ └── cron/
|
|
98
|
+
├── src/
|
|
99
|
+
│ ├── services/
|
|
100
|
+
│ └── utils/
|
|
101
|
+
└── motia.config.ts
|
|
102
|
+
```
|
|
103
|
+
|
|
90
104
|
### Step Naming Conventions
|
|
91
105
|
|
|
92
106
|
**TypeScript/JavaScript:** `my-step.step.ts` (kebab-case)
|
|
@@ -194,6 +208,7 @@ When working on Motia projects, follow this pattern:
|
|
|
194
208
|
|
|
195
209
|
## Critical Rules
|
|
196
210
|
|
|
211
|
+
- **ALWAYS** ensure `package.json` has `"type": "module"` (read `motia-config.mdc` for details)
|
|
197
212
|
- **ALWAYS** read `.cursor/rules/` guides before writing step code
|
|
198
213
|
- **ALWAYS** run `npx motia generate-types` after modifying configs
|
|
199
214
|
- **ALWAYS** list emits in config before using them in handlers
|
|
@@ -12,7 +12,7 @@ This project has detailed development guides in **`.cursor/rules/`** directory.
|
|
|
12
12
|
|
|
13
13
|
**A pre-configured subagent is ready!**
|
|
14
14
|
|
|
15
|
-
The `motia-developer` subagent in `.claude/agents/` automatically references all
|
|
15
|
+
The `motia-developer` subagent in `.claude/agents/` automatically references all 11 cursor rules when coding.
|
|
16
16
|
|
|
17
17
|
Use it: `/agents` → select `motia-developer`
|
|
18
18
|
|
|
@@ -27,10 +27,13 @@ Read .cursor/rules/motia/api-steps.mdc and create an API endpoint
|
|
|
27
27
|
for user registration following the patterns shown.
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
## Available Guides (
|
|
30
|
+
## Available Guides (11 Comprehensive Files)
|
|
31
31
|
|
|
32
32
|
All guides in `.cursor/rules/` with **TypeScript, JavaScript, and Python** examples:
|
|
33
33
|
|
|
34
|
+
**Configuration** (`.cursor/rules/motia/`):
|
|
35
|
+
- `motia-config.mdc` - Essential project setup, package.json requirements, plugin naming
|
|
36
|
+
|
|
34
37
|
**Step Types** (`.cursor/rules/motia/`):
|
|
35
38
|
- `api-steps.mdc`, `event-steps.mdc`, `cron-steps.mdc`
|
|
36
39
|
|
|
@@ -45,6 +48,8 @@ All guides in `.cursor/rules/` with **TypeScript, JavaScript, and Python** examp
|
|
|
45
48
|
|
|
46
49
|
See `AGENTS.md` in this directory for a quick overview and links to specific guides.
|
|
47
50
|
|
|
51
|
+
**Important**: Motia discovers steps from both `/src` and `/steps` folders. Modern projects use `/src` for a familiar structure.
|
|
52
|
+
|
|
48
53
|
## Key Commands
|
|
49
54
|
|
|
50
55
|
```bash
|
|
@@ -38,7 +38,8 @@ const installPluginDependencies = async (baseDir, printer) => {
|
|
|
38
38
|
const installCommand = {
|
|
39
39
|
npm: "npm install",
|
|
40
40
|
yarn: "yarn install",
|
|
41
|
-
pnpm: "pnpm install"
|
|
41
|
+
pnpm: "pnpm install",
|
|
42
|
+
bun: "bun install"
|
|
42
43
|
}[packageManager] || "npm install";
|
|
43
44
|
try {
|
|
44
45
|
await executeCommand(installCommand, baseDir, { silent: false });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-plugin-dependencies.mjs","names":["missingDependencies: string[]"],"sources":["../../src/plugins/install-plugin-dependencies.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Printer } from '@motiadev/core'\nimport { executeCommand } from '../utils/execute-command'\nimport { getPackageManager } from '../utils/get-package-manager'\nimport { version } from '../version'\nimport { pluginDependencies } from './plugin-dependencies'\n\nexport const installPluginDependencies = async (baseDir: string, printer: Printer): Promise<void> => {\n const packageJsonPath = path.join(baseDir, 'package.json')\n\n if (!fs.existsSync(packageJsonPath)) {\n printer.printPluginWarn('No package.json found, skipping plugin dependency installation')\n return\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n if (!packageJson.dependencies) {\n packageJson.dependencies = {}\n }\n\n const missingDependencies: string[] = []\n\n for (const dep of pluginDependencies) {\n if (packageJson.devDependencies?.[dep]) {\n delete packageJson.devDependencies[dep]\n }\n\n if (!packageJson.dependencies[dep]) {\n packageJson.dependencies[dep] = version\n missingDependencies.push(dep)\n }\n }\n\n if (missingDependencies.length === 0) {\n printer.printPluginLog('All plugin dependencies already installed')\n return\n }\n\n printer.printPluginLog(`Adding missing plugin dependencies: ${missingDependencies.join(', ')}`)\n\n fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`)\n printer.printPluginLog('Updated package.json with plugin dependencies')\n\n let packageManager = getPackageManager(baseDir)\n if (packageManager === 'unknown') {\n printer.printPluginError('No package manager found, using npm as default')\n packageManager = 'npm'\n }\n printer.printPluginLog(`Installing dependencies using ${packageManager}...`)\n\n const installCommands: Record<string, string> = {\n npm: 'npm install',\n yarn: 'yarn install',\n pnpm: 'pnpm install',\n }\n\n const installCommand = installCommands[packageManager] || 'npm install'\n\n try {\n await executeCommand(installCommand, baseDir, { silent: false })\n printer.printPluginLog('Plugin dependencies installed successfully')\n } catch (error) {\n printer.printPluginError('Failed to install plugin dependencies:', error)\n printer.printPluginWarn(`Please run '${installCommand}' manually to install the dependencies`)\n }\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,4BAA4B,OAAO,SAAiB,YAAoC;CACnG,MAAM,kBAAkB,KAAK,KAAK,SAAS,eAAe;AAE1D,KAAI,CAAC,GAAG,WAAW,gBAAgB,EAAE;AACnC,UAAQ,gBAAgB,iEAAiE;AACzF;;CAGF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AAEzE,KAAI,CAAC,YAAY,aACf,aAAY,eAAe,EAAE;CAG/B,MAAMA,sBAAgC,EAAE;AAExC,MAAK,MAAM,OAAO,oBAAoB;AACpC,MAAI,YAAY,kBAAkB,KAChC,QAAO,YAAY,gBAAgB;AAGrC,MAAI,CAAC,YAAY,aAAa,MAAM;AAClC,eAAY,aAAa,OAAO;AAChC,uBAAoB,KAAK,IAAI;;;AAIjC,KAAI,oBAAoB,WAAW,GAAG;AACpC,UAAQ,eAAe,4CAA4C;AACnE;;AAGF,SAAQ,eAAe,uCAAuC,oBAAoB,KAAK,KAAK,GAAG;AAE/F,IAAG,cAAc,iBAAiB,GAAG,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC,IAAI;AAC9E,SAAQ,eAAe,gDAAgD;CAEvE,IAAI,iBAAiB,kBAAkB,QAAQ;AAC/C,KAAI,mBAAmB,WAAW;AAChC,UAAQ,iBAAiB,iDAAiD;AAC1E,mBAAiB;;AAEnB,SAAQ,eAAe,iCAAiC,eAAe,KAAK;
|
|
1
|
+
{"version":3,"file":"install-plugin-dependencies.mjs","names":["missingDependencies: string[]"],"sources":["../../src/plugins/install-plugin-dependencies.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Printer } from '@motiadev/core'\nimport { executeCommand } from '../utils/execute-command'\nimport { getPackageManager } from '../utils/get-package-manager'\nimport { version } from '../version'\nimport { pluginDependencies } from './plugin-dependencies'\n\nexport const installPluginDependencies = async (baseDir: string, printer: Printer): Promise<void> => {\n const packageJsonPath = path.join(baseDir, 'package.json')\n\n if (!fs.existsSync(packageJsonPath)) {\n printer.printPluginWarn('No package.json found, skipping plugin dependency installation')\n return\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n if (!packageJson.dependencies) {\n packageJson.dependencies = {}\n }\n\n const missingDependencies: string[] = []\n\n for (const dep of pluginDependencies) {\n if (packageJson.devDependencies?.[dep]) {\n delete packageJson.devDependencies[dep]\n }\n\n if (!packageJson.dependencies[dep]) {\n packageJson.dependencies[dep] = version\n missingDependencies.push(dep)\n }\n }\n\n if (missingDependencies.length === 0) {\n printer.printPluginLog('All plugin dependencies already installed')\n return\n }\n\n printer.printPluginLog(`Adding missing plugin dependencies: ${missingDependencies.join(', ')}`)\n\n fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`)\n printer.printPluginLog('Updated package.json with plugin dependencies')\n\n let packageManager = getPackageManager(baseDir)\n if (packageManager === 'unknown') {\n printer.printPluginError('No package manager found, using npm as default')\n packageManager = 'npm'\n }\n printer.printPluginLog(`Installing dependencies using ${packageManager}...`)\n\n const installCommands: Record<string, string> = {\n npm: 'npm install',\n yarn: 'yarn install',\n pnpm: 'pnpm install',\n bun: 'bun install',\n }\n\n const installCommand = installCommands[packageManager] || 'npm install'\n\n try {\n await executeCommand(installCommand, baseDir, { silent: false })\n printer.printPluginLog('Plugin dependencies installed successfully')\n } catch (error) {\n printer.printPluginError('Failed to install plugin dependencies:', error)\n printer.printPluginWarn(`Please run '${installCommand}' manually to install the dependencies`)\n }\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,4BAA4B,OAAO,SAAiB,YAAoC;CACnG,MAAM,kBAAkB,KAAK,KAAK,SAAS,eAAe;AAE1D,KAAI,CAAC,GAAG,WAAW,gBAAgB,EAAE;AACnC,UAAQ,gBAAgB,iEAAiE;AACzF;;CAGF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AAEzE,KAAI,CAAC,YAAY,aACf,aAAY,eAAe,EAAE;CAG/B,MAAMA,sBAAgC,EAAE;AAExC,MAAK,MAAM,OAAO,oBAAoB;AACpC,MAAI,YAAY,kBAAkB,KAChC,QAAO,YAAY,gBAAgB;AAGrC,MAAI,CAAC,YAAY,aAAa,MAAM;AAClC,eAAY,aAAa,OAAO;AAChC,uBAAoB,KAAK,IAAI;;;AAIjC,KAAI,oBAAoB,WAAW,GAAG;AACpC,UAAQ,eAAe,4CAA4C;AACnE;;AAGF,SAAQ,eAAe,uCAAuC,oBAAoB,KAAK,KAAK,GAAG;AAE/F,IAAG,cAAc,iBAAiB,GAAG,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC,IAAI;AAC9E,SAAQ,eAAe,gDAAgD;CAEvE,IAAI,iBAAiB,kBAAkB,QAAQ;AAC/C,KAAI,mBAAmB,WAAW;AAChC,UAAQ,iBAAiB,iDAAiD;AAC1E,mBAAiB;;AAEnB,SAAQ,eAAe,iCAAiC,eAAe,KAAK;CAS5E,MAAM,iBAP0C;EAC9C,KAAK;EACL,MAAM;EACN,MAAM;EACN,KAAK;EACN,CAEsC,mBAAmB;AAE1D,KAAI;AACF,QAAM,eAAe,gBAAgB,SAAS,EAAE,QAAQ,OAAO,CAAC;AAChE,UAAQ,eAAe,6CAA6C;UAC7D,OAAO;AACd,UAAQ,iBAAiB,0CAA0C,MAAM;AACzE,UAAQ,gBAAgB,eAAe,eAAe,wCAAwC"}
|
|
@@ -1,19 +1,50 @@
|
|
|
1
1
|
import { checkIfFileExists } from "../create/utils.mjs";
|
|
2
|
+
import fs from "fs";
|
|
2
3
|
import path from "path";
|
|
3
4
|
|
|
4
5
|
//#region src/utils/get-package-manager.ts
|
|
6
|
+
const getPackageManagerFromEnv = () => {
|
|
7
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
8
|
+
if (!userAgent) return null;
|
|
9
|
+
const match = userAgent.match(/^(npm|pnpm|yarn|bun)\//);
|
|
10
|
+
if (match) return match[1];
|
|
11
|
+
return null;
|
|
12
|
+
};
|
|
13
|
+
const readPackageManagerFromPackageJson = (dir) => {
|
|
14
|
+
const packageJsonPath = path.join(dir, "package.json");
|
|
15
|
+
if (!checkIfFileExists(dir, "package.json")) return null;
|
|
16
|
+
try {
|
|
17
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
18
|
+
if (packageJson.packageManager) {
|
|
19
|
+
const pm = packageJson.packageManager.split("@")[0];
|
|
20
|
+
if ([
|
|
21
|
+
"npm",
|
|
22
|
+
"yarn",
|
|
23
|
+
"pnpm",
|
|
24
|
+
"bun"
|
|
25
|
+
].includes(pm)) return pm;
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
5
32
|
const getPackageManager = (dir) => {
|
|
33
|
+
const envPackageManager = getPackageManagerFromEnv();
|
|
34
|
+
if (envPackageManager) return envPackageManager;
|
|
6
35
|
let currentDir = dir;
|
|
7
36
|
while (currentDir !== path.dirname(currentDir)) {
|
|
8
37
|
if (checkIfFileExists(currentDir, "yarn.lock")) return "yarn";
|
|
9
38
|
else if (checkIfFileExists(currentDir, "pnpm-lock.yaml")) return "pnpm";
|
|
10
39
|
else if (checkIfFileExists(currentDir, "package-lock.json")) return "npm";
|
|
11
40
|
else if (checkIfFileExists(currentDir, "bun.lockb") || checkIfFileExists(currentDir, "bun.lock")) return "bun";
|
|
41
|
+
const packageManagerFromJson = readPackageManagerFromPackageJson(currentDir);
|
|
42
|
+
if (packageManagerFromJson) return packageManagerFromJson;
|
|
12
43
|
currentDir = path.dirname(currentDir);
|
|
13
44
|
}
|
|
14
45
|
return "npm";
|
|
15
46
|
};
|
|
16
47
|
|
|
17
48
|
//#endregion
|
|
18
|
-
export { getPackageManager };
|
|
49
|
+
export { getPackageManager, getPackageManagerFromEnv };
|
|
19
50
|
//# sourceMappingURL=get-package-manager.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-package-manager.mjs","names":[],"sources":["../../src/utils/get-package-manager.ts"],"sourcesContent":["import path from 'path'\nimport { checkIfFileExists } from '../create/utils'\n\nexport const getPackageManager = (dir: string): string => {\n let currentDir = dir\n\n while (currentDir !== path.dirname(currentDir)) {\n if (checkIfFileExists(currentDir, 'yarn.lock')) {\n return 'yarn'\n } else if (checkIfFileExists(currentDir, 'pnpm-lock.yaml')) {\n return 'pnpm'\n } else if (checkIfFileExists(currentDir, 'package-lock.json')) {\n return 'npm'\n } else if (checkIfFileExists(currentDir, 'bun.lockb') || checkIfFileExists(currentDir, 'bun.lock')) {\n return 'bun'\n }\n currentDir = path.dirname(currentDir)\n }\n\n return 'npm'\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"get-package-manager.mjs","names":[],"sources":["../../src/utils/get-package-manager.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { checkIfFileExists } from '../create/utils'\n\nexport const getPackageManagerFromEnv = (): string | null => {\n const userAgent = process.env.npm_config_user_agent\n if (!userAgent) {\n return null\n }\n\n const match = userAgent.match(/^(npm|pnpm|yarn|bun)\\//)\n if (match) {\n return match[1]\n }\n\n return null\n}\n\nconst readPackageManagerFromPackageJson = (dir: string): string | null => {\n const packageJsonPath = path.join(dir, 'package.json')\n if (!checkIfFileExists(dir, 'package.json')) {\n return null\n }\n\n try {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n if (packageJson.packageManager) {\n const pm = packageJson.packageManager.split('@')[0]\n if (['npm', 'yarn', 'pnpm', 'bun'].includes(pm)) {\n return pm\n }\n }\n } catch {\n return null\n }\n\n return null\n}\n\nexport const getPackageManager = (dir: string): string => {\n const envPackageManager = getPackageManagerFromEnv()\n if (envPackageManager) {\n return envPackageManager\n }\n\n let currentDir = dir\n\n while (currentDir !== path.dirname(currentDir)) {\n if (checkIfFileExists(currentDir, 'yarn.lock')) {\n return 'yarn'\n } else if (checkIfFileExists(currentDir, 'pnpm-lock.yaml')) {\n return 'pnpm'\n } else if (checkIfFileExists(currentDir, 'package-lock.json')) {\n return 'npm'\n } else if (checkIfFileExists(currentDir, 'bun.lockb') || checkIfFileExists(currentDir, 'bun.lock')) {\n return 'bun'\n }\n\n const packageManagerFromJson = readPackageManagerFromPackageJson(currentDir)\n if (packageManagerFromJson) {\n return packageManagerFromJson\n }\n\n currentDir = path.dirname(currentDir)\n }\n\n return 'npm'\n}\n"],"mappings":";;;;;AAIA,MAAa,iCAAgD;CAC3D,MAAM,YAAY,QAAQ,IAAI;AAC9B,KAAI,CAAC,UACH,QAAO;CAGT,MAAM,QAAQ,UAAU,MAAM,yBAAyB;AACvD,KAAI,MACF,QAAO,MAAM;AAGf,QAAO;;AAGT,MAAM,qCAAqC,QAA+B;CACxE,MAAM,kBAAkB,KAAK,KAAK,KAAK,eAAe;AACtD,KAAI,CAAC,kBAAkB,KAAK,eAAe,CACzC,QAAO;AAGT,KAAI;EACF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AACzE,MAAI,YAAY,gBAAgB;GAC9B,MAAM,KAAK,YAAY,eAAe,MAAM,IAAI,CAAC;AACjD,OAAI;IAAC;IAAO;IAAQ;IAAQ;IAAM,CAAC,SAAS,GAAG,CAC7C,QAAO;;SAGL;AACN,SAAO;;AAGT,QAAO;;AAGT,MAAa,qBAAqB,QAAwB;CACxD,MAAM,oBAAoB,0BAA0B;AACpD,KAAI,kBACF,QAAO;CAGT,IAAI,aAAa;AAEjB,QAAO,eAAe,KAAK,QAAQ,WAAW,EAAE;AAC9C,MAAI,kBAAkB,YAAY,YAAY,CAC5C,QAAO;WACE,kBAAkB,YAAY,iBAAiB,CACxD,QAAO;WACE,kBAAkB,YAAY,oBAAoB,CAC3D,QAAO;WACE,kBAAkB,YAAY,YAAY,IAAI,kBAAkB,YAAY,WAAW,CAChG,QAAO;EAGT,MAAM,yBAAyB,kCAAkC,WAAW;AAC5E,MAAI,uBACF,QAAO;AAGT,eAAa,KAAK,QAAQ,WAAW;;AAGvC,QAAO"}
|
|
@@ -19,6 +19,7 @@ async function validatePythonEnvironment({ baseDir, hasPythonFiles, pythonVersio
|
|
|
19
19
|
success: true,
|
|
20
20
|
hasPythonFiles: false
|
|
21
21
|
};
|
|
22
|
+
const installCmd = getInstallCommand(baseDir);
|
|
22
23
|
try {
|
|
23
24
|
await getPythonCommand(pythonVersion, baseDir);
|
|
24
25
|
} catch {
|
|
@@ -32,7 +33,6 @@ async function validatePythonEnvironment({ baseDir, hasPythonFiles, pythonVersio
|
|
|
32
33
|
}
|
|
33
34
|
const venvPath = path.join(baseDir, "python_modules");
|
|
34
35
|
if (!fs.existsSync(venvPath)) {
|
|
35
|
-
const installCmd = getInstallCommand(baseDir);
|
|
36
36
|
internalLogger.error("Python environment not configured");
|
|
37
37
|
internalLogger.info("The python_modules directory was not found");
|
|
38
38
|
internalLogger.info(`Run '${installCmd}' to set up your Python environment`);
|
|
@@ -43,7 +43,6 @@ async function validatePythonEnvironment({ baseDir, hasPythonFiles, pythonVersio
|
|
|
43
43
|
}
|
|
44
44
|
const libPath = path.join(venvPath, "lib");
|
|
45
45
|
if (!fs.existsSync(libPath)) {
|
|
46
|
-
const installCmd = getInstallCommand(baseDir);
|
|
47
46
|
internalLogger.error("Python environment is incomplete");
|
|
48
47
|
internalLogger.info("The python_modules directory exists but appears to be corrupted");
|
|
49
48
|
internalLogger.info(`Run '${installCmd}' to recreate your Python environment`);
|
|
@@ -54,7 +53,6 @@ async function validatePythonEnvironment({ baseDir, hasPythonFiles, pythonVersio
|
|
|
54
53
|
}
|
|
55
54
|
try {
|
|
56
55
|
if (fs.readdirSync(libPath).filter((item) => item.startsWith("python3")).length === 0) {
|
|
57
|
-
const installCmd = getInstallCommand(baseDir);
|
|
58
56
|
internalLogger.error("Python environment is incomplete");
|
|
59
57
|
internalLogger.info("The python_modules/lib directory exists but contains no Python version directories");
|
|
60
58
|
internalLogger.info(`Run '${installCmd}' to recreate your Python environment`);
|
|
@@ -64,7 +62,6 @@ async function validatePythonEnvironment({ baseDir, hasPythonFiles, pythonVersio
|
|
|
64
62
|
};
|
|
65
63
|
}
|
|
66
64
|
} catch (error) {
|
|
67
|
-
const installCmd = getInstallCommand(baseDir);
|
|
68
65
|
internalLogger.error("Python environment is incomplete");
|
|
69
66
|
internalLogger.info("The python_modules/lib directory cannot be read");
|
|
70
67
|
internalLogger.info(`Run '${installCmd}' to recreate your Python environment`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-python-environment.mjs","names":["error: any"],"sources":["../../src/utils/validate-python-environment.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { getPackageManager } from './get-package-manager'\nimport { internalLogger } from './internal-logger'\nimport { getPythonCommand } from './python-version-utils'\n\nexport interface ValidationResult {\n success: boolean\n hasPythonFiles: boolean\n}\n\ninterface ValidateConfig {\n baseDir: string\n hasPythonFiles: boolean\n pythonVersion?: string\n}\n\nfunction getInstallCommand(baseDir: string): string {\n const pm = getPackageManager(baseDir)\n switch (pm) {\n case 'yarn':\n return 'yarn install'\n case 'pnpm':\n return 'pnpm install'\n case 'bun':\n return 'bun install'\n case 'npm':\n default:\n return 'npm install'\n }\n}\n\nexport async function validatePythonEnvironment({\n baseDir,\n hasPythonFiles,\n pythonVersion = '3.13',\n}: ValidateConfig): Promise<ValidationResult> {\n if (!hasPythonFiles) {\n return { success: true, hasPythonFiles: false }\n }\n\n try {\n await getPythonCommand(pythonVersion, baseDir)\n } catch {\n internalLogger.error('Python is not installed')\n internalLogger.info('Python files were detected in your project but Python 3 is not available')\n internalLogger.info('Please install Python 3.10 or higher: https://www.python.org/downloads/')\n return { success: false, hasPythonFiles: true }\n }\n\n const venvPath = path.join(baseDir, 'python_modules')\n if (!fs.existsSync(venvPath)) {\n
|
|
1
|
+
{"version":3,"file":"validate-python-environment.mjs","names":["error: any"],"sources":["../../src/utils/validate-python-environment.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { getPackageManager } from './get-package-manager'\nimport { internalLogger } from './internal-logger'\nimport { getPythonCommand } from './python-version-utils'\n\nexport interface ValidationResult {\n success: boolean\n hasPythonFiles: boolean\n}\n\ninterface ValidateConfig {\n baseDir: string\n hasPythonFiles: boolean\n pythonVersion?: string\n}\n\nfunction getInstallCommand(baseDir: string): string {\n const pm = getPackageManager(baseDir)\n switch (pm) {\n case 'yarn':\n return 'yarn install'\n case 'pnpm':\n return 'pnpm install'\n case 'bun':\n return 'bun install'\n case 'npm':\n default:\n return 'npm install'\n }\n}\n\nexport async function validatePythonEnvironment({\n baseDir,\n hasPythonFiles,\n pythonVersion = '3.13',\n}: ValidateConfig): Promise<ValidationResult> {\n if (!hasPythonFiles) {\n return { success: true, hasPythonFiles: false }\n }\n\n const installCmd = getInstallCommand(baseDir)\n\n try {\n await getPythonCommand(pythonVersion, baseDir)\n } catch {\n internalLogger.error('Python is not installed')\n internalLogger.info('Python files were detected in your project but Python 3 is not available')\n internalLogger.info('Please install Python 3.10 or higher: https://www.python.org/downloads/')\n return { success: false, hasPythonFiles: true }\n }\n\n const venvPath = path.join(baseDir, 'python_modules')\n if (!fs.existsSync(venvPath)) {\n internalLogger.error('Python environment not configured')\n internalLogger.info('The python_modules directory was not found')\n internalLogger.info(`Run '${installCmd}' to set up your Python environment`)\n return { success: false, hasPythonFiles: true }\n }\n\n const libPath = path.join(venvPath, 'lib')\n if (!fs.existsSync(libPath)) {\n internalLogger.error('Python environment is incomplete')\n internalLogger.info('The python_modules directory exists but appears to be corrupted')\n internalLogger.info(`Run '${installCmd}' to recreate your Python environment`)\n return { success: false, hasPythonFiles: true }\n }\n\n try {\n const libContents = fs.readdirSync(libPath)\n const pythonDirs = libContents.filter((item) => item.startsWith('python3'))\n\n if (pythonDirs.length === 0) {\n internalLogger.error('Python environment is incomplete')\n internalLogger.info('The python_modules/lib directory exists but contains no Python version directories')\n internalLogger.info(`Run '${installCmd}' to recreate your Python environment`)\n return { success: false, hasPythonFiles: true }\n }\n } catch (error: any) {\n internalLogger.error('Python environment is incomplete')\n internalLogger.info('The python_modules/lib directory cannot be read')\n internalLogger.info(`Run '${installCmd}' to recreate your Python environment`)\n return { success: false, hasPythonFiles: true }\n }\n\n return { success: true, hasPythonFiles: true }\n}\n"],"mappings":";;;;;;;AAiBA,SAAS,kBAAkB,SAAyB;AAElD,SADW,kBAAkB,QAAQ,EACrC;EACE,KAAK,OACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,MACH,QAAO;EACT,KAAK;EACL,QACE,QAAO;;;AAIb,eAAsB,0BAA0B,EAC9C,SACA,gBACA,gBAAgB,UAC4B;AAC5C,KAAI,CAAC,eACH,QAAO;EAAE,SAAS;EAAM,gBAAgB;EAAO;CAGjD,MAAM,aAAa,kBAAkB,QAAQ;AAE7C,KAAI;AACF,QAAM,iBAAiB,eAAe,QAAQ;SACxC;AACN,iBAAe,MAAM,0BAA0B;AAC/C,iBAAe,KAAK,2EAA2E;AAC/F,iBAAe,KAAK,0EAA0E;AAC9F,SAAO;GAAE,SAAS;GAAO,gBAAgB;GAAM;;CAGjD,MAAM,WAAW,KAAK,KAAK,SAAS,iBAAiB;AACrD,KAAI,CAAC,GAAG,WAAW,SAAS,EAAE;AAC5B,iBAAe,MAAM,oCAAoC;AACzD,iBAAe,KAAK,6CAA6C;AACjE,iBAAe,KAAK,QAAQ,WAAW,qCAAqC;AAC5E,SAAO;GAAE,SAAS;GAAO,gBAAgB;GAAM;;CAGjD,MAAM,UAAU,KAAK,KAAK,UAAU,MAAM;AAC1C,KAAI,CAAC,GAAG,WAAW,QAAQ,EAAE;AAC3B,iBAAe,MAAM,mCAAmC;AACxD,iBAAe,KAAK,kEAAkE;AACtF,iBAAe,KAAK,QAAQ,WAAW,uCAAuC;AAC9E,SAAO;GAAE,SAAS;GAAO,gBAAgB;GAAM;;AAGjD,KAAI;AAIF,MAHoB,GAAG,YAAY,QAAQ,CACZ,QAAQ,SAAS,KAAK,WAAW,UAAU,CAAC,CAE5D,WAAW,GAAG;AAC3B,kBAAe,MAAM,mCAAmC;AACxD,kBAAe,KAAK,qFAAqF;AACzG,kBAAe,KAAK,QAAQ,WAAW,uCAAuC;AAC9E,UAAO;IAAE,SAAS;IAAO,gBAAgB;IAAM;;UAE1CA,OAAY;AACnB,iBAAe,MAAM,mCAAmC;AACxD,iBAAe,KAAK,kDAAkD;AACtE,iBAAe,KAAK,QAAQ,WAAW,uCAAuC;AAC9E,SAAO;GAAE,SAAS;GAAO,gBAAgB;GAAM;;AAGjD,QAAO;EAAE,SAAS;EAAM,gBAAgB;EAAM"}
|
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.15.5-beta.174-
|
|
4
|
+
"version": "0.15.5-beta.174-096320",
|
|
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.15.5-beta.174-
|
|
50
|
-
"@motiadev/adapter-redis-cron": "0.15.5-beta.174-
|
|
51
|
-
"@motiadev/
|
|
52
|
-
"@motiadev/core": "0.15.5-beta.174-
|
|
53
|
-
"@motiadev/
|
|
54
|
-
"@motiadev/
|
|
55
|
-
"@motiadev/
|
|
49
|
+
"@motiadev/adapter-bullmq-events": "0.15.5-beta.174-096320",
|
|
50
|
+
"@motiadev/adapter-redis-cron": "0.15.5-beta.174-096320",
|
|
51
|
+
"@motiadev/adapter-redis-state": "0.15.5-beta.174-096320",
|
|
52
|
+
"@motiadev/core": "0.15.5-beta.174-096320",
|
|
53
|
+
"@motiadev/stream-client-node": "0.15.5-beta.174-096320",
|
|
54
|
+
"@motiadev/adapter-redis-streams": "0.15.5-beta.174-096320",
|
|
55
|
+
"@motiadev/workbench": "0.15.5-beta.174-096320"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@amplitude/analytics-types": "^2.9.2",
|