motia 0.15.5-beta.174-720958 → 0.15.5-beta.174-093524
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 +9 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/cloud/config-utils.mjs +1 -1
- package/dist/cloud/config-utils.mjs.map +1 -1
- package/dist/cloud/new-deployment/build.mjs +11 -1
- package/dist/cloud/new-deployment/build.mjs.map +1 -1
- package/dist/create/index.mjs +28 -12
- package/dist/create/index.mjs.map +1 -1
- package/dist/cursor-rules/dot-files/.claude/agents/motia-developer.md +20 -11
- package/dist/cursor-rules/dot-files/.cursor/architecture/architecture.mdc +124 -28
- package/dist/cursor-rules/dot-files/.cursor/rules/motia/motia-config.mdc +66 -0
- package/dist/cursor-rules/dot-files/AGENTS.md +31 -16
- package/dist/cursor-rules/dot-files/CLAUDE.md +7 -2
- package/dist/cursor-rules/dot-files/opencode.json +1 -0
- package/dist/dev.mjs +8 -2
- package/dist/dev.mjs.map +1 -1
- package/dist/generate-locked-data.d.mts.map +1 -1
- package/dist/generate-locked-data.mjs +0 -2
- package/dist/generate-locked-data.mjs.map +1 -1
- package/dist/install.mjs +1 -1
- package/dist/plugins/install-plugin-dependencies.mjs +4 -3
- package/dist/plugins/install-plugin-dependencies.mjs.map +1 -1
- package/dist/start.mjs +8 -2
- package/dist/start.mjs.map +1 -1
- package/dist/utils/activate-python-env.mjs +9 -15
- package/dist/utils/activate-python-env.mjs.map +1 -1
- package/dist/utils/get-package-manager.mjs +42 -5
- package/dist/utils/get-package-manager.mjs.map +1 -1
- package/dist/utils/validate-python-environment.mjs +81 -0
- package/dist/utils/validate-python-environment.mjs.map +1 -0
- package/dist/watcher.mjs +1 -3
- package/dist/watcher.mjs.map +1 -1
- package/package.json +8 -8
|
@@ -7,6 +7,43 @@ alwaysApply: false
|
|
|
7
7
|
|
|
8
8
|
The `motia.config.ts` file is the central configuration file for your Motia application. It allows you to customize plugins, adapters, stream authentication, and Express app settings.
|
|
9
9
|
|
|
10
|
+
## Critical Requirement: package.json Must Use ES Modules
|
|
11
|
+
|
|
12
|
+
**All Motia projects MUST have `"type": "module"` in their `package.json`.**
|
|
13
|
+
|
|
14
|
+
Motia uses ES modules internally and requires this setting to function correctly. Without it, you may encounter import/export errors during runtime.
|
|
15
|
+
|
|
16
|
+
### Correct package.json Setup
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"name": "my-motia-project",
|
|
21
|
+
"description": "My Motia application",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"postinstall": "motia install",
|
|
25
|
+
"dev": "motia dev",
|
|
26
|
+
"start": "motia start",
|
|
27
|
+
"build": "motia build",
|
|
28
|
+
"generate-types": "motia generate-types"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Migration from Existing Projects
|
|
34
|
+
|
|
35
|
+
If you have an existing Motia project, ensure you add `"type": "module"` to your `package.json`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"name": "my-project",
|
|
40
|
+
"type": "module", // ← Add this line
|
|
41
|
+
"scripts": {
|
|
42
|
+
"dev": "motia dev"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
10
47
|
## Creating the Configuration File
|
|
11
48
|
|
|
12
49
|
Create a `motia.config.ts` file in the root of your project:
|
|
@@ -157,6 +194,21 @@ export default config({
|
|
|
157
194
|
|
|
158
195
|
### Creating a Local Plugin
|
|
159
196
|
|
|
197
|
+
**Project structure:**
|
|
198
|
+
```
|
|
199
|
+
project/
|
|
200
|
+
├── src/ # Steps can be in /src or /steps
|
|
201
|
+
│ └── api/
|
|
202
|
+
│ └── example.step.ts
|
|
203
|
+
├── plugins/
|
|
204
|
+
│ └── my-plugin/
|
|
205
|
+
│ ├── components/
|
|
206
|
+
│ │ └── my-plugin-panel.tsx
|
|
207
|
+
│ └── index.ts
|
|
208
|
+
└── motia.config.ts
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Plugin implementation (`plugins/my-plugin/index.ts`):**
|
|
160
212
|
```typescript
|
|
161
213
|
import path from 'node:path'
|
|
162
214
|
import { config, type MotiaPlugin, type MotiaPluginContext } from 'motia'
|
|
@@ -195,6 +247,20 @@ export default config({
|
|
|
195
247
|
})
|
|
196
248
|
```
|
|
197
249
|
|
|
250
|
+
### Common Plugin Errors
|
|
251
|
+
|
|
252
|
+
**Error: Component not found**
|
|
253
|
+
- **Cause**: `packageName` doesn't match the actual folder structure
|
|
254
|
+
- **Solution**: Ensure `packageName: '~/plugins/my-plugin'` matches `plugins/my-plugin/` folder
|
|
255
|
+
|
|
256
|
+
**Error: Plugin not loading in workbench**
|
|
257
|
+
- **Cause**: Plugin function not exported correctly
|
|
258
|
+
- **Solution**: Use `export default function` in plugin's `index.ts`
|
|
259
|
+
|
|
260
|
+
**Error: Module resolution failed**
|
|
261
|
+
- **Cause**: Using incorrect casing in folder/file names
|
|
262
|
+
- **Solution**: Use `kebab-case` for folders/files, `PascalCase` for React components
|
|
263
|
+
|
|
198
264
|
## Adapters
|
|
199
265
|
|
|
200
266
|
Adapters allow you to customize the underlying infrastructure for state management, event handling, cron jobs, and streams. This is useful for horizontal scaling or using custom storage backends.
|
|
@@ -40,6 +40,7 @@ These guides are written in markdown and can be read by any AI coding tool. The
|
|
|
40
40
|
|
|
41
41
|
Read these files in `.cursor/rules/motia/` for detailed patterns:
|
|
42
42
|
|
|
43
|
+
- **`motia-config.mdc`** - Essential project setup, package.json requirements, plugin naming
|
|
43
44
|
- **`api-steps.mdc`** - Creating HTTP endpoints with schemas, validation, and middleware
|
|
44
45
|
- **`event-steps.mdc`** - Background task processing and event-driven workflows
|
|
45
46
|
- **`cron-steps.mdc`** - Scheduled tasks with cron expressions
|
|
@@ -62,31 +63,44 @@ Architecture guides in `.cursor/architecture/`:
|
|
|
62
63
|
|
|
63
64
|
### Project Structure
|
|
64
65
|
|
|
66
|
+
Motia discovers steps from both `/src` and `/steps` folders. Modern projects typically use `/src`:
|
|
67
|
+
|
|
68
|
+
**Recommended Structure (using `/src`):**
|
|
65
69
|
```
|
|
66
70
|
project/
|
|
67
71
|
├── .cursor/rules/ # DETAILED GUIDES - Read these first!
|
|
68
|
-
├── steps/ # All step definitions
|
|
69
|
-
│ ├── api/ # API endpoints
|
|
70
|
-
│ │ └── api.step.ts
|
|
71
|
-
│ │ └── api.step.js
|
|
72
|
-
│ │ └── api_step.py
|
|
73
|
-
│ ├── events/ # Events
|
|
74
|
-
│ │ └── events.step.ts
|
|
75
|
-
│ │ └── events.step.js
|
|
76
|
-
│ │ └── events_step.py
|
|
77
|
-
│ └── cron/ # Scheduled tasks
|
|
78
|
-
│ │ └── cron.step.ts
|
|
79
|
-
│ │ └── cron.step.js
|
|
80
|
-
│ │ └── cron_step.py
|
|
81
|
-
├── middlewares/ # Reusable middleware
|
|
82
|
-
│ └── middleware.middleware.ts
|
|
83
72
|
├── src/
|
|
73
|
+
│ ├── api/ # API endpoints
|
|
74
|
+
│ │ ├── users.step.ts
|
|
75
|
+
│ │ ├── orders.step.js
|
|
76
|
+
│ │ └── products_step.py
|
|
77
|
+
│ ├── events/ # Event handlers
|
|
78
|
+
│ │ ├── order-processing.step.ts
|
|
79
|
+
│ │ └── notifications_step.py
|
|
80
|
+
│ ├── cron/ # Scheduled tasks
|
|
81
|
+
│ │ └── cleanup.step.ts
|
|
84
82
|
│ ├── services/ # Business logic
|
|
83
|
+
│ ├── repositories/ # Data access
|
|
85
84
|
│ └── utils/ # Utilities
|
|
86
|
-
├──
|
|
85
|
+
├── middlewares/ # Reusable middleware
|
|
86
|
+
│ └── auth.middleware.ts
|
|
87
|
+
├── motia.config.ts # Motia configuration
|
|
87
88
|
└── types.d.ts # Auto-generated types
|
|
88
89
|
```
|
|
89
90
|
|
|
91
|
+
**Alternative Structure (using `/steps`):**
|
|
92
|
+
```
|
|
93
|
+
project/
|
|
94
|
+
├── steps/ # Step definitions
|
|
95
|
+
│ ├── api/
|
|
96
|
+
│ ├── events/
|
|
97
|
+
│ └── cron/
|
|
98
|
+
├── src/
|
|
99
|
+
│ ├── services/
|
|
100
|
+
│ └── utils/
|
|
101
|
+
└── motia.config.ts
|
|
102
|
+
```
|
|
103
|
+
|
|
90
104
|
### Step Naming Conventions
|
|
91
105
|
|
|
92
106
|
**TypeScript/JavaScript:** `my-step.step.ts` (kebab-case)
|
|
@@ -194,6 +208,7 @@ When working on Motia projects, follow this pattern:
|
|
|
194
208
|
|
|
195
209
|
## Critical Rules
|
|
196
210
|
|
|
211
|
+
- **ALWAYS** ensure `package.json` has `"type": "module"` (read `motia-config.mdc` for details)
|
|
197
212
|
- **ALWAYS** read `.cursor/rules/` guides before writing step code
|
|
198
213
|
- **ALWAYS** run `npx motia generate-types` after modifying configs
|
|
199
214
|
- **ALWAYS** list emits in config before using them in handlers
|
|
@@ -12,7 +12,7 @@ This project has detailed development guides in **`.cursor/rules/`** directory.
|
|
|
12
12
|
|
|
13
13
|
**A pre-configured subagent is ready!**
|
|
14
14
|
|
|
15
|
-
The `motia-developer` subagent in `.claude/agents/` automatically references all
|
|
15
|
+
The `motia-developer` subagent in `.claude/agents/` automatically references all 11 cursor rules when coding.
|
|
16
16
|
|
|
17
17
|
Use it: `/agents` → select `motia-developer`
|
|
18
18
|
|
|
@@ -27,10 +27,13 @@ Read .cursor/rules/motia/api-steps.mdc and create an API endpoint
|
|
|
27
27
|
for user registration following the patterns shown.
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
## Available Guides (
|
|
30
|
+
## Available Guides (11 Comprehensive Files)
|
|
31
31
|
|
|
32
32
|
All guides in `.cursor/rules/` with **TypeScript, JavaScript, and Python** examples:
|
|
33
33
|
|
|
34
|
+
**Configuration** (`.cursor/rules/motia/`):
|
|
35
|
+
- `motia-config.mdc` - Essential project setup, package.json requirements, plugin naming
|
|
36
|
+
|
|
34
37
|
**Step Types** (`.cursor/rules/motia/`):
|
|
35
38
|
- `api-steps.mdc`, `event-steps.mdc`, `cron-steps.mdc`
|
|
36
39
|
|
|
@@ -45,6 +48,8 @@ All guides in `.cursor/rules/` with **TypeScript, JavaScript, and Python** examp
|
|
|
45
48
|
|
|
46
49
|
See `AGENTS.md` in this directory for a quick overview and links to specific guides.
|
|
47
50
|
|
|
51
|
+
**Important**: Motia discovers steps from both `/src` and `/steps` folders. Modern projects use `/src` for a familiar structure.
|
|
52
|
+
|
|
48
53
|
## Key Commands
|
|
49
54
|
|
|
50
55
|
```bash
|
package/dist/dev.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { activatePythonVenv } from "./utils/activate-python-env.mjs";
|
|
2
1
|
import { generateLockedData, getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
|
|
3
2
|
import { version } from "./version.mjs";
|
|
4
3
|
import { identifyUser } from "./utils/analytics.mjs";
|
|
4
|
+
import { activatePythonVenv } from "./utils/activate-python-env.mjs";
|
|
5
|
+
import { validatePythonEnvironment } from "./utils/validate-python-environment.mjs";
|
|
5
6
|
import { loadMotiaConfig } from "./load-motia-config.mjs";
|
|
6
7
|
import { deployEndpoints } from "./cloud/endpoints.mjs";
|
|
7
8
|
import { isTutorialDisabled, workbenchBase } from "./constants.mjs";
|
|
@@ -31,7 +32,12 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
|
|
|
31
32
|
total_step_files: stepFiles.length,
|
|
32
33
|
project_name: getProjectIdentifier(baseDir)
|
|
33
34
|
});
|
|
34
|
-
|
|
35
|
+
const pythonValidation = await validatePythonEnvironment({
|
|
36
|
+
baseDir,
|
|
37
|
+
hasPythonFiles
|
|
38
|
+
});
|
|
39
|
+
if (!pythonValidation.success) process.exit(1);
|
|
40
|
+
if (pythonValidation.hasPythonFiles) {
|
|
35
41
|
activatePythonVenv({
|
|
36
42
|
baseDir,
|
|
37
43
|
isVerbose
|
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 { getRedisClient, getRedisConnectionInfo, 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: getRedisConnectionInfo(),\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":"
|
|
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, getRedisConnectionInfo, stopRedisConnection } from './redis/connection'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { identifyUser } from './utils/analytics'\nimport { validatePythonEnvironment } from './utils/validate-python-environment'\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 const pythonValidation = await validatePythonEnvironment({ baseDir, hasPythonFiles })\n if (!pythonValidation.success) {\n process.exit(1)\n }\n\n if (pythonValidation.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: getRedisConnectionInfo(),\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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-locked-data.d.mts","names":[],"sources":["../src/generate-locked-data.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"generate-locked-data.d.mts","names":[],"sources":["../src/generate-locked-data.ts"],"sourcesContent":[],"mappings":";;;;AAwDa,cAAA,YA4EZ,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,EA5EkE,UA4ElE,EAAA,GA5E+E,OA4E/E,CA5EuF,IA4EvF,EAAA,CAAA;KAEI,iBAAA,GA9E8D;EAAqB,YAAA,EA+ExE,gBA/EwE,CAAA,cAAA,CAAA;EAAR,aAAA,CAAA,EAgF9D,UAhF8D;CAAO;AA8ElF,cAKQ,kBAJG,EAAA,CAAA,MAAA,EAAA;EAIH,UAAA,EAAA,MAAA;EAEK,aAAA,CAAA,EAAA,oBAAA;EACF,WAAA,CAAA,EAAA,eAAA;EAED,WAAA,CAAA,EAAA,UAAA,GAAA,SAAA;EACH,UAAA,CAAA,EADG,iBACH;CAAR,EAAA,GAAA,OAAA,CAAQ,UAAR,CAAA"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { activatePythonVenv } from "./utils/activate-python-env.mjs";
|
|
2
1
|
import { CompilationError } from "./utils/errors/compilation.error.mjs";
|
|
3
2
|
import { LockedDataGenerationError } from "./utils/errors/locked-data-generation.error.mjs";
|
|
4
3
|
import { LockedData, MemoryStreamAdapterManager, NoPrinter, Printer, getStepConfig, getStreamConfig } from "@motiadev/core";
|
|
@@ -53,7 +52,6 @@ const collectFlows = async (projectDir, lockedData) => {
|
|
|
53
52
|
absolute: true,
|
|
54
53
|
cwd: srcDir
|
|
55
54
|
}) : []];
|
|
56
|
-
if (stepFiles.some((file) => file.endsWith(".py")) || streamFiles.some((file) => file.endsWith(".py"))) activatePythonVenv({ baseDir: projectDir });
|
|
57
55
|
for (const filePath of stepFiles) try {
|
|
58
56
|
const config = await getStepConfig(filePath, projectDir);
|
|
59
57
|
if (!config) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-locked-data.mjs","names":["invalidSteps: Step[]"],"sources":["../src/generate-locked-data.ts"],"sourcesContent":["import {\n getStepConfig,\n getStreamConfig,\n type JsonSchema,\n LockedData,\n MemoryStreamAdapterManager,\n NoPrinter,\n Printer,\n type Step,\n type StreamAdapterManager,\n type StreamAuthConfig,\n} from '@motiadev/core'\nimport { randomUUID } from 'crypto'\nimport { existsSync } from 'fs'\nimport { globSync } from 'glob'\nimport path from 'path'\nimport pc from 'picocolors'\nimport type { RedisClientType } from 'redis'\nimport {
|
|
1
|
+
{"version":3,"file":"generate-locked-data.mjs","names":["invalidSteps: Step[]"],"sources":["../src/generate-locked-data.ts"],"sourcesContent":["import {\n getStepConfig,\n getStreamConfig,\n type JsonSchema,\n LockedData,\n MemoryStreamAdapterManager,\n NoPrinter,\n Printer,\n type Step,\n type StreamAdapterManager,\n type StreamAuthConfig,\n} from '@motiadev/core'\nimport { randomUUID } from 'crypto'\nimport { existsSync } from 'fs'\nimport { globSync } from 'glob'\nimport path from 'path'\nimport pc from 'picocolors'\nimport type { RedisClientType } from 'redis'\nimport { CompilationError } from './utils/errors/compilation.error'\nimport { LockedDataGenerationError } from './utils/errors/locked-data-generation.error'\n\nconst version = `${randomUUID()}:${Math.floor(Date.now() / 1000)}`\n\nconst getStepFilesFromDir = (dir: string): string[] => {\n if (!existsSync(dir)) {\n return []\n }\n return [\n ...globSync('**/*.step.{ts,js,rb}', { absolute: true, cwd: dir }),\n ...globSync('**/*_step.{ts,js,py,rb}', { absolute: true, cwd: dir }),\n ]\n}\n\nexport const getStepFiles = (projectDir: string): string[] => {\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n return [...getStepFilesFromDir(stepsDir), ...getStepFilesFromDir(srcDir)]\n}\n\nconst getStreamFilesFromDir = (dir: string): string[] => {\n if (!existsSync(dir)) {\n return []\n }\n return [\n ...globSync('**/*.stream.{ts,js,rb}', { absolute: true, cwd: dir }),\n ...globSync('**/*_stream.{ts,js,py,rb}', { absolute: true, cwd: dir }),\n ]\n}\n\nexport const getStreamFiles = (projectDir: string): string[] => {\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n return [...getStreamFilesFromDir(stepsDir), ...getStreamFilesFromDir(srcDir)]\n}\n\n// Helper function to recursively collect flow data\nexport const collectFlows = async (projectDir: string, lockedData: LockedData): Promise<Step[]> => {\n const invalidSteps: Step[] = []\n const stepFiles = getStepFiles(projectDir)\n const streamFiles = getStreamFiles(projectDir)\n const stepsDir = path.join(projectDir, 'steps')\n const srcDir = path.join(projectDir, 'src')\n const deprecatedSteps = [\n ...(existsSync(stepsDir) ? globSync('**/*.step.py', { absolute: true, cwd: stepsDir }) : []),\n ...(existsSync(srcDir) ? globSync('**/*.step.py', { absolute: true, cwd: srcDir }) : []),\n ]\n\n for (const filePath of stepFiles) {\n try {\n const config = await getStepConfig(filePath, projectDir)\n\n if (!config) {\n console.warn(`No config found in step ${filePath}, step skipped`)\n continue\n }\n\n const result = lockedData.createStep({ filePath, version, config }, { disableTypeCreation: true })\n\n if (!result) {\n invalidSteps.push({ filePath, version, config })\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err)\n if (errorMessage.includes('Executable ruby not found') || errorMessage.includes('Executable python not found')) {\n console.warn(pc.yellow(`! [WARNING] Skipping step ${filePath}: ${errorMessage}`))\n continue\n }\n throw new CompilationError(`Error collecting flow ${filePath}`, path.relative(projectDir, filePath), err as Error)\n }\n }\n\n for (const filePath of streamFiles) {\n const config = await getStreamConfig(filePath)\n\n if (!config) {\n console.warn(`No config found in stream ${filePath}, stream skipped`)\n continue\n }\n\n lockedData.createStream({ filePath, config }, { disableTypeCreation: true })\n }\n\n if (deprecatedSteps.length > 0) {\n const warning = pc.yellow('! [WARNING]')\n console.warn(\n pc.yellow(\n [\n '',\n '========================================',\n warning,\n '',\n `Python steps with ${pc.gray('.step.py')} extensions are no longer supported.`,\n `Please rename them to ${pc.gray('_step.py')}.`,\n '',\n pc.bold('Steps:'),\n ...deprecatedSteps.map((step) =>\n pc.reset(\n `- ${pc.cyan(pc.bold(step.replace(projectDir, '')))} rename to ${pc.gray(`${step.replace(projectDir, '').replace('.step.py', '_step.py')}`)}`,\n ),\n ),\n\n '',\n 'Make sure the step names are importable from Python:',\n `- Don't use numbers, dots, dashes, commas, spaces, colons, or special characters`,\n '========================================',\n '',\n ].join('\\n'),\n ),\n )\n }\n\n return invalidSteps\n}\n\ntype StreamAuthOptions = {\n authenticate: StreamAuthConfig['authenticate']\n contextSchema?: JsonSchema\n}\n\nexport const generateLockedData = async (config: {\n projectDir: string\n streamAdapter?: StreamAdapterManager\n redisClient?: RedisClientType\n printerType?: 'disabled' | 'default'\n streamAuth?: StreamAuthOptions\n}): Promise<LockedData> => {\n try {\n const {\n projectDir,\n streamAdapter = new MemoryStreamAdapterManager(),\n printerType = 'default',\n redisClient,\n streamAuth,\n } = config\n const printer = printerType === 'disabled' ? new NoPrinter() : new Printer(projectDir)\n /*\n * NOTE: right now for performance and simplicity let's enforce a folder,\n * but we might want to remove this and scan the entire current directory\n */\n const lockedData = new LockedData(projectDir, streamAdapter, printer, redisClient)\n lockedData.setStreamAuthConfig(streamAuth)\n\n await collectFlows(projectDir, lockedData)\n lockedData.saveTypes()\n\n return lockedData\n } catch (error) {\n console.error(error)\n\n throw new LockedDataGenerationError(\n 'Failed to parse the project, generating locked data step failed',\n error as Error,\n )\n }\n}\n"],"mappings":"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"}
|
package/dist/install.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
|
|
1
2
|
import { executeCommand } from "./utils/execute-command.mjs";
|
|
2
3
|
import { getPythonCommand } from "./utils/python-version-utils.mjs";
|
|
3
4
|
import { activatePythonVenv } from "./utils/activate-python-env.mjs";
|
|
4
|
-
import { getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
|
|
5
5
|
import { ensureUvInstalled } from "./utils/ensure-uv.mjs";
|
|
6
6
|
import { installLambdaPythonPackages } from "./utils/install-lambda-python-packages.mjs";
|
|
7
7
|
import fs from "fs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { executeCommand } from "../utils/execute-command.mjs";
|
|
2
1
|
import { version } from "../version.mjs";
|
|
3
|
-
import {
|
|
2
|
+
import { executeCommand } from "../utils/execute-command.mjs";
|
|
4
3
|
import { getPackageManager } from "../utils/get-package-manager.mjs";
|
|
4
|
+
import { pluginDependencies } from "./plugin-dependencies.mjs";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
|
|
@@ -38,7 +38,8 @@ const installPluginDependencies = async (baseDir, printer) => {
|
|
|
38
38
|
const installCommand = {
|
|
39
39
|
npm: "npm install",
|
|
40
40
|
yarn: "yarn install",
|
|
41
|
-
pnpm: "pnpm install"
|
|
41
|
+
pnpm: "pnpm install",
|
|
42
|
+
bun: "bun install"
|
|
42
43
|
}[packageManager] || "npm install";
|
|
43
44
|
try {
|
|
44
45
|
await executeCommand(installCommand, baseDir, { silent: false });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-plugin-dependencies.mjs","names":["missingDependencies: string[]"],"sources":["../../src/plugins/install-plugin-dependencies.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Printer } from '@motiadev/core'\nimport { executeCommand } from '../utils/execute-command'\nimport { getPackageManager } from '../utils/get-package-manager'\nimport { version } from '../version'\nimport { pluginDependencies } from './plugin-dependencies'\n\nexport const installPluginDependencies = async (baseDir: string, printer: Printer): Promise<void> => {\n const packageJsonPath = path.join(baseDir, 'package.json')\n\n if (!fs.existsSync(packageJsonPath)) {\n printer.printPluginWarn('No package.json found, skipping plugin dependency installation')\n return\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n if (!packageJson.dependencies) {\n packageJson.dependencies = {}\n }\n\n const missingDependencies: string[] = []\n\n for (const dep of pluginDependencies) {\n if (packageJson.devDependencies?.[dep]) {\n delete packageJson.devDependencies[dep]\n }\n\n if (!packageJson.dependencies[dep]) {\n packageJson.dependencies[dep] = version\n missingDependencies.push(dep)\n }\n }\n\n if (missingDependencies.length === 0) {\n printer.printPluginLog('All plugin dependencies already installed')\n return\n }\n\n printer.printPluginLog(`Adding missing plugin dependencies: ${missingDependencies.join(', ')}`)\n\n fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`)\n printer.printPluginLog('Updated package.json with plugin dependencies')\n\n let packageManager = getPackageManager(baseDir)\n if (packageManager === 'unknown') {\n printer.printPluginError('No package manager found, using npm as default')\n packageManager = 'npm'\n }\n printer.printPluginLog(`Installing dependencies using ${packageManager}...`)\n\n const installCommands: Record<string, string> = {\n npm: 'npm install',\n yarn: 'yarn install',\n pnpm: 'pnpm install',\n }\n\n const installCommand = installCommands[packageManager] || 'npm install'\n\n try {\n await executeCommand(installCommand, baseDir, { silent: false })\n printer.printPluginLog('Plugin dependencies installed successfully')\n } catch (error) {\n printer.printPluginError('Failed to install plugin dependencies:', error)\n printer.printPluginWarn(`Please run '${installCommand}' manually to install the dependencies`)\n }\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,4BAA4B,OAAO,SAAiB,YAAoC;CACnG,MAAM,kBAAkB,KAAK,KAAK,SAAS,eAAe;AAE1D,KAAI,CAAC,GAAG,WAAW,gBAAgB,EAAE;AACnC,UAAQ,gBAAgB,iEAAiE;AACzF;;CAGF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AAEzE,KAAI,CAAC,YAAY,aACf,aAAY,eAAe,EAAE;CAG/B,MAAMA,sBAAgC,EAAE;AAExC,MAAK,MAAM,OAAO,oBAAoB;AACpC,MAAI,YAAY,kBAAkB,KAChC,QAAO,YAAY,gBAAgB;AAGrC,MAAI,CAAC,YAAY,aAAa,MAAM;AAClC,eAAY,aAAa,OAAO;AAChC,uBAAoB,KAAK,IAAI;;;AAIjC,KAAI,oBAAoB,WAAW,GAAG;AACpC,UAAQ,eAAe,4CAA4C;AACnE;;AAGF,SAAQ,eAAe,uCAAuC,oBAAoB,KAAK,KAAK,GAAG;AAE/F,IAAG,cAAc,iBAAiB,GAAG,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC,IAAI;AAC9E,SAAQ,eAAe,gDAAgD;CAEvE,IAAI,iBAAiB,kBAAkB,QAAQ;AAC/C,KAAI,mBAAmB,WAAW;AAChC,UAAQ,iBAAiB,iDAAiD;AAC1E,mBAAiB;;AAEnB,SAAQ,eAAe,iCAAiC,eAAe,KAAK;
|
|
1
|
+
{"version":3,"file":"install-plugin-dependencies.mjs","names":["missingDependencies: string[]"],"sources":["../../src/plugins/install-plugin-dependencies.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Printer } from '@motiadev/core'\nimport { executeCommand } from '../utils/execute-command'\nimport { getPackageManager } from '../utils/get-package-manager'\nimport { version } from '../version'\nimport { pluginDependencies } from './plugin-dependencies'\n\nexport const installPluginDependencies = async (baseDir: string, printer: Printer): Promise<void> => {\n const packageJsonPath = path.join(baseDir, 'package.json')\n\n if (!fs.existsSync(packageJsonPath)) {\n printer.printPluginWarn('No package.json found, skipping plugin dependency installation')\n return\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n if (!packageJson.dependencies) {\n packageJson.dependencies = {}\n }\n\n const missingDependencies: string[] = []\n\n for (const dep of pluginDependencies) {\n if (packageJson.devDependencies?.[dep]) {\n delete packageJson.devDependencies[dep]\n }\n\n if (!packageJson.dependencies[dep]) {\n packageJson.dependencies[dep] = version\n missingDependencies.push(dep)\n }\n }\n\n if (missingDependencies.length === 0) {\n printer.printPluginLog('All plugin dependencies already installed')\n return\n }\n\n printer.printPluginLog(`Adding missing plugin dependencies: ${missingDependencies.join(', ')}`)\n\n fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\\n`)\n printer.printPluginLog('Updated package.json with plugin dependencies')\n\n let packageManager = getPackageManager(baseDir)\n if (packageManager === 'unknown') {\n printer.printPluginError('No package manager found, using npm as default')\n packageManager = 'npm'\n }\n printer.printPluginLog(`Installing dependencies using ${packageManager}...`)\n\n const installCommands: Record<string, string> = {\n npm: 'npm install',\n yarn: 'yarn install',\n pnpm: 'pnpm install',\n bun: 'bun install',\n }\n\n const installCommand = installCommands[packageManager] || 'npm install'\n\n try {\n await executeCommand(installCommand, baseDir, { silent: false })\n printer.printPluginLog('Plugin dependencies installed successfully')\n } catch (error) {\n printer.printPluginError('Failed to install plugin dependencies:', error)\n printer.printPluginWarn(`Please run '${installCommand}' manually to install the dependencies`)\n }\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,4BAA4B,OAAO,SAAiB,YAAoC;CACnG,MAAM,kBAAkB,KAAK,KAAK,SAAS,eAAe;AAE1D,KAAI,CAAC,GAAG,WAAW,gBAAgB,EAAE;AACnC,UAAQ,gBAAgB,iEAAiE;AACzF;;CAGF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AAEzE,KAAI,CAAC,YAAY,aACf,aAAY,eAAe,EAAE;CAG/B,MAAMA,sBAAgC,EAAE;AAExC,MAAK,MAAM,OAAO,oBAAoB;AACpC,MAAI,YAAY,kBAAkB,KAChC,QAAO,YAAY,gBAAgB;AAGrC,MAAI,CAAC,YAAY,aAAa,MAAM;AAClC,eAAY,aAAa,OAAO;AAChC,uBAAoB,KAAK,IAAI;;;AAIjC,KAAI,oBAAoB,WAAW,GAAG;AACpC,UAAQ,eAAe,4CAA4C;AACnE;;AAGF,SAAQ,eAAe,uCAAuC,oBAAoB,KAAK,KAAK,GAAG;AAE/F,IAAG,cAAc,iBAAiB,GAAG,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC,IAAI;AAC9E,SAAQ,eAAe,gDAAgD;CAEvE,IAAI,iBAAiB,kBAAkB,QAAQ;AAC/C,KAAI,mBAAmB,WAAW;AAChC,UAAQ,iBAAiB,iDAAiD;AAC1E,mBAAiB;;AAEnB,SAAQ,eAAe,iCAAiC,eAAe,KAAK;CAS5E,MAAM,iBAP0C;EAC9C,KAAK;EACL,MAAM;EACN,MAAM;EACN,KAAK;EACN,CAEsC,mBAAmB;AAE1D,KAAI;AACF,QAAM,eAAe,gBAAgB,SAAS,EAAE,QAAQ,OAAO,CAAC;AAChE,UAAQ,eAAe,6CAA6C;UAC7D,OAAO;AACd,UAAQ,iBAAiB,0CAA0C,MAAM;AACzE,UAAQ,gBAAgB,eAAe,eAAe,wCAAwC"}
|
package/dist/start.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { activatePythonVenv } from "./utils/activate-python-env.mjs";
|
|
2
1
|
import { generateLockedData, getStepFiles, getStreamFiles } from "./generate-locked-data.mjs";
|
|
3
2
|
import { version } from "./version.mjs";
|
|
3
|
+
import { activatePythonVenv } from "./utils/activate-python-env.mjs";
|
|
4
|
+
import { validatePythonEnvironment } from "./utils/validate-python-environment.mjs";
|
|
4
5
|
import { loadMotiaConfig } from "./load-motia-config.mjs";
|
|
5
6
|
import { workbenchBase } from "./constants.mjs";
|
|
6
7
|
import { processPlugins } from "./plugins/process-plugins.mjs";
|
|
@@ -17,7 +18,12 @@ import { RedisStreamAdapterManager } from "@motiadev/adapter-redis-streams";
|
|
|
17
18
|
const start = async (port, hostname, disableVerbose, motiaFileStorageDir) => {
|
|
18
19
|
const baseDir = process.cwd();
|
|
19
20
|
const isVerbose = !disableVerbose;
|
|
20
|
-
|
|
21
|
+
const pythonValidation = await validatePythonEnvironment({
|
|
22
|
+
baseDir,
|
|
23
|
+
hasPythonFiles: [...getStepFiles(baseDir), ...getStreamFiles(baseDir)].some((file) => file.endsWith(".py"))
|
|
24
|
+
});
|
|
25
|
+
if (!pythonValidation.success) process.exit(1);
|
|
26
|
+
if (pythonValidation.hasPythonFiles) {
|
|
21
27
|
console.log("⚙️ Activating Python environment...");
|
|
22
28
|
activatePythonVenv({
|
|
23
29
|
baseDir,
|
package/dist/start.mjs.map
CHANGED
|
@@ -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 { getRedisClient, getRedisConnectionInfo, 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)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: getRedisConnectionInfo(),\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":"
|
|
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, getRedisConnectionInfo, stopRedisConnection } from './redis/connection'\nimport { activatePythonVenv } from './utils/activate-python-env'\nimport { validatePythonEnvironment } from './utils/validate-python-environment'\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 const pythonValidation = await validatePythonEnvironment({ baseDir, hasPythonFiles })\n if (!pythonValidation.success) {\n process.exit(1)\n }\n\n if (pythonValidation.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)\n\n const adapters = {\n eventAdapter:\n appConfig.adapters?.events ||\n new BullMQEventAdapter({\n connection: getRedisConnectionInfo(),\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":";;;;;;;;;;;;;;;;;AAgBA,MAAa,QAAQ,OACnB,MACA,UACA,gBACA,wBACkB;CAClB,MAAM,UAAU,QAAQ,KAAK;CAC7B,MAAM,YAAY,CAAC;CAKnB,MAAM,mBAAmB,MAAM,0BAA0B;EAAE;EAAS,gBAHlD,CAAC,GAAG,aAAa,QAAQ,EAAE,GAAG,eAAe,QAAQ,CAAC,CACvC,MAAM,SAAS,KAAK,SAAS,MAAM,CAAC;EAEe,CAAC;AACrF,KAAI,CAAC,iBAAiB,QACpB,SAAQ,KAAK,EAAE;AAGjB,KAAI,iBAAiB,gBAAgB;AACnC,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,UAAU;CAE9E,MAAM,WAAW;EACf,cACE,UAAU,UAAU,UACpB,IAAI,mBAAmB,EACrB,YAAY,wBAAwB,EACrC,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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { internalLogger } from "./internal-logger.mjs";
|
|
2
2
|
import { findPythonSitePackagesDir } from "./python-version-utils.mjs";
|
|
3
|
-
import fs from "fs";
|
|
4
3
|
import path from "path";
|
|
5
4
|
|
|
6
5
|
//#region src/utils/activate-python-env.ts
|
|
@@ -17,20 +16,15 @@ const activatePythonVenv = ({ baseDir, isVerbose = false, pythonVersion = "3.13"
|
|
|
17
16
|
baseDir,
|
|
18
17
|
pythonVersion
|
|
19
18
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
internalLogger.info("Site-packages path", relativePath(sitePackagesPath));
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
internalLogger.error("Python virtual environment not found in python_modules/");
|
|
33
|
-
internalLogger.error("Please run `motia install` to create a new virtual environment");
|
|
19
|
+
process.env.PATH = `${venvBinPath}${path.delimiter}${process.env.PATH}`;
|
|
20
|
+
process.env.VIRTUAL_ENV = venvPath;
|
|
21
|
+
process.env.PYTHON_SITE_PACKAGES = sitePackagesPath;
|
|
22
|
+
delete process.env.PYTHONHOME;
|
|
23
|
+
if (isVerbose) {
|
|
24
|
+
const pythonPath = process.platform === "win32" ? path.join(venvBinPath, "python.exe") : path.join(venvBinPath, "python");
|
|
25
|
+
const relativePath = (path$1) => path$1.replace(baseDir, "<projectDir>");
|
|
26
|
+
internalLogger.info("Using Python", relativePath(pythonPath));
|
|
27
|
+
internalLogger.info("Site-packages path", relativePath(sitePackagesPath));
|
|
34
28
|
}
|
|
35
29
|
};
|
|
36
30
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activate-python-env.mjs","names":["path"],"sources":["../../src/utils/activate-python-env.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { internalLogger } from './internal-logger'\nimport { findPythonSitePackagesDir } from './python-version-utils'\n\ninterface VenvConfig {\n baseDir: string\n isVerbose?: boolean\n pythonVersion?: string\n}\n\nexport const getSitePackagesPath = ({ baseDir, pythonVersion = '3.13' }: VenvConfig): string => {\n const venvPath = path.join(baseDir, 'python_modules')\n const libPath = path.join(venvPath, 'lib')\n const actualPythonVersionPath = findPythonSitePackagesDir(libPath, pythonVersion)\n return path.join(venvPath, 'lib', actualPythonVersionPath, 'site-packages')\n}\n\nexport const activatePythonVenv = ({ baseDir, isVerbose = false, pythonVersion = '3.13' }: VenvConfig): void => {\n internalLogger.info('Activating Python environment')\n\n // Set the virtual environment path\n const venvPath = path.join(baseDir, 'python_modules')\n const venvBinPath = path.join(venvPath, process.platform === 'win32' ? 'Scripts' : 'bin')\n\n // Find the Python version directory using the utility function\n const sitePackagesPath = getSitePackagesPath({ baseDir, pythonVersion })\n\n //
|
|
1
|
+
{"version":3,"file":"activate-python-env.mjs","names":["path"],"sources":["../../src/utils/activate-python-env.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { internalLogger } from './internal-logger'\nimport { findPythonSitePackagesDir } from './python-version-utils'\n\ninterface VenvConfig {\n baseDir: string\n isVerbose?: boolean\n pythonVersion?: string\n}\n\nexport const getSitePackagesPath = ({ baseDir, pythonVersion = '3.13' }: VenvConfig): string => {\n const venvPath = path.join(baseDir, 'python_modules')\n const libPath = path.join(venvPath, 'lib')\n const actualPythonVersionPath = findPythonSitePackagesDir(libPath, pythonVersion)\n return path.join(venvPath, 'lib', actualPythonVersionPath, 'site-packages')\n}\n\nexport const activatePythonVenv = ({ baseDir, isVerbose = false, pythonVersion = '3.13' }: VenvConfig): void => {\n internalLogger.info('Activating Python environment')\n\n // Set the virtual environment path\n const venvPath = path.join(baseDir, 'python_modules')\n const venvBinPath = path.join(venvPath, process.platform === 'win32' ? 'Scripts' : 'bin')\n\n // Find the Python version directory using the utility function\n const sitePackagesPath = getSitePackagesPath({ baseDir, pythonVersion })\n\n // Add virtual environment to PATH\n process.env.PATH = `${venvBinPath}${path.delimiter}${process.env.PATH}`\n // Set VIRTUAL_ENV environment variable\n process.env.VIRTUAL_ENV = venvPath\n // Set PYTHON_SITE_PACKAGES with the site-packages path\n process.env.PYTHON_SITE_PACKAGES = sitePackagesPath\n // Remove PYTHONHOME if it exists as it can interfere with venv\n delete process.env.PYTHONHOME\n\n // Log Python environment information if verbose mode is enabled\n if (isVerbose) {\n const pythonPath =\n process.platform === 'win32' ? path.join(venvBinPath, 'python.exe') : path.join(venvBinPath, 'python')\n\n const relativePath = (path: string) => path.replace(baseDir, '<projectDir>')\n\n internalLogger.info('Using Python', relativePath(pythonPath))\n internalLogger.info('Site-packages path', relativePath(sitePackagesPath))\n }\n}\n"],"mappings":";;;;;AAWA,MAAa,uBAAuB,EAAE,SAAS,gBAAgB,aAAiC;CAC9F,MAAM,WAAW,KAAK,KAAK,SAAS,iBAAiB;CAErD,MAAM,0BAA0B,0BADhB,KAAK,KAAK,UAAU,MAAM,EACyB,cAAc;AACjF,QAAO,KAAK,KAAK,UAAU,OAAO,yBAAyB,gBAAgB;;AAG7E,MAAa,sBAAsB,EAAE,SAAS,YAAY,OAAO,gBAAgB,aAA+B;AAC9G,gBAAe,KAAK,gCAAgC;CAGpD,MAAM,WAAW,KAAK,KAAK,SAAS,iBAAiB;CACrD,MAAM,cAAc,KAAK,KAAK,UAAU,QAAQ,aAAa,UAAU,YAAY,MAAM;CAGzF,MAAM,mBAAmB,oBAAoB;EAAE;EAAS;EAAe,CAAC;AAGxE,SAAQ,IAAI,OAAO,GAAG,cAAc,KAAK,YAAY,QAAQ,IAAI;AAEjE,SAAQ,IAAI,cAAc;AAE1B,SAAQ,IAAI,uBAAuB;AAEnC,QAAO,QAAQ,IAAI;AAGnB,KAAI,WAAW;EACb,MAAM,aACJ,QAAQ,aAAa,UAAU,KAAK,KAAK,aAAa,aAAa,GAAG,KAAK,KAAK,aAAa,SAAS;EAExG,MAAM,gBAAgB,WAAiBA,OAAK,QAAQ,SAAS,eAAe;AAE5E,iBAAe,KAAK,gBAAgB,aAAa,WAAW,CAAC;AAC7D,iBAAe,KAAK,sBAAsB,aAAa,iBAAiB,CAAC"}
|
|
@@ -1,13 +1,50 @@
|
|
|
1
1
|
import { checkIfFileExists } from "../create/utils.mjs";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
2
4
|
|
|
3
5
|
//#region src/utils/get-package-manager.ts
|
|
6
|
+
const getPackageManagerFromEnv = () => {
|
|
7
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
8
|
+
if (!userAgent) return null;
|
|
9
|
+
const match = userAgent.match(/^(npm|pnpm|yarn|bun)\//);
|
|
10
|
+
if (match) return match[1];
|
|
11
|
+
return null;
|
|
12
|
+
};
|
|
13
|
+
const readPackageManagerFromPackageJson = (dir) => {
|
|
14
|
+
const packageJsonPath = path.join(dir, "package.json");
|
|
15
|
+
if (!checkIfFileExists(dir, "package.json")) return null;
|
|
16
|
+
try {
|
|
17
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
18
|
+
if (packageJson.packageManager) {
|
|
19
|
+
const pm = packageJson.packageManager.split("@")[0];
|
|
20
|
+
if ([
|
|
21
|
+
"npm",
|
|
22
|
+
"yarn",
|
|
23
|
+
"pnpm",
|
|
24
|
+
"bun"
|
|
25
|
+
].includes(pm)) return pm;
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
4
32
|
const getPackageManager = (dir) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
33
|
+
const envPackageManager = getPackageManagerFromEnv();
|
|
34
|
+
if (envPackageManager) return envPackageManager;
|
|
35
|
+
let currentDir = dir;
|
|
36
|
+
while (currentDir !== path.dirname(currentDir)) {
|
|
37
|
+
if (checkIfFileExists(currentDir, "yarn.lock")) return "yarn";
|
|
38
|
+
else if (checkIfFileExists(currentDir, "pnpm-lock.yaml")) return "pnpm";
|
|
39
|
+
else if (checkIfFileExists(currentDir, "package-lock.json")) return "npm";
|
|
40
|
+
else if (checkIfFileExists(currentDir, "bun.lockb") || checkIfFileExists(currentDir, "bun.lock")) return "bun";
|
|
41
|
+
const packageManagerFromJson = readPackageManagerFromPackageJson(currentDir);
|
|
42
|
+
if (packageManagerFromJson) return packageManagerFromJson;
|
|
43
|
+
currentDir = path.dirname(currentDir);
|
|
44
|
+
}
|
|
45
|
+
return "npm";
|
|
9
46
|
};
|
|
10
47
|
|
|
11
48
|
//#endregion
|
|
12
|
-
export { getPackageManager };
|
|
49
|
+
export { getPackageManager, getPackageManagerFromEnv };
|
|
13
50
|
//# sourceMappingURL=get-package-manager.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-package-manager.mjs","names":[],"sources":["../../src/utils/get-package-manager.ts"],"sourcesContent":["import { checkIfFileExists } from '../create/utils'\n\nexport const
|
|
1
|
+
{"version":3,"file":"get-package-manager.mjs","names":[],"sources":["../../src/utils/get-package-manager.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { checkIfFileExists } from '../create/utils'\n\nexport const getPackageManagerFromEnv = (): string | null => {\n const userAgent = process.env.npm_config_user_agent\n if (!userAgent) {\n return null\n }\n\n const match = userAgent.match(/^(npm|pnpm|yarn|bun)\\//)\n if (match) {\n return match[1]\n }\n\n return null\n}\n\nconst readPackageManagerFromPackageJson = (dir: string): string | null => {\n const packageJsonPath = path.join(dir, 'package.json')\n if (!checkIfFileExists(dir, 'package.json')) {\n return null\n }\n\n try {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n if (packageJson.packageManager) {\n const pm = packageJson.packageManager.split('@')[0]\n if (['npm', 'yarn', 'pnpm', 'bun'].includes(pm)) {\n return pm\n }\n }\n } catch {\n return null\n }\n\n return null\n}\n\nexport const getPackageManager = (dir: string): string => {\n const envPackageManager = getPackageManagerFromEnv()\n if (envPackageManager) {\n return envPackageManager\n }\n\n let currentDir = dir\n\n while (currentDir !== path.dirname(currentDir)) {\n if (checkIfFileExists(currentDir, 'yarn.lock')) {\n return 'yarn'\n } else if (checkIfFileExists(currentDir, 'pnpm-lock.yaml')) {\n return 'pnpm'\n } else if (checkIfFileExists(currentDir, 'package-lock.json')) {\n return 'npm'\n } else if (checkIfFileExists(currentDir, 'bun.lockb') || checkIfFileExists(currentDir, 'bun.lock')) {\n return 'bun'\n }\n\n const packageManagerFromJson = readPackageManagerFromPackageJson(currentDir)\n if (packageManagerFromJson) {\n return packageManagerFromJson\n }\n\n currentDir = path.dirname(currentDir)\n }\n\n return 'npm'\n}\n"],"mappings":";;;;;AAIA,MAAa,iCAAgD;CAC3D,MAAM,YAAY,QAAQ,IAAI;AAC9B,KAAI,CAAC,UACH,QAAO;CAGT,MAAM,QAAQ,UAAU,MAAM,yBAAyB;AACvD,KAAI,MACF,QAAO,MAAM;AAGf,QAAO;;AAGT,MAAM,qCAAqC,QAA+B;CACxE,MAAM,kBAAkB,KAAK,KAAK,KAAK,eAAe;AACtD,KAAI,CAAC,kBAAkB,KAAK,eAAe,CACzC,QAAO;AAGT,KAAI;EACF,MAAM,cAAc,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC;AACzE,MAAI,YAAY,gBAAgB;GAC9B,MAAM,KAAK,YAAY,eAAe,MAAM,IAAI,CAAC;AACjD,OAAI;IAAC;IAAO;IAAQ;IAAQ;IAAM,CAAC,SAAS,GAAG,CAC7C,QAAO;;SAGL;AACN,SAAO;;AAGT,QAAO;;AAGT,MAAa,qBAAqB,QAAwB;CACxD,MAAM,oBAAoB,0BAA0B;AACpD,KAAI,kBACF,QAAO;CAGT,IAAI,aAAa;AAEjB,QAAO,eAAe,KAAK,QAAQ,WAAW,EAAE;AAC9C,MAAI,kBAAkB,YAAY,YAAY,CAC5C,QAAO;WACE,kBAAkB,YAAY,iBAAiB,CACxD,QAAO;WACE,kBAAkB,YAAY,oBAAoB,CAC3D,QAAO;WACE,kBAAkB,YAAY,YAAY,IAAI,kBAAkB,YAAY,WAAW,CAChG,QAAO;EAGT,MAAM,yBAAyB,kCAAkC,WAAW;AAC5E,MAAI,uBACF,QAAO;AAGT,eAAa,KAAK,QAAQ,WAAW;;AAGvC,QAAO"}
|