@ezetgalaxy/titan 26.7.4 → 26.8.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 +88 -201
- package/index.js +552 -489
- package/package.json +6 -5
- package/templates/js/_gitignore +37 -0
- package/templates/{package.json → js/package.json} +4 -1
- package/templates/js/server/action_map.json +3 -0
- package/templates/js/server/actions/hello.jsbundle +48 -0
- package/templates/js/server/routes.json +16 -0
- package/templates/js/server/src/actions_rust/mod.rs +15 -0
- package/templates/{server → js/server}/src/extensions.rs +149 -17
- package/templates/{titan → js/titan}/bundle.js +22 -9
- package/templates/js/titan/dev.js +194 -0
- package/templates/{titan → js/titan}/titan.js +25 -1
- package/templates/rust/Dockerfile +66 -0
- package/templates/rust/_dockerignore +3 -0
- package/templates/rust/_gitignore +37 -0
- package/templates/rust/app/actions/hello.js +5 -0
- package/templates/rust/app/actions/rust_hello.rs +14 -0
- package/templates/rust/app/app.js +11 -0
- package/templates/rust/app/titan.d.ts +101 -0
- package/templates/rust/jsconfig.json +19 -0
- package/templates/rust/package.json +13 -0
- package/templates/rust/server/Cargo.lock +2869 -0
- package/templates/rust/server/Cargo.toml +27 -0
- package/templates/rust/server/action_map.json +3 -0
- package/templates/rust/server/actions/hello.jsbundle +47 -0
- package/templates/rust/server/routes.json +22 -0
- package/templates/rust/server/src/action_management.rs +131 -0
- package/templates/rust/server/src/actions_rust/mod.rs +19 -0
- package/templates/rust/server/src/actions_rust/rust_hello.rs +14 -0
- package/templates/rust/server/src/errors.rs +10 -0
- package/templates/rust/server/src/extensions.rs +989 -0
- package/templates/rust/server/src/main.rs +443 -0
- package/templates/rust/server/src/utils.rs +33 -0
- package/templates/rust/titan/bundle.js +157 -0
- package/templates/rust/titan/dev.js +194 -0
- package/templates/rust/titan/titan.js +122 -0
- package/titanpl-sdk/package.json +1 -1
- package/titanpl-sdk/templates/Dockerfile +4 -17
- package/titanpl-sdk/templates/server/src/extensions.rs +218 -423
- package/titanpl-sdk/templates/server/src/main.rs +68 -134
- package/scripts/make_dist.sh +0 -71
- package/templates/titan/dev.js +0 -144
- /package/templates/{Dockerfile → js/Dockerfile} +0 -0
- /package/templates/{.dockerignore → js/_dockerignore} +0 -0
- /package/templates/{app → js/app}/actions/hello.js +0 -0
- /package/templates/{app → js/app}/app.js +0 -0
- /package/templates/{app → js/app}/titan.d.ts +0 -0
- /package/templates/{jsconfig.json → js/jsconfig.json} +0 -0
- /package/templates/{server → js/server}/Cargo.lock +0 -0
- /package/templates/{server → js/server}/Cargo.toml +0 -0
- /package/templates/{server → js/server}/src/action_management.rs +0 -0
- /package/templates/{server → js/server}/src/errors.rs +0 -0
- /package/templates/{server → js/server}/src/main.rs +0 -0
- /package/templates/{server → js/server}/src/utils.rs +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import chokidar from "chokidar";
|
|
2
|
+
import { spawn, execSync } from "child_process";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import { bundle } from "./bundle.js";
|
|
7
|
+
|
|
8
|
+
// Required for __dirname in ES modules
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// Colors
|
|
14
|
+
import { createRequire } from "module";
|
|
15
|
+
|
|
16
|
+
// Colors
|
|
17
|
+
const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
|
|
18
|
+
const green = (t) => `\x1b[32m${t}\x1b[0m`;
|
|
19
|
+
const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
|
|
20
|
+
const red = (t) => `\x1b[31m${t}\x1b[0m`;
|
|
21
|
+
const gray = (t) => `\x1b[90m${t}\x1b[0m`;
|
|
22
|
+
const bold = (t) => `\x1b[1m${t}\x1b[0m`;
|
|
23
|
+
|
|
24
|
+
function getTitanVersion() {
|
|
25
|
+
try {
|
|
26
|
+
// 1. Try resolving from node_modules (standard user case)
|
|
27
|
+
const require = createRequire(import.meta.url);
|
|
28
|
+
// We look for @ezetgalaxy/titan/package.json
|
|
29
|
+
const pkgPath = require.resolve("@ezetgalaxy/titan/package.json");
|
|
30
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
31
|
+
return pkg.version;
|
|
32
|
+
} catch (e) {
|
|
33
|
+
try {
|
|
34
|
+
// 2. Fallback for local dev (path to repo root)
|
|
35
|
+
const localPath = path.join(__dirname, "..", "..", "..", "package.json");
|
|
36
|
+
if (fs.existsSync(localPath)) {
|
|
37
|
+
const pkg = JSON.parse(fs.readFileSync(localPath, "utf-8"));
|
|
38
|
+
if (pkg.name === "@ezetgalaxy/titan") {
|
|
39
|
+
return pkg.version;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch (e2) { }
|
|
43
|
+
}
|
|
44
|
+
return "0.1.0"; // Fallback
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let serverProcess = null;
|
|
48
|
+
let isKilling = false;
|
|
49
|
+
|
|
50
|
+
// ... (killServer same as before)
|
|
51
|
+
async function killServer() {
|
|
52
|
+
if (!serverProcess) return;
|
|
53
|
+
|
|
54
|
+
isKilling = true;
|
|
55
|
+
const pid = serverProcess.pid;
|
|
56
|
+
const killPromise = new Promise((resolve) => {
|
|
57
|
+
if (serverProcess.exitCode !== null) return resolve();
|
|
58
|
+
serverProcess.once("close", resolve);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (process.platform === "win32") {
|
|
62
|
+
try {
|
|
63
|
+
execSync(`taskkill /pid ${pid} /f /t`, { stdio: 'ignore' });
|
|
64
|
+
} catch (e) {
|
|
65
|
+
// Ignore errors if process is already dead
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
serverProcess.kill();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
await killPromise;
|
|
73
|
+
} catch (e) { }
|
|
74
|
+
serverProcess = null;
|
|
75
|
+
isKilling = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function startRustServer(retryCount = 0) {
|
|
79
|
+
const waitTime = retryCount > 0 ? 2000 : 1000;
|
|
80
|
+
|
|
81
|
+
await killServer();
|
|
82
|
+
await new Promise(r => setTimeout(r, waitTime));
|
|
83
|
+
|
|
84
|
+
const serverPath = path.join(process.cwd(), "server");
|
|
85
|
+
const startTime = Date.now();
|
|
86
|
+
|
|
87
|
+
if (retryCount > 0) {
|
|
88
|
+
console.log(yellow(`[Titan] Retrying Rust server (Attempt ${retryCount})...`));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
serverProcess = spawn("cargo", ["run", "--jobs", "1"], {
|
|
92
|
+
cwd: serverPath,
|
|
93
|
+
stdio: "inherit",
|
|
94
|
+
shell: true,
|
|
95
|
+
env: { ...process.env, CARGO_INCREMENTAL: "0" }
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
serverProcess.on("close", async (code) => {
|
|
99
|
+
if (isKilling) return;
|
|
100
|
+
const runTime = Date.now() - startTime;
|
|
101
|
+
if (code !== 0 && code !== null && runTime < 15000 && retryCount < 5) {
|
|
102
|
+
await startRustServer(retryCount + 1);
|
|
103
|
+
} else if (code !== 0 && code !== null && retryCount >= 5) {
|
|
104
|
+
console.log(red(`[Titan] Server failed to start after multiple attempts.`));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function rebuild() {
|
|
110
|
+
// process.stdout.write(gray("[Titan] Preparing runtime... "));
|
|
111
|
+
const start = Date.now();
|
|
112
|
+
try {
|
|
113
|
+
execSync("node app/app.js", { stdio: "ignore" });
|
|
114
|
+
await bundle();
|
|
115
|
+
// console.log(green("Done"));
|
|
116
|
+
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
|
117
|
+
console.log(gray(` A new orbit is ready for your app in ${elapsed}s`));
|
|
118
|
+
console.log(green(` Your app is now orbiting Titan Planet`));
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.log(red("Failed"));
|
|
121
|
+
console.log(red("[Titan] Failed to prepare runtime. Check your app/app.js"));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function startDev() {
|
|
126
|
+
const root = process.cwd();
|
|
127
|
+
// Check if Rust actions exist by looking for .rs files in app/actions
|
|
128
|
+
const actionsDir = path.join(root, "app", "actions");
|
|
129
|
+
let hasRust = false;
|
|
130
|
+
if (fs.existsSync(actionsDir)) {
|
|
131
|
+
hasRust = fs.readdirSync(actionsDir).some(f => f.endsWith(".rs"));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const mode = hasRust ? "Rust + JS Actions" : "JS Actions";
|
|
135
|
+
const version = getTitanVersion();
|
|
136
|
+
|
|
137
|
+
console.clear();
|
|
138
|
+
console.log("");
|
|
139
|
+
console.log(` ${bold(cyan("Titan Planet"))} ${gray("v" + version)} ${yellow("[ Dev Mode ]")}`);
|
|
140
|
+
console.log("");
|
|
141
|
+
console.log(` ${gray("Type: ")} ${mode}`);
|
|
142
|
+
console.log(` ${gray("Hot Reload: ")} ${green("Enabled")}`);
|
|
143
|
+
|
|
144
|
+
if (fs.existsSync(path.join(root, ".env"))) {
|
|
145
|
+
console.log(` ${gray("Env: ")} ${yellow("Loaded")}`);
|
|
146
|
+
}
|
|
147
|
+
console.log(""); // Spacer
|
|
148
|
+
|
|
149
|
+
// FIRST BUILD
|
|
150
|
+
try {
|
|
151
|
+
await rebuild();
|
|
152
|
+
await startRustServer();
|
|
153
|
+
} catch (e) {
|
|
154
|
+
console.log(red("[Titan] Initial build failed. Waiting for changes..."));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ... watcher logic same as before but using color vars ...
|
|
158
|
+
const watcher = chokidar.watch(["app", ".env"], {
|
|
159
|
+
ignoreInitial: true,
|
|
160
|
+
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
let timer = null;
|
|
164
|
+
watcher.on("all", async (event, file) => {
|
|
165
|
+
if (timer) clearTimeout(timer);
|
|
166
|
+
timer = setTimeout(async () => {
|
|
167
|
+
console.log(""); // Spacer before reload logs
|
|
168
|
+
if (file.includes(".env")) {
|
|
169
|
+
console.log(yellow("[Titan] Env Refreshed"));
|
|
170
|
+
} else {
|
|
171
|
+
console.log(cyan(`[Titan] Change: ${path.basename(file)}`));
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
await killServer();
|
|
175
|
+
await rebuild();
|
|
176
|
+
await startRustServer();
|
|
177
|
+
} catch (e) {
|
|
178
|
+
console.log(red("[Titan] Build failed -- waiting for changes..."));
|
|
179
|
+
}
|
|
180
|
+
}, 1000);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Handle graceful exit to release file locks
|
|
185
|
+
async function handleExit() {
|
|
186
|
+
console.log("\n[Titan] Stopping server...");
|
|
187
|
+
await killServer();
|
|
188
|
+
process.exit(0);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
process.on("SIGINT", handleExit);
|
|
192
|
+
process.on("SIGTERM", handleExit);
|
|
193
|
+
|
|
194
|
+
startDev();
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { bundle } from "./bundle.js";
|
|
4
|
+
|
|
5
|
+
const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
|
|
6
|
+
const green = (t) => `\x1b[32m${t}\x1b[0m`;
|
|
7
|
+
|
|
8
|
+
const routes = {};
|
|
9
|
+
const dynamicRoutes = {};
|
|
10
|
+
const actionMap = {};
|
|
11
|
+
|
|
12
|
+
function addRoute(method, route) {
|
|
13
|
+
const key = `${method.toUpperCase()}:${route}`;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
reply(value) {
|
|
18
|
+
routes[key] = {
|
|
19
|
+
type: typeof value === "object" ? "json" : "text",
|
|
20
|
+
value
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
action(name) {
|
|
25
|
+
if (route.includes(":")) {
|
|
26
|
+
if (!dynamicRoutes[method]) dynamicRoutes[method] = [];
|
|
27
|
+
dynamicRoutes[method].push({
|
|
28
|
+
method: method.toUpperCase(),
|
|
29
|
+
pattern: route,
|
|
30
|
+
action: name
|
|
31
|
+
});
|
|
32
|
+
} else {
|
|
33
|
+
routes[key] = {
|
|
34
|
+
type: "action",
|
|
35
|
+
value: name
|
|
36
|
+
};
|
|
37
|
+
actionMap[key] = name;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {Object} RouteHandler
|
|
45
|
+
* @property {(value: any) => void} reply - Send a direct response
|
|
46
|
+
* @property {(name: string) => void} action - Bind to a server-side action
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Titan App Builder
|
|
51
|
+
*/
|
|
52
|
+
const t = {
|
|
53
|
+
/**
|
|
54
|
+
* Define a GET route
|
|
55
|
+
* @param {string} route
|
|
56
|
+
* @returns {RouteHandler}
|
|
57
|
+
*/
|
|
58
|
+
get(route) {
|
|
59
|
+
return addRoute("GET", route);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Define a POST route
|
|
64
|
+
* @param {string} route
|
|
65
|
+
* @returns {RouteHandler}
|
|
66
|
+
*/
|
|
67
|
+
post(route) {
|
|
68
|
+
return addRoute("POST", route);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
log(module, msg) {
|
|
72
|
+
console.log(`[\x1b[35m${module}\x1b[0m] ${msg}`);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Start the Titan Server
|
|
77
|
+
* @param {number} [port=3000]
|
|
78
|
+
* @param {string} [msg=""]
|
|
79
|
+
*/
|
|
80
|
+
async start(port = 3000, msg = "") {
|
|
81
|
+
try {
|
|
82
|
+
console.log(cyan("[Titan] Preparing runtime..."));
|
|
83
|
+
await bundle();
|
|
84
|
+
|
|
85
|
+
const base = path.join(process.cwd(), "server");
|
|
86
|
+
if (!fs.existsSync(base)) {
|
|
87
|
+
fs.mkdirSync(base, { recursive: true });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const routesPath = path.join(base, "routes.json");
|
|
91
|
+
const actionMapPath = path.join(base, "action_map.json");
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(
|
|
94
|
+
routesPath,
|
|
95
|
+
JSON.stringify(
|
|
96
|
+
{
|
|
97
|
+
__config: { port },
|
|
98
|
+
routes,
|
|
99
|
+
__dynamic_routes: Object.values(dynamicRoutes).flat()
|
|
100
|
+
},
|
|
101
|
+
null,
|
|
102
|
+
2
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
fs.writeFileSync(
|
|
107
|
+
actionMapPath,
|
|
108
|
+
JSON.stringify(actionMap, null, 2)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
console.log(green("✔ Titan metadata written successfully"));
|
|
112
|
+
if (msg) console.log(cyan(msg));
|
|
113
|
+
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.error(`\x1b[31m[Titan] Build Error: ${e.message}\x1b[0m`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
export default t;
|
package/titanpl-sdk/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "titanpl-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Development SDK for Titan Planet. Provides TypeScript type definitions for the global 't' runtime object and a 'lite' test-harness runtime for building and verifying extensions.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -18,20 +18,6 @@ COPY . .
|
|
|
18
18
|
# Install JS dependencies (needed for Titan DSL + bundler)
|
|
19
19
|
RUN npm install
|
|
20
20
|
|
|
21
|
-
SHELL ["/bin/bash", "-c"]
|
|
22
|
-
|
|
23
|
-
# Extract Titan extensions into .ext
|
|
24
|
-
RUN mkdir -p /app/.ext && \
|
|
25
|
-
find /app/node_modules -maxdepth 5 -type f -name "titan.json" -print0 | \
|
|
26
|
-
while IFS= read -r -d '' file; do \
|
|
27
|
-
pkg_dir="$(dirname "$file")"; \
|
|
28
|
-
pkg_name="$(basename "$pkg_dir")"; \
|
|
29
|
-
echo "Copying Titan extension: $pkg_name from $pkg_dir"; \
|
|
30
|
-
cp -r "$pkg_dir" "/app/.ext/$pkg_name"; \
|
|
31
|
-
done && \
|
|
32
|
-
echo "Extensions in .ext:" && \
|
|
33
|
-
ls -R /app/.ext
|
|
34
|
-
|
|
35
21
|
# Build Titan metadata + bundle JS actions
|
|
36
22
|
RUN titan build
|
|
37
23
|
|
|
@@ -48,7 +34,7 @@ FROM debian:stable-slim
|
|
|
48
34
|
WORKDIR /app
|
|
49
35
|
|
|
50
36
|
# Copy Rust binary from builder stage
|
|
51
|
-
COPY --from=builder /app/server/target/release/
|
|
37
|
+
COPY --from=builder /app/server/target/release/server ./titan-server
|
|
52
38
|
|
|
53
39
|
# Copy Titan routing metadata
|
|
54
40
|
COPY --from=builder /app/server/routes.json ./routes.json
|
|
@@ -58,9 +44,10 @@ COPY --from=builder /app/server/action_map.json ./action_map.json
|
|
|
58
44
|
RUN mkdir -p /app/actions
|
|
59
45
|
COPY --from=builder /app/server/actions /app/actions
|
|
60
46
|
|
|
61
|
-
|
|
62
|
-
COPY --from=builder /app/.ext ./.ext
|
|
47
|
+
COPY --from=builder /app/db /app/assets
|
|
63
48
|
|
|
49
|
+
# Expose Titan port
|
|
64
50
|
EXPOSE 3000
|
|
65
51
|
|
|
52
|
+
# Start Titan
|
|
66
53
|
CMD ["./titan-server"]
|