tw-framework 0.2.7__tar.gz → 0.2.9__tar.gz

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 (63) hide show
  1. {tw_framework-0.2.7 → tw_framework-0.2.9}/PKG-INFO +1 -1
  2. {tw_framework-0.2.7 → tw_framework-0.2.9}/pyproject.toml +4 -1
  3. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/framework.py +3 -3
  4. tw_framework-0.2.9/tw_framework/twm_api_runner.js +469 -0
  5. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/PKG-INFO +1 -1
  6. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/SOURCES.txt +1 -0
  7. {tw_framework-0.2.7 → tw_framework-0.2.9}/README.md +0 -0
  8. {tw_framework-0.2.7 → tw_framework-0.2.9}/setup.cfg +0 -0
  9. {tw_framework-0.2.7 → tw_framework-0.2.9}/tests/test_deployment.py +0 -0
  10. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/__init__.py +0 -0
  11. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/__init__.py +0 -0
  12. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/cloudflare.py +0 -0
  13. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/netlify.py +0 -0
  14. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/vercel.py +0 -0
  15. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/vercel_functions.py +0 -0
  16. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/advanced_diagnostics.py +0 -0
  17. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/asset_optimizer.py +0 -0
  18. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/ast_nodes.py +0 -0
  19. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/build.py +0 -0
  20. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/build_performance.py +0 -0
  21. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/build_report.py +0 -0
  22. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/cli.py +0 -0
  23. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/code_splitting.py +0 -0
  24. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/common.py +0 -0
  25. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/compiler.py +0 -0
  26. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/compiler_stats.py +0 -0
  27. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/dead_code.py +0 -0
  28. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/dependency_graph.py +0 -0
  29. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/deploy.py +0 -0
  30. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/dev.py +0 -0
  31. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/diagnostics.py +0 -0
  32. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/dynamic_imports.py +0 -0
  33. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/error_formatter.py +0 -0
  34. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/hydration.py +0 -0
  35. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/incremental_cache.py +0 -0
  36. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/interpreter.py +0 -0
  37. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/ir.py +0 -0
  38. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/lexer.py +0 -0
  39. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/lowering.py +0 -0
  40. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/parser.py +0 -0
  41. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/partial_rebuild.py +0 -0
  42. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/performance_analyzer.py +0 -0
  43. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/plugin_runtime.py +0 -0
  44. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/production_optimizer.py +0 -0
  45. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/reactivity.py +0 -0
  46. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/render_css.py +0 -0
  47. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/render_html.py +0 -0
  48. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/route_optimizer.py +0 -0
  49. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/router.py +0 -0
  50. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/runtime_model.py +0 -0
  51. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/runtime_values.py +0 -0
  52. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/semantic.py +0 -0
  53. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/server.py +0 -0
  54. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/signature.py +0 -0
  55. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/static_dynamic_auto.py +0 -0
  56. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/streaming.py +0 -0
  57. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/test_build_cli_pytest.py +0 -0
  58. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/tree_shaking.py +0 -0
  59. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/twm_parser.py +0 -0
  60. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/dependency_links.txt +0 -0
  61. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/entry_points.txt +0 -0
  62. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/requires.txt +0 -0
  63. {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tw-framework
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: TW framework CLI with create, dev, build, and deploy commands
5
5
  Author: TW Framework
6
6
  Requires-Python: >=3.9
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tw-framework"
7
- version = "0.2.7"
7
+ version = "0.2.9"
8
8
  description = "TW framework CLI with create, dev, build, and deploy commands"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -24,3 +24,6 @@ tw = "tw_framework.cli:main"
24
24
 
25
25
  [tool.setuptools]
26
26
  packages = ["tw_framework", "tw_framework.adapters"]
27
+
28
+ [tool.setuptools.package-data]
29
+ tw_framework = ["*.js"]
@@ -2526,13 +2526,13 @@ def write_route_artifacts(output_dir: str):
2526
2526
 
2527
2527
  def strip_html_to_text(html_text: str) -> str:
2528
2528
  # Remove script/style blocks
2529
- html_text = re.sub(r"<script[\\s\\S]*?</script>", " ", html_text, flags=re.I)
2530
- html_text = re.sub(r"<style[\\s\\S]*?</style>", " ", html_text, flags=re.I)
2529
+ html_text = re.sub(r"<script[\s\S]*?</script>", " ", html_text, flags=re.I)
2530
+ html_text = re.sub(r"<style[\s\S]*?</style>", " ", html_text, flags=re.I)
2531
2531
  # Remove tags
2532
2532
  html_text = re.sub(r"<[^>]+>", " ", html_text)
2533
2533
  # Decode entities + normalize whitespace
2534
2534
  html_text = html.unescape(html_text)
2535
- html_text = re.sub(r"\\s+", " ", html_text).strip()
2535
+ html_text = re.sub(r"\s+", " ", html_text).strip()
2536
2536
  return html_text
2537
2537
 
2538
2538
 
@@ -0,0 +1,469 @@
1
+ /* eslint-disable no-console */
2
+ // TWLang server-side `.twm` API runner.
3
+ //
4
+ // Usage:
5
+ // node twm_api_runner.js /abs/path/to/compiled_module.cjs
6
+ //
7
+ // Reads request JSON from stdin and prints ONE JSON response to stdout.
8
+
9
+ "use strict";
10
+
11
+ const fs = require("fs");
12
+ const path = require("path");
13
+ const { createRequire } = require("module");
14
+
15
+ function findProjectRoot(startPath) {
16
+ let current = path.resolve(startPath || process.cwd());
17
+ if (!fs.existsSync(current)) {
18
+ current = path.dirname(current);
19
+ }
20
+ if (fs.existsSync(current) && fs.statSync(current).isFile()) {
21
+ current = path.dirname(current);
22
+ }
23
+ while (true) {
24
+ if (
25
+ fs.existsSync(path.join(current, "package.json")) ||
26
+ fs.existsSync(path.join(current, "tw.config")) ||
27
+ fs.existsSync(path.join(current, "[home]"))
28
+ ) {
29
+ return current;
30
+ }
31
+ const parent = path.dirname(current);
32
+ if (parent === current) {
33
+ return process.cwd();
34
+ }
35
+ current = parent;
36
+ }
37
+ }
38
+
39
+ function readJsonFile(filePath, fallbackValue) {
40
+ try {
41
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
42
+ } catch (error) {
43
+ return fallbackValue;
44
+ }
45
+ }
46
+
47
+ function normalizeHeaderValue(headers, key) {
48
+ if (!headers || typeof headers !== "object") return undefined;
49
+ const target = String(key || "").toLowerCase();
50
+ for (const [name, value] of Object.entries(headers)) {
51
+ if (String(name).toLowerCase() === target) return value;
52
+ }
53
+ return undefined;
54
+ }
55
+
56
+ function createTimeoutSignal(timeoutMs) {
57
+ const timeout = Number(timeoutMs || 0);
58
+ if (!Number.isFinite(timeout) || timeout <= 0) return {};
59
+ if (typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function") {
60
+ return { signal: AbortSignal.timeout(timeout) };
61
+ }
62
+ if (typeof AbortController === "undefined") {
63
+ return {};
64
+ }
65
+ const controller = new AbortController();
66
+ const timer = setTimeout(() => controller.abort(new Error(`Request timeout after ${timeout}ms`)), timeout);
67
+ return {
68
+ signal: controller.signal,
69
+ cleanup() {
70
+ clearTimeout(timer);
71
+ },
72
+ };
73
+ }
74
+
75
+ function normalizeHttpData(rawText, headers) {
76
+ const contentType = String(normalizeHeaderValue(headers, "content-type") || "").toLowerCase();
77
+ if (contentType.includes("application/json")) {
78
+ try {
79
+ return JSON.parse(rawText);
80
+ } catch (error) {
81
+ return rawText;
82
+ }
83
+ }
84
+ return rawText;
85
+ }
86
+
87
+ function buildHttpHelpers(defaultHeaders) {
88
+ async function request(url, options = {}) {
89
+ if (typeof fetch !== "function") {
90
+ throw new Error("Global `fetch` is not available. Use Node.js 18+ for built-in http helpers.");
91
+ }
92
+ const opts = options && typeof options === "object" ? { ...options } : {};
93
+ const method = String(opts.method || "GET").toUpperCase();
94
+ const headers = { ...(defaultHeaders || {}), ...(opts.headers || {}) };
95
+ let body = opts.body;
96
+ const hasContentType = Object.keys(headers).some((name) => String(name).toLowerCase() === "content-type");
97
+ if (
98
+ body !== undefined &&
99
+ body !== null &&
100
+ typeof body === "object" &&
101
+ !Buffer.isBuffer(body) &&
102
+ !(body instanceof URLSearchParams) &&
103
+ typeof body !== "string"
104
+ ) {
105
+ if (!hasContentType) headers["Content-Type"] = "application/json";
106
+ body = JSON.stringify(body);
107
+ }
108
+ const timeoutCtl = createTimeoutSignal(opts.timeout || opts.timeoutMs || 0);
109
+ try {
110
+ const response = await fetch(url, {
111
+ ...opts,
112
+ method,
113
+ headers,
114
+ body,
115
+ signal: timeoutCtl.signal || opts.signal,
116
+ });
117
+ const text = await response.text();
118
+ const responseHeaders = Object.fromEntries(response.headers.entries());
119
+ return {
120
+ ok: response.ok,
121
+ status: response.status,
122
+ statusText: response.statusText,
123
+ url: response.url,
124
+ headers: responseHeaders,
125
+ text,
126
+ data: normalizeHttpData(text, responseHeaders),
127
+ };
128
+ } finally {
129
+ if (typeof timeoutCtl.cleanup === "function") timeoutCtl.cleanup();
130
+ }
131
+ }
132
+
133
+ return {
134
+ request,
135
+ get(url, options = {}) {
136
+ return request(url, { ...options, method: "GET" });
137
+ },
138
+ post(url, body, options = {}) {
139
+ return request(url, { ...options, method: "POST", body });
140
+ },
141
+ put(url, body, options = {}) {
142
+ return request(url, { ...options, method: "PUT", body });
143
+ },
144
+ patch(url, body, options = {}) {
145
+ return request(url, { ...options, method: "PATCH", body });
146
+ },
147
+ delete(url, options = {}) {
148
+ return request(url, { ...options, method: "DELETE" });
149
+ },
150
+ };
151
+ }
152
+
153
+ function buildValueHelper(values, label) {
154
+ const source = values && typeof values === "object" ? { ...values } : {};
155
+ const api = {
156
+ get(name, fallbackValue = undefined) {
157
+ return Object.prototype.hasOwnProperty.call(source, name) ? source[name] : fallbackValue;
158
+ },
159
+ has(name) {
160
+ return Object.prototype.hasOwnProperty.call(source, name);
161
+ },
162
+ require(name) {
163
+ if (!Object.prototype.hasOwnProperty.call(source, name) || source[name] === "") {
164
+ throw new Error(`Missing ${label}: ${name}`);
165
+ }
166
+ return source[name];
167
+ },
168
+ all() {
169
+ return { ...source };
170
+ },
171
+ };
172
+ return new Proxy(api, {
173
+ get(target, prop) {
174
+ if (prop in target) return target[prop];
175
+ if (typeof prop === "string" && Object.prototype.hasOwnProperty.call(source, prop)) {
176
+ return source[prop];
177
+ }
178
+ return undefined;
179
+ },
180
+ has(target, prop) {
181
+ return prop in target || (typeof prop === "string" && Object.prototype.hasOwnProperty.call(source, prop));
182
+ },
183
+ });
184
+ }
185
+
186
+ function createProjectRequire(projectRoot) {
187
+ const packageJsonPath = path.join(projectRoot, "package.json");
188
+ if (fs.existsSync(packageJsonPath)) {
189
+ return createRequire(packageJsonPath);
190
+ }
191
+ return createRequire(__filename);
192
+ }
193
+
194
+ function createPackageHelper(projectRoot) {
195
+ const projectRequire = createProjectRequire(projectRoot);
196
+ const packageJson = readJsonFile(path.join(projectRoot, "package.json"), {});
197
+ return {
198
+ json() {
199
+ return { ...packageJson };
200
+ },
201
+ has(name) {
202
+ const deps = {
203
+ ...((packageJson && packageJson.dependencies) || {}),
204
+ ...((packageJson && packageJson.devDependencies) || {}),
205
+ };
206
+ return Object.prototype.hasOwnProperty.call(deps, name);
207
+ },
208
+ require(name) {
209
+ try {
210
+ return projectRequire(name);
211
+ } catch (error) {
212
+ if (error && (error.code === "MODULE_NOT_FOUND" || String(error.message || "").includes("Cannot find module"))) {
213
+ throw new Error(
214
+ `Package \`${name}\` nahi mila. Isko project ke package.json me add karke \`npm install ${name}\` chalao.`
215
+ );
216
+ }
217
+ throw error;
218
+ }
219
+ },
220
+ resolve(name) {
221
+ return projectRequire.resolve(name);
222
+ },
223
+ };
224
+ }
225
+
226
+ function parseServiceAccount(rawValue, projectRoot) {
227
+ if (!rawValue) return null;
228
+ if (typeof rawValue === "object") return rawValue;
229
+ const text = String(rawValue).trim();
230
+ if (!text) return null;
231
+ if (text.startsWith("{")) {
232
+ return JSON.parse(text);
233
+ }
234
+ const absolutePath = path.isAbsolute(text) ? text : path.join(projectRoot, text);
235
+ return readJsonFile(absolutePath, null);
236
+ }
237
+
238
+ function createFirebaseHelper(projectRoot, envHelper, packageHelper) {
239
+ function loadAdmin() {
240
+ return packageHelper.require("firebase-admin");
241
+ }
242
+
243
+ function resolveCredential(admin) {
244
+ const inlineJson =
245
+ envHelper.get("FIREBASE_SERVICE_ACCOUNT_JSON") ||
246
+ envHelper.get("FIREBASE_ADMIN_CREDENTIALS_JSON") ||
247
+ envHelper.get("GOOGLE_APPLICATION_CREDENTIALS_JSON");
248
+ const parsedInline = parseServiceAccount(inlineJson, projectRoot);
249
+ if (parsedInline) {
250
+ return admin.credential.cert(parsedInline);
251
+ }
252
+ const filePath =
253
+ envHelper.get("FIREBASE_SERVICE_ACCOUNT_PATH") ||
254
+ envHelper.get("FIREBASE_ADMIN_CREDENTIALS_PATH") ||
255
+ envHelper.get("GOOGLE_APPLICATION_CREDENTIALS");
256
+ const parsedFile = parseServiceAccount(filePath, projectRoot);
257
+ if (parsedFile) {
258
+ return admin.credential.cert(parsedFile);
259
+ }
260
+ return undefined;
261
+ }
262
+
263
+ function app(options = {}) {
264
+ const admin = loadAdmin();
265
+ if (Array.isArray(admin.apps) && admin.apps.length > 0) {
266
+ return admin.app();
267
+ }
268
+ const initOptions = { ...(options || {}) };
269
+ if (!initOptions.credential) {
270
+ const credential = resolveCredential(admin);
271
+ if (credential) initOptions.credential = credential;
272
+ }
273
+ if (!initOptions.projectId) {
274
+ initOptions.projectId = envHelper.get("FIREBASE_PROJECT_ID") || envHelper.get("GOOGLE_CLOUD_PROJECT");
275
+ }
276
+ return admin.initializeApp(initOptions);
277
+ }
278
+
279
+ return {
280
+ admin: loadAdmin,
281
+ app,
282
+ firestore() {
283
+ return app().firestore();
284
+ },
285
+ auth() {
286
+ return app().auth();
287
+ },
288
+ messaging() {
289
+ return app().messaging();
290
+ },
291
+ storage() {
292
+ return app().storage();
293
+ },
294
+ };
295
+ }
296
+
297
+ function installRuntimeHelpers(request, compiledPath) {
298
+ const projectRoot = findProjectRoot(
299
+ (request && request.project_root) || compiledPath || process.cwd()
300
+ );
301
+ const envHelper = buildValueHelper((request && request.env) || {}, "env value");
302
+ const secretsHelper = buildValueHelper((request && request.env) || {}, "secret");
303
+ const httpHelper = buildHttpHelpers({
304
+ Accept: "application/json, text/plain;q=0.9, */*;q=0.8",
305
+ });
306
+ const packageHelper = createPackageHelper(projectRoot);
307
+ const firebaseHelper = createFirebaseHelper(projectRoot, envHelper, packageHelper);
308
+ globalThis.http = httpHelper;
309
+ globalThis.env = envHelper;
310
+ globalThis.secrets = secretsHelper;
311
+ globalThis.pkg = packageHelper;
312
+ globalThis.firebase = firebaseHelper;
313
+ globalThis.helpers = {
314
+ http: httpHelper,
315
+ env: envHelper,
316
+ secrets: secretsHelper,
317
+ pkg: packageHelper,
318
+ firebase: firebaseHelper,
319
+ projectRoot,
320
+ };
321
+ }
322
+
323
+ function toPairs(headers) {
324
+ if (!headers) return [];
325
+ if (Array.isArray(headers)) return headers;
326
+ if (typeof headers === "object") return Object.entries(headers);
327
+ return [];
328
+ }
329
+
330
+ function normalizeResult(result) {
331
+ // Allowed return shapes:
332
+ // - string
333
+ // - object (auto JSON), or object with {json|text|html|body,status,headers,cookies}
334
+ // - tuple-like array: [body, status] or [body, status, headers]
335
+ let status = 200;
336
+ let headers = [];
337
+ let cookies = [];
338
+
339
+ // Tuple: [body, status, headers]
340
+ if (Array.isArray(result)) {
341
+ const body = result[0];
342
+ status = Number(result[1] ?? 200) || 200;
343
+ headers = toPairs(result[2]);
344
+ if (typeof body === "string") {
345
+ return { status, content_type: "text/plain; charset=utf-8", body, headers, cookies };
346
+ }
347
+ return {
348
+ status,
349
+ content_type: "application/json; charset=utf-8",
350
+ body: JSON.stringify(body),
351
+ headers,
352
+ cookies,
353
+ };
354
+ }
355
+
356
+ if (typeof result === "string") {
357
+ return { status: 200, content_type: "text/plain; charset=utf-8", body: result, headers: [], cookies: [] };
358
+ }
359
+
360
+ if (result && typeof result === "object") {
361
+ status = Number(result.status ?? 200) || 200;
362
+ headers = toPairs(result.headers);
363
+ cookies = Array.isArray(result.cookies) ? result.cookies : toPairs(result.cookies);
364
+
365
+ if (Object.prototype.hasOwnProperty.call(result, "json")) {
366
+ const payload = result.json;
367
+ return {
368
+ status,
369
+ content_type: "application/json; charset=utf-8",
370
+ body: typeof payload === "string" ? payload : JSON.stringify(payload),
371
+ headers,
372
+ cookies,
373
+ };
374
+ }
375
+ if (Object.prototype.hasOwnProperty.call(result, "html")) {
376
+ return { status, content_type: "text/html; charset=utf-8", body: String(result.html ?? ""), headers, cookies };
377
+ }
378
+ if (Object.prototype.hasOwnProperty.call(result, "text")) {
379
+ return { status, content_type: "text/plain; charset=utf-8", body: String(result.text ?? ""), headers, cookies };
380
+ }
381
+ if (Object.prototype.hasOwnProperty.call(result, "body")) {
382
+ const bodyVal = result.body;
383
+ const ct = String(result.content_type || result.contentType || "text/plain; charset=utf-8");
384
+ if (bodyVal && typeof bodyVal === "object" && ct.includes("application/json")) {
385
+ return { status, content_type: ct, body: JSON.stringify(bodyVal), headers, cookies };
386
+ }
387
+ return { status, content_type: ct, body: typeof bodyVal === "string" ? bodyVal : JSON.stringify(bodyVal), headers, cookies };
388
+ }
389
+
390
+ // Default: treat object as JSON
391
+ return {
392
+ status,
393
+ content_type: "application/json; charset=utf-8",
394
+ body: JSON.stringify(result),
395
+ headers,
396
+ cookies,
397
+ };
398
+ }
399
+
400
+ return { status: 200, content_type: "text/plain; charset=utf-8", body: String(result ?? ""), headers: [], cookies: [] };
401
+ }
402
+
403
+ function methodList(mod) {
404
+ const methods = ["get", "post", "put", "patch", "delete", "options"];
405
+ return methods.filter((m) => typeof mod[m] === "function").map((m) => m.toUpperCase());
406
+ }
407
+
408
+ async function main() {
409
+ const compiledPath = process.argv[2];
410
+ if (!compiledPath) {
411
+ process.stderr.write("Missing compiled module path\n");
412
+ process.exit(2);
413
+ }
414
+
415
+ const mod = require(path.resolve(compiledPath));
416
+
417
+ let input = "";
418
+ process.stdin.setEncoding("utf8");
419
+ process.stdin.on("data", (chunk) => (input += chunk));
420
+ process.stdin.on("end", async () => {
421
+ let request = {};
422
+ try {
423
+ request = JSON.parse(input || "{}");
424
+ } catch (e) {
425
+ const resp = { status: 400, content_type: "application/json; charset=utf-8", body: JSON.stringify({ error: "Invalid JSON request" }), headers: [], cookies: [] };
426
+ process.stdout.write(JSON.stringify(resp));
427
+ return;
428
+ }
429
+
430
+ const method = String(request.method || "GET").toLowerCase();
431
+ const fn = (typeof mod[method] === "function" ? mod[method] : null) || (typeof mod.handler === "function" ? mod.handler : null);
432
+ if (!fn) {
433
+ const allowed = methodList(mod);
434
+ const resp = {
435
+ status: 405,
436
+ content_type: "application/json; charset=utf-8",
437
+ body: JSON.stringify({ error: "Method not allowed", allowed }),
438
+ headers: [["Allow", allowed.join(", ")]],
439
+ cookies: [],
440
+ };
441
+ process.stdout.write(JSON.stringify(resp));
442
+ return;
443
+ }
444
+
445
+ try {
446
+ installRuntimeHelpers(request, compiledPath);
447
+ const result = await fn(request);
448
+ const resp = normalizeResult(result);
449
+ process.stdout.write(JSON.stringify(resp));
450
+ } catch (e) {
451
+ const resp = {
452
+ status: 500,
453
+ content_type: "application/json; charset=utf-8",
454
+ body: JSON.stringify({ error: "TWM handler error", name: e && e.name ? e.name : "Error", message: e && e.message ? e.message : String(e) }),
455
+ headers: [],
456
+ cookies: [],
457
+ };
458
+ process.stdout.write(JSON.stringify(resp));
459
+ }
460
+ });
461
+
462
+ // Important: start consuming stdin
463
+ process.stdin.resume();
464
+ }
465
+
466
+ main().catch((e) => {
467
+ process.stderr.write(String(e && e.stack ? e.stack : e) + "\n");
468
+ process.exit(1);
469
+ });
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tw-framework
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: TW framework CLI with create, dev, build, and deploy commands
5
5
  Author: TW Framework
6
6
  Requires-Python: >=3.9
@@ -46,6 +46,7 @@ tw_framework/static_dynamic_auto.py
46
46
  tw_framework/streaming.py
47
47
  tw_framework/test_build_cli_pytest.py
48
48
  tw_framework/tree_shaking.py
49
+ tw_framework/twm_api_runner.js
49
50
  tw_framework/twm_parser.py
50
51
  tw_framework.egg-info/PKG-INFO
51
52
  tw_framework.egg-info/SOURCES.txt
File without changes
File without changes