motia 0.7.2-beta.135-745094 → 0.7.3-beta.136
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/cjs/create/templates/generate.js +2 -2
- package/dist/cjs/create/templates/generate.ts +2 -2
- package/dist/cjs/create/templates/nodejs/services/pet-store.ts.txt +29 -0
- package/dist/cjs/create/templates/nodejs/steps/{petstore/api.step.ts-features.json.txt → api.step.ts-features.json.txt} +9 -9
- package/dist/{esm/create/templates/nodejs/steps/petstore → cjs/create/templates/nodejs/steps}/api.step.ts.txt +2 -1
- package/dist/cjs/create/templates/nodejs/steps/{petstore/process-food-order.step.ts.txt → process-food-order.step.ts.txt} +1 -1
- package/dist/cjs/cursor-rules/dot-files/.claude/CLAUDE.md +467 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/README.md +97 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/agents/code-reviewer.md +153 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/agents/debugger.md +259 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/agents/test-runner.md +268 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/add-authentication.md +491 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/ai-ml-patterns.md +748 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/authentication.md +515 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/backend-types.md +719 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/build-api.md +407 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/claude-workflows.md +1032 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/complete-backend.md +345 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/create-api.md +96 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/data-processing.md +977 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/integrate-ai.md +852 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/javascript-patterns.md +678 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/multi-language-workflow.md +756 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/multi-language.md +141 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/process-background-jobs.md +587 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/commands/process-events.md +89 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/hooks/pre-commit.sh +84 -0
- package/dist/cjs/cursor-rules/dot-files/.claude/settings.json +37 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/ai-agent-patterns.mdc +725 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/api-design-patterns.mdc +740 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/api-steps.mdc +230 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/architecture.mdc +189 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/authentication-patterns.mdc +620 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/background-job-patterns.mdc +628 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/complete-application-patterns.mdc +433 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/complete-backend-generator.mdc +415 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/cron-steps.mdc +257 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/event-steps.mdc +504 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/instructions.mdc +15 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/multi-language-workflows.mdc +1059 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/noop-steps.mdc +57 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/production-deployment.mdc +668 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/realtime-streaming.mdc +656 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/state-management.mdc +371 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/steps.mdc +373 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/testing.mdc +329 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/typescript.mdc +409 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/ui-steps.mdc +429 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/workflow-patterns.mdc +938 -0
- package/dist/cjs/cursor-rules/dot-files/AGENTS.md +397 -0
- package/dist/cjs/cursor-rules/dot-files/README.md +58 -0
- package/dist/esm/create/templates/generate.js +2 -2
- package/dist/esm/create/templates/generate.ts +2 -2
- package/dist/esm/create/templates/nodejs/services/pet-store.ts.txt +29 -0
- package/dist/esm/create/templates/nodejs/steps/{petstore/api.step.ts-features.json.txt → api.step.ts-features.json.txt} +9 -9
- package/dist/{cjs/create/templates/nodejs/steps/petstore → esm/create/templates/nodejs/steps}/api.step.ts.txt +2 -1
- package/dist/esm/create/templates/nodejs/steps/{petstore/process-food-order.step.ts.txt → process-food-order.step.ts.txt} +1 -1
- package/dist/esm/cursor-rules/dot-files/.claude/CLAUDE.md +467 -0
- package/dist/esm/cursor-rules/dot-files/.claude/README.md +97 -0
- package/dist/esm/cursor-rules/dot-files/.claude/agents/code-reviewer.md +153 -0
- package/dist/esm/cursor-rules/dot-files/.claude/agents/debugger.md +259 -0
- package/dist/esm/cursor-rules/dot-files/.claude/agents/test-runner.md +268 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/add-authentication.md +491 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/ai-ml-patterns.md +748 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/authentication.md +515 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/backend-types.md +719 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/build-api.md +407 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/claude-workflows.md +1032 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/complete-backend.md +345 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/create-api.md +96 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/data-processing.md +977 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/integrate-ai.md +852 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/javascript-patterns.md +678 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/multi-language-workflow.md +756 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/multi-language.md +141 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/process-background-jobs.md +587 -0
- package/dist/esm/cursor-rules/dot-files/.claude/commands/process-events.md +89 -0
- package/dist/esm/cursor-rules/dot-files/.claude/hooks/pre-commit.sh +84 -0
- package/dist/esm/cursor-rules/dot-files/.claude/settings.json +37 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/ai-agent-patterns.mdc +725 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/api-design-patterns.mdc +740 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/api-steps.mdc +230 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/architecture.mdc +189 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/authentication-patterns.mdc +620 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/background-job-patterns.mdc +628 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/complete-application-patterns.mdc +433 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/complete-backend-generator.mdc +415 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/cron-steps.mdc +257 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/event-steps.mdc +504 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/instructions.mdc +15 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/multi-language-workflows.mdc +1059 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/noop-steps.mdc +57 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/production-deployment.mdc +668 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/realtime-streaming.mdc +656 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/state-management.mdc +371 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/steps.mdc +373 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/testing.mdc +329 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/typescript.mdc +409 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/ui-steps.mdc +429 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/workflow-patterns.mdc +938 -0
- package/dist/esm/cursor-rules/dot-files/AGENTS.md +397 -0
- package/dist/esm/cursor-rules/dot-files/README.md +58 -0
- package/package.json +4 -4
- package/dist/cjs/create/templates/nodejs/.cursor/architecture/database/database-migration.mdc +0 -49
- package/dist/cjs/create/templates/nodejs/.cursor/architecture/database/database.mdc +0 -83
- package/dist/cjs/create/templates/nodejs/src/services/pet-store/create-order.ts.txt +0 -15
- package/dist/cjs/create/templates/nodejs/src/services/pet-store/create-pet.ts.txt +0 -14
- package/dist/cjs/create/templates/nodejs/src/services/pet-store/index.ts.txt +0 -9
- package/dist/cjs/cursor-rules/dot-files/.cursor/architecture/architecture.mdc +0 -96
- package/dist/cjs/cursor-rules/dot-files/.cursor/architecture/error-handling.mdc +0 -122
- package/dist/cjs/cursor-rules/dot-files/.cursor/index.mdc +0 -26
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/api-steps.mdc +0 -317
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/cron-steps.mdc +0 -144
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/event-steps.mdc +0 -157
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/middlewares.mdc +0 -122
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/realtime-streaming.mdc +0 -231
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/state-management.mdc +0 -73
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/ui-steps.mdc +0 -76
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/virtual-steps.mdc +0 -172
- package/dist/esm/create/templates/nodejs/.cursor/architecture/database/database-migration.mdc +0 -49
- package/dist/esm/create/templates/nodejs/.cursor/architecture/database/database.mdc +0 -83
- package/dist/esm/create/templates/nodejs/src/services/pet-store/create-order.ts.txt +0 -15
- package/dist/esm/create/templates/nodejs/src/services/pet-store/create-pet.ts.txt +0 -14
- package/dist/esm/create/templates/nodejs/src/services/pet-store/index.ts.txt +0 -9
- package/dist/esm/cursor-rules/dot-files/.cursor/architecture/architecture.mdc +0 -96
- package/dist/esm/cursor-rules/dot-files/.cursor/architecture/error-handling.mdc +0 -122
- package/dist/esm/cursor-rules/dot-files/.cursor/index.mdc +0 -26
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/api-steps.mdc +0 -317
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/cron-steps.mdc +0 -144
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/event-steps.mdc +0 -157
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/middlewares.mdc +0 -122
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/realtime-streaming.mdc +0 -231
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/state-management.mdc +0 -73
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/ui-steps.mdc +0 -76
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/virtual-steps.mdc +0 -172
- /package/dist/cjs/create/templates/nodejs/{src/services/pet-store → services}/types.ts.txt +0 -0
- /package/dist/cjs/create/templates/nodejs/steps/{petstore/notification.step.ts.txt → notification.step.ts.txt} +0 -0
- /package/dist/cjs/create/templates/nodejs/steps/{petstore/process-food-order.step.ts-features.json.txt → process-food-order.step.ts-features.json.txt} +0 -0
- /package/dist/cjs/create/templates/nodejs/steps/{petstore/state-audit-cron.step.ts-features.json.txt → state-audit-cron.step.ts-features.json.txt} +0 -0
- /package/dist/cjs/create/templates/nodejs/steps/{petstore/state-audit-cron.step.ts.txt → state-audit-cron.step.ts.txt} +0 -0
- /package/dist/esm/create/templates/nodejs/{src/services/pet-store → services}/types.ts.txt +0 -0
- /package/dist/esm/create/templates/nodejs/steps/{petstore/notification.step.ts.txt → notification.step.ts.txt} +0 -0
- /package/dist/esm/create/templates/nodejs/steps/{petstore/process-food-order.step.ts-features.json.txt → process-food-order.step.ts-features.json.txt} +0 -0
- /package/dist/esm/create/templates/nodejs/steps/{petstore/state-audit-cron.step.ts-features.json.txt → state-audit-cron.step.ts-features.json.txt} +0 -0
- /package/dist/esm/create/templates/nodejs/steps/{petstore/state-audit-cron.step.ts.txt → state-audit-cron.step.ts.txt} +0 -0
|
@@ -40,7 +40,7 @@ const glob_1 = require("glob");
|
|
|
40
40
|
const generateTemplateSteps = (templateFolder) => {
|
|
41
41
|
return async (rootDir, context) => {
|
|
42
42
|
const templatePath = path.join(__dirname, templateFolder);
|
|
43
|
-
const files = (0, glob_1.globSync)('**/*', { absolute: false, cwd: templatePath
|
|
43
|
+
const files = (0, glob_1.globSync)('**/*', { absolute: false, cwd: templatePath });
|
|
44
44
|
try {
|
|
45
45
|
for (const fileName of files) {
|
|
46
46
|
const filePath = path.join(templatePath, fileName);
|
|
@@ -54,7 +54,7 @@ const generateTemplateSteps = (templateFolder) => {
|
|
|
54
54
|
(0, fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
55
55
|
}
|
|
56
56
|
if ((0, fs_1.statSync)(filePath).isDirectory()) {
|
|
57
|
-
const folderPath =
|
|
57
|
+
const folderPath = path.basename(filePath);
|
|
58
58
|
(0, fs_1.mkdirSync)(path.join(rootDir, folderPath), { recursive: true });
|
|
59
59
|
continue;
|
|
60
60
|
}
|
|
@@ -8,7 +8,7 @@ export type Generator = (rootDir: string, context: CliContext) => Promise<void>
|
|
|
8
8
|
export const generateTemplateSteps = (templateFolder: string): Generator => {
|
|
9
9
|
return async (rootDir: string, context: CliContext): Promise<void> => {
|
|
10
10
|
const templatePath = path.join(__dirname, templateFolder)
|
|
11
|
-
const files = globSync('**/*', { absolute: false, cwd: templatePath
|
|
11
|
+
const files = globSync('**/*', { absolute: false, cwd: templatePath })
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
14
|
for (const fileName of files) {
|
|
@@ -24,7 +24,7 @@ export const generateTemplateSteps = (templateFolder: string): Generator => {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
if (statSync(filePath).isDirectory()) {
|
|
27
|
-
const folderPath =
|
|
27
|
+
const folderPath = path.basename(filePath)
|
|
28
28
|
mkdirSync(path.join(rootDir, folderPath), { recursive: true })
|
|
29
29
|
continue
|
|
30
30
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Order, Pet } from './types'
|
|
2
|
+
|
|
3
|
+
export const petStoreService = {
|
|
4
|
+
createPet: async (pet: Omit<Pet, 'id'>): Promise<Pet> => {
|
|
5
|
+
const response = await fetch('https://petstore.swagger.io/v2/pet', {
|
|
6
|
+
method: 'POST',
|
|
7
|
+
body: JSON.stringify({
|
|
8
|
+
name: pet?.name ?? '',
|
|
9
|
+
photoUrls: [pet?.photoUrl ?? ''],
|
|
10
|
+
status: 'available',
|
|
11
|
+
}),
|
|
12
|
+
headers: { 'Content-Type': 'application/json' },
|
|
13
|
+
})
|
|
14
|
+
return response.json()
|
|
15
|
+
},
|
|
16
|
+
createOrder: async (order: Omit<Order, 'id'>): Promise<Order> => {
|
|
17
|
+
const response = await fetch('https://petstore.swagger.io/v2/store/order', {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
body: JSON.stringify({
|
|
20
|
+
quantity: order?.quantity ?? 1,
|
|
21
|
+
petId: 1,
|
|
22
|
+
shipDate: order?.shipDate ?? new Date().toISOString(),
|
|
23
|
+
status: order?.status ?? 'placed',
|
|
24
|
+
}),
|
|
25
|
+
headers: { 'Content-Type': 'application/json' },
|
|
26
|
+
})
|
|
27
|
+
return response.json()
|
|
28
|
+
},
|
|
29
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "Step Configuration",
|
|
5
5
|
"description": "All steps should have a defined configuration, this is how you define the step's behavior and how it will be triggered.",
|
|
6
6
|
"lines": [
|
|
7
|
-
"
|
|
7
|
+
"6-30"
|
|
8
8
|
]
|
|
9
9
|
},
|
|
10
10
|
{
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"title": "API Step",
|
|
13
13
|
"description": "Definition of an API endpoint",
|
|
14
14
|
"lines": [
|
|
15
|
-
"
|
|
15
|
+
"12-13"
|
|
16
16
|
]
|
|
17
17
|
},
|
|
18
18
|
{
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"title": "Request body",
|
|
21
21
|
"description": "Definition of the expected request body. Motia will automatically generate types based on this schema.",
|
|
22
22
|
"lines": [
|
|
23
|
-
"
|
|
23
|
+
"14-25"
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
26
|
{
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"title": "Response Payload",
|
|
29
29
|
"description": "Definition of the expected response payload, Motia will generate the types automatically based on this schema. This is also important to create the Open API spec later.",
|
|
30
30
|
"lines": [
|
|
31
|
-
"
|
|
31
|
+
"26-28"
|
|
32
32
|
]
|
|
33
33
|
},
|
|
34
34
|
{
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"title": "Emits",
|
|
37
37
|
"description": "We can define the events that this step will emit, this is how we can trigger other Motia Steps.",
|
|
38
38
|
"lines": [
|
|
39
|
-
"
|
|
40
|
-
"
|
|
39
|
+
"29",
|
|
40
|
+
"39-46"
|
|
41
41
|
]
|
|
42
42
|
},
|
|
43
43
|
{
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"title": "Handler",
|
|
46
46
|
"description": "The handler is the function that will be executed when the step is triggered. This one receives the request body and emits events.",
|
|
47
47
|
"lines": [
|
|
48
|
-
"
|
|
48
|
+
"32-50"
|
|
49
49
|
]
|
|
50
50
|
},
|
|
51
51
|
{
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"title": "Logger",
|
|
54
54
|
"description": "The logger is a utility that allows you to log messages to the console. It is available in the handler function. We encourage you to use it instead of console.log. It will automatically be tied to the trace id of the request.",
|
|
55
55
|
"lines": [
|
|
56
|
-
"
|
|
56
|
+
"33"
|
|
57
57
|
]
|
|
58
58
|
},
|
|
59
59
|
{
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"title": "HTTP Response",
|
|
62
62
|
"description": "The handler can return a response to the client. This is how we can return a response to the client. It must comply with the responseSchema defined in the step configuration.",
|
|
63
63
|
"lines": [
|
|
64
|
-
"
|
|
64
|
+
"49"
|
|
65
65
|
]
|
|
66
66
|
}
|
|
67
67
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ApiRouteConfig, Handlers } from 'motia'
|
|
2
2
|
import { z } from 'zod'
|
|
3
|
-
import { petStoreService
|
|
3
|
+
import { petStoreService } from '../services/pet-store'
|
|
4
|
+
import { petSchema } from '../services/types'
|
|
4
5
|
|
|
5
6
|
export const config: ApiRouteConfig = {
|
|
6
7
|
type: 'api',
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
# Motia Framework Development Assistant
|
|
2
|
+
|
|
3
|
+
You are helping develop a **Motia project** - a unified backend framework that uses event-driven architecture with multiple programming languages.
|
|
4
|
+
|
|
5
|
+
## Core Motia Concepts
|
|
6
|
+
|
|
7
|
+
### Steps Architecture
|
|
8
|
+
|
|
9
|
+
- **Steps** are the fundamental building blocks - each has `config` and `handler`
|
|
10
|
+
- **API Steps**: HTTP endpoints (`type: 'api'`)
|
|
11
|
+
- **Event Steps**: Event processors (`type: 'event'`)
|
|
12
|
+
- **Cron Steps**: Scheduled tasks (`type: 'cron'`)
|
|
13
|
+
- **Stream Steps**: Real-time data (`type: 'stream'`)
|
|
14
|
+
- **NOOP Steps**: Workflow routing (`type: 'noop'`)
|
|
15
|
+
|
|
16
|
+
### Event-Driven Communication
|
|
17
|
+
|
|
18
|
+
- Steps communicate via `emit({ topic: 'event-name', data: {...} })`
|
|
19
|
+
- Subscribe to events: `subscribes: ['topic-name']` in config
|
|
20
|
+
- Creates loose coupling and parallel execution
|
|
21
|
+
|
|
22
|
+
### State Management
|
|
23
|
+
|
|
24
|
+
- Persistent state via `state.set(group, key, value)` and `state.get(group, key)`
|
|
25
|
+
- State is scoped by groups ('orders', 'users', etc.) and keys
|
|
26
|
+
- Supports trace-scoped and global persistence
|
|
27
|
+
|
|
28
|
+
## File Structure
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
steps/ # All step implementations
|
|
32
|
+
├── *.step.ts # Step files (config + handler)
|
|
33
|
+
├── *-features.json # Tutorial/workbench metadata
|
|
34
|
+
services/ # Shared business logic
|
|
35
|
+
types.d.ts # Auto-generated types from step configs
|
|
36
|
+
motia-workbench.json # Visual flow configuration
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Step Implementation Patterns
|
|
40
|
+
|
|
41
|
+
Motia supports **multiple programming languages** in the same project. Choose the best language for each task:
|
|
42
|
+
|
|
43
|
+
- **TypeScript/JavaScript**: APIs, web logic, real-time features
|
|
44
|
+
- **Python**: AI/ML, data science, image processing, analytics
|
|
45
|
+
- **Ruby**: Reports, data exports, file processing, templating
|
|
46
|
+
|
|
47
|
+
### TypeScript API Step
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { z } from 'zod'
|
|
51
|
+
import type { ApiRouteConfig, Handlers } from '@motia/core'
|
|
52
|
+
|
|
53
|
+
export const config: ApiRouteConfig = {
|
|
54
|
+
type: 'api',
|
|
55
|
+
name: 'CreateOrder',
|
|
56
|
+
method: 'POST',
|
|
57
|
+
path: '/orders',
|
|
58
|
+
bodySchema: z.object({
|
|
59
|
+
productId: z.string(),
|
|
60
|
+
quantity: z.number(),
|
|
61
|
+
}),
|
|
62
|
+
emits: ['order.created'],
|
|
63
|
+
flows: ['ecommerce'],
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const handler: Handlers['CreateOrder'] = async (req, { logger, emit, state, traceId }) => {
|
|
67
|
+
const order = { id: crypto.randomUUID(), ...req.body, createdAt: new Date() }
|
|
68
|
+
await state.set('orders', order.id, order)
|
|
69
|
+
await emit({ topic: 'order.created', data: order })
|
|
70
|
+
return { status: 201, body: order }
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### JavaScript Event Step
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
// steps/process-payment.step.js
|
|
78
|
+
exports.config = {
|
|
79
|
+
type: 'event',
|
|
80
|
+
name: 'ProcessPayment',
|
|
81
|
+
subscribes: ['order.created'],
|
|
82
|
+
emits: ['payment.processed', 'payment.failed'],
|
|
83
|
+
input: {
|
|
84
|
+
id: 'string',
|
|
85
|
+
amount: 'number',
|
|
86
|
+
currency: 'string',
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
exports.handler = async (order, { logger, emit, state }) => {
|
|
91
|
+
try {
|
|
92
|
+
logger.info('Processing payment', { orderId: order.id })
|
|
93
|
+
|
|
94
|
+
// Simulate payment processing
|
|
95
|
+
const paymentResult = await processPayment(order)
|
|
96
|
+
|
|
97
|
+
await state.set('payments', order.id, paymentResult)
|
|
98
|
+
await emit({
|
|
99
|
+
topic: 'payment.processed',
|
|
100
|
+
data: { orderId: order.id, paymentId: paymentResult.id },
|
|
101
|
+
})
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logger.error('Payment failed', { orderId: order.id, error: error.message })
|
|
104
|
+
await emit({ topic: 'payment.failed', data: { orderId: order.id, error: error.message } })
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Python AI/ML Event Step
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# steps/analyze_sentiment.step.py
|
|
113
|
+
import asyncio
|
|
114
|
+
from transformers import pipeline
|
|
115
|
+
|
|
116
|
+
config = {
|
|
117
|
+
"type": "event",
|
|
118
|
+
"name": "AnalyzeSentiment",
|
|
119
|
+
"subscribes": ["review.submitted"],
|
|
120
|
+
"emits": ["sentiment.analyzed"],
|
|
121
|
+
"input": {
|
|
122
|
+
"reviewId": "string",
|
|
123
|
+
"text": "string",
|
|
124
|
+
"userId": "string"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Initialize ML model
|
|
129
|
+
sentiment_analyzer = pipeline("sentiment-analysis")
|
|
130
|
+
|
|
131
|
+
async def handler(review_data, context):
|
|
132
|
+
logger = context["logger"]
|
|
133
|
+
emit = context["emit"]
|
|
134
|
+
state = context["state"]
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
logger.info(f"Analyzing sentiment for review {review_data['reviewId']}")
|
|
138
|
+
|
|
139
|
+
# Run sentiment analysis
|
|
140
|
+
result = sentiment_analyzer(review_data["text"])
|
|
141
|
+
sentiment_score = result[0]["score"]
|
|
142
|
+
sentiment_label = result[0]["label"]
|
|
143
|
+
|
|
144
|
+
analysis = {
|
|
145
|
+
"reviewId": review_data["reviewId"],
|
|
146
|
+
"sentiment": sentiment_label,
|
|
147
|
+
"confidence": sentiment_score,
|
|
148
|
+
"analyzedAt": datetime.now().isoformat()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
await state.set("sentiment_analyses", review_data["reviewId"], analysis)
|
|
152
|
+
await emit({
|
|
153
|
+
"topic": "sentiment.analyzed",
|
|
154
|
+
"data": analysis
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
except Exception as error:
|
|
158
|
+
logger.error(f"Sentiment analysis failed: {str(error)}")
|
|
159
|
+
await emit({
|
|
160
|
+
"topic": "analysis.failed",
|
|
161
|
+
"data": {"reviewId": review_data["reviewId"], "error": str(error)}
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Python Data Processing Step
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# steps/generate_analytics.step.py
|
|
169
|
+
import pandas as pd
|
|
170
|
+
import numpy as np
|
|
171
|
+
from datetime import datetime, timedelta
|
|
172
|
+
|
|
173
|
+
config = {
|
|
174
|
+
"type": "cron",
|
|
175
|
+
"name": "GenerateAnalytics",
|
|
176
|
+
"cron": "0 0 * * 1", # Weekly on Monday
|
|
177
|
+
"emits": ["analytics.generated"]
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async def handler(context):
|
|
181
|
+
logger = context["logger"]
|
|
182
|
+
emit = context["emit"]
|
|
183
|
+
state = context["state"]
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
# Fetch all orders from state
|
|
187
|
+
orders_data = await state.get_group("orders")
|
|
188
|
+
|
|
189
|
+
if not orders_data:
|
|
190
|
+
logger.info("No orders data available")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
# Convert to pandas DataFrame for analysis
|
|
194
|
+
df = pd.DataFrame(list(orders_data.values()))
|
|
195
|
+
df['createdAt'] = pd.to_datetime(df['createdAt'])
|
|
196
|
+
|
|
197
|
+
# Generate analytics
|
|
198
|
+
last_week = datetime.now() - timedelta(days=7)
|
|
199
|
+
recent_orders = df[df['createdAt'] >= last_week]
|
|
200
|
+
|
|
201
|
+
analytics = {
|
|
202
|
+
"totalOrders": len(df),
|
|
203
|
+
"weeklyOrders": len(recent_orders),
|
|
204
|
+
"averageOrderValue": float(df['amount'].mean()) if 'amount' in df else 0,
|
|
205
|
+
"topProducts": df['productId'].value_counts().head(5).to_dict(),
|
|
206
|
+
"generatedAt": datetime.now().isoformat()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
await state.set("analytics", "weekly", analytics)
|
|
210
|
+
await emit({"topic": "analytics.generated", "data": analytics})
|
|
211
|
+
|
|
212
|
+
logger.info("Analytics generated successfully")
|
|
213
|
+
|
|
214
|
+
except Exception as error:
|
|
215
|
+
logger.error(f"Analytics generation failed: {str(error)}")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Ruby Report Generation Step
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
# steps/generate_report.step.rb
|
|
222
|
+
require 'csv'
|
|
223
|
+
require 'json'
|
|
224
|
+
|
|
225
|
+
def config
|
|
226
|
+
{
|
|
227
|
+
type: 'event',
|
|
228
|
+
name: 'GenerateReport',
|
|
229
|
+
subscribes: ['analytics.generated'],
|
|
230
|
+
emits: ['report.generated'],
|
|
231
|
+
input: {
|
|
232
|
+
totalOrders: 'number',
|
|
233
|
+
weeklyOrders: 'number',
|
|
234
|
+
topProducts: 'object'
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def handler(analytics_data, context)
|
|
240
|
+
logger = context[:logger]
|
|
241
|
+
emit = context[:emit]
|
|
242
|
+
state = context[:state]
|
|
243
|
+
|
|
244
|
+
begin
|
|
245
|
+
logger.info("Generating CSV report from analytics")
|
|
246
|
+
|
|
247
|
+
# Generate CSV report
|
|
248
|
+
csv_data = CSV.generate do |csv|
|
|
249
|
+
csv << ['Metric', 'Value']
|
|
250
|
+
csv << ['Total Orders', analytics_data['totalOrders']]
|
|
251
|
+
csv << ['Weekly Orders', analytics_data['weeklyOrders']]
|
|
252
|
+
csv << ['Average Order Value', analytics_data['averageOrderValue']]
|
|
253
|
+
|
|
254
|
+
# Add top products
|
|
255
|
+
analytics_data['topProducts'].each do |product, count|
|
|
256
|
+
csv << ["Product #{product}", count]
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Generate HTML report
|
|
261
|
+
html_report = generate_html_template(analytics_data)
|
|
262
|
+
|
|
263
|
+
report = {
|
|
264
|
+
id: SecureRandom.uuid,
|
|
265
|
+
csv_data: csv_data,
|
|
266
|
+
html_report: html_report,
|
|
267
|
+
generated_at: Time.now.iso8601
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
state.set('reports', report[:id], report)
|
|
271
|
+
emit.call(topic: 'report.generated', data: report)
|
|
272
|
+
|
|
273
|
+
logger.info("Report generated successfully", report_id: report[:id])
|
|
274
|
+
|
|
275
|
+
rescue => error
|
|
276
|
+
logger.error("Report generation failed", error: error.message)
|
|
277
|
+
emit.call(topic: 'report.failed', data: { error: error.message })
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
private
|
|
282
|
+
|
|
283
|
+
def generate_html_template(data)
|
|
284
|
+
<<~HTML
|
|
285
|
+
<html>
|
|
286
|
+
<head><title>Analytics Report</title></head>
|
|
287
|
+
<body>
|
|
288
|
+
<h1>Weekly Analytics Report</h1>
|
|
289
|
+
<p>Total Orders: #{data['totalOrders']}</p>
|
|
290
|
+
<p>Weekly Orders: #{data['weeklyOrders']}</p>
|
|
291
|
+
<p>Generated: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}</p>
|
|
292
|
+
</body>
|
|
293
|
+
</html>
|
|
294
|
+
HTML
|
|
295
|
+
end
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Development Commands
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
# Start development server with workbench UI
|
|
302
|
+
npm run dev
|
|
303
|
+
|
|
304
|
+
# Run specific step
|
|
305
|
+
motia run step-name
|
|
306
|
+
|
|
307
|
+
# Build for production
|
|
308
|
+
motia build
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Code Standards
|
|
312
|
+
|
|
313
|
+
### Multi-Language File Naming
|
|
314
|
+
|
|
315
|
+
- **TypeScript**: `step-name.step.ts`
|
|
316
|
+
- **JavaScript**: `step-name.step.js`
|
|
317
|
+
- **Python**: `step_name.step.py`
|
|
318
|
+
- **Ruby**: `step-name.step.rb`
|
|
319
|
+
|
|
320
|
+
### Language-Specific Patterns
|
|
321
|
+
|
|
322
|
+
#### TypeScript/JavaScript
|
|
323
|
+
|
|
324
|
+
- Use Zod schemas for validation (TypeScript) or simple objects (JavaScript)
|
|
325
|
+
- Leverage auto-generated types from `types.d.ts` (TypeScript only)
|
|
326
|
+
- Use `async/await` for all async operations
|
|
327
|
+
- Export `config` and `handler` using ES6 modules (TypeScript) or CommonJS (JavaScript)
|
|
328
|
+
|
|
329
|
+
#### Python
|
|
330
|
+
|
|
331
|
+
- Use dictionary for config with snake_case keys
|
|
332
|
+
- Use `async def handler()` for async operations
|
|
333
|
+
- Access context via dictionary: `context["logger"]`, `context["emit"]`
|
|
334
|
+
- Use type hints where helpful for clarity
|
|
335
|
+
- Follow PEP 8 naming conventions
|
|
336
|
+
|
|
337
|
+
#### Ruby
|
|
338
|
+
|
|
339
|
+
- Use method `config` returning a hash
|
|
340
|
+
- Use method `handler(input, context)`
|
|
341
|
+
- Access context via hash: `context[:logger]`, `context[:emit]`
|
|
342
|
+
- Follow Ruby naming conventions (snake_case)
|
|
343
|
+
- Use proper exception handling with `rescue`
|
|
344
|
+
|
|
345
|
+
### Universal Naming Conventions
|
|
346
|
+
|
|
347
|
+
- Step names: `CamelCase` across all languages
|
|
348
|
+
- Topics: `category.action` (e.g., `order.created`, `user.updated`)
|
|
349
|
+
- State groups: `plural-noun` (e.g., `orders`, `users`)
|
|
350
|
+
- File names follow language conventions but descriptive
|
|
351
|
+
|
|
352
|
+
### Error Handling by Language
|
|
353
|
+
|
|
354
|
+
#### TypeScript/JavaScript
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
try {
|
|
358
|
+
// Step logic
|
|
359
|
+
} catch (error) {
|
|
360
|
+
logger.error('Operation failed', { error: error.message, traceId })
|
|
361
|
+
// For API steps: return error response
|
|
362
|
+
// For Event steps: emit error event or rethrow
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### Python
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
try:
|
|
370
|
+
# Step logic
|
|
371
|
+
pass
|
|
372
|
+
except Exception as error:
|
|
373
|
+
logger.error(f"Operation failed: {str(error)}")
|
|
374
|
+
# Emit error event or re-raise
|
|
375
|
+
await emit({"topic": "error.occurred", "data": {"error": str(error)}})
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### Ruby
|
|
379
|
+
|
|
380
|
+
```ruby
|
|
381
|
+
begin
|
|
382
|
+
# Step logic
|
|
383
|
+
rescue => error
|
|
384
|
+
logger.error("Operation failed", error: error.message)
|
|
385
|
+
# Emit error event or re-raise
|
|
386
|
+
emit.call(topic: 'error.occurred', data: { error: error.message })
|
|
387
|
+
end
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### State Management Best Practices
|
|
391
|
+
|
|
392
|
+
- Use semantic group names for state organization
|
|
393
|
+
- Store complex objects, not just primitives
|
|
394
|
+
- Consider trace-scoped vs global state based on use case
|
|
395
|
+
- Clean up unused state in cron jobs
|
|
396
|
+
|
|
397
|
+
## Testing & Debugging
|
|
398
|
+
|
|
399
|
+
### Workbench Features
|
|
400
|
+
|
|
401
|
+
- Visual flow representation and editing
|
|
402
|
+
- API endpoint testing with real data
|
|
403
|
+
- Real-time logging and tracing
|
|
404
|
+
- State inspection and management
|
|
405
|
+
- Tutorial system integration
|
|
406
|
+
|
|
407
|
+
### Logging
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
logger.info('Processing started', { userId, traceId })
|
|
411
|
+
logger.error('Validation failed', { errors, input, traceId })
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Project-Specific Context
|
|
415
|
+
|
|
416
|
+
This project implements a **pet store ordering system** with:
|
|
417
|
+
|
|
418
|
+
- API endpoint for pet creation and food orders
|
|
419
|
+
- Event processing for order fulfillment
|
|
420
|
+
- Notification system for order updates
|
|
421
|
+
- Scheduled auditing for overdue orders
|
|
422
|
+
|
|
423
|
+
When working on this codebase:
|
|
424
|
+
|
|
425
|
+
- Follow the existing event flow patterns
|
|
426
|
+
- Use the established state groups (`orders`, `pets`)
|
|
427
|
+
- Maintain the tutorial-friendly structure for the workbench
|
|
428
|
+
- Test changes using the development workbench UI
|
|
429
|
+
|
|
430
|
+
## Multi-Language Workflow Examples
|
|
431
|
+
|
|
432
|
+
### Complete E-commerce Flow
|
|
433
|
+
|
|
434
|
+
```
|
|
435
|
+
TypeScript API → JavaScript Payment → Python ML → Ruby Reports
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
1. **API Endpoint** (TypeScript): Handle orders with validation
|
|
439
|
+
2. **Payment Processing** (JavaScript): Fast payment logic
|
|
440
|
+
3. **Recommendation Engine** (Python): ML-based product recommendations
|
|
441
|
+
4. **Report Generation** (Ruby): Beautiful HTML/PDF reports
|
|
442
|
+
|
|
443
|
+
### AI-Powered Content Pipeline
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
JavaScript API → Python AI → Ruby Templates → TypeScript Notifications
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
1. **Content Upload** (JavaScript): Quick file handling
|
|
450
|
+
2. **AI Processing** (Python): Image recognition, text analysis
|
|
451
|
+
3. **Template Generation** (Ruby): Dynamic content templates
|
|
452
|
+
4. **Real-time Updates** (TypeScript): WebSocket notifications
|
|
453
|
+
|
|
454
|
+
## Common Tasks
|
|
455
|
+
|
|
456
|
+
- **Adding API endpoints**: Create API step in TypeScript/JavaScript with validation
|
|
457
|
+
- **Processing background jobs**: Create event steps in the most suitable language
|
|
458
|
+
- **AI/ML tasks**: Use Python steps with appropriate libraries
|
|
459
|
+
- **Data processing**: Use Python for analytics, Ruby for reports
|
|
460
|
+
- **Scheduled maintenance**: Use cron steps for periodic cleanup and auditing
|
|
461
|
+
- **Real-time features**: Use TypeScript/JavaScript for WebSocket handling
|
|
462
|
+
|
|
463
|
+
## Resources
|
|
464
|
+
|
|
465
|
+
- Local development: `npm run dev` → http://localhost:5173
|
|
466
|
+
- Motia documentation: https://motia.dev/docs
|
|
467
|
+
- Workbench tutorials: Available in development mode
|