@swissjs/swite 0.3.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/.changeset/config.json +11 -0
- package/.github/workflows/ci.yml +59 -0
- package/.github/workflows/publish.yml +50 -0
- package/.github/workflows/release.yml +53 -0
- package/BUILD_ANALYSIS.md +89 -0
- package/BUILD_STRATEGY.md +75 -0
- package/CHANGELOG.md +53 -0
- package/DIRECTIVE.md +488 -0
- package/__tests__/css-extraction.test.ts +261 -0
- package/__tests__/css-injection-integration.test.ts +247 -0
- package/__tests__/css-middleware.test.ts +191 -0
- package/__tests__/import-rewriter-bug.test.ts +135 -0
- package/dist/builder.d.ts +36 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +772 -0
- package/dist/cache/compilation-cache.d.ts +33 -0
- package/dist/cache/compilation-cache.d.ts.map +1 -0
- package/dist/cache/compilation-cache.js +130 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +85 -0
- package/dist/config-loader.d.ts +8 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +40 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +7 -0
- package/dist/dev/pythonDevManager.d.ts +12 -0
- package/dist/dev/pythonDevManager.d.ts.map +1 -0
- package/dist/dev/pythonDevManager.js +85 -0
- package/dist/env.d.ts +19 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +112 -0
- package/dist/handlers/base-handler.d.ts +21 -0
- package/dist/handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/base-handler.js +38 -0
- package/dist/handlers/js-handler.d.ts +10 -0
- package/dist/handlers/js-handler.d.ts.map +1 -0
- package/dist/handlers/js-handler.js +87 -0
- package/dist/handlers/mjs-handler.d.ts +8 -0
- package/dist/handlers/mjs-handler.d.ts.map +1 -0
- package/dist/handlers/mjs-handler.js +44 -0
- package/dist/handlers/node-module-handler.d.ts +16 -0
- package/dist/handlers/node-module-handler.d.ts.map +1 -0
- package/dist/handlers/node-module-handler.js +267 -0
- package/dist/handlers/ts-handler.d.ts +11 -0
- package/dist/handlers/ts-handler.d.ts.map +1 -0
- package/dist/handlers/ts-handler.js +120 -0
- package/dist/handlers/ui-handler.d.ts +12 -0
- package/dist/handlers/ui-handler.d.ts.map +1 -0
- package/dist/handlers/ui-handler.js +182 -0
- package/dist/handlers/uix-handler.d.ts +12 -0
- package/dist/handlers/uix-handler.d.ts.map +1 -0
- package/dist/handlers/uix-handler.js +135 -0
- package/dist/hmr.d.ts +20 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hmr.js +265 -0
- package/dist/import-rewriter.d.ts +3 -0
- package/dist/import-rewriter.d.ts.map +1 -0
- package/dist/import-rewriter.js +351 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/middleware/hmr-routes.d.ts +12 -0
- package/dist/middleware/hmr-routes.d.ts.map +1 -0
- package/dist/middleware/hmr-routes.js +97 -0
- package/dist/middleware/middleware-setup.d.ts +23 -0
- package/dist/middleware/middleware-setup.d.ts.map +1 -0
- package/dist/middleware/middleware-setup.js +596 -0
- package/dist/middleware/static-files.d.ts +15 -0
- package/dist/middleware/static-files.d.ts.map +1 -0
- package/dist/middleware/static-files.js +585 -0
- package/dist/proxy/SwiteProxyError.d.ts +6 -0
- package/dist/proxy/SwiteProxyError.d.ts.map +1 -0
- package/dist/proxy/SwiteProxyError.js +9 -0
- package/dist/proxy/proxyToPython.d.ts +28 -0
- package/dist/proxy/proxyToPython.d.ts.map +1 -0
- package/dist/proxy/proxyToPython.js +66 -0
- package/dist/resolver/bare-import-resolver.d.ts +9 -0
- package/dist/resolver/bare-import-resolver.d.ts.map +1 -0
- package/dist/resolver/bare-import-resolver.js +363 -0
- package/dist/resolver/symlink-registry.d.ts +13 -0
- package/dist/resolver/symlink-registry.d.ts.map +1 -0
- package/dist/resolver/symlink-registry.js +98 -0
- package/dist/resolver/url-resolver.d.ts +11 -0
- package/dist/resolver/url-resolver.d.ts.map +1 -0
- package/dist/resolver/url-resolver.js +268 -0
- package/dist/resolver/workspace-package-resolver.d.ts +10 -0
- package/dist/resolver/workspace-package-resolver.d.ts.map +1 -0
- package/dist/resolver/workspace-package-resolver.js +185 -0
- package/dist/resolver.d.ts +17 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +191 -0
- package/dist/router/file-router.d.ts +19 -0
- package/dist/router/file-router.d.ts.map +1 -0
- package/dist/router/file-router.js +114 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +122 -0
- package/dist/utils/cdn-fallback.d.ts +14 -0
- package/dist/utils/cdn-fallback.d.ts.map +1 -0
- package/dist/utils/cdn-fallback.js +36 -0
- package/dist/utils/file-path-resolver.d.ts +9 -0
- package/dist/utils/file-path-resolver.d.ts.map +1 -0
- package/dist/utils/file-path-resolver.js +187 -0
- package/dist/utils/generate-import-map-cli.d.ts +3 -0
- package/dist/utils/generate-import-map-cli.d.ts.map +1 -0
- package/dist/utils/generate-import-map-cli.js +32 -0
- package/dist/utils/generate-import-map.d.ts +21 -0
- package/dist/utils/generate-import-map.d.ts.map +1 -0
- package/dist/utils/generate-import-map.js +119 -0
- package/dist/utils/package-finder.d.ts +24 -0
- package/dist/utils/package-finder.d.ts.map +1 -0
- package/dist/utils/package-finder.js +161 -0
- package/dist/utils/package-registry.d.ts +36 -0
- package/dist/utils/package-registry.d.ts.map +1 -0
- package/dist/utils/package-registry.js +159 -0
- package/dist/utils/workspace.d.ts +6 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +65 -0
- package/docs/IMPORT_REWRITING.md +164 -0
- package/docs/IMPORT_REWRITING_TROUBLESHOOTING.md +139 -0
- package/docs/PATH_RESOLUTION_GUIDE.md +221 -0
- package/package.json +49 -0
- package/src/adapters/proxy/SwiteProxyError.ts +12 -0
- package/src/adapters/proxy/proxyToPython.ts +88 -0
- package/src/build-engine/builder.ts +960 -0
- package/src/cli.ts +109 -0
- package/src/config/config-loader.ts +46 -0
- package/src/config/config.ts +34 -0
- package/src/config/env.ts +98 -0
- package/src/dev-engine/handlers/base-handler.ts +68 -0
- package/src/dev-engine/handlers/js-handler.ts +134 -0
- package/src/dev-engine/handlers/mjs-handler.ts +65 -0
- package/src/dev-engine/handlers/node-module-handler.ts +339 -0
- package/src/dev-engine/handlers/ts-handler.ts +143 -0
- package/src/dev-engine/handlers/ui-handler.ts +105 -0
- package/src/dev-engine/handlers/uix-handler.ts +90 -0
- package/src/dev-engine/hmr/hmr-client-template.ts +122 -0
- package/src/dev-engine/hmr/hmr.ts +173 -0
- package/src/dev-engine/middleware/hmr-routes.ts +120 -0
- package/src/dev-engine/middleware/middleware-setup.ts +351 -0
- package/src/dev-engine/middleware/static-files.ts +728 -0
- package/src/dev-engine/pythonDevManager.ts +116 -0
- package/src/dev-engine/router/file-router.ts +164 -0
- package/src/dev-engine/server.ts +152 -0
- package/src/index.ts +26 -0
- package/src/internal/cache/compilation-cache.ts +182 -0
- package/src/internal/generate-import-map-cli.ts +40 -0
- package/src/internal/generate-import-map.ts +154 -0
- package/src/kernel/package-finder.ts +164 -0
- package/src/kernel/package-registry.ts +198 -0
- package/src/kernel/workspace.ts +62 -0
- package/src/resolution/bare-import-resolver.ts +400 -0
- package/src/resolution/cdn/cdn-fallback.ts +37 -0
- package/src/resolution/path/file-path-resolver.ts +190 -0
- package/src/resolution/path/path-fixup.ts +19 -0
- package/src/resolution/resolver.ts +198 -0
- package/src/resolution/rewriting/import-rewriter.ts +237 -0
- package/src/resolution/symlink-registry.ts +114 -0
- package/src/resolution/url-resolver.ts +231 -0
- package/src/resolution/workspace-package-resolver.ts +94 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Path Resolution Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
SWITE must correctly resolve URL paths to filesystem paths for different types of resources:
|
|
6
|
+
- **App files**: `/src/index.ui` → `apps/alpine/src/index.ui`
|
|
7
|
+
- **Workspace packages**: `/packages/ai-agents/...` → `SwissEnterpriseRepo/packages/ai-agents/...`
|
|
8
|
+
- **Business modules**: `/businessModules/cart/...` → `SwissEnterpriseRepo/businessModules/cart/...`
|
|
9
|
+
- **SWISS packages**: `/swiss-packages/core/...` → `SWISS/packages/core/...`
|
|
10
|
+
|
|
11
|
+
## Common Issues
|
|
12
|
+
|
|
13
|
+
### Issue 1: 500 Errors for `/businessModules/` Files
|
|
14
|
+
|
|
15
|
+
**Browser Error:**
|
|
16
|
+
```
|
|
17
|
+
GET http://localhost:3001/businessModules/cart/src/context/PosContext.js
|
|
18
|
+
net::ERR_ABORTED 500 (Internal Server Error)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Server Error:**
|
|
22
|
+
```
|
|
23
|
+
Error: File not found: /businessModules/cart/src/context/PosContext.js (tried .js, .ts, .ui, .uix)
|
|
24
|
+
filePath was: C:\...\apps\alpine\businessModules\cart\src\context\PosContext.js
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Root Cause:**
|
|
28
|
+
|
|
29
|
+
The file resolver was looking in the WRONG location:
|
|
30
|
+
- ❌ Looking in: `apps/alpine/businessModules/cart/...`
|
|
31
|
+
- ✅ Should look in: `SwissEnterpriseRepo/businessModules/cart/...` (workspace root)
|
|
32
|
+
|
|
33
|
+
**Why This Happened:**
|
|
34
|
+
|
|
35
|
+
The `file-path-resolver.ts` only had special handling for `/lib/` and `/packages/` paths. When a URL like `/businessModules/cart/...` was requested, it fell through to generic path resolution logic that incorrectly resolved to the app directory instead of workspace root.
|
|
36
|
+
|
|
37
|
+
**The Fix:**
|
|
38
|
+
|
|
39
|
+
Added `/businessModules/` to the workspace-level directory list in `src/utils/file-path-resolver.ts`:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// Workspace-level directories: always resolve from workspace root
|
|
43
|
+
if (url.startsWith("/lib/") ||
|
|
44
|
+
url.startsWith("/packages/") ||
|
|
45
|
+
url.startsWith("/businessModules/")) {
|
|
46
|
+
const wsRoot = workspaceRoot || (await findWorkspaceRoot(root));
|
|
47
|
+
if (wsRoot) {
|
|
48
|
+
return path.join(wsRoot, url);
|
|
49
|
+
} else {
|
|
50
|
+
return path.join(root, url);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Testing:**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Should return compiled JavaScript code
|
|
59
|
+
curl http://localhost:3001/businessModules/cart/src/context/PosContext.js
|
|
60
|
+
|
|
61
|
+
# Should NOT return error
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Path Resolution Rules
|
|
65
|
+
|
|
66
|
+
### 1. SWISS Packages (`/swiss-packages/*`)
|
|
67
|
+
|
|
68
|
+
**URL Pattern:** `/swiss-packages/core/src/index.ts`
|
|
69
|
+
|
|
70
|
+
**Resolution:**
|
|
71
|
+
1. Try: `SWS/SWISS/packages/core/src/index.ts`
|
|
72
|
+
2. Try: Alternative SWISS monorepo locations
|
|
73
|
+
3. Fallback: First path even if file doesn't exist (will error later)
|
|
74
|
+
|
|
75
|
+
**Location:** Always in SWISS monorepo, not SwissEnterpriseRepo
|
|
76
|
+
|
|
77
|
+
### 2. Workspace Packages (`/packages/*`, `/lib/*`, `/businessModules/*`)
|
|
78
|
+
|
|
79
|
+
**URL Pattern:**
|
|
80
|
+
- `/packages/ai-agents/src/index.ui`
|
|
81
|
+
- `/lib/skltn/src/index.ui`
|
|
82
|
+
- `/businessModules/cart/src/context/PosContext.uix`
|
|
83
|
+
|
|
84
|
+
**Resolution:**
|
|
85
|
+
1. Find workspace root (pnpm-workspace.yaml)
|
|
86
|
+
2. Join: `{workspaceRoot}{url}`
|
|
87
|
+
3. Example: `SwissEnterpriseRepo` + `/packages/ai-agents/...`
|
|
88
|
+
|
|
89
|
+
**Location:** Always at workspace root
|
|
90
|
+
|
|
91
|
+
### 3. App Files (`/src/*`, `/public/*`, `/assets/*`)
|
|
92
|
+
|
|
93
|
+
**URL Pattern:** `/src/index.ui`
|
|
94
|
+
|
|
95
|
+
**Resolution:**
|
|
96
|
+
1. Join: `{appRoot}{url}`
|
|
97
|
+
2. Example: `apps/alpine` + `/src/index.ui`
|
|
98
|
+
|
|
99
|
+
**Location:** Always in app directory
|
|
100
|
+
|
|
101
|
+
### 4. Generic Absolute URLs (`/*`)
|
|
102
|
+
|
|
103
|
+
**URL Pattern:** `/some/path/file.js`
|
|
104
|
+
|
|
105
|
+
**Resolution:**
|
|
106
|
+
1. Try: workspace root first
|
|
107
|
+
2. Check if file exists
|
|
108
|
+
3. Fallback: app root
|
|
109
|
+
|
|
110
|
+
**Location:** Depends on file existence
|
|
111
|
+
|
|
112
|
+
## Extension Resolution
|
|
113
|
+
|
|
114
|
+
The handlers try multiple extensions when a file isn't found:
|
|
115
|
+
|
|
116
|
+
**For `.js` URL:** Try in order:
|
|
117
|
+
1. `.js` - Actual JavaScript file
|
|
118
|
+
2. `.ts` - TypeScript source (compiled on-the-fly)
|
|
119
|
+
3. `.ui` - Swiss UI component (compiled on-the-fly)
|
|
120
|
+
4. `.uix` - Swiss UIX component (compiled on-the-fly)
|
|
121
|
+
|
|
122
|
+
**Example:**
|
|
123
|
+
- Request: `/businessModules/cart/src/context/PosContext.js`
|
|
124
|
+
- File exists as: `.../PosContext.uix`
|
|
125
|
+
- Handler compiles `.uix` → JavaScript and returns it
|
|
126
|
+
|
|
127
|
+
## Debugging Path Resolution
|
|
128
|
+
|
|
129
|
+
### 1. Check Server Logs
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
tail -f /tmp/alpine-dev.log | grep "file-path-resolver"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Look for:
|
|
136
|
+
```
|
|
137
|
+
[file-path-resolver] Found SWISS package at: ...
|
|
138
|
+
[file-path-resolver] SWISS package not found, using: ...
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 2. Check Handler Logs
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
tail -f /tmp/alpine-dev.log | grep "\[.js\]"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Look for:
|
|
148
|
+
```
|
|
149
|
+
[.js→.ts] C:\...\file.ts not found, trying next...
|
|
150
|
+
[.js→.ui] C:\...\file.ui not found, trying next...
|
|
151
|
+
[.js→.uix] C:\...\file.uix not found, trying next...
|
|
152
|
+
[.js] File not found: /path/to/file.js (tried .js, .ts, .ui, .uix)
|
|
153
|
+
[.js] filePath was: C:\actual\path\tried
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 3. Test Specific URLs
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Test workspace package
|
|
160
|
+
curl -I http://localhost:3001/packages/ai-agents/src/index.ui
|
|
161
|
+
|
|
162
|
+
# Test business module
|
|
163
|
+
curl -I http://localhost:3001/businessModules/cart/src/context/PosContext.js
|
|
164
|
+
|
|
165
|
+
# Test SWISS package
|
|
166
|
+
curl -I http://localhost:3001/swiss-packages/core/src/index.ts
|
|
167
|
+
|
|
168
|
+
# Test app file
|
|
169
|
+
curl -I http://localhost:3001/src/index.ui
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Expected: `200 OK` for all valid paths
|
|
173
|
+
|
|
174
|
+
## Adding New Workspace Directories
|
|
175
|
+
|
|
176
|
+
If you add a new workspace-level directory (like `/modules/` or `/components/`), add it to the resolver:
|
|
177
|
+
|
|
178
|
+
1. **Edit:** `src/utils/file-path-resolver.ts`
|
|
179
|
+
2. **Add to condition:**
|
|
180
|
+
```typescript
|
|
181
|
+
if (url.startsWith("/lib/") ||
|
|
182
|
+
url.startsWith("/packages/") ||
|
|
183
|
+
url.startsWith("/businessModules/") ||
|
|
184
|
+
url.startsWith("/YOUR_NEW_DIR/")) {
|
|
185
|
+
```
|
|
186
|
+
3. **Rebuild:** `pnpm build`
|
|
187
|
+
4. **Test:** Restart dev server and test URLs
|
|
188
|
+
|
|
189
|
+
## Static Files vs. Compiled Files
|
|
190
|
+
|
|
191
|
+
### Static Files (express.static)
|
|
192
|
+
- **Served by:** Express static middleware
|
|
193
|
+
- **Directories:** `/public`, `/node_modules`, some `/lib`, some `/packages`
|
|
194
|
+
- **Processing:** None (direct file serving)
|
|
195
|
+
- **Use for:** CSS, images, pre-built JS
|
|
196
|
+
|
|
197
|
+
### Compiled Files (handlers)
|
|
198
|
+
- **Served by:** SWITE middleware (handlers)
|
|
199
|
+
- **Extensions:** `.ts`, `.ui`, `.uix`
|
|
200
|
+
- **Processing:** Compilation + import rewriting
|
|
201
|
+
- **Use for:** Source files that need compilation
|
|
202
|
+
|
|
203
|
+
**Note:** `/businessModules/`, `/packages/`, and `/lib/` are NOT served as static files because they may contain `.ui`/`.uix` files that need compilation. The handlers process them on-demand.
|
|
204
|
+
|
|
205
|
+
## Troubleshooting Checklist
|
|
206
|
+
|
|
207
|
+
- [ ] URL starts with correct prefix (`/swiss-packages/`, `/packages/`, `/businessModules/`, `/src/`)
|
|
208
|
+
- [ ] File exists at expected location (check filesystem)
|
|
209
|
+
- [ ] File has correct extension (`.ts`, `.ui`, `.uix`, `.js`)
|
|
210
|
+
- [ ] Workspace root is detected correctly (has `pnpm-workspace.yaml`)
|
|
211
|
+
- [ ] Path resolver includes the directory in special handling
|
|
212
|
+
- [ ] Server has been restarted after code changes
|
|
213
|
+
- [ ] Browser cache has been cleared (hard refresh)
|
|
214
|
+
|
|
215
|
+
## Related Files
|
|
216
|
+
|
|
217
|
+
- `src/utils/file-path-resolver.ts` - Main path resolution logic
|
|
218
|
+
- `src/handlers/base-handler.ts` - Handler base class with file resolution
|
|
219
|
+
- `src/handlers/js-handler.ts` - Extension resolution for .js URLs
|
|
220
|
+
- `src/middleware/static-files.ts` - Static file serving setup
|
|
221
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@swissjs/swite",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "SWITE - SWISS Development Server (Vite replacement)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"swite": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@swissjs/core": "link:../swiss-lib/runtime",
|
|
13
|
+
"@swissjs/compiler": "link:../swiss-lib/compiler",
|
|
14
|
+
"@swissjs/plugin-file-router": "link:../swiss-lib/plugins/file-router",
|
|
15
|
+
"chalk": "^5.3.0",
|
|
16
|
+
"chokidar": "^3.5.3",
|
|
17
|
+
"es-module-lexer": "^1.3.1",
|
|
18
|
+
"esbuild": "^0.25.0",
|
|
19
|
+
"express": "^4.18.2",
|
|
20
|
+
"ws": "^8.20.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/express": "^4.17.21",
|
|
24
|
+
"@types/node": "18.19.121",
|
|
25
|
+
"@types/ws": "^8.5.8",
|
|
26
|
+
"@changesets/cli": "^2.29.5",
|
|
27
|
+
"tsx": "^4.21.0",
|
|
28
|
+
"typescript": "^5.0.0"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"registry": "https://registry.npmjs.org",
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/kibologic/alpine-erp-core.git"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc -b",
|
|
41
|
+
"dev": "tsc -b --watch",
|
|
42
|
+
"clean": "rm -rf dist node_modules && rm -f tsconfig.tsbuildinfo",
|
|
43
|
+
"generate-import-map": "tsx src/internal/generate-import-map-cli.ts",
|
|
44
|
+
"test": "node --import tsx --test __tests__/import-rewriter-bug.test.ts",
|
|
45
|
+
"changeset": "changeset",
|
|
46
|
+
"release:version": "changeset version",
|
|
47
|
+
"release:publish": "changeset publish"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class SwiteProxyError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
readonly responseBody: unknown;
|
|
4
|
+
|
|
5
|
+
constructor(status: number, message: string, responseBody?: unknown) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "SwiteProxyError";
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.responseBody = responseBody ?? null;
|
|
10
|
+
Object.setPrototypeOf(this, SwiteProxyError.prototype);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { PythonServiceConfig } from "../../config/config.js";
|
|
2
|
+
import { SwiteProxyError } from "./SwiteProxyError.js";
|
|
3
|
+
|
|
4
|
+
let _pythonConfig: PythonServiceConfig | null = null;
|
|
5
|
+
let _productionMode = false;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Called by swite start on startup.
|
|
9
|
+
* Disables localhost fallback — PYTHON_SERVICE_URL is the only valid base URL.
|
|
10
|
+
*/
|
|
11
|
+
export function setProductionMode(): void {
|
|
12
|
+
_productionMode = true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Called by the swite dev process manager (S-03) on startup.
|
|
17
|
+
* Stores the resolved python service config for use by proxyToPython.
|
|
18
|
+
*/
|
|
19
|
+
export function initPythonProxy(config: PythonServiceConfig): void {
|
|
20
|
+
_pythonConfig = config;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ProxyOptions {
|
|
24
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
25
|
+
path: string;
|
|
26
|
+
body?: unknown;
|
|
27
|
+
headers?: Record<string, string>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Proxy a request from the Node server to the internal Python service.
|
|
32
|
+
*
|
|
33
|
+
* Resolves base URL from PYTHON_SERVICE_URL env var if set,
|
|
34
|
+
* otherwise falls back to http://localhost:{python.port} from config.
|
|
35
|
+
*
|
|
36
|
+
* Always injects X-Internal-Token header.
|
|
37
|
+
* Throws SwiteProxyError on non-2xx responses.
|
|
38
|
+
*/
|
|
39
|
+
export async function proxyToPython<T>(options: ProxyOptions): Promise<T> {
|
|
40
|
+
const envBaseUrl = process.env["PYTHON_SERVICE_URL"];
|
|
41
|
+
|
|
42
|
+
let baseUrl: string;
|
|
43
|
+
if (envBaseUrl) {
|
|
44
|
+
baseUrl = envBaseUrl.replace(/\/$/, "");
|
|
45
|
+
} else if (!_productionMode && _pythonConfig) {
|
|
46
|
+
baseUrl = `http://localhost:${_pythonConfig.port}`;
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error(
|
|
49
|
+
_productionMode
|
|
50
|
+
? "PYTHON_SERVICE_URL is required in production mode but is not set."
|
|
51
|
+
: "Python service not configured. Call initPythonProxy() before using proxyToPython, or set PYTHON_SERVICE_URL.",
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const token = process.env["INTERNAL_API_TOKEN"] ?? "";
|
|
56
|
+
const url = `${baseUrl}${options.path}`;
|
|
57
|
+
|
|
58
|
+
const requestHeaders: Record<string, string> = {
|
|
59
|
+
"X-Internal-Token": token,
|
|
60
|
+
...options.headers,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
if (options.body !== undefined) {
|
|
64
|
+
requestHeaders["Content-Type"] = "application/json";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const response = await fetch(url, {
|
|
68
|
+
method: options.method,
|
|
69
|
+
headers: requestHeaders,
|
|
70
|
+
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
let responseBody: unknown;
|
|
75
|
+
try {
|
|
76
|
+
responseBody = await response.json();
|
|
77
|
+
} catch {
|
|
78
|
+
responseBody = await response.text();
|
|
79
|
+
}
|
|
80
|
+
throw new SwiteProxyError(
|
|
81
|
+
response.status,
|
|
82
|
+
`Python service responded with ${response.status} on ${options.method} ${options.path}`,
|
|
83
|
+
responseBody,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return response.json() as Promise<T>;
|
|
88
|
+
}
|