motia 0.15.4-beta.169 → 0.15.4-beta.170-500956

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/cli.mjs CHANGED
@@ -4,7 +4,6 @@ import { wrapAction } from "./utils/analytics.mjs";
4
4
  import { handler } from "./cloud/config-utils.mjs";
5
5
  import "./cloud/index.mjs";
6
6
  import { loadMotiaConfig } from "./load-motia-config.mjs";
7
- import { instanceRedisMemoryServer, stopRedisMemoryServer } from "./redis-memory-manager.mjs";
8
7
  import "dotenv/config";
9
8
  import { program } from "commander";
10
9
 
@@ -15,7 +14,7 @@ program.command("version").description("Display detailed version information").a
15
14
  console.log(`Motia CLI v${version}`);
16
15
  process.exit(0);
17
16
  });
18
- program.command("create [name]").description("Create a new motia project").option("-t, --template <template>", "The template to use for your project").option("-p, --plugin", "Create a plugin project").option("-i, --interactive", "Use interactive prompts to create project").action((projectName, options) => {
17
+ program.command("create [name]").description("Create a new motia project").option("-t, --template <template>", "The template to use for your project").option("-p, --plugin", "Create a plugin project").option("-i, --interactive", "Use interactive prompts to create project").option("--skip-redis", "Skip Redis binary installation and use external Redis").action((projectName, options) => {
19
18
  const mergedArgs = {
20
19
  ...options,
21
20
  name: projectName
@@ -25,7 +24,8 @@ program.command("create [name]").description("Create a new motia project").optio
25
24
  await createInteractive({
26
25
  name: arg.name,
27
26
  template: arg.template,
28
- plugin: !!arg.plugin
27
+ plugin: !!arg.plugin,
28
+ skipRedis: !!arg.skipRedis
29
29
  }, context);
30
30
  })(mergedArgs);
31
31
  });
@@ -90,16 +90,13 @@ generate.command("openapi").description("Generate OpenAPI spec for your project"
90
90
  const { MemoryStreamAdapterManager } = await import("@motiadev/core");
91
91
  const baseDir = process.cwd();
92
92
  const appConfig = await loadMotiaConfig(baseDir);
93
- const redisClient = await instanceRedisMemoryServer(baseDir, true);
94
93
  const apiSteps = (await generateLockedData({
95
94
  projectDir: baseDir,
96
95
  streamAdapter: new MemoryStreamAdapterManager(),
97
96
  streamAuth: appConfig.streamAuth,
98
- redisClient,
99
97
  printerType: "disabled"
100
98
  })).apiSteps();
101
99
  generateOpenApi(process.cwd(), apiSteps, options.title, options.version, options.output);
102
- await stopRedisMemoryServer();
103
100
  process.exit(0);
104
101
  }));
105
102
  const docker = program.command("docker").description("Motia docker commands");
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["redisClient: RedisClientType"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport 'dotenv/config'\n\nimport { program } from 'commander'\nimport { type CliContext, handler } from './cloud/config-utils'\nimport './cloud/index'\nimport type { RedisClientType } from 'redis'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { instanceRedisMemoryServer, stopRedisMemoryServer } from './redis-memory-manager'\nimport { wrapAction } from './utils/analytics'\nimport { version } from './version'\n\nconst defaultPort = 3000\nconst defaultHost = '0.0.0.0'\n\nprogram\n .command('version')\n .description('Display detailed version information')\n .action(() => {\n console.log(`Motia CLI v${version}`)\n process.exit(0)\n })\n\nprogram\n .command('create [name]')\n .description('Create a new motia project')\n .option('-t, --template <template>', 'The template to use for your project')\n .option('-p, --plugin', 'Create a plugin project')\n .option('-i, --interactive', 'Use interactive prompts to create project') // it's default\n .action((projectName, options) => {\n const mergedArgs = { ...options, name: projectName }\n return handler(async (arg: any, context: CliContext) => {\n const { createInteractive } = await import('./create/interactive')\n await createInteractive(\n {\n name: arg.name,\n template: arg.template,\n plugin: !!arg.plugin,\n },\n context,\n )\n })(mergedArgs)\n })\n\nprogram\n .command('rules')\n .command('pull')\n .description('Install essential AI development guides (AGENTS.md, CLAUDE.md) and optional Cursor IDE rules')\n .option('-f, --force', 'Overwrite existing files')\n .action(\n handler(async (arg: any, context: CliContext) => {\n const { pullRules } = await import('./create/pull-rules')\n await pullRules({ force: arg.force, rootDir: process.cwd() }, context)\n }),\n )\n\nprogram\n .command('generate-types')\n .description('Generate types.d.ts file for your project')\n .action(\n wrapAction(async () => {\n const { generateTypes } = await import('./generate-types')\n await generateTypes(process.cwd())\n process.exit(0)\n }),\n )\n\nprogram\n .command('install')\n .description('Sets up Python virtual environment and install dependencies')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(\n wrapAction(async (options: any) => {\n const { install } = await import('./install')\n await install({ isVerbose: options.verbose })\n }),\n )\n\nprogram\n .command('dev')\n .description('Start the development server')\n .option('-p, --port <port>', 'The port to run the server on', `${defaultPort}`)\n .option('-H, --host [host]', 'The host address for the server', `${defaultHost}`)\n .option('-v, --disable-verbose', 'Disable verbose logging')\n .option('-d, --debug', 'Enable debug logging')\n .option('-m, --mermaid', 'Enable mermaid diagram generation')\n .option('--motia-dir <path>', 'Path where .motia folder will be created')\n .action(\n wrapAction(async (arg: any) => {\n if (arg.debug) {\n console.log('🔍 Debug logging enabled')\n process.env.LOG_LEVEL = 'debug'\n }\n\n const port = arg.port ? parseInt(arg.port) : defaultPort\n const host = arg.host ? arg.host : defaultHost\n const { dev } = await import('./dev')\n await dev(port, host, arg.disableVerbose, arg.mermaid, arg.motiaDir)\n }),\n )\n\nprogram\n .command('start')\n .description('Start a server to run your Motia project')\n .option('-p, --port <port>', 'The port to run the server on', `${defaultPort}`)\n .option('-H, --host [host]', 'The host address for the server', `${defaultHost}`)\n .option('-v, --disable-verbose', 'Disable verbose logging')\n .option('-d, --debug', 'Enable debug logging')\n .option('--motia-dir <path>', 'Path where .motia folder will be created')\n .action(\n wrapAction(async (arg: any) => {\n if (arg.debug) {\n console.log('🔍 Debug logging enabled')\n process.env.LOG_LEVEL = 'debug'\n }\n\n const port = arg.port ? parseInt(arg.port) : defaultPort\n const host = arg.host ? arg.host : defaultHost\n const { start } = await import('./start')\n await start(port, host, arg.disableVerbose, arg.motiaDir)\n }),\n )\n\nprogram\n .command('emit')\n .description('Emit an event to the Motia server')\n .requiredOption('--topic <topic>', 'Event topic/type to emit')\n .requiredOption('--message <message>', 'Event payload as JSON string')\n .option('-p, --port <number>', 'Port number (default: 3000)')\n .action(\n wrapAction(async (options: any) => {\n const port = options.port || 3000\n const url = `http://localhost:${port}/emit`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n topic: options.topic,\n data: JSON.parse(options.message),\n }),\n })\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n\n const result = await response.json()\n console.log('Event emitted successfully:', result)\n }),\n )\n\nconst generate = program.command('generate').description('Generate motia resources')\n\ngenerate\n .command('step')\n .description('Create a new step with interactive prompts')\n .option('-d, --dir <step file path>', 'The path relative to the steps directory, used to create the step file')\n .action(\n wrapAction(async (arg: any) => {\n const { createStep } = await import('./create-step/index')\n return createStep({\n stepFilePath: arg.dir,\n })\n }),\n )\n\ngenerate\n .command('openapi')\n .description('Generate OpenAPI spec for your project')\n .option('-t, --title <title>', 'Title for the OpenAPI document. Defaults to project name')\n .option('-v, --version <version>', 'Version for the OpenAPI document. Defaults to 1.0.0', '1.0.0')\n .option('-o, --output <output>', 'Output file for the OpenAPI document. Defaults to openapi.json', 'openapi.json')\n .action(\n wrapAction(async (options: any) => {\n const { generateLockedData } = await import('./generate-locked-data')\n const { generateOpenApi } = await import('./openapi/generate')\n const { MemoryStreamAdapterManager } = await import('@motiadev/core')\n\n const baseDir = process.cwd()\n const appConfig = await loadMotiaConfig(baseDir)\n const redisClient: RedisClientType = await instanceRedisMemoryServer(baseDir, true)\n\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: new MemoryStreamAdapterManager(),\n streamAuth: appConfig.streamAuth,\n redisClient,\n printerType: 'disabled',\n })\n const apiSteps = lockedData.apiSteps()\n\n generateOpenApi(process.cwd(), apiSteps, options.title, options.version, options.output)\n await stopRedisMemoryServer()\n process.exit(0)\n }),\n )\n\nconst docker = program.command('docker').description('Motia docker commands')\n\ndocker\n .command('setup')\n .description('Setup a motia-docker for your project')\n .action(\n wrapAction(async () => {\n const { setup } = await import('./docker/setup')\n await setup()\n process.exit(0)\n }),\n )\n\ndocker\n .command('run')\n .description('Build and run your project in a docker container')\n .option('-p, --port <port>', 'The port to run the server on', `${defaultPort}`)\n .option('-n, --project-name <project name>', 'The name for your project')\n .option('-s, --skip-build', 'Skip docker build')\n .action(\n wrapAction(async (arg: any) => {\n const { run } = await import('./docker/run')\n await run(arg.port, arg.projectName, arg.skipBuild)\n process.exit(0)\n }),\n )\n\ndocker\n .command('build')\n .description('Build your project in a docker container')\n .option('-n, --project-name <project name>', 'The name for your project')\n .action(\n wrapAction(async (arg: any) => {\n const { build } = await import('./docker/build')\n await build(arg.projectName)\n process.exit(0)\n }),\n )\n\nprogram.version(version, '-V, --version', 'Output the current version')\nprogram.parseAsync(process.argv).catch(() => {\n process.exit(1)\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport 'dotenv/config'\n\nimport { program } from 'commander'\nimport { type CliContext, handler } from './cloud/config-utils'\nimport './cloud/index'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { wrapAction } from './utils/analytics'\nimport { version } from './version'\n\nconst defaultPort = 3000\nconst defaultHost = '0.0.0.0'\n\nprogram\n .command('version')\n .description('Display detailed version information')\n .action(() => {\n console.log(`Motia CLI v${version}`)\n process.exit(0)\n })\n\nprogram\n .command('create [name]')\n .description('Create a new motia project')\n .option('-t, --template <template>', 'The template to use for your project')\n .option('-p, --plugin', 'Create a plugin project')\n .option('-i, --interactive', 'Use interactive prompts to create project') // it's default\n .option('--skip-redis', 'Skip Redis binary installation and use external Redis')\n .action((projectName, options) => {\n const mergedArgs = { ...options, name: projectName }\n return handler(async (arg: any, context: CliContext) => {\n const { createInteractive } = await import('./create/interactive')\n await createInteractive(\n {\n name: arg.name,\n template: arg.template,\n plugin: !!arg.plugin,\n skipRedis: !!arg.skipRedis,\n },\n context,\n )\n })(mergedArgs)\n })\n\nprogram\n .command('rules')\n .command('pull')\n .description('Install essential AI development guides (AGENTS.md, CLAUDE.md) and optional Cursor IDE rules')\n .option('-f, --force', 'Overwrite existing files')\n .action(\n handler(async (arg: any, context: CliContext) => {\n const { pullRules } = await import('./create/pull-rules')\n await pullRules({ force: arg.force, rootDir: process.cwd() }, context)\n }),\n )\n\nprogram\n .command('generate-types')\n .description('Generate types.d.ts file for your project')\n .action(\n wrapAction(async () => {\n const { generateTypes } = await import('./generate-types')\n await generateTypes(process.cwd())\n process.exit(0)\n }),\n )\n\nprogram\n .command('install')\n .description('Sets up Python virtual environment and install dependencies')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(\n wrapAction(async (options: any) => {\n const { install } = await import('./install')\n await install({ isVerbose: options.verbose })\n }),\n )\n\nprogram\n .command('dev')\n .description('Start the development server')\n .option('-p, --port <port>', 'The port to run the server on', `${defaultPort}`)\n .option('-H, --host [host]', 'The host address for the server', `${defaultHost}`)\n .option('-v, --disable-verbose', 'Disable verbose logging')\n .option('-d, --debug', 'Enable debug logging')\n .option('-m, --mermaid', 'Enable mermaid diagram generation')\n .option('--motia-dir <path>', 'Path where .motia folder will be created')\n .action(\n wrapAction(async (arg: any) => {\n if (arg.debug) {\n console.log('🔍 Debug logging enabled')\n process.env.LOG_LEVEL = 'debug'\n }\n\n const port = arg.port ? parseInt(arg.port) : defaultPort\n const host = arg.host ? arg.host : defaultHost\n const { dev } = await import('./dev')\n await dev(port, host, arg.disableVerbose, arg.mermaid, arg.motiaDir)\n }),\n )\n\nprogram\n .command('start')\n .description('Start a server to run your Motia project')\n .option('-p, --port <port>', 'The port to run the server on', `${defaultPort}`)\n .option('-H, --host [host]', 'The host address for the server', `${defaultHost}`)\n .option('-v, --disable-verbose', 'Disable verbose logging')\n .option('-d, --debug', 'Enable debug logging')\n .option('--motia-dir <path>', 'Path where .motia folder will be created')\n .action(\n wrapAction(async (arg: any) => {\n if (arg.debug) {\n console.log('🔍 Debug logging enabled')\n process.env.LOG_LEVEL = 'debug'\n }\n\n const port = arg.port ? parseInt(arg.port) : defaultPort\n const host = arg.host ? arg.host : defaultHost\n const { start } = await import('./start')\n await start(port, host, arg.disableVerbose, arg.motiaDir)\n }),\n )\n\nprogram\n .command('emit')\n .description('Emit an event to the Motia server')\n .requiredOption('--topic <topic>', 'Event topic/type to emit')\n .requiredOption('--message <message>', 'Event payload as JSON string')\n .option('-p, --port <number>', 'Port number (default: 3000)')\n .action(\n wrapAction(async (options: any) => {\n const port = options.port || 3000\n const url = `http://localhost:${port}/emit`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n topic: options.topic,\n data: JSON.parse(options.message),\n }),\n })\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n\n const result = await response.json()\n console.log('Event emitted successfully:', result)\n }),\n )\n\nconst generate = program.command('generate').description('Generate motia resources')\n\ngenerate\n .command('step')\n .description('Create a new step with interactive prompts')\n .option('-d, --dir <step file path>', 'The path relative to the steps directory, used to create the step file')\n .action(\n wrapAction(async (arg: any) => {\n const { createStep } = await import('./create-step/index')\n return createStep({\n stepFilePath: arg.dir,\n })\n }),\n )\n\ngenerate\n .command('openapi')\n .description('Generate OpenAPI spec for your project')\n .option('-t, --title <title>', 'Title for the OpenAPI document. Defaults to project name')\n .option('-v, --version <version>', 'Version for the OpenAPI document. Defaults to 1.0.0', '1.0.0')\n .option('-o, --output <output>', 'Output file for the OpenAPI document. Defaults to openapi.json', 'openapi.json')\n .action(\n wrapAction(async (options: any) => {\n const { generateLockedData } = await import('./generate-locked-data')\n const { generateOpenApi } = await import('./openapi/generate')\n const { MemoryStreamAdapterManager } = await import('@motiadev/core')\n\n const baseDir = process.cwd()\n const appConfig = await loadMotiaConfig(baseDir)\n\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: new MemoryStreamAdapterManager(),\n streamAuth: appConfig.streamAuth,\n printerType: 'disabled',\n })\n const apiSteps = lockedData.apiSteps()\n\n generateOpenApi(process.cwd(), apiSteps, options.title, options.version, options.output)\n process.exit(0)\n }),\n )\n\nconst docker = program.command('docker').description('Motia docker commands')\n\ndocker\n .command('setup')\n .description('Setup a motia-docker for your project')\n .action(\n wrapAction(async () => {\n const { setup } = await import('./docker/setup')\n await setup()\n process.exit(0)\n }),\n )\n\ndocker\n .command('run')\n .description('Build and run your project in a docker container')\n .option('-p, --port <port>', 'The port to run the server on', `${defaultPort}`)\n .option('-n, --project-name <project name>', 'The name for your project')\n .option('-s, --skip-build', 'Skip docker build')\n .action(\n wrapAction(async (arg: any) => {\n const { run } = await import('./docker/run')\n await run(arg.port, arg.projectName, arg.skipBuild)\n process.exit(0)\n }),\n )\n\ndocker\n .command('build')\n .description('Build your project in a docker container')\n .option('-n, --project-name <project name>', 'The name for your project')\n .action(\n wrapAction(async (arg: any) => {\n const { build } = await import('./docker/build')\n await build(arg.projectName)\n process.exit(0)\n }),\n )\n\nprogram.version(version, '-V, --version', 'Output the current version')\nprogram.parseAsync(process.argv).catch(() => {\n process.exit(1)\n})\n"],"mappings":"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"}
@@ -12,6 +12,9 @@ import path from "path";
12
12
  import pc from "picocolors";
13
13
 
14
14
  //#region src/create/index.ts
15
+ const generateRedisConfig = (skipRedis) => {
16
+ return skipRedis ? ` redis: {\n useMemoryServer: false,\n host: '127.0.0.1',\n port: 6379,\n },\n` : ` redis: {\n useMemoryServer: true,\n },\n`;
17
+ };
15
18
  const installRequiredDependencies = async (packageManager, rootDir, context) => {
16
19
  context.log("installing-dependencies", (message) => message.tag("info").append("Installing dependencies..."));
17
20
  const installCommand = {
@@ -31,8 +34,8 @@ const installRequiredDependencies = async (packageManager, rootDir, context) =>
31
34
  "@types/react@19.1.1"
32
35
  ].join(" ");
33
36
  try {
34
- await executeCommand(`${installCommand} ${dependencies}`, rootDir);
35
- await executeCommand(`${installCommand} -D ${devDependencies}`, rootDir);
37
+ await executeCommand(`${installCommand} ${dependencies}`, rootDir, { env: { REDISMS_DISABLE_POSTINSTALL: "1" } });
38
+ await executeCommand(`${installCommand} -D ${devDependencies}`, rootDir, { env: { REDISMS_DISABLE_POSTINSTALL: "1" } });
36
39
  context.log("dependencies-installed", (message) => message.tag("success").append("Dependencies installed"));
37
40
  } catch (error) {
38
41
  console.error("❌ Failed to install dependencies:", error);
@@ -55,11 +58,12 @@ const installNodeDependencies = async (rootDir, context) => {
55
58
  });
56
59
  return packageManager;
57
60
  };
58
- const create = async ({ projectName, template, cursorEnabled, context }) => {
61
+ const create = async ({ projectName, template, cursorEnabled, context, skipRedis = false }) => {
59
62
  console.log("\n\n\n _____ ______ ______ ______\n /'\\_/`\\/\\ __`\\/\\__ _\\/\\__ _\\ /\\ _ \\\n /\\ \\ \\ \\/\\ \\/_/\\ \\/\\/_/\\ \\/ \\ \\ \\L\\ \\\n \\ \\ \\__\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ __ \\\n \\ \\ \\_/\\ \\ \\ \\_\\ \\ \\ \\ \\ \\_\\ \\__\\ \\ \\/\\ \\\n \\ \\_\\\\ \\_\\ \\_____\\ \\ \\_\\ /\\_____\\\\ \\_\\ \\_\\\n \\/_/ \\/_/\\/_____/ \\/_/ \\/_____/ \\/_/\\/_/\n \n\n");
60
63
  const isCurrentDir = projectName === "." || projectName === "./" || projectName === ".\\";
61
64
  const rootDir = isCurrentDir ? process.cwd() : path.join(process.cwd(), projectName);
62
65
  const isPluginTemplate = template === "plugin";
66
+ process.env.REDISMS_DISABLE_POSTINSTALL = "1";
63
67
  if (!isCurrentDir && !checkIfDirectoryExists(rootDir)) {
64
68
  fs.mkdirSync(path.join(rootDir));
65
69
  context.log("directory-created", (message) => message.tag("success").append("Directory created ").append(projectName, "gray"));
@@ -147,6 +151,34 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
147
151
  rootDir
148
152
  }, context);
149
153
  if (template) await setupTemplate(template, rootDir, context);
154
+ if (!isPluginTemplate) {
155
+ const motiaConfigPath = path.join(rootDir, "motia.config.ts");
156
+ const redisConfig = generateRedisConfig(skipRedis);
157
+ if (checkIfFileExists(rootDir, "motia.config.ts")) {
158
+ let configContent = fs.readFileSync(motiaConfigPath, "utf-8");
159
+ if (!configContent.includes("redis:")) {
160
+ if (configContent.includes("export default config({")) configContent = configContent.replace(/export default config\(/, `export default config({\n${redisConfig}`);
161
+ else if (configContent.includes("config({")) configContent = configContent.replace(/config\(/, `config({\n${redisConfig}`);
162
+ else {
163
+ const lines = configContent.split("\n");
164
+ const lastLine = lines[lines.length - 1];
165
+ if (lastLine.trim() === "})" || lastLine.trim() === ")") {
166
+ lines.splice(lines.length - 1, 0, redisConfig.trim());
167
+ configContent = lines.join("\n");
168
+ }
169
+ }
170
+ fs.writeFileSync(motiaConfigPath, configContent);
171
+ }
172
+ } else {
173
+ const configContent = `import { config } from '@motiadev/core'
174
+
175
+ export default config({
176
+ ${redisConfig}})
177
+ `;
178
+ fs.writeFileSync(motiaConfigPath, configContent);
179
+ context.log("motia-config-created", (message) => message.tag("success").append("File").append("motia.config.ts", "cyan").append("has been created."));
180
+ }
181
+ }
150
182
  let packageManager;
151
183
  if (!isPluginTemplate) {
152
184
  packageManager = await installNodeDependencies(rootDir, context);
@@ -168,8 +200,12 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
168
200
  context.log("success-blank-5", (message) => message.text(""));
169
201
  context.log("success-docs", (message) => message.text(`Docs: ${pc.cyan("https://www.motia.dev/docs")}`));
170
202
  context.log("success-blank-6", (message) => message.text(""));
203
+ if (skipRedis) {
204
+ context.log("redis-skip-warning", (message) => message.tag("warning").append("⚠️ You skipped Redis binary installation. Make sure to provide a Redis connection before running Motia."));
205
+ context.log("success-blank-7", (message) => message.text(""));
206
+ }
171
207
  context.log("success-signoff", (message) => message.text("Happy coding! 🚀"));
172
- context.log("success-blank-7", (message) => message.text(""));
208
+ context.log("success-blank-8", (message) => message.text(""));
173
209
  };
174
210
 
175
211
  //#endregion
@@ -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 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 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}\n\nexport const create = async ({ projectName, template, cursorEnabled, context }: 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 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 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 context.log('success-signoff', (message) => message.text('Happy coding! 🚀'))\n context.log('success-blank-7', (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 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 generateRedisConfig = (skipRedis: boolean): string => {\n return skipRedis\n ? ` redis: {\\n useMemoryServer: false,\\n host: '127.0.0.1',\\n port: 6379,\\n },\\n`\n : ` redis: {\\n useMemoryServer: true,\\n },\\n`\n}\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, { env: { REDISMS_DISABLE_POSTINSTALL: '1' } })\n await executeCommand(`${installCommand} -D ${devDependencies}`, rootDir, {\n env: { REDISMS_DISABLE_POSTINSTALL: '1' },\n })\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) {\n const motiaConfigPath = path.join(rootDir, 'motia.config.ts')\n const redisConfig = generateRedisConfig(skipRedis)\n\n if (checkIfFileExists(rootDir, 'motia.config.ts')) {\n let configContent = fs.readFileSync(motiaConfigPath, 'utf-8')\n if (!configContent.includes('redis:')) {\n if (configContent.includes('export default config({')) {\n configContent = configContent.replace(/export default config\\(/, `export default config({\\n${redisConfig}`)\n } else if (configContent.includes('config({')) {\n configContent = configContent.replace(/config\\(/, `config({\\n${redisConfig}`)\n } else {\n const lines = configContent.split('\\n')\n const lastLine = lines[lines.length - 1]\n if (lastLine.trim() === '})' || lastLine.trim() === ')') {\n lines.splice(lines.length - 1, 0, redisConfig.trim())\n configContent = lines.join('\\n')\n }\n }\n fs.writeFileSync(motiaConfigPath, configContent)\n }\n } else {\n const configContent = `import { config } from '@motiadev/core'\n\nexport default config({\n${redisConfig}})\n`\n fs.writeFileSync(motiaConfigPath, configContent)\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\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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"}
@@ -58,7 +58,8 @@ const createInteractive = async (args, context) => {
58
58
  projectName: name || ".",
59
59
  template: template || "motia-tutorial-typescript",
60
60
  cursorEnabled: true,
61
- context
61
+ context,
62
+ skipRedis: args.skipRedis
62
63
  });
63
64
  process.exit(0);
64
65
  };
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.mjs","names":["choices: Record<string, string>","questions: QuestionCollection<never>[]","answers: InteractiveAnswers"],"sources":["../../src/create/interactive.ts"],"sourcesContent":["import inquirer, { type QuestionCollection } from 'inquirer'\nimport pc from 'picocolors'\nimport type { CliContext, Message } from '../cloud/config-utils'\nimport { create } from './index'\n\ninterface InteractiveAnswers {\n template: string\n projectName: string\n}\n\nconst choices: Record<string, string> = {\n 'motia-tutorial-typescript': 'Tutorial (TypeScript only)',\n 'motia-tutorial-python': 'Tutorial (Python only)',\n 'starter-multilang': 'Starter (All languages; TS/JS + Python)',\n 'starter-typescript': 'Starter (TypeScript only)',\n 'starter-javascript': 'Starter (JavaScript only)',\n 'starter-python': 'Starter (Python only)',\n}\n\ninterface CreateInteractiveArgs {\n name?: string\n template?: string\n plugin?: boolean\n}\n\nexport const createInteractive = async (args: CreateInteractiveArgs, context: CliContext): Promise<void> => {\n context.log('welcome', (message: Message) =>\n message.append(\n `\\n🚀 ${pc.bold(args.plugin ? 'Welcome to Motia Plugin Creator!' : 'Welcome to Motia Project Creator!')}`,\n ),\n )\n\n const questions: QuestionCollection<never>[] = []\n\n let name = args.name\n let template = args.template\n\n if (args.plugin) {\n if (!args.name) {\n context.log('failed', (message: Message) =>\n message.tag('failed').append(`Project name is required: ${pc.bold('motia create --plugin [project-name]')}\\n`),\n )\n return\n }\n\n return create({\n projectName: args.name,\n template: 'plugin',\n cursorEnabled: false,\n context,\n })\n } else if (!args.template) {\n questions.push({\n type: 'list',\n name: 'template',\n message: 'What template do you want to use? (Use arrow keys)',\n choices: Object.keys(choices).map((key) => ({\n name: choices[key],\n value: key,\n })),\n })\n }\n\n if (!args.name) {\n questions.push({\n type: 'input',\n name: 'projectName',\n message: 'Project name (leave blank to use current folder):',\n validate: (input: string) => {\n if (input && input.trim().length > 0) {\n if (!/^[a-zA-Z0-9][a-zA-Z0-9-_]*$/.test(input.trim())) {\n return 'Project name must start with a letter or number and contain only letters, numbers, hyphens, and underscores'\n }\n }\n return true\n },\n filter: (input: string) => input.trim(),\n })\n }\n\n if (questions.length > 0) {\n const answers: InteractiveAnswers = await inquirer.prompt(questions)\n name = args.name || answers.projectName\n template = args.template || answers.template\n }\n\n context.log('creating', (message: Message) => message.append('\\n🔨 Creating your Motia project...\\n'))\n\n await create({\n projectName: name || '.',\n template: template || 'motia-tutorial-typescript',\n cursorEnabled: true, // Default to true for cursor rules\n context,\n })\n\n process.exit(0)\n}\n"],"mappings":";;;;;AAUA,MAAMA,UAAkC;CACtC,6BAA6B;CAC7B,yBAAyB;CACzB,qBAAqB;CACrB,sBAAsB;CACtB,sBAAsB;CACtB,kBAAkB;CACnB;AAQD,MAAa,oBAAoB,OAAO,MAA6B,YAAuC;AAC1G,SAAQ,IAAI,YAAY,YACtB,QAAQ,OACN,QAAQ,GAAG,KAAK,KAAK,SAAS,qCAAqC,oCAAoC,GACxG,CACF;CAED,MAAMC,YAAyC,EAAE;CAEjD,IAAI,OAAO,KAAK;CAChB,IAAI,WAAW,KAAK;AAEpB,KAAI,KAAK,QAAQ;AACf,MAAI,CAAC,KAAK,MAAM;AACd,WAAQ,IAAI,WAAW,YACrB,QAAQ,IAAI,SAAS,CAAC,OAAO,6BAA6B,GAAG,KAAK,uCAAuC,CAAC,IAAI,CAC/G;AACD;;AAGF,SAAO,OAAO;GACZ,aAAa,KAAK;GAClB,UAAU;GACV,eAAe;GACf;GACD,CAAC;YACO,CAAC,KAAK,SACf,WAAU,KAAK;EACb,MAAM;EACN,MAAM;EACN,SAAS;EACT,SAAS,OAAO,KAAK,QAAQ,CAAC,KAAK,SAAS;GAC1C,MAAM,QAAQ;GACd,OAAO;GACR,EAAE;EACJ,CAAC;AAGJ,KAAI,CAAC,KAAK,KACR,WAAU,KAAK;EACb,MAAM;EACN,MAAM;EACN,SAAS;EACT,WAAW,UAAkB;AAC3B,OAAI,SAAS,MAAM,MAAM,CAAC,SAAS,GACjC;QAAI,CAAC,8BAA8B,KAAK,MAAM,MAAM,CAAC,CACnD,QAAO;;AAGX,UAAO;;EAET,SAAS,UAAkB,MAAM,MAAM;EACxC,CAAC;AAGJ,KAAI,UAAU,SAAS,GAAG;EACxB,MAAMC,UAA8B,MAAM,SAAS,OAAO,UAAU;AACpE,SAAO,KAAK,QAAQ,QAAQ;AAC5B,aAAW,KAAK,YAAY,QAAQ;;AAGtC,SAAQ,IAAI,aAAa,YAAqB,QAAQ,OAAO,wCAAwC,CAAC;AAEtG,OAAM,OAAO;EACX,aAAa,QAAQ;EACrB,UAAU,YAAY;EACtB,eAAe;EACf;EACD,CAAC;AAEF,SAAQ,KAAK,EAAE"}
1
+ {"version":3,"file":"interactive.mjs","names":["choices: Record<string, string>","questions: QuestionCollection<never>[]","answers: InteractiveAnswers"],"sources":["../../src/create/interactive.ts"],"sourcesContent":["import inquirer, { type QuestionCollection } from 'inquirer'\nimport pc from 'picocolors'\nimport type { CliContext, Message } from '../cloud/config-utils'\nimport { create } from './index'\n\ninterface InteractiveAnswers {\n template: string\n projectName: string\n}\n\nconst choices: Record<string, string> = {\n 'motia-tutorial-typescript': 'Tutorial (TypeScript only)',\n 'motia-tutorial-python': 'Tutorial (Python only)',\n 'starter-multilang': 'Starter (All languages; TS/JS + Python)',\n 'starter-typescript': 'Starter (TypeScript only)',\n 'starter-javascript': 'Starter (JavaScript only)',\n 'starter-python': 'Starter (Python only)',\n}\n\ninterface CreateInteractiveArgs {\n name?: string\n template?: string\n plugin?: boolean\n skipRedis?: boolean\n}\n\nexport const createInteractive = async (args: CreateInteractiveArgs, context: CliContext): Promise<void> => {\n context.log('welcome', (message: Message) =>\n message.append(\n `\\n🚀 ${pc.bold(args.plugin ? 'Welcome to Motia Plugin Creator!' : 'Welcome to Motia Project Creator!')}`,\n ),\n )\n\n const questions: QuestionCollection<never>[] = []\n\n let name = args.name\n let template = args.template\n\n if (args.plugin) {\n if (!args.name) {\n context.log('failed', (message: Message) =>\n message.tag('failed').append(`Project name is required: ${pc.bold('motia create --plugin [project-name]')}\\n`),\n )\n return\n }\n\n return create({\n projectName: args.name,\n template: 'plugin',\n cursorEnabled: false,\n context,\n })\n } else if (!args.template) {\n questions.push({\n type: 'list',\n name: 'template',\n message: 'What template do you want to use? (Use arrow keys)',\n choices: Object.keys(choices).map((key) => ({\n name: choices[key],\n value: key,\n })),\n })\n }\n\n if (!args.name) {\n questions.push({\n type: 'input',\n name: 'projectName',\n message: 'Project name (leave blank to use current folder):',\n validate: (input: string) => {\n if (input && input.trim().length > 0) {\n if (!/^[a-zA-Z0-9][a-zA-Z0-9-_]*$/.test(input.trim())) {\n return 'Project name must start with a letter or number and contain only letters, numbers, hyphens, and underscores'\n }\n }\n return true\n },\n filter: (input: string) => input.trim(),\n })\n }\n\n if (questions.length > 0) {\n const answers: InteractiveAnswers = await inquirer.prompt(questions)\n name = args.name || answers.projectName\n template = args.template || answers.template\n }\n\n context.log('creating', (message: Message) => message.append('\\n🔨 Creating your Motia project...\\n'))\n\n await create({\n projectName: name || '.',\n template: template || 'motia-tutorial-typescript',\n cursorEnabled: true,\n context,\n skipRedis: args.skipRedis,\n })\n\n process.exit(0)\n}\n"],"mappings":";;;;;AAUA,MAAMA,UAAkC;CACtC,6BAA6B;CAC7B,yBAAyB;CACzB,qBAAqB;CACrB,sBAAsB;CACtB,sBAAsB;CACtB,kBAAkB;CACnB;AASD,MAAa,oBAAoB,OAAO,MAA6B,YAAuC;AAC1G,SAAQ,IAAI,YAAY,YACtB,QAAQ,OACN,QAAQ,GAAG,KAAK,KAAK,SAAS,qCAAqC,oCAAoC,GACxG,CACF;CAED,MAAMC,YAAyC,EAAE;CAEjD,IAAI,OAAO,KAAK;CAChB,IAAI,WAAW,KAAK;AAEpB,KAAI,KAAK,QAAQ;AACf,MAAI,CAAC,KAAK,MAAM;AACd,WAAQ,IAAI,WAAW,YACrB,QAAQ,IAAI,SAAS,CAAC,OAAO,6BAA6B,GAAG,KAAK,uCAAuC,CAAC,IAAI,CAC/G;AACD;;AAGF,SAAO,OAAO;GACZ,aAAa,KAAK;GAClB,UAAU;GACV,eAAe;GACf;GACD,CAAC;YACO,CAAC,KAAK,SACf,WAAU,KAAK;EACb,MAAM;EACN,MAAM;EACN,SAAS;EACT,SAAS,OAAO,KAAK,QAAQ,CAAC,KAAK,SAAS;GAC1C,MAAM,QAAQ;GACd,OAAO;GACR,EAAE;EACJ,CAAC;AAGJ,KAAI,CAAC,KAAK,KACR,WAAU,KAAK;EACb,MAAM;EACN,MAAM;EACN,SAAS;EACT,WAAW,UAAkB;AAC3B,OAAI,SAAS,MAAM,MAAM,CAAC,SAAS,GACjC;QAAI,CAAC,8BAA8B,KAAK,MAAM,MAAM,CAAC,CACnD,QAAO;;AAGX,UAAO;;EAET,SAAS,UAAkB,MAAM,MAAM;EACxC,CAAC;AAGJ,KAAI,UAAU,SAAS,GAAG;EACxB,MAAMC,UAA8B,MAAM,SAAS,OAAO,UAAU;AACpE,SAAO,KAAK,QAAQ,QAAQ;AAC5B,aAAW,KAAK,YAAY,QAAQ;;AAGtC,SAAQ,IAAI,aAAa,YAAqB,QAAQ,OAAO,wCAAwC,CAAC;AAEtG,OAAM,OAAO;EACX,aAAa,QAAQ;EACrB,UAAU,YAAY;EACtB,eAAe;EACf;EACA,WAAW,KAAK;EACjB,CAAC;AAEF,SAAQ,KAAK,EAAE"}
package/dist/dev.mjs CHANGED
@@ -3,12 +3,12 @@ import { generateLockedData, getStepFiles, getStreamFiles } from "./generate-loc
3
3
  import { version } from "./version.mjs";
4
4
  import { identifyUser } from "./utils/analytics.mjs";
5
5
  import { loadMotiaConfig } from "./load-motia-config.mjs";
6
- import { instanceRedisMemoryServer, stopRedisMemoryServer } from "./redis-memory-manager.mjs";
7
6
  import { deployEndpoints } from "./cloud/endpoints.mjs";
8
7
  import { isTutorialDisabled, workbenchBase } from "./constants.mjs";
9
8
  import { createDevWatchers } from "./dev-watchers.mjs";
10
9
  import { processPlugins } from "./plugins/process-plugins.mjs";
11
10
  import "./plugins/index.mjs";
11
+ import { getRedisClient, stopRedisConnection } from "./redis/connection.mjs";
12
12
  import { createMermaidGenerator, createServer, getProjectIdentifier, trackEvent } from "@motiadev/core";
13
13
  import { flush } from "@amplitude/analytics-node";
14
14
  import { BullMQEventAdapter } from "@motiadev/adapter-bullmq-events";
@@ -40,7 +40,7 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
40
40
  }
41
41
  const motiaFileStoragePath = motiaFileStorageDir || ".motia";
42
42
  const appConfig = await loadMotiaConfig(baseDir);
43
- const redisClient = await instanceRedisMemoryServer(motiaFileStoragePath, true);
43
+ const redisClient = await getRedisClient(motiaFileStoragePath, appConfig);
44
44
  const adapters = {
45
45
  eventAdapter: appConfig.adapters?.events || new BullMQEventAdapter({
46
46
  connection: {
@@ -99,7 +99,7 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
99
99
  trackEvent("dev_server_shutdown", { reason: "SIGTERM" });
100
100
  motiaServer.server.close();
101
101
  await watcher.stop();
102
- await stopRedisMemoryServer();
102
+ await stopRedisConnection();
103
103
  await flush().promise;
104
104
  process.exit(0);
105
105
  });
@@ -107,7 +107,7 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
107
107
  trackEvent("dev_server_shutdown", { reason: "SIGINT" });
108
108
  motiaServer.server.close();
109
109
  await watcher.stop();
110
- await stopRedisMemoryServer();
110
+ await stopRedisConnection();
111
111
  await flush().promise;
112
112
  process.exit(0);
113
113
  });
package/dist/dev.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dev.mjs","names":["redisClient: RedisClientType","plugins: MotiaPlugin[]"],"sources":["../src/dev.ts"],"sourcesContent":["import { flush } from '@amplitude/analytics-node'\nimport { BullMQEventAdapter } from '@motiadev/adapter-bullmq-events'\nimport { RedisCronAdapter } from '@motiadev/adapter-redis-cron'\nimport { RedisStateAdapter } from '@motiadev/adapter-redis-state'\nimport { RedisStreamAdapterManager } from '@motiadev/adapter-redis-streams'\nimport {\n createMermaidGenerator,\n createServer,\n getProjectIdentifier,\n type MotiaPlugin,\n trackEvent,\n} from '@motiadev/core'\nimport type { RedisClientType } from 'redis'\nimport { deployEndpoints } from './cloud/endpoints'\nimport { isTutorialDisabled, workbenchBase } from './constants'\nimport { createDevWatchers } from './dev-watchers'\nimport { generateLockedData, getStepFiles, getStreamFiles } from './generate-locked-data'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { processPlugins } from './plugins'\nimport { instanceRedisMemoryServer, stopRedisMemoryServer } from './redis-memory-manager'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { identifyUser } from './utils/analytics'\nimport { version } from './version'\n\nexport const dev = async (\n port: number,\n hostname: string,\n disableVerbose: boolean,\n enableMermaid: boolean,\n motiaFileStorageDir?: string,\n): Promise<void> => {\n const baseDir = process.cwd()\n const isVerbose = !disableVerbose\n\n identifyUser()\n\n const stepFiles = [...getStepFiles(baseDir), ...getStreamFiles(baseDir)]\n const hasPythonFiles = stepFiles.some((file) => file.endsWith('.py'))\n\n trackEvent('dev_server_started', {\n port,\n verbose_mode: isVerbose,\n mermaid_enabled: enableMermaid,\n has_python_files: hasPythonFiles,\n total_step_files: stepFiles.length,\n project_name: getProjectIdentifier(baseDir),\n })\n\n if (hasPythonFiles) {\n activatePythonVenv({ baseDir, isVerbose })\n trackEvent('python_environment_activated')\n }\n\n const motiaFileStoragePath = motiaFileStorageDir || '.motia'\n\n const appConfig = await loadMotiaConfig(baseDir)\n\n const redisClient: RedisClientType = await instanceRedisMemoryServer(motiaFileStoragePath, true)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: {\n host: (redisClient.options.socket as { host?: string })?.host || 'localhost',\n port: (redisClient.options.socket as { port?: number })?.port || 6379,\n },\n prefix: 'motia:events',\n }),\n cronAdapter: appConfig.adapters?.cron || new RedisCronAdapter(redisClient),\n streamAdapter: appConfig.adapters?.streams || new RedisStreamAdapterManager(redisClient),\n }\n\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: adapters.streamAdapter,\n redisClient,\n streamAuth: appConfig.streamAuth,\n })\n\n const state = appConfig.adapters?.state || new RedisStateAdapter(redisClient)\n\n const config = { isVerbose }\n\n const motiaServer = createServer(lockedData, state, config, adapters, appConfig.app)\n const watcher = createDevWatchers(lockedData, motiaServer, motiaServer.motiaEventManager, motiaServer.cronManager)\n const plugins: MotiaPlugin[] = await processPlugins(motiaServer)\n\n // Initialize mermaid generator\n if (enableMermaid) {\n const mermaidGenerator = createMermaidGenerator(baseDir)\n mermaidGenerator.initialize(lockedData)\n trackEvent('mermaid_generator_initialized')\n }\n\n deployEndpoints(motiaServer, lockedData)\n\n motiaServer.app.get('/__motia', (_, res) => {\n const meta = {\n version,\n isDev: true,\n isTutorialDisabled,\n workbenchBase,\n }\n\n res //\n .header('Access-Control-Allow-Origin', '*')\n .header('Access-Control-Allow-Private-Network', 'true')\n .status(200)\n .json(meta)\n })\n\n trackEvent('dev_server_ready', {\n port,\n flows_count: lockedData.flows?.length || 0,\n steps_count: lockedData.activeSteps?.length || 0,\n flows: Object.keys(lockedData.flows || {}),\n steps: lockedData.activeSteps.map((step) => step.config.name),\n streams: Object.keys(lockedData.getStreams() || {}),\n runtime_version: version,\n environment: process.env.NODE_ENV || 'development',\n })\n\n const { applyMiddleware } = await import('@motiadev/workbench/middleware')\n\n await applyMiddleware({\n app: motiaServer.app,\n port,\n workbenchBase,\n plugins: plugins.flatMap((item) => item.workbench),\n })\n\n motiaServer.server.listen(port, hostname)\n console.log('🚀 Server ready and listening on port', port)\n console.log(`🔗 Open http://localhost:${port}${workbenchBase} to open workbench 🛠️`)\n\n process.on('SIGTERM', async () => {\n trackEvent('dev_server_shutdown', { reason: 'SIGTERM' })\n motiaServer.server.close()\n await watcher.stop()\n await stopRedisMemoryServer()\n await flush().promise\n process.exit(0)\n })\n\n process.on('SIGINT', async () => {\n trackEvent('dev_server_shutdown', { reason: 'SIGINT' })\n motiaServer.server.close()\n await watcher.stop()\n await stopRedisMemoryServer()\n await flush().promise\n process.exit(0)\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dev.mjs","names":["redisClient: RedisClientType","plugins: MotiaPlugin[]"],"sources":["../src/dev.ts"],"sourcesContent":["import { flush } from '@amplitude/analytics-node'\nimport { BullMQEventAdapter } from '@motiadev/adapter-bullmq-events'\nimport { RedisCronAdapter } from '@motiadev/adapter-redis-cron'\nimport { RedisStateAdapter } from '@motiadev/adapter-redis-state'\nimport { RedisStreamAdapterManager } from '@motiadev/adapter-redis-streams'\nimport {\n createMermaidGenerator,\n createServer,\n getProjectIdentifier,\n type MotiaPlugin,\n trackEvent,\n} from '@motiadev/core'\nimport type { RedisClientType } from 'redis'\nimport { deployEndpoints } from './cloud/endpoints'\nimport { isTutorialDisabled, workbenchBase } from './constants'\nimport { createDevWatchers } from './dev-watchers'\nimport { generateLockedData, getStepFiles, getStreamFiles } from './generate-locked-data'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { processPlugins } from './plugins'\nimport { getRedisClient, stopRedisConnection } from './redis/connection'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { identifyUser } from './utils/analytics'\nimport { version } from './version'\n\nexport const dev = async (\n port: number,\n hostname: string,\n disableVerbose: boolean,\n enableMermaid: boolean,\n motiaFileStorageDir?: string,\n): Promise<void> => {\n const baseDir = process.cwd()\n const isVerbose = !disableVerbose\n\n identifyUser()\n\n const stepFiles = [...getStepFiles(baseDir), ...getStreamFiles(baseDir)]\n const hasPythonFiles = stepFiles.some((file) => file.endsWith('.py'))\n\n trackEvent('dev_server_started', {\n port,\n verbose_mode: isVerbose,\n mermaid_enabled: enableMermaid,\n has_python_files: hasPythonFiles,\n total_step_files: stepFiles.length,\n project_name: getProjectIdentifier(baseDir),\n })\n\n if (hasPythonFiles) {\n activatePythonVenv({ baseDir, isVerbose })\n trackEvent('python_environment_activated')\n }\n\n const motiaFileStoragePath = motiaFileStorageDir || '.motia'\n\n const appConfig = await loadMotiaConfig(baseDir)\n\n const redisClient: RedisClientType = await getRedisClient(motiaFileStoragePath, appConfig)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: {\n host: (redisClient.options.socket as { host?: string })?.host || 'localhost',\n port: (redisClient.options.socket as { port?: number })?.port || 6379,\n },\n prefix: 'motia:events',\n }),\n cronAdapter: appConfig.adapters?.cron || new RedisCronAdapter(redisClient),\n streamAdapter: appConfig.adapters?.streams || new RedisStreamAdapterManager(redisClient),\n }\n\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: adapters.streamAdapter,\n redisClient,\n streamAuth: appConfig.streamAuth,\n })\n\n const state = appConfig.adapters?.state || new RedisStateAdapter(redisClient)\n\n const config = { isVerbose }\n\n const motiaServer = createServer(lockedData, state, config, adapters, appConfig.app)\n const watcher = createDevWatchers(lockedData, motiaServer, motiaServer.motiaEventManager, motiaServer.cronManager)\n const plugins: MotiaPlugin[] = await processPlugins(motiaServer)\n\n // Initialize mermaid generator\n if (enableMermaid) {\n const mermaidGenerator = createMermaidGenerator(baseDir)\n mermaidGenerator.initialize(lockedData)\n trackEvent('mermaid_generator_initialized')\n }\n\n deployEndpoints(motiaServer, lockedData)\n\n motiaServer.app.get('/__motia', (_, res) => {\n const meta = {\n version,\n isDev: true,\n isTutorialDisabled,\n workbenchBase,\n }\n\n res //\n .header('Access-Control-Allow-Origin', '*')\n .header('Access-Control-Allow-Private-Network', 'true')\n .status(200)\n .json(meta)\n })\n\n trackEvent('dev_server_ready', {\n port,\n flows_count: lockedData.flows?.length || 0,\n steps_count: lockedData.activeSteps?.length || 0,\n flows: Object.keys(lockedData.flows || {}),\n steps: lockedData.activeSteps.map((step) => step.config.name),\n streams: Object.keys(lockedData.getStreams() || {}),\n runtime_version: version,\n environment: process.env.NODE_ENV || 'development',\n })\n\n const { applyMiddleware } = await import('@motiadev/workbench/middleware')\n\n await applyMiddleware({\n app: motiaServer.app,\n port,\n workbenchBase,\n plugins: plugins.flatMap((item) => item.workbench),\n })\n\n motiaServer.server.listen(port, hostname)\n console.log('🚀 Server ready and listening on port', port)\n console.log(`🔗 Open http://localhost:${port}${workbenchBase} to open workbench 🛠️`)\n\n process.on('SIGTERM', async () => {\n trackEvent('dev_server_shutdown', { reason: 'SIGTERM' })\n motiaServer.server.close()\n await watcher.stop()\n await stopRedisConnection()\n await flush().promise\n process.exit(0)\n })\n\n process.on('SIGINT', async () => {\n trackEvent('dev_server_shutdown', { reason: 'SIGINT' })\n motiaServer.server.close()\n await watcher.stop()\n await stopRedisConnection()\n await flush().promise\n process.exit(0)\n })\n}\n"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { RedisMemoryManager } from "./memory-manager.mjs";
2
+ import { isExternalRedisConfig, shouldUseMemoryServer } from "./utils.mjs";
3
+ import { createClient } from "redis";
4
+
5
+ //#region src/redis/connection.ts
6
+ var RedisConnectionManager = class {
7
+ constructor() {
8
+ this.client = null;
9
+ }
10
+ async getConnectionInfo(baseDir, config) {
11
+ if (shouldUseMemoryServer(config)) return await new RedisMemoryManager().startServer(baseDir);
12
+ if (isExternalRedisConfig(config.redis)) return {
13
+ host: config.redis.host,
14
+ port: config.redis.port
15
+ };
16
+ return {
17
+ host: process.env.MOTIA_REDIS_HOST || "127.0.0.1",
18
+ port: parseInt(process.env.MOTIA_REDIS_PORT || "6379")
19
+ };
20
+ }
21
+ async connect(baseDir, config) {
22
+ if (this.client) return this.client;
23
+ const { host, port } = await this.getConnectionInfo(baseDir, config);
24
+ this.client = createClient({ socket: {
25
+ host,
26
+ port,
27
+ noDelay: true,
28
+ keepAlive: true,
29
+ reconnectStrategy: (retries) => {
30
+ if (retries > 10) return /* @__PURE__ */ new Error("Redis connection retry limit exceeded");
31
+ return Math.min(retries * 100, 3e3);
32
+ },
33
+ connectTimeout: 1e4
34
+ } });
35
+ return this.client;
36
+ }
37
+ async stop() {
38
+ if (this.client) {
39
+ await this.client.quit();
40
+ this.client = null;
41
+ }
42
+ }
43
+ isRunning() {
44
+ return !!this.client?.isOpen;
45
+ }
46
+ getClient() {
47
+ return this.client;
48
+ }
49
+ };
50
+ const manager = new RedisConnectionManager();
51
+ const getRedisClient = (baseDir, config) => manager.connect(baseDir, config);
52
+ const stopRedisConnection = () => manager.stop();
53
+
54
+ //#endregion
55
+ export { getRedisClient, stopRedisConnection };
56
+ //# sourceMappingURL=connection.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.mjs","names":[],"sources":["../../src/redis/connection.ts"],"sourcesContent":["import { createClient, type RedisClientType } from 'redis'\nimport type { LoadedMotiaConfig } from '../load-motia-config'\nimport { RedisMemoryManager } from './memory-manager'\nimport type { RedisConnectionInfo } from './types'\nimport { isExternalRedisConfig, shouldUseMemoryServer } from './utils'\n\nexport type { RedisConnectionInfo } from './types'\n\nclass RedisConnectionManager {\n private client: RedisClientType | null = null\n\n private async getConnectionInfo(baseDir: string, config: LoadedMotiaConfig): Promise<RedisConnectionInfo> {\n if (shouldUseMemoryServer(config)) {\n return await new RedisMemoryManager().startServer(baseDir)\n }\n if (isExternalRedisConfig(config.redis)) {\n return { host: config.redis.host, port: config.redis.port }\n }\n return { host: process.env.MOTIA_REDIS_HOST || '127.0.0.1', port: parseInt(process.env.MOTIA_REDIS_PORT || '6379') }\n }\n\n async connect(baseDir: string, config: LoadedMotiaConfig): Promise<RedisClientType> {\n if (this.client) {\n return this.client\n }\n\n const { host, port } = await this.getConnectionInfo(baseDir, config)\n\n this.client = createClient({\n socket: {\n host,\n port,\n noDelay: true,\n keepAlive: true,\n reconnectStrategy: (retries: number) => {\n if (retries > 10) {\n return new Error('Redis connection retry limit exceeded')\n }\n return Math.min(retries * 100, 3000)\n },\n connectTimeout: 10000,\n },\n })\n return this.client\n }\n\n async stop(): Promise<void> {\n if (this.client) {\n await this.client.quit()\n this.client = null\n }\n }\n\n isRunning(): boolean {\n return !!this.client?.isOpen\n }\n\n getClient(): RedisClientType | null {\n return this.client\n }\n}\n\nconst manager = new RedisConnectionManager()\n\nexport const getRedisClient = (baseDir: string, config: LoadedMotiaConfig): Promise<RedisClientType> =>\n manager.connect(baseDir, config)\n\nexport const stopRedisConnection = (): Promise<void> => manager.stop()\n"],"mappings":";;;;;AAQA,IAAM,yBAAN,MAA6B;;gBACc;;CAEzC,MAAc,kBAAkB,SAAiB,QAAyD;AACxG,MAAI,sBAAsB,OAAO,CAC/B,QAAO,MAAM,IAAI,oBAAoB,CAAC,YAAY,QAAQ;AAE5D,MAAI,sBAAsB,OAAO,MAAM,CACrC,QAAO;GAAE,MAAM,OAAO,MAAM;GAAM,MAAM,OAAO,MAAM;GAAM;AAE7D,SAAO;GAAE,MAAM,QAAQ,IAAI,oBAAoB;GAAa,MAAM,SAAS,QAAQ,IAAI,oBAAoB,OAAO;GAAE;;CAGtH,MAAM,QAAQ,SAAiB,QAAqD;AAClF,MAAI,KAAK,OACP,QAAO,KAAK;EAGd,MAAM,EAAE,MAAM,SAAS,MAAM,KAAK,kBAAkB,SAAS,OAAO;AAEpE,OAAK,SAAS,aAAa,EACzB,QAAQ;GACN;GACA;GACA,SAAS;GACT,WAAW;GACX,oBAAoB,YAAoB;AACtC,QAAI,UAAU,GACZ,wBAAO,IAAI,MAAM,wCAAwC;AAE3D,WAAO,KAAK,IAAI,UAAU,KAAK,IAAK;;GAEtC,gBAAgB;GACjB,EACF,CAAC;AACF,SAAO,KAAK;;CAGd,MAAM,OAAsB;AAC1B,MAAI,KAAK,QAAQ;AACf,SAAM,KAAK,OAAO,MAAM;AACxB,QAAK,SAAS;;;CAIlB,YAAqB;AACnB,SAAO,CAAC,CAAC,KAAK,QAAQ;;CAGxB,YAAoC;AAClC,SAAO,KAAK;;;AAIhB,MAAM,UAAU,IAAI,wBAAwB;AAE5C,MAAa,kBAAkB,SAAiB,WAC9C,QAAQ,QAAQ,SAAS,OAAO;AAElC,MAAa,4BAA2C,QAAQ,MAAM"}
@@ -0,0 +1,68 @@
1
+ import { mkdirSync } from "fs";
2
+ import { RedisMemoryServer } from "redis-memory-server";
3
+
4
+ //#region src/redis/memory-manager.ts
5
+ var RedisMemoryManager = class {
6
+ constructor() {
7
+ this.server = null;
8
+ this.client = null;
9
+ this.running = false;
10
+ }
11
+ registerCleanupHandlers() {
12
+ process.on("exit", async () => {
13
+ await this.stop();
14
+ });
15
+ process.on("SIGTERM", async () => {
16
+ await this.stop();
17
+ });
18
+ process.on("SIGINT", async () => {
19
+ await this.stop();
20
+ });
21
+ }
22
+ async startServer(baseDir) {
23
+ if (!this.server) try {
24
+ mkdirSync(baseDir, { recursive: true });
25
+ const instance = { args: [
26
+ "--appendonly",
27
+ "yes",
28
+ "--appendfsync",
29
+ "everysec",
30
+ "--save",
31
+ "\"\"",
32
+ "--dir",
33
+ baseDir
34
+ ] };
35
+ if (process.env.MOTIA_REDIS_PORT) instance.port = parseInt(process.env.MOTIA_REDIS_PORT || "6379");
36
+ this.server = new RedisMemoryServer({ instance });
37
+ this.running = true;
38
+ this.registerCleanupHandlers();
39
+ } catch (error) {
40
+ console.error("[Redis Memory Server] Failed to start:", error);
41
+ throw error;
42
+ }
43
+ return {
44
+ host: await this.server.getHost(),
45
+ port: await this.server.getPort()
46
+ };
47
+ }
48
+ async stop() {
49
+ if (this.server && this.running) try {
50
+ await this.server.stop();
51
+ this.running = false;
52
+ } catch (error) {
53
+ console.error("[Redis Memory Server] Error stopping:", error?.message);
54
+ } finally {
55
+ this.server = null;
56
+ }
57
+ }
58
+ isRunning() {
59
+ return this.running;
60
+ }
61
+ getClient() {
62
+ return this.client;
63
+ }
64
+ };
65
+
66
+ //#endregion
67
+ export { RedisMemoryManager };
68
+ //# sourceMappingURL=memory-manager.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-manager.mjs","names":["instance: RedisMemoryInstancePropT","error: unknown"],"sources":["../../src/redis/memory-manager.ts"],"sourcesContent":["import { mkdirSync } from 'fs'\nimport type { RedisClientType } from 'redis'\nimport { RedisMemoryServer } from 'redis-memory-server'\nimport type { RedisMemoryInstancePropT } from 'redis-memory-server/lib/types'\nimport type { RedisConnectionInfo } from './types'\n\nexport class RedisMemoryManager {\n private server: RedisMemoryServer | null = null\n private client: RedisClientType | null = null\n private running = false\n\n private registerCleanupHandlers(): void {\n process.on('exit', async () => {\n await this.stop()\n })\n process.on('SIGTERM', async () => {\n await this.stop()\n })\n\n process.on('SIGINT', async () => {\n await this.stop()\n })\n }\n\n async startServer(baseDir: string): Promise<RedisConnectionInfo> {\n if (!this.server) {\n try {\n mkdirSync(baseDir, { recursive: true })\n\n const instance: RedisMemoryInstancePropT = {\n args: ['--appendonly', 'yes', '--appendfsync', 'everysec', '--save', '\"\"', '--dir', baseDir],\n }\n\n if (process.env.MOTIA_REDIS_PORT) {\n instance.port = parseInt(process.env.MOTIA_REDIS_PORT || '6379')\n }\n\n this.server = new RedisMemoryServer({ instance })\n this.running = true\n this.registerCleanupHandlers()\n } catch (error) {\n console.error('[Redis Memory Server] Failed to start:', error)\n throw error\n }\n }\n\n const host = await this.server.getHost()\n const port = await this.server.getPort()\n\n return { host, port }\n }\n\n async stop(): Promise<void> {\n if (this.server && this.running) {\n try {\n await this.server.stop()\n this.running = false\n } catch (error: unknown) {\n console.error('[Redis Memory Server] Error stopping:', (error as Error)?.message)\n } finally {\n this.server = null\n }\n }\n }\n\n isRunning(): boolean {\n return this.running\n }\n\n getClient(): RedisClientType | null {\n return this.client\n }\n}\n"],"mappings":";;;;AAMA,IAAa,qBAAb,MAAgC;;gBACa;gBACF;iBACvB;;CAElB,AAAQ,0BAAgC;AACtC,UAAQ,GAAG,QAAQ,YAAY;AAC7B,SAAM,KAAK,MAAM;IACjB;AACF,UAAQ,GAAG,WAAW,YAAY;AAChC,SAAM,KAAK,MAAM;IACjB;AAEF,UAAQ,GAAG,UAAU,YAAY;AAC/B,SAAM,KAAK,MAAM;IACjB;;CAGJ,MAAM,YAAY,SAA+C;AAC/D,MAAI,CAAC,KAAK,OACR,KAAI;AACF,aAAU,SAAS,EAAE,WAAW,MAAM,CAAC;GAEvC,MAAMA,WAAqC,EACzC,MAAM;IAAC;IAAgB;IAAO;IAAiB;IAAY;IAAU;IAAM;IAAS;IAAQ,EAC7F;AAED,OAAI,QAAQ,IAAI,iBACd,UAAS,OAAO,SAAS,QAAQ,IAAI,oBAAoB,OAAO;AAGlE,QAAK,SAAS,IAAI,kBAAkB,EAAE,UAAU,CAAC;AACjD,QAAK,UAAU;AACf,QAAK,yBAAyB;WACvB,OAAO;AACd,WAAQ,MAAM,0CAA0C,MAAM;AAC9D,SAAM;;AAOV,SAAO;GAAE,MAHI,MAAM,KAAK,OAAO,SAAS;GAGzB,MAFF,MAAM,KAAK,OAAO,SAAS;GAEnB;;CAGvB,MAAM,OAAsB;AAC1B,MAAI,KAAK,UAAU,KAAK,QACtB,KAAI;AACF,SAAM,KAAK,OAAO,MAAM;AACxB,QAAK,UAAU;WACRC,OAAgB;AACvB,WAAQ,MAAM,yCAA0C,OAAiB,QAAQ;YACzE;AACR,QAAK,SAAS;;;CAKpB,YAAqB;AACnB,SAAO,KAAK;;CAGd,YAAoC;AAClC,SAAO,KAAK"}
@@ -0,0 +1,15 @@
1
+ //#region src/redis/utils.ts
2
+ const shouldUseMemoryServer = (config) => {
3
+ if (process.env.MOTIA_DISABLE_MEMORY_SERVER === "true") return false;
4
+ if (process.env.MOTIA_REDIS_HOST && process.env.MOTIA_REDIS_HOST !== "127.0.0.1") return false;
5
+ const redisConfig = config.redis;
6
+ if (redisConfig && "useMemoryServer" in redisConfig && redisConfig.useMemoryServer === false) return false;
7
+ return redisConfig?.useMemoryServer !== void 0 ? redisConfig.useMemoryServer === true : true;
8
+ };
9
+ const isExternalRedisConfig = (redisConfig) => {
10
+ return redisConfig !== void 0 && "host" in redisConfig && "port" in redisConfig;
11
+ };
12
+
13
+ //#endregion
14
+ export { isExternalRedisConfig, shouldUseMemoryServer };
15
+ //# sourceMappingURL=utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../../src/redis/utils.ts"],"sourcesContent":["import type { RedisConfig } from '@motiadev/core'\nimport type { LoadedMotiaConfig } from '../load-motia-config'\n\nexport const shouldUseMemoryServer = (config: LoadedMotiaConfig): boolean => {\n if (process.env.MOTIA_DISABLE_MEMORY_SERVER === 'true') {\n return false\n }\n\n if (process.env.MOTIA_REDIS_HOST && process.env.MOTIA_REDIS_HOST !== '127.0.0.1') {\n return false\n }\n\n const redisConfig = config.redis\n if (redisConfig && 'useMemoryServer' in redisConfig && redisConfig.useMemoryServer === false) {\n return false\n }\n\n return redisConfig?.useMemoryServer !== undefined ? redisConfig.useMemoryServer === true : true\n}\n\nexport const isExternalRedisConfig = (\n redisConfig: RedisConfig | undefined,\n): redisConfig is Extract<RedisConfig, { useMemoryServer?: false; host: string; port: number }> => {\n return redisConfig !== undefined && 'host' in redisConfig && 'port' in redisConfig\n}\n"],"mappings":";AAGA,MAAa,yBAAyB,WAAuC;AAC3E,KAAI,QAAQ,IAAI,gCAAgC,OAC9C,QAAO;AAGT,KAAI,QAAQ,IAAI,oBAAoB,QAAQ,IAAI,qBAAqB,YACnE,QAAO;CAGT,MAAM,cAAc,OAAO;AAC3B,KAAI,eAAe,qBAAqB,eAAe,YAAY,oBAAoB,MACrF,QAAO;AAGT,QAAO,aAAa,oBAAoB,SAAY,YAAY,oBAAoB,OAAO;;AAG7F,MAAa,yBACX,gBACiG;AACjG,QAAO,gBAAgB,UAAa,UAAU,eAAe,UAAU"}
package/dist/start.mjs CHANGED
@@ -2,10 +2,10 @@ import { activatePythonVenv } from "./utils/activate-python-env.mjs";
2
2
  import { generateLockedData, getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
3
3
  import { version } from "./version.mjs";
4
4
  import { loadMotiaConfig } from "./load-motia-config.mjs";
5
- import { instanceRedisMemoryServer, stopRedisMemoryServer } from "./redis-memory-manager.mjs";
6
5
  import { workbenchBase } from "./constants.mjs";
7
6
  import { processPlugins } from "./plugins/process-plugins.mjs";
8
7
  import "./plugins/index.mjs";
8
+ import { getRedisClient, stopRedisConnection } from "./redis/connection.mjs";
9
9
  import { createServer } from "@motiadev/core";
10
10
  import path from "path";
11
11
  import { BullMQEventAdapter } from "@motiadev/adapter-bullmq-events";
@@ -27,7 +27,7 @@ const start = async (port, hostname, disableVerbose, motiaFileStorageDir) => {
27
27
  const motiaFileStoragePath = motiaFileStorageDir || ".motia";
28
28
  const dotMotia = path.join(baseDir, motiaFileStoragePath);
29
29
  const appConfig = await loadMotiaConfig(baseDir);
30
- const redisClient = await instanceRedisMemoryServer(dotMotia);
30
+ const redisClient = await getRedisClient(dotMotia, appConfig, true);
31
31
  const adapters = {
32
32
  eventAdapter: appConfig.adapters?.events || new BullMQEventAdapter({ connection: {
33
33
  host: redisClient.options.socket?.host || "localhost",
@@ -61,12 +61,12 @@ const start = async (port, hostname, disableVerbose, motiaFileStorageDir) => {
61
61
  console.log(`🔗 Open http://${hostname}:${port}${workbenchBase} to open workbench 🛠️`);
62
62
  process.on("SIGTERM", async () => {
63
63
  motiaServer.server.close();
64
- await stopRedisMemoryServer();
64
+ await stopRedisConnection();
65
65
  process.exit(0);
66
66
  });
67
67
  process.on("SIGINT", async () => {
68
68
  motiaServer.server.close();
69
- await stopRedisMemoryServer();
69
+ await stopRedisConnection();
70
70
  process.exit(0);
71
71
  });
72
72
  };
@@ -1 +1 @@
1
- {"version":3,"file":"start.mjs","names":["redisClient: RedisClientType","plugins: MotiaPlugin[]"],"sources":["../src/start.ts"],"sourcesContent":["import { BullMQEventAdapter } from '@motiadev/adapter-bullmq-events'\nimport { RedisCronAdapter } from '@motiadev/adapter-redis-cron'\nimport { RedisStateAdapter } from '@motiadev/adapter-redis-state'\nimport { RedisStreamAdapterManager } from '@motiadev/adapter-redis-streams'\nimport { createServer, type MotiaPlugin } from '@motiadev/core'\nimport path from 'path'\nimport type { RedisClientType } from 'redis'\nimport { workbenchBase } from './constants'\nimport { generateLockedData, getStepFiles, getStreamFiles } from './generate-locked-data'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { processPlugins } from './plugins/index'\nimport { instanceRedisMemoryServer, stopRedisMemoryServer } from './redis-memory-manager'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { version } from './version'\n\nexport const start = async (\n port: number,\n hostname: string,\n disableVerbose: boolean,\n motiaFileStorageDir?: string,\n): Promise<void> => {\n const baseDir = process.cwd()\n const isVerbose = !disableVerbose\n\n const stepFiles = [...getStepFiles(baseDir), ...getStreamFiles(baseDir)]\n const hasPythonFiles = stepFiles.some((file) => file.endsWith('.py'))\n\n if (hasPythonFiles) {\n console.log('⚙️ Activating Python environment...')\n activatePythonVenv({ baseDir, isVerbose })\n }\n\n const motiaFileStoragePath = motiaFileStorageDir || '.motia'\n\n const dotMotia = path.join(baseDir, motiaFileStoragePath)\n const appConfig = await loadMotiaConfig(baseDir)\n\n const redisClient: RedisClientType = await instanceRedisMemoryServer(dotMotia)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: {\n host: (redisClient.options.socket as { host?: string })?.host || 'localhost',\n port: (redisClient.options.socket as { port?: number })?.port || 6379,\n },\n }),\n cronAdapter: appConfig.adapters?.cron || new RedisCronAdapter(redisClient),\n streamAdapter: appConfig.adapters?.streams || new RedisStreamAdapterManager(redisClient),\n }\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: adapters.streamAdapter,\n redisClient,\n streamAuth: appConfig.streamAuth,\n })\n\n const state = appConfig.adapters?.state || new RedisStateAdapter(redisClient)\n\n const config = { isVerbose, isDev: false, version }\n\n const motiaServer = createServer(lockedData, state, config, adapters, appConfig.app)\n const plugins: MotiaPlugin[] = await processPlugins(motiaServer)\n\n if (!process.env.MOTIA_DOCKER_DISABLE_WORKBENCH) {\n const { applyMiddleware } = await import('@motiadev/workbench/middleware')\n await applyMiddleware({\n app: motiaServer.app,\n port,\n workbenchBase,\n plugins: plugins.flatMap((item) => item.workbench),\n })\n }\n\n motiaServer.server.listen(port, hostname)\n console.log('🚀 Server ready and listening on port', port)\n console.log(`🔗 Open http://${hostname}:${port}${workbenchBase} to open workbench 🛠️`)\n\n process.on('SIGTERM', async () => {\n motiaServer.server.close()\n await stopRedisMemoryServer()\n process.exit(0)\n })\n\n process.on('SIGINT', async () => {\n motiaServer.server.close()\n await stopRedisMemoryServer()\n process.exit(0)\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,MAAa,QAAQ,OACnB,MACA,UACA,gBACA,wBACkB;CAClB,MAAM,UAAU,QAAQ,KAAK;CAC7B,MAAM,YAAY,CAAC;AAKnB,KAHkB,CAAC,GAAG,aAAa,QAAQ,EAAE,GAAG,eAAe,QAAQ,CAAC,CACvC,MAAM,SAAS,KAAK,SAAS,MAAM,CAAC,EAEjD;AAClB,UAAQ,IAAI,sCAAsC;AAClD,qBAAmB;GAAE;GAAS;GAAW,CAAC;;CAG5C,MAAM,uBAAuB,uBAAuB;CAEpD,MAAM,WAAW,KAAK,KAAK,SAAS,qBAAqB;CACzD,MAAM,YAAY,MAAM,gBAAgB,QAAQ;CAEhD,MAAMA,cAA+B,MAAM,0BAA0B,SAAS;CAE9E,MAAM,WAAW;EACf,cACE,UAAU,UAAU,UACpB,IAAI,mBAAmB,EACrB,YAAY;GACV,MAAO,YAAY,QAAQ,QAA8B,QAAQ;GACjE,MAAO,YAAY,QAAQ,QAA8B,QAAQ;GAClE,EACF,CAAC;EACJ,aAAa,UAAU,UAAU,QAAQ,IAAI,iBAAiB,YAAY;EAC1E,eAAe,UAAU,UAAU,WAAW,IAAI,0BAA0B,YAAY;EACzF;CAYD,MAAM,cAAc,aAXD,MAAM,mBAAmB;EAC1C,YAAY;EACZ,eAAe,SAAS;EACxB;EACA,YAAY,UAAU;EACvB,CAAC,EAEY,UAAU,UAAU,SAAS,IAAI,kBAAkB,YAAY,EAE9D;EAAE;EAAW,OAAO;EAAO;EAAS,EAES,UAAU,UAAU,IAAI;CACpF,MAAMC,UAAyB,MAAM,eAAe,YAAY;AAEhE,KAAI,CAAC,QAAQ,IAAI,gCAAgC;EAC/C,MAAM,EAAE,oBAAoB,MAAM,OAAO;AACzC,QAAM,gBAAgB;GACpB,KAAK,YAAY;GACjB;GACA;GACA,SAAS,QAAQ,SAAS,SAAS,KAAK,UAAU;GACnD,CAAC;;AAGJ,aAAY,OAAO,OAAO,MAAM,SAAS;AACzC,SAAQ,IAAI,yCAAyC,KAAK;AAC1D,SAAQ,IAAI,kBAAkB,SAAS,GAAG,OAAO,cAAc,wBAAwB;AAEvF,SAAQ,GAAG,WAAW,YAAY;AAChC,cAAY,OAAO,OAAO;AAC1B,QAAM,uBAAuB;AAC7B,UAAQ,KAAK,EAAE;GACf;AAEF,SAAQ,GAAG,UAAU,YAAY;AAC/B,cAAY,OAAO,OAAO;AAC1B,QAAM,uBAAuB;AAC7B,UAAQ,KAAK,EAAE;GACf"}
1
+ {"version":3,"file":"start.mjs","names":["redisClient: RedisClientType","plugins: MotiaPlugin[]"],"sources":["../src/start.ts"],"sourcesContent":["import { BullMQEventAdapter } from '@motiadev/adapter-bullmq-events'\nimport { RedisCronAdapter } from '@motiadev/adapter-redis-cron'\nimport { RedisStateAdapter } from '@motiadev/adapter-redis-state'\nimport { RedisStreamAdapterManager } from '@motiadev/adapter-redis-streams'\nimport { createServer, type MotiaPlugin } from '@motiadev/core'\nimport path from 'path'\nimport type { RedisClientType } from 'redis'\nimport { workbenchBase } from './constants'\nimport { generateLockedData, getStepFiles, getStreamFiles } from './generate-locked-data'\nimport { loadMotiaConfig } from './load-motia-config'\nimport { processPlugins } from './plugins/index'\nimport { getRedisClient, stopRedisConnection } from './redis/connection'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { version } from './version'\n\nexport const start = async (\n port: number,\n hostname: string,\n disableVerbose: boolean,\n motiaFileStorageDir?: string,\n): Promise<void> => {\n const baseDir = process.cwd()\n const isVerbose = !disableVerbose\n\n const stepFiles = [...getStepFiles(baseDir), ...getStreamFiles(baseDir)]\n const hasPythonFiles = stepFiles.some((file) => file.endsWith('.py'))\n\n if (hasPythonFiles) {\n console.log('⚙️ Activating Python environment...')\n activatePythonVenv({ baseDir, isVerbose })\n }\n\n const motiaFileStoragePath = motiaFileStorageDir || '.motia'\n\n const dotMotia = path.join(baseDir, motiaFileStoragePath)\n const appConfig = await loadMotiaConfig(baseDir)\n\n const redisClient: RedisClientType = await getRedisClient(dotMotia, appConfig, true)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: {\n host: (redisClient.options.socket as { host?: string })?.host || 'localhost',\n port: (redisClient.options.socket as { port?: number })?.port || 6379,\n },\n }),\n cronAdapter: appConfig.adapters?.cron || new RedisCronAdapter(redisClient),\n streamAdapter: appConfig.adapters?.streams || new RedisStreamAdapterManager(redisClient),\n }\n const lockedData = await generateLockedData({\n projectDir: baseDir,\n streamAdapter: adapters.streamAdapter,\n redisClient,\n streamAuth: appConfig.streamAuth,\n })\n\n const state = appConfig.adapters?.state || new RedisStateAdapter(redisClient)\n\n const config = { isVerbose, isDev: false, version }\n\n const motiaServer = createServer(lockedData, state, config, adapters, appConfig.app)\n const plugins: MotiaPlugin[] = await processPlugins(motiaServer)\n\n if (!process.env.MOTIA_DOCKER_DISABLE_WORKBENCH) {\n const { applyMiddleware } = await import('@motiadev/workbench/middleware')\n await applyMiddleware({\n app: motiaServer.app,\n port,\n workbenchBase,\n plugins: plugins.flatMap((item) => item.workbench),\n })\n }\n\n motiaServer.server.listen(port, hostname)\n console.log('🚀 Server ready and listening on port', port)\n console.log(`🔗 Open http://${hostname}:${port}${workbenchBase} to open workbench 🛠️`)\n\n process.on('SIGTERM', async () => {\n motiaServer.server.close()\n await stopRedisConnection()\n process.exit(0)\n })\n\n process.on('SIGINT', async () => {\n motiaServer.server.close()\n await stopRedisConnection()\n process.exit(0)\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,MAAa,QAAQ,OACnB,MACA,UACA,gBACA,wBACkB;CAClB,MAAM,UAAU,QAAQ,KAAK;CAC7B,MAAM,YAAY,CAAC;AAKnB,KAHkB,CAAC,GAAG,aAAa,QAAQ,EAAE,GAAG,eAAe,QAAQ,CAAC,CACvC,MAAM,SAAS,KAAK,SAAS,MAAM,CAAC,EAEjD;AAClB,UAAQ,IAAI,sCAAsC;AAClD,qBAAmB;GAAE;GAAS;GAAW,CAAC;;CAG5C,MAAM,uBAAuB,uBAAuB;CAEpD,MAAM,WAAW,KAAK,KAAK,SAAS,qBAAqB;CACzD,MAAM,YAAY,MAAM,gBAAgB,QAAQ;CAEhD,MAAMA,cAA+B,MAAM,eAAe,UAAU,WAAW,KAAK;CAEpF,MAAM,WAAW;EACf,cACE,UAAU,UAAU,UACpB,IAAI,mBAAmB,EACrB,YAAY;GACV,MAAO,YAAY,QAAQ,QAA8B,QAAQ;GACjE,MAAO,YAAY,QAAQ,QAA8B,QAAQ;GAClE,EACF,CAAC;EACJ,aAAa,UAAU,UAAU,QAAQ,IAAI,iBAAiB,YAAY;EAC1E,eAAe,UAAU,UAAU,WAAW,IAAI,0BAA0B,YAAY;EACzF;CAYD,MAAM,cAAc,aAXD,MAAM,mBAAmB;EAC1C,YAAY;EACZ,eAAe,SAAS;EACxB;EACA,YAAY,UAAU;EACvB,CAAC,EAEY,UAAU,UAAU,SAAS,IAAI,kBAAkB,YAAY,EAE9D;EAAE;EAAW,OAAO;EAAO;EAAS,EAES,UAAU,UAAU,IAAI;CACpF,MAAMC,UAAyB,MAAM,eAAe,YAAY;AAEhE,KAAI,CAAC,QAAQ,IAAI,gCAAgC;EAC/C,MAAM,EAAE,oBAAoB,MAAM,OAAO;AACzC,QAAM,gBAAgB;GACpB,KAAK,YAAY;GACjB;GACA;GACA,SAAS,QAAQ,SAAS,SAAS,KAAK,UAAU;GACnD,CAAC;;AAGJ,aAAY,OAAO,OAAO,MAAM,SAAS;AACzC,SAAQ,IAAI,yCAAyC,KAAK;AAC1D,SAAQ,IAAI,kBAAkB,SAAS,GAAG,OAAO,cAAc,wBAAwB;AAEvF,SAAQ,GAAG,WAAW,YAAY;AAChC,cAAY,OAAO,OAAO;AAC1B,QAAM,qBAAqB;AAC3B,UAAQ,KAAK,EAAE;GACf;AAEF,SAAQ,GAAG,UAAU,YAAY;AAC/B,cAAY,OAAO,OAAO;AAC1B,QAAM,qBAAqB;AAC3B,UAAQ,KAAK,EAAE;GACf"}
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.4-beta.169",
4
+ "version": "0.15.4-beta.170-500956",
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.4-beta.169",
50
- "@motiadev/adapter-redis-state": "0.15.4-beta.169",
51
- "@motiadev/core": "0.15.4-beta.169",
52
- "@motiadev/adapter-redis-streams": "0.15.4-beta.169",
53
- "@motiadev/stream-client-node": "0.15.4-beta.169",
54
- "@motiadev/adapter-redis-cron": "0.15.4-beta.169",
55
- "@motiadev/workbench": "0.15.4-beta.169"
49
+ "@motiadev/adapter-bullmq-events": "0.15.4-beta.170-500956",
50
+ "@motiadev/adapter-redis-cron": "0.15.4-beta.170-500956",
51
+ "@motiadev/adapter-redis-state": "0.15.4-beta.170-500956",
52
+ "@motiadev/stream-client-node": "0.15.4-beta.170-500956",
53
+ "@motiadev/adapter-redis-streams": "0.15.4-beta.170-500956",
54
+ "@motiadev/core": "0.15.4-beta.170-500956",
55
+ "@motiadev/workbench": "0.15.4-beta.170-500956"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@amplitude/analytics-types": "^2.9.2",
@@ -1,109 +0,0 @@
1
- import { mkdirSync } from "fs";
2
- import { createClient } from "redis";
3
- import { RedisMemoryServer } from "redis-memory-server";
4
-
5
- //#region src/redis-memory-manager.ts
6
- var RedisMemoryManager = class {
7
- constructor() {
8
- this.server = null;
9
- this.client = null;
10
- this.running = false;
11
- this.cleanupHandlersRegistered = false;
12
- }
13
- registerCleanupHandlers() {
14
- if (this.cleanupHandlersRegistered) return;
15
- process.on("exit", () => {
16
- if (this.client?.isOpen && this.running) this.client.quit().catch((error) => {
17
- console.error("[Redis Memory Server] Error closing client on exit:", error);
18
- });
19
- if (this.server && this.running) this.server.stop().catch((error) => {
20
- console.error("[Redis Memory Server] Error stopping on exit:", error);
21
- });
22
- });
23
- process.on("SIGTERM", async () => {
24
- await this.stop();
25
- });
26
- process.on("SIGINT", async () => {
27
- await this.stop();
28
- });
29
- this.cleanupHandlersRegistered = true;
30
- }
31
- async start(baseDir, autoStart) {
32
- if (this.client && this.running) return this.client;
33
- try {
34
- mkdirSync(baseDir, { recursive: true });
35
- const instance = {
36
- ip: process.env.MOTIA_REDIS_HOST || "127.0.0.1",
37
- args: [
38
- "--appendonly",
39
- "yes",
40
- "--save",
41
- "900 1",
42
- "--save",
43
- "300 10",
44
- "--save",
45
- "60 100",
46
- "--dir",
47
- baseDir
48
- ]
49
- };
50
- if (process.env.MOTIA_REDIS_PORT) instance.port = parseInt(process.env.MOTIA_REDIS_PORT || "6379");
51
- this.server = new RedisMemoryServer({
52
- instance,
53
- autoStart
54
- });
55
- const host = await this.server.getHost();
56
- const port = await this.server.getPort();
57
- this.client = createClient({ socket: {
58
- host,
59
- port,
60
- noDelay: true,
61
- keepAlive: true,
62
- reconnectStrategy: (retries) => {
63
- if (retries > 10) return /* @__PURE__ */ new Error("Redis connection retry limit exceeded");
64
- return Math.min(retries * 100, 3e3);
65
- },
66
- connectTimeout: 1e4
67
- } });
68
- this.client.on("error", (err) => {
69
- if (err.message.includes("ECONNRESET") || err.message.includes("ECONNREFUSED")) return;
70
- console.error("[Redis Memory Server] Client error:", err);
71
- });
72
- await this.client.connect();
73
- this.running = true;
74
- this.registerCleanupHandlers();
75
- console.log(`[Redis Memory Server] Started on ${host}:${port}`);
76
- return this.client;
77
- } catch (error) {
78
- console.error("[Redis Memory Server] Failed to start:", error);
79
- throw error;
80
- }
81
- }
82
- async stop() {
83
- if (this.client && this.running) try {
84
- if (this.client.isOpen) await this.client.quit().catch();
85
- this.client = null;
86
- } catch (error) {}
87
- if (this.server && this.running) try {
88
- await this.server.stop();
89
- this.running = false;
90
- } catch (error) {
91
- console.error("[Redis Memory Server] Error stopping:", error);
92
- } finally {
93
- this.server = null;
94
- }
95
- }
96
- isRunning() {
97
- return this.running;
98
- }
99
- getClient() {
100
- return this.client;
101
- }
102
- };
103
- const manager = new RedisMemoryManager();
104
- const instanceRedisMemoryServer = (baseDir, autoStart = true) => manager.start(baseDir, autoStart);
105
- const stopRedisMemoryServer = () => manager.stop();
106
-
107
- //#endregion
108
- export { instanceRedisMemoryServer, stopRedisMemoryServer };
109
- //# sourceMappingURL=redis-memory-manager.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"redis-memory-manager.mjs","names":["instance: RedisMemoryInstancePropT"],"sources":["../src/redis-memory-manager.ts"],"sourcesContent":["import { mkdirSync } from 'fs'\nimport { createClient, type RedisClientType } from 'redis'\nimport { RedisMemoryServer } from 'redis-memory-server'\nimport type { RedisMemoryInstancePropT } from 'redis-memory-server/lib/types'\n\nexport interface RedisConnectionInfo {\n host: string\n port: number\n}\n\nclass RedisMemoryManager {\n private server: RedisMemoryServer | null = null\n private client: RedisClientType | null = null\n private running = false\n private cleanupHandlersRegistered = false\n\n private registerCleanupHandlers(): void {\n if (this.cleanupHandlersRegistered) return\n\n process.on('exit', () => {\n if (this.client?.isOpen && this.running) {\n this.client.quit().catch((error: unknown) => {\n console.error('[Redis Memory Server] Error closing client on exit:', error)\n })\n }\n if (this.server && this.running) {\n this.server.stop().catch((error: unknown) => {\n console.error('[Redis Memory Server] Error stopping on exit:', error)\n })\n }\n })\n\n process.on('SIGTERM', async () => {\n await this.stop()\n })\n\n process.on('SIGINT', async () => {\n await this.stop()\n })\n\n this.cleanupHandlersRegistered = true\n }\n\n async start(baseDir: string, autoStart: boolean): Promise<RedisClientType> {\n if (this.client && this.running) {\n return this.client\n }\n\n try {\n mkdirSync(baseDir, { recursive: true })\n\n const instance: RedisMemoryInstancePropT = {\n ip: process.env.MOTIA_REDIS_HOST || '127.0.0.1',\n args: ['--appendonly', 'yes', '--save', '900 1', '--save', '300 10', '--save', '60 100', '--dir', baseDir],\n }\n\n if (process.env.MOTIA_REDIS_PORT) {\n instance.port = parseInt(process.env.MOTIA_REDIS_PORT || '6379')\n }\n\n this.server = new RedisMemoryServer({\n instance,\n autoStart,\n })\n\n const host = await this.server.getHost()\n const port = await this.server.getPort()\n\n this.client = createClient({\n socket: {\n host,\n port,\n noDelay: true,\n keepAlive: true,\n reconnectStrategy: (retries: number) => {\n if (retries > 10) {\n return new Error('Redis connection retry limit exceeded')\n }\n return Math.min(retries * 100, 3000)\n },\n connectTimeout: 10000,\n },\n })\n\n this.client.on('error', (err: Error) => {\n if (err.message.includes('ECONNRESET') || err.message.includes('ECONNREFUSED')) {\n return\n }\n console.error('[Redis Memory Server] Client error:', err)\n })\n\n await this.client.connect()\n\n this.running = true\n this.registerCleanupHandlers()\n console.log(`[Redis Memory Server] Started on ${host}:${port}`)\n\n return this.client\n } catch (error) {\n console.error('[Redis Memory Server] Failed to start:', error)\n throw error\n }\n }\n\n async stop(): Promise<void> {\n if (this.client && this.running) {\n try {\n if (this.client.isOpen) {\n await this.client.quit().catch()\n }\n this.client = null\n } catch (error) {}\n }\n\n if (this.server && this.running) {\n try {\n await this.server.stop()\n this.running = false\n } catch (error) {\n console.error('[Redis Memory Server] Error stopping:', error)\n } finally {\n this.server = null\n }\n }\n }\n\n isRunning(): boolean {\n return this.running\n }\n\n getClient(): RedisClientType | null {\n return this.client\n }\n}\n\nconst manager = new RedisMemoryManager()\n\nexport const instanceRedisMemoryServer = (baseDir: string, autoStart: boolean = true): Promise<RedisClientType> =>\n manager.start(baseDir, autoStart)\n\nexport const stopRedisMemoryServer = (): Promise<void> => manager.stop()\n"],"mappings":";;;;;AAUA,IAAM,qBAAN,MAAyB;;gBACoB;gBACF;iBACvB;mCACkB;;CAEpC,AAAQ,0BAAgC;AACtC,MAAI,KAAK,0BAA2B;AAEpC,UAAQ,GAAG,cAAc;AACvB,OAAI,KAAK,QAAQ,UAAU,KAAK,QAC9B,MAAK,OAAO,MAAM,CAAC,OAAO,UAAmB;AAC3C,YAAQ,MAAM,uDAAuD,MAAM;KAC3E;AAEJ,OAAI,KAAK,UAAU,KAAK,QACtB,MAAK,OAAO,MAAM,CAAC,OAAO,UAAmB;AAC3C,YAAQ,MAAM,iDAAiD,MAAM;KACrE;IAEJ;AAEF,UAAQ,GAAG,WAAW,YAAY;AAChC,SAAM,KAAK,MAAM;IACjB;AAEF,UAAQ,GAAG,UAAU,YAAY;AAC/B,SAAM,KAAK,MAAM;IACjB;AAEF,OAAK,4BAA4B;;CAGnC,MAAM,MAAM,SAAiB,WAA8C;AACzE,MAAI,KAAK,UAAU,KAAK,QACtB,QAAO,KAAK;AAGd,MAAI;AACF,aAAU,SAAS,EAAE,WAAW,MAAM,CAAC;GAEvC,MAAMA,WAAqC;IACzC,IAAI,QAAQ,IAAI,oBAAoB;IACpC,MAAM;KAAC;KAAgB;KAAO;KAAU;KAAS;KAAU;KAAU;KAAU;KAAU;KAAS;KAAQ;IAC3G;AAED,OAAI,QAAQ,IAAI,iBACd,UAAS,OAAO,SAAS,QAAQ,IAAI,oBAAoB,OAAO;AAGlE,QAAK,SAAS,IAAI,kBAAkB;IAClC;IACA;IACD,CAAC;GAEF,MAAM,OAAO,MAAM,KAAK,OAAO,SAAS;GACxC,MAAM,OAAO,MAAM,KAAK,OAAO,SAAS;AAExC,QAAK,SAAS,aAAa,EACzB,QAAQ;IACN;IACA;IACA,SAAS;IACT,WAAW;IACX,oBAAoB,YAAoB;AACtC,SAAI,UAAU,GACZ,wBAAO,IAAI,MAAM,wCAAwC;AAE3D,YAAO,KAAK,IAAI,UAAU,KAAK,IAAK;;IAEtC,gBAAgB;IACjB,EACF,CAAC;AAEF,QAAK,OAAO,GAAG,UAAU,QAAe;AACtC,QAAI,IAAI,QAAQ,SAAS,aAAa,IAAI,IAAI,QAAQ,SAAS,eAAe,CAC5E;AAEF,YAAQ,MAAM,uCAAuC,IAAI;KACzD;AAEF,SAAM,KAAK,OAAO,SAAS;AAE3B,QAAK,UAAU;AACf,QAAK,yBAAyB;AAC9B,WAAQ,IAAI,oCAAoC,KAAK,GAAG,OAAO;AAE/D,UAAO,KAAK;WACL,OAAO;AACd,WAAQ,MAAM,0CAA0C,MAAM;AAC9D,SAAM;;;CAIV,MAAM,OAAsB;AAC1B,MAAI,KAAK,UAAU,KAAK,QACtB,KAAI;AACF,OAAI,KAAK,OAAO,OACd,OAAM,KAAK,OAAO,MAAM,CAAC,OAAO;AAElC,QAAK,SAAS;WACP,OAAO;AAGlB,MAAI,KAAK,UAAU,KAAK,QACtB,KAAI;AACF,SAAM,KAAK,OAAO,MAAM;AACxB,QAAK,UAAU;WACR,OAAO;AACd,WAAQ,MAAM,yCAAyC,MAAM;YACrD;AACR,QAAK,SAAS;;;CAKpB,YAAqB;AACnB,SAAO,KAAK;;CAGd,YAAoC;AAClC,SAAO,KAAK;;;AAIhB,MAAM,UAAU,IAAI,oBAAoB;AAExC,MAAa,6BAA6B,SAAiB,YAAqB,SAC9E,QAAQ,MAAM,SAAS,UAAU;AAEnC,MAAa,8BAA6C,QAAQ,MAAM"}