motia 0.5.9-beta.118-482384 → 0.5.11-beta.119
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/README.md +136 -96
- package/dist/cjs/cli.js +21 -0
- package/dist/cjs/cloud/build/builders/node/router-template.d.ts +1 -1
- package/dist/cjs/cloud/build/builders/node/router-template.ts +1 -1
- package/dist/cjs/cloud/build/builders/python/index.js +4 -15
- package/dist/cjs/cloud/build/builders/python/router_template.py +13 -74
- package/dist/cjs/cloud/cli/deploy.js +8 -4
- package/dist/cjs/cloud/new-deployment/cloud-api/create-deployment.d.ts +1 -0
- package/dist/cjs/cloud/new-deployment/cloud-api/endpoints.js +1 -1
- package/dist/cjs/cloud/new-deployment/cloud-api/index.d.ts +1 -0
- package/dist/cjs/cloud/new-deployment/deploy.js +10 -5
- package/dist/cjs/cloud/new-deployment/listeners/cli-listener.js +4 -3
- package/dist/cjs/cloud/new-deployment/listeners/print-deployment-status.js +9 -4
- package/dist/cjs/cloud/new-deployment/utils/upload.js +1 -1
- package/dist/cjs/create/index.d.ts +1 -0
- package/dist/cjs/create/index.js +21 -27
- package/dist/cjs/create/setup-template.d.ts +2 -0
- package/dist/cjs/create/setup-template.js +13 -0
- package/dist/cjs/create/setup-tutorial-flow.d.ts +6 -0
- package/dist/cjs/create/setup-tutorial-flow.js +30 -0
- package/dist/cjs/create/templates/basic-tutorial/00-noop.step.ts.txt +30 -0
- package/dist/cjs/create/templates/basic-tutorial/01-api.step.ts.txt +90 -0
- package/dist/cjs/create/templates/basic-tutorial/02-process-food-order.step.ts.txt +54 -0
- package/dist/cjs/create/templates/basic-tutorial/03-state-audit-cron.step.ts.txt +42 -0
- package/dist/cjs/create/templates/basic-tutorial/04_new_order_notifications.step.py.txt +22 -0
- package/dist/cjs/create/templates/basic-tutorial/motia-workbench.json +27 -0
- package/dist/cjs/create/templates/basic-tutorial/services/pet-store.ts.txt +32 -0
- package/dist/cjs/create/templates/generate.d.ts +1 -1
- package/dist/cjs/create/templates/generate.js +23 -5
- package/dist/cjs/create/templates/generate.ts +31 -6
- package/dist/cjs/create/templates/index.js +1 -0
- package/dist/cjs/create/templates/index.ts +1 -0
- package/dist/cjs/create/utils.d.ts +2 -0
- package/dist/cjs/create/utils.js +21 -0
- package/dist/cjs/docker/utils/print-intro.js +0 -1
- package/dist/esm/cli.js +18 -0
- package/dist/esm/cloud/build/builders/node/router-template.d.ts +1 -1
- package/dist/esm/cloud/build/builders/node/router-template.ts +1 -1
- package/dist/esm/cloud/build/builders/python/index.js +4 -15
- package/dist/esm/cloud/build/builders/python/router_template.py +13 -74
- package/dist/esm/cloud/cli/deploy.js +8 -4
- package/dist/esm/cloud/new-deployment/cloud-api/create-deployment.d.ts +1 -0
- package/dist/esm/cloud/new-deployment/cloud-api/endpoints.js +1 -1
- package/dist/esm/cloud/new-deployment/cloud-api/index.d.ts +1 -0
- package/dist/esm/cloud/new-deployment/deploy.js +10 -5
- package/dist/esm/cloud/new-deployment/listeners/cli-listener.js +4 -3
- package/dist/esm/cloud/new-deployment/listeners/print-deployment-status.js +9 -4
- package/dist/esm/cloud/new-deployment/utils/upload.js +1 -1
- package/dist/esm/create/index.d.ts +1 -0
- package/dist/esm/create/index.js +11 -17
- package/dist/esm/create/setup-template.d.ts +2 -0
- package/dist/esm/create/setup-template.js +9 -0
- package/dist/esm/create/setup-tutorial-flow.d.ts +6 -0
- package/dist/esm/create/setup-tutorial-flow.js +23 -0
- package/dist/esm/create/templates/basic-tutorial/00-noop.step.ts.txt +30 -0
- package/dist/esm/create/templates/basic-tutorial/01-api.step.ts.txt +90 -0
- package/dist/esm/create/templates/basic-tutorial/02-process-food-order.step.ts.txt +54 -0
- package/dist/esm/create/templates/basic-tutorial/03-state-audit-cron.step.ts.txt +42 -0
- package/dist/esm/create/templates/basic-tutorial/04_new_order_notifications.step.py.txt +22 -0
- package/dist/esm/create/templates/basic-tutorial/motia-workbench.json +27 -0
- package/dist/esm/create/templates/basic-tutorial/services/pet-store.ts.txt +32 -0
- package/dist/esm/create/templates/generate.d.ts +1 -1
- package/dist/esm/create/templates/generate.js +24 -6
- package/dist/esm/create/templates/generate.ts +31 -6
- package/dist/esm/create/templates/index.js +1 -0
- package/dist/esm/create/templates/index.ts +1 -0
- package/dist/esm/create/utils.d.ts +2 -0
- package/dist/esm/create/utils.js +13 -0
- package/dist/esm/docker/utils/print-intro.js +0 -1
- package/dist/requirements-snap.txt +0 -1
- package/dist/types/cloud/build/builders/node/router-template.d.ts +1 -1
- package/dist/types/cloud/new-deployment/cloud-api/create-deployment.d.ts +1 -0
- package/dist/types/cloud/new-deployment/cloud-api/index.d.ts +1 -0
- package/dist/types/create/index.d.ts +1 -0
- package/dist/types/create/setup-template.d.ts +2 -0
- package/dist/types/create/setup-tutorial-flow.d.ts +6 -0
- package/dist/types/create/templates/generate.d.ts +1 -1
- package/dist/types/create/utils.d.ts +2 -0
- package/package.json +4 -4
- /package/dist/cjs/create/templates/default/{steps/00-noop.step.ts.txt → 00-noop.step.ts.txt} +0 -0
- /package/dist/cjs/create/templates/default/{steps/00-noop.step.tsx.txt → 00-noop.step.tsx.txt} +0 -0
- /package/dist/cjs/create/templates/default/{steps/01-api.step.ts.txt → 01-api.step.ts.txt} +0 -0
- /package/dist/cjs/create/templates/default/{steps/02-test-state.step.ts.txt → 02-test-state.step.ts.txt} +0 -0
- /package/dist/cjs/create/templates/default/{steps/03-check-state-change.step.ts.txt → 03-check-state-change.step.ts.txt} +0 -0
- /package/dist/cjs/create/templates/python/{steps/00_noop_step.py.txt → 00_noop_step.py.txt} +0 -0
- /package/dist/cjs/create/templates/python/{steps/00_noop_step.tsx.txt → 00_noop_step.tsx.txt} +0 -0
- /package/dist/cjs/create/templates/python/{steps/01_api_step.py.txt → 01_api_step.py.txt} +0 -0
- /package/dist/cjs/create/templates/python/{steps/02_test_state_step.py.txt → 02_test_state_step.py.txt} +0 -0
- /package/dist/cjs/create/templates/python/{steps/03_check_state_change_step.py.txt → 03_check_state_change_step.py.txt} +0 -0
- /package/dist/esm/create/templates/default/{steps/00-noop.step.ts.txt → 00-noop.step.ts.txt} +0 -0
- /package/dist/esm/create/templates/default/{steps/00-noop.step.tsx.txt → 00-noop.step.tsx.txt} +0 -0
- /package/dist/esm/create/templates/default/{steps/01-api.step.ts.txt → 01-api.step.ts.txt} +0 -0
- /package/dist/esm/create/templates/default/{steps/02-test-state.step.ts.txt → 02-test-state.step.ts.txt} +0 -0
- /package/dist/esm/create/templates/default/{steps/03-check-state-change.step.ts.txt → 03-check-state-change.step.ts.txt} +0 -0
- /package/dist/esm/create/templates/python/{steps/00_noop_step.py.txt → 00_noop_step.py.txt} +0 -0
- /package/dist/esm/create/templates/python/{steps/00_noop_step.tsx.txt → 00_noop_step.tsx.txt} +0 -0
- /package/dist/esm/create/templates/python/{steps/01_api_step.py.txt → 01_api_step.py.txt} +0 -0
- /package/dist/esm/create/templates/python/{steps/02_test_state_step.py.txt → 02_test_state_step.py.txt} +0 -0
- /package/dist/esm/create/templates/python/{steps/03_check_state_change_step.py.txt → 03_check_state_change_step.py.txt} +0 -0
package/README.md
CHANGED
|
@@ -1,45 +1,88 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<a href="https://twitter.com/motiadev" target="_blank">
|
|
22
|
-
<img src="https://img.shields.io/badge/Follow-@motiadev-1DA1F2?style=flat&logo=twitter&logoColor=white&labelColor=000000" alt="Twitter Follow">
|
|
23
|
-
</a>
|
|
24
|
-
<a href="https://discord.gg/EnfDRFYW" target="_blank">
|
|
25
|
-
<img src="https://img.shields.io/discord/1322278831184281721?style=flat&logo=discord&logoColor=white&color=5865F2&label=Discord&labelColor=000000" alt="Discord">
|
|
26
|
-
</a>
|
|
27
|
-
</p>
|
|
28
|
-
|
|
29
|
-
Motia is a **modern backend framework** that unifies APIs, background jobs, events, and AI agents into a single cohesive system. Eliminate runtime complexity and build unified backends where **JavaScript, TypeScript, Python, etc**, work together in event-driven workflows, with built-in state management, observability, and one-click deployments.
|
|
1
|
+
[](https://motia.dev/)
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/motia)
|
|
4
|
+
[](https://github.com/MotiaDev/motia/blob/main/LICENSE)
|
|
5
|
+
[](https://github.com/MotiaDev/motia)
|
|
6
|
+
[](https://twitter.com/motiadev)
|
|
7
|
+
[](https://discord.gg/motia)
|
|
8
|
+
|
|
9
|
+
**🔥 A Modern Unified Backend Framework for APIs, background jobs, workflows, and Agents 🔥**
|
|
10
|
+
|
|
11
|
+
[💡 Motia Manifesto](https://www.motia.dev/manifesto) •
|
|
12
|
+
[🚀 Quick Start](https://www.motia.dev/docs/getting-started/quick-start) •
|
|
13
|
+
[📋 Defining Steps](https://www.motia.dev/docs/concepts/steps/defining-steps) •
|
|
14
|
+
[📚 Docs](https://www.motia.dev/docs)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🎯 What is Motia?
|
|
19
|
+
|
|
20
|
+
Motia is a **modern backend framework** that unifies APIs, background jobs, workflows, and AI agents into a single cohesive system. Eliminate runtime complexity and build unified backends where **JavaScript, TypeScript, Python, etc**, work together in event-driven workflows, with built-in state management, observability, and one-click deployments.
|
|
30
21
|
|
|
31
22
|
Motia brings cohesion to the fragmented backend world with our core primitive: the **Step**.
|
|
32
23
|
|
|
33
|
-

|
|
34
25
|
|
|
35
|
-
|
|
26
|
+
---
|
|
36
27
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
## 🚀 Quickstart
|
|
29
|
+
|
|
30
|
+
Get Motia project up and running in **under 60 seconds**:
|
|
31
|
+
|
|
32
|
+
### 1. Bootstrap a New Motia Project
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx motia@latest create -i # runs the interactive terminal
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Follow the prompts to pick a template, project name, and language.
|
|
39
|
+
|
|
40
|
+
### 2. Start the Workbench
|
|
41
|
+
|
|
42
|
+
Inside your new project folder, launch the dev server:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx motia dev # ➜ http://localhost:3000
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This spins up the Motia Workbench – a local UI for building, testing & observing your backend in real-time.
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
### 3. Hit Your First Endpoint
|
|
53
|
+
|
|
54
|
+
Open a new terminal tab and run:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
curl http://localhost:3000/default
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You should see the JSON response:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{ "message": "Hello World from Motia!" }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4. Explore the Workbench UI
|
|
67
|
+
|
|
68
|
+

|
|
69
|
+
|
|
70
|
+
The Workbench is your command centre:
|
|
71
|
+
|
|
72
|
+
- **🌊 Flows** – Visualise how your Steps connect.
|
|
73
|
+
- **🔌 Endpoints** – Test APIs with one click and stream results live.
|
|
74
|
+
- **👁️ Traces** – Inspect end-to-end traces of every execution.
|
|
75
|
+
- **📊 Logs** – View structured logs grouped by trace.
|
|
76
|
+
- **🏪 State** – Inspect the key-value store across Steps.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
🎉 **That's it!** You now have a fully-featured Motia project with:
|
|
81
|
+
|
|
82
|
+
- ✅ `/default` API endpoint
|
|
83
|
+
- ✅ Visual debugger & flow inspector
|
|
84
|
+
- ✅ Built-in observability
|
|
85
|
+
- ✅ Hot-reload for instant feedback
|
|
43
86
|
|
|
44
87
|
---
|
|
45
88
|
|
|
@@ -55,6 +98,56 @@ Backend teams juggle **fragmented runtimes** across APIs, background queues, and
|
|
|
55
98
|
|
|
56
99
|
Motia unifies your entire backend into a **unified state**. APIs, background jobs, and AI agents become interconnected Steps with shared state and integrated observability.
|
|
57
100
|
|
|
101
|
+
| **Before** | **After (Motia)** |
|
|
102
|
+
|---|---|
|
|
103
|
+
| Multiple deployment targets | **Single unified deployment** |
|
|
104
|
+
| Fragmented observability | **End-to-end tracing** |
|
|
105
|
+
| Language dependent | **JavaScript, TypeScript, Python, etc** |
|
|
106
|
+
| Context-switching overhead | **Single intuitive model** |
|
|
107
|
+
| Complex error handling | **Automatic retries & fault tolerance** |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 🔧 Supported Step Types
|
|
112
|
+
|
|
113
|
+
| Type | Trigger | Use Case |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| **`api`** | HTTP Request | Expose REST endpoints |
|
|
116
|
+
| **`event`** | Emitted Topics | React to internal or external events |
|
|
117
|
+
| **`cron`** | Scheduled Time (cron) | Automate recurring jobs |
|
|
118
|
+
| **`noop`** | None | Placeholder for manual/external tasks |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### 🤔 How it Works
|
|
123
|
+
|
|
124
|
+
Motia's architecture is built around a single, powerful primitive: the **Step**. A Step is not just a trigger; it's a powerful container for your business logic. You can write anything from a simple database query to a complex AI agent interaction inside a single step. Instead of managing separate services for APIs, background workers, and scheduled tasks, you simply define how your steps are triggered.
|
|
125
|
+
|
|
126
|
+
- **Need a public API?** Create an `api` step. This defines a route and handler for HTTP requests. You can build a complete REST or GraphQL API just with these steps.
|
|
127
|
+
- **Need a background job or queue?** Have your `api` step `emit` an event. An `event` step subscribed to that event's topic will pick up the job and process it asynchronously. This is how you handle anything that shouldn't block the main request thread, from sending emails to complex data processing.
|
|
128
|
+
- **Need to run a task on a schedule?** Use a `cron` step. It will trigger automatically based on the schedule you define.
|
|
129
|
+
|
|
130
|
+
This model means you no longer need to glue together separate frameworks and tools. A single Motia application can replace a stack that might otherwise include **Nest.js** (for APIs), **Temporal** (for workflows), and **Celery/BullMQ** (for background jobs). It's all just steps and events.
|
|
131
|
+
|
|
132
|
+
## ⚡ Core Concepts
|
|
133
|
+
|
|
134
|
+
The **Step** is Motia's core primitive. The following concepts are deeply integrated with Steps to help you build powerful, complex, and scalable backends:
|
|
135
|
+
|
|
136
|
+
### 🧱 The Step Philosophy
|
|
137
|
+
|
|
138
|
+
- **🎯 Your Logic, Your Step**: A Step holds your business logic. It can be a simple function, a call to a database, or a complex AI agent. This is where your application's real work gets done.
|
|
139
|
+
- **🌍 Any Language, One Workflow**: Write Steps in TypeScript, Python, and other languages to come. all in the same project. Use Python for your AI agents and TypeScript for your API, and Motia makes them work together effortlessly.
|
|
140
|
+
- **⚡ Full Power, No Boilerplate**: Inside a Step's `handler`, you have the full power of the Node.js or Python ecosystem. Install any package, call any API, connect to any database. No restrictions, just your code.
|
|
141
|
+
- **👁️ Zero-Config Observability**: Get full end-to-end tracing and logging for every Step execution, automatically. No setup required. See exactly what happened, when, and why.
|
|
142
|
+
- **🌊 Simple & Powerful Workflows**: Connect Steps together by emitting and subscribing to events. Build complex, multi-stage processes with simple, declarative code.
|
|
143
|
+
- **🏪 Unified State**: Share data between Steps effortlessly. Motia provides built-in state management that is automatically traced, giving you a complete picture of your data's lifecycle through a workflow.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## ✅ The Unified System
|
|
148
|
+
|
|
149
|
+
Motia unifies your entire backend into a **unified state**. APIs, background jobs, and AI agents become interconnected Steps with shared state and integrated observability.
|
|
150
|
+
|
|
58
151
|
| **Before** | **After (Motia)** |
|
|
59
152
|
| --------------------------- | --------------------------------------- |
|
|
60
153
|
| Multiple deployment targets | **Single unified deployment** |
|
|
@@ -113,44 +206,6 @@ Every execution generates a full trace, capturing step timelines, state operatio
|
|
|
113
206
|
|
|
114
207
|
---
|
|
115
208
|
|
|
116
|
-
## 🚀 Quickstart
|
|
117
|
-
|
|
118
|
-
Get up and running in **under 60 seconds**:
|
|
119
|
-
|
|
120
|
-
### 1. Create Your Project
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
npx motia@latest create -i
|
|
124
|
-
```
|
|
125
|
-
- Enter project details like template, project name, etc
|
|
126
|
-
|
|
127
|
-
### 2. Launch the Workbench
|
|
128
|
-
|
|
129
|
-
Start the Motia Workbench:
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
npx motia dev
|
|
133
|
-
# Opens at http://localhost:3000
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
🎉 **That's it!** You now have a fully functional Motia app with:
|
|
137
|
-
- ✅ API endpoint at `/hello-world`
|
|
138
|
-
- ✅ Visual debugger and flow inspector
|
|
139
|
-
- ✅ Built-in observability
|
|
140
|
-
- ✅ Hot reload for instant feedback
|
|
141
|
-
|
|
142
|
-
### 4. Explore the Workbench
|
|
143
|
-
|
|
144
|
-
The Workbench is your command center for developing and monitoring your Motia application.
|
|
145
|
-
|
|
146
|
-
- **🌊 Flows**: Visually inspect, design, and understand the connections between your steps.
|
|
147
|
-
- **🔌 Endpoints**: Test your API endpoints directly from the UI, with full support for streaming responses.
|
|
148
|
-
- **👁️ Traces**: Dive into detailed, end-to-end traces of your workflows. See exactly how data flows, where time is spent, and what errors occurred.
|
|
149
|
-
- **📊 Logs**: View structured, correlated logs for every step execution.
|
|
150
|
-
- **🏪 States**: Inspect the internal state and data passed between steps for any given workflow execution.
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
209
|
## 🔧 CLI Commands
|
|
155
210
|
|
|
156
211
|
Motia comes with a range of [powerful CLI commands](https://www.motia.dev/docs/concepts/cli) to help you manage your projects:
|
|
@@ -191,7 +246,7 @@ bun run dev [options]
|
|
|
191
246
|
### `npx motia build`
|
|
192
247
|
Compiles all your steps (Node.js, Python and more) and builds a lock file based on your current project setup, which is then used by the Motia ecosystem.
|
|
193
248
|
|
|
194
|
-
```
|
|
249
|
+
```bash
|
|
195
250
|
motia build
|
|
196
251
|
```
|
|
197
252
|
|
|
@@ -221,20 +276,13 @@ motia <command> --help
|
|
|
221
276
|
```
|
|
222
277
|
|
|
223
278
|
### 💬 **Get Help**
|
|
224
|
-
- **📋 Questions**: Use our [Discord community](https://discord.gg/
|
|
279
|
+
- **📋 Questions**: Use our [Discord community](https://discord.gg/motia)
|
|
225
280
|
- **🐛 Bug Reports**: [GitHub Issues](https://github.com/MotiaDev/motia/issues)
|
|
226
281
|
- **📖 Documentation**: [Official Docs](https://motia.dev/docs)
|
|
282
|
+
- **🎥 Blog**: [Motia Blog](https://blog.motia.dev/)
|
|
227
283
|
|
|
228
284
|
### 🤝 **Contributing**
|
|
229
285
|
|
|
230
|
-
#### 🚀 Roadmap
|
|
231
|
-
|
|
232
|
-
We're building Motia in the open, and we'd love for you to be a part of the journey.
|
|
233
|
-
|
|
234
|
-
Check out our public roadmap to see what's planned, what's in progress, and what's recently shipped:
|
|
235
|
-
|
|
236
|
-
👉 [View our public Roadmap](https://github.com/orgs/MotiaDev/projects/2/views/2)
|
|
237
|
-
|
|
238
286
|
We welcome contributions! Whether it's:
|
|
239
287
|
- 🐛 Bug fixes and improvements
|
|
240
288
|
- ✨ New features and step types
|
|
@@ -246,18 +294,10 @@ Check out our [Contributing Guide](https://github.com/MotiaDev/motia/blob/main/C
|
|
|
246
294
|
|
|
247
295
|
---
|
|
248
296
|
|
|
249
|
-
## 📄 License
|
|
250
|
-
|
|
251
|
-
This project is licensed under the [MIT License.](https://github.com/MotiaDev/motia?tab=MIT-1-ov-file)
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
<div align="center">
|
|
256
|
-
|
|
257
297
|
**🌟 Ready to unify your backend?**
|
|
258
298
|
|
|
259
|
-
[🚀 **Get Started Now**](https://motia.dev) • [📖 **Read the Docs**](https://motia.dev/docs) • [💬 **Join Discord**](https://discord.gg/
|
|
299
|
+
[🚀 **Get Started Now**](https://motia.dev) • [📖 **Read the Docs**](https://motia.dev/docs) • [💬 **Join Discord**](https://discord.gg/motia)
|
|
260
300
|
|
|
261
|
-
|
|
301
|
+
---
|
|
262
302
|
|
|
263
|
-
|
|
303
|
+
Built with ❤️ by the Motia team • **Star us if you find [Motia](https://github.com/MotiaDev/motia) useful!** ⭐
|
package/dist/cjs/cli.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
3
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
7
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
5
8
|
const commander_1 = require("commander");
|
|
6
9
|
require("./cloud");
|
|
7
10
|
const version_1 = require("./version");
|
|
8
11
|
const config_utils_1 = require("./cloud/config-utils");
|
|
12
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
13
|
const defaultPort = 3000;
|
|
10
14
|
const defaultHost = '0.0.0.0';
|
|
11
15
|
require('dotenv/config');
|
|
@@ -28,14 +32,23 @@ commander_1.program
|
|
|
28
32
|
.option('-c, --cursor', 'Copy .cursor folder from template')
|
|
29
33
|
.option('-i, --interactive', 'Use interactive prompts to create project')
|
|
30
34
|
.option('-y, --skip-confirmation', 'Skip confirmation prompt')
|
|
35
|
+
.option('-d, --skip-tutorial', 'Skip the motia tutorial', false)
|
|
31
36
|
.action((0, config_utils_1.handler)(async (arg, context) => {
|
|
32
37
|
if (arg.name || arg.template || arg.cursor) {
|
|
33
38
|
const { create } = require('./create');
|
|
39
|
+
const disableTutorial = await inquirer_1.default.prompt({
|
|
40
|
+
type: 'confirm',
|
|
41
|
+
name: 'disableTutorial',
|
|
42
|
+
message: 'Do you wish to disable the motia tutorial?',
|
|
43
|
+
default: arg.skipTutorial,
|
|
44
|
+
when: () => arg.skipTutorial === false,
|
|
45
|
+
});
|
|
34
46
|
await create({
|
|
35
47
|
projectName: arg.name ?? '.',
|
|
36
48
|
template: arg.template ?? 'default',
|
|
37
49
|
cursorEnabled: arg.cursor,
|
|
38
50
|
context,
|
|
51
|
+
skipTutorialTemplates: disableTutorial.disableTutorial,
|
|
39
52
|
});
|
|
40
53
|
}
|
|
41
54
|
else {
|
|
@@ -143,6 +156,14 @@ generate
|
|
|
143
156
|
stepFilePath: arg.dir,
|
|
144
157
|
});
|
|
145
158
|
});
|
|
159
|
+
generate
|
|
160
|
+
.command('tutorial-flow')
|
|
161
|
+
.description('Download the tutorial flow into an existing motia project')
|
|
162
|
+
.action((0, config_utils_1.handler)(async (_, context) => {
|
|
163
|
+
const { createTutorialFlow } = require('./create/setup-tutorial-flow');
|
|
164
|
+
await createTutorialFlow({ context });
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}));
|
|
146
167
|
const docker = commander_1.program.command('docker').description('Motia docker commands');
|
|
147
168
|
docker
|
|
148
169
|
.command('setup')
|
|
@@ -2,7 +2,7 @@ import type { ApiRouteConfig, ApiRouteHandler } from '@motiadev/core'
|
|
|
2
2
|
// {{imports}}
|
|
3
3
|
|
|
4
4
|
type RouterPath = {
|
|
5
|
-
|
|
5
|
+
stepName: string
|
|
6
6
|
method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head'
|
|
7
7
|
handler: ApiRouteHandler
|
|
8
8
|
config: ApiRouteConfig
|
|
@@ -76,12 +76,9 @@ class PythonBuilder {
|
|
|
76
76
|
.replace(/(.*)\.py$/, '$1')
|
|
77
77
|
.replace(/\//g, '.');
|
|
78
78
|
};
|
|
79
|
-
const toFastAPI = (path) => {
|
|
80
|
-
return path.replace(/:(\w+)/g, '{${1}}');
|
|
81
|
-
};
|
|
82
79
|
const zipName = 'router-python.zip';
|
|
83
80
|
const archive = new archiver_1.Archiver(path_1.default.join(constants_1.distDir, zipName));
|
|
84
|
-
const dependencies = ['
|
|
81
|
+
const dependencies = ['uvicorn', 'pydantic', 'pydantic_core', 'uvloop', 'starlette', 'typing_inspection'];
|
|
85
82
|
const lambdaSitePackages = `${process.env.PYTHON_SITE_PACKAGES}-lambda`;
|
|
86
83
|
await Promise.all(dependencies.map(async (packageName) => (0, add_package_to_archive_1.addPackageToArchive)(archive, lambdaSitePackages, packageName)));
|
|
87
84
|
for (const step of steps) {
|
|
@@ -92,17 +89,9 @@ class PythonBuilder {
|
|
|
92
89
|
.replace('# {{imports}}', steps
|
|
93
90
|
.map((step, index) => `from ${getStepPath(step)} import handler as route${index}_handler, config as route${index}_config`)
|
|
94
91
|
.join('\n'))
|
|
95
|
-
.replace('# {{
|
|
96
|
-
.map((step, index) => {
|
|
97
|
-
|
|
98
|
-
return [
|
|
99
|
-
`@app.${method}('${toFastAPI(step.config.path)}')`,
|
|
100
|
-
`async def handler_${index}(request: Request, response: Response):`,
|
|
101
|
-
` handler_route = await router(route${index}_handler, route${index}_config, create_context(context, '${step.config.name}'))`,
|
|
102
|
-
` return await handler_route(request, response)`,
|
|
103
|
-
].join('\n ');
|
|
104
|
-
})
|
|
105
|
-
.join('\n\n '));
|
|
92
|
+
.replace('# {{router paths}}', steps
|
|
93
|
+
.map((step, index) => `'${step.config.method} ${step.config.path}': RouterPath('${step.config.name}', '${step.config.method.toLowerCase()}', route${index}_handler, route${index}_config)`)
|
|
94
|
+
.join(',\n '));
|
|
106
95
|
archive.append(file, 'router.py');
|
|
107
96
|
(0, include_static_files_1.includeStaticFiles)(steps, this.builder, archive);
|
|
108
97
|
// Finalize the archive and wait for completion
|
|
@@ -1,78 +1,17 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Dict, Callable, Any, Literal
|
|
2
2
|
# {{imports}}
|
|
3
3
|
|
|
4
|
-
def create_api_step_handler(handler, config):
|
|
5
|
-
async def middleware_handler(req, ctx):
|
|
6
|
-
if not config.get('middleware'):
|
|
7
|
-
return await handler(req, ctx)
|
|
8
|
-
|
|
9
|
-
# # Compose middleware in reverse order
|
|
10
|
-
composed_handler = lambda: handler(req, ctx)
|
|
11
|
-
for middleware in reversed(config.get('middleware', [])):
|
|
12
|
-
current_handler = composed_handler
|
|
13
|
-
composed_handler = lambda: middleware(req, ctx, current_handler)
|
|
14
|
-
|
|
15
|
-
return await composed_handler()
|
|
16
|
-
|
|
17
|
-
return middleware_handler
|
|
18
|
-
|
|
19
|
-
def create_context(context, step_name):
|
|
20
|
-
context.logger = context.logger.child({ 'step': step_name })
|
|
21
|
-
|
|
22
|
-
return context
|
|
23
|
-
|
|
24
|
-
async def router(handler, config, context):
|
|
25
|
-
async def route(request: Request, response: Response):
|
|
26
|
-
data = {
|
|
27
|
-
"body": await request.json() if await request.body() else {},
|
|
28
|
-
"headers": dict(request.headers),
|
|
29
|
-
"path_params": request.path_params,
|
|
30
|
-
"query_params": dict(request.query_params)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
try:
|
|
34
|
-
middleware_handler = create_api_step_handler(handler, config)
|
|
35
|
-
result = await middleware_handler(data, context)
|
|
36
|
-
|
|
37
|
-
if result:
|
|
38
|
-
if result.get('headers'):
|
|
39
|
-
for key, value in result['headers'].items():
|
|
40
|
-
response.headers[key] = value
|
|
41
|
-
|
|
42
|
-
response.status_code = result['status']
|
|
43
|
-
return result['body']
|
|
44
|
-
else:
|
|
45
|
-
response.status_code = 200
|
|
46
|
-
return {}
|
|
47
4
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
5
|
+
class RouterPath:
|
|
6
|
+
def __init__(self, step_name: str, method: Literal['get', 'post', 'put', 'delete', 'patch', 'options', 'head'], handler: Callable, config: Dict[str, Any]):
|
|
7
|
+
self.step_name = step_name
|
|
8
|
+
self.method = method
|
|
9
|
+
self.handler = handler
|
|
10
|
+
self.config = config
|
|
52
11
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# @app.get('/')
|
|
60
|
-
# async def handler_0(request: Request, response: Response):
|
|
61
|
-
# handler_route = await router(handler, config, create_context(context, 'StepName'))
|
|
62
|
-
# return await handler_route(request, response)
|
|
63
|
-
|
|
64
|
-
# {{routes}}
|
|
65
|
-
|
|
66
|
-
@app.middleware("http")
|
|
67
|
-
async def not_found(request: Request, call_next):
|
|
68
|
-
response = await call_next(request)
|
|
69
|
-
|
|
70
|
-
if response.status_code == 404:
|
|
71
|
-
return Response(
|
|
72
|
-
content = '{"error": "Not found"}',
|
|
73
|
-
status_code = 404,
|
|
74
|
-
media_type = 'application/json'
|
|
75
|
-
)
|
|
76
|
-
return response
|
|
77
|
-
|
|
78
|
-
return app
|
|
12
|
+
router_paths: Dict[str, RouterPath] = {
|
|
13
|
+
# Example:
|
|
14
|
+
# 'POST /api/parallel-merge/python': RouterPath('Parallel Merge Python', 'post', route0_handler, route0_config)
|
|
15
|
+
|
|
16
|
+
# {{router paths}}
|
|
17
|
+
}
|
|
@@ -14,9 +14,10 @@ cli_1.cloudCli
|
|
|
14
14
|
.description('Deploy a new version to Motia Cloud')
|
|
15
15
|
.requiredOption('-k, --api-key <key>', 'The API key for authentication', process.env.MOTIA_API_KEY)
|
|
16
16
|
.requiredOption('-v, --version-name <version>', 'The version to deploy')
|
|
17
|
-
.option('-p, --project-id <id>', 'Project ID')
|
|
18
|
-
.option('-s, --environment-id <id>', 'Environment ID')
|
|
17
|
+
.option('-p, --project-id <id>', 'Project ID (Deprecated)')
|
|
18
|
+
.option('-s, --environment-id <id>', 'Environment ID', process.env.MOTIA_ENVIRONMENT_ID)
|
|
19
19
|
.option('-e, --env-file <path>', 'Path to environment file')
|
|
20
|
+
.option('-n, --project-name <name>', 'Project name (used when creating a new project)', '')
|
|
20
21
|
.action((0, config_utils_1.handler)(async (arg, context) => {
|
|
21
22
|
const listener = new cli_listener_1.CliListener(context);
|
|
22
23
|
const builder = await (0, build_1.build)(listener);
|
|
@@ -26,11 +27,14 @@ cli_1.cloudCli
|
|
|
26
27
|
}
|
|
27
28
|
context.log('build-completed', (message) => message.tag('success').append('Build completed'));
|
|
28
29
|
context.log('creating-deployment', (message) => message.tag('progress').append('Creating deployment...'));
|
|
29
|
-
const deployment = await cloud_api_1.cloudApi
|
|
30
|
+
const deployment = await cloud_api_1.cloudApi
|
|
31
|
+
.createDeployment({
|
|
30
32
|
apiKey: arg.apiKey,
|
|
31
33
|
versionName: arg.versionName,
|
|
32
34
|
environmentId: arg.environmentId,
|
|
33
|
-
|
|
35
|
+
projectName: arg.projectName,
|
|
36
|
+
})
|
|
37
|
+
.catch((error) => {
|
|
34
38
|
context.log('creating-deployment', (message) => message.tag('failed').append('Failed to create deployment'));
|
|
35
39
|
throw error;
|
|
36
40
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.cloudEndpoints = exports.cloudApiWsUrl = exports.cloudApiBaseUrl = void 0;
|
|
4
4
|
exports.cloudApiBaseUrl = process.env.MOTIACLOUD_API_BASE_URL || 'https://motia-hub-api.motiahub.com';
|
|
5
|
-
exports.cloudApiWsUrl = process.env.MOTIACLOUD_API_WS_URL || 'wss://motia-hub-api.motiahub.com';
|
|
5
|
+
exports.cloudApiWsUrl = process.env.MOTIACLOUD_API_WS_URL || 'wss://ws-motia-hub-api.motiahub.com';
|
|
6
6
|
exports.cloudEndpoints = {
|
|
7
7
|
createDeployment: `${exports.cloudApiBaseUrl}/v1/deployments`,
|
|
8
8
|
startDeployment: `${exports.cloudApiBaseUrl}/v1/deployments/start`,
|
|
@@ -16,19 +16,24 @@ const deploy = async (input) => {
|
|
|
16
16
|
});
|
|
17
17
|
context.log('starting-deployment', (message) => message.tag('success').append('Deployment started'));
|
|
18
18
|
const subscription = client.subscribeItem('deployment', deploymentId, 'data');
|
|
19
|
+
const interval = setInterval(() => {
|
|
20
|
+
const state = subscription.getState();
|
|
21
|
+
if (state) {
|
|
22
|
+
listener.onDeployProgress(state);
|
|
23
|
+
}
|
|
24
|
+
}, 1000);
|
|
19
25
|
await new Promise((resolve) => {
|
|
20
26
|
subscription.addChangeListener((item) => {
|
|
21
|
-
if (item) {
|
|
27
|
+
if (item && ['failed', 'completed'].includes(item.status)) {
|
|
28
|
+
clearInterval(interval);
|
|
22
29
|
listener.onDeployProgress(item);
|
|
23
30
|
if (item.status === 'completed') {
|
|
24
31
|
listener.onDeployEnd({
|
|
25
32
|
output: item.outputs,
|
|
26
33
|
});
|
|
27
34
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
resolve();
|
|
31
|
-
}
|
|
35
|
+
client.close();
|
|
36
|
+
resolve();
|
|
32
37
|
}
|
|
33
38
|
});
|
|
34
39
|
});
|
|
@@ -50,14 +50,15 @@ class CliListener {
|
|
|
50
50
|
this.context.log('build-failed', (message) => {
|
|
51
51
|
message.box(['Unable to deploy to Motia Cloud, please fix the following errors'], 'red');
|
|
52
52
|
});
|
|
53
|
-
console.log('');
|
|
54
53
|
const errorTag = colors_1.default.red('✗ [ERROR]');
|
|
55
54
|
errors.map((error) => {
|
|
56
55
|
const filePath = colors_1.default.gray(`[${error.relativePath}]`);
|
|
57
|
-
|
|
56
|
+
this.context.log(`build-errors-${error.relativePath}`, (message) => {
|
|
57
|
+
message.tag('failed').append(`${errorTag} ${filePath} ${error.message}`);
|
|
58
|
+
});
|
|
58
59
|
});
|
|
59
|
-
console.log(colors_1.default.gray('\n--------------------------------\n'));
|
|
60
60
|
this.context.log('build-failed-end', (message) => {
|
|
61
|
+
message.append(colors_1.default.gray('\n--------------------------------\n'));
|
|
61
62
|
message.tag('failed').append('Deployment canceled', 'red');
|
|
62
63
|
});
|
|
63
64
|
}
|
|
@@ -29,26 +29,30 @@ const printDeploymentStatus = (data, context) => {
|
|
|
29
29
|
return colors_1.default.gray(spinner);
|
|
30
30
|
};
|
|
31
31
|
context.log('deployment-status-blank-1', (message) => message.append(''));
|
|
32
|
-
const generateEmitList = (emits) => {
|
|
32
|
+
const generateEmitList = (emits = []) => {
|
|
33
33
|
return emits.map((e) => colors_1.default.white(`⌁ ${e}`)).join(colors_1.default.white(', '));
|
|
34
34
|
};
|
|
35
35
|
const getStatus = (status) => {
|
|
36
36
|
return status === 'failed' ? colors_1.default.red('✘') : status === 'completed' ? colors_1.default.green('✓') : getSpinner();
|
|
37
37
|
};
|
|
38
38
|
const lambda = (stepName, color) => color(`λ ${stepName}`);
|
|
39
|
+
const eventStatus = data.events.reduce((acc, event) => {
|
|
40
|
+
return acc === 'failed' ? 'failed' : acc === 'completed' ? event.status : acc;
|
|
41
|
+
}, 'completed');
|
|
39
42
|
const cronStatus = data.cron.reduce((acc, cron) => {
|
|
40
43
|
return acc === 'failed' ? 'failed' : acc === 'completed' ? cron.status : acc;
|
|
41
44
|
}, 'completed');
|
|
42
45
|
const apigwStatus = data.endpoints.reduce((acc, endpoint) => {
|
|
43
46
|
return acc === 'failed' ? 'failed' : acc === 'completed' ? endpoint.status : acc;
|
|
44
47
|
}, 'completed');
|
|
48
|
+
context.log('deployment-status-step-handler', (message) => message.append(`[${getStatus(eventStatus)}] [λ Step Handler]`));
|
|
45
49
|
// Display event listeners
|
|
46
50
|
data.events.forEach((event) => {
|
|
47
51
|
const color = (0, exports.getStatusColor)(event.status);
|
|
48
52
|
const status = getStatus(event.status);
|
|
49
53
|
const topicList = generateEmitList(event.topics);
|
|
50
54
|
context.log(`event-${event.stepName}`, (message) => {
|
|
51
|
-
message.append(`[${status}]
|
|
55
|
+
message.append(`[${status}] ↳ [${topicList}] → [≡ ${color(event.queue)}] → [${lambda(event.stepName, color)}]`);
|
|
52
56
|
});
|
|
53
57
|
});
|
|
54
58
|
context.log('deployment-status-blank-2', (message) => message.append(''));
|
|
@@ -67,10 +71,11 @@ const printDeploymentStatus = (data, context) => {
|
|
|
67
71
|
context.log('deployment-status-cron', (message) => message.append(`[${getStatus(cronStatus)}] [↺ Cron]`));
|
|
68
72
|
if (data.cron.length > 0) {
|
|
69
73
|
data.cron.forEach((cronJob) => {
|
|
74
|
+
const color = (0, exports.getStatusColor)(cronJob.status);
|
|
70
75
|
const status = getStatus(cronJob.status);
|
|
71
76
|
const emitsList = generateEmitList(cronJob.emits);
|
|
72
77
|
context.log(`cron-${cronJob.stepName}`, (message) => {
|
|
73
|
-
message.append(`[${status}] ↳ ${cronJob.cron} → [${emitsList}]`);
|
|
78
|
+
message.append(`[${status}] ↳ ${cronJob.cron} → [${lambda(cronJob.stepName, color)}] → [${emitsList}]`);
|
|
74
79
|
});
|
|
75
80
|
});
|
|
76
81
|
}
|
|
@@ -82,7 +87,7 @@ const printDeploymentStatus = (data, context) => {
|
|
|
82
87
|
context.log('deployment-status-legent-queue', (message) => message.append('↳ ≡ Queue'));
|
|
83
88
|
context.log('deployment-status-legent-api-gateway', (message) => message.append('↳ ⛩ API Gateway'));
|
|
84
89
|
context.log('deployment-status-legent-topic', (message) => message.append('↳ ⌁ Topic'));
|
|
85
|
-
context.log('deployment-status-legent-function-handler', (message) => message.append('↳ λ
|
|
90
|
+
context.log('deployment-status-legent-function-handler', (message) => message.append('↳ λ Step Handler'));
|
|
86
91
|
context.log('deployment-status-legent-cron-job', (message) => message.append('↳ ↺ Cron Job'));
|
|
87
92
|
};
|
|
88
93
|
exports.printDeploymentStatus = printDeploymentStatus;
|