@t8n/ui 1.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.
@@ -0,0 +1,249 @@
1
+
2
+ // -- Module Definitions (for imports from "titan") --
3
+
4
+ export interface RouteHandler {
5
+ reply(value: any): void;
6
+ action(name: string): void;
7
+ }
8
+
9
+ export interface TitanBuilder {
10
+ get(route: string): RouteHandler;
11
+ post(route: string): RouteHandler;
12
+ log(module: string, msg: string): void;
13
+ start(port?: number, msg?: string): Promise<void>;
14
+ }
15
+
16
+ declare const builder: TitanBuilder;
17
+ export const Titan: TitanBuilder;
18
+ export default builder;
19
+
20
+ export declare function defineAction<T>(actionFn: (req: TitanRequest) => T): (req: TitanRequest) => T;
21
+
22
+ // -- Global Definitions (Runtime Environment) --
23
+
24
+ /**
25
+ * # Drift - Orchestration Engine
26
+ *
27
+ * Revolutionary system for high-performance asynchronous operations using a **Deterministic Replay-based Suspension** model.
28
+ *
29
+ * ## Mechanism
30
+ * Drift utilizes a suspension model similar to **Algebraic Effects**. When a `drift()` operation is encountered,
31
+ * the runtime suspends the isolate, offloads the task to the background Tokio executor, and frees the isolate
32
+ * to handle other requests. Upon completion, the code is efficiently **re-played** with the result injected.
33
+ *
34
+ * @param promise - The promise or expression to drift.
35
+ * @returns The resolved value of the input promise.
36
+ *
37
+ * @example
38
+ * ```javascript
39
+ * const resp = drift t.fetch("http://api.titan.com");
40
+ * ```
41
+ */
42
+ declare var drift: <T>(promise: Promise<T> | T) => T;
43
+
44
+ declare global {
45
+ /**
46
+ * Titan Global Drift
47
+ */
48
+ var drift: <T>(promise: Promise<T> | T) => T;
49
+
50
+ interface TitanRequest {
51
+ body: any;
52
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
53
+ path: string;
54
+ headers: {
55
+ host?: string;
56
+ "content-type"?: string;
57
+ "user-agent"?: string;
58
+ authorization?: string;
59
+ [key: string]: string | undefined;
60
+ };
61
+ params: Record<string, string>;
62
+ query: Record<string, string>;
63
+ }
64
+
65
+ interface DbConnection {
66
+ query(sql: string, params?: any[]): any[];
67
+ }
68
+
69
+ function defineAction<T>(actionFn: (req: TitanRequest) => T): (req: TitanRequest) => T;
70
+
71
+ var req: TitanRequest;
72
+
73
+ interface TitanRuntimeUtils {
74
+ log(...args: any[]): void;
75
+ read(path: string): string;
76
+ fetch(url: string, options?: {
77
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
78
+ headers?: Record<string, string>;
79
+ body?: string | object;
80
+ }): {
81
+ ok: boolean;
82
+ status?: number;
83
+ body?: string;
84
+ error?: string;
85
+ };
86
+
87
+ jwt: {
88
+ sign(payload: object, secret: string, options?: { expiresIn?: string | number }): string;
89
+ verify(token: string, secret: string): any;
90
+ };
91
+
92
+ password: {
93
+ hash(password: string): string;
94
+ verify(password: string, hash: string): boolean;
95
+ };
96
+
97
+ /** ### `db` (Database Connection) */
98
+ db: {
99
+ connect(url: string): DbConnection;
100
+ };
101
+
102
+ /** ### `fs` (File System) */
103
+ fs: TitanCore.FileSystem;
104
+
105
+ /** ### `path` (Path Manipulation) */
106
+ path: TitanCore.Path;
107
+
108
+ /** ### `crypto` (Cryptography) */
109
+ crypto: TitanCore.Crypto;
110
+
111
+ /** ### `buffer` (Buffer Utilities) */
112
+ buffer: TitanCore.BufferModule;
113
+
114
+ /** ### `ls` / `localStorage` (Persistent Storage) */
115
+ ls: TitanCore.LocalStorage;
116
+ localStorage: TitanCore.LocalStorage;
117
+
118
+ /** ### `session` (Server-side Sessions) */
119
+ session: TitanCore.Session;
120
+
121
+ /** ### `cookies` (HTTP Cookies) */
122
+ cookies: TitanCore.Cookies;
123
+
124
+ /** ### `os` (Operating System) */
125
+ os: TitanCore.OS;
126
+
127
+ /** ### `net` (Network) */
128
+ net: TitanCore.Net;
129
+
130
+ /** ### `proc` (Process) */
131
+ proc: TitanCore.Process;
132
+
133
+ /** ### `time` (Time) */
134
+ time: TitanCore.Time;
135
+
136
+ /** ### `url` (URL) */
137
+ url: TitanCore.URLModule;
138
+
139
+ /** ### `response` (HTTP Response Builder) */
140
+ response: TitanCore.ResponseModule;
141
+
142
+ valid: any;
143
+ [key: string]: any;
144
+ }
145
+
146
+ const t: TitanRuntimeUtils;
147
+ const Titan: TitanRuntimeUtils;
148
+
149
+ namespace TitanCore {
150
+ interface FileSystem {
151
+ readFile(path: string): string;
152
+ writeFile(path: string, content: string): void;
153
+ readdir(path: string): string[];
154
+ mkdir(path: string): void;
155
+ exists(path: string): boolean;
156
+ stat(path: string): { size: number, isFile: boolean, isDir: boolean, modified: number };
157
+ remove(path: string): void;
158
+ }
159
+
160
+ interface Path {
161
+ join(...args: string[]): string;
162
+ resolve(...args: string[]): string;
163
+ extname(path: string): string;
164
+ dirname(path: string): string;
165
+ basename(path: string): string;
166
+ }
167
+
168
+ interface Crypto {
169
+ hash(algorithm: 'sha256' | 'sha512' | 'md5', data: string): string;
170
+ randomBytes(size: number): string;
171
+ uuid(): string;
172
+ compare(hash: string, target: string): boolean;
173
+ encrypt(algorithm: string, key: string, plaintext: string): string;
174
+ decrypt(algorithm: string, key: string, ciphertext: string): string;
175
+ hashKeyed(algorithm: 'hmac-sha256' | 'hmac-sha512', key: string, message: string): string;
176
+ }
177
+
178
+ interface BufferModule {
179
+ fromBase64(str: string): Uint8Array;
180
+ toBase64(bytes: Uint8Array | string): string;
181
+ fromHex(str: string): Uint8Array;
182
+ toHex(bytes: Uint8Array | string): string;
183
+ fromUtf8(str: string): Uint8Array;
184
+ toUtf8(bytes: Uint8Array): string;
185
+ }
186
+
187
+ interface LocalStorage {
188
+ get(key: string): string | null;
189
+ set(key: string, value: string): void;
190
+ remove(key: string): void;
191
+ clear(): void;
192
+ keys(): string[];
193
+ }
194
+
195
+ interface Session {
196
+ get(sessionId: string, key: string): string | null;
197
+ set(sessionId: string, key: string, value: string): void;
198
+ delete(sessionId: string, key: string): void;
199
+ clear(sessionId: string): void;
200
+ }
201
+
202
+ interface Cookies {
203
+ get(req: any, name: string): string | null;
204
+ set(res: any, name: string, value: string, options?: any): void;
205
+ delete(res: any, name: string): void;
206
+ }
207
+
208
+ interface OS {
209
+ platform(): string;
210
+ cpus(): number;
211
+ totalMemory(): number;
212
+ freeMemory(): number;
213
+ tmpdir(): string;
214
+ }
215
+
216
+ interface Net {
217
+ resolveDNS(hostname: string): string[];
218
+ ip(): string;
219
+ ping(host: string): boolean;
220
+ }
221
+
222
+ interface Process {
223
+ pid(): number;
224
+ uptime(): number;
225
+ memory(): Record<string, any>;
226
+ }
227
+
228
+ interface Time {
229
+ sleep(ms: number): void;
230
+ now(): number;
231
+ timestamp(): string;
232
+ }
233
+
234
+ interface URLModule {
235
+ parse(url: string): any;
236
+ format(urlObj: any): string;
237
+ SearchParams: any;
238
+ }
239
+
240
+ interface ResponseModule {
241
+ (options: any): any;
242
+ text(content: string, status?: number): any;
243
+ html(content: string, status?: number): any;
244
+ json(content: any, status?: number): any;
245
+ redirect(url: string, status?: number): any;
246
+ empty(status?: number): any;
247
+ }
248
+ }
249
+ }
@@ -0,0 +1,8 @@
1
+ import { titanpl } from 'eslint-plugin-titanpl';
2
+
3
+ export default [
4
+ {
5
+ ignores: ['**/*.d.ts']
6
+ },
7
+ titanpl
8
+ ];
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "esnext",
4
+ "target": "esnext",
5
+ "checkJs": false,
6
+ "noImplicitAny": false,
7
+ "allowJs": true,
8
+ "moduleResolution": "node",
9
+ "baseUrl": ".",
10
+ "paths": {
11
+ "*": [
12
+ "./app/*"
13
+ ]
14
+ }
15
+ },
16
+ "include": [
17
+ "app/**/*",
18
+ "titan/**/*"
19
+ ]
20
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "titanpl",
3
+ "version": "1.0.0",
4
+ "description": "A Titan Planet server",
5
+ "type": "module",
6
+ "titan": {
7
+ "template": "js"
8
+ },
9
+ "dependencies": {
10
+ "@titanpl/core": "latest",
11
+ "chokidar": "^5.0.0",
12
+ "esbuild": "^0.27.2"
13
+ },
14
+ "scripts": {
15
+ "build": "titan build",
16
+ "dev": "titan dev",
17
+ "start": "titan start",
18
+ "lint": "eslint .",
19
+ "lint:fix": "eslint . --fix"
20
+ },
21
+ "devDependencies": {
22
+ "eslint": "^9.39.2",
23
+ "eslint-plugin-titanpl": "^1.0.0"
24
+ }
25
+ }
@@ -0,0 +1,32 @@
1
+
2
+ [package]
3
+ name = "titan-server"
4
+ version = "0.1.0"
5
+ edition = "2024"
6
+
7
+ [dependencies]
8
+ axum = "0.8.7"
9
+ dotenv = "0.15.0"
10
+ reqwest = { version = "0.12.24", features = ["json", "rustls-tls", "gzip", "brotli", "blocking"] }
11
+ serde = { version = "1.0.228", features = ["derive"] }
12
+ serde_json = "1.0.145"
13
+ thiserror = "2.0.17"
14
+ tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros", "process"] }
15
+ tower-http = { version = "0.6.7", features = ["cors"] }
16
+ tracing = "0.1.43"
17
+ tracing-subscriber = "0.3.22"
18
+ anyhow = "1"
19
+ v8 = "0.106.0"
20
+ dotenvy = "0.15"
21
+ base64 = "0.21"
22
+ regex = "1.10"
23
+ bcrypt = "0.15"
24
+ jsonwebtoken = "9"
25
+ postgres = { version = "0.19", features = ["with-serde_json-1"] }
26
+ libloading = "0.8"
27
+ walkdir = "2"
28
+ crossbeam = "0.8.4"
29
+ dashmap = "6.1.0"
30
+ bytes = "1.11.0"
31
+ smallvec = "1.15.1"
32
+ num_cpus = "1.17.0"
@@ -0,0 +1,171 @@
1
+ use std::collections::HashMap;
2
+ use std::env;
3
+ use std::path::{Path, PathBuf};
4
+ use serde::Deserialize;
5
+ use serde_json::Value;
6
+
7
+ /// Route configuration (loaded from routes.json)
8
+ #[derive(Debug, Deserialize, Clone)]
9
+ pub struct RouteVal {
10
+ pub r#type: String,
11
+ #[serde(alias = "target")]
12
+ pub value: Value,
13
+ }
14
+
15
+ #[derive(Debug, Deserialize, Clone)]
16
+ pub struct DynamicRoute {
17
+ pub method: String,
18
+ pub pattern: String,
19
+ pub action: String,
20
+ }
21
+
22
+ // -------------------------
23
+ // ACTION DIRECTORY RESOLUTION
24
+ // -------------------------
25
+
26
+ pub fn resolve_actions_dir() -> PathBuf {
27
+ // Respect explicit override first
28
+ if let Ok(override_dir) = env::var("TITAN_ACTIONS_DIR") {
29
+ return PathBuf::from(override_dir);
30
+ }
31
+
32
+ // Production container layout
33
+ if Path::new("/app/actions").exists() {
34
+ return PathBuf::from("/app/actions");
35
+ }
36
+
37
+ // Try to walk up from the executing binary to discover `<...>/server/actions`
38
+ if let Ok(exe) = std::env::current_exe() {
39
+ if let Some(parent) = exe.parent() {
40
+ if let Some(target_dir) = parent.parent() {
41
+ if let Some(server_dir) = target_dir.parent() {
42
+ let candidate = server_dir.join("actions");
43
+ if candidate.exists() {
44
+ return candidate;
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ // Fall back to local ./actions
52
+ PathBuf::from("./actions")
53
+ }
54
+
55
+ /// Try to find the directory that contains compiled action bundles.
56
+ pub fn find_actions_dir(project_root: &PathBuf) -> Option<PathBuf> {
57
+ let candidates = [
58
+ project_root.join("app").join("actions"),
59
+ project_root.join("actions"),
60
+ project_root.join("server").join("actions"),
61
+ project_root.join("..").join("server").join("actions"),
62
+ PathBuf::from("/app").join("actions"),
63
+ PathBuf::from("actions"),
64
+ ];
65
+
66
+ for p in &candidates {
67
+ if p.exists() && p.is_dir() {
68
+ return Some(p.clone());
69
+ }
70
+ }
71
+
72
+ None
73
+ }
74
+
75
+ // Dynamic Matcher (Core Logic)
76
+
77
+ pub fn match_dynamic_route(
78
+ method: &str,
79
+ path: &str,
80
+ routes: &[DynamicRoute],
81
+ ) -> Option<(String, HashMap<String, String>)> {
82
+ let path_segments: Vec<&str> =
83
+ path.trim_matches('/').split('/').collect();
84
+
85
+ for route in routes {
86
+ if route.method != method {
87
+ continue;
88
+ }
89
+
90
+ let pattern_segments: Vec<&str> =
91
+ route.pattern.trim_matches('/').split('/').collect();
92
+
93
+ if pattern_segments.len() != path_segments.len() {
94
+ continue;
95
+ }
96
+
97
+ let mut params = HashMap::new();
98
+ let mut matched = true;
99
+
100
+ for (pat, val) in pattern_segments.iter().zip(path_segments.iter()) {
101
+ if pat.starts_with(':') {
102
+ let inner = &pat[1..];
103
+
104
+ let (name, ty) = inner
105
+ .split_once('<')
106
+ .map(|(n, t)| (n, t.trim_end_matches('>')))
107
+ .unwrap_or((inner, "string"));
108
+
109
+ let valid = match ty {
110
+ "number" => val.parse::<i64>().is_ok(),
111
+ "string" => true,
112
+ _ => false,
113
+ };
114
+
115
+ if !valid {
116
+ matched = false;
117
+ break;
118
+ }
119
+
120
+ params.insert(name.to_string(), (*val).to_string());
121
+ } else if pat != val {
122
+ matched = false;
123
+ break;
124
+ }
125
+ }
126
+
127
+ if matched {
128
+ return Some((route.action.clone(), params));
129
+ }
130
+ }
131
+
132
+ None
133
+ }
134
+
135
+ // -------------------------
136
+ // ACTION SCANNING
137
+ // -------------------------
138
+
139
+ pub fn scan_actions(root: &PathBuf) -> HashMap<String, PathBuf> {
140
+ let mut map = HashMap::new();
141
+
142
+ // Locate actions dir
143
+ let actions_dir = resolve_actions_dir();
144
+ let dir = if actions_dir.exists() {
145
+ actions_dir
146
+ } else {
147
+ match find_actions_dir(root) {
148
+ Some(d) => d,
149
+ None => return map,
150
+ }
151
+ };
152
+
153
+ if let Ok(entries) = std::fs::read_dir(dir) {
154
+ for entry in entries.flatten() {
155
+ let path = entry.path();
156
+ if !path.is_file() { continue; }
157
+
158
+ let ext = path.extension().and_then(|s| s.to_str()).unwrap_or("");
159
+ if ext != "js" && ext != "jsbundle" {
160
+ continue;
161
+ }
162
+
163
+ let file_stem = path.file_stem().and_then(|s| s.to_str()).unwrap_or("");
164
+ if file_stem.is_empty() { continue; }
165
+
166
+ map.insert(file_stem.to_string(), path);
167
+ }
168
+ }
169
+
170
+ map
171
+ }
@@ -0,0 +1,10 @@
1
+ use v8::JsError;
2
+
3
+ // A helper to Format v8 Errors
4
+ pub fn format_js_error(err: JsError, action: &str) -> String {
5
+ format!(
6
+ "Action: {}\n{}",
7
+ action,
8
+ err.to_string()
9
+ )
10
+ }