@ezetgalaxy/titan 26.7.5 → 26.8.2

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 (53) hide show
  1. package/README.md +86 -200
  2. package/index.js +87 -23
  3. package/package.json +4 -3
  4. package/templates/js/_gitignore +37 -0
  5. package/templates/{package.json → js/package.json} +3 -0
  6. package/templates/{server → js/server}/actions/hello.jsbundle +4 -1
  7. package/templates/{server → js/server}/src/extensions.rs +149 -17
  8. package/templates/{server → js/server}/src/main.rs +1 -6
  9. package/templates/{titan → js/titan}/bundle.js +22 -9
  10. package/templates/js/titan/dev.js +258 -0
  11. package/templates/js/titan/titan.js +122 -0
  12. package/templates/rust/Dockerfile +66 -0
  13. package/templates/rust/_dockerignore +3 -0
  14. package/templates/rust/_gitignore +37 -0
  15. package/templates/rust/app/actions/hello.js +5 -0
  16. package/templates/rust/app/actions/rust_hello.rs +14 -0
  17. package/templates/rust/app/app.js +11 -0
  18. package/templates/rust/app/titan.d.ts +101 -0
  19. package/templates/rust/jsconfig.json +19 -0
  20. package/templates/rust/package.json +13 -0
  21. package/templates/rust/server/Cargo.lock +2869 -0
  22. package/templates/rust/server/Cargo.toml +39 -0
  23. package/templates/rust/server/action_map.json +3 -0
  24. package/templates/rust/server/actions/hello.jsbundle +47 -0
  25. package/templates/rust/server/routes.json +22 -0
  26. package/templates/rust/server/src/action_management.rs +131 -0
  27. package/templates/rust/server/src/actions_rust/mod.rs +19 -0
  28. package/templates/rust/server/src/actions_rust/rust_hello.rs +14 -0
  29. package/templates/rust/server/src/errors.rs +10 -0
  30. package/templates/rust/server/src/extensions.rs +989 -0
  31. package/templates/rust/server/src/main.rs +437 -0
  32. package/templates/rust/server/src/utils.rs +33 -0
  33. package/templates/rust/titan/bundle.js +157 -0
  34. package/templates/rust/titan/dev.js +266 -0
  35. package/templates/{titan → rust/titan}/titan.js +122 -98
  36. package/titanpl-sdk/templates/Dockerfile +4 -17
  37. package/titanpl-sdk/templates/server/src/extensions.rs +218 -423
  38. package/titanpl-sdk/templates/server/src/main.rs +68 -134
  39. package/templates/_gitignore +0 -25
  40. package/templates/titan/dev.js +0 -144
  41. /package/templates/{Dockerfile → js/Dockerfile} +0 -0
  42. /package/templates/{.dockerignore → js/_dockerignore} +0 -0
  43. /package/templates/{app → js/app}/actions/hello.js +0 -0
  44. /package/templates/{app → js/app}/app.js +0 -0
  45. /package/templates/{app → js/app}/titan.d.ts +0 -0
  46. /package/templates/{jsconfig.json → js/jsconfig.json} +0 -0
  47. /package/templates/{server → js/server}/Cargo.lock +0 -0
  48. /package/templates/{server → js/server}/Cargo.toml +0 -0
  49. /package/templates/{server → js/server}/action_map.json +0 -0
  50. /package/templates/{server → js/server}/routes.json +0 -0
  51. /package/templates/{server → js/server}/src/action_management.rs +0 -0
  52. /package/templates/{server → js/server}/src/errors.rs +0 -0
  53. /package/templates/{server → js/server}/src/utils.rs +0 -0
@@ -0,0 +1,266 @@
1
+ import chokidar from "chokidar";
2
+ import { spawn, execSync } from "child_process";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import fs from "fs";
6
+ import { bundle } from "./bundle.js";
7
+
8
+ // Required for __dirname in ES modules
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+
13
+ // Colors
14
+ import { createRequire } from "module";
15
+
16
+ // Colors
17
+ const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
18
+ const green = (t) => `\x1b[32m${t}\x1b[0m`;
19
+ const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
20
+ const red = (t) => `\x1b[31m${t}\x1b[0m`;
21
+ const gray = (t) => `\x1b[90m${t}\x1b[0m`;
22
+ const bold = (t) => `\x1b[1m${t}\x1b[0m`;
23
+
24
+ function getTitanVersion() {
25
+ try {
26
+ const require = createRequire(import.meta.url);
27
+ const pkgPath = require.resolve("@ezetgalaxy/titan/package.json");
28
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
29
+ } catch (e) {
30
+ try {
31
+ // Check levels up to find the framework root
32
+ let cur = __dirname;
33
+ for (let i = 0; i < 5; i++) {
34
+ const pkgPath = path.join(cur, "package.json");
35
+ if (fs.existsSync(pkgPath)) {
36
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
37
+ if (pkg.name === "@ezetgalaxy/titan") return pkg.version;
38
+ }
39
+ cur = path.join(cur, "..");
40
+ }
41
+ } catch (e2) { }
42
+ }
43
+ return "0.1.0";
44
+ }
45
+
46
+ let serverProcess = null;
47
+ let isKilling = false;
48
+
49
+ // ... (killServer same as before)
50
+ async function killServer() {
51
+ if (!serverProcess) return;
52
+
53
+ isKilling = true;
54
+ const pid = serverProcess.pid;
55
+ const killPromise = new Promise((resolve) => {
56
+ if (serverProcess.exitCode !== null) return resolve();
57
+ serverProcess.once("close", resolve);
58
+ });
59
+
60
+ if (process.platform === "win32") {
61
+ try {
62
+ execSync(`taskkill /pid ${pid} /f /t`, { stdio: 'ignore' });
63
+ } catch (e) {
64
+ // Ignore errors if process is already dead
65
+ }
66
+ } else {
67
+ serverProcess.kill();
68
+ }
69
+
70
+ try {
71
+ await killPromise;
72
+ } catch (e) { }
73
+ serverProcess = null;
74
+ isKilling = false;
75
+ }
76
+
77
+ const delay = (ms) => new Promise(res => setTimeout(res, ms));
78
+
79
+ let spinnerTimer = null;
80
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
81
+ let frameIdx = 0;
82
+
83
+ function startSpinner(text) {
84
+ if (spinnerTimer) clearInterval(spinnerTimer);
85
+ process.stdout.write("\x1B[?25l"); // Hide cursor
86
+ spinnerTimer = setInterval(() => {
87
+ process.stdout.write(`\r ${cyan(frames[frameIdx])} ${gray(text)}`);
88
+ frameIdx = (frameIdx + 1) % frames.length;
89
+ }, 80);
90
+ }
91
+
92
+ function stopSpinner(success = true, text = "") {
93
+ if (spinnerTimer) {
94
+ clearInterval(spinnerTimer);
95
+ spinnerTimer = null;
96
+ }
97
+ process.stdout.write("\r\x1B[K"); // Clear line
98
+ process.stdout.write("\x1B[?25h"); // Show cursor
99
+ if (text) {
100
+ if (success) {
101
+ console.log(` ${green("✔")} ${green(text)}`);
102
+ } else {
103
+ console.log(` ${red("✖")} ${red(text)}`);
104
+ }
105
+ }
106
+ }
107
+
108
+ async function startRustServer(retryCount = 0) {
109
+ const waitTime = retryCount > 0 ? 500 : 200;
110
+
111
+ await killServer();
112
+ await delay(waitTime);
113
+
114
+ const serverPath = path.join(process.cwd(), "server");
115
+ const startTime = Date.now();
116
+
117
+ startSpinner("Stabilizing your app on its orbit...");
118
+
119
+ let isReady = false;
120
+ let stdoutBuffer = "";
121
+ let buildLogs = "";
122
+
123
+ // If it takes more than 15s, update the message
124
+ const slowTimer = setTimeout(() => {
125
+ if (!isReady && !isKilling) {
126
+ startSpinner("Still stabilizing... (the first orbit takes longer)");
127
+ }
128
+ }, 15000);
129
+
130
+ serverProcess = spawn("cargo", ["run", "--quiet"], {
131
+ cwd: serverPath,
132
+ stdio: ["ignore", "pipe", "pipe"],
133
+ env: { ...process.env, CARGO_INCREMENTAL: "1" }
134
+ });
135
+
136
+ serverProcess.on("error", (err) => {
137
+ stopSpinner(false, "Failed to start orbit");
138
+ console.error(red(`[Titan] Error: ${err.message}`));
139
+ });
140
+
141
+ serverProcess.stderr.on("data", (data) => {
142
+ const str = data.toString();
143
+ if (isReady) {
144
+ process.stderr.write(data);
145
+ } else {
146
+ buildLogs += str;
147
+ }
148
+ });
149
+
150
+ serverProcess.stdout.on("data", (data) => {
151
+ const out = data.toString();
152
+
153
+ if (!isReady) {
154
+ stdoutBuffer += out;
155
+ if (stdoutBuffer.includes("Titan server running") || stdoutBuffer.includes("████████╗")) {
156
+ isReady = true;
157
+ clearTimeout(slowTimer);
158
+ stopSpinner(true, "Your app is now orbiting Titan Planet");
159
+ process.stdout.write(stdoutBuffer);
160
+ stdoutBuffer = "";
161
+ }
162
+ } else {
163
+ process.stdout.write(data);
164
+ }
165
+ });
166
+
167
+ serverProcess.on("close", async (code) => {
168
+ clearTimeout(slowTimer);
169
+ if (isKilling) return;
170
+ const runTime = Date.now() - startTime;
171
+
172
+ if (code !== 0 && code !== null) {
173
+ stopSpinner(false, "Orbit stabilization failed");
174
+ if (!isReady) {
175
+ console.log(gray("\n--- Build Logs ---"));
176
+ console.log(buildLogs);
177
+ console.log(gray("------------------\n"));
178
+ }
179
+
180
+ if (runTime < 15000 && retryCount < 5) {
181
+ await delay(2000);
182
+ await startRustServer(retryCount + 1);
183
+ }
184
+ }
185
+ });
186
+ }
187
+
188
+ async function rebuild() {
189
+ try {
190
+ execSync("node app/app.js", { stdio: "ignore" });
191
+ await bundle();
192
+ } catch (e) {
193
+ stopSpinner(false, "Failed to prepare runtime");
194
+ console.log(red(`[Titan] Error: ${e.message}`));
195
+ }
196
+ }
197
+
198
+ async function startDev() {
199
+ const root = process.cwd();
200
+ const actionsDir = path.join(root, "app", "actions");
201
+ let hasRust = false;
202
+ if (fs.existsSync(actionsDir)) {
203
+ hasRust = fs.readdirSync(actionsDir).some(f => f.endsWith(".rs"));
204
+ }
205
+
206
+ const mode = hasRust ? "Rust + JS Actions" : "JS Actions";
207
+ const version = getTitanVersion();
208
+
209
+ console.clear();
210
+ console.log("");
211
+ console.log(` ${bold(cyan("Titan Planet"))} ${gray("v" + version)} ${yellow("[ Dev Mode ]")}`);
212
+ console.log("");
213
+ console.log(` ${gray("Type: ")} ${mode}`);
214
+ console.log(` ${gray("Hot Reload: ")} ${green("Enabled")}`);
215
+
216
+ if (fs.existsSync(path.join(root, ".env"))) {
217
+ console.log(` ${gray("Env: ")} ${yellow("Loaded")}`);
218
+ }
219
+ console.log("");
220
+
221
+ try {
222
+ await rebuild();
223
+ await startRustServer();
224
+ } catch (e) {
225
+ // console.log(red("[Titan] Initial build failed. Waiting for changes..."));
226
+ }
227
+
228
+ const watcher = chokidar.watch(["app", ".env"], {
229
+ ignoreInitial: true,
230
+ awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
231
+ });
232
+
233
+ let timer = null;
234
+ watcher.on("all", async (event, file) => {
235
+ if (timer) clearTimeout(timer);
236
+ timer = setTimeout(async () => {
237
+ // console.log("");
238
+ /*
239
+ if (file.includes(".env")) {
240
+ console.log(yellow("[Titan] Env Refreshed"));
241
+ } else {
242
+ console.log(cyan(`[Titan] Change: ${path.basename(file)}`));
243
+ }
244
+ */
245
+ try {
246
+ await killServer();
247
+ await rebuild();
248
+ await startRustServer();
249
+ } catch (e) {
250
+ // console.log(red("[Titan] Build failed -- waiting for changes..."));
251
+ }
252
+ }, 300);
253
+ });
254
+ }
255
+
256
+ async function handleExit() {
257
+ stopSpinner();
258
+ console.log(gray("\n[Titan] Stopping server..."));
259
+ await killServer();
260
+ process.exit(0);
261
+ }
262
+
263
+ process.on("SIGINT", handleExit);
264
+ process.on("SIGTERM", handleExit);
265
+
266
+ startDev();
@@ -1,98 +1,122 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { bundle } from "./bundle.js";
4
-
5
- const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
6
- const green = (t) => `\x1b[32m${t}\x1b[0m`;
7
-
8
- const routes = {};
9
- const dynamicRoutes = {};
10
- const actionMap = {};
11
-
12
- function addRoute(method, route) {
13
- const key = `${method.toUpperCase()}:${route}`;
14
-
15
-
16
- return {
17
- reply(value) {
18
- routes[key] = {
19
- type: typeof value === "object" ? "json" : "text",
20
- value
21
- };
22
- },
23
-
24
- action(name) {
25
- if (route.includes(":")) {
26
- if (!dynamicRoutes[method]) dynamicRoutes[method] = [];
27
- dynamicRoutes[method].push({
28
- method: method.toUpperCase(),
29
- pattern: route,
30
- action: name
31
- });
32
- } else {
33
- routes[key] = {
34
- type: "action",
35
- value: name
36
- };
37
- actionMap[key] = name;
38
- }
39
- }
40
- };
41
- }
42
-
43
- const t = {
44
- get(route) {
45
- return addRoute("GET", route);
46
- },
47
-
48
- post(route) {
49
- return addRoute("POST", route);
50
- },
51
-
52
- log(module, msg) {
53
- console.log(`[\x1b[35m${module}\x1b[0m] ${msg}`);
54
- },
55
-
56
- async start(port = 3000, msg = "") {
57
- try {
58
- console.log(cyan("[Titan] Preparing runtime..."));
59
- await bundle();
60
-
61
- const base = path.join(process.cwd(), "server");
62
- if (!fs.existsSync(base)) {
63
- fs.mkdirSync(base, { recursive: true });
64
- }
65
-
66
- const routesPath = path.join(base, "routes.json");
67
- const actionMapPath = path.join(base, "action_map.json");
68
-
69
- fs.writeFileSync(
70
- routesPath,
71
- JSON.stringify(
72
- {
73
- __config: { port },
74
- routes,
75
- __dynamic_routes: Object.values(dynamicRoutes).flat()
76
- },
77
- null,
78
- 2
79
- )
80
- );
81
-
82
- fs.writeFileSync(
83
- actionMapPath,
84
- JSON.stringify(actionMap, null, 2)
85
- );
86
-
87
- console.log(green("✔ Titan metadata written successfully"));
88
- if (msg) console.log(cyan(msg));
89
-
90
- } catch (e) {
91
- console.error(`\x1b[31m[Titan] Build Error: ${e.message}\x1b[0m`);
92
- process.exit(1);
93
- }
94
- }
95
- };
96
-
97
- globalThis.t = t;
98
- export default t;
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { bundle } from "./bundle.js";
4
+
5
+ const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
6
+ const green = (t) => `\x1b[32m${t}\x1b[0m`;
7
+
8
+ const routes = {};
9
+ const dynamicRoutes = {};
10
+ const actionMap = {};
11
+
12
+ function addRoute(method, route) {
13
+ const key = `${method.toUpperCase()}:${route}`;
14
+
15
+
16
+ return {
17
+ reply(value) {
18
+ routes[key] = {
19
+ type: typeof value === "object" ? "json" : "text",
20
+ value
21
+ };
22
+ },
23
+
24
+ action(name) {
25
+ if (route.includes(":")) {
26
+ if (!dynamicRoutes[method]) dynamicRoutes[method] = [];
27
+ dynamicRoutes[method].push({
28
+ method: method.toUpperCase(),
29
+ pattern: route,
30
+ action: name
31
+ });
32
+ } else {
33
+ routes[key] = {
34
+ type: "action",
35
+ value: name
36
+ };
37
+ actionMap[key] = name;
38
+ }
39
+ }
40
+ };
41
+ }
42
+
43
+ /**
44
+ * @typedef {Object} RouteHandler
45
+ * @property {(value: any) => void} reply - Send a direct response
46
+ * @property {(name: string) => void} action - Bind to a server-side action
47
+ */
48
+
49
+ /**
50
+ * Titan App Builder
51
+ */
52
+ const t = {
53
+ /**
54
+ * Define a GET route
55
+ * @param {string} route
56
+ * @returns {RouteHandler}
57
+ */
58
+ get(route) {
59
+ return addRoute("GET", route);
60
+ },
61
+
62
+ /**
63
+ * Define a POST route
64
+ * @param {string} route
65
+ * @returns {RouteHandler}
66
+ */
67
+ post(route) {
68
+ return addRoute("POST", route);
69
+ },
70
+
71
+ log(module, msg) {
72
+ console.log(`[\x1b[35m${module}\x1b[0m] ${msg}`);
73
+ },
74
+
75
+ /**
76
+ * Start the Titan Server
77
+ * @param {number} [port=3000]
78
+ * @param {string} [msg=""]
79
+ */
80
+ async start(port = 3000, msg = "") {
81
+ try {
82
+ console.log(cyan("[Titan] Preparing runtime..."));
83
+ await bundle();
84
+
85
+ const base = path.join(process.cwd(), "server");
86
+ if (!fs.existsSync(base)) {
87
+ fs.mkdirSync(base, { recursive: true });
88
+ }
89
+
90
+ const routesPath = path.join(base, "routes.json");
91
+ const actionMapPath = path.join(base, "action_map.json");
92
+
93
+ fs.writeFileSync(
94
+ routesPath,
95
+ JSON.stringify(
96
+ {
97
+ __config: { port },
98
+ routes,
99
+ __dynamic_routes: Object.values(dynamicRoutes).flat()
100
+ },
101
+ null,
102
+ 2
103
+ )
104
+ );
105
+
106
+ fs.writeFileSync(
107
+ actionMapPath,
108
+ JSON.stringify(actionMap, null, 2)
109
+ );
110
+
111
+ console.log(green("✔ Titan metadata written successfully"));
112
+ if (msg) console.log(cyan(msg));
113
+
114
+ } catch (e) {
115
+ console.error(`\x1b[31m[Titan] Build Error: ${e.message}\x1b[0m`);
116
+ process.exit(1);
117
+ }
118
+ }
119
+ };
120
+
121
+
122
+ export default t;
@@ -18,20 +18,6 @@ COPY . .
18
18
  # Install JS dependencies (needed for Titan DSL + bundler)
19
19
  RUN npm install
20
20
 
21
- SHELL ["/bin/bash", "-c"]
22
-
23
- # Extract Titan extensions into .ext
24
- RUN mkdir -p /app/.ext && \
25
- find /app/node_modules -maxdepth 5 -type f -name "titan.json" -print0 | \
26
- while IFS= read -r -d '' file; do \
27
- pkg_dir="$(dirname "$file")"; \
28
- pkg_name="$(basename "$pkg_dir")"; \
29
- echo "Copying Titan extension: $pkg_name from $pkg_dir"; \
30
- cp -r "$pkg_dir" "/app/.ext/$pkg_name"; \
31
- done && \
32
- echo "Extensions in .ext:" && \
33
- ls -R /app/.ext
34
-
35
21
  # Build Titan metadata + bundle JS actions
36
22
  RUN titan build
37
23
 
@@ -48,7 +34,7 @@ FROM debian:stable-slim
48
34
  WORKDIR /app
49
35
 
50
36
  # Copy Rust binary from builder stage
51
- COPY --from=builder /app/server/target/release/titan-server ./titan-server
37
+ COPY --from=builder /app/server/target/release/server ./titan-server
52
38
 
53
39
  # Copy Titan routing metadata
54
40
  COPY --from=builder /app/server/routes.json ./routes.json
@@ -58,9 +44,10 @@ COPY --from=builder /app/server/action_map.json ./action_map.json
58
44
  RUN mkdir -p /app/actions
59
45
  COPY --from=builder /app/server/actions /app/actions
60
46
 
61
- # Copy only Titan extensions
62
- COPY --from=builder /app/.ext ./.ext
47
+ COPY --from=builder /app/db /app/assets
63
48
 
49
+ # Expose Titan port
64
50
  EXPOSE 3000
65
51
 
52
+ # Start Titan
66
53
  CMD ["./titan-server"]