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 +2 -2
- package/package.json +1 -1
- package/packages/cli/package.json +2 -2
- package/packages/cli/src/commands/update.js +24 -3
- package/packages/cli/src/engine.js +25 -5
- package/packages/packet/js/titan/bundle.js +27 -14
- package/packages/packet/package.json +1 -1
- package/packages/packet/ts/titan/bundle.js +27 -14
- package/templates/common/Dockerfile +51 -11
- package/templates/common/README.md +1 -1
- package/titanpl-sdk/templates/server/src/extensions/builtin.rs +22 -5
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://
|
|
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://
|
|
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.
|
|
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": "
|
|
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": "
|
|
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.
|
|
62
|
-
const
|
|
63
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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: [
|
|
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: {
|
|
@@ -34,20 +34,32 @@ const NODE_BUILTIN_MAP = {
|
|
|
34
34
|
"node:util": "@titanpl/node/util",
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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: [
|
|
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
|
|
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
|
|
23
|
-
|
|
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
|
-
#
|
|
42
|
-
COPY --from=builder /app/
|
|
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
|
-
#
|
|
45
|
-
|
|
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
|
-
#
|
|
60
|
-
|
|
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**: [
|
|
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()
|
|
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
|
-
|
|
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)
|
|
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
|
|