@ezetgalaxy/titan 26.9.0 → 26.9.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.
- package/README.md +39 -17
- package/index.js +227 -120
- package/package.json +17 -5
- package/templates/{js → common}/app/titan.d.ts +1 -1
- package/templates/{rust → rust-js}/package.json +1 -1
- package/templates/rust-ts/app/actions/hello.ts +10 -4
- package/templates/rust-ts/app/app.ts +1 -1
- package/templates/rust-ts/titan/bundle.js +15 -9
- package/templates/rust-ts/titan/dev.js +2 -2
- package/templates/rust-ts/titan/runtime.d.ts +1 -0
- package/templates/rust-ts/titan/runtime.js +1 -0
- package/templates/rust-ts/titan/titan.d.ts +117 -0
- package/templates/rust-ts/titan/titan.js +95 -95
- package/templates/ts/app/actions/hello.ts +2 -0
- package/templates/ts/app/app.ts +1 -1
- package/templates/ts/titan/builder.js +121 -0
- package/templates/ts/titan/bundle.js +9 -11
- package/templates/ts/titan/dev.js +2 -2
- package/templates/ts/titan/runtime.d.ts +1 -0
- package/templates/ts/titan/runtime.js +1 -0
- package/templates/ts/titan/titan.d.ts +117 -0
- package/templates/ts/titan/titan.js +95 -95
- package/titanpl-sdk/README.md +4 -4
- package/titanpl-sdk/bin/run.js +74 -77
- package/titanpl-sdk/package.json +1 -1
- package/templates/rust/Dockerfile +0 -66
- package/templates/rust/_dockerignore +0 -3
- package/templates/rust/_gitignore +0 -38
- package/templates/rust/app/titan.d.ts +0 -101
- package/templates/rust-ts/Dockerfile +0 -66
- package/templates/rust-ts/_dockerignore +0 -3
- package/templates/rust-ts/_gitignore +0 -38
- package/templates/rust-ts/app/titan.d.ts +0 -101
- package/templates/ts/Dockerfile +0 -66
- package/templates/ts/_dockerignore +0 -3
- package/templates/ts/_gitignore +0 -38
- package/templates/ts/app/titan.d.ts +0 -102
- /package/templates/{js → common}/Dockerfile +0 -0
- /package/templates/{js → common}/_dockerignore +0 -0
- /package/templates/{js → common}/_gitignore +0 -0
- /package/templates/{rust → rust-js}/app/actions/hello.js +0 -0
- /package/templates/{rust → rust-js}/app/actions/rust_hello.rs +0 -0
- /package/templates/{rust → rust-js}/app/app.js +0 -0
- /package/templates/{rust → rust-js}/jsconfig.json +0 -0
- /package/templates/{rust → rust-js}/server/Cargo.lock +0 -0
- /package/templates/{rust → rust-js}/server/Cargo.toml +0 -0
- /package/templates/{rust → rust-js}/server/src/action_management.rs +0 -0
- /package/templates/{rust → rust-js}/server/src/errors.rs +0 -0
- /package/templates/{rust → rust-js}/server/src/extensions.rs +0 -0
- /package/templates/{rust → rust-js}/server/src/main.rs +0 -0
- /package/templates/{rust → rust-js}/server/src/utils.rs +0 -0
- /package/templates/{rust → rust-js}/titan/bundle.js +0 -0
- /package/templates/{rust → rust-js}/titan/dev.js +0 -0
- /package/templates/{rust → rust-js}/titan/titan.js +0 -0
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ Titan = **TS/JS productivity × Rust performance × Zero DevOps**
|
|
|
44
44
|
| Zero-config Docker deploy | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
45
45
|
| Action-based architecture | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
46
46
|
| Hot reload dev server | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
47
|
+
| Modular, Isolated Templates | ✅ Yes | ❌ No | ❌ No | ❌ No |
|
|
47
48
|
|
|
48
49
|
---
|
|
49
50
|
|
|
@@ -59,15 +60,26 @@ npm install -g @ezetgalaxy/titan
|
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
### 3. Initialize & Run
|
|
63
|
+
Titan guides you through selecting the perfect architecture for your needs.
|
|
64
|
+
|
|
62
65
|
```bash
|
|
63
66
|
titan init my-app
|
|
64
|
-
# Follow the interactive prompt to choose:
|
|
65
|
-
# - JavaScript (Standard)
|
|
66
|
-
# - TypeScript (Strict)
|
|
67
|
-
# - Rust + JavaScript (Beta)
|
|
68
|
-
# - Rust + TypeScript (Beta)
|
|
69
67
|
```
|
|
70
68
|
|
|
69
|
+
**Select your language:**
|
|
70
|
+
1. `JavaScript` (Fast, lightweight)
|
|
71
|
+
2. `TypeScript` (Strict, typed)
|
|
72
|
+
|
|
73
|
+
**Select your architecture:**
|
|
74
|
+
1. `Standard` (Pure JS/TS)
|
|
75
|
+
2. `Rust + JS/TS (Hybrid)` (High-performance native actions)
|
|
76
|
+
|
|
77
|
+
This creates one of four isolated environments:
|
|
78
|
+
* **Standard JS:** Lightweight server, zero Rust overhead.
|
|
79
|
+
* **Standard TS:** Strict server, zero Rust overhead.
|
|
80
|
+
* **Hybrid JS:** Full Rust integration + JS flexibility.
|
|
81
|
+
* **Hybrid TS:** Full Rust integration + TS strictness.
|
|
82
|
+
|
|
71
83
|
Inside your project:
|
|
72
84
|
```bash
|
|
73
85
|
cd my-app
|
|
@@ -76,7 +88,7 @@ titan dev
|
|
|
76
88
|
|
|
77
89
|
You'll see the Titan Dev Server spin up:
|
|
78
90
|
```
|
|
79
|
-
Titan Planet v26.9.
|
|
91
|
+
Titan Planet v26.9.1 [ Dev Mode ]
|
|
80
92
|
|
|
81
93
|
Type: Rust + TS Actions
|
|
82
94
|
Hot Reload: Enabled
|
|
@@ -93,34 +105,45 @@ You'll see the Titan Dev Server spin up:
|
|
|
93
105
|
|
|
94
106
|
Titan is unique because it allows you to write endpoints in **JavaScript, TypeScript, and Rust** within the same project.
|
|
95
107
|
|
|
108
|
+
| Feature | Status | Notes |
|
|
109
|
+
| :--- | :--- | :--- |
|
|
110
|
+
| **Standard JavaScript** | ✅ Stable | Production Ready |
|
|
111
|
+
| **Standard TypeScript** | 🚧 Beta | **Ready for Dev**, Production Under Testing |
|
|
112
|
+
| **Rust + JS (Hybrid)** | 🧪 Experimental | **Dev Only**, Production Under Testing |
|
|
113
|
+
| **Rust + TS (Hybrid)** | 🧪 Experimental | **Dev Only**, Production Under Testing |
|
|
114
|
+
|
|
96
115
|
### 🔵 TypeScript Actions (`app/actions/hello.ts`)
|
|
97
116
|
Fully typed, strict, and auto-compiled.
|
|
117
|
+
|
|
98
118
|
```typescript
|
|
99
|
-
import {
|
|
119
|
+
import { defineAction } from "../../titan/titan";
|
|
100
120
|
|
|
101
|
-
|
|
121
|
+
interface HelloResponse {
|
|
122
|
+
message: string;
|
|
123
|
+
user_name: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// "defineAction" provides automatic type inference for "req"
|
|
127
|
+
export const hello = defineAction((req): HelloResponse => {
|
|
102
128
|
t.log("Handling request with strict types...");
|
|
103
|
-
|
|
104
|
-
// Type checking allows safe property access
|
|
105
|
-
const user = req.body as { name: string };
|
|
106
|
-
|
|
129
|
+
|
|
107
130
|
return {
|
|
108
131
|
message: "Hello from TypeScript!",
|
|
109
|
-
user_name:
|
|
132
|
+
user_name: req.body.name || "Guest"
|
|
110
133
|
};
|
|
111
|
-
}
|
|
134
|
+
});
|
|
112
135
|
```
|
|
113
136
|
|
|
114
137
|
### 🟡 JavaScript Actions (`app/actions/hello.js`)
|
|
115
138
|
Perfect for business logic, rapid prototyping, and IO-bound tasks.
|
|
116
139
|
```javascript
|
|
117
|
-
export
|
|
140
|
+
export const hello = defineAction((req) => {
|
|
118
141
|
t.log("Handling user request...");
|
|
119
142
|
return {
|
|
120
143
|
message: "Hello from JavaScript!",
|
|
121
144
|
user_id: req.params.id
|
|
122
145
|
};
|
|
123
|
-
}
|
|
146
|
+
});
|
|
124
147
|
```
|
|
125
148
|
|
|
126
149
|
### 🔴 Rust Actions (Beta)
|
|
@@ -212,4 +235,3 @@ Titan is **not** a Node.js framework. It is a Rust server that speaks JavaScript
|
|
|
212
235
|
* Strict TypeScript Support
|
|
213
236
|
* Native Rust Performance
|
|
214
237
|
* Zero-Config Cloud Deployment
|
|
215
|
-
|
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import prompts from "prompts";
|
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { execSync, spawn } from "child_process";
|
|
6
|
-
import { fileURLToPath } from "url";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
7
7
|
|
|
8
8
|
/* Resolve __dirname for ES modules */
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -12,17 +12,17 @@ const __dirname = path.dirname(__filename);
|
|
|
12
12
|
/* -------------------------------------------------------
|
|
13
13
|
* Colors
|
|
14
14
|
* ----------------------------------------------------- */
|
|
15
|
-
const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
|
|
16
|
-
const green = (t) => `\x1b[32m${t}\x1b[0m`;
|
|
17
|
-
const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
|
|
18
|
-
const red = (t) => `\x1b[31m${t}\x1b[0m`;
|
|
19
|
-
const bold = (t) => `\x1b[1m${t}\x1b[0m`;
|
|
20
|
-
const gray = (t) => `\x1b[90m${t}\x1b[0m`;
|
|
15
|
+
export const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
|
|
16
|
+
export const green = (t) => `\x1b[32m${t}\x1b[0m`;
|
|
17
|
+
export const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
|
|
18
|
+
export const red = (t) => `\x1b[31m${t}\x1b[0m`;
|
|
19
|
+
export const bold = (t) => `\x1b[1m${t}\x1b[0m`;
|
|
20
|
+
export const gray = (t) => `\x1b[90m${t}\x1b[0m`;
|
|
21
21
|
|
|
22
22
|
/* -------------------------------------------------------
|
|
23
23
|
* Invocation detection (tit vs titan)
|
|
24
24
|
* ----------------------------------------------------- */
|
|
25
|
-
function wasInvokedAsTit() {
|
|
25
|
+
export function wasInvokedAsTit() {
|
|
26
26
|
const script = process.argv[1];
|
|
27
27
|
if (script) {
|
|
28
28
|
const base = path.basename(script, path.extname(script)).toLowerCase();
|
|
@@ -51,7 +51,6 @@ function wasInvokedAsTit() {
|
|
|
51
51
|
|
|
52
52
|
return false;
|
|
53
53
|
}
|
|
54
|
-
|
|
55
54
|
const isTitAlias = wasInvokedAsTit();
|
|
56
55
|
|
|
57
56
|
if (isTitAlias) {
|
|
@@ -63,24 +62,25 @@ if (isTitAlias) {
|
|
|
63
62
|
);
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
/* -------------------------------------------------------
|
|
67
|
-
* Args
|
|
68
|
-
* ----------------------------------------------------- */
|
|
69
|
-
const args = process.argv.slice(2);
|
|
70
|
-
const cmd = args[0];
|
|
71
|
-
|
|
72
65
|
/* -------------------------------------------------------
|
|
73
66
|
* Titan version
|
|
74
67
|
* ----------------------------------------------------- */
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
let TITAN_VERSION = "0.1.0";
|
|
69
|
+
try {
|
|
70
|
+
const pkg = JSON.parse(
|
|
71
|
+
fs.readFileSync(path.join(__dirname, "package.json"), "utf8")
|
|
72
|
+
);
|
|
73
|
+
TITAN_VERSION = pkg.version;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// Use default version
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { TITAN_VERSION };
|
|
79
79
|
|
|
80
80
|
/* -------------------------------------------------------
|
|
81
81
|
* Utils
|
|
82
82
|
* ----------------------------------------------------- */
|
|
83
|
-
function copyDir(src, dest, excludes = []) {
|
|
83
|
+
export function copyDir(src, dest, excludes = []) {
|
|
84
84
|
fs.mkdirSync(dest, { recursive: true });
|
|
85
85
|
|
|
86
86
|
for (const file of fs.readdirSync(src)) {
|
|
@@ -103,26 +103,27 @@ function copyDir(src, dest, excludes = []) {
|
|
|
103
103
|
/* -------------------------------------------------------
|
|
104
104
|
* HELP
|
|
105
105
|
* ----------------------------------------------------- */
|
|
106
|
-
function help() {
|
|
106
|
+
export function help() {
|
|
107
107
|
console.log(`
|
|
108
|
-
${bold(cyan("Titan Planet"))} v${TITAN_VERSION}
|
|
108
|
+
${bold(cyan("Titan Planet"))} v${TITAN_VERSION}
|
|
109
109
|
|
|
110
|
-
${green("titan init <project>")} Create new Titan project
|
|
111
|
-
${green("titan create ext <name>")} Create new Titan extension
|
|
112
|
-
${green("titan dev")} Dev mode (hot reload)
|
|
113
|
-
${green("titan build")} Build production Rust server
|
|
114
|
-
${green("titan start")} Start production binary
|
|
115
|
-
${green("titan update")} Update Titan engine
|
|
116
|
-
${green("titan --version")} Show Titan CLI version
|
|
110
|
+
${green("titan init <project> [-t <template>]")} Create new Titan project
|
|
111
|
+
${green("titan create ext <name>")} Create new Titan extension
|
|
112
|
+
${green("titan dev")} Dev mode (hot reload)
|
|
113
|
+
${green("titan build")} Build production Rust server
|
|
114
|
+
${green("titan start")} Start production binary
|
|
115
|
+
${green("titan update")} Update Titan engine
|
|
116
|
+
${green("titan --version")} Show Titan CLI version
|
|
117
117
|
|
|
118
|
-
${yellow("Note: `tit` is supported as a legacy alias.")}
|
|
118
|
+
${yellow("Note: `tit` is supported as a legacy alias.")}
|
|
119
119
|
`);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/* -------------------------------------------------------
|
|
123
123
|
* INIT
|
|
124
124
|
* ----------------------------------------------------- */
|
|
125
|
-
async function initProject(name, templateName) {
|
|
125
|
+
export async function initProject(name, templateName) {
|
|
126
|
+
// console.log(`DEBUG: initProject name=${name}, templateName=${templateName}`);
|
|
126
127
|
let projName = name;
|
|
127
128
|
|
|
128
129
|
if (!projName) {
|
|
@@ -150,7 +151,7 @@ async function initProject(name, templateName) {
|
|
|
150
151
|
message: 'Select language:',
|
|
151
152
|
choices: [
|
|
152
153
|
{ title: 'JavaScript', value: 'js' },
|
|
153
|
-
{ title: 'TypeScript', value: 'ts' }
|
|
154
|
+
{ title: 'TypeScript', value: 'ts' },
|
|
154
155
|
],
|
|
155
156
|
initial: 0
|
|
156
157
|
});
|
|
@@ -188,7 +189,7 @@ async function initProject(name, templateName) {
|
|
|
188
189
|
const arch = archRes.value;
|
|
189
190
|
|
|
190
191
|
if (lang === 'js') {
|
|
191
|
-
selectedTemplate = arch === 'standard' ? 'js' : 'rust';
|
|
192
|
+
selectedTemplate = arch === 'standard' ? 'js' : 'rust-js';
|
|
192
193
|
} else {
|
|
193
194
|
selectedTemplate = arch === 'standard' ? 'ts' : 'rust-ts';
|
|
194
195
|
}
|
|
@@ -196,11 +197,16 @@ async function initProject(name, templateName) {
|
|
|
196
197
|
|
|
197
198
|
const target = path.join(process.cwd(), projName);
|
|
198
199
|
const templateDir = path.join(__dirname, "templates", selectedTemplate);
|
|
200
|
+
const commonDir = path.join(__dirname, "templates", "common");
|
|
199
201
|
|
|
200
202
|
if (!fs.existsSync(templateDir)) {
|
|
201
203
|
console.log(red(`Template '${selectedTemplate}' not found.`));
|
|
202
204
|
return;
|
|
203
205
|
}
|
|
206
|
+
if (!fs.existsSync(commonDir)) {
|
|
207
|
+
console.log(red(`Common template folder not found.`));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
204
210
|
|
|
205
211
|
if (fs.existsSync(target)) {
|
|
206
212
|
console.log(yellow(`Folder already exists: ${target}`));
|
|
@@ -212,12 +218,17 @@ async function initProject(name, templateName) {
|
|
|
212
218
|
console.log(gray(` Template: ${selectedTemplate}`));
|
|
213
219
|
|
|
214
220
|
// ----------------------------------------------------------
|
|
215
|
-
// 1. Copy full
|
|
221
|
+
// 1. Copy full COMMON directory
|
|
222
|
+
// ----------------------------------------------------------
|
|
223
|
+
copyDir(commonDir, target, ["_gitignore", "_dockerignore"]);
|
|
224
|
+
|
|
225
|
+
// ----------------------------------------------------------
|
|
226
|
+
// 2. Copy full SELECTED template directory
|
|
216
227
|
// ----------------------------------------------------------
|
|
217
228
|
copyDir(templateDir, target, ["_gitignore", "_dockerignore"]);
|
|
218
229
|
|
|
219
230
|
// ----------------------------------------------------------
|
|
220
|
-
//
|
|
231
|
+
// 3. Explicitly install dotfiles from COMMON directory
|
|
221
232
|
// ----------------------------------------------------------
|
|
222
233
|
const dotfiles = {
|
|
223
234
|
"_gitignore": ".gitignore",
|
|
@@ -225,7 +236,7 @@ async function initProject(name, templateName) {
|
|
|
225
236
|
};
|
|
226
237
|
|
|
227
238
|
for (const [srcName, destName] of Object.entries(dotfiles)) {
|
|
228
|
-
const src = path.join(
|
|
239
|
+
const src = path.join(commonDir, srcName);
|
|
229
240
|
const dest = path.join(target, destName);
|
|
230
241
|
|
|
231
242
|
if (fs.existsSync(src)) {
|
|
@@ -233,10 +244,18 @@ async function initProject(name, templateName) {
|
|
|
233
244
|
}
|
|
234
245
|
}
|
|
235
246
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (fs.existsSync(
|
|
239
|
-
|
|
247
|
+
const pkgPath = path.join(target, "package.json");
|
|
248
|
+
|
|
249
|
+
if (fs.existsSync(pkgPath)) {
|
|
250
|
+
try {
|
|
251
|
+
const pkgContent = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
252
|
+
if (!pkgContent.titan) pkgContent.titan = {};
|
|
253
|
+
pkgContent.titan.template = selectedTemplate;
|
|
254
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkgContent, null, 2));
|
|
255
|
+
console.log(gray(` Metadata set: ${selectedTemplate}`));
|
|
256
|
+
} catch (e) {
|
|
257
|
+
console.log(yellow("⚠ Could not write template metadata to package.json"));
|
|
258
|
+
}
|
|
240
259
|
}
|
|
241
260
|
|
|
242
261
|
console.log(green("✔ Project structure created"));
|
|
@@ -263,7 +282,7 @@ async function initProject(name, templateName) {
|
|
|
263
282
|
/* -------------------------------------------------------
|
|
264
283
|
* DEV SERVER
|
|
265
284
|
* ----------------------------------------------------- */
|
|
266
|
-
async function devServer() {
|
|
285
|
+
export async function devServer() {
|
|
267
286
|
const root = process.cwd();
|
|
268
287
|
const devScript = path.join(root, "titan", "dev.js");
|
|
269
288
|
|
|
@@ -289,25 +308,79 @@ async function devServer() {
|
|
|
289
308
|
/* -------------------------------------------------------
|
|
290
309
|
* BUILD
|
|
291
310
|
* ----------------------------------------------------- */
|
|
292
|
-
function buildProd() {
|
|
311
|
+
export async function buildProd() {
|
|
293
312
|
console.log(cyan("Titan: Building production output..."));
|
|
294
313
|
|
|
295
314
|
const root = process.cwd();
|
|
296
315
|
const appJs = path.join(root, "app", "app.js");
|
|
316
|
+
const appTs = path.join(root, "app", "app.ts");
|
|
297
317
|
const serverDir = path.join(root, "server");
|
|
298
318
|
const actionsOut = path.join(serverDir, "actions");
|
|
299
319
|
|
|
300
320
|
// BASIC CHECKS
|
|
301
|
-
if (!fs.existsSync(appJs)) {
|
|
302
|
-
console.log(red("ERROR: app/app.js not found."));
|
|
321
|
+
if (!fs.existsSync(appJs) && !fs.existsSync(appTs)) {
|
|
322
|
+
console.log(red("ERROR: app/app.js or app/app.ts not found."));
|
|
303
323
|
process.exit(1);
|
|
304
324
|
}
|
|
305
325
|
|
|
326
|
+
// COMPILE TYPESCRIPT IF NEEDED
|
|
327
|
+
if (fs.existsSync(path.join(root, "tsconfig.json"))) {
|
|
328
|
+
console.log(cyan("→ Compiling TypeScript..."));
|
|
329
|
+
try {
|
|
330
|
+
// We use esbuild for speed and consistency with dev mode
|
|
331
|
+
const { buildSync } = await import("esbuild");
|
|
332
|
+
buildSync({
|
|
333
|
+
entryPoints: [path.join(root, "app", "app.ts")],
|
|
334
|
+
outfile: appJs,
|
|
335
|
+
bundle: true,
|
|
336
|
+
platform: "node",
|
|
337
|
+
format: "esm",
|
|
338
|
+
external: ["fs", "path", "esbuild", "chokidar", "typescript"],
|
|
339
|
+
packages: "external",
|
|
340
|
+
});
|
|
341
|
+
console.log(green("✔ TypeScript compiled"));
|
|
342
|
+
} catch (e) {
|
|
343
|
+
console.log(red("ERROR: Failed to compile TypeScript."));
|
|
344
|
+
console.error(e);
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
306
349
|
// ----------------------------------------------------
|
|
307
|
-
// 1) BUILD METADATA + BUNDLE ACTIONS
|
|
350
|
+
// 1) BUILD METADATA + BUNDLE ACTIONS
|
|
308
351
|
// ----------------------------------------------------
|
|
309
|
-
console.log(cyan("→ Building Titan metadata
|
|
310
|
-
|
|
352
|
+
console.log(cyan("→ Building Titan metadata..."));
|
|
353
|
+
|
|
354
|
+
// Si es TypeScript, compilar primero
|
|
355
|
+
if (fs.existsSync(appTs)) {
|
|
356
|
+
const dotTitan = path.join(root, ".titan");
|
|
357
|
+
const compiledApp = path.join(dotTitan, "app.js");
|
|
358
|
+
|
|
359
|
+
if (!fs.existsSync(dotTitan)) fs.mkdirSync(dotTitan, { recursive: true });
|
|
360
|
+
|
|
361
|
+
// Importar esbuild dinámicamente
|
|
362
|
+
const esbuild = await import("esbuild");
|
|
363
|
+
await esbuild.build({
|
|
364
|
+
entryPoints: [appTs],
|
|
365
|
+
outfile: compiledApp,
|
|
366
|
+
bundle: true,
|
|
367
|
+
platform: "node",
|
|
368
|
+
format: "esm",
|
|
369
|
+
packages: "external",
|
|
370
|
+
logLevel: "silent"
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
execSync(`node "${compiledApp}" --build`, { stdio: "inherit" });
|
|
374
|
+
} else {
|
|
375
|
+
execSync("node app/app.js --build", { stdio: "inherit" });
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
console.log(cyan("→ Bundling actions..."));
|
|
379
|
+
const bundlePath = path.join(root, "titan", "bundle.js");
|
|
380
|
+
// Convert Windows path to file:// URL for ESM import
|
|
381
|
+
const bundleUrl = pathToFileURL(bundlePath).href;
|
|
382
|
+
const { bundle } = await import(bundleUrl);
|
|
383
|
+
await bundle();
|
|
311
384
|
|
|
312
385
|
// ensure actions directory exists
|
|
313
386
|
fs.mkdirSync(actionsOut, { recursive: true });
|
|
@@ -315,9 +388,13 @@ function buildProd() {
|
|
|
315
388
|
// verify bundled actions exist
|
|
316
389
|
const bundles = fs.readdirSync(actionsOut).filter(f => f.endsWith(".jsbundle"));
|
|
317
390
|
if (bundles.length === 0) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
391
|
+
const rustActionsDir = path.join(serverDir, "src", "actions_rust");
|
|
392
|
+
const hasRustActions = fs.existsSync(rustActionsDir) &&
|
|
393
|
+
fs.readdirSync(rustActionsDir).some(f => f.endsWith(".rs") && f !== "mod.rs");
|
|
394
|
+
|
|
395
|
+
if (!hasRustActions) {
|
|
396
|
+
console.log(yellow("⚠ Warning: No JS or Rust actions found."));
|
|
397
|
+
}
|
|
321
398
|
}
|
|
322
399
|
|
|
323
400
|
bundles.forEach(file => {
|
|
@@ -330,37 +407,54 @@ function buildProd() {
|
|
|
330
407
|
// 2) BUILD RUST BINARY
|
|
331
408
|
// ----------------------------------------------------
|
|
332
409
|
console.log(cyan("→ Building Rust release binary..."));
|
|
333
|
-
execSync("cargo build --release", {
|
|
334
|
-
cwd: serverDir,
|
|
335
|
-
stdio: "inherit"
|
|
336
|
-
});
|
|
337
410
|
|
|
338
|
-
|
|
411
|
+
// Only build rust if it's a rust project (check Cargo.toml)
|
|
412
|
+
if (fs.existsSync(path.join(serverDir, "Cargo.toml"))) {
|
|
413
|
+
execSync("cargo build --release", {
|
|
414
|
+
cwd: serverDir,
|
|
415
|
+
stdio: "inherit"
|
|
416
|
+
});
|
|
417
|
+
console.log(green("✔ Titan production build complete!"));
|
|
418
|
+
} else {
|
|
419
|
+
console.log(green("✔ Titan production build complete (pure JS/TS)!"));
|
|
420
|
+
}
|
|
339
421
|
}
|
|
340
422
|
|
|
341
423
|
/* -------------------------------------------------------
|
|
342
424
|
* START
|
|
343
425
|
* ----------------------------------------------------- */
|
|
344
|
-
function startProd() {
|
|
426
|
+
export function startProd() {
|
|
345
427
|
const isWin = process.platform === "win32";
|
|
346
428
|
const bin = isWin ? "titan-server.exe" : "titan-server";
|
|
429
|
+
const root = process.cwd();
|
|
347
430
|
|
|
348
|
-
const exe = path.join(
|
|
349
|
-
|
|
431
|
+
const exe = path.join(root, "server", "target", "release", bin);
|
|
432
|
+
|
|
433
|
+
if (fs.existsSync(exe)) {
|
|
434
|
+
execSync(`"${exe}"`, { stdio: "inherit" });
|
|
435
|
+
} else {
|
|
436
|
+
// Fallback to pure node start if no rust binary
|
|
437
|
+
const appJs = path.join(root, "app", "app.js");
|
|
438
|
+
// Actually, typically we run the bundled/compiled app if we don't have rust server?
|
|
439
|
+
// But wait, the pure TS template runs `node .titan/app.js` in Docker.
|
|
440
|
+
// But locally `titan start` relies on `app/app.js` being compiled?
|
|
441
|
+
// In `buildProd` above we compiled to `app/app.js`.
|
|
442
|
+
// Let's check for `.titan/app.js` which is dev artifact? No, use the prod build artifact.
|
|
443
|
+
execSync(`node "${appJs}"`, { stdio: "inherit" });
|
|
444
|
+
}
|
|
350
445
|
}
|
|
351
446
|
|
|
352
447
|
/* -------------------------------------------------------
|
|
353
448
|
* UPDATE
|
|
354
449
|
* ----------------------------------------------------- */
|
|
355
|
-
|
|
356
|
-
function updateTitan() {
|
|
450
|
+
export function updateTitan() {
|
|
357
451
|
const root = process.cwd();
|
|
358
452
|
|
|
359
453
|
const projectTitan = path.join(root, "titan");
|
|
360
454
|
const projectServer = path.join(root, "server");
|
|
361
455
|
const projectPkg = path.join(root, "package.json");
|
|
362
456
|
|
|
363
|
-
let templateType = "js";
|
|
457
|
+
let templateType = "js";
|
|
364
458
|
if (fs.existsSync(projectPkg)) {
|
|
365
459
|
try {
|
|
366
460
|
const pkg = JSON.parse(fs.readFileSync(projectPkg, "utf-8"));
|
|
@@ -379,10 +473,8 @@ function updateTitan() {
|
|
|
379
473
|
return;
|
|
380
474
|
}
|
|
381
475
|
|
|
382
|
-
if (!fs.existsSync(
|
|
383
|
-
console.log(red(`
|
|
384
|
-
console.log(red(`Expected server template at: ${templateServer}`));
|
|
385
|
-
console.log(yellow(`If you are running from npx, try clearing cache or installing a specific version.`));
|
|
476
|
+
if (!fs.existsSync(templatesRoot)) {
|
|
477
|
+
console.log(red(`Template type '${templateType}' not found in CLI templates.`));
|
|
386
478
|
return;
|
|
387
479
|
}
|
|
388
480
|
|
|
@@ -391,15 +483,18 @@ function updateTitan() {
|
|
|
391
483
|
// ----------------------------------------------------------
|
|
392
484
|
// 1. Update titan/ runtime (authoritative, safe to replace)
|
|
393
485
|
// ----------------------------------------------------------
|
|
394
|
-
fs.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
486
|
+
if (fs.existsSync(templateTitan)) {
|
|
487
|
+
fs.rmSync(projectTitan, {
|
|
488
|
+
recursive: true,
|
|
489
|
+
force: true,
|
|
490
|
+
maxRetries: 10,
|
|
491
|
+
retryDelay: 500,
|
|
492
|
+
});
|
|
493
|
+
copyDir(templateTitan, projectTitan);
|
|
494
|
+
console.log(green("✔ Updated titan/ runtime"));
|
|
495
|
+
} else {
|
|
496
|
+
console.log(yellow(`⚠ No titan/ folder found in template '${templateType}', skipping.`));
|
|
497
|
+
}
|
|
403
498
|
|
|
404
499
|
// ----------------------------------------------------------
|
|
405
500
|
// 2. Update server/ WITHOUT deleting the folder
|
|
@@ -421,18 +516,19 @@ function updateTitan() {
|
|
|
421
516
|
const projectSrc = path.join(projectServer, "src");
|
|
422
517
|
const templateSrc = path.join(templateServer, "src");
|
|
423
518
|
|
|
424
|
-
if (fs.existsSync(
|
|
425
|
-
fs.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
519
|
+
if (fs.existsSync(templateSrc)) {
|
|
520
|
+
if (fs.existsSync(projectSrc)) {
|
|
521
|
+
fs.rmSync(projectSrc, {
|
|
522
|
+
recursive: true,
|
|
523
|
+
force: true,
|
|
524
|
+
maxRetries: 10,
|
|
525
|
+
retryDelay: 500,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
copyDir(templateSrc, projectSrc);
|
|
529
|
+
console.log(green("✔ Updated server/src/"));
|
|
431
530
|
}
|
|
432
531
|
|
|
433
|
-
copyDir(templateSrc, projectSrc);
|
|
434
|
-
console.log(green("✔ Updated server/src/"));
|
|
435
|
-
|
|
436
532
|
// Root-level config files
|
|
437
533
|
const rootFiles = {
|
|
438
534
|
"_gitignore": ".gitignore",
|
|
@@ -453,15 +549,16 @@ function updateTitan() {
|
|
|
453
549
|
|
|
454
550
|
// app/titan.d.ts (JS typing contract)
|
|
455
551
|
const appDir = path.join(root, "app");
|
|
456
|
-
const srcDts = path.join(templateServer, "../app/titan.d.ts");
|
|
552
|
+
const srcDts = path.join(templateServer, "../app/titan.d.ts");
|
|
553
|
+
const fallbackDts = path.join(templatesRoot, "app", "titan.d.ts");
|
|
554
|
+
const finalDtsSrc = fs.existsSync(srcDts) ? srcDts : (fs.existsSync(fallbackDts) ? fallbackDts : null);
|
|
457
555
|
const destDts = path.join(appDir, "titan.d.ts");
|
|
458
556
|
|
|
459
|
-
if (
|
|
557
|
+
if (finalDtsSrc) {
|
|
460
558
|
if (!fs.existsSync(appDir)) {
|
|
461
559
|
fs.mkdirSync(appDir);
|
|
462
560
|
}
|
|
463
|
-
|
|
464
|
-
fs.copyFileSync(srcDts, destDts);
|
|
561
|
+
fs.copyFileSync(finalDtsSrc, destDts);
|
|
465
562
|
console.log(green("✔ Updated app/titan.d.ts"));
|
|
466
563
|
}
|
|
467
564
|
|
|
@@ -474,7 +571,7 @@ function updateTitan() {
|
|
|
474
571
|
/* -------------------------------------------------------
|
|
475
572
|
* CREATE EXTENSION
|
|
476
573
|
* ----------------------------------------------------- */
|
|
477
|
-
function createExtension(name) {
|
|
574
|
+
export function createExtension(name) {
|
|
478
575
|
if (!name) {
|
|
479
576
|
console.log(red("Usage: titan create ext <name>"));
|
|
480
577
|
return;
|
|
@@ -543,7 +640,7 @@ Next steps:
|
|
|
543
640
|
`);
|
|
544
641
|
}
|
|
545
642
|
|
|
546
|
-
function runExtension() {
|
|
643
|
+
export function runExtension() {
|
|
547
644
|
const localSdk = path.join(__dirname, "titanpl-sdk", "bin", "run.js");
|
|
548
645
|
|
|
549
646
|
if (fs.existsSync(localSdk)) {
|
|
@@ -566,35 +663,45 @@ function runExtension() {
|
|
|
566
663
|
/* -------------------------------------------------------
|
|
567
664
|
* ROUTER
|
|
568
665
|
* ----------------------------------------------------- */
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
666
|
+
const isMainModule = process.argv[1] === fileURLToPath(import.meta.url);
|
|
667
|
+
|
|
668
|
+
if (isMainModule) {
|
|
669
|
+
const args = process.argv.slice(2);
|
|
670
|
+
// console.log("DEBUG: args", args);
|
|
671
|
+
const cmd = args[0];
|
|
672
|
+
|
|
673
|
+
(async () => {
|
|
674
|
+
// "titan create ext <name>" -> args = ["create", "ext", "calc_ext"]
|
|
675
|
+
if (cmd === "create" && args[1] === "ext") {
|
|
676
|
+
createExtension(args[2]);
|
|
677
|
+
} else if (cmd === "run" && args[1] === "ext") {
|
|
678
|
+
runExtension();
|
|
679
|
+
} else {
|
|
680
|
+
switch (cmd) {
|
|
681
|
+
case "init": {
|
|
682
|
+
const projName = args[1];
|
|
683
|
+
let tpl = null;
|
|
684
|
+
|
|
685
|
+
const tIndex = args.indexOf("--template") > -1 ? args.indexOf("--template") : args.indexOf("-t");
|
|
686
|
+
if (tIndex > -1 && args[tIndex + 1]) {
|
|
687
|
+
tpl = args[tIndex + 1];
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
await initProject(projName, tpl);
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
case "dev": devServer(); break;
|
|
694
|
+
case "build": await buildProd(); break;
|
|
695
|
+
case "start": startProd(); break;
|
|
696
|
+
case "update": updateTitan(); break;
|
|
697
|
+
case "--version":
|
|
698
|
+
case "-v":
|
|
699
|
+
case "version":
|
|
700
|
+
console.log(cyan(`Titan v${TITAN_VERSION}`));
|
|
701
|
+
break;
|
|
702
|
+
default:
|
|
703
|
+
help();
|
|
583
704
|
}
|
|
584
|
-
|
|
585
|
-
initProject(projName, tpl);
|
|
586
|
-
break;
|
|
587
705
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
case "start": startProd(); break;
|
|
591
|
-
case "update": updateTitan(); break;
|
|
592
|
-
case "--version":
|
|
593
|
-
case "-v":
|
|
594
|
-
case "version":
|
|
595
|
-
console.log(cyan(`Titan v${TITAN_VERSION}`));
|
|
596
|
-
break;
|
|
597
|
-
default:
|
|
598
|
-
help();
|
|
599
|
-
}
|
|
600
|
-
}
|
|
706
|
+
})();
|
|
707
|
+
}
|