titanpl 3.0.0 β 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -5
- package/package.json +1 -1
- package/packages/cli/README.md +19 -2
- package/packages/cli/index.js +5 -4
- package/packages/cli/package.json +12 -5
- package/packages/cli/src/commands/build.js +5 -4
- package/packages/cli/src/commands/init.js +15 -1
- package/packages/cli/src/engine.js +13 -2
- package/packages/packet/index.js +205 -0
- package/templates/common/Dockerfile +11 -49
- package/templates/common/README.md +85 -0
- package/templates/common/_gitignore +1 -0
- package/templates/common/_tanfig.json +14 -0
- package/templates/js/package.json +5 -1
- package/templates/ts/package.json +6 -1
package/README.md
CHANGED
|
@@ -237,13 +237,57 @@ Extend the runtime with custom Rust engines using **Titan Extensions**.
|
|
|
237
237
|
|
|
238
238
|
---
|
|
239
239
|
|
|
240
|
-
# π¦ Deployment
|
|
240
|
+
# π¦ Building & Deployment
|
|
241
241
|
|
|
242
|
-
Titan
|
|
242
|
+
Titan offers a unified build system that packages your JS/TS code, assets, and the native engine into a single, self-contained `build/` directory.
|
|
243
243
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
### 1. Production Build
|
|
245
|
+
Run this command from your project root:
|
|
246
|
+
```bash
|
|
247
|
+
titan build --release
|
|
248
|
+
```
|
|
249
|
+
This will:
|
|
250
|
+
* Bundle your actions into optimized `.jsbundle` files.
|
|
251
|
+
* Extract all required Titan extensions and native libraries to `.ext/`.
|
|
252
|
+
* Copy your `public/`, `static/`, and other configured assets.
|
|
253
|
+
* Generate a `titan-server` executable in the `build/` folder.
|
|
254
|
+
|
|
255
|
+
### 2. Run the Build
|
|
256
|
+
```bash
|
|
257
|
+
cd build
|
|
258
|
+
./titan-server
|
|
259
|
+
```
|
|
260
|
+
Your application is now running as a standalone native server.
|
|
261
|
+
|
|
262
|
+
### π³ Docker Deployment
|
|
263
|
+
The `titan build --release` command is optimized for Docker. Your `Dockerfile` can be as simple as:
|
|
264
|
+
|
|
265
|
+
```dockerfile
|
|
266
|
+
FROM node:20-slim AS builder
|
|
267
|
+
WORKDIR /app
|
|
268
|
+
COPY . .
|
|
269
|
+
RUN npm install --include=optional
|
|
270
|
+
RUN npx titan build --release
|
|
271
|
+
|
|
272
|
+
FROM ubuntu:24.04
|
|
273
|
+
WORKDIR /app
|
|
274
|
+
COPY --from=builder /app/build .
|
|
275
|
+
CMD ["./titan-server", "run", "dist"]
|
|
276
|
+
```
|
|
277
|
+
This produces a tiny, high-performance production image with zero `node_modules`.
|
|
278
|
+
|
|
279
|
+
### π Configuration (`tanfig.json`)
|
|
280
|
+
You can customize the build process in your `tanfig.json`:
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"build": {
|
|
284
|
+
"purpose": "deploy",
|
|
285
|
+
"files": ["public", "db", "secrets"]
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
* **`purpose`**: Use `deploy` to skip `node_modules` junctions for the smallest possible footprint.
|
|
290
|
+
* **`files`**: List any extra folders you want included in the `build/` output.
|
|
247
291
|
|
|
248
292
|
---
|
|
249
293
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "titanpl",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
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",
|
package/packages/cli/README.md
CHANGED
|
@@ -5,13 +5,30 @@ The command-line interface (CLI) for Titan Planet. It provides the `titan` and `
|
|
|
5
5
|
## What it works (What it does)
|
|
6
6
|
The CLI is responsible for bridging your JavaScript codebase with the underlying Rust/Axum engine. It handles scaffolding, compiling JS actions, generating metadata, and running the server.
|
|
7
7
|
|
|
8
|
+
## Commands
|
|
9
|
+
|
|
10
|
+
| Command | Arguments | Description |
|
|
11
|
+
| :--- | :--- | :--- |
|
|
12
|
+
| `init` | `<dir>` | Initialize a new Titan project in the specified directory. |
|
|
13
|
+
| `dev` | | Start the development server with hot-reload and strict type checking. |
|
|
14
|
+
| `build` | | Bundle JavaScript/TypeScript actions into `.jsbundle` files. |
|
|
15
|
+
| `build` | `--release` | Create a self-contained production bundle in `build/` (incl. engine). |
|
|
16
|
+
| `start` | | Start the Titan server (checks for `build/` or local engine). |
|
|
17
|
+
| `update` | | Update the local engine to the latest version. |
|
|
18
|
+
| `ext create` | `<name>` | Scaffold a new native Extension. |
|
|
19
|
+
| `ext run` | | Run the current extension in a test harness. |
|
|
20
|
+
| `help` | | Show available commands and options. |
|
|
21
|
+
|
|
8
22
|
## How it works
|
|
9
23
|
You can install this package globally or use it via your package runner (e.g., `npx`). Alternatively, you can install it as a dev dependency in your project.
|
|
10
24
|
|
|
11
25
|
```bash
|
|
12
|
-
|
|
26
|
+
npm install -g @titanpl/cli
|
|
27
|
+
titan help
|
|
13
28
|
```
|
|
14
29
|
|
|
15
30
|
It parses your application source code, coordinates with `@titanpl/packet` to build the required JS endpoints, and then spins up the pre-compiled native core engine for your OS.
|
|
16
31
|
|
|
17
|
-
**
|
|
32
|
+
**Note:** All commands now prioritize `tanfig.json` for project configuration.
|
|
33
|
+
|
|
34
|
+
**Note on Platform Architecture:** Titan Planet's new v2 architecture supports Windows and Linux (incl. Docker). MacOS support is in active development.
|
package/packages/cli/index.js
CHANGED
|
@@ -53,7 +53,7 @@ ${bold(cyan("β°βββββββββββββββββββββ
|
|
|
53
53
|
${bold("Commands:")}
|
|
54
54
|
${cyan("init")} ${gray("Scaffold a new Titan project")}
|
|
55
55
|
${cyan("create")} ${gray("Create a new project or extension (e.g. 'titan create ext my-ext')")}
|
|
56
|
-
${cyan("build")} ${gray("Compile actions and build production dist")}
|
|
56
|
+
${cyan("build")} ${gray("Compile actions and build production dist. Use --release or -r for a production-ready folder.")}
|
|
57
57
|
${cyan("dev")} ${gray("Start the Gravity Engine in dev/watch mode")}
|
|
58
58
|
${cyan("start")} ${gray("Start the production Gravity Engine")}
|
|
59
59
|
${cyan("update")} ${gray("Update an existing project to latest Titan version")}
|
|
@@ -122,9 +122,10 @@ const cmd = process.argv[2];
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
case "build":
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
const isRelease = process.argv.includes("--release") || process.argv.includes("-r");
|
|
126
|
+
console.log(cyan(`β Building Titan project${isRelease ? " (Release mode)" : ""}...`));
|
|
127
|
+
await buildCommand(isRelease);
|
|
128
|
+
console.log(green(`β ${isRelease ? "Release" : "Build"} complete`));
|
|
128
129
|
break;
|
|
129
130
|
|
|
130
131
|
case "dev":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@titanpl/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
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",
|
|
@@ -12,8 +12,16 @@
|
|
|
12
12
|
"author": "ezetgalaxy",
|
|
13
13
|
"type": "module",
|
|
14
14
|
"main": "index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"src/",
|
|
18
|
+
"templates/",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
15
21
|
"scripts": {
|
|
16
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
22
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
23
|
+
"prepack": "node -e \"const fs=require('fs');const p=require('path');const src=p.resolve(__dirname,'..','..','templates');const dest=p.resolve(__dirname,'templates');if(fs.existsSync(src)&&!fs.existsSync(dest)){const cp=(s,d)=>{fs.mkdirSync(d,{recursive:true});for(const f of fs.readdirSync(s)){const sp=p.join(s,f),dp=p.join(d,f);fs.lstatSync(sp).isDirectory()?cp(sp,dp):fs.copyFileSync(sp,dp)}};cp(src,dest);console.log('Copied templates for packaging')}\"",
|
|
24
|
+
"postpack": "node -e \"const fs=require('fs');const p=require('path');const d=p.resolve(__dirname,'templates');if(fs.existsSync(d)){fs.rmSync(d,{recursive:true,force:true});console.log('Cleaned up templates after packaging')}\""
|
|
17
25
|
},
|
|
18
26
|
"bin": {
|
|
19
27
|
"titan": "./index.js",
|
|
@@ -22,8 +30,7 @@
|
|
|
22
30
|
},
|
|
23
31
|
"optionalDependencies": {
|
|
24
32
|
"@titanpl/engine-win32-x64": "2.0.4",
|
|
25
|
-
"@titanpl/engine-linux-x64": "2.0.4"
|
|
26
|
-
"@titanpl/engine-darwin-arm64": "2.0.4"
|
|
33
|
+
"@titanpl/engine-linux-x64": "2.0.4"
|
|
27
34
|
},
|
|
28
35
|
"dependencies": {
|
|
29
36
|
"@titanpl/packet": "2.0.4",
|
|
@@ -31,4 +38,4 @@
|
|
|
31
38
|
"commander": "^11.0.0",
|
|
32
39
|
"chalk": "^4.1.2"
|
|
33
40
|
}
|
|
34
|
-
}
|
|
41
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { build } from "@titanpl/packet";
|
|
1
|
+
import { build, release } from "@titanpl/packet";
|
|
2
2
|
|
|
3
|
-
export async function buildCommand() {
|
|
4
|
-
const
|
|
5
|
-
|
|
3
|
+
export async function buildCommand(isRelease = false) {
|
|
4
|
+
const buildFn = isRelease ? release : build;
|
|
5
|
+
const dist = await buildFn(process.cwd());
|
|
6
|
+
console.log(`β ${isRelease ? 'Release' : 'Build'} complete β`, dist);
|
|
6
7
|
}
|
|
@@ -164,7 +164,8 @@ export async function initCommand(projectName, templateName) {
|
|
|
164
164
|
const remapping = {
|
|
165
165
|
"_gitignore": ".gitignore",
|
|
166
166
|
"_dockerignore": ".dockerignore",
|
|
167
|
-
"
|
|
167
|
+
"_tanfig.json": "tanfig.json",
|
|
168
|
+
"_titan.json": "tanfig.json",
|
|
168
169
|
".env": ".env"
|
|
169
170
|
};
|
|
170
171
|
for (const [srcName, destName] of Object.entries(remapping)) {
|
|
@@ -175,6 +176,19 @@ export async function initCommand(projectName, templateName) {
|
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
// 4. Ensure tanfig.json exists with default build config
|
|
180
|
+
const tanfigPath = path.join(target, "tanfig.json");
|
|
181
|
+
if (!fs.existsSync(tanfigPath)) {
|
|
182
|
+
const defaultConfig = {
|
|
183
|
+
name: projName,
|
|
184
|
+
build: {
|
|
185
|
+
files: ["public", "static", "db", "config"]
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
fs.writeFileSync(tanfigPath, JSON.stringify(defaultConfig, null, 2));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
178
192
|
// Recursive template substitution
|
|
179
193
|
const substitute = (dir) => {
|
|
180
194
|
for (const file of fs.readdirSync(dir)) {
|
|
@@ -51,11 +51,21 @@ export function resolveEngineBinaryPath() {
|
|
|
51
51
|
const siblingBin = path.join(cliParent, pkgName, 'bin', binName);
|
|
52
52
|
if (fs.existsSync(siblingBin)) return siblingBin;
|
|
53
53
|
|
|
54
|
-
// 4. Walk upwards from current dir searching for node_modules
|
|
54
|
+
// 4. Walk upwards from current dir searching for binary in root or .ext/node_modules
|
|
55
55
|
let searchDir = process.cwd();
|
|
56
56
|
for (let i = 0; i < 5; i++) {
|
|
57
|
+
// Check root (Directly in build/ folder)
|
|
58
|
+
const rootBin = path.join(searchDir, binName);
|
|
59
|
+
if (fs.existsSync(rootBin)) return rootBin;
|
|
60
|
+
|
|
61
|
+
// Check node_modules
|
|
57
62
|
const nmBin = path.join(searchDir, 'node_modules', pkgName, 'bin', binName);
|
|
58
63
|
if (fs.existsSync(nmBin)) return nmBin;
|
|
64
|
+
|
|
65
|
+
// Check .ext (Release mode layout)
|
|
66
|
+
const extBin = path.join(searchDir, '.ext', pkgName, 'bin', binName);
|
|
67
|
+
if (fs.existsSync(extBin)) return extBin;
|
|
68
|
+
|
|
59
69
|
const parent = path.dirname(searchDir);
|
|
60
70
|
if (parent === searchDir) break;
|
|
61
71
|
searchDir = parent;
|
|
@@ -110,7 +120,8 @@ export function startEngine(watchMode = false) {
|
|
|
110
120
|
env: {
|
|
111
121
|
...process.env,
|
|
112
122
|
TITAN_ENV: watchMode ? 'development' : 'production',
|
|
113
|
-
|
|
123
|
+
TITAN_DEV: watchMode ? '1' : '0',
|
|
124
|
+
NODE_ENV: watchMode ? 'development' : 'production'
|
|
114
125
|
}
|
|
115
126
|
});
|
|
116
127
|
|
package/packages/packet/index.js
CHANGED
|
@@ -32,6 +32,25 @@ function ensureDist(root) {
|
|
|
32
32
|
return dist;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Recursive copy
|
|
37
|
+
*/
|
|
38
|
+
function copyDir(src, dest, filter) {
|
|
39
|
+
if (filter && !filter(src)) return;
|
|
40
|
+
|
|
41
|
+
const stats = fs.lstatSync(src);
|
|
42
|
+
if (stats.isDirectory()) {
|
|
43
|
+
if (!fs.existsSync(dest)) {
|
|
44
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
for (const file of fs.readdirSync(src)) {
|
|
47
|
+
copyDir(path.join(src, file), path.join(dest, file), filter);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
fs.copyFileSync(src, dest);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
35
54
|
/**
|
|
36
55
|
* Production build
|
|
37
56
|
*/
|
|
@@ -51,6 +70,192 @@ export async function build(root = process.cwd()) {
|
|
|
51
70
|
return dist;
|
|
52
71
|
}
|
|
53
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Release build (Production ready folder)
|
|
75
|
+
*/
|
|
76
|
+
export async function release(root = process.cwd()) {
|
|
77
|
+
const dist = await build(root);
|
|
78
|
+
const buildDir = path.join(root, "build");
|
|
79
|
+
|
|
80
|
+
// Read config
|
|
81
|
+
let config = {};
|
|
82
|
+
const configPath = fs.existsSync(path.join(root, "tanfig.json"))
|
|
83
|
+
? path.join(root, "tanfig.json")
|
|
84
|
+
: fs.existsSync(path.join(root, "titan.json"))
|
|
85
|
+
? path.join(root, "titan.json")
|
|
86
|
+
: null;
|
|
87
|
+
|
|
88
|
+
if (configPath) {
|
|
89
|
+
try {
|
|
90
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
91
|
+
} catch (e) { }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const filesToCopy = config.build && config.build.files ? config.build.files : ["public", "static", "db", "config"];
|
|
95
|
+
|
|
96
|
+
// Clear or ensure build dir
|
|
97
|
+
if (fs.existsSync(buildDir)) {
|
|
98
|
+
fs.rmSync(buildDir, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
fs.mkdirSync(buildDir, { recursive: true });
|
|
101
|
+
|
|
102
|
+
// 1. Copy dist
|
|
103
|
+
copyDir(dist, path.join(buildDir, "dist"));
|
|
104
|
+
|
|
105
|
+
// 2. Extra files/folders from root based on config
|
|
106
|
+
for (const item of filesToCopy) {
|
|
107
|
+
const src = path.join(root, item);
|
|
108
|
+
if (fs.existsSync(src)) {
|
|
109
|
+
const dest = path.join(buildDir, item);
|
|
110
|
+
copyDir(src, dest);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 3. Copy package.json
|
|
115
|
+
const pkgPath = path.join(root, "package.json");
|
|
116
|
+
if (fs.existsSync(pkgPath)) {
|
|
117
|
+
fs.copyFileSync(pkgPath, path.join(buildDir, "package.json"));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 4. Create .env
|
|
121
|
+
fs.writeFileSync(path.join(buildDir, ".env"), "TITAN_DEV=0\n");
|
|
122
|
+
|
|
123
|
+
// 5. Extract extensions
|
|
124
|
+
const extDir = path.join(buildDir, ".ext");
|
|
125
|
+
fs.mkdirSync(extDir, { recursive: true });
|
|
126
|
+
|
|
127
|
+
const nodeModules = path.join(root, "node_modules");
|
|
128
|
+
if (fs.existsSync(nodeModules)) {
|
|
129
|
+
const findExtensions = (dir, depth = 0) => {
|
|
130
|
+
if (depth > 2) return;
|
|
131
|
+
if (!fs.existsSync(dir)) return;
|
|
132
|
+
|
|
133
|
+
const files = fs.readdirSync(dir);
|
|
134
|
+
for (const file of files) {
|
|
135
|
+
const fullPath = path.join(dir, file);
|
|
136
|
+
try {
|
|
137
|
+
const stats = fs.lstatSync(fullPath);
|
|
138
|
+
if (stats.isDirectory()) {
|
|
139
|
+
if (file === "node_modules" && depth > 0) continue;
|
|
140
|
+
|
|
141
|
+
const titanJson = path.join(fullPath, "titan.json");
|
|
142
|
+
if (fs.existsSync(titanJson)) {
|
|
143
|
+
let targetPkgName = file;
|
|
144
|
+
const parentDirName = path.basename(dir);
|
|
145
|
+
if (parentDirName.startsWith("@")) {
|
|
146
|
+
targetPkgName = path.join(parentDirName, file);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const destPath = path.join(extDir, targetPkgName);
|
|
150
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
151
|
+
|
|
152
|
+
copyDir(fullPath, destPath, (src) => {
|
|
153
|
+
const rel = path.relative(fullPath, src);
|
|
154
|
+
return !rel.startsWith("node_modules");
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
findExtensions(fullPath, depth + 1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch (e) { }
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
findExtensions(nodeModules);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 6. Copy Engine binaries
|
|
167
|
+
if (fs.existsSync(path.join(nodeModules, "@titanpl"))) {
|
|
168
|
+
const scopeDir = path.join(nodeModules, "@titanpl");
|
|
169
|
+
const folders = fs.readdirSync(scopeDir);
|
|
170
|
+
for (const folder of folders) {
|
|
171
|
+
if (folder.startsWith("engine-")) {
|
|
172
|
+
const engineDest = path.join(extDir, "@titanpl", folder);
|
|
173
|
+
copyDir(path.join(scopeDir, folder), engineDest);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 7. Create node_modules junction to .ext for engine resolution
|
|
179
|
+
// If env is 'deploy' or 'production', we might want to skip this for a cleaner build
|
|
180
|
+
const buildEnv = config.build && (config.build.env || config.build.purpose) ? (config.build.env || config.build.purpose) : "test";
|
|
181
|
+
|
|
182
|
+
if (buildEnv !== "deploy" && buildEnv !== "production") {
|
|
183
|
+
const nmSymlink = path.join(buildDir, "node_modules");
|
|
184
|
+
if (!fs.existsSync(nmSymlink)) {
|
|
185
|
+
try {
|
|
186
|
+
// Junctions don't require admin on Windows
|
|
187
|
+
fs.symlinkSync(".ext", nmSymlink, "junction");
|
|
188
|
+
} catch (e) {
|
|
189
|
+
try {
|
|
190
|
+
fs.symlinkSync(".ext", nmSymlink, "dir");
|
|
191
|
+
} catch (e2) {
|
|
192
|
+
// Fallback or ignore if symlink creation is totally restricted
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 8. Create 'titan' executable link in the build root for easy starting
|
|
199
|
+
const binName = process.platform === "win32" ? "titan-server.exe" : "titan-server";
|
|
200
|
+
let engineBin = null;
|
|
201
|
+
|
|
202
|
+
// Strategy A: Search in the .ext we just populated
|
|
203
|
+
const findInExt = (dir) => {
|
|
204
|
+
if (!fs.existsSync(dir)) return null;
|
|
205
|
+
const entries = fs.readdirSync(dir);
|
|
206
|
+
for (const entry of entries) {
|
|
207
|
+
const full = path.join(dir, entry);
|
|
208
|
+
if (fs.statSync(full).isDirectory()) {
|
|
209
|
+
// Check both pkgRoot/bin/titan-server and pkgRoot/titan-server (some layouts differ)
|
|
210
|
+
const p1 = path.join(full, "bin", binName);
|
|
211
|
+
if (fs.existsSync(p1)) return p1;
|
|
212
|
+
const p2 = path.join(full, binName);
|
|
213
|
+
if (fs.existsSync(p2)) return p2;
|
|
214
|
+
|
|
215
|
+
const found = findInExt(full);
|
|
216
|
+
if (found) return found;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
engineBin = findInExt(extDir);
|
|
223
|
+
|
|
224
|
+
// Strategy B: Monorepo fallback (if building inside the titanpl repo)
|
|
225
|
+
if (!engineBin) {
|
|
226
|
+
let current = root;
|
|
227
|
+
for (let i = 0; i < 5; i++) {
|
|
228
|
+
const potential = path.join(current, "engine", "target", "release", binName);
|
|
229
|
+
if (fs.existsSync(potential)) {
|
|
230
|
+
engineBin = potential;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
const parent = path.dirname(current);
|
|
234
|
+
if (parent === current) break;
|
|
235
|
+
current = parent;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (engineBin) {
|
|
240
|
+
const linkName = binName; // Keep the original name 'titan-server'
|
|
241
|
+
const linkPath = path.join(buildDir, linkName);
|
|
242
|
+
|
|
243
|
+
if (!fs.existsSync(linkPath)) {
|
|
244
|
+
try {
|
|
245
|
+
// Always copy the binary to the root for maximum portability in the 'build' folder
|
|
246
|
+
fs.copyFileSync(engineBin, linkPath);
|
|
247
|
+
if (process.platform !== "win32") {
|
|
248
|
+
fs.chmodSync(linkPath, 0o755);
|
|
249
|
+
}
|
|
250
|
+
} catch (e) {
|
|
251
|
+
console.error(`[Titan] Failed to create titan binary: ${e.message}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return buildDir;
|
|
257
|
+
}
|
|
258
|
+
|
|
54
259
|
/**
|
|
55
260
|
* Dev mode build
|
|
56
261
|
*/
|
|
@@ -14,31 +14,14 @@ ENV NODE_ENV=production
|
|
|
14
14
|
|
|
15
15
|
COPY package.json package-lock.json* ./
|
|
16
16
|
|
|
17
|
-
# Install
|
|
17
|
+
# Install dependencies (including optional engines)
|
|
18
18
|
RUN npm install --include=optional
|
|
19
19
|
|
|
20
|
-
# ------------------------------------------------
|
|
21
|
-
# Extract Titan Extensions (packages with titan.json)
|
|
22
|
-
# ------------------------------------------------
|
|
23
|
-
RUN mkdir -p /app/.ext && \
|
|
24
|
-
find node_modules -mindepth 2 -maxdepth 3 -type f -name "titan.json" | while read file; do \
|
|
25
|
-
pkg_dir=$(dirname "$file"); \
|
|
26
|
-
pkg_name=$(basename "$pkg_dir"); \
|
|
27
|
-
echo "Extracting Titan extension: $pkg_name"; \
|
|
28
|
-
cp -a "$pkg_dir" "/app/.ext/$pkg_name"; \
|
|
29
|
-
rm -rf "/app/.ext/$pkg_name/node_modules"; \
|
|
30
|
-
done
|
|
31
|
-
|
|
32
|
-
# ------------------------------------------------
|
|
33
|
-
# Copy ANY installed Titan Engine (Architecture agnostic)
|
|
34
|
-
# ------------------------------------------------
|
|
35
|
-
RUN mkdir -p /app/.ext/@titanpl && \
|
|
36
|
-
cp -r node_modules/@titanpl/engine-linux-* /app/.ext/@titanpl/
|
|
37
|
-
|
|
38
20
|
COPY . .
|
|
39
21
|
|
|
40
|
-
# Run the Titan build step
|
|
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
25
|
|
|
43
26
|
|
|
44
27
|
# ================================================================
|
|
@@ -55,31 +38,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
|
55
38
|
ca-certificates curl \
|
|
56
39
|
&& rm -rf /var/lib/apt/lists/*
|
|
57
40
|
|
|
58
|
-
#
|
|
59
|
-
COPY --from=builder /app/
|
|
60
|
-
|
|
61
|
-
# titan extensions + engine
|
|
62
|
-
COPY --from=builder /app/.ext ./.ext
|
|
63
|
-
|
|
64
|
-
# runtime assets
|
|
65
|
-
COPY --from=builder /app/package.json ./package.json
|
|
66
|
-
|
|
67
|
-
# ---------------- OPTIONAL APP FOLDERS ----------------
|
|
68
|
-
# Static assets
|
|
69
|
-
# COPY --from=builder /app/app/static ./static
|
|
70
|
-
|
|
71
|
-
# Public assets
|
|
72
|
-
# COPY --from=builder /app/app/public ./public
|
|
73
|
-
|
|
74
|
-
# DB
|
|
75
|
-
# COPY --from=builder /app/app/db ./db
|
|
41
|
+
# Copy the entire release build folder prepared by Stage 1
|
|
42
|
+
COPY --from=builder /app/build ./
|
|
76
43
|
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
# 2. Node modules symlink for extension JS dependency resolution
|
|
80
|
-
RUN echo "TITAN_DEV=0" > .env && \
|
|
81
|
-
ln -s /app/.ext /app/node_modules && \
|
|
82
|
-
chown -R titan:titan /app
|
|
44
|
+
# Ensure permissions
|
|
45
|
+
RUN chown -R titan:titan /app
|
|
83
46
|
|
|
84
47
|
# Standard environment variables
|
|
85
48
|
ENV HOST=0.0.0.0
|
|
@@ -89,10 +52,9 @@ ENV TITAN_DEV=0
|
|
|
89
52
|
USER titan
|
|
90
53
|
EXPOSE 5100
|
|
91
54
|
|
|
92
|
-
# Health check
|
|
55
|
+
# Health check
|
|
93
56
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
|
|
94
57
|
CMD curl -f http://localhost:5100/ || exit 1
|
|
95
58
|
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
CMD ["/bin/sh", "-c", "exec $(find .ext/@titanpl/engine-linux-* -name titan-server -type f | head -n 1) run dist"]
|
|
59
|
+
# Start the server using the 'titan-server' binary created by the release process
|
|
60
|
+
CMD ["./titan-server", "run", "dist"]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# β£ Titan Project
|
|
2
|
+
|
|
3
|
+
Welcome to your new **Titan Planet** project! Titan is a high-performance web framework designed for scale, speed, and developer happiness.
|
|
4
|
+
|
|
5
|
+
## π Getting Started
|
|
6
|
+
|
|
7
|
+
### 1. Install Dependencies
|
|
8
|
+
```bash
|
|
9
|
+
npm install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### 2. Start Development Server
|
|
13
|
+
Run the project in development mode with hot-reloading:
|
|
14
|
+
```bash
|
|
15
|
+
titan dev
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 3. Build for Production
|
|
19
|
+
Create a self-contained production bundle in the `build/` directory:
|
|
20
|
+
```bash
|
|
21
|
+
titan build --release
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 4. Run Production Server
|
|
25
|
+
```bash
|
|
26
|
+
cd build
|
|
27
|
+
titan start
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## π Project Structure
|
|
33
|
+
|
|
34
|
+
- `app/actions/` - Your JavaScript/TypeScript backend logic.
|
|
35
|
+
- `public/` - Static assets served directly (images, robots.txt, etc.).
|
|
36
|
+
- `tanfig.json` - Core project configuration and build settings.
|
|
37
|
+
- `.env` - Environment variables.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## π Configuration (`tanfig.json`)
|
|
42
|
+
|
|
43
|
+
Your project uses `tanfig.json` to control the build and runtime behavior.
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"name": "my-titan-app",
|
|
48
|
+
"build": {
|
|
49
|
+
"purpose": "test",
|
|
50
|
+
"files": ["public", "static", "db", "config"]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Build Options:
|
|
56
|
+
- **`purpose`**:
|
|
57
|
+
- `test`: (Default) Creates a `node_modules` junction for local testing.
|
|
58
|
+
- `deploy`: Slim build without `node_modules`, ready for production.
|
|
59
|
+
- **`files`**: List of folders/files from the root to include in the production `build/` folder.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## π³ Docker Deployment
|
|
64
|
+
|
|
65
|
+
This project comes with a pre-configured, multi-stage `Dockerfile` optimized for Titan's native engine.
|
|
66
|
+
|
|
67
|
+
### Build Image
|
|
68
|
+
```bash
|
|
69
|
+
docker build -t my-titan-app .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Run Container
|
|
73
|
+
```bash
|
|
74
|
+
docker run -p 5100:5100 my-titan-app
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## π Community & Support
|
|
80
|
+
|
|
81
|
+
- **Documentation**: [docs.titanpl.com](https://docs.titanpl.com)
|
|
82
|
+
- **GitHub**: [github.com/t8nlab/titanpl](https://github.com/t8nlab/titanpl)
|
|
83
|
+
- **Discord**: [Join our community](https://discord.gg/titanpl)
|
|
84
|
+
|
|
85
|
+
Built with β€οΈ by the **Titan Planet** team.
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"@titanpl/node": "latest",
|
|
14
14
|
"@titanpl/packet": "2.0.4"
|
|
15
15
|
},
|
|
16
|
+
"optionalDependencies": {
|
|
17
|
+
"@titanpl/engine-linux-x64": "2.0.5",
|
|
18
|
+
"@titanpl/engine-win32-x64": "2.0.3"
|
|
19
|
+
},
|
|
16
20
|
"scripts": {
|
|
17
21
|
"build": "titan build",
|
|
18
22
|
"dev": "titan dev",
|
|
@@ -25,4 +29,4 @@
|
|
|
25
29
|
"eslint-plugin-titanpl": "latest"
|
|
26
30
|
},
|
|
27
31
|
"version": "2.0.4"
|
|
28
|
-
}
|
|
32
|
+
}
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"@titanpl/packet": "2.0.4",
|
|
15
15
|
"typescript": "^5.0.0"
|
|
16
16
|
},
|
|
17
|
+
"optionalDependencies": {
|
|
18
|
+
"@titanpl/engine-linux-arm64": "2.0.5",
|
|
19
|
+
"@titanpl/engine-linux-x64": "2.0.5",
|
|
20
|
+
"@titanpl/engine-win32-x64": "2.0.3"
|
|
21
|
+
},
|
|
17
22
|
"scripts": {
|
|
18
23
|
"build": "titan build",
|
|
19
24
|
"dev": "titan dev",
|
|
@@ -27,4 +32,4 @@
|
|
|
27
32
|
"@typescript-eslint/parser": "^8.54.0"
|
|
28
33
|
},
|
|
29
34
|
"version": "2.0.4"
|
|
30
|
-
}
|
|
35
|
+
}
|