motia 0.5.12-beta.120 → 0.6.0-beta.120

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.
Files changed (68) hide show
  1. package/dist/cjs/cli.js +21 -0
  2. package/dist/cjs/create/index.d.ts +1 -0
  3. package/dist/cjs/create/index.js +21 -27
  4. package/dist/cjs/create/setup-template.d.ts +2 -0
  5. package/dist/cjs/create/setup-template.js +13 -0
  6. package/dist/cjs/create/setup-tutorial-flow.d.ts +6 -0
  7. package/dist/cjs/create/setup-tutorial-flow.js +30 -0
  8. package/dist/cjs/create/templates/basic-tutorial/01-api.step.ts.txt +58 -0
  9. package/dist/cjs/create/templates/basic-tutorial/02-process-food-order.step.ts.txt +30 -0
  10. package/dist/cjs/create/templates/basic-tutorial/03-state-audit-cron.step.ts.txt +42 -0
  11. package/dist/cjs/create/templates/basic-tutorial/04_new_order_notifications.step.py.txt +27 -0
  12. package/dist/cjs/create/templates/basic-tutorial/motia-workbench.json +28 -0
  13. package/dist/cjs/create/templates/basic-tutorial/services/pet-store.ts.txt +32 -0
  14. package/dist/cjs/create/templates/generate.d.ts +1 -1
  15. package/dist/cjs/create/templates/generate.js +25 -5
  16. package/dist/cjs/create/templates/generate.ts +33 -6
  17. package/dist/cjs/create/templates/index.js +1 -0
  18. package/dist/cjs/create/templates/index.ts +1 -0
  19. package/dist/cjs/create/utils.d.ts +2 -0
  20. package/dist/cjs/create/utils.js +21 -0
  21. package/dist/cjs/docker/utils/print-intro.js +0 -1
  22. package/dist/esm/cli.js +18 -0
  23. package/dist/esm/create/index.d.ts +1 -0
  24. package/dist/esm/create/index.js +11 -17
  25. package/dist/esm/create/setup-template.d.ts +2 -0
  26. package/dist/esm/create/setup-template.js +9 -0
  27. package/dist/esm/create/setup-tutorial-flow.d.ts +6 -0
  28. package/dist/esm/create/setup-tutorial-flow.js +23 -0
  29. package/dist/esm/create/templates/basic-tutorial/01-api.step.ts.txt +58 -0
  30. package/dist/esm/create/templates/basic-tutorial/02-process-food-order.step.ts.txt +30 -0
  31. package/dist/esm/create/templates/basic-tutorial/03-state-audit-cron.step.ts.txt +42 -0
  32. package/dist/esm/create/templates/basic-tutorial/04_new_order_notifications.step.py.txt +27 -0
  33. package/dist/esm/create/templates/basic-tutorial/motia-workbench.json +28 -0
  34. package/dist/esm/create/templates/basic-tutorial/services/pet-store.ts.txt +32 -0
  35. package/dist/esm/create/templates/generate.d.ts +1 -1
  36. package/dist/esm/create/templates/generate.js +26 -6
  37. package/dist/esm/create/templates/generate.ts +33 -6
  38. package/dist/esm/create/templates/index.js +1 -0
  39. package/dist/esm/create/templates/index.ts +1 -0
  40. package/dist/esm/create/utils.d.ts +2 -0
  41. package/dist/esm/create/utils.js +13 -0
  42. package/dist/esm/docker/utils/print-intro.js +0 -1
  43. package/dist/types/create/index.d.ts +1 -0
  44. package/dist/types/create/setup-template.d.ts +2 -0
  45. package/dist/types/create/setup-tutorial-flow.d.ts +6 -0
  46. package/dist/types/create/templates/generate.d.ts +1 -1
  47. package/dist/types/create/utils.d.ts +2 -0
  48. package/package.json +4 -4
  49. /package/dist/cjs/create/templates/default/{steps/00-noop.step.ts.txt → 00-noop.step.ts.txt} +0 -0
  50. /package/dist/cjs/create/templates/default/{steps/00-noop.step.tsx.txt → 00-noop.step.tsx.txt} +0 -0
  51. /package/dist/cjs/create/templates/default/{steps/01-api.step.ts.txt → 01-api.step.ts.txt} +0 -0
  52. /package/dist/cjs/create/templates/default/{steps/02-test-state.step.ts.txt → 02-test-state.step.ts.txt} +0 -0
  53. /package/dist/cjs/create/templates/default/{steps/03-check-state-change.step.ts.txt → 03-check-state-change.step.ts.txt} +0 -0
  54. /package/dist/cjs/create/templates/python/{steps/00_noop_step.py.txt → 00_noop_step.py.txt} +0 -0
  55. /package/dist/cjs/create/templates/python/{steps/00_noop_step.tsx.txt → 00_noop_step.tsx.txt} +0 -0
  56. /package/dist/cjs/create/templates/python/{steps/01_api_step.py.txt → 01_api_step.py.txt} +0 -0
  57. /package/dist/cjs/create/templates/python/{steps/02_test_state_step.py.txt → 02_test_state_step.py.txt} +0 -0
  58. /package/dist/cjs/create/templates/python/{steps/03_check_state_change_step.py.txt → 03_check_state_change_step.py.txt} +0 -0
  59. /package/dist/esm/create/templates/default/{steps/00-noop.step.ts.txt → 00-noop.step.ts.txt} +0 -0
  60. /package/dist/esm/create/templates/default/{steps/00-noop.step.tsx.txt → 00-noop.step.tsx.txt} +0 -0
  61. /package/dist/esm/create/templates/default/{steps/01-api.step.ts.txt → 01-api.step.ts.txt} +0 -0
  62. /package/dist/esm/create/templates/default/{steps/02-test-state.step.ts.txt → 02-test-state.step.ts.txt} +0 -0
  63. /package/dist/esm/create/templates/default/{steps/03-check-state-change.step.ts.txt → 03-check-state-change.step.ts.txt} +0 -0
  64. /package/dist/esm/create/templates/python/{steps/00_noop_step.py.txt → 00_noop_step.py.txt} +0 -0
  65. /package/dist/esm/create/templates/python/{steps/00_noop_step.tsx.txt → 00_noop_step.tsx.txt} +0 -0
  66. /package/dist/esm/create/templates/python/{steps/01_api_step.py.txt → 01_api_step.py.txt} +0 -0
  67. /package/dist/esm/create/templates/python/{steps/02_test_state_step.py.txt → 02_test_state_step.py.txt} +0 -0
  68. /package/dist/esm/create/templates/python/{steps/03_check_state_change_step.py.txt → 03_check_state_change_step.py.txt} +0 -0
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')
@@ -4,6 +4,7 @@ type Args = {
4
4
  template?: string;
5
5
  cursorEnabled?: boolean;
6
6
  context: CliContext;
7
+ skipTutorialTemplates?: boolean;
7
8
  };
8
9
  export declare const create: ({ projectName, template, cursorEnabled, context }: Args) => Promise<void>;
9
10
  export {};
@@ -6,35 +6,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.create = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
- const templates_1 = require("./templates");
10
9
  const execute_command_1 = require("../utils/execute-command");
11
10
  const install_1 = require("../install");
12
11
  const generate_types_1 = require("../generate-types");
13
12
  const version_1 = require("../version");
13
+ const setup_template_1 = require("./setup-template");
14
+ const utils_1 = require("./utils");
14
15
  // eslint-disable-next-line @typescript-eslint/no-require-imports
15
16
  require('ts-node').register({
16
17
  transpileOnly: true,
17
18
  compilerOptions: { module: 'commonjs' },
18
19
  });
19
- const checkIfFileExists = (dir, fileName) => {
20
- return fs_1.default.existsSync(path_1.default.join(dir, fileName));
21
- };
22
- const checkIfDirectoryExists = (dir) => {
23
- try {
24
- return fs_1.default.statSync(dir).isDirectory();
25
- }
26
- catch {
27
- return false;
28
- }
29
- };
30
20
  const getPackageManager = (dir) => {
31
- if (checkIfFileExists(dir, 'yarn.lock')) {
21
+ if ((0, utils_1.checkIfFileExists)(dir, 'yarn.lock')) {
32
22
  return 'yarn';
33
23
  }
34
- else if (checkIfFileExists(dir, 'pnpm-lock.yaml')) {
24
+ else if ((0, utils_1.checkIfFileExists)(dir, 'pnpm-lock.yaml')) {
35
25
  return 'pnpm';
36
26
  }
37
- else if (checkIfFileExists(dir, 'package-lock.json')) {
27
+ else if ((0, utils_1.checkIfFileExists)(dir, 'package-lock.json')) {
38
28
  return 'npm';
39
29
  }
40
30
  else {
@@ -97,14 +87,14 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
97
87
  '\n\n');
98
88
  const isCurrentDir = projectName === '.' || projectName === './' || projectName === '.\\';
99
89
  const rootDir = isCurrentDir ? process.cwd() : path_1.default.join(process.cwd(), projectName);
100
- if (!isCurrentDir && !checkIfDirectoryExists(rootDir)) {
90
+ if (!isCurrentDir && !(0, utils_1.checkIfDirectoryExists)(rootDir)) {
101
91
  fs_1.default.mkdirSync(path_1.default.join(rootDir));
102
92
  context.log('directory-created', (message) => message.tag('success').append('Directory created ').append(projectName, 'gray'));
103
93
  }
104
94
  else {
105
95
  context.log('directory-using', (message) => message.tag('info').append('Using current directory'));
106
96
  }
107
- if (!checkIfFileExists(rootDir, 'package.json')) {
97
+ if (!(0, utils_1.checkIfFileExists)(rootDir, 'package.json')) {
108
98
  const packageJsonContent = {
109
99
  name: projectName,
110
100
  description: '',
@@ -144,7 +134,7 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
144
134
  .append('command to')
145
135
  .append('package.json', 'gray'));
146
136
  }
147
- if (!checkIfFileExists(rootDir, 'tsconfig.json')) {
137
+ if (!(0, utils_1.checkIfFileExists)(rootDir, 'tsconfig.json')) {
148
138
  const tsconfigContent = {
149
139
  compilerOptions: {
150
140
  target: 'ES2020',
@@ -167,7 +157,7 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
167
157
  fs_1.default.writeFileSync(path_1.default.join(rootDir, 'tsconfig.json'), JSON.stringify(tsconfigContent, null, 2));
168
158
  context.log('tsconfig-json-created', (message) => message.tag('success').append('File').append('tsconfig.json', 'cyan').append('has been created.'));
169
159
  }
170
- if (!checkIfFileExists(rootDir, '.gitignore')) {
160
+ if (!(0, utils_1.checkIfFileExists)(rootDir, '.gitignore')) {
171
161
  const gitignoreContent = [
172
162
  'node_modules',
173
163
  'python_modules',
@@ -183,24 +173,28 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
183
173
  }
184
174
  const cursorTemplateDir = path_1.default.join(__dirname, '../../dot-files/.cursor');
185
175
  const cursorTargetDir = path_1.default.join(rootDir, '.cursor');
186
- if (cursorEnabled && !checkIfDirectoryExists(cursorTargetDir)) {
176
+ if (cursorEnabled && !(0, utils_1.checkIfDirectoryExists)(cursorTargetDir)) {
187
177
  fs_1.default.cpSync(cursorTemplateDir, cursorTargetDir, { recursive: true });
188
178
  context.log('cursor-folder-created', (message) => message.tag('success').append('Folder').append('.cursor', 'cyan').append('has been created.'));
189
179
  }
190
180
  const stepsDir = path_1.default.join(rootDir, 'steps');
191
- if (!checkIfDirectoryExists(stepsDir)) {
181
+ if (!(0, utils_1.checkIfDirectoryExists)(stepsDir)) {
192
182
  fs_1.default.mkdirSync(stepsDir);
193
183
  context.log('steps-directory-created', (message) => message.tag('success').append('Folder').append('steps', 'cyan').append('has been created.'));
194
184
  }
195
- if (!template || !(template in templates_1.templates)) {
196
- context.log('template-not-found', (message) => message.tag('failed').append(`Template ${template} not found, please use one of the following:`));
197
- context.log('available-templates', (message) => message.tag('info').append(`Available templates: \n\n ${Object.keys(templates_1.templates).join('\n')}`));
198
- return;
185
+ if (!(0, utils_1.checkIfDirectoryExists)(path_1.default.join(stepsDir, 'basic-tutorial'))) {
186
+ fs_1.default.mkdirSync(path_1.default.join(stepsDir, 'basic-tutorial'));
187
+ }
188
+ await (0, setup_template_1.setupTemplate)('basic-tutorial', stepsDir, context);
189
+ if (template) {
190
+ if (!(0, utils_1.checkIfDirectoryExists)(path_1.default.join(stepsDir, template))) {
191
+ fs_1.default.mkdirSync(path_1.default.join(stepsDir, template));
192
+ }
193
+ await (0, setup_template_1.setupTemplate)(template, stepsDir, context);
199
194
  }
200
- await templates_1.templates[template](rootDir, context);
201
195
  const packageManager = await installNodeDependencies(rootDir, context);
202
196
  if (template === 'python') {
203
- if (!checkIfFileExists(rootDir, 'requirements.txt')) {
197
+ if (!(0, utils_1.checkIfFileExists)(rootDir, 'requirements.txt')) {
204
198
  const requirementsContent = [
205
199
  // TODO: motia PyPi package
206
200
  // Add other Python dependencies as needed
@@ -0,0 +1,2 @@
1
+ import { CliContext } from '@/cloud/config-utils';
2
+ export declare const setupTemplate: (template: string, rootDir: string, context: CliContext) => Promise<void>;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupTemplate = void 0;
4
+ const templates_1 = require("./templates");
5
+ const setupTemplate = async (template, rootDir, context) => {
6
+ if (!template || !(template in templates_1.templates)) {
7
+ context.log('template-not-found', (message) => message.tag('failed').append(`Template ${template} not found, please use one of the following:`));
8
+ context.log('available-templates', (message) => message.tag('info').append(`Available templates: \n\n ${Object.keys(templates_1.templates).join('\n')}`));
9
+ return;
10
+ }
11
+ await templates_1.templates[template](rootDir, context);
12
+ };
13
+ exports.setupTemplate = setupTemplate;
@@ -0,0 +1,6 @@
1
+ import { CliContext } from '@/cloud/config-utils';
2
+ type Args = {
3
+ context: CliContext;
4
+ };
5
+ export declare const createTutorialFlow: ({ context }: Args) => Promise<void>;
6
+ export {};
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createTutorialFlow = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const utils_1 = require("./utils");
10
+ const setup_template_1 = require("./setup-template");
11
+ const createTutorialFlow = async ({ context }) => {
12
+ const rootDir = process.cwd();
13
+ if (!(0, utils_1.checkIfFileExists)(rootDir, 'motia-workbench.json')) {
14
+ context.log('invalid-project', (message) => message
15
+ .tag('failed')
16
+ .append('In order to setup the Motia tutorial you need to be in a valid Motia project, motia-workbench.json not found, if this is not a Motia project you can create one using the motia create cli command.'));
17
+ return;
18
+ }
19
+ const stepsDir = path_1.default.join(rootDir, 'steps');
20
+ if (!(0, utils_1.checkIfDirectoryExists)(stepsDir)) {
21
+ fs_1.default.mkdirSync(stepsDir);
22
+ context.log('steps-directory-created', (message) => message.tag('success').append('Folder').append('steps', 'cyan').append('has been created.'));
23
+ }
24
+ if (!(0, utils_1.checkIfDirectoryExists)(path_1.default.join(stepsDir, 'basic-tutorial'))) {
25
+ fs_1.default.mkdirSync(path_1.default.join(stepsDir, 'basic-tutorial'));
26
+ }
27
+ await (0, setup_template_1.setupTemplate)('basic-tutorial', stepsDir, context);
28
+ context.log('tutorial-flow-setup-completed', (message) => message.tag('success').append('Tutorial flow setup completed, you can now start the tutorial from Workbench.'));
29
+ };
30
+ exports.createTutorialFlow = createTutorialFlow;
@@ -0,0 +1,58 @@
1
+ import { ApiRouteConfig, Handlers } from 'motia'
2
+ import { z } from 'zod'
3
+ import { petStoreService } from './services/pet-store'
4
+
5
+ export const config: ApiRouteConfig = {
6
+ type: 'api',
7
+ name: 'ApiTrigger',
8
+ description: 'basic-tutorial api trigger',
9
+ flows: ['basic-tutorial'],
10
+
11
+ method: 'POST',
12
+ path: '/basic-tutorial',
13
+ bodySchema: z.object({
14
+ pet: z.object({
15
+ name: z.string(),
16
+ photoUrl: z.string(),
17
+ }),
18
+ foodOrder: z
19
+ .object({
20
+ id: z.string(),
21
+ quantity: z.number(),
22
+ })
23
+ .optional(),
24
+ }),
25
+ responseSchema: {
26
+ 200: z.object({
27
+ message: z.string(),
28
+ traceId: z.string(),
29
+ }),
30
+ },
31
+ emits: ['process-food-order'],
32
+ }
33
+
34
+ export const handler: Handlers['ApiTrigger'] = async (req, { logger, emit, traceId }) => {
35
+ logger.info('Step 01 – Processing API Step', { body: req.body })
36
+
37
+ const { pet, foodOrder } = req.body
38
+
39
+ const newPetRecord = await petStoreService.createPet(pet)
40
+
41
+ if (foodOrder) {
42
+ await emit({
43
+ topic: 'process-food-order',
44
+ data: {
45
+ ...foodOrder,
46
+ petId: newPetRecord.id,
47
+ },
48
+ })
49
+ }
50
+
51
+ return {
52
+ status: 200,
53
+ body: {
54
+ traceId,
55
+ message: 'Your pet has been registered and your order is being processed',
56
+ },
57
+ }
58
+ }
@@ -0,0 +1,30 @@
1
+ import { EventConfig, Handlers } from 'motia'
2
+ import { z } from 'zod'
3
+ import { petStoreService } from './services/pet-store'
4
+
5
+ export const config: EventConfig = {
6
+ type: 'event',
7
+ name: 'ProcessFoodOrder',
8
+ description: 'basic-tutorial event step, demonstrates how to consume an event from a topic and persist data in state',
9
+ flows: ['basic-tutorial'],
10
+ subscribes: ['process-food-order'],
11
+ emits: ['new-order-notification'],
12
+ input: z.object({
13
+ id: z.string(),
14
+ quantity: z.number(),
15
+ petId: z.number(),
16
+ }),
17
+ }
18
+
19
+ export const handler: Handlers['ProcessFoodOrder'] = async (input, { traceId, logger, state, emit }) => {
20
+ logger.info('Step 02 – Process food order', { input, traceId })
21
+
22
+ const order = await petStoreService.createOrder(input)
23
+
24
+ await state.set<string>('orders', order.id, order)
25
+
26
+ await emit({
27
+ topic: 'new-order-notification',
28
+ data: { order_id: order.id },
29
+ })
30
+ }
@@ -0,0 +1,42 @@
1
+ import { CronConfig, Handlers } from 'motia'
2
+
3
+ export const config: CronConfig = {
4
+ type: 'cron' as const,
5
+ name: 'StateAuditJob',
6
+ description: 'Runs every minute and emits a timestamp',
7
+ cron: '*/5 * * * *', // run every hour at minute 0
8
+ emits: ['order-audit-error', 'order-audit-warning'],
9
+ flows: ['basic-tutorial'],
10
+ }
11
+
12
+ export const handler: Handlers['StateAuditJob'] = async ({ state, emit }) => {
13
+ const stateValue = await state.getGroup<{
14
+ id: number
15
+ petId: number
16
+ quantity: number
17
+ shipDate: string
18
+ status: string
19
+ complete: boolean
20
+ }>('orders')
21
+
22
+ if (!Array.isArray(stateValue)) {
23
+ await emit({
24
+ topic: 'state-audit-error',
25
+ data: { message: 'State value is not an array' },
26
+ })
27
+
28
+ return
29
+ }
30
+
31
+ for (const item of stateValue) {
32
+ // check if current date is after item.shipDate
33
+ const currentDate = new Date()
34
+ const shipDate = new Date(item.shipDate)
35
+ if (!item.complete && currentDate > shipDate) {
36
+ await emit({
37
+ topic: 'order-audit-warning',
38
+ data: { message: 'Order is not complete and ship date is past' },
39
+ })
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,27 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ class NewOrderNotificationInput(BaseModel):
4
+ order_id: str = Field(description="pet store order id")
5
+
6
+ config = {
7
+ "type": "event",
8
+ "name": "NewOrderNotifications",
9
+ "description": "Checks a state change using python",
10
+ "subscribes": ["new-order-notification"],
11
+ "emits": [],
12
+ "flows": ["basic-tutorial"],
13
+ "input": NewOrderNotificationInput.model_json_schema(),
14
+ }
15
+
16
+ async def handler(input, ctx):
17
+ ctx.logger.info('Processing NewOrderNotifications', input)
18
+ ctx.logger.info('[NewOrderNotifications] order id', input.get('order_id'))
19
+
20
+ order = await ctx.state.get('orders', input.get('order_id'))
21
+
22
+ # This represents a call to some sort of notification service ot indicate that a new order has been placed
23
+ ctx.logger.info('New order notification sent using Python: ', {
24
+ 'order_id': input.get('order_id'),
25
+ 'order': order,
26
+ 'trace_id': ctx.trace_id
27
+ })
@@ -0,0 +1,28 @@
1
+ [
2
+ {
3
+ "id": "basic-tutorial",
4
+ "config": {
5
+ "steps/basic-tutorial/04_new_order_notifications.step.py": {
6
+ "x": 668,
7
+ "y": 266,
8
+ "targetHandlePosition": "left"
9
+ },
10
+ "steps/basic-tutorial/03-state-audit-cron.step.ts": {
11
+ "x": 224,
12
+ "y": 520
13
+ },
14
+ "steps/basic-tutorial/02-process-food-order.step.ts": {
15
+ "x": 220,
16
+ "y": 242,
17
+ "sourceHandlePosition": "right",
18
+ "targetHandlePosition": "left"
19
+ },
20
+ "steps/basic-tutorial/01-api.step.ts": {
21
+ "x": -243,
22
+ "y": 199,
23
+ "sourceHandlePosition": "right",
24
+ "targetHandlePosition": "left"
25
+ }
26
+ }
27
+ }
28
+ ]
@@ -0,0 +1,32 @@
1
+ export const petStoreService = {
2
+ createPet: async (pet: { name: string; photoUrl: string }) => {
3
+ const response = await fetch('https://petstore.swagger.io/v2/pet', {
4
+ method: 'POST',
5
+ body: JSON.stringify({
6
+ name: pet.name,
7
+ photoUrls: [pet.photoUrl],
8
+ status: 'available',
9
+ }),
10
+ headers: {
11
+ 'Content-Type': 'application/json',
12
+ },
13
+ })
14
+ return response.json()
15
+ },
16
+ createOrder: async (order: { id: string; quantity: number; petId: number }) => {
17
+ const response = await fetch('https://petstore.swagger.io/v2/store/order', {
18
+ method: 'POST',
19
+ body: JSON.stringify({
20
+ id: order.id,
21
+ quantity: order.quantity,
22
+ petId: order.petId,
23
+ shipDate: new Date().toISOString(),
24
+ status: 'placed',
25
+ }),
26
+ headers: {
27
+ 'Content-Type': 'application/json',
28
+ },
29
+ })
30
+ return response.json()
31
+ },
32
+ }
@@ -1,3 +1,3 @@
1
1
  import { CliContext } from '../../cloud/config-utils';
2
2
  export type Generator = (rootDir: string, context: CliContext) => Promise<void>;
3
- export declare const generateTemplateSteps: (templateDir: string) => Generator;
3
+ export declare const generateTemplateSteps: (templateFolder: string) => Generator;
@@ -37,20 +37,40 @@ exports.generateTemplateSteps = void 0;
37
37
  const fs_1 = require("fs");
38
38
  const path = __importStar(require("path"));
39
39
  const glob_1 = require("glob");
40
- const generateTemplateSteps = (templateDir) => {
40
+ const generateTemplateSteps = (templateFolder) => {
41
41
  return async (rootDir, context) => {
42
- const templatePath = path.join(__dirname, templateDir);
42
+ const templatePath = path.join(__dirname, templateFolder);
43
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);
47
- if ((0, fs_1.statSync)(filePath).isDirectory()) {
47
+ if ((0, fs_1.statSync)(filePath).isDirectory() && !filePath.match(/services|utils|lib/)) {
48
48
  // ignore folders
49
49
  continue;
50
50
  }
51
+ if ((0, fs_1.statSync)(filePath).isDirectory()) {
52
+ const folderPath = path.basename(filePath);
53
+ (0, fs_1.mkdirSync)(path.join(rootDir, templateFolder, folderPath));
54
+ continue;
55
+ }
51
56
  const sanitizedFileName = fileName.replace('.txt', '');
52
- const generateFilePath = path.join(rootDir, sanitizedFileName);
53
- const content = await fs_1.promises.readFile(filePath, 'utf8');
57
+ const isWorkbenchConfig = fileName.match('motia-workbench.json');
58
+ const generateFilePath = path.join(...(isWorkbenchConfig
59
+ ? [rootDir.match(/steps/) ? path.join(rootDir, '..') : rootDir, sanitizedFileName]
60
+ : [rootDir, templateFolder, sanitizedFileName]));
61
+ let content = await fs_1.promises.readFile(filePath, 'utf8');
62
+ // Make sure statSync doesn't break the execution if the file doesn't exist
63
+ try {
64
+ if (isWorkbenchConfig && (0, fs_1.statSync)(generateFilePath).isFile()) {
65
+ const existingWorkbenchConfig = await fs_1.promises.readFile(generateFilePath, 'utf8');
66
+ const workbenchContent = JSON.parse(content);
67
+ content = JSON.stringify([...JSON.parse(existingWorkbenchConfig), ...workbenchContent], null, 2);
68
+ context.log('workbench-config-updated', (message) => message.tag('success').append('Workbench config').append('has been updated.'));
69
+ }
70
+ }
71
+ catch {
72
+ void 0;
73
+ }
54
74
  await fs_1.promises.writeFile(generateFilePath, content, 'utf8');
55
75
  context.log(sanitizedFileName, (message) => {
56
76
  message.tag('success').append('File').append(sanitizedFileName, 'cyan').append('has been created.');
@@ -1,27 +1,54 @@
1
- import { promises as fs, statSync } from 'fs'
1
+ import { promises as fs, statSync, mkdirSync } from 'fs'
2
2
  import * as path from 'path'
3
3
  import { globSync } from 'glob'
4
4
  import { CliContext } from '../../cloud/config-utils'
5
5
 
6
6
  export type Generator = (rootDir: string, context: CliContext) => Promise<void>
7
7
 
8
- export const generateTemplateSteps = (templateDir: string): Generator => {
8
+ export const generateTemplateSteps = (templateFolder: string): Generator => {
9
9
  return async (rootDir: string, context: CliContext): Promise<void> => {
10
- const templatePath = path.join(__dirname, templateDir)
10
+ const templatePath = path.join(__dirname, templateFolder)
11
11
  const files = globSync('**/*', { absolute: false, cwd: templatePath })
12
12
 
13
13
  try {
14
14
  for (const fileName of files) {
15
15
  const filePath = path.join(templatePath, fileName)
16
16
 
17
- if (statSync(filePath).isDirectory()) {
17
+ if (statSync(filePath).isDirectory() && !filePath.match(/services|utils|lib/)) {
18
18
  // ignore folders
19
19
  continue
20
20
  }
21
21
 
22
+ if (statSync(filePath).isDirectory()) {
23
+ const folderPath = path.basename(filePath)
24
+ mkdirSync(path.join(rootDir, templateFolder, folderPath))
25
+ continue
26
+ }
27
+
22
28
  const sanitizedFileName = fileName.replace('.txt', '')
23
- const generateFilePath = path.join(rootDir, sanitizedFileName)
24
- const content = await fs.readFile(filePath, 'utf8')
29
+ const isWorkbenchConfig = fileName.match('motia-workbench.json')
30
+ const generateFilePath = path.join(
31
+ ...(isWorkbenchConfig
32
+ ? [rootDir.match(/steps/) ? path.join(rootDir, '..') : rootDir, sanitizedFileName]
33
+ : [rootDir, templateFolder, sanitizedFileName]),
34
+ )
35
+ let content = await fs.readFile(filePath, 'utf8')
36
+
37
+ // Make sure statSync doesn't break the execution if the file doesn't exist
38
+ try {
39
+ if (isWorkbenchConfig && statSync(generateFilePath).isFile()) {
40
+ const existingWorkbenchConfig = await fs.readFile(generateFilePath, 'utf8')
41
+ const workbenchContent = JSON.parse(content)
42
+
43
+ content = JSON.stringify([...JSON.parse(existingWorkbenchConfig), ...workbenchContent], null, 2)
44
+
45
+ context.log('workbench-config-updated', (message) =>
46
+ message.tag('success').append('Workbench config').append('has been updated.'),
47
+ )
48
+ }
49
+ } catch {
50
+ void 0
51
+ }
25
52
 
26
53
  await fs.writeFile(generateFilePath, content, 'utf8')
27
54
  context.log(sanitizedFileName, (message) => {
@@ -5,4 +5,5 @@ const generate_1 = require("./generate");
5
5
  exports.templates = {
6
6
  default: (0, generate_1.generateTemplateSteps)('default'),
7
7
  python: (0, generate_1.generateTemplateSteps)('python'),
8
+ 'basic-tutorial': (0, generate_1.generateTemplateSteps)('basic-tutorial'),
8
9
  };
@@ -3,4 +3,5 @@ import { generateTemplateSteps, Generator } from './generate'
3
3
  export const templates: Record<string, Generator> = {
4
4
  default: generateTemplateSteps('default'),
5
5
  python: generateTemplateSteps('python'),
6
+ 'basic-tutorial': generateTemplateSteps('basic-tutorial'),
6
7
  }
@@ -0,0 +1,2 @@
1
+ export declare const checkIfFileExists: (dir: string, fileName: string) => boolean;
2
+ export declare const checkIfDirectoryExists: (dir: string) => boolean;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkIfDirectoryExists = exports.checkIfFileExists = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const checkIfFileExists = (dir, fileName) => {
10
+ return fs_1.default.existsSync(path_1.default.join(dir, fileName));
11
+ };
12
+ exports.checkIfFileExists = checkIfFileExists;
13
+ const checkIfDirectoryExists = (dir) => {
14
+ try {
15
+ return fs_1.default.statSync(dir).isDirectory();
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ };
21
+ exports.checkIfDirectoryExists = checkIfDirectoryExists;
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable no-useless-escape */
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.printMotiaDockerIntro = void 0;
5
4
  const printMotiaDockerIntro = () => {