motia 0.5.11-beta.120-492039 → 0.5.11-beta.120-356715
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/cloud/build/builder.js +6 -2
- package/dist/cjs/cloud/build/builders/node/index.js +1 -8
- package/dist/cjs/cloud/build/builders/python/add-package-to-archive.js +19 -16
- package/dist/cjs/cloud/build/builders/python/index.js +20 -16
- package/dist/cjs/cloud/build/builders/python/python-builder.py +204 -22
- package/dist/cjs/cloud/new-deployment/build.js +2 -3
- package/dist/cjs/cloud/new-deployment/listeners/build-printer.d.ts +1 -1
- package/dist/cjs/cloud/new-deployment/listeners/build-printer.js +1 -1
- package/dist/cjs/cloud/new-deployment/listeners/cli-listener.d.ts +1 -1
- package/dist/cjs/cloud/new-deployment/listeners/listener.types.d.ts +1 -1
- package/dist/cjs/dev-watchers.js +2 -3
- package/dist/cjs/generate-locked-data.js +2 -2
- package/dist/cjs/generate-types.js +2 -2
- package/dist/cjs/utils/install-lambda-python-packages.js +1 -1
- package/dist/cjs/watcher.d.ts +1 -2
- package/dist/cjs/watcher.js +5 -6
- package/dist/dot-files/.cursor/rules/ai-agent-patterns.mdc +725 -0
- package/dist/dot-files/.cursor/rules/api-design-patterns.mdc +740 -0
- package/dist/dot-files/.cursor/rules/api-steps.mdc +125 -64
- package/dist/dot-files/.cursor/rules/authentication-patterns.mdc +620 -0
- package/dist/dot-files/.cursor/rules/background-job-patterns.mdc +628 -0
- package/dist/dot-files/.cursor/rules/complete-application-patterns.mdc +433 -0
- package/dist/dot-files/.cursor/rules/complete-backend-generator.mdc +415 -0
- package/dist/dot-files/.cursor/rules/event-steps.mdc +271 -133
- package/dist/dot-files/.cursor/rules/multi-language-workflows.mdc +1059 -0
- package/dist/dot-files/.cursor/rules/production-deployment.mdc +668 -0
- package/dist/dot-files/.cursor/rules/realtime-streaming.mdc +656 -0
- package/dist/dot-files/.cursor/rules/state-management.mdc +133 -87
- package/dist/dot-files/.cursor/rules/steps.mdc +68 -12
- package/dist/dot-files/.cursor/rules/workflow-patterns.mdc +938 -0
- package/dist/dot-files/CLAUDE.md +334 -129
- package/dist/esm/cloud/build/builder.js +6 -2
- package/dist/esm/cloud/build/builders/node/index.js +1 -8
- package/dist/esm/cloud/build/builders/python/add-package-to-archive.js +19 -16
- package/dist/esm/cloud/build/builders/python/index.js +20 -16
- package/dist/esm/cloud/build/builders/python/python-builder.py +204 -22
- package/dist/esm/cloud/new-deployment/build.js +2 -3
- package/dist/esm/cloud/new-deployment/listeners/build-printer.d.ts +1 -1
- package/dist/esm/cloud/new-deployment/listeners/build-printer.js +1 -1
- package/dist/esm/cloud/new-deployment/listeners/cli-listener.d.ts +1 -1
- package/dist/esm/cloud/new-deployment/listeners/listener.types.d.ts +1 -1
- package/dist/esm/dev-watchers.js +2 -3
- package/dist/esm/generate-locked-data.js +2 -2
- package/dist/esm/generate-types.js +2 -2
- package/dist/esm/utils/install-lambda-python-packages.js +1 -1
- package/dist/esm/watcher.d.ts +1 -2
- package/dist/esm/watcher.js +5 -6
- package/dist/types/cloud/new-deployment/listeners/build-printer.d.ts +1 -1
- package/dist/types/cloud/new-deployment/listeners/cli-listener.d.ts +1 -1
- package/dist/types/cloud/new-deployment/listeners/listener.types.d.ts +1 -1
- package/dist/types/watcher.d.ts +1 -2
- package/package.json +4 -4
- package/dist/cjs/cloud/build/builders/python/trace_packages.py +0 -212
- package/dist/cjs/cloud/build/builders/python/trace_project_files.py +0 -43
- package/dist/esm/cloud/build/builders/python/trace_packages.py +0 -212
- package/dist/esm/cloud/build/builders/python/trace_project_files.py +0 -43
|
@@ -51,11 +51,15 @@ class Builder {
|
|
|
51
51
|
const pythonBuilder = this.builders.get('python');
|
|
52
52
|
this.routersConfig = {};
|
|
53
53
|
if (nodeSteps.length > 0 && nodeBuilder) {
|
|
54
|
-
|
|
54
|
+
this.listener.onApiRouterBuilding('node');
|
|
55
|
+
const { size, path } = await nodeBuilder.buildApiSteps(nodeSteps);
|
|
56
|
+
this.listener.onApiRouterBuilt('node', size);
|
|
55
57
|
this.routersConfig.node = path;
|
|
56
58
|
}
|
|
57
59
|
if (pythonSteps.length > 0 && pythonBuilder) {
|
|
58
|
-
|
|
60
|
+
this.listener.onApiRouterBuilding('python');
|
|
61
|
+
const { size, path } = await pythonBuilder.buildApiSteps(pythonSteps);
|
|
62
|
+
this.listener.onApiRouterBuilt('python', size);
|
|
59
63
|
this.routersConfig.python = path;
|
|
60
64
|
}
|
|
61
65
|
}
|
|
@@ -74,14 +74,8 @@ class NodeBuilder {
|
|
|
74
74
|
.readFileSync(path_1.default.join(__dirname, 'router-template.ts'), 'utf-8')
|
|
75
75
|
.replace('// {{imports}}', steps.map((step, index) => `import * as route${index} from '${getStepPath(step)}'`).join('\n'))
|
|
76
76
|
.replace('// {{router paths}}', steps
|
|
77
|
-
.map((step, index) => {
|
|
78
|
-
this.listener.onBuildStart(step);
|
|
79
|
-
const route = `'${step.config.method} ${step.config.path}': { stepName: '${step.config.name}', handler: route${index}.handler, config: route${index}.config }`;
|
|
80
|
-
this.listener.onBuildEnd(step);
|
|
81
|
-
return route;
|
|
82
|
-
})
|
|
77
|
+
.map((step, index) => `'${step.config.method} ${step.config.path}': { stepName: '${step.config.name}', handler: route${index}.handler, config: route${index}.config }`)
|
|
83
78
|
.join(',\n'));
|
|
84
|
-
this.listener.onApiRouterBuilding('node');
|
|
85
79
|
const tsRouter = path_1.default.join(constants_1.distDir, 'router.ts');
|
|
86
80
|
fs_1.default.writeFileSync(tsRouter, file);
|
|
87
81
|
const userConfig = await this.loadEsbuildConfig();
|
|
@@ -104,7 +98,6 @@ class NodeBuilder {
|
|
|
104
98
|
fs_1.default.unlinkSync(tsRouter);
|
|
105
99
|
fs_1.default.unlinkSync(routerJs);
|
|
106
100
|
fs_1.default.unlinkSync(routerMap);
|
|
107
|
-
this.listener.onApiRouterBuilt('node', size);
|
|
108
101
|
return { size, path: zipName };
|
|
109
102
|
}
|
|
110
103
|
async build(step) {
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.addPackageToArchive = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const colors_1 = __importDefault(require("colors"));
|
|
9
10
|
const shouldIgnore = (filePath) => {
|
|
10
11
|
const ignorePatterns = [/\.pyc$/, /\.egg$/, /__pycache__/, /\.dist-info$/];
|
|
11
12
|
return ignorePatterns.some((pattern) => pattern.test(filePath));
|
|
@@ -30,24 +31,26 @@ const addDirectoryToArchive = async (archive, baseDir, dirPath) => {
|
|
|
30
31
|
.filter(Boolean));
|
|
31
32
|
};
|
|
32
33
|
const addPackageToArchive = async (archive, sitePackagesDir, packageName) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
// First try the package name as is
|
|
35
|
+
let fullPath = path_1.default.join(sitePackagesDir, packageName);
|
|
36
|
+
// If not found, try with .py extension
|
|
37
|
+
if (!fs_1.default.existsSync(fullPath)) {
|
|
38
|
+
const pyPath = path_1.default.join(sitePackagesDir, `${packageName}.py`);
|
|
39
|
+
if (fs_1.default.existsSync(pyPath)) {
|
|
40
|
+
fullPath = pyPath;
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
const relativePath = path_1.default.relative(sitePackagesDir, fullPath);
|
|
47
|
-
archive.append(fs_1.default.createReadStream(fullPath), relativePath);
|
|
48
|
-
}
|
|
49
|
-
// package added successfully
|
|
42
|
+
}
|
|
43
|
+
if (!fs_1.default.existsSync(fullPath)) {
|
|
44
|
+
console.log(colors_1.default.yellow(`Warning: Package not found in site-packages: ${packageName}`));
|
|
50
45
|
return;
|
|
51
46
|
}
|
|
47
|
+
const stat = fs_1.default.statSync(fullPath);
|
|
48
|
+
if (stat.isDirectory()) {
|
|
49
|
+
await addDirectoryToArchive(archive, sitePackagesDir, fullPath);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const relativePath = path_1.default.relative(sitePackagesDir, fullPath);
|
|
53
|
+
archive.append(fs_1.default.createReadStream(fullPath), relativePath);
|
|
54
|
+
}
|
|
52
55
|
};
|
|
53
56
|
exports.addPackageToArchive = addPackageToArchive;
|
|
@@ -23,33 +23,41 @@ class PythonBuilder {
|
|
|
23
23
|
const normalizedEntrypointPath = entrypointPath.replace(/[.]step.py$/, '_step.py');
|
|
24
24
|
const sitePackagesDir = `${process.env.PYTHON_SITE_PACKAGES}-lambda`;
|
|
25
25
|
// Get Python builder response
|
|
26
|
-
const { packages
|
|
26
|
+
const { packages } = await this.getPythonBuilderData(step);
|
|
27
27
|
// Add main file to archive
|
|
28
28
|
if (!fs_1.default.existsSync(step.filePath)) {
|
|
29
29
|
throw new Error(`Source file not found: ${step.filePath}`);
|
|
30
30
|
}
|
|
31
31
|
archive.append(fs_1.default.createReadStream(step.filePath), path_1.default.relative(this.builder.projectDir, normalizedEntrypointPath));
|
|
32
|
-
|
|
33
|
-
if (packages.length > 0) {
|
|
34
|
-
await Promise.all(packages.map((pkg) => (0, add_package_to_archive_1.addPackageToArchive)(archive, sitePackagesDir, pkg.name)));
|
|
35
|
-
this.listener.onBuildProgress(step, `Added ${packages.length} packages to archive`);
|
|
36
|
-
}
|
|
32
|
+
await Promise.all(packages.map(async (packageName) => (0, add_package_to_archive_1.addPackageToArchive)(archive, sitePackagesDir, packageName)));
|
|
37
33
|
return normalizedEntrypointPath;
|
|
38
34
|
}
|
|
39
35
|
async build(step) {
|
|
40
36
|
const entrypointPath = step.filePath.replace(this.builder.projectDir, '');
|
|
41
37
|
const bundlePath = path_1.default.join('python', entrypointPath.replace(/(.*)\.py$/, '$1.zip'));
|
|
38
|
+
const normalizedEntrypointPath = entrypointPath.replace(/[.]step.py$/, '_step.py');
|
|
42
39
|
const outfile = path_1.default.join(constants_1.distDir, bundlePath);
|
|
43
40
|
try {
|
|
44
41
|
// Create output directory
|
|
45
42
|
fs_1.default.mkdirSync(path_1.default.dirname(outfile), { recursive: true });
|
|
46
43
|
this.listener.onBuildStart(step);
|
|
47
|
-
//
|
|
44
|
+
// Get Python builder response
|
|
45
|
+
const { packages } = await this.getPythonBuilderData(step);
|
|
48
46
|
const stepArchiver = new archiver_1.Archiver(outfile);
|
|
49
|
-
// Build the step
|
|
50
47
|
const stepPath = await this.buildStep(step, stepArchiver);
|
|
51
|
-
// Add
|
|
48
|
+
// Add main file to archive
|
|
49
|
+
if (!fs_1.default.existsSync(step.filePath)) {
|
|
50
|
+
throw new Error(`Source file not found: ${step.filePath}`);
|
|
51
|
+
}
|
|
52
|
+
stepArchiver.append(fs_1.default.createReadStream(step.filePath), path_1.default.relative(this.builder.projectDir, normalizedEntrypointPath));
|
|
53
|
+
// Add all imported files to archive
|
|
54
|
+
this.listener.onBuildProgress(step, 'Adding imported files to archive...');
|
|
55
|
+
const sitePackagesDir = `${process.env.PYTHON_SITE_PACKAGES}-lambda`;
|
|
52
56
|
(0, include_static_files_1.includeStaticFiles)([step], this.builder, stepArchiver);
|
|
57
|
+
if (packages.length > 0) {
|
|
58
|
+
await Promise.all(packages.map(async (packageName) => (0, add_package_to_archive_1.addPackageToArchive)(stepArchiver, sitePackagesDir, packageName)));
|
|
59
|
+
this.listener.onBuildProgress(step, `Added ${packages.length} packages to archive`);
|
|
60
|
+
}
|
|
53
61
|
// Finalize the archive and wait for completion
|
|
54
62
|
const size = await stepArchiver.finalize();
|
|
55
63
|
this.builder.registerStep({ entrypointPath: stepPath, bundlePath, step, type: 'python' });
|
|
@@ -73,12 +81,9 @@ class PythonBuilder {
|
|
|
73
81
|
const dependencies = ['uvicorn', 'pydantic', 'pydantic_core', 'uvloop', 'starlette', 'typing_inspection'];
|
|
74
82
|
const lambdaSitePackages = `${process.env.PYTHON_SITE_PACKAGES}-lambda`;
|
|
75
83
|
await Promise.all(dependencies.map(async (packageName) => (0, add_package_to_archive_1.addPackageToArchive)(archive, lambdaSitePackages, packageName)));
|
|
76
|
-
|
|
77
|
-
this.listener.onBuildStart(step);
|
|
84
|
+
for (const step of steps) {
|
|
78
85
|
await this.buildStep(step, archive);
|
|
79
|
-
|
|
80
|
-
}));
|
|
81
|
-
this.listener.onApiRouterBuilding('python');
|
|
86
|
+
}
|
|
82
87
|
const file = fs_1.default
|
|
83
88
|
.readFileSync(path_1.default.join(__dirname, 'router_template.py'), 'utf-8')
|
|
84
89
|
.replace('# {{imports}}', steps
|
|
@@ -91,12 +96,11 @@ class PythonBuilder {
|
|
|
91
96
|
(0, include_static_files_1.includeStaticFiles)(steps, this.builder, archive);
|
|
92
97
|
// Finalize the archive and wait for completion
|
|
93
98
|
const size = await archive.finalize();
|
|
94
|
-
this.listener.onApiRouterBuilt('python', size);
|
|
95
99
|
return { size, path: zipName };
|
|
96
100
|
}
|
|
97
101
|
async getPythonBuilderData(step) {
|
|
98
102
|
return new Promise((resolve, reject) => {
|
|
99
|
-
const child = (0, child_process_1.spawn)('python', [path_1.default.join(__dirname, 'python-builder.py'),
|
|
103
|
+
const child = (0, child_process_1.spawn)('python', [path_1.default.join(__dirname, 'python-builder.py'), step.filePath], {
|
|
100
104
|
cwd: this.builder.projectDir,
|
|
101
105
|
stdio: [undefined, undefined, 'pipe', 'ipc'],
|
|
102
106
|
});
|
|
@@ -1,39 +1,221 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
import json
|
|
4
|
+
import importlib.util
|
|
4
5
|
import traceback
|
|
6
|
+
import site
|
|
7
|
+
import builtins
|
|
8
|
+
import ast
|
|
9
|
+
import importlib.metadata
|
|
10
|
+
import subprocess
|
|
11
|
+
import re
|
|
12
|
+
from typing import Set, List, Tuple, Optional, Dict, Any
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from functools import lru_cache
|
|
5
15
|
|
|
6
|
-
|
|
7
|
-
from trace_project_files import trace_project_files
|
|
16
|
+
NODEIPCFD = int(os.environ["NODE_CHANNEL_FD"])
|
|
8
17
|
|
|
9
|
-
|
|
18
|
+
# Cache for built-in modules to avoid repeated checks
|
|
19
|
+
_builtin_modules_cache: Set[str] = set()
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
@lru_cache(maxsize=1024)
|
|
22
|
+
def is_valid_package_name(name: str) -> bool:
|
|
23
|
+
"""Check if a name is a valid package name."""
|
|
24
|
+
if not name or name.startswith('_'):
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
# Skip common special cases
|
|
28
|
+
invalid_names = {'__main__', 'module', 'cython_runtime', 'builtins'}
|
|
29
|
+
return name not in invalid_names
|
|
30
|
+
|
|
31
|
+
@lru_cache(maxsize=1024)
|
|
32
|
+
def get_package_name(module_name: str) -> str:
|
|
33
|
+
"""Get the top-level package name from a module name."""
|
|
34
|
+
return module_name.split('.')[0]
|
|
35
|
+
|
|
36
|
+
@lru_cache(maxsize=1024)
|
|
37
|
+
def clean_package_name(package_name: str) -> str:
|
|
38
|
+
"""Clean package name by removing version specifiers and other metadata."""
|
|
39
|
+
# Remove version specifiers and conditions using regex
|
|
40
|
+
package_name = re.sub(r'[<>=~!;].*$', '', package_name)
|
|
41
|
+
# Remove any remaining whitespace and convert underscores to hyphens
|
|
42
|
+
return package_name.strip().replace('_', '-')
|
|
16
43
|
|
|
17
|
-
|
|
18
|
-
|
|
44
|
+
@lru_cache(maxsize=1024)
|
|
45
|
+
def extract_base_package_name(dependency_spec: str) -> str:
|
|
46
|
+
"""
|
|
47
|
+
Extract the base package name from a complex dependency specification.
|
|
48
|
+
Handles cases like:
|
|
49
|
+
- 'package (>=1.2.1,<2.0.0)'
|
|
50
|
+
- 'package[extra] (>=1.2.1)'
|
|
51
|
+
- 'package ; extra == "vlm"'
|
|
52
|
+
- 'package (>=1.2.1) ; sys_platform == "darwin"'
|
|
53
|
+
"""
|
|
54
|
+
# First, remove any conditions after semicolon
|
|
55
|
+
base_spec = dependency_spec.split(';')[0].strip()
|
|
56
|
+
|
|
57
|
+
# Extract the package name before any version specifiers or extras
|
|
58
|
+
match = re.match(r'^([a-zA-Z0-9_.-]+)(?:\[[^\]]+\])?(?:\s*\([^)]*\))?$', base_spec)
|
|
59
|
+
|
|
60
|
+
return clean_package_name(match.group(1) if match else base_spec)
|
|
19
61
|
|
|
62
|
+
@lru_cache(maxsize=1024)
|
|
63
|
+
def is_package_installed(package_name: str) -> bool:
|
|
64
|
+
"""Check if a package is installed in the current environment."""
|
|
65
|
+
try:
|
|
66
|
+
# Try both hyphenated and non-hyphenated versions
|
|
67
|
+
try:
|
|
68
|
+
importlib.metadata.distribution(package_name)
|
|
69
|
+
return True
|
|
70
|
+
except importlib.metadata.PackageNotFoundError:
|
|
71
|
+
# Try with hyphens replaced by underscores
|
|
72
|
+
alt_name = package_name.replace('-', '_')
|
|
73
|
+
if alt_name != package_name:
|
|
74
|
+
importlib.metadata.distribution(alt_name)
|
|
75
|
+
return True
|
|
76
|
+
return False
|
|
77
|
+
except importlib.metadata.PackageNotFoundError:
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
@lru_cache(maxsize=1024)
|
|
81
|
+
def is_builtin_module(module_name: str) -> bool:
|
|
82
|
+
"""Check if a module is a Python built-in module."""
|
|
83
|
+
if module_name in _builtin_modules_cache:
|
|
84
|
+
return True
|
|
85
|
+
|
|
20
86
|
try:
|
|
21
|
-
|
|
22
|
-
packages = trace_packages(project_dir, entry_file)
|
|
23
|
-
files = trace_project_files(project_dir, entry_file)
|
|
87
|
+
module = importlib.import_module(module_name)
|
|
24
88
|
|
|
25
|
-
#
|
|
89
|
+
# Built-in modules either have no __file__ attribute or their file is in the standard library
|
|
90
|
+
if not hasattr(module, '__file__'):
|
|
91
|
+
_builtin_modules_cache.add(module_name)
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
# Get the standard library path
|
|
95
|
+
stdlib_path = os.path.dirname(os.__file__)
|
|
96
|
+
|
|
97
|
+
# Check if the module's file is in the standard library
|
|
98
|
+
is_builtin = module.__file__ and module.__file__.startswith(stdlib_path)
|
|
99
|
+
if is_builtin:
|
|
100
|
+
_builtin_modules_cache.add(module_name)
|
|
101
|
+
return is_builtin
|
|
102
|
+
except ImportError:
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
def get_direct_imports(file_path: str) -> Set[str]:
|
|
106
|
+
"""Extract direct imports from a Python file using AST parsing."""
|
|
107
|
+
direct_imports = set()
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
with open(file_path, 'r') as f:
|
|
111
|
+
content = f.read()
|
|
112
|
+
|
|
113
|
+
tree = ast.parse(content)
|
|
114
|
+
for node in ast.walk(tree):
|
|
115
|
+
if isinstance(node, ast.Import):
|
|
116
|
+
for name in node.names:
|
|
117
|
+
base_pkg = name.name.split('.')[0]
|
|
118
|
+
if is_valid_package_name(base_pkg) and not is_builtin_module(base_pkg):
|
|
119
|
+
direct_imports.add(base_pkg)
|
|
120
|
+
elif isinstance(node, ast.ImportFrom):
|
|
121
|
+
if node.module:
|
|
122
|
+
base_pkg = node.module.split('.')[0]
|
|
123
|
+
if is_valid_package_name(base_pkg) and not is_builtin_module(base_pkg):
|
|
124
|
+
direct_imports.add(base_pkg)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
print(f"Warning: Could not parse imports from {file_path}: {str(e)}")
|
|
127
|
+
|
|
128
|
+
return direct_imports
|
|
129
|
+
|
|
130
|
+
@lru_cache(maxsize=1024)
|
|
131
|
+
def is_optional_dependency(req: str) -> bool:
|
|
132
|
+
"""Check if a dependency is an optional dependency."""
|
|
133
|
+
return '[' in req or 'extra ==' in req
|
|
134
|
+
|
|
135
|
+
def get_package_dependencies(package_name: str, processed: Set[str] = None) -> Set[str]:
|
|
136
|
+
"""Get all dependencies (including sub-dependencies) for a given package."""
|
|
137
|
+
if processed is None:
|
|
138
|
+
processed = set()
|
|
139
|
+
|
|
140
|
+
if package_name in processed or is_builtin_module(package_name):
|
|
141
|
+
return set()
|
|
142
|
+
|
|
143
|
+
processed.add(package_name)
|
|
144
|
+
all_dependencies = set()
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# Try to get the distribution
|
|
148
|
+
try:
|
|
149
|
+
dist = importlib.metadata.distribution(package_name)
|
|
150
|
+
except importlib.metadata.PackageNotFoundError:
|
|
151
|
+
print(f'Warning: Package {package_name} not found')
|
|
152
|
+
return all_dependencies
|
|
153
|
+
|
|
154
|
+
# Filter out optional dependencies
|
|
155
|
+
sub_dependencies = list(filter(lambda dep: not is_optional_dependency(dep), dist.requires or []))
|
|
156
|
+
|
|
157
|
+
# Get direct dependencies
|
|
158
|
+
for req in sub_dependencies:
|
|
159
|
+
base_pkg = extract_base_package_name(req)
|
|
160
|
+
|
|
161
|
+
if base_pkg and base_pkg not in processed:
|
|
162
|
+
# Try both hyphenated and non-hyphenated versions
|
|
163
|
+
for dep_name in [base_pkg, base_pkg.replace('-', '_'), base_pkg.replace('_', '-')]:
|
|
164
|
+
try:
|
|
165
|
+
importlib.import_module(dep_name)
|
|
166
|
+
all_dependencies.add(dep_name)
|
|
167
|
+
# Recursively get sub-dependencies
|
|
168
|
+
all_dependencies.update(get_package_dependencies(dep_name, processed))
|
|
169
|
+
break
|
|
170
|
+
except ImportError:
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print(f"Warning: Error processing {package_name}: {str(e)}")
|
|
175
|
+
|
|
176
|
+
return all_dependencies
|
|
177
|
+
|
|
178
|
+
def trace_imports(entry_file: str) -> List[str]:
|
|
179
|
+
"""Find all imported Python packages and files starting from an entry file."""
|
|
180
|
+
entry_file = os.path.abspath(entry_file)
|
|
181
|
+
module_dir = os.path.dirname(entry_file)
|
|
182
|
+
|
|
183
|
+
if module_dir not in sys.path:
|
|
184
|
+
sys.path.insert(0, module_dir)
|
|
185
|
+
|
|
186
|
+
# Get direct imports from the entry file
|
|
187
|
+
direct_imports = get_direct_imports(entry_file)
|
|
188
|
+
|
|
189
|
+
# Initialize sets to track packages
|
|
190
|
+
all_packages = set()
|
|
191
|
+
processed_packages = set()
|
|
192
|
+
|
|
193
|
+
# Process each direct import and its dependencies
|
|
194
|
+
for package_name in direct_imports:
|
|
195
|
+
if is_valid_package_name(package_name):
|
|
196
|
+
all_packages.add(package_name)
|
|
197
|
+
# Get all dependencies including sub-dependencies
|
|
198
|
+
all_packages.update(get_package_dependencies(package_name, processed_packages))
|
|
199
|
+
|
|
200
|
+
# Filter out built-in packages
|
|
201
|
+
non_builtin_packages = {pkg for pkg in all_packages if not is_builtin_module(pkg)}
|
|
202
|
+
|
|
203
|
+
return sorted(list(non_builtin_packages))
|
|
204
|
+
|
|
205
|
+
def main() -> None:
|
|
206
|
+
"""Main entry point for the script."""
|
|
207
|
+
if len(sys.argv) != 2:
|
|
208
|
+
print("Usage: python python-builder.py <entry_file>", file=sys.stderr)
|
|
209
|
+
sys.exit(1)
|
|
210
|
+
|
|
211
|
+
entry_file = sys.argv[1]
|
|
212
|
+
try:
|
|
213
|
+
packages = trace_imports(entry_file)
|
|
26
214
|
output = {
|
|
27
|
-
'packages': packages
|
|
28
|
-
'files': files,
|
|
215
|
+
'packages': packages
|
|
29
216
|
}
|
|
30
|
-
|
|
31
|
-
# Output as JSON
|
|
32
217
|
bytes_message = (json.dumps(output) + '\n').encode('utf-8')
|
|
33
|
-
|
|
34
|
-
os.write(NODEIPCFD, bytes_message)
|
|
35
|
-
else:
|
|
36
|
-
print(bytes_message)
|
|
218
|
+
os.write(NODEIPCFD, bytes_message)
|
|
37
219
|
sys.exit(0)
|
|
38
220
|
except Exception as e:
|
|
39
221
|
print(f"Error: {str(e)}", file=sys.stderr)
|
|
@@ -30,9 +30,8 @@ const build = async (listener) => {
|
|
|
30
30
|
if (invalidSteps.length > 0) {
|
|
31
31
|
throw new Error('Project contains invalid steps, please fix them before building');
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
await Promise.all([...stepPromises, apiStepPromise]);
|
|
33
|
+
await Promise.all(lockedData.activeSteps.map((step) => builder.buildStep(step)));
|
|
34
|
+
await builder.buildApiSteps(lockedData.activeSteps.filter(core_1.isApiStep));
|
|
36
35
|
const streams = lockedData.listStreams();
|
|
37
36
|
for (const stream of streams) {
|
|
38
37
|
if (stream.config.baseConfig.storageType === 'default') {
|
|
@@ -6,7 +6,7 @@ export declare class BuildPrinter {
|
|
|
6
6
|
getLanguage(language: string): string;
|
|
7
7
|
getStepLanguage(step: Step): string;
|
|
8
8
|
printStepBuilding(step: Step, progressMessage?: string): void;
|
|
9
|
-
printStepBuilt(step: Step, size
|
|
9
|
+
printStepBuilt(step: Step, size: number): void;
|
|
10
10
|
printApiRouterBuilding(language: string): void;
|
|
11
11
|
printApiRouterBuilt(language: string, size: number): void;
|
|
12
12
|
printStepFailed(step: Step, error: Error): void;
|
|
@@ -62,7 +62,7 @@ class BuildPrinter {
|
|
|
62
62
|
const stepPath = this.printer.getStepPath(step);
|
|
63
63
|
this.output.log(step.filePath, (message) => message
|
|
64
64
|
.append(`${built} ${stepTag} ${stepLanguage} ${stepType} ${stepPath}`)
|
|
65
|
-
.append(`${
|
|
65
|
+
.append(`${(0, pretty_bytes_1.prettyBytes)(size)}`, 'gray'));
|
|
66
66
|
}
|
|
67
67
|
printApiRouterBuilding(language) {
|
|
68
68
|
const fileName = `router-${language}.zip`;
|
|
@@ -11,7 +11,7 @@ export declare class CliListener implements DeploymentListener {
|
|
|
11
11
|
constructor(context: CliContext);
|
|
12
12
|
onBuildStart(step: Step): void;
|
|
13
13
|
onBuildProgress(step: Step, message: string): void;
|
|
14
|
-
onBuildEnd(step: Step, size
|
|
14
|
+
onBuildEnd(step: Step, size: number): void;
|
|
15
15
|
onBuildError(step: Step, error: Error): void;
|
|
16
16
|
onBuildSkip(step: Step, reason: string): void;
|
|
17
17
|
onStreamCreated(stream: Stream): void;
|
|
@@ -5,7 +5,7 @@ import { ValidationError } from '../utils/validation';
|
|
|
5
5
|
export type BuildListener = {
|
|
6
6
|
onBuildStart: (step: Step) => void;
|
|
7
7
|
onBuildProgress: (step: Step, message: string) => void;
|
|
8
|
-
onBuildEnd: (step: Step, size
|
|
8
|
+
onBuildEnd: (step: Step, size: number) => void;
|
|
9
9
|
onBuildError: (step: Step, error: Error) => void;
|
|
10
10
|
onBuildSkip: (step: Step, reason: string) => void;
|
|
11
11
|
onApiRouterBuilding: (language: string) => void;
|
package/dist/cjs/dev-watchers.js
CHANGED
|
@@ -8,9 +8,8 @@ const core_1 = require("@motiadev/core");
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const watcher_1 = require("./watcher");
|
|
10
10
|
const createDevWatchers = (lockedData, server, eventHandler, cronManager) => {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const watcher = new watcher_1.Watcher(projectDir, stepDir, lockedData);
|
|
11
|
+
const stepDir = path_1.default.join(process.cwd(), 'steps');
|
|
12
|
+
const watcher = new watcher_1.Watcher(stepDir, lockedData);
|
|
14
13
|
watcher.onStreamChange((oldStream, stream) => {
|
|
15
14
|
(0, core_1.trackEvent)('stream_updated', {
|
|
16
15
|
streamName: stream.config.name,
|
|
@@ -27,7 +27,7 @@ const collectFlows = async (projectDir, lockedData) => {
|
|
|
27
27
|
...(0, glob_1.globSync)(path_1.default.join(projectDir, '{steps,streams}/**/*_stream.{ts,js,py}')),
|
|
28
28
|
];
|
|
29
29
|
for (const filePath of stepFiles) {
|
|
30
|
-
const config = await (0, core_1.getStepConfig)(
|
|
30
|
+
const config = await (0, core_1.getStepConfig)(filePath);
|
|
31
31
|
if (!config) {
|
|
32
32
|
console.warn(`No config found in step ${filePath}, step skipped`);
|
|
33
33
|
continue;
|
|
@@ -38,7 +38,7 @@ const collectFlows = async (projectDir, lockedData) => {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
for (const filePath of streamFiles) {
|
|
41
|
-
const config = await (0, core_1.getStreamConfig)(
|
|
41
|
+
const config = await (0, core_1.getStreamConfig)(filePath);
|
|
42
42
|
if (!config) {
|
|
43
43
|
console.warn(`No config found in stream ${filePath}, stream skipped`);
|
|
44
44
|
continue;
|
|
@@ -15,13 +15,13 @@ const generateTypes = async (projectDir) => {
|
|
|
15
15
|
const streamsFiles = (0, glob_1.globSync)('**/*.stream.{ts,js,py,rb}', { absolute: true, cwd: stepsDir });
|
|
16
16
|
const lockedData = new core_1.LockedData(projectDir, 'memory', new core_1.Printer(projectDir));
|
|
17
17
|
for (const filePath of files) {
|
|
18
|
-
const config = await (0, core_1.getStepConfig)(
|
|
18
|
+
const config = await (0, core_1.getStepConfig)(filePath);
|
|
19
19
|
if (config) {
|
|
20
20
|
lockedData.createStep({ filePath, version, config }, { disableTypeCreation: true });
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
for (const filePath of streamsFiles) {
|
|
24
|
-
const config = await (0, core_1.getStreamConfig)(
|
|
24
|
+
const config = await (0, core_1.getStreamConfig)(filePath);
|
|
25
25
|
if (config) {
|
|
26
26
|
lockedData.createStream({ filePath, config }, { disableTypeCreation: true });
|
|
27
27
|
}
|
|
@@ -14,7 +14,7 @@ const installLambdaPythonPackages = ({ isVerbose = false, requirementsList }) =>
|
|
|
14
14
|
if (isVerbose) {
|
|
15
15
|
internal_logger_1.internalLogger.warn(`requirements.txt not found at ${requirement}`);
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
return;
|
|
18
18
|
}
|
|
19
19
|
try {
|
|
20
20
|
// Install packages to lambda site-packages with platform specification
|
package/dist/cjs/watcher.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ type StreamChangeHandler = (oldStream: Stream, newStream: Stream) => void;
|
|
|
7
7
|
type StreamCreateHandler = (stream: Stream) => void;
|
|
8
8
|
type StreamDeleteHandler = (stream: Stream) => void;
|
|
9
9
|
export declare class Watcher {
|
|
10
|
-
private readonly baseDir;
|
|
11
10
|
private readonly dir;
|
|
12
11
|
private lockedData;
|
|
13
12
|
private watcher?;
|
|
@@ -17,7 +16,7 @@ export declare class Watcher {
|
|
|
17
16
|
private streamChangeHandler?;
|
|
18
17
|
private streamCreateHandler?;
|
|
19
18
|
private streamDeleteHandler?;
|
|
20
|
-
constructor(
|
|
19
|
+
constructor(dir: string, lockedData: LockedData);
|
|
21
20
|
onStepChange(handler: StepChangeHandler): void;
|
|
22
21
|
onStepCreate(handler: StepCreateHandler): void;
|
|
23
22
|
onStepDelete(handler: StepDeleteHandler): void;
|
package/dist/cjs/watcher.js
CHANGED
|
@@ -8,8 +8,7 @@ const chokidar_1 = __importDefault(require("chokidar"));
|
|
|
8
8
|
const crypto_1 = require("crypto");
|
|
9
9
|
const core_1 = require("@motiadev/core");
|
|
10
10
|
class Watcher {
|
|
11
|
-
constructor(
|
|
12
|
-
this.baseDir = baseDir;
|
|
11
|
+
constructor(dir, lockedData) {
|
|
13
12
|
this.dir = dir;
|
|
14
13
|
this.lockedData = lockedData;
|
|
15
14
|
}
|
|
@@ -40,7 +39,7 @@ class Watcher {
|
|
|
40
39
|
console.warn(`No step create handler, step skipped`);
|
|
41
40
|
return;
|
|
42
41
|
}
|
|
43
|
-
const config = await (0, core_1.getStepConfig)(
|
|
42
|
+
const config = await (0, core_1.getStepConfig)(path).catch((err) => console.error(err));
|
|
44
43
|
if (!config) {
|
|
45
44
|
return;
|
|
46
45
|
}
|
|
@@ -49,7 +48,7 @@ class Watcher {
|
|
|
49
48
|
this.stepCreateHandler?.(step);
|
|
50
49
|
}
|
|
51
50
|
async onStepFileChange(path) {
|
|
52
|
-
const config = await (0, core_1.getStepConfig)(
|
|
51
|
+
const config = await (0, core_1.getStepConfig)(path).catch((err) => {
|
|
53
52
|
console.error(err);
|
|
54
53
|
});
|
|
55
54
|
const step = this.findStep(path);
|
|
@@ -81,7 +80,7 @@ class Watcher {
|
|
|
81
80
|
this.stepDeleteHandler?.(step);
|
|
82
81
|
}
|
|
83
82
|
async onStreamFileAdd(path) {
|
|
84
|
-
const config = await (0, core_1.getStreamConfig)(
|
|
83
|
+
const config = await (0, core_1.getStreamConfig)(path).catch((err) => console.error(err));
|
|
85
84
|
if (!config) {
|
|
86
85
|
return;
|
|
87
86
|
}
|
|
@@ -89,7 +88,7 @@ class Watcher {
|
|
|
89
88
|
}
|
|
90
89
|
async onStreamFileChange(path) {
|
|
91
90
|
const stream = this.lockedData.findStream(path);
|
|
92
|
-
const config = await (0, core_1.getStreamConfig)(
|
|
91
|
+
const config = await (0, core_1.getStreamConfig)(path).catch((err) => console.error(err));
|
|
93
92
|
if (!stream && config) {
|
|
94
93
|
this.streamCreateHandler?.({ filePath: path, config, factory: null });
|
|
95
94
|
}
|