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.
- {tw_framework-0.2.7 → tw_framework-0.2.9}/PKG-INFO +1 -1
- {tw_framework-0.2.7 → tw_framework-0.2.9}/pyproject.toml +4 -1
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/framework.py +3 -3
- tw_framework-0.2.9/tw_framework/twm_api_runner.js +469 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/PKG-INFO +1 -1
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/SOURCES.txt +1 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/README.md +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/setup.cfg +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tests/test_deployment.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/__init__.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/__init__.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/cloudflare.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/netlify.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/vercel.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/adapters/vercel_functions.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/advanced_diagnostics.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/asset_optimizer.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/ast_nodes.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/build.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/build_performance.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/build_report.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/cli.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/code_splitting.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/common.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/compiler.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/compiler_stats.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/dead_code.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/dependency_graph.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/deploy.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/dev.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/diagnostics.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/dynamic_imports.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/error_formatter.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/hydration.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/incremental_cache.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/interpreter.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/ir.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/lexer.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/lowering.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/parser.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/partial_rebuild.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/performance_analyzer.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/plugin_runtime.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/production_optimizer.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/reactivity.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/render_css.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/render_html.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/route_optimizer.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/router.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/runtime_model.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/runtime_values.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/semantic.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/server.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/signature.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/static_dynamic_auto.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/streaming.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/test_build_cli_pytest.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/tree_shaking.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework/twm_parser.py +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/dependency_links.txt +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/entry_points.txt +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/requires.txt +0 -0
- {tw_framework-0.2.7 → tw_framework-0.2.9}/tw_framework.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tw-framework"
|
|
7
|
-
version = "0.2.
|
|
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[
|
|
2530
|
-
html_text = re.sub(r"<style[
|
|
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"
|
|
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
|
+
});
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|