motia 0.0.16 → 0.0.18

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/src/cli.js CHANGED
@@ -7,11 +7,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const commander_1 = require("commander");
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const defaultPort = 3000;
10
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
10
11
  require('dotenv/config');
12
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
11
13
  require('ts-node').register({
12
14
  transpileOnly: true,
13
15
  compilerOptions: { module: 'commonjs' },
14
16
  });
17
+ commander_1.program
18
+ .command('create')
19
+ .description('Create a new motia project')
20
+ .option('-n, --name <project name>', 'The name for your project, used to create a directory, use ./ or . to create it under the existing directory')
21
+ .option('-t, --template <template name>', 'The motia template name to use for your project')
22
+ .action(async (arg) => {
23
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
24
+ const { create } = require('./create');
25
+ await create({
26
+ projectName: arg.project ?? '.',
27
+ template: arg.template ?? undefined,
28
+ });
29
+ });
30
+ commander_1.program
31
+ .command('templates')
32
+ .description('Prints the list of available templates')
33
+ .action(async () => {
34
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
35
+ const { templates } = require('./src/create/templates');
36
+ console.log(`📝 Available templates: \n\n ${Object.keys(templates).join('\n')}`);
37
+ });
15
38
  commander_1.program
16
39
  .command('dev')
17
40
  .description('Start the development server')
@@ -23,7 +46,7 @@ commander_1.program
23
46
  process.env.LOG_LEVEL = 'debug';
24
47
  }
25
48
  const port = arg.port ? parseInt(arg.port) : defaultPort;
26
- const { dev } = require('./dev');
49
+ const { dev } = require('./dev'); // eslint-disable-line @typescript-eslint/no-require-imports
27
50
  await dev(port);
28
51
  });
29
52
  commander_1.program
@@ -31,14 +54,44 @@ commander_1.program
31
54
  .description('Get the generated config for your project')
32
55
  .option('-o, --output <port>', 'Path to write the generated config')
33
56
  .action(async (arg) => {
57
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
34
58
  const { generateLockedData } = require('./src/generate/locked-data');
35
59
  const lockedData = await generateLockedData(path_1.default.join(process.cwd()));
36
60
  if (arg.output) {
37
- const fs = require('fs');
61
+ const fs = require('fs'); // eslint-disable-line @typescript-eslint/no-require-imports
38
62
  fs.writeFileSync(path_1.default.join(arg.output, '.motia.generated.json'), JSON.stringify(lockedData, null, 2));
39
63
  console.log(`📄 Wrote locked data to ${arg.output}`);
40
64
  return;
41
65
  }
42
66
  console.log(JSON.stringify(lockedData, null, 2));
43
67
  });
68
+ commander_1.program
69
+ .command('emit')
70
+ .description('Emit an event to the Motia server')
71
+ .requiredOption('--topic <topic>', 'Event topic/type to emit')
72
+ .requiredOption('--message <message>', 'Event payload as JSON string')
73
+ .option('-p, --port <number>', 'Port number (default: 3000)')
74
+ .action(async (options) => {
75
+ const port = options.port || 3000;
76
+ const url = `http://localhost:${port}/emit`;
77
+ try {
78
+ const response = await fetch(url, {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify({
82
+ type: options.topic,
83
+ data: JSON.parse(options.message),
84
+ }),
85
+ });
86
+ if (!response.ok) {
87
+ throw new Error(`HTTP error! status: ${response.status}`);
88
+ }
89
+ const result = await response.json();
90
+ console.log('Event emitted successfully:', result);
91
+ }
92
+ catch (error) {
93
+ console.error('Error:', error instanceof Error ? error.message : 'Unknown error');
94
+ process.exit(1);
95
+ }
96
+ });
44
97
  commander_1.program.parse(process.argv);
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.create = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const child_process_1 = require("child_process");
10
+ const templates_1 = require("./templates");
11
+ const figlet_1 = __importDefault(require("figlet"));
12
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
13
+ require('ts-node').register({
14
+ transpileOnly: true,
15
+ compilerOptions: { module: 'commonjs' },
16
+ });
17
+ const checkIfFileExists = (dir, fileName) => {
18
+ return fs_1.default.existsSync(path_1.default.join(dir, fileName));
19
+ };
20
+ const checkIfDirectoryExists = (dir) => {
21
+ try {
22
+ return fs_1.default.statSync(dir).isDirectory();
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ };
28
+ const getPackageManager = (dir) => {
29
+ if (checkIfFileExists(dir, 'yarn.lock')) {
30
+ return 'yarn';
31
+ }
32
+ else if (checkIfFileExists(dir, 'pnpm-lock.yaml')) {
33
+ return 'pnpm';
34
+ }
35
+ else if (checkIfFileExists(dir, 'package-lock.json')) {
36
+ return 'npm';
37
+ }
38
+ else {
39
+ return 'unknown';
40
+ }
41
+ };
42
+ const executeCommand = async (command, rootDir) => {
43
+ return new Promise((resolve, reject) => {
44
+ (0, child_process_1.exec)(command, { cwd: rootDir }, (error, stdout, stderr) => {
45
+ if (error) {
46
+ console.error(`exec error: ${error}`);
47
+ reject(error);
48
+ return;
49
+ }
50
+ if (stdout)
51
+ console.log(stdout.toString());
52
+ if (stderr)
53
+ console.error(stderr.toString());
54
+ resolve(stdout);
55
+ });
56
+ });
57
+ };
58
+ const installRequiredDependencies = async (packageManager, rootDir) => {
59
+ console.log('📦 Installing dependencies...');
60
+ const installCommand = {
61
+ npm: 'npm install',
62
+ yarn: 'yarn add',
63
+ pnpm: 'pnpm add',
64
+ }[packageManager];
65
+ const dependencies = ['@motiadev/core', 'motia', '@motiadev/workbench', 'zod'].join(' ');
66
+ const devDependencies = ['ts-node@^10.9.2', 'typescript@^5.7.3'].join(' ');
67
+ try {
68
+ await executeCommand(`${installCommand} ${dependencies}`, rootDir);
69
+ await executeCommand(`${installCommand} -D ${devDependencies}`, rootDir);
70
+ console.log('✅ Dependencies installed');
71
+ }
72
+ catch (error) {
73
+ console.error('❌ Failed to install dependencies:', error);
74
+ }
75
+ };
76
+ const preparePackageManager = async (rootDir) => {
77
+ let packageManager = 'pnpm';
78
+ const detectedPackageManager = getPackageManager(rootDir);
79
+ if (detectedPackageManager !== 'unknown') {
80
+ console.log(`📦 Detected package manager: ${packageManager}`);
81
+ packageManager = detectedPackageManager;
82
+ }
83
+ else {
84
+ console.log(`📦 Using default package manager: ${packageManager}`);
85
+ const pnpmCheck = await executeCommand('pnpm --version', rootDir).catch(() => null);
86
+ if (!pnpmCheck) {
87
+ console.log('📦 pnpm is not installed. Installing pnpm...');
88
+ await executeCommand('npm install -g pnpm', rootDir);
89
+ console.log('✅ pnpm installed globally');
90
+ }
91
+ }
92
+ return packageManager;
93
+ };
94
+ const wrapUpSetup = async (rootDir) => {
95
+ const packageManager = await preparePackageManager(rootDir);
96
+ await installRequiredDependencies(packageManager, rootDir).catch((error) => {
97
+ console.log('❌ Failed to install dependencies');
98
+ console.error(error);
99
+ });
100
+ console.log(`\n\nTo start the development server, run:\n\n${packageManager} run dev\n\n`);
101
+ console.log('🚀 Project setup completed, happy coding!');
102
+ };
103
+ const create = async ({ projectName, template }) => {
104
+ console.log('\n\n' +
105
+ figlet_1.default.textSync('MOTIA', {
106
+ font: 'Larry 3D',
107
+ horizontalLayout: 'default',
108
+ verticalLayout: 'default',
109
+ width: 80,
110
+ whitespaceBreak: true,
111
+ }) +
112
+ '\n\n');
113
+ const isCurrentDir = !!projectName.match(/\.\/?/);
114
+ const rootDir = isCurrentDir ? process.cwd() : path_1.default.join(process.cwd(), projectName);
115
+ console.log(`🛠️ Welcome to motia! Let's get you setup.`);
116
+ if (!isCurrentDir && !checkIfDirectoryExists(rootDir)) {
117
+ fs_1.default.mkdirSync(path_1.default.join(rootDir));
118
+ console.log(`✅ ${projectName} directory created`);
119
+ }
120
+ else {
121
+ console.log(`📁 Using current directory`);
122
+ }
123
+ if (!checkIfFileExists(rootDir, 'package.json')) {
124
+ const packageJsonContent = {
125
+ name: 'my-motia-project',
126
+ description: '',
127
+ scripts: {
128
+ dev: 'motia dev',
129
+ 'dev:debug': 'motia dev --debug',
130
+ 'generate:config': 'motia get-config --output ./',
131
+ },
132
+ keywords: ['motia'],
133
+ };
134
+ fs_1.default.writeFileSync(path_1.default.join(rootDir, 'package.json'), JSON.stringify(packageJsonContent, null, 2));
135
+ console.log('✅ package.json created');
136
+ }
137
+ if (!checkIfFileExists(rootDir, 'tsconfig.json')) {
138
+ const tsconfigContent = {
139
+ compilerOptions: {
140
+ target: 'ES2020',
141
+ module: 'ESNext',
142
+ moduleResolution: 'Node',
143
+ esModuleInterop: true,
144
+ strict: true,
145
+ skipLibCheck: true,
146
+ forceConsistentCasingInFileNames: true,
147
+ resolveJsonModule: true,
148
+ allowJs: true,
149
+ outDir: 'dist',
150
+ rootDir: '.',
151
+ baseUrl: '.',
152
+ jsx: 'react-jsx',
153
+ },
154
+ include: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
155
+ exclude: ['node_modules', 'dist', 'tests'],
156
+ };
157
+ fs_1.default.writeFileSync(path_1.default.join(rootDir, 'tsconfig.json'), JSON.stringify(tsconfigContent, null, 2));
158
+ console.log('✅ tsconfig.json created');
159
+ }
160
+ const stepsDir = path_1.default.join(rootDir, 'steps');
161
+ if (!checkIfDirectoryExists(stepsDir)) {
162
+ fs_1.default.mkdirSync(stepsDir);
163
+ console.log('✅ steps directory created');
164
+ }
165
+ if (!template) {
166
+ await wrapUpSetup(rootDir);
167
+ return;
168
+ }
169
+ if (template && !(template in templates_1.templates)) {
170
+ console.error(`❌ Template ${template} not found, please use one of the following:`);
171
+ console.log(`📝 Available templates: \n\n ${Object.keys(templates_1.templates).join('\n')}`);
172
+ await wrapUpSetup(rootDir);
173
+ return;
174
+ }
175
+ await templates_1.templates[template](stepsDir);
176
+ await wrapUpSetup(rootDir);
177
+ };
178
+ exports.create = create;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateDefaultTemplateSteps = exports.generateStepFiles = void 0;
37
+ const fs_1 = require("fs");
38
+ const path = __importStar(require("path"));
39
+ const generateStepFiles = async (flowDir, files) => {
40
+ try {
41
+ for (const [fileName, content] of Object.entries(files)) {
42
+ const filePath = path.join(flowDir, fileName);
43
+ await fs_1.promises.writeFile(filePath, content, 'utf8');
44
+ console.log(`Step ${filePath} has been created.`);
45
+ }
46
+ }
47
+ catch (error) {
48
+ console.error('Error generating template files:', error);
49
+ }
50
+ };
51
+ exports.generateStepFiles = generateStepFiles;
52
+ const generateDefaultTemplateSteps = async (flowDir) => {
53
+ const stepFiles = {
54
+ 'one.step.ts': await fs_1.promises.readFile(path.join(__dirname, 'steps', 'one.step.txt'), 'utf8'),
55
+ 'two.step.ts': await fs_1.promises.readFile(path.join(__dirname, 'steps', 'two.step.txt'), 'utf8'),
56
+ 'api.step.ts': await fs_1.promises.readFile(path.join(__dirname, 'steps', 'api.step.txt'), 'utf8'),
57
+ };
58
+ await (0, exports.generateStepFiles)(flowDir, stepFiles);
59
+ };
60
+ exports.generateDefaultTemplateSteps = generateDefaultTemplateSteps;
@@ -0,0 +1,29 @@
1
+ import { ApiRouteConfig, StepHandler } from '@motiadev/core'
2
+ import { z } from 'zod'
3
+
4
+ const inputSchema = z.object({})
5
+
6
+ export const config: ApiRouteConfig = {
7
+ type: 'api',
8
+ name: 'default flow api trigger',
9
+ description: 'default template api trigger',
10
+ path: '/default',
11
+ method: 'POST',
12
+ emits: ['test-state'],
13
+ bodySchema: inputSchema,
14
+ flows: ['default'],
15
+ }
16
+
17
+ export const handler: StepHandler<typeof config> = async (req, { logger, emit }) => {
18
+ logger.info('processing default flow api step', req)
19
+
20
+ await emit({
21
+ type: 'test-state',
22
+ data: {},
23
+ })
24
+
25
+ return {
26
+ status: 200,
27
+ body: { message: 'test-state topic emitted' },
28
+ }
29
+ }
@@ -0,0 +1,29 @@
1
+ import { EventConfig, StepHandler } from '@motiadev/core'
2
+ import { z } from 'zod'
3
+ import equal from 'deep-equal'
4
+
5
+ type Input = typeof inputSchema
6
+
7
+ const inputSchema = z.object({})
8
+
9
+ export const config: EventConfig<Input> = {
10
+ type: 'event',
11
+ name: 'Set state change',
12
+ description: 'set a state change for evaluation',
13
+ subscribes: ['test-state'],
14
+ emits: ['check-state-change'],
15
+ input: inputSchema,
16
+ flows: ['default'],
17
+ }
18
+
19
+ export const handler: StepHandler<typeof config> = async (input, { traceId, logger, state, emit }) => {
20
+ logger.info('step one, set a value in state')
21
+
22
+ const message = 'welcome to motia!';
23
+ await state.set<any>(traceId, 'test', message)
24
+
25
+ await emit({
26
+ type: 'check-state-change',
27
+ data: {key: 'test', expected: message}
28
+ })
29
+ }
@@ -0,0 +1,32 @@
1
+ import { EventConfig, StepHandler } from '@motiadev/core'
2
+ import { z } from 'zod'
3
+ import equal from 'deep-equal'
4
+
5
+ type Input = typeof inputSchema
6
+
7
+ const inputSchema = z.object({
8
+ key: z.string(),
9
+ expected: z.optional(z.unknown()),
10
+ })
11
+
12
+ export const config: EventConfig<Input> = {
13
+ type: 'event',
14
+ name: 'Check state change',
15
+ description: 'check state change',
16
+ subscribes: ['check-state-change'],
17
+ emits: [],
18
+ input: inputSchema,
19
+ flows: ['default'],
20
+ }
21
+
22
+ export const handler: StepHandler<typeof config> = async (input, { traceId, logger, state }) => {
23
+ logger.info('received check-state-change event', input)
24
+
25
+ const value = await state.get<any>(traceId, input.key)
26
+
27
+ if (value !== input.expected) {
28
+ logger.error(`🔴 the provided value for the state key ${input.key} does not match`, { value, expected: input.expected })
29
+ } else {
30
+ logger.info(`🟢 the provided value matches the state value for key ${input.key} 🏁`)
31
+ }
32
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.templates = void 0;
4
+ const generate_1 = require("./default/generate");
5
+ exports.templates = {
6
+ default: generate_1.generateDefaultTemplateSteps,
7
+ };
package/dist/src/dev.js CHANGED
@@ -7,6 +7,7 @@ exports.dev = void 0;
7
7
  const core_1 = require("@motiadev/core");
8
8
  const generate_locked_data_1 = require("./generate-locked-data");
9
9
  const path_1 = __importDefault(require("path"));
10
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
10
11
  require('ts-node').register({
11
12
  transpileOnly: true,
12
13
  compilerOptions: { module: 'commonjs' },
@@ -25,6 +26,7 @@ const dev = async (port) => {
25
26
  server.listen(port);
26
27
  console.log('🚀 Server ready and listening on port', port);
27
28
  console.log(`🔗 Open http://localhost:${port}/ to open workbench 🛠️`);
29
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
28
30
  const { applyMiddleware } = require('@motiadev/workbench/dist/middleware');
29
31
  await applyMiddleware(app);
30
32
  // 6) Gracefully shut down on SIGTERM
@@ -10,8 +10,9 @@ const path_1 = __importDefault(require("path"));
10
10
  const yaml_1 = __importDefault(require("yaml"));
11
11
  const core_1 = require("@motiadev/core");
12
12
  const version = `${(0, crypto_1.randomUUID)()}:${Math.floor(Date.now() / 1000)}`;
13
- const baseFlowRegex = new RegExp(/flows\"?\s?.*\s*\[([^\]]+)\]/);
13
+ const baseFlowRegex = new RegExp(/flows"?\s?.*\s*\[([^\]]+)\]/);
14
14
  // Helper function to read config.yml
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
16
  const readConfig = (configPath) => {
16
17
  if (!fs_1.default.existsSync(configPath)) {
17
18
  console.warn(`Config file not found at ${configPath}`);
@@ -43,7 +44,7 @@ const collectFlows = async (baseDir) => {
43
44
  if (item.isDirectory()) {
44
45
  steps = steps.concat(await collectFlows(itemPath));
45
46
  }
46
- else if (!!item.name.match(/\.step\.(ts|js|py|rb)$/)) {
47
+ else if (item.name.match(/\.step\.(ts|js|py|rb)$/)) {
47
48
  const fileContent = fs_1.default.readFileSync(itemPath, 'utf-8');
48
49
  const flowMatch = fileContent.match(baseFlowRegex);
49
50
  const config = await extractStepConfig(itemPath);
package/package.json CHANGED
@@ -1,25 +1,29 @@
1
1
  {
2
2
  "name": "motia",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "motia": "dist/src/cli.js"
7
7
  },
8
8
  "dependencies": {
9
- "dotenv": "^16.4.7",
10
9
  "commander": "^13.0.0",
10
+ "dotenv": "^16.4.7",
11
+ "figlet": "^1.8.0",
11
12
  "ts-node": "^10.9.2",
12
13
  "yaml": "^2.7.0",
13
- "@motiadev/core": "0.0.16",
14
- "@motiadev/workbench": "0.0.16"
14
+ "@motiadev/core": "0.0.18",
15
+ "@motiadev/workbench": "0.0.18"
15
16
  },
16
17
  "devDependencies": {
18
+ "@types/figlet": "^1.7.0",
17
19
  "@types/jest": "^29.5.14",
18
20
  "jest": "^29.7.0",
19
21
  "ts-jest": "^29.2.5",
20
22
  "typescript": "^5.7.2"
21
23
  },
22
24
  "scripts": {
23
- "build": "rm -rf dist && tsc"
25
+ "move:templates": "sh scripts/move-templates.sh",
26
+ "build": "rm -rf dist && tsc && pnpm run move:templates",
27
+ "lint": "eslint --config ../../eslint.config.js"
24
28
  }
25
29
  }
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+
3
+ # Define source and destination directories
4
+ SRC_DIR="$(dirname "$0")/../src/create/templates"
5
+ DEST_DIR="$(dirname "$0")/../dist/src/create/templates"
6
+
7
+ # Create the destination directory if it doesn't exist
8
+ mkdir -p "$DEST_DIR"
9
+
10
+ # Copy all .txt files while preserving the directory structure
11
+ find "$SRC_DIR" -type f -name "*.txt" | while read -r file; do
12
+ # Get the relative path of the file
13
+ rel_path="${file#$SRC_DIR/}"
14
+ # Create the destination directory for the file
15
+ mkdir -p "$DEST_DIR/$(dirname "$rel_path")"
16
+ # Copy the file to the destination directory
17
+ cp "$file" "$DEST_DIR/$rel_path"
18
+ done
19
+
20
+ echo "All .txt files have been copied successfully."