@ezetgalaxy/titan 26.8.2 → 26.9.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 (97) hide show
  1. package/README.md +65 -25
  2. package/index.js +62 -15
  3. package/package.json +2 -2
  4. package/templates/extension/README.md +104 -104
  5. package/templates/extension/index.js +27 -27
  6. package/templates/extension/jsconfig.json +12 -12
  7. package/templates/extension/native/Cargo.toml +9 -9
  8. package/templates/extension/native/src/lib.rs +5 -5
  9. package/templates/extension/package.json +20 -20
  10. package/templates/extension/titan.json +17 -17
  11. package/templates/js/Dockerfile +66 -66
  12. package/templates/js/_dockerignore +3 -3
  13. package/templates/js/_gitignore +1 -0
  14. package/templates/js/app/actions/hello.js +5 -5
  15. package/templates/js/app/titan.d.ts +87 -87
  16. package/templates/js/jsconfig.json +18 -18
  17. package/templates/js/server/src/action_management.rs +131 -131
  18. package/templates/js/server/src/errors.rs +10 -10
  19. package/templates/js/server/src/extensions.rs +989 -989
  20. package/templates/js/server/src/utils.rs +33 -33
  21. package/templates/js/titan/bundle.js +78 -78
  22. package/templates/js/titan/dev.js +35 -3
  23. package/templates/js/titan/titan.js +122 -122
  24. package/templates/rust/Dockerfile +66 -66
  25. package/templates/rust/_dockerignore +3 -3
  26. package/templates/rust/_gitignore +1 -0
  27. package/templates/rust/app/actions/hello.js +5 -5
  28. package/templates/rust/app/actions/rust_hello.rs +14 -14
  29. package/templates/rust/app/titan.d.ts +101 -101
  30. package/templates/rust/jsconfig.json +18 -18
  31. package/templates/rust/server/src/action_management.rs +131 -131
  32. package/templates/rust/server/src/errors.rs +10 -10
  33. package/templates/rust/server/src/extensions.rs +989 -989
  34. package/templates/rust/server/src/utils.rs +33 -33
  35. package/templates/rust/titan/dev.js +36 -12
  36. package/templates/rust-ts/Dockerfile +66 -0
  37. package/templates/rust-ts/_dockerignore +3 -0
  38. package/templates/rust-ts/_gitignore +38 -0
  39. package/templates/rust-ts/app/actions/hello.ts +5 -0
  40. package/templates/rust-ts/app/actions/rust_hello.rs +14 -0
  41. package/templates/rust-ts/app/app.ts +11 -0
  42. package/templates/rust-ts/app/titan.d.ts +101 -0
  43. package/templates/rust-ts/package.json +14 -0
  44. package/templates/rust-ts/server/Cargo.lock +2869 -0
  45. package/templates/rust-ts/server/Cargo.toml +39 -0
  46. package/templates/rust-ts/server/src/action_management.rs +131 -0
  47. package/templates/rust-ts/server/src/errors.rs +51 -0
  48. package/templates/rust-ts/server/src/extensions.rs +989 -0
  49. package/templates/rust-ts/server/src/main.rs +468 -0
  50. package/templates/rust-ts/server/src/utils.rs +33 -0
  51. package/templates/rust-ts/titan/bundle.js +157 -0
  52. package/templates/rust-ts/titan/dev.js +402 -0
  53. package/templates/rust-ts/titan/titan.js +122 -0
  54. package/templates/rust-ts/tsconfig.json +21 -0
  55. package/templates/ts/Dockerfile +66 -0
  56. package/templates/ts/_dockerignore +3 -0
  57. package/templates/ts/_gitignore +38 -0
  58. package/templates/ts/app/actions/hello.ts +9 -0
  59. package/templates/ts/app/app.ts +10 -0
  60. package/templates/ts/app/titan.d.ts +102 -0
  61. package/templates/ts/package.json +26 -0
  62. package/templates/ts/server/Cargo.lock +2869 -0
  63. package/templates/ts/server/Cargo.toml +27 -0
  64. package/templates/ts/server/src/action_management.rs +131 -0
  65. package/templates/ts/server/src/errors.rs +51 -0
  66. package/templates/ts/server/src/extensions.rs +989 -0
  67. package/templates/ts/server/src/main.rs +437 -0
  68. package/templates/ts/server/src/utils.rs +33 -0
  69. package/templates/ts/titan/bundle.js +78 -0
  70. package/templates/ts/titan/dev.js +402 -0
  71. package/templates/ts/titan/titan.js +122 -0
  72. package/templates/ts/tsconfig.json +16 -0
  73. package/titanpl-sdk/README.md +109 -109
  74. package/titanpl-sdk/bin/run.js +254 -254
  75. package/titanpl-sdk/index.d.ts +46 -46
  76. package/titanpl-sdk/index.js +5 -5
  77. package/titanpl-sdk/package.json +32 -32
  78. package/titanpl-sdk/templates/.dockerignore +3 -3
  79. package/titanpl-sdk/templates/Dockerfile +53 -53
  80. package/titanpl-sdk/templates/app/actions/hello.js +5 -5
  81. package/titanpl-sdk/templates/app/titan.d.ts +87 -87
  82. package/titanpl-sdk/templates/jsconfig.json +18 -18
  83. package/titanpl-sdk/templates/server/src/action_management.rs +131 -131
  84. package/titanpl-sdk/templates/server/src/errors.rs +10 -10
  85. package/titanpl-sdk/templates/server/src/extensions.rs +640 -640
  86. package/titanpl-sdk/templates/server/src/utils.rs +33 -33
  87. package/titanpl-sdk/templates/titan/bundle.js +65 -65
  88. package/titanpl-sdk/templates/titan/dev.js +113 -113
  89. package/titanpl-sdk/templates/titan/titan.js +98 -98
  90. package/templates/js/server/action_map.json +0 -3
  91. package/templates/js/server/actions/hello.jsbundle +0 -48
  92. package/templates/js/server/routes.json +0 -16
  93. package/templates/rust/server/action_map.json +0 -3
  94. package/templates/rust/server/actions/hello.jsbundle +0 -47
  95. package/templates/rust/server/routes.json +0 -22
  96. package/templates/rust/server/src/actions_rust/mod.rs +0 -19
  97. package/templates/rust/server/src/actions_rust/rust_hello.rs +0 -14
@@ -1,33 +1,33 @@
1
- pub fn blue(s: &str) -> String {
2
- format!("\x1b[38;5;39m{}\x1b[0m", s)
3
- }
4
- pub fn white(s: &str) -> String {
5
- format!("\x1b[39m{}\x1b[0m", s)
6
- }
7
- pub fn yellow(s: &str) -> String {
8
- format!("\x1b[33m{}\x1b[0m", s)
9
- }
10
- pub fn green(s: &str) -> String {
11
- format!("\x1b[32m{}\x1b[0m", s)
12
- }
13
- pub fn gray(s: &str) -> String {
14
- format!("\x1b[90m{}\x1b[0m", s)
15
- }
16
- pub fn red(s: &str) -> String {
17
- format!("\x1b[31m{}\x1b[0m", s)
18
- }
19
-
20
- pub fn parse_expires_in(value: &str) -> Option<u64> {
21
- let (num, unit) = value.split_at(value.len() - 1);
22
- let n: u64 = num.parse().ok()?;
23
-
24
- match unit {
25
- "s" => Some(n),
26
- "m" => Some(n * 60),
27
- "h" => Some(n * 60 * 60),
28
- "d" => Some(n * 60 * 60 * 24),
29
- _ => None,
30
- }
31
- }
32
-
33
-
1
+ pub fn blue(s: &str) -> String {
2
+ format!("\x1b[38;5;39m{}\x1b[0m", s)
3
+ }
4
+ pub fn white(s: &str) -> String {
5
+ format!("\x1b[39m{}\x1b[0m", s)
6
+ }
7
+ pub fn yellow(s: &str) -> String {
8
+ format!("\x1b[33m{}\x1b[0m", s)
9
+ }
10
+ pub fn green(s: &str) -> String {
11
+ format!("\x1b[32m{}\x1b[0m", s)
12
+ }
13
+ pub fn gray(s: &str) -> String {
14
+ format!("\x1b[90m{}\x1b[0m", s)
15
+ }
16
+ pub fn red(s: &str) -> String {
17
+ format!("\x1b[31m{}\x1b[0m", s)
18
+ }
19
+
20
+ pub fn parse_expires_in(value: &str) -> Option<u64> {
21
+ let (num, unit) = value.split_at(value.len() - 1);
22
+ let n: u64 = num.parse().ok()?;
23
+
24
+ match unit {
25
+ "s" => Some(n),
26
+ "m" => Some(n * 60),
27
+ "h" => Some(n * 60 * 60),
28
+ "d" => Some(n * 60 * 60 * 24),
29
+ _ => None,
30
+ }
31
+ }
32
+
33
+
@@ -1,78 +1,78 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import esbuild from "esbuild";
4
-
5
- const root = process.cwd();
6
- const actionsDir = path.join(root, "app", "actions");
7
- const outDir = path.join(root, "server", "actions");
8
-
9
- export async function bundle() {
10
- const start = Date.now();
11
- await bundleJs();
12
- // console.log(`[Titan] Bundle finished in ${((Date.now() - start) / 1000).toFixed(2)}s`);
13
- }
14
-
15
- async function bundleJs() {
16
- // console.log("[Titan] Bundling JS actions...");
17
-
18
- fs.mkdirSync(outDir, { recursive: true });
19
-
20
- // Clean old bundles
21
- if (fs.existsSync(outDir)) {
22
- const oldFiles = fs.readdirSync(outDir);
23
- for (const file of oldFiles) {
24
- fs.unlinkSync(path.join(outDir, file));
25
- }
26
- }
27
-
28
- const files = fs.readdirSync(actionsDir).filter(f => f.endsWith(".js") || f.endsWith(".ts"));
29
- if (files.length === 0) return;
30
-
31
- // console.log(`[Titan] Bundling ${files.length} JS actions...`);
32
-
33
- for (const file of files) {
34
- const actionName = path.basename(file, path.extname(file));
35
-
36
- const entry = path.join(actionsDir, file);
37
-
38
- // Rust runtime expects `.jsbundle` extension — consistent with previous design
39
- const outfile = path.join(outDir, actionName + ".jsbundle");
40
-
41
- // console.log(`[Titan] Bundling ${entry} → ${outfile}`);
42
-
43
- await esbuild.build({
44
- entryPoints: [entry],
45
- outfile,
46
- bundle: true,
47
- format: "iife",
48
- globalName: "__titan_exports",
49
- platform: "neutral",
50
- target: "es2020",
51
- logLevel: "silent",
52
- banner: {
53
- js: "const defineAction = (fn) => fn;"
54
- },
55
-
56
- footer: {
57
- js: `
58
- (function () {
59
- const fn =
60
- __titan_exports["${actionName}"] ||
61
- __titan_exports.default;
62
-
63
- if (typeof fn !== "function") {
64
- throw new Error("[Titan] Action '${actionName}' not found or not a function");
65
- }
66
-
67
- globalThis["${actionName}"] = function(request_arg) {
68
- globalThis.req = request_arg;
69
- return fn(request_arg);
70
- };
71
- })();
72
- `
73
- }
74
- });
75
- }
76
-
77
- // console.log("[Titan] JS Bundling finished.");
78
- }
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import esbuild from "esbuild";
4
+
5
+ const root = process.cwd();
6
+ const actionsDir = path.join(root, "app", "actions");
7
+ const outDir = path.join(root, "server", "actions");
8
+
9
+ export async function bundle() {
10
+ const start = Date.now();
11
+ await bundleJs();
12
+ // console.log(`[Titan] Bundle finished in ${((Date.now() - start) / 1000).toFixed(2)}s`);
13
+ }
14
+
15
+ async function bundleJs() {
16
+ // console.log("[Titan] Bundling JS actions...");
17
+
18
+ fs.mkdirSync(outDir, { recursive: true });
19
+
20
+ // Clean old bundles
21
+ if (fs.existsSync(outDir)) {
22
+ const oldFiles = fs.readdirSync(outDir);
23
+ for (const file of oldFiles) {
24
+ fs.unlinkSync(path.join(outDir, file));
25
+ }
26
+ }
27
+
28
+ const files = fs.readdirSync(actionsDir).filter(f => f.endsWith(".js") || f.endsWith(".ts"));
29
+ if (files.length === 0) return;
30
+
31
+ // console.log(`[Titan] Bundling ${files.length} JS actions...`);
32
+
33
+ for (const file of files) {
34
+ const actionName = path.basename(file, path.extname(file));
35
+
36
+ const entry = path.join(actionsDir, file);
37
+
38
+ // Rust runtime expects `.jsbundle` extension — consistent with previous design
39
+ const outfile = path.join(outDir, actionName + ".jsbundle");
40
+
41
+ // console.log(`[Titan] Bundling ${entry} → ${outfile}`);
42
+
43
+ await esbuild.build({
44
+ entryPoints: [entry],
45
+ outfile,
46
+ bundle: true,
47
+ format: "iife",
48
+ globalName: "__titan_exports",
49
+ platform: "neutral",
50
+ target: "es2020",
51
+ logLevel: "silent",
52
+ banner: {
53
+ js: "const defineAction = (fn) => fn;"
54
+ },
55
+
56
+ footer: {
57
+ js: `
58
+ (function () {
59
+ const fn =
60
+ __titan_exports["${actionName}"] ||
61
+ __titan_exports.default;
62
+
63
+ if (typeof fn !== "function") {
64
+ throw new Error("[Titan] Action '${actionName}' not found or not a function");
65
+ }
66
+
67
+ globalThis["${actionName}"] = function(request_arg) {
68
+ globalThis.req = request_arg;
69
+ return fn(request_arg);
70
+ };
71
+ })();
72
+ `
73
+ }
74
+ });
75
+ }
76
+
77
+ // console.log("[Titan] JS Bundling finished.");
78
+ }
@@ -3,7 +3,6 @@ import { spawn, execSync } from "child_process";
3
3
  import path from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import fs from "fs";
6
- import { bundle } from "./bundle.js";
7
6
 
8
7
  // Required for __dirname in ES modules
9
8
  const __filename = fileURLToPath(import.meta.url);
@@ -39,12 +38,20 @@ function getTitanVersion() {
39
38
  cur = path.join(cur, "..");
40
39
  }
41
40
  } catch (e2) { }
41
+
42
+ try {
43
+ // Fallback to calling tit --version
44
+ const output = execSync("tit --version", { encoding: "utf-8" }).trim();
45
+ const match = output.match(/v(\d+\.\d+\.\d+)/);
46
+ if (match) return match[1];
47
+ } catch (e3) { }
42
48
  }
43
49
  return "0.1.0";
44
50
  }
45
51
 
46
52
  let serverProcess = null;
47
53
  let isKilling = false;
54
+ let isFirstBoot = true;
48
55
 
49
56
  // ... (killServer same as before)
50
57
  async function killServer() {
@@ -156,7 +163,24 @@ async function startRustServer(retryCount = 0) {
156
163
  isReady = true;
157
164
  clearTimeout(slowTimer);
158
165
  stopSpinner(true, "Your app is now orbiting Titan Planet");
159
- process.stdout.write(stdoutBuffer);
166
+
167
+ if (isFirstBoot) {
168
+ process.stdout.write(stdoutBuffer);
169
+ isFirstBoot = false;
170
+ } else {
171
+ // On subsequent reloads, only print non-banner lines from the buffer
172
+ const lines = stdoutBuffer.split("\n");
173
+ for (const line of lines) {
174
+ const isBanner = line.includes("Titan server running") ||
175
+ line.includes("████████╗") ||
176
+ line.includes("╚══") ||
177
+ line.includes(" ██║") ||
178
+ line.includes(" ╚═╝");
179
+ if (!isBanner && line.trim()) {
180
+ process.stdout.write(line + "\n");
181
+ }
182
+ }
183
+ }
160
184
  stdoutBuffer = "";
161
185
  }
162
186
  } else {
@@ -203,7 +227,15 @@ async function startDev() {
203
227
  hasRust = fs.readdirSync(actionsDir).some(f => f.endsWith(".rs"));
204
228
  }
205
229
 
206
- const mode = hasRust ? "Rust + JS Actions" : "JS Actions";
230
+ const isTs = fs.existsSync(path.join(root, "tsconfig.json")) ||
231
+ fs.existsSync(path.join(root, "app", "app.ts"));
232
+
233
+ let mode = "";
234
+ if (hasRust) {
235
+ mode = isTs ? "Rust + TS Actions" : "Rust + JS Actions";
236
+ } else {
237
+ mode = isTs ? "TS Actions" : "JS Actions";
238
+ }
207
239
  const version = getTitanVersion();
208
240
 
209
241
  console.clear();
@@ -1,122 +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
- /**
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;
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;
@@ -1,66 +1,66 @@
1
- # ================================================================
2
- # STAGE 1 — Build Titan (JS → Rust)
3
- # ================================================================
4
- FROM rust:1.91.1 AS builder
5
-
6
- # Install Node for Titan CLI + bundler
7
- RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
8
- && apt-get install -y nodejs
9
-
10
- # Install Titan CLI (latest)
11
- RUN npm install -g @ezetgalaxy/titan@latest
12
-
13
- WORKDIR /app
14
-
15
- # Copy project files
16
- COPY . .
17
-
18
- # Install JS dependencies (needed for Titan DSL + bundler)
19
- RUN npm install
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
- # Build Titan metadata + bundle JS actions
36
- RUN titan build
37
-
38
- # Build Rust binary
39
- RUN cd server && cargo build --release
40
-
41
-
42
-
43
- # ================================================================
44
- # STAGE 2 — Runtime Image (Lightweight)
45
- # ================================================================
46
- FROM debian:stable-slim
47
-
48
- WORKDIR /app
49
-
50
- # Copy Rust binary from builder stage
51
- COPY --from=builder /app/server/target/release/titan-server ./titan-server
52
-
53
- # Copy Titan routing metadata
54
- COPY --from=builder /app/server/routes.json ./routes.json
55
- COPY --from=builder /app/server/action_map.json ./action_map.json
56
-
57
- # Copy Titan JS bundles
58
- RUN mkdir -p /app/actions
59
- COPY --from=builder /app/server/actions /app/actions
60
-
61
- # Copy only Titan extensions
62
- COPY --from=builder /app/.ext ./.ext
63
-
64
- EXPOSE 3000
65
-
66
- CMD ["./titan-server"]
1
+ # ================================================================
2
+ # STAGE 1 — Build Titan (JS → Rust)
3
+ # ================================================================
4
+ FROM rust:1.91.1 AS builder
5
+
6
+ # Install Node for Titan CLI + bundler
7
+ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
8
+ && apt-get install -y nodejs
9
+
10
+ # Install Titan CLI (latest)
11
+ RUN npm install -g @ezetgalaxy/titan@latest
12
+
13
+ WORKDIR /app
14
+
15
+ # Copy project files
16
+ COPY . .
17
+
18
+ # Install JS dependencies (needed for Titan DSL + bundler)
19
+ RUN npm install
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
+ # Build Titan metadata + bundle JS actions
36
+ RUN titan build
37
+
38
+ # Build Rust binary
39
+ RUN cd server && cargo build --release
40
+
41
+
42
+
43
+ # ================================================================
44
+ # STAGE 2 — Runtime Image (Lightweight)
45
+ # ================================================================
46
+ FROM debian:stable-slim
47
+
48
+ WORKDIR /app
49
+
50
+ # Copy Rust binary from builder stage
51
+ COPY --from=builder /app/server/target/release/titan-server ./titan-server
52
+
53
+ # Copy Titan routing metadata
54
+ COPY --from=builder /app/server/routes.json ./routes.json
55
+ COPY --from=builder /app/server/action_map.json ./action_map.json
56
+
57
+ # Copy Titan JS bundles
58
+ RUN mkdir -p /app/actions
59
+ COPY --from=builder /app/server/actions /app/actions
60
+
61
+ # Copy only Titan extensions
62
+ COPY --from=builder /app/.ext ./.ext
63
+
64
+ EXPOSE 3000
65
+
66
+ CMD ["./titan-server"]
@@ -1,3 +1,3 @@
1
- node_modules
2
- npm-debug.log
3
- .git
1
+ node_modules
2
+ npm-debug.log
3
+ .git