@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.
Files changed (163) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.github/workflows/ci.yml +59 -0
  3. package/.github/workflows/publish.yml +50 -0
  4. package/.github/workflows/release.yml +53 -0
  5. package/BUILD_ANALYSIS.md +89 -0
  6. package/BUILD_STRATEGY.md +75 -0
  7. package/CHANGELOG.md +53 -0
  8. package/DIRECTIVE.md +488 -0
  9. package/__tests__/css-extraction.test.ts +261 -0
  10. package/__tests__/css-injection-integration.test.ts +247 -0
  11. package/__tests__/css-middleware.test.ts +191 -0
  12. package/__tests__/import-rewriter-bug.test.ts +135 -0
  13. package/dist/builder.d.ts +36 -0
  14. package/dist/builder.d.ts.map +1 -0
  15. package/dist/builder.js +772 -0
  16. package/dist/cache/compilation-cache.d.ts +33 -0
  17. package/dist/cache/compilation-cache.d.ts.map +1 -0
  18. package/dist/cache/compilation-cache.js +130 -0
  19. package/dist/cli.d.ts +3 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +85 -0
  22. package/dist/config-loader.d.ts +8 -0
  23. package/dist/config-loader.d.ts.map +1 -0
  24. package/dist/config-loader.js +40 -0
  25. package/dist/config.d.ts +29 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +7 -0
  28. package/dist/dev/pythonDevManager.d.ts +12 -0
  29. package/dist/dev/pythonDevManager.d.ts.map +1 -0
  30. package/dist/dev/pythonDevManager.js +85 -0
  31. package/dist/env.d.ts +19 -0
  32. package/dist/env.d.ts.map +1 -0
  33. package/dist/env.js +112 -0
  34. package/dist/handlers/base-handler.d.ts +21 -0
  35. package/dist/handlers/base-handler.d.ts.map +1 -0
  36. package/dist/handlers/base-handler.js +38 -0
  37. package/dist/handlers/js-handler.d.ts +10 -0
  38. package/dist/handlers/js-handler.d.ts.map +1 -0
  39. package/dist/handlers/js-handler.js +87 -0
  40. package/dist/handlers/mjs-handler.d.ts +8 -0
  41. package/dist/handlers/mjs-handler.d.ts.map +1 -0
  42. package/dist/handlers/mjs-handler.js +44 -0
  43. package/dist/handlers/node-module-handler.d.ts +16 -0
  44. package/dist/handlers/node-module-handler.d.ts.map +1 -0
  45. package/dist/handlers/node-module-handler.js +267 -0
  46. package/dist/handlers/ts-handler.d.ts +11 -0
  47. package/dist/handlers/ts-handler.d.ts.map +1 -0
  48. package/dist/handlers/ts-handler.js +120 -0
  49. package/dist/handlers/ui-handler.d.ts +12 -0
  50. package/dist/handlers/ui-handler.d.ts.map +1 -0
  51. package/dist/handlers/ui-handler.js +182 -0
  52. package/dist/handlers/uix-handler.d.ts +12 -0
  53. package/dist/handlers/uix-handler.d.ts.map +1 -0
  54. package/dist/handlers/uix-handler.js +135 -0
  55. package/dist/hmr.d.ts +20 -0
  56. package/dist/hmr.d.ts.map +1 -0
  57. package/dist/hmr.js +265 -0
  58. package/dist/import-rewriter.d.ts +3 -0
  59. package/dist/import-rewriter.d.ts.map +1 -0
  60. package/dist/import-rewriter.js +351 -0
  61. package/dist/index.d.ts +14 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +13 -0
  64. package/dist/middleware/hmr-routes.d.ts +12 -0
  65. package/dist/middleware/hmr-routes.d.ts.map +1 -0
  66. package/dist/middleware/hmr-routes.js +97 -0
  67. package/dist/middleware/middleware-setup.d.ts +23 -0
  68. package/dist/middleware/middleware-setup.d.ts.map +1 -0
  69. package/dist/middleware/middleware-setup.js +596 -0
  70. package/dist/middleware/static-files.d.ts +15 -0
  71. package/dist/middleware/static-files.d.ts.map +1 -0
  72. package/dist/middleware/static-files.js +585 -0
  73. package/dist/proxy/SwiteProxyError.d.ts +6 -0
  74. package/dist/proxy/SwiteProxyError.d.ts.map +1 -0
  75. package/dist/proxy/SwiteProxyError.js +9 -0
  76. package/dist/proxy/proxyToPython.d.ts +28 -0
  77. package/dist/proxy/proxyToPython.d.ts.map +1 -0
  78. package/dist/proxy/proxyToPython.js +66 -0
  79. package/dist/resolver/bare-import-resolver.d.ts +9 -0
  80. package/dist/resolver/bare-import-resolver.d.ts.map +1 -0
  81. package/dist/resolver/bare-import-resolver.js +363 -0
  82. package/dist/resolver/symlink-registry.d.ts +13 -0
  83. package/dist/resolver/symlink-registry.d.ts.map +1 -0
  84. package/dist/resolver/symlink-registry.js +98 -0
  85. package/dist/resolver/url-resolver.d.ts +11 -0
  86. package/dist/resolver/url-resolver.d.ts.map +1 -0
  87. package/dist/resolver/url-resolver.js +268 -0
  88. package/dist/resolver/workspace-package-resolver.d.ts +10 -0
  89. package/dist/resolver/workspace-package-resolver.d.ts.map +1 -0
  90. package/dist/resolver/workspace-package-resolver.js +185 -0
  91. package/dist/resolver.d.ts +17 -0
  92. package/dist/resolver.d.ts.map +1 -0
  93. package/dist/resolver.js +191 -0
  94. package/dist/router/file-router.d.ts +19 -0
  95. package/dist/router/file-router.d.ts.map +1 -0
  96. package/dist/router/file-router.js +114 -0
  97. package/dist/server.d.ts +22 -0
  98. package/dist/server.d.ts.map +1 -0
  99. package/dist/server.js +122 -0
  100. package/dist/utils/cdn-fallback.d.ts +14 -0
  101. package/dist/utils/cdn-fallback.d.ts.map +1 -0
  102. package/dist/utils/cdn-fallback.js +36 -0
  103. package/dist/utils/file-path-resolver.d.ts +9 -0
  104. package/dist/utils/file-path-resolver.d.ts.map +1 -0
  105. package/dist/utils/file-path-resolver.js +187 -0
  106. package/dist/utils/generate-import-map-cli.d.ts +3 -0
  107. package/dist/utils/generate-import-map-cli.d.ts.map +1 -0
  108. package/dist/utils/generate-import-map-cli.js +32 -0
  109. package/dist/utils/generate-import-map.d.ts +21 -0
  110. package/dist/utils/generate-import-map.d.ts.map +1 -0
  111. package/dist/utils/generate-import-map.js +119 -0
  112. package/dist/utils/package-finder.d.ts +24 -0
  113. package/dist/utils/package-finder.d.ts.map +1 -0
  114. package/dist/utils/package-finder.js +161 -0
  115. package/dist/utils/package-registry.d.ts +36 -0
  116. package/dist/utils/package-registry.d.ts.map +1 -0
  117. package/dist/utils/package-registry.js +159 -0
  118. package/dist/utils/workspace.d.ts +6 -0
  119. package/dist/utils/workspace.d.ts.map +1 -0
  120. package/dist/utils/workspace.js +65 -0
  121. package/docs/IMPORT_REWRITING.md +164 -0
  122. package/docs/IMPORT_REWRITING_TROUBLESHOOTING.md +139 -0
  123. package/docs/PATH_RESOLUTION_GUIDE.md +221 -0
  124. package/package.json +49 -0
  125. package/src/adapters/proxy/SwiteProxyError.ts +12 -0
  126. package/src/adapters/proxy/proxyToPython.ts +88 -0
  127. package/src/build-engine/builder.ts +960 -0
  128. package/src/cli.ts +109 -0
  129. package/src/config/config-loader.ts +46 -0
  130. package/src/config/config.ts +34 -0
  131. package/src/config/env.ts +98 -0
  132. package/src/dev-engine/handlers/base-handler.ts +68 -0
  133. package/src/dev-engine/handlers/js-handler.ts +134 -0
  134. package/src/dev-engine/handlers/mjs-handler.ts +65 -0
  135. package/src/dev-engine/handlers/node-module-handler.ts +339 -0
  136. package/src/dev-engine/handlers/ts-handler.ts +143 -0
  137. package/src/dev-engine/handlers/ui-handler.ts +105 -0
  138. package/src/dev-engine/handlers/uix-handler.ts +90 -0
  139. package/src/dev-engine/hmr/hmr-client-template.ts +122 -0
  140. package/src/dev-engine/hmr/hmr.ts +173 -0
  141. package/src/dev-engine/middleware/hmr-routes.ts +120 -0
  142. package/src/dev-engine/middleware/middleware-setup.ts +351 -0
  143. package/src/dev-engine/middleware/static-files.ts +728 -0
  144. package/src/dev-engine/pythonDevManager.ts +116 -0
  145. package/src/dev-engine/router/file-router.ts +164 -0
  146. package/src/dev-engine/server.ts +152 -0
  147. package/src/index.ts +26 -0
  148. package/src/internal/cache/compilation-cache.ts +182 -0
  149. package/src/internal/generate-import-map-cli.ts +40 -0
  150. package/src/internal/generate-import-map.ts +154 -0
  151. package/src/kernel/package-finder.ts +164 -0
  152. package/src/kernel/package-registry.ts +198 -0
  153. package/src/kernel/workspace.ts +62 -0
  154. package/src/resolution/bare-import-resolver.ts +400 -0
  155. package/src/resolution/cdn/cdn-fallback.ts +37 -0
  156. package/src/resolution/path/file-path-resolver.ts +190 -0
  157. package/src/resolution/path/path-fixup.ts +19 -0
  158. package/src/resolution/resolver.ts +198 -0
  159. package/src/resolution/rewriting/import-rewriter.ts +237 -0
  160. package/src/resolution/symlink-registry.ts +114 -0
  161. package/src/resolution/url-resolver.ts +231 -0
  162. package/src/resolution/workspace-package-resolver.ts +94 -0
  163. 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
+ }