@ezetgalaxy/titan 26.9.1 → 26.9.3

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 +17 -5
  2. package/index.js +177 -112
  3. package/package.json +19 -5
  4. package/templates/common/app/titan.d.ts +87 -0
  5. package/templates/extension/node_modules/.bin/esbuild +16 -0
  6. package/templates/extension/node_modules/.bin/esbuild.cmd +17 -0
  7. package/templates/extension/node_modules/.bin/esbuild.ps1 +28 -0
  8. package/templates/extension/node_modules/.bin/titanpl-sdk +16 -0
  9. package/templates/extension/node_modules/.bin/titanpl-sdk.cmd +17 -0
  10. package/templates/extension/node_modules/.bin/titanpl-sdk.ps1 +28 -0
  11. package/templates/extension/node_modules/.package-lock.json +111 -0
  12. package/templates/extension/node_modules/@esbuild/win32-x64/README.md +3 -0
  13. package/templates/extension/node_modules/@esbuild/win32-x64/esbuild.exe +0 -0
  14. package/templates/extension/node_modules/@esbuild/win32-x64/package.json +20 -0
  15. package/templates/extension/node_modules/@titanpl/core/LICENSE +15 -0
  16. package/templates/extension/node_modules/@titanpl/core/README.md +127 -0
  17. package/templates/extension/node_modules/@titanpl/core/globals.d.ts +17 -0
  18. package/templates/extension/node_modules/@titanpl/core/index.js +250 -0
  19. package/templates/extension/node_modules/@titanpl/core/native/target/release/titan_core.dll +0 -0
  20. package/templates/extension/node_modules/@titanpl/core/package.json +41 -0
  21. package/templates/extension/node_modules/@titanpl/core/titan.json +115 -0
  22. package/templates/extension/node_modules/chokidar/LICENSE +21 -0
  23. package/templates/extension/node_modules/chokidar/README.md +305 -0
  24. package/templates/extension/node_modules/chokidar/handler.d.ts +90 -0
  25. package/templates/extension/node_modules/chokidar/handler.js +632 -0
  26. package/templates/extension/node_modules/chokidar/index.d.ts +217 -0
  27. package/templates/extension/node_modules/chokidar/index.js +822 -0
  28. package/templates/extension/node_modules/chokidar/package.json +63 -0
  29. package/templates/extension/node_modules/esbuild/LICENSE.md +21 -0
  30. package/templates/extension/node_modules/esbuild/README.md +3 -0
  31. package/templates/extension/node_modules/esbuild/bin/esbuild +223 -0
  32. package/templates/extension/node_modules/esbuild/install.js +289 -0
  33. package/templates/extension/node_modules/esbuild/lib/main.d.ts +716 -0
  34. package/templates/extension/node_modules/esbuild/lib/main.js +2242 -0
  35. package/templates/extension/node_modules/esbuild/package.json +49 -0
  36. package/templates/extension/node_modules/readdirp/LICENSE +21 -0
  37. package/templates/extension/node_modules/readdirp/README.md +120 -0
  38. package/templates/extension/node_modules/readdirp/index.d.ts +108 -0
  39. package/templates/extension/node_modules/readdirp/index.js +272 -0
  40. package/templates/extension/node_modules/readdirp/package.json +66 -0
  41. package/templates/extension/node_modules/titanpl-sdk/LICENSE +15 -0
  42. package/templates/extension/node_modules/titanpl-sdk/README.md +109 -0
  43. package/templates/extension/node_modules/titanpl-sdk/assets/titanpl-sdk.png +0 -0
  44. package/templates/extension/node_modules/titanpl-sdk/bin/run.js +251 -0
  45. package/templates/extension/node_modules/titanpl-sdk/index.d.ts +46 -0
  46. package/templates/extension/node_modules/titanpl-sdk/index.js +5 -0
  47. package/templates/extension/node_modules/titanpl-sdk/package.json +33 -0
  48. package/templates/{rust-js → extension/node_modules/titanpl-sdk/templates}/Dockerfile +4 -17
  49. package/templates/extension/node_modules/titanpl-sdk/templates/app/actions/hello.js +5 -0
  50. package/templates/extension/node_modules/titanpl-sdk/templates/app/app.js +10 -0
  51. package/templates/extension/node_modules/titanpl-sdk/templates/jsconfig.json +19 -0
  52. package/templates/extension/node_modules/titanpl-sdk/templates/server/Cargo.lock +2839 -0
  53. package/templates/extension/node_modules/titanpl-sdk/templates/server/Cargo.toml +27 -0
  54. package/templates/extension/node_modules/titanpl-sdk/templates/server/src/action_management.rs +131 -0
  55. package/templates/extension/node_modules/titanpl-sdk/templates/server/src/errors.rs +10 -0
  56. package/templates/extension/node_modules/titanpl-sdk/templates/server/src/extensions.rs +640 -0
  57. package/templates/extension/node_modules/titanpl-sdk/templates/server/src/main.rs +345 -0
  58. package/templates/extension/node_modules/titanpl-sdk/templates/server/src/utils.rs +33 -0
  59. package/templates/extension/node_modules/titanpl-sdk/templates/titan/bundle.js +65 -0
  60. package/templates/extension/node_modules/titanpl-sdk/templates/titan/dev.js +113 -0
  61. package/templates/extension/node_modules/titanpl-sdk/templates/titan/titan.js +98 -0
  62. package/templates/extension/package-lock.json +522 -0
  63. package/templates/extension/package.json +4 -3
  64. package/templates/rust-ts/app/actions/hello.ts +1 -1
  65. package/templates/rust-ts/titan/runtime.d.ts +1 -0
  66. package/templates/rust-ts/titan/runtime.js +1 -0
  67. package/templates/rust-ts/titan/titan.d.ts +117 -117
  68. package/templates/rust-ts/titan/titan.js +1 -1
  69. package/templates/ts/app/actions/hello.ts +1 -1
  70. package/templates/ts/titan/builder.js +121 -121
  71. package/templates/ts/titan/runtime.d.ts +1 -0
  72. package/templates/ts/titan/runtime.js +1 -1
  73. package/templates/ts/titan/titan.d.ts +117 -117
  74. package/templates/ts/titan/titan.js +1 -1
  75. package/titanpl-sdk/node_modules/.package-lock.json +17 -0
  76. package/titanpl-sdk/node_modules/@titanpl/core/LICENSE +15 -0
  77. package/titanpl-sdk/node_modules/@titanpl/core/README.md +127 -0
  78. package/titanpl-sdk/node_modules/@titanpl/core/globals.d.ts +17 -0
  79. package/titanpl-sdk/node_modules/@titanpl/core/index.js +250 -0
  80. package/titanpl-sdk/node_modules/@titanpl/core/native/target/release/titan_core.dll +0 -0
  81. package/titanpl-sdk/node_modules/@titanpl/core/package.json +41 -0
  82. package/titanpl-sdk/node_modules/@titanpl/core/titan.json +115 -0
  83. package/titanpl-sdk/package-lock.json +28 -0
  84. package/titanpl-sdk/package.json +6 -3
  85. package/templates/rust-js/_gitignore +0 -38
  86. package/templates/rust-js/app/titan.d.ts +0 -101
  87. package/templates/rust-ts/Dockerfile +0 -66
  88. package/templates/rust-ts/_dockerignore +0 -3
  89. package/templates/rust-ts/_gitignore +0 -38
  90. package/templates/ts/Dockerfile +0 -40
  91. package/templates/ts/_dockerignore +0 -3
  92. package/templates/ts/_gitignore +0 -38
  93. /package/templates/{js → common}/Dockerfile +0 -0
  94. /package/templates/{js → common}/_dockerignore +0 -0
  95. /package/templates/{js → common}/_gitignore +0 -0
  96. /package/templates/{rust-js/_dockerignore → extension/node_modules/titanpl-sdk/templates/.dockerignore} +0 -0
  97. /package/templates/{js → extension/node_modules/titanpl-sdk/templates}/app/titan.d.ts +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
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
- const pkg = JSON.parse(
76
- fs.readFileSync(path.join(__dirname, "package.json"), "utf8")
77
- );
78
- const TITAN_VERSION = pkg.version;
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> [-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
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
  });
@@ -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 template directory
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
- // 2. Explicitly install dotfiles
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(templateDir, srcName);
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
- // Dockerfile is safe as-is
237
- const dockerfileSrc = path.join(templateDir, "Dockerfile");
238
- if (fs.existsSync(dockerfileSrc)) {
239
- fs.copyFileSync(dockerfileSrc, path.join(target, "Dockerfile"));
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,16 +308,17 @@ async function devServer() {
289
308
  /* -------------------------------------------------------
290
309
  * BUILD
291
310
  * ----------------------------------------------------- */
292
- async 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) && !fs.existsSync(path.join(root, "app", "app.ts"))) {
321
+ if (!fs.existsSync(appJs) && !fs.existsSync(appTs)) {
302
322
  console.log(red("ERROR: app/app.js or app/app.ts not found."));
303
323
  process.exit(1);
304
324
  }
@@ -327,10 +347,40 @@ async function buildProd() {
327
347
  }
328
348
 
329
349
  // ----------------------------------------------------
330
- // 1) BUILD METADATA + BUNDLE ACTIONS (ONE TIME ONLY)
350
+ // 1) BUILD METADATA + BUNDLE ACTIONS
331
351
  // ----------------------------------------------------
332
- console.log(cyan("→ Building Titan metadata + bundling actions..."));
333
- execSync("node app/app.js --build", { stdio: "inherit" });
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();
334
384
 
335
385
  // ensure actions directory exists
336
386
  fs.mkdirSync(actionsOut, { recursive: true });
@@ -338,9 +388,13 @@ async function buildProd() {
338
388
  // verify bundled actions exist
339
389
  const bundles = fs.readdirSync(actionsOut).filter(f => f.endsWith(".jsbundle"));
340
390
  if (bundles.length === 0) {
341
- console.log(red("ERROR: No actions bundled."));
342
- console.log(red("Make sure your DSL outputs to server/actions."));
343
- process.exit(1);
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
+ }
344
398
  }
345
399
 
346
400
  bundles.forEach(file => {
@@ -369,7 +423,7 @@ async function buildProd() {
369
423
  /* -------------------------------------------------------
370
424
  * START
371
425
  * ----------------------------------------------------- */
372
- async function startProd() {
426
+ export function startProd() {
373
427
  const isWin = process.platform === "win32";
374
428
  const bin = isWin ? "titan-server.exe" : "titan-server";
375
429
  const root = process.cwd();
@@ -388,21 +442,19 @@ async function startProd() {
388
442
  // Let's check for `.titan/app.js` which is dev artifact? No, use the prod build artifact.
389
443
  execSync(`node "${appJs}"`, { stdio: "inherit" });
390
444
  }
391
-
392
445
  }
393
446
 
394
447
  /* -------------------------------------------------------
395
448
  * UPDATE
396
449
  * ----------------------------------------------------- */
397
-
398
- function updateTitan() {
450
+ export function updateTitan() {
399
451
  const root = process.cwd();
400
452
 
401
453
  const projectTitan = path.join(root, "titan");
402
454
  const projectServer = path.join(root, "server");
403
455
  const projectPkg = path.join(root, "package.json");
404
456
 
405
- let templateType = "js"; // Default
457
+ let templateType = "js";
406
458
  if (fs.existsSync(projectPkg)) {
407
459
  try {
408
460
  const pkg = JSON.parse(fs.readFileSync(projectPkg, "utf-8"));
@@ -421,10 +473,8 @@ function updateTitan() {
421
473
  return;
422
474
  }
423
475
 
424
- if (!fs.existsSync(templateServer)) {
425
- console.log(red(`CLI seems corrupted or incomplete.`));
426
- console.log(red(`Expected server template at: ${templateServer}`));
427
- 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.`));
428
478
  return;
429
479
  }
430
480
 
@@ -433,15 +483,18 @@ function updateTitan() {
433
483
  // ----------------------------------------------------------
434
484
  // 1. Update titan/ runtime (authoritative, safe to replace)
435
485
  // ----------------------------------------------------------
436
- fs.rmSync(projectTitan, {
437
- recursive: true,
438
- force: true,
439
- maxRetries: 10,
440
- retryDelay: 500,
441
- });
442
-
443
- copyDir(templateTitan, projectTitan);
444
- console.log(green("✔ Updated titan/ runtime"));
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
+ }
445
498
 
446
499
  // ----------------------------------------------------------
447
500
  // 2. Update server/ WITHOUT deleting the folder
@@ -463,18 +516,19 @@ function updateTitan() {
463
516
  const projectSrc = path.join(projectServer, "src");
464
517
  const templateSrc = path.join(templateServer, "src");
465
518
 
466
- if (fs.existsSync(projectSrc)) {
467
- fs.rmSync(projectSrc, {
468
- recursive: true,
469
- force: true,
470
- maxRetries: 10,
471
- retryDelay: 500,
472
- });
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/"));
473
530
  }
474
531
 
475
- copyDir(templateSrc, projectSrc);
476
- console.log(green("✔ Updated server/src/"));
477
-
478
532
  // Root-level config files
479
533
  const rootFiles = {
480
534
  "_gitignore": ".gitignore",
@@ -495,15 +549,16 @@ function updateTitan() {
495
549
 
496
550
  // app/titan.d.ts (JS typing contract)
497
551
  const appDir = path.join(root, "app");
498
- const srcDts = path.join(templateServer, "../app/titan.d.ts"); // templates/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);
499
555
  const destDts = path.join(appDir, "titan.d.ts");
500
556
 
501
- if (fs.existsSync(srcDts)) {
557
+ if (finalDtsSrc) {
502
558
  if (!fs.existsSync(appDir)) {
503
559
  fs.mkdirSync(appDir);
504
560
  }
505
-
506
- fs.copyFileSync(srcDts, destDts);
561
+ fs.copyFileSync(finalDtsSrc, destDts);
507
562
  console.log(green("✔ Updated app/titan.d.ts"));
508
563
  }
509
564
 
@@ -516,7 +571,7 @@ function updateTitan() {
516
571
  /* -------------------------------------------------------
517
572
  * CREATE EXTENSION
518
573
  * ----------------------------------------------------- */
519
- function createExtension(name) {
574
+ export function createExtension(name) {
520
575
  if (!name) {
521
576
  console.log(red("Usage: titan create ext <name>"));
522
577
  return;
@@ -585,7 +640,7 @@ Next steps:
585
640
  `);
586
641
  }
587
642
 
588
- function runExtension() {
643
+ export function runExtension() {
589
644
  const localSdk = path.join(__dirname, "titanpl-sdk", "bin", "run.js");
590
645
 
591
646
  if (fs.existsSync(localSdk)) {
@@ -608,35 +663,45 @@ function runExtension() {
608
663
  /* -------------------------------------------------------
609
664
  * ROUTER
610
665
  * ----------------------------------------------------- */
611
- // "titan create ext <name>" -> args = ["create", "ext", "calc_ext"]
612
- if (cmd === "create" && args[1] === "ext") {
613
- createExtension(args[2]);
614
- } else if (cmd === "run" && args[1] === "ext") {
615
- runExtension();
616
- } else {
617
- switch (cmd) {
618
- case "init": {
619
- const projName = args[1];
620
- let tpl = null;
621
-
622
- const tIndex = args.indexOf("--template") > -1 ? args.indexOf("--template") : args.indexOf("-t");
623
- if (tIndex > -1 && args[tIndex + 1]) {
624
- tpl = args[tIndex + 1];
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();
625
704
  }
626
-
627
- initProject(projName, tpl);
628
- break;
629
705
  }
630
- case "dev": devServer(); break;
631
- case "build": await buildProd(); break;
632
- case "start": await startProd(); break;
633
- case "update": updateTitan(); break;
634
- case "--version":
635
- case "-v":
636
- case "version":
637
- console.log(cyan(`Titan v${TITAN_VERSION}`));
638
- break;
639
- default:
640
- help();
641
- }
642
- }
706
+ })();
707
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezetgalaxy/titan",
3
- "version": "26.9.1",
3
+ "version": "26.9.3",
4
4
  "description": "Titan Planet is a JavaScript-first backend framework that embeds JS actions into a Rust + Axum server and ships as a single native binary. Routes are compiled to static metadata; only actions run in the embedded JS runtime. No Node.js. No event loop in production.",
5
5
  "license": "ISC",
6
6
  "author": "ezetgalaxy",
@@ -42,13 +42,27 @@
42
42
  "super-backend"
43
43
  ],
44
44
  "scripts": {
45
- "build": "echo \"No build step\"",
46
- "test": "echo \"No tests specified\"",
47
- "test_titan_init": "node scripts/test_titan_init.js"
45
+ "init": "rm -rf build && node index.js init build",
46
+ "build": "cd build && node ../index.js build",
47
+ "dev": "cd build && node ../index.js dev",
48
+ "start": "cd build && node ../index.js start",
49
+ "help": "cd build && node ../index.js help",
50
+ "update": "cd build && node ../index.js update",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "test:coverage": "vitest run --coverage",
54
+ "test:cov": "vitest run --coverage",
55
+ "test:ui": "vitest --ui"
48
56
  },
49
57
  "dependencies": {
58
+ "@titanpl/core": "^1.0.1",
50
59
  "chokidar": "^5.0.0",
51
60
  "esbuild": "^0.27.2",
52
61
  "prompts": "^2.4.2"
62
+ },
63
+ "devDependencies": {
64
+ "@vitest/coverage-v8": "^4.0.17",
65
+ "@vitest/ui": "^4.0.17",
66
+ "vitest": "^4.0.17"
53
67
  }
54
- }
68
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * TITAN TYPE DEFINITIONS
3
+ * ----------------------
4
+ * These types are globally available in your Titan project.
5
+ */
6
+
7
+ /**
8
+ * The Titan Request Object passed to actions.
9
+ */
10
+ declare interface TitanRequest {
11
+ body: any;
12
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
13
+ path: string;
14
+ headers: {
15
+ host?: string;
16
+ "content-type"?: string;
17
+ "user-agent"?: string;
18
+ authorization?: string;
19
+ [key: string]: string | undefined;
20
+ };
21
+ params: Record<string, string>;
22
+ query: Record<string, string>;
23
+ }
24
+
25
+ interface DbConnection {
26
+ /**
27
+ * Execute a SQL query.
28
+ * @param sql The SQL query string.
29
+ * @param params (Optional) Parameters for the query ($1, $2, etc).
30
+ */
31
+ query(sql: string, params?: any[]): any[];
32
+ }
33
+
34
+ /**
35
+ * Define a Titan Action with type inference.
36
+ * @example
37
+ * export const hello = defineAction((req) => {
38
+ * return req.headers;
39
+ * });
40
+ */
41
+ declare function defineAction<T>(actionFn: (req: TitanRequest) => T): (req: TitanRequest) => T;
42
+
43
+ /**
44
+ * Titan Runtime Utilities
45
+ */
46
+ declare const t: {
47
+ /**
48
+ * Log messages to the server console with Titan formatting.
49
+ */
50
+ log(...args: any[]): void;
51
+
52
+ /**
53
+ * Read a file contents as string.
54
+ * @param path Relative path to the file from project root.
55
+ */
56
+ read(path: string): string;
57
+
58
+ fetch(url: string, options?: {
59
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
60
+ headers?: Record<string, string>;
61
+ body?: string | object;
62
+ }): {
63
+ ok: boolean;
64
+ status?: number;
65
+ body?: string;
66
+ error?: string;
67
+ };
68
+
69
+ jwt: {
70
+ sign(
71
+ payload: object,
72
+ secret: string,
73
+ options?: { expiresIn?: string | number }
74
+ ): string;
75
+ verify(token: string, secret: string): any;
76
+ };
77
+
78
+ password: {
79
+ hash(password: string): string;
80
+ verify(password: string, hash: string): boolean;
81
+ };
82
+
83
+ db: {
84
+ connect(url: string): DbConnection;
85
+ };
86
+ };
87
+
@@ -0,0 +1,16 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -x "$basedir/node" ]; then
13
+ exec "$basedir/node" "$basedir/../esbuild/bin/esbuild" "$@"
14
+ else
15
+ exec node "$basedir/../esbuild/bin/esbuild" "$@"
16
+ fi