titanpl 4.0.0 → 4.0.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 CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <a href="https://titan-docs-ez.vercel.app/" target="_blank">
2
+ <a href="https://titanpl.vercel.app/" target="_blank">
3
3
  <img src="https://i.ibb.co/VpBsTg6m/tpl-Logo.png" width="120" alt="TitanPl Logo" />
4
4
  </a>
5
5
  </p>
@@ -292,4 +292,4 @@ You can customize the build process in your `tanfig.json`:
292
292
  ---
293
293
 
294
294
 
295
- **To know more read docs 💟 **Titan Planet docs:** https://titan-docs-ez.vercel.app/docs**
295
+ **To know more read docs 💟 **Titan Planet docs:** https://titanpl.vercel.app/docs**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "titanpl",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@titanpl/cli",
3
- "version": "4.0.0",
3
+ "version": "5.0.6",
4
4
  "description": "The unified CLI for Titan Planet. Use it to create, manage, build, and deploy high-performance backend projects.",
5
5
  "keywords": [
6
6
  "titanpl",
@@ -33,7 +33,7 @@
33
33
  "@titanpl/engine-linux-x64": "2.0.4"
34
34
  },
35
35
  "dependencies": {
36
- "@titanpl/packet": "2.0.4",
36
+ "@titanpl/packet": "4.0.2",
37
37
  "prompts": "^2.4.2",
38
38
  "commander": "^11.0.0",
39
39
  "chalk": "^4.1.2"
@@ -58,9 +58,30 @@ export async function updateCommand() {
58
58
  console.log(chalk.yellow(` ⚠️ Failed to update package.json: ${e.message}`));
59
59
  }
60
60
 
61
- // 2. Refresh Dockerfile and dotfiles from templates
62
- const commonDir = path.resolve(__dirname, '..', '..', '..', '..', 'templates', 'common');
63
- if (fs.existsSync(commonDir)) {
61
+ // 2. Migration: rename titan.json to tanfig.json if needed
62
+ const oldConfigPath = path.join(root, 'titan.json');
63
+ const newConfigPath = path.join(root, 'tanfig.json');
64
+ if (fs.existsSync(oldConfigPath) && !fs.existsSync(newConfigPath)) {
65
+ fs.renameSync(oldConfigPath, newConfigPath);
66
+ console.log(chalk.green(` ✔ Migrated titan.json to tanfig.json`));
67
+ }
68
+
69
+ // 3. Refresh Dockerfile and dotfiles from templates
70
+ let commonDir = null;
71
+ const tryCommonPaths = [
72
+ path.resolve(__dirname, '..', '..', '..', '..', 'templates', 'common'), // Monorepo
73
+ path.resolve(__dirname, '..', '..', 'templates', 'common'), // NPM cli package
74
+ path.resolve(__dirname, '..', '..', '..', 'templates', 'common') // Fallback
75
+ ];
76
+
77
+ for (const p of tryCommonPaths) {
78
+ if (fs.existsSync(p)) {
79
+ commonDir = p;
80
+ break;
81
+ }
82
+ }
83
+
84
+ if (commonDir) {
64
85
  const filesToSync = [
65
86
  ['Dockerfile', 'Dockerfile'],
66
87
  ['_dockerignore', '.dockerignore'],
@@ -19,6 +19,7 @@ export function resolveEngineBinaryPath() {
19
19
  const arch = os.arch();
20
20
  const pkgName = `@titanpl/engine-${platform}-${arch}`;
21
21
  const binName = platform === 'win32' ? 'titan-server.exe' : 'titan-server';
22
+ const shortPkgName = pkgName.split('/').pop();
22
23
 
23
24
  // 1. Monorepo search (local dev)
24
25
  const searchPaths = [
@@ -31,8 +32,14 @@ export function resolveEngineBinaryPath() {
31
32
  for (let startPath of searchPaths) {
32
33
  let current = startPath;
33
34
  for (let i = 0; i < 8; i++) {
34
- const potential = path.join(current, 'engine', 'target', 'release', binName);
35
- if (fs.existsSync(potential)) return potential;
35
+ // Check built binary (engine/target/release/...)
36
+ const builtBin = path.join(current, 'engine', 'target', 'release', binName);
37
+ if (fs.existsSync(builtBin)) return builtBin;
38
+
39
+ // Check package binary (packages/engine-*/bin/...)
40
+ const pkgBin = path.join(current, 'packages', shortPkgName, 'bin', binName);
41
+ if (fs.existsSync(pkgBin)) return pkgBin;
42
+
36
43
  const parent = path.dirname(current);
37
44
  if (parent === current) break;
38
45
  current = parent;
@@ -47,9 +54,22 @@ export function resolveEngineBinaryPath() {
47
54
  } catch (e) { }
48
55
 
49
56
  // 3. Fallback: sibling in node_modules (global install layout)
50
- const cliParent = path.dirname(path.dirname(__dirname)); // up from cli/src → cli → parent
51
- const siblingBin = path.join(cliParent, pkgName, 'bin', binName);
52
- if (fs.existsSync(siblingBin)) return siblingBin;
57
+ // We need to handle scoped packages correctly.
58
+ // If we are at .../node_modules/@titanpl/cli/src
59
+ // Up 1: .../node_modules/@titanpl/cli
60
+ // Up 2: .../node_modules/@titanpl
61
+ // Up 3: .../node_modules (This is where the engine package should be)
62
+ let current = __dirname;
63
+ for (let i = 0; i < 5; i++) {
64
+ const potentialNm = path.join(current, 'node_modules');
65
+ if (fs.existsSync(potentialNm)) {
66
+ const siblingBin = path.join(potentialNm, pkgName, 'bin', binName);
67
+ if (fs.existsSync(siblingBin)) return siblingBin;
68
+ }
69
+ const parent = path.dirname(current);
70
+ if (parent === current) break;
71
+ current = parent;
72
+ }
53
73
 
54
74
  // 4. Walk upwards from current dir searching for binary in root or .ext/node_modules
55
75
  let searchDir = process.cwd();
@@ -33,20 +33,32 @@ const NODE_BUILTIN_MAP = {
33
33
  "node:util": "@titanpl/node/util",
34
34
  };
35
35
 
36
- const titanNodeCompatPlugin = {
37
- name: "titan-node-compat",
38
- setup(build) {
39
- build.onResolve({ filter: /.*/ }, args => {
40
- if (NODE_BUILTIN_MAP[args.path]) {
41
- try {
42
- const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
43
- return { path: resolved };
44
- } catch (e) {
45
- throw new Error(`[TitanPL] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`);
36
+ const createTitanNodeCompatPlugin = (root) => {
37
+ const rootRequire = createRequire(path.join(root, 'package.json'));
38
+
39
+ return {
40
+ name: "titan-node-compat",
41
+ setup(build) {
42
+ build.onResolve({ filter: /.*/ }, args => {
43
+ if (NODE_BUILTIN_MAP[args.path]) {
44
+ const shimPkg = NODE_BUILTIN_MAP[args.path];
45
+ try {
46
+ // 1. Try to resolve from project root (local node_modules)
47
+ const resolved = rootRequire.resolve(shimPkg);
48
+ return { path: resolved };
49
+ } catch (e) {
50
+ try {
51
+ // 2. Fallback to CLI's own context
52
+ const resolved = require.resolve(shimPkg);
53
+ return { path: resolved };
54
+ } catch (e2) {
55
+ throw new Error(`[TitanPL] Failed to resolve Node shim: ${shimPkg}. Ensure @titanpl/node is installed.`);
56
+ }
57
+ }
46
58
  }
47
- }
48
- });
49
- }
59
+ });
60
+ }
61
+ };
50
62
  };
51
63
 
52
64
  function getTitanVersion() {
@@ -96,7 +108,7 @@ export async function bundleFile(options) {
96
108
  platform: 'node',
97
109
  target,
98
110
  logLevel: 'silent',
99
- plugins: [titanNodeCompatPlugin],
111
+ plugins: [createTitanNodeCompatPlugin(options.root || process.cwd())],
100
112
  banner: { js: "var Titan = t;" },
101
113
  footer: options.footer || {}
102
114
  });
@@ -135,6 +147,7 @@ export async function bundle(options = {}) {
135
147
 
136
148
  try {
137
149
  await bundleFile({
150
+ root,
138
151
  entryPoint,
139
152
  outfile,
140
153
  footer: {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@titanpl/packet",
3
- "version": "2.0.4",
3
+ "version": "4.0.2",
4
4
  "description": "The bundler for TitanPl servers.",
5
5
  "keywords": [
6
6
  "bundler",
@@ -34,20 +34,32 @@ const NODE_BUILTIN_MAP = {
34
34
  "node:util": "@titanpl/node/util",
35
35
  };
36
36
 
37
- const titanNodeCompatPlugin = {
38
- name: "titan-node-compat",
39
- setup(build) {
40
- build.onResolve({ filter: /.*/ }, args => {
41
- if (NODE_BUILTIN_MAP[args.path]) {
42
- try {
43
- const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
44
- return { path: resolved };
45
- } catch (e) {
46
- throw new Error(`[TitanPL] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`);
37
+ const createTitanNodeCompatPlugin = (root) => {
38
+ const rootRequire = createRequire(path.join(root, 'package.json'));
39
+
40
+ return {
41
+ name: "titan-node-compat",
42
+ setup(build) {
43
+ build.onResolve({ filter: /.*/ }, args => {
44
+ if (NODE_BUILTIN_MAP[args.path]) {
45
+ const shimPkg = NODE_BUILTIN_MAP[args.path];
46
+ try {
47
+ // 1. Try to resolve from project root (local node_modules)
48
+ const resolved = rootRequire.resolve(shimPkg);
49
+ return { path: resolved };
50
+ } catch (e) {
51
+ try {
52
+ // 2. Fallback to CLI's own context
53
+ const resolved = require.resolve(shimPkg);
54
+ return { path: resolved };
55
+ } catch (e2) {
56
+ throw new Error(`[TitanPL] Failed to resolve Node shim: ${shimPkg}. Ensure @titanpl/node is installed.`);
57
+ }
58
+ }
47
59
  }
48
- }
49
- });
50
- }
60
+ });
61
+ }
62
+ };
51
63
  };
52
64
 
53
65
  function getTitanVersion() {
@@ -140,7 +152,7 @@ export async function bundleFile(options) {
140
152
  platform: 'node',
141
153
  target,
142
154
  logLevel: 'silent',
143
- plugins: [titanNodeCompatPlugin],
155
+ plugins: [createTitanNodeCompatPlugin(options.root || process.cwd())],
144
156
  banner: { js: "var Titan = t;" },
145
157
  footer: options.footer || {}
146
158
  });
@@ -198,6 +210,7 @@ export async function bundle(options = {}) {
198
210
 
199
211
  try {
200
212
  await bundleFile({
213
+ root,
201
214
  entryPoint,
202
215
  outfile,
203
216
  footer: {
@@ -14,14 +14,33 @@ ENV NODE_ENV=production
14
14
 
15
15
  COPY package.json package-lock.json* ./
16
16
 
17
- # Install dependencies (including optional engines)
17
+ # Install with optional dependencies so it grabs the correct engine for the Linux builder
18
+ RUN npm install -g @titanpl/cli
19
+
18
20
  RUN npm install --include=optional
19
21
 
22
+ # ------------------------------------------------
23
+ # Extract Titan Extensions (packages with titan.json)
24
+ # ------------------------------------------------
25
+ RUN mkdir -p /app/.ext && \
26
+ find node_modules -mindepth 2 -maxdepth 3 -type f -name "titan.json" | while read file; do \
27
+ pkg_dir=$(dirname "$file"); \
28
+ pkg_name=$(basename "$pkg_dir"); \
29
+ echo "Extracting Titan extension: $pkg_name"; \
30
+ cp -a "$pkg_dir" "/app/.ext/$pkg_name"; \
31
+ rm -rf "/app/.ext/$pkg_name/node_modules"; \
32
+ done
33
+
34
+ # ------------------------------------------------
35
+ # Copy ANY installed Titan Engine (Architecture agnostic)
36
+ # ------------------------------------------------
37
+ RUN mkdir -p /app/.ext/@titanpl && \
38
+ cp -r node_modules/@titanpl/engine-linux-* /app/.ext/@titanpl/
39
+
20
40
  COPY . .
21
41
 
22
- # Run the Titan release build step
23
- # This extracts extensions to .ext and prepares the 'build/' folder
24
- RUN npx titan build --release
42
+ # Run the Titan build step
43
+ RUN npx titan build
25
44
 
26
45
 
27
46
  # ================================================================
@@ -38,11 +57,31 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
38
57
  ca-certificates curl \
39
58
  && rm -rf /var/lib/apt/lists/*
40
59
 
41
- # Copy the entire release build folder prepared by Stage 1
42
- COPY --from=builder /app/build ./
60
+ # copy dist contents into /app/dist
61
+ COPY --from=builder /app/dist/ ./dist/
62
+
63
+ # titan extensions + engine
64
+ COPY --from=builder /app/.ext ./.ext
65
+
66
+ # runtime assets
67
+ COPY --from=builder /app/package.json ./package.json
68
+
69
+ # ---------------- OPTIONAL APP FOLDERS ----------------
70
+ # Static assets
71
+ # COPY --from=builder /app/app/static ./static
72
+
73
+ # Public assets
74
+ # COPY --from=builder /app/app/public ./public
75
+
76
+ # DB
77
+ # COPY --from=builder /app/app/db ./db
43
78
 
44
- # Ensure permissions
45
- RUN chown -R titan:titan /app
79
+ # CRITICAL SYSTEM SETUP:
80
+ # 1. Mandatory .env file (Engine requires it for config parsing)
81
+ # 2. Node modules symlink for extension JS dependency resolution
82
+ RUN echo "TITAN_DEV=0" > .env && \
83
+ ln -s /app/.ext /app/node_modules && \
84
+ chown -R titan:titan /app
46
85
 
47
86
  # Standard environment variables
48
87
  ENV HOST=0.0.0.0
@@ -52,9 +91,10 @@ ENV TITAN_DEV=0
52
91
  USER titan
53
92
  EXPOSE 5100
54
93
 
55
- # Health check
94
+ # Health check to ensure the server is alive
56
95
  HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
57
96
  CMD curl -f http://localhost:5100/ || exit 1
58
97
 
59
- # Start the server using the 'titan-server' binary created by the release process
60
- CMD ["./titan-server", "run", "dist"]
98
+ # DYNAMIC ENTRYPOINT: Finds the correct architecture binary and starts it
99
+ # This allows the SAME image to work on x64 vs ARM64 servers.
100
+ CMD ["/bin/sh", "-c", "exec $(find .ext/@titanpl/engine-linux-* -name titan-server -type f | head -n 1) run dist"]
@@ -78,7 +78,7 @@ docker run -p 5100:5100 my-titan-app
78
78
 
79
79
  ## 🌐 Community & Support
80
80
 
81
- - **Documentation**: [docs.titanpl.com](https://docs.titanpl.com)
81
+ - **Documentation**: [titanpl.vercel.app](https://titanpl.vercel.app)
82
82
  - **GitHub**: [github.com/t8nlab/titanpl](https://github.com/t8nlab/titanpl)
83
83
  - **Discord**: [Join our community](https://discord.gg/titanpl)
84
84
 
@@ -472,20 +472,37 @@ fn native_db_connect(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArgu
472
472
  }
473
473
 
474
474
  if DB_POOL.get().is_none() {
475
- let cfg: Config = conn_string.parse().unwrap();
475
+ let cfg: Config = match conn_string.parse() {
476
+ Ok(c) => c,
477
+ Err(e) => {
478
+ throw(scope, &format!("t.db.connect(): Invalid connection string: {}", e));
479
+ return;
480
+ }
481
+ };
476
482
  let mgr = Manager::new(cfg, NoTls);
477
483
 
478
- let pool = Pool::builder(mgr)
484
+ let pool = match Pool::builder(mgr)
479
485
  .max_size(max_size)
480
- .build()
481
- .unwrap();
486
+ .build() {
487
+ Ok(p) => p,
488
+ Err(e) => {
489
+ throw(scope, &format!("t.db.connect(): Failed to build connection pool: {}", e));
490
+ return;
491
+ }
492
+ };
482
493
 
483
494
  DB_POOL.set(pool).ok();
484
495
  }
485
496
 
486
497
  let db_conn_obj = v8::Object::new(scope);
487
498
 
488
- let query_fn = v8::Function::new(scope, native_db_query).unwrap();
499
+ let query_fn = match v8::Function::new(scope, native_db_query) {
500
+ Some(f) => f,
501
+ None => {
502
+ throw(scope, "t.db.connect(): Failed to create query function");
503
+ return;
504
+ }
505
+ };
489
506
  let query_key = v8_str(scope, "query");
490
507
  db_conn_obj.set(scope, query_key.into(), query_fn.into());
491
508