nodepyx 1.0.0
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/LICENSE +22 -0
- package/README.md +399 -0
- package/binding.gyp +73 -0
- package/dist/core/PyCallable.d.ts +65 -0
- package/dist/core/PyCallable.d.ts.map +1 -0
- package/dist/core/PyCallable.js +109 -0
- package/dist/core/PyCallable.js.map +1 -0
- package/dist/core/PyContext.d.ts +76 -0
- package/dist/core/PyContext.d.ts.map +1 -0
- package/dist/core/PyContext.js +228 -0
- package/dist/core/PyContext.js.map +1 -0
- package/dist/core/PyIterator.d.ts +84 -0
- package/dist/core/PyIterator.d.ts.map +1 -0
- package/dist/core/PyIterator.js +243 -0
- package/dist/core/PyIterator.js.map +1 -0
- package/dist/core/PyModule.d.ts +55 -0
- package/dist/core/PyModule.d.ts.map +1 -0
- package/dist/core/PyModule.js +172 -0
- package/dist/core/PyModule.js.map +1 -0
- package/dist/core/PyProxy.d.ts +65 -0
- package/dist/core/PyProxy.d.ts.map +1 -0
- package/dist/core/PyProxy.js +483 -0
- package/dist/core/PyProxy.js.map +1 -0
- package/dist/core/PyRuntime.d.ts +105 -0
- package/dist/core/PyRuntime.d.ts.map +1 -0
- package/dist/core/PyRuntime.js +438 -0
- package/dist/core/PyRuntime.js.map +1 -0
- package/dist/env/CondaManager.d.ts +118 -0
- package/dist/env/CondaManager.d.ts.map +1 -0
- package/dist/env/CondaManager.js +401 -0
- package/dist/env/CondaManager.js.map +1 -0
- package/dist/env/PackageInstaller.d.ts +233 -0
- package/dist/env/PackageInstaller.d.ts.map +1 -0
- package/dist/env/PackageInstaller.js +609 -0
- package/dist/env/PackageInstaller.js.map +1 -0
- package/dist/env/PythonDetector.d.ts +103 -0
- package/dist/env/PythonDetector.d.ts.map +1 -0
- package/dist/env/PythonDetector.js +381 -0
- package/dist/env/PythonDetector.js.map +1 -0
- package/dist/env/VenvManager.d.ts +117 -0
- package/dist/env/VenvManager.d.ts.map +1 -0
- package/dist/env/VenvManager.js +331 -0
- package/dist/env/VenvManager.js.map +1 -0
- package/dist/index.d.ts +169 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +393 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/Plugin.interface.d.ts +41 -0
- package/dist/plugins/Plugin.interface.d.ts.map +1 -0
- package/dist/plugins/Plugin.interface.js +12 -0
- package/dist/plugins/Plugin.interface.js.map +1 -0
- package/dist/plugins/PluginManager.d.ts +26 -0
- package/dist/plugins/PluginManager.d.ts.map +1 -0
- package/dist/plugins/PluginManager.js +174 -0
- package/dist/plugins/PluginManager.js.map +1 -0
- package/dist/plugins/builtin/NumpyPlugin.d.ts +17 -0
- package/dist/plugins/builtin/NumpyPlugin.d.ts.map +1 -0
- package/dist/plugins/builtin/NumpyPlugin.js +41 -0
- package/dist/plugins/builtin/NumpyPlugin.js.map +1 -0
- package/dist/plugins/builtin/PandasPlugin.d.ts +19 -0
- package/dist/plugins/builtin/PandasPlugin.d.ts.map +1 -0
- package/dist/plugins/builtin/PandasPlugin.js +57 -0
- package/dist/plugins/builtin/PandasPlugin.js.map +1 -0
- package/dist/plugins/builtin/TorchPlugin.d.ts +23 -0
- package/dist/plugins/builtin/TorchPlugin.d.ts.map +1 -0
- package/dist/plugins/builtin/TorchPlugin.js +50 -0
- package/dist/plugins/builtin/TorchPlugin.js.map +1 -0
- package/dist/plugins/index.d.ts +7 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +12 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/serialization/DataFrameBridge.d.ts +141 -0
- package/dist/serialization/DataFrameBridge.d.ts.map +1 -0
- package/dist/serialization/DataFrameBridge.js +355 -0
- package/dist/serialization/DataFrameBridge.js.map +1 -0
- package/dist/serialization/MsgPackSerializer.d.ts +45 -0
- package/dist/serialization/MsgPackSerializer.d.ts.map +1 -0
- package/dist/serialization/MsgPackSerializer.js +242 -0
- package/dist/serialization/MsgPackSerializer.js.map +1 -0
- package/dist/serialization/NumpyBridge.d.ts +96 -0
- package/dist/serialization/NumpyBridge.d.ts.map +1 -0
- package/dist/serialization/NumpyBridge.js +323 -0
- package/dist/serialization/NumpyBridge.js.map +1 -0
- package/dist/serialization/Serializer.d.ts +78 -0
- package/dist/serialization/Serializer.d.ts.map +1 -0
- package/dist/serialization/Serializer.js +281 -0
- package/dist/serialization/Serializer.js.map +1 -0
- package/dist/types/PythonTypeMapper.d.ts +87 -0
- package/dist/types/PythonTypeMapper.d.ts.map +1 -0
- package/dist/types/PythonTypeMapper.js +449 -0
- package/dist/types/PythonTypeMapper.js.map +1 -0
- package/dist/types/StubCache.d.ts +109 -0
- package/dist/types/StubCache.d.ts.map +1 -0
- package/dist/types/StubCache.js +333 -0
- package/dist/types/StubCache.js.map +1 -0
- package/dist/types/TypeGenerator.d.ts +139 -0
- package/dist/types/TypeGenerator.d.ts.map +1 -0
- package/dist/types/TypeGenerator.js +372 -0
- package/dist/types/TypeGenerator.js.map +1 -0
- package/dist/types/addon.d.ts +114 -0
- package/dist/types/addon.d.ts.map +1 -0
- package/dist/types/addon.js +32 -0
- package/dist/types/addon.js.map +1 -0
- package/dist/types/config.d.ts +175 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +35 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/python.d.ts +235 -0
- package/dist/types/python.d.ts.map +1 -0
- package/dist/types/python.js +7 -0
- package/dist/types/python.js.map +1 -0
- package/dist/utils/ErrorTranslator.d.ts +83 -0
- package/dist/utils/ErrorTranslator.d.ts.map +1 -0
- package/dist/utils/ErrorTranslator.js +210 -0
- package/dist/utils/ErrorTranslator.js.map +1 -0
- package/dist/utils/Logger.d.ts +27 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +115 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/MemoryMonitor.d.ts +44 -0
- package/dist/utils/MemoryMonitor.d.ts.map +1 -0
- package/dist/utils/MemoryMonitor.js +143 -0
- package/dist/utils/MemoryMonitor.js.map +1 -0
- package/package.json +177 -0
- package/python/error_handler.py +433 -0
- package/python/nodepyx_runtime.py +575 -0
- package/python/serializer.py +379 -0
- package/python/type_inspector.py +288 -0
- package/scripts/build-native.js +68 -0
- package/scripts/download-prebuilds.js +99 -0
- package/scripts/generate-stubs.js +405 -0
- package/scripts/install.js +260 -0
- package/src/core/PyCallable.ts +137 -0
- package/src/core/PyContext.ts +296 -0
- package/src/core/PyIterator.ts +294 -0
- package/src/core/PyModule.ts +194 -0
- package/src/core/PyProxy.ts +605 -0
- package/src/core/PyRuntime.ts +504 -0
- package/src/env/CondaManager.ts +451 -0
- package/src/env/PackageInstaller.ts +738 -0
- package/src/env/PythonDetector.ts +414 -0
- package/src/env/VenvManager.ts +396 -0
- package/src/index.ts +425 -0
- package/src/native/gil_guard.cpp +26 -0
- package/src/native/gil_guard.h +175 -0
- package/src/native/nodepyx_addon.cpp +886 -0
- package/src/native/python_bridge.cpp +790 -0
- package/src/native/python_bridge.h +257 -0
- package/src/native/thread_pool.cpp +336 -0
- package/src/native/thread_pool.h +175 -0
- package/src/native/type_converter.cpp +901 -0
- package/src/native/type_converter.h +272 -0
- package/src/nextjs/PyProvider.tsx +123 -0
- package/src/nextjs/index.ts +21 -0
- package/src/nextjs/usePython.ts +106 -0
- package/src/nextjs/withnodepyx.ts +88 -0
- package/src/plugins/Plugin.interface.ts +51 -0
- package/src/plugins/PluginManager.ts +155 -0
- package/src/plugins/builtin/NumpyPlugin.ts +36 -0
- package/src/plugins/builtin/PandasPlugin.ts +49 -0
- package/src/plugins/builtin/TorchPlugin.ts +56 -0
- package/src/plugins/index.ts +7 -0
- package/src/serialization/DataFrameBridge.ts +398 -0
- package/src/serialization/MsgPackSerializer.ts +220 -0
- package/src/serialization/NumpyBridge.ts +332 -0
- package/src/serialization/Serializer.ts +320 -0
- package/src/types/PythonTypeMapper.ts +495 -0
- package/src/types/StubCache.ts +340 -0
- package/src/types/TypeGenerator.ts +491 -0
- package/src/types/addon.ts +170 -0
- package/src/types/config.ts +226 -0
- package/src/types/index.ts +55 -0
- package/src/types/python.ts +309 -0
- package/src/types/stubs/numpy.d.ts +441 -0
- package/src/types/stubs/pandas.d.ts +575 -0
- package/src/types/stubs/sklearn.d.ts +728 -0
- package/src/types/stubs/torch.d.ts +694 -0
- package/src/utils/ErrorTranslator.ts +220 -0
- package/src/utils/Logger.ts +119 -0
- package/src/utils/MemoryMonitor.ts +175 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nodepyx contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# nodepyx
|
|
2
|
+
|
|
3
|
+
> **Run Python libraries from Node.js as if they were native** — embed CPython in-process with full TypeScript types, Proxy-based API, async/await support, and Next.js integration.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/nodepyx)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](https://python.org)
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
<img src="assets/images/nodepyx.jpg" width="1024" height="636">
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Why nodepyx?
|
|
17
|
+
|
|
18
|
+
Traditional Python↔Node.js bridges spawn a **separate Python process** and communicate via stdin/stdout or HTTP — introducing serialization overhead, process startup latency, and complex lifecycle management.
|
|
19
|
+
|
|
20
|
+
**nodepyx embeds CPython directly inside the Node.js process** via a native N-API addon. Every Python call happens in-process: no IPC, no child processes, no HTTP servers.
|
|
21
|
+
|
|
22
|
+
| Feature | nodepyx | python-shell / pythonia | gRPC/HTTP |
|
|
23
|
+
|---------|--------|------------------------|-----------|
|
|
24
|
+
| In-process (zero IPC) | ✅ | ❌ | ❌ |
|
|
25
|
+
| TypeScript types | ✅ Full .d.ts | Partial | Manual |
|
|
26
|
+
| `await` on Python objects | ✅ Proxy thenable | ❌ | ❌ |
|
|
27
|
+
| NumPy zero-copy | ✅ SharedArrayBuffer | ❌ | ❌ |
|
|
28
|
+
| DataFrame native | ✅ DataFrameResult | ❌ | ❌ |
|
|
29
|
+
| Next.js integration | ✅ withnodepyx() | ❌ | ❌ |
|
|
30
|
+
| Auto venv/conda | ✅ | Partial | ❌ |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install nodepyx
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { init, py } from 'nodepyx';
|
|
42
|
+
|
|
43
|
+
await init({
|
|
44
|
+
virtualenv: {
|
|
45
|
+
path: './.venv',
|
|
46
|
+
packages: ['pandas', 'numpy', 'scikit-learn'],
|
|
47
|
+
autoInstall: true,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Import Python modules — works exactly like Python `import`
|
|
52
|
+
const pd = await py.import('pandas');
|
|
53
|
+
const np = await py.import('numpy');
|
|
54
|
+
|
|
55
|
+
// Call functions with await
|
|
56
|
+
const df = await pd.read_csv('data.csv');
|
|
57
|
+
const arr = await np.arange(100); // → Float64Array
|
|
58
|
+
|
|
59
|
+
// Chain attribute access
|
|
60
|
+
const mean = await df.describe().loc['mean'];
|
|
61
|
+
|
|
62
|
+
// Iterate Python generators
|
|
63
|
+
for await (const row of df.iterrows()) {
|
|
64
|
+
console.log(row);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
### Prerequisites
|
|
73
|
+
|
|
74
|
+
- **Node.js** ≥ 18.0.0
|
|
75
|
+
- **Python** 3.8 – 3.12 (auto-detected; or set `nodepyx_PYTHON`)
|
|
76
|
+
- **C++ build tools** (for native addon)
|
|
77
|
+
- Linux: `gcc`, `g++`, `make`, `python3`
|
|
78
|
+
- macOS: Xcode Command Line Tools (`xcode-select --install`)
|
|
79
|
+
- Windows: Visual Studio Build Tools + Python
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Install from npm
|
|
83
|
+
npm install nodepyx
|
|
84
|
+
|
|
85
|
+
# Or with pre-built binary (skips compilation)
|
|
86
|
+
nodepyx_SKIP_BUILD=1 npm install nodepyx && npm run download-prebuilds
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { init } from 'nodepyx';
|
|
95
|
+
|
|
96
|
+
await init({
|
|
97
|
+
// Python executable (default: auto-detect)
|
|
98
|
+
pythonExecutable: '/usr/bin/python3.11',
|
|
99
|
+
|
|
100
|
+
// Virtualenv (recommended)
|
|
101
|
+
virtualenv: {
|
|
102
|
+
path: './.venv',
|
|
103
|
+
packages: ['pandas>=2.0', 'numpy>=1.24', 'scikit-learn'],
|
|
104
|
+
autoInstall: true, // install missing packages automatically
|
|
105
|
+
autoCreate: true, // create venv if it does not exist
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
// OR use Conda
|
|
109
|
+
conda: {
|
|
110
|
+
envName: 'my-env',
|
|
111
|
+
packages: ['pytorch', 'torchvision'],
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// Thread pool for GIL management
|
|
115
|
+
threadPoolSize: 4, // default: 4
|
|
116
|
+
|
|
117
|
+
// Logging
|
|
118
|
+
logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error'
|
|
119
|
+
|
|
120
|
+
// Memory limits
|
|
121
|
+
memory: {
|
|
122
|
+
heapLimitMB: 2048,
|
|
123
|
+
gcThresholdMB: 512,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## API Reference
|
|
131
|
+
|
|
132
|
+
### Lifecycle
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { init, shutdown, isInitialized } from 'nodepyx';
|
|
136
|
+
|
|
137
|
+
await init(config?); // Initialize Python runtime
|
|
138
|
+
await shutdown(); // Release all Python resources
|
|
139
|
+
isInitialized(); // → boolean
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### `py` — Main Proxy Object
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { py } from 'nodepyx';
|
|
146
|
+
|
|
147
|
+
// Import a module
|
|
148
|
+
const np = await py.import('numpy');
|
|
149
|
+
|
|
150
|
+
// Evaluate a Python expression
|
|
151
|
+
const result = await py.eval('1 + 2 + 3'); // → 6
|
|
152
|
+
|
|
153
|
+
// Execute statements
|
|
154
|
+
await py.exec(`
|
|
155
|
+
import sys
|
|
156
|
+
print(sys.version)
|
|
157
|
+
`);
|
|
158
|
+
|
|
159
|
+
// Run a .py file
|
|
160
|
+
await py.runFile('./my_script.py');
|
|
161
|
+
|
|
162
|
+
// Install packages at runtime
|
|
163
|
+
await py.installPackages(['requests', 'httpx']);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Proxy API
|
|
167
|
+
|
|
168
|
+
Every Python object returned by nodepyx is a **JavaScript Proxy** with full `await` support:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const pd = await py.import('pandas');
|
|
172
|
+
|
|
173
|
+
// Attribute access
|
|
174
|
+
const version = await pd.__version__; // string
|
|
175
|
+
|
|
176
|
+
// Method calls
|
|
177
|
+
const df = await pd.DataFrame({ a: [1,2,3], b: [4,5,6] });
|
|
178
|
+
const shape = await df.shape; // [3, 2]
|
|
179
|
+
const desc = await df.describe(); // DataFrameResult
|
|
180
|
+
|
|
181
|
+
// Chained calls
|
|
182
|
+
const top5 = await df.sort_values('a', { ascending: false }).head(5);
|
|
183
|
+
|
|
184
|
+
// Indexing (df['column'])
|
|
185
|
+
const col = await df['a']; // SeriesResult
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### NumPy Arrays
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const np = await py.import('numpy');
|
|
192
|
+
const arr = await np.linspace(0, 1, 100); // NumPyArrayResult
|
|
193
|
+
|
|
194
|
+
// NumPyArrayResult interface:
|
|
195
|
+
arr.data // Float64Array (or Int32Array, Uint8Array, etc.)
|
|
196
|
+
arr.shape // number[] — e.g. [100] or [3, 4]
|
|
197
|
+
arr.dtype // 'float64' | 'float32' | 'int32' | ...
|
|
198
|
+
arr.ndim // number
|
|
199
|
+
arr.size // number — total element count
|
|
200
|
+
|
|
201
|
+
// Send TypedArrays back to Python
|
|
202
|
+
const NumpyBridge = new NumpyBridge();
|
|
203
|
+
const wire = NumpyBridge.serializeTypedArray(new Float64Array([1,2,3]), { dtype:'float64', itemsize:8 }, [3]);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Pandas DataFrames
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const pd = await py.import('pandas');
|
|
210
|
+
const df = await pd.read_csv('data.csv'); // DataFrameResult
|
|
211
|
+
|
|
212
|
+
// DataFrameResult interface:
|
|
213
|
+
df.columns // string[]
|
|
214
|
+
df.records // Record<string, unknown>[] — one object per row
|
|
215
|
+
df.index // unknown[]
|
|
216
|
+
df.dtypes // Record<string, string>
|
|
217
|
+
df.shape // [rows, cols]
|
|
218
|
+
|
|
219
|
+
// Static helpers
|
|
220
|
+
import { DataFrameBridge } from 'nodepyx';
|
|
221
|
+
const cols = DataFrameBridge.toColumnArrays(df); // column-keyed arrays
|
|
222
|
+
const filtered = DataFrameBridge.filterRows(df, row => row.score > 0.9);
|
|
223
|
+
const stats = DataFrameBridge.describeColumn(df, 'price'); // {count,mean,std,min,max}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Error Handling
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { PythonError, isPythonError, isPythonErrorOfType } from 'nodepyx';
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
await py.eval('1 / 0');
|
|
233
|
+
} catch (err) {
|
|
234
|
+
if (isPythonError(err)) {
|
|
235
|
+
console.log(err.pythonType); // 'ZeroDivisionError'
|
|
236
|
+
console.log(err.message); // '[Python ZeroDivisionError] division by zero'
|
|
237
|
+
console.log(err.traceback); // Full Python traceback
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Type-specific check
|
|
242
|
+
if (isPythonErrorOfType(err, 'ImportError')) { ... }
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Environment Management
|
|
248
|
+
|
|
249
|
+
### Virtualenv (recommended)
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { VenvManager } from 'nodepyx';
|
|
253
|
+
|
|
254
|
+
const mgr = new VenvManager({
|
|
255
|
+
venvPath: './.venv',
|
|
256
|
+
packages: ['pandas', 'numpy', 'scikit-learn'],
|
|
257
|
+
autoCreate: true,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const result = await mgr.setup();
|
|
261
|
+
console.log('Installed:', result.installedPackages);
|
|
262
|
+
console.log('Venv python:', result.pythonExecutable);
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Conda
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { CondaManager } from 'nodepyx';
|
|
269
|
+
|
|
270
|
+
const conda = new CondaManager({ envName: 'ml-env', packages: ['pytorch', 'pip::some-pip-package'] });
|
|
271
|
+
const result = await conda.setup();
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### PackageInstaller
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { PackageInstaller } from 'nodepyx';
|
|
278
|
+
|
|
279
|
+
const pip = new PackageInstaller({ pythonExecutable: '/usr/bin/python3' });
|
|
280
|
+
|
|
281
|
+
// Install with progress events
|
|
282
|
+
pip.on('progress', (ev) => console.log(ev.package, ev.status));
|
|
283
|
+
|
|
284
|
+
const result = await pip.install(['pandas>=2.0', 'numpy>=1.24']);
|
|
285
|
+
console.log('installed:', result.installed);
|
|
286
|
+
console.log('already present:', result.alreadyInstalled);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## TypeScript Stubs
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Generate .d.ts stubs for Python modules
|
|
295
|
+
npx nodepyx-stubs pandas numpy sklearn torch
|
|
296
|
+
|
|
297
|
+
# Regenerate all cached stubs
|
|
298
|
+
npx nodepyx-stubs --all
|
|
299
|
+
|
|
300
|
+
# List cached stubs
|
|
301
|
+
npx nodepyx-stubs --list
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Then in your TypeScript code:
|
|
305
|
+
```typescript
|
|
306
|
+
import type { DataFrame } from 'nodepyx/pandas'; // full IDE autocomplete
|
|
307
|
+
import type { ndarray } from 'nodepyx/numpy';
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Next.js Integration
|
|
313
|
+
|
|
314
|
+
```js
|
|
315
|
+
// next.config.js
|
|
316
|
+
const { withnodepyx } = require('nodepyx/next');
|
|
317
|
+
|
|
318
|
+
module.exports = withnodepyx({
|
|
319
|
+
// your Next.js config
|
|
320
|
+
}, {
|
|
321
|
+
virtualenv: { path: './.venv', packages: ['pandas'], autoInstall: true },
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
// app/api/python/route.ts (Server Route — Node.js runtime only)
|
|
327
|
+
import { NextResponse } from 'next/server';
|
|
328
|
+
import { init, evalPython } from 'nodepyx';
|
|
329
|
+
|
|
330
|
+
let ready = false;
|
|
331
|
+
async function ensureReady() {
|
|
332
|
+
if (!ready) { await init(); ready = true; }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export async function GET() {
|
|
336
|
+
await ensureReady();
|
|
337
|
+
const result = await evalPython('list(range(10))');
|
|
338
|
+
return NextResponse.json(result);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
// Client component
|
|
344
|
+
'use client';
|
|
345
|
+
import { usePython } from 'nodepyx/next';
|
|
346
|
+
|
|
347
|
+
export function Chart() {
|
|
348
|
+
const { data, loading, run } = usePython(async (py) => {
|
|
349
|
+
const np = await py.import('numpy');
|
|
350
|
+
return (np as any).random.randn(100).tolist();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
return <button onClick={run}>{loading ? '…' : 'Generate'}</button>;
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Plugins
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { init } from 'nodepyx';
|
|
363
|
+
import type { nodepyxPlugin } from 'nodepyx';
|
|
364
|
+
|
|
365
|
+
const myPlugin: nodepyxPlugin = {
|
|
366
|
+
name: 'my-plugin',
|
|
367
|
+
version: '1.0.0',
|
|
368
|
+
supportedModules: ['my_lib'],
|
|
369
|
+
handledTypes: ['MySpecialType'],
|
|
370
|
+
|
|
371
|
+
async onInit(ctx) {
|
|
372
|
+
ctx.registerTypeHandler('MySpecialType', (raw, typeHint) => {
|
|
373
|
+
// Transform raw wire dict into a JS value
|
|
374
|
+
return { specialData: JSON.parse(raw.data as string) };
|
|
375
|
+
});
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
await init({ plugins: [myPlugin] });
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Supported Python Versions
|
|
385
|
+
|
|
386
|
+
| Python | Status |
|
|
387
|
+
|--------|--------|
|
|
388
|
+
| 3.12 | ✅ Recommended |
|
|
389
|
+
| 3.11 | ✅ Supported |
|
|
390
|
+
| 3.10 | ✅ Supported |
|
|
391
|
+
| 3.9 | ✅ Supported |
|
|
392
|
+
| 3.8 | ✅ Supported (EOL) |
|
|
393
|
+
| 3.7 | ❌ Not supported |
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## License
|
|
398
|
+
|
|
399
|
+
MIT © nodepyx contributors
|
package/binding.gyp
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [{
|
|
3
|
+
"target_name": "nodepyx_addon",
|
|
4
|
+
"sources": [
|
|
5
|
+
"src/native/nodepyx_addon.cpp",
|
|
6
|
+
"src/native/python_bridge.cpp",
|
|
7
|
+
"src/native/type_converter.cpp",
|
|
8
|
+
"src/native/thread_pool.cpp",
|
|
9
|
+
"src/native/gil_guard.cpp"
|
|
10
|
+
],
|
|
11
|
+
"include_dirs": [
|
|
12
|
+
"<!@(node -p \"require('node-addon-api').include\")"
|
|
13
|
+
],
|
|
14
|
+
"cflags_cc": [
|
|
15
|
+
"-std=c++17",
|
|
16
|
+
"-O2",
|
|
17
|
+
"-Wall",
|
|
18
|
+
"-Wextra",
|
|
19
|
+
"-Wno-unused-parameter"
|
|
20
|
+
],
|
|
21
|
+
"defines": [
|
|
22
|
+
"NAPI_DISABLE_CPP_EXCEPTIONS",
|
|
23
|
+
"NAPI_VERSION=8",
|
|
24
|
+
"PY_SSIZE_T_CLEAN"
|
|
25
|
+
],
|
|
26
|
+
"conditions": [
|
|
27
|
+
["OS=='linux'", {
|
|
28
|
+
"include_dirs+": [
|
|
29
|
+
"<!@(python3-config --includes 2>/dev/null | sed 's/-I//g' | tr ' ' '\\n' | head -1)"
|
|
30
|
+
],
|
|
31
|
+
"libraries": [
|
|
32
|
+
"<!@(python3-config --ldflags 2>/dev/null)",
|
|
33
|
+
"<!@(python3-config --libs 2>/dev/null)"
|
|
34
|
+
],
|
|
35
|
+
"cflags_cc": [
|
|
36
|
+
"-fno-exceptions",
|
|
37
|
+
"-fvisibility=hidden"
|
|
38
|
+
]
|
|
39
|
+
}],
|
|
40
|
+
["OS=='mac'", {
|
|
41
|
+
"include_dirs+": [
|
|
42
|
+
"<!@(python3-config --includes 2>/dev/null | sed 's/-I//g' | tr ' ' '\\n' | head -1)"
|
|
43
|
+
],
|
|
44
|
+
"libraries": [
|
|
45
|
+
"<!@(python3-config --ldflags 2>/dev/null)"
|
|
46
|
+
],
|
|
47
|
+
"xcode_settings": {
|
|
48
|
+
"MACOSX_DEPLOYMENT_TARGET": "11.0",
|
|
49
|
+
"OTHER_CFLAGS": ["-std=c++17", "-stdlib=libc++"],
|
|
50
|
+
"GCC_ENABLE_CPP_EXCEPTIONS": "NO",
|
|
51
|
+
"OTHER_LDFLAGS": [
|
|
52
|
+
"<!@(python3-config --libs 2>/dev/null)"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}],
|
|
56
|
+
["OS=='win'", {
|
|
57
|
+
"include_dirs+": [
|
|
58
|
+
"<!@(python -c \"import sysconfig; print(sysconfig.get_path('include'))\" 2>nul)"
|
|
59
|
+
],
|
|
60
|
+
"libraries": [
|
|
61
|
+
"<!@(python -c \"import sysconfig; import os; print(os.path.join(sysconfig.get_config_var('LIBDIR') or '', sysconfig.get_config_var('LDLIBRARY') or 'python3.lib'))\" 2>nul)"
|
|
62
|
+
],
|
|
63
|
+
"msvs_settings": {
|
|
64
|
+
"VCCLCompilerTool": {
|
|
65
|
+
"ExceptionHandling": 0,
|
|
66
|
+
"AdditionalOptions": ["/std:c++17"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}]
|
|
70
|
+
]
|
|
71
|
+
}]
|
|
72
|
+
}
|
|
73
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodepyx — PyCallable
|
|
3
|
+
* Represents a Python callable (function, method, class constructor).
|
|
4
|
+
* Extends PyProxy with call metadata and convenience methods.
|
|
5
|
+
*/
|
|
6
|
+
import { PyProxy } from './PyProxy';
|
|
7
|
+
import type { NativeAddon } from '../types/addon';
|
|
8
|
+
import type { PyObjectId, AttributePath, PyCallableInfo } from '../types/python';
|
|
9
|
+
/**
|
|
10
|
+
* PyCallable — wraps a Python function or method.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const np = await py.import('numpy');
|
|
15
|
+
* const zeros = await np.zeros; // PyCallable
|
|
16
|
+
* const arr = await zeros([3, 3], { dtype: 'float64' });
|
|
17
|
+
*
|
|
18
|
+
* // Or more concisely:
|
|
19
|
+
* const arr2 = await np.zeros([3, 3]);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare class PyCallable extends PyProxy {
|
|
23
|
+
private readonly _callableName;
|
|
24
|
+
private _info;
|
|
25
|
+
constructor(objectId: PyObjectId, path: AttributePath, callableName: string, addon: NativeAddon, callTimeout?: number);
|
|
26
|
+
/** Callable name */
|
|
27
|
+
get callableName(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Call the callable with given arguments.
|
|
30
|
+
* Same as using the callable directly (proxy syntax).
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const result = await callable.invoke([1, 2, 3], { axis: 0 });
|
|
35
|
+
* // equivalent to: await callable([1, 2, 3], { axis: 0 })
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
invoke(...args: unknown[]): Promise<unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* Call with explicit positional args and keyword args.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const result = await callable.call([1, 2, 3], { axis: 0, keepdims: true });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
call(args: unknown[], kwargs?: Record<string, unknown>): Promise<unknown>;
|
|
48
|
+
/**
|
|
49
|
+
* Partially apply arguments (like functools.partial in Python).
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const readCsvUtf8 = await pd.read_csv.partial({ encoding: 'utf-8' });
|
|
54
|
+
* const df = await readCsvUtf8('data.csv');
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
partial(...partialArgs: unknown[]): PyCallable;
|
|
58
|
+
/**
|
|
59
|
+
* Get introspection information about this callable.
|
|
60
|
+
*/
|
|
61
|
+
getInfo(): Promise<PyCallableInfo | null>;
|
|
62
|
+
static fromProxy(proxy: PyProxy, name: string): PyCallable;
|
|
63
|
+
toString(): string;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=PyCallable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PyCallable.d.ts","sourceRoot":"","sources":["../../src/core/PyCallable.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAoB,MAAM,WAAW,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjF;;;;;;;;;;;;GAYG;AACH,qBAAa,UAAW,SAAQ,OAAO;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,KAAK,CAA+B;gBAG1C,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,aAAa,EACnB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,WAAW,EAClB,WAAW,CAAC,EAAE,MAAM;IAatB,oBAAoB;IACpB,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;;;;;;;OASG;IACG,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlD;;;;;;;OAOG;IACG,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAO/E;;;;;;;;OAQG;IACH,OAAO,CAAC,GAAG,WAAW,EAAE,OAAO,EAAE,GAAG,UAAU;IAuB9C;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAO/C,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU;IAWjD,QAAQ,IAAI,MAAM;CAG5B"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* nodepyx — PyCallable
|
|
4
|
+
* Represents a Python callable (function, method, class constructor).
|
|
5
|
+
* Extends PyProxy with call metadata and convenience methods.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.PyCallable = void 0;
|
|
9
|
+
const PyProxy_1 = require("./PyProxy");
|
|
10
|
+
/**
|
|
11
|
+
* PyCallable — wraps a Python function or method.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const np = await py.import('numpy');
|
|
16
|
+
* const zeros = await np.zeros; // PyCallable
|
|
17
|
+
* const arr = await zeros([3, 3], { dtype: 'float64' });
|
|
18
|
+
*
|
|
19
|
+
* // Or more concisely:
|
|
20
|
+
* const arr2 = await np.zeros([3, 3]);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
class PyCallable extends PyProxy_1.PyProxy {
|
|
24
|
+
_callableName;
|
|
25
|
+
_info = null;
|
|
26
|
+
constructor(objectId, path, callableName, addon, callTimeout) {
|
|
27
|
+
super({
|
|
28
|
+
objectId,
|
|
29
|
+
path,
|
|
30
|
+
addon,
|
|
31
|
+
isResolved: false,
|
|
32
|
+
callTimeout,
|
|
33
|
+
childCache: new Map(),
|
|
34
|
+
});
|
|
35
|
+
this._callableName = callableName;
|
|
36
|
+
}
|
|
37
|
+
/** Callable name */
|
|
38
|
+
get callableName() {
|
|
39
|
+
return this._callableName;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Call the callable with given arguments.
|
|
43
|
+
* Same as using the callable directly (proxy syntax).
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const result = await callable.invoke([1, 2, 3], { axis: 0 });
|
|
48
|
+
* // equivalent to: await callable([1, 2, 3], { axis: 0 })
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
async invoke(...args) {
|
|
52
|
+
return this(...args);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Call with explicit positional args and keyword args.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const result = await callable.call([1, 2, 3], { axis: 0, keepdims: true });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
async call(args, kwargs) {
|
|
63
|
+
if (kwargs) {
|
|
64
|
+
return this(...args, kwargs);
|
|
65
|
+
}
|
|
66
|
+
return this(...args);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Partially apply arguments (like functools.partial in Python).
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const readCsvUtf8 = await pd.read_csv.partial({ encoding: 'utf-8' });
|
|
74
|
+
* const df = await readCsvUtf8('data.csv');
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
partial(...partialArgs) {
|
|
78
|
+
const internal = this[PyProxy_1.PYPROXY_INTERNAL];
|
|
79
|
+
const callableName = `${this._callableName}(partial)`;
|
|
80
|
+
const wrapper = new PyCallable(internal.objectId, internal.path, callableName, internal.addon, internal.callTimeout);
|
|
81
|
+
// Override: always prepend partialArgs (use arrow to capture outer `this` lexically)
|
|
82
|
+
const callSelf = (...a) => this(...a);
|
|
83
|
+
const overrideProxy = new Proxy(wrapper, {
|
|
84
|
+
apply(_target, _thisArg, args) {
|
|
85
|
+
return callSelf(...partialArgs, ...args);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return overrideProxy;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get introspection information about this callable.
|
|
92
|
+
*/
|
|
93
|
+
async getInfo() {
|
|
94
|
+
if (this._info) {
|
|
95
|
+
return this._info;
|
|
96
|
+
}
|
|
97
|
+
// Not yet implemented — would require Python introspection
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
static fromProxy(proxy, name) {
|
|
101
|
+
const internal = proxy[PyProxy_1.PYPROXY_INTERNAL];
|
|
102
|
+
return new PyCallable(internal.objectId, internal.path, name, internal.addon, internal.callTimeout);
|
|
103
|
+
}
|
|
104
|
+
toString() {
|
|
105
|
+
return `[PyCallable '${this._callableName}' id=${this[PyProxy_1.PYPROXY_INTERNAL].objectId}]`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.PyCallable = PyCallable;
|
|
109
|
+
//# sourceMappingURL=PyCallable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PyCallable.js","sourceRoot":"","sources":["../../src/core/PyCallable.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,uCAAsD;AAItD;;;;;;;;;;;;GAYG;AACH,MAAa,UAAW,SAAQ,iBAAO;IACpB,aAAa,CAAS;IAC/B,KAAK,GAA0B,IAAI,CAAC;IAE5C,YACE,QAAoB,EACpB,IAAmB,EACnB,YAAoB,EACpB,KAAkB,EAClB,WAAoB;QAEpB,KAAK,CAAC;YACJ,QAAQ;YACR,IAAI;YACJ,KAAK;YACL,UAAU,EAAE,KAAK;YACjB,WAAW;YACX,UAAU,EAAE,IAAI,GAAG,EAAE;SACtB,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;IACpC,CAAC;IAED,oBAAoB;IACpB,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,MAAM,CAAC,GAAG,IAAe;QAC7B,OAAQ,IAAgD,CAAC,GAAG,IAAI,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,CAAC,IAAe,EAAE,MAAgC;QAC1D,IAAI,MAAM,EAAE,CAAC;YACX,OAAQ,IAAgD,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5E,CAAC;QACD,OAAQ,IAAgD,CAAC,GAAG,IAAI,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,GAAG,WAAsB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,0BAAgB,CAAC,CAAC;QACxC,MAAM,YAAY,GAAG,GAAG,IAAI,CAAC,aAAa,WAAW,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,UAAU,CAC5B,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,IAAI,EACb,YAAY,EACZ,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,WAAW,CACrB,CAAC;QAEF,qFAAqF;QACrF,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAY,EAAE,EAAE,CAClC,IAAgD,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE;YACvC,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAe;gBACtC,OAAO,QAAQ,CAAC,GAAG,WAAW,EAAE,GAAG,IAAI,CAAC,CAAC;YAC3C,CAAC;SACF,CAAC,CAAC;QAEH,OAAO,aAA2B,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAAA,OAAO,IAAI,CAAC,KAAK,CAAC;QAAA,CAAC;QAEpC,6DAA6D;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,SAAS,CAAC,KAAc,EAAE,IAAY;QAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,0BAAgB,CAAC,CAAC;QACzC,OAAO,IAAI,UAAU,CACnB,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,IAAI,EACb,IAAI,EACJ,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,WAAW,CACrB,CAAC;IACJ,CAAC;IAEQ,QAAQ;QACf,OAAO,gBAAgB,IAAI,CAAC,aAAa,QAAQ,IAAI,CAAC,0BAAgB,CAAC,CAAC,QAAQ,GAAG,CAAC;IACtF,CAAC;CACF;AAhHD,gCAgHC"}
|