@titanpl/core 2.0.0 → 2.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 +33 -0
- package/index.js +359 -35
- package/native/target/release/titan_core.dll +0 -0
- package/package.json +3 -3
- package/titan.json +97 -2
package/README.md
CHANGED
|
@@ -49,6 +49,9 @@ Cryptographic utilities using native Rust implementations.
|
|
|
49
49
|
- `crypto.randomBytes(size: number): string` - Generate random bytes as hex string.
|
|
50
50
|
- `crypto.uuid(): string` - Generate a UUID v4.
|
|
51
51
|
- `crypto.compare(hash: string, target: string): boolean` - Constant-time comparison.
|
|
52
|
+
- `crypto.encrypt(algorithm: string, key: string, plaintext: string): string` - AES-256-GCM Encrypt (Returns Base64).
|
|
53
|
+
- `crypto.decrypt(algorithm: string, key: string, ciphertext: string): string` - AES-256-GCM Decrypt.
|
|
54
|
+
- `crypto.hashKeyed(algo: string, key: string, message: string): string` - HMAC-SHA256/512.
|
|
52
55
|
|
|
53
56
|
**Example:**
|
|
54
57
|
```javascript
|
|
@@ -59,6 +62,36 @@ const valid = t.core.crypto.compare(
|
|
|
59
62
|
);
|
|
60
63
|
```
|
|
61
64
|
|
|
65
|
+
### `buffer` (Buffer Utilities)
|
|
66
|
+
Utilities for binary data manipulation.
|
|
67
|
+
- `buffer.fromBase64(str: string): Uint8Array` - Decode Base64 string.
|
|
68
|
+
- `buffer.toBase64(bytes: Uint8Array|string): string` - Encode to Base64.
|
|
69
|
+
- `buffer.fromHex(str: string): Uint8Array` - Decode Hex string.
|
|
70
|
+
- `buffer.toHex(bytes: Uint8Array|string): string` - Encode to Hex.
|
|
71
|
+
- `buffer.fromUtf8(str: string): Uint8Array` - Encode UTF-8 string to bytes.
|
|
72
|
+
- `buffer.toUtf8(bytes: Uint8Array): string` - Decode bytes to UTF-8 string.
|
|
73
|
+
|
|
74
|
+
### `ls` / `localStorage` (Persistent Storage)
|
|
75
|
+
Key-value storage persisted to disk (via Sled).
|
|
76
|
+
- `ls.get(key: string): string|null` - Get value.
|
|
77
|
+
- `ls.set(key: string, value: string): void` - Set value.
|
|
78
|
+
- `ls.remove(key: string): void` - Remove key.
|
|
79
|
+
- `ls.clear(): void` - Clear all storage.
|
|
80
|
+
- `ls.keys(): string[]` - List all keys.
|
|
81
|
+
|
|
82
|
+
### `session` (Server-side Sessions)
|
|
83
|
+
Session management backed by persistent storage.
|
|
84
|
+
- `session.get(sessionId: string, key: string): string|null` - Get session value.
|
|
85
|
+
- `session.set(sessionId: string, key: string, value: string): void` - Set session value.
|
|
86
|
+
- `session.delete(sessionId: string, key: string): void` - Delete session value.
|
|
87
|
+
- `session.clear(sessionId: string): void` - Clear entire session.
|
|
88
|
+
|
|
89
|
+
### `cookies` (HTTP Cookies)
|
|
90
|
+
Cookie parsing and serialization.
|
|
91
|
+
- `cookies.get(req: object, name: string): string|null` - Parse cookie from request headers.
|
|
92
|
+
- `cookies.set(res: object, name: string, value: string, options: object): void` - Set `Set-Cookie` header on response. Options: `{ httpOnly, secure, sameSite, path, maxAge }`.
|
|
93
|
+
- `cookies.delete(res: object, name: string): void` - Delete cookie (expire).
|
|
94
|
+
|
|
62
95
|
### `os` (Operating System)
|
|
63
96
|
Get system information.
|
|
64
97
|
- `os.platform(): string` - OS platform (e.g., `linux`, `windows`).
|
package/index.js
CHANGED
|
@@ -1,10 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
2
|
+
|
|
3
|
+
function local_btoa(input) {
|
|
4
|
+
let str = String(input);
|
|
5
|
+
let output = '';
|
|
6
|
+
|
|
7
|
+
for (let i = 0; i < str.length; i += 3) {
|
|
8
|
+
const char1 = str.charCodeAt(i);
|
|
9
|
+
const char2 = str.charCodeAt(i + 1);
|
|
10
|
+
const char3 = str.charCodeAt(i + 2);
|
|
11
|
+
|
|
12
|
+
const enc1 = char1 >> 2;
|
|
13
|
+
const enc2 = ((char1 & 3) << 4) | (char2 >> 4);
|
|
14
|
+
let enc3 = ((char2 & 15) << 2) | (char3 >> 6);
|
|
15
|
+
let enc4 = char3 & 63;
|
|
16
|
+
|
|
17
|
+
if (isNaN(char2)) {
|
|
18
|
+
enc3 = enc4 = 64;
|
|
19
|
+
} else if (isNaN(char3)) {
|
|
20
|
+
enc4 = 64;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
output += b64chars.charAt(enc1) + b64chars.charAt(enc2);
|
|
24
|
+
output += (enc3 === 64) ? '=' : b64chars.charAt(enc3);
|
|
25
|
+
output += (enc4 === 64) ? '=' : b64chars.charAt(enc4);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return output;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function local_atob(input) {
|
|
32
|
+
// Remove whitespace and padding '='
|
|
33
|
+
let str = String(input).replace(/[\t\n\f\r =]/g, "");
|
|
34
|
+
let output = '';
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < str.length; i += 4) {
|
|
37
|
+
const c1Str = str.charAt(i);
|
|
38
|
+
const c2Str = str.charAt(i + 1);
|
|
39
|
+
const c3Str = str.charAt(i + 2);
|
|
40
|
+
const c4Str = str.charAt(i + 3);
|
|
41
|
+
|
|
42
|
+
const e1 = b64chars.indexOf(c1Str);
|
|
43
|
+
const e2 = c2Str ? b64chars.indexOf(c2Str) : -1;
|
|
44
|
+
const e3 = c3Str ? b64chars.indexOf(c3Str) : -1;
|
|
45
|
+
const e4 = c4Str ? b64chars.indexOf(c4Str) : -1;
|
|
46
|
+
|
|
47
|
+
// e1 and e2 are required
|
|
48
|
+
if (e1 < 0 || e2 < 0) continue;
|
|
49
|
+
|
|
50
|
+
// Shift and mask to reconstruct bytes
|
|
51
|
+
const c1 = (e1 << 2) | (e2 >> 4);
|
|
52
|
+
output += String.fromCharCode(c1);
|
|
53
|
+
|
|
54
|
+
if (e3 !== -1) {
|
|
55
|
+
const c2 = ((e2 & 15) << 4) | (e3 >> 2);
|
|
56
|
+
output += String.fromCharCode(c2);
|
|
57
|
+
}
|
|
58
|
+
if (e4 !== -1) {
|
|
59
|
+
const c3 = ((e3 & 3) << 6) | e4;
|
|
60
|
+
output += String.fromCharCode(c3);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return output;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function local_utf8_encode(str) {
|
|
68
|
+
let result = [];
|
|
69
|
+
for (let i = 0; i < str.length; i++) {
|
|
70
|
+
let c = str.charCodeAt(i);
|
|
71
|
+
if (c < 0x80) { result.push(c); }
|
|
72
|
+
else if (c < 0x800) {
|
|
73
|
+
result.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
|
|
74
|
+
}
|
|
75
|
+
else if (c < 0xd800 || c >= 0xe000) {
|
|
76
|
+
result.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
i++;
|
|
80
|
+
c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
|
|
81
|
+
result.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return new Uint8Array(result);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function local_utf8_decode(bytes) {
|
|
88
|
+
let str = "";
|
|
89
|
+
let i = 0;
|
|
90
|
+
while (i < bytes.length) {
|
|
91
|
+
let c = bytes[i++];
|
|
92
|
+
if (c > 127) {
|
|
93
|
+
if (c > 191 && c < 224) {
|
|
94
|
+
c = ((c & 31) << 6) | (bytes[i++] & 63);
|
|
95
|
+
} else if (c > 223 && c < 240) {
|
|
96
|
+
c = ((c & 15) << 12) | ((bytes[i++] & 63) << 6) | (bytes[i++] & 63);
|
|
97
|
+
} else if (c > 239 && c < 248) {
|
|
98
|
+
c = ((c & 7) << 18) | ((bytes[i++] & 63) << 12) | ((bytes[i++] & 63) << 6) | (bytes[i++] & 63);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (c <= 0xffff) str += String.fromCharCode(c);
|
|
102
|
+
else if (c <= 0x10ffff) {
|
|
103
|
+
c -= 0x10000;
|
|
104
|
+
str += String.fromCharCode(c >> 10 | 0xd800) + String.fromCharCode(c & 0x3ff | 0xdc00);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return str;
|
|
108
|
+
}
|
|
109
|
+
|
|
3
110
|
|
|
4
111
|
// Native bindings are loaded by the runtime into t["@titanpl/core"]
|
|
5
|
-
// We capture them here before defining our high-level API.
|
|
6
112
|
const natives = t["@titanpl/core"] || {};
|
|
7
113
|
|
|
114
|
+
|
|
115
|
+
// Native Function bindings
|
|
8
116
|
const native_fs_read_file = natives.fs_read_file;
|
|
9
117
|
const native_fs_write_file = natives.fs_write_file;
|
|
10
118
|
const native_fs_readdir = natives.fs_readdir;
|
|
@@ -13,45 +121,70 @@ const native_fs_exists = natives.fs_exists;
|
|
|
13
121
|
const native_fs_stat = natives.fs_stat;
|
|
14
122
|
const native_fs_remove = natives.fs_remove;
|
|
15
123
|
const native_path_cwd = natives.path_cwd;
|
|
124
|
+
|
|
16
125
|
const native_crypto_hash = natives.crypto_hash;
|
|
17
126
|
const native_crypto_random_bytes = natives.crypto_random_bytes;
|
|
18
127
|
const native_crypto_uuid = natives.crypto_uuid;
|
|
128
|
+
const native_crypto_encrypt = natives.crypto_encrypt;
|
|
129
|
+
const native_crypto_decrypt = natives.crypto_decrypt;
|
|
130
|
+
const native_crypto_hash_keyed = natives.crypto_hash_keyed;
|
|
131
|
+
const native_crypto_compare = natives.crypto_compare;
|
|
132
|
+
|
|
19
133
|
const native_os_info = natives.os_info;
|
|
20
134
|
const native_net_resolve = natives.net_resolve;
|
|
21
135
|
const native_net_ip = natives.net_ip;
|
|
22
136
|
const native_proc_info = natives.proc_info;
|
|
23
137
|
const native_time_sleep = natives.time_sleep;
|
|
24
138
|
|
|
139
|
+
const native_ls_get = natives.ls_get;
|
|
140
|
+
const native_ls_set = natives.ls_set;
|
|
141
|
+
const native_ls_remove = natives.ls_remove;
|
|
142
|
+
const native_ls_clear = natives.ls_clear;
|
|
143
|
+
const native_ls_keys = natives.ls_keys;
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
const native_session_get = natives.session_get;
|
|
148
|
+
const native_session_set = natives.session_set;
|
|
149
|
+
const native_session_delete = natives.session_delete;
|
|
150
|
+
const native_session_clear = natives.session_clear;
|
|
151
|
+
|
|
25
152
|
// --- FS ---
|
|
153
|
+
/** File System module */
|
|
26
154
|
const fs = {
|
|
155
|
+
/** Reads file content as UTF-8 string */
|
|
27
156
|
readFile: (path) => {
|
|
28
157
|
if (!native_fs_read_file) throw new Error("Native fs_read_file not found");
|
|
29
158
|
const res = native_fs_read_file(path);
|
|
30
|
-
if (res.startsWith("ERROR:")) throw new Error(res);
|
|
159
|
+
if (res && res.startsWith("ERROR:")) throw new Error(res);
|
|
31
160
|
return res;
|
|
32
161
|
},
|
|
162
|
+
/** Writes content to file */
|
|
33
163
|
writeFile: (path, content) => {
|
|
34
164
|
if (!native_fs_write_file) throw new Error("Native fs_write_file not found");
|
|
35
165
|
native_fs_write_file(path, content);
|
|
36
166
|
},
|
|
167
|
+
/** Reads directory contents */
|
|
37
168
|
readdir: (path) => {
|
|
38
169
|
if (!native_fs_readdir) throw new Error("Native fs_readdir not found");
|
|
39
170
|
const res = native_fs_readdir(path);
|
|
40
|
-
// Runtime might return empty string on error or failures, or "[]"
|
|
41
171
|
try {
|
|
42
172
|
return JSON.parse(res);
|
|
43
173
|
} catch (e) {
|
|
44
174
|
return [];
|
|
45
175
|
}
|
|
46
176
|
},
|
|
177
|
+
/** Creates direction recursively */
|
|
47
178
|
mkdir: (path) => {
|
|
48
179
|
if (!native_fs_mkdir) throw new Error("Native fs_mkdir not found");
|
|
49
180
|
native_fs_mkdir(path);
|
|
50
181
|
},
|
|
182
|
+
/** Checks if path exists */
|
|
51
183
|
exists: (path) => {
|
|
52
184
|
if (!native_fs_exists) throw new Error("Native fs_exists not found");
|
|
53
185
|
return native_fs_exists(path);
|
|
54
186
|
},
|
|
187
|
+
/** Returns file stats */
|
|
55
188
|
stat: (path) => {
|
|
56
189
|
if (!native_fs_stat) throw new Error("Native fs_stat not found");
|
|
57
190
|
const res = native_fs_stat(path);
|
|
@@ -61,6 +194,7 @@ const fs = {
|
|
|
61
194
|
return {};
|
|
62
195
|
}
|
|
63
196
|
},
|
|
197
|
+
/** Removes file or directory */
|
|
64
198
|
remove: (path) => {
|
|
65
199
|
if (!native_fs_remove) throw new Error("Native fs_remove not found");
|
|
66
200
|
native_fs_remove(path);
|
|
@@ -68,15 +202,12 @@ const fs = {
|
|
|
68
202
|
};
|
|
69
203
|
|
|
70
204
|
// --- Path ---
|
|
71
|
-
|
|
205
|
+
/** Path manipulation module */
|
|
72
206
|
const path = {
|
|
73
207
|
join: (...args) => {
|
|
74
|
-
// Normalize to forward slashes for internal join logic, or keep native?
|
|
75
|
-
// Let's normalize to / for consistency unless user requests native path separator
|
|
76
208
|
return args
|
|
77
209
|
.map((part, i) => {
|
|
78
210
|
if (!part) return '';
|
|
79
|
-
// Replace backslashes with forward slashes for easier joining
|
|
80
211
|
let p = part.replace(/\\/g, '/');
|
|
81
212
|
if (i === 0) return p.trim().replace(/[\/]*$/g, '');
|
|
82
213
|
return p.trim().replace(/(^[\/]*|[\/]*$)/g, '');
|
|
@@ -90,10 +221,8 @@ const path = {
|
|
|
90
221
|
resolved = path.join(resolved, arg);
|
|
91
222
|
}
|
|
92
223
|
if (!resolved.startsWith('/')) {
|
|
93
|
-
// If windows, check for drive letter C:\ or D:\ or start with \
|
|
94
224
|
const isWindowsAbs = /^[a-zA-Z]:\\/.test(resolved) || resolved.startsWith('\\');
|
|
95
225
|
if (!isWindowsAbs && native_path_cwd) {
|
|
96
|
-
// native_path_cwd returns result of std::env::current_dir()
|
|
97
226
|
const cwd = native_path_cwd();
|
|
98
227
|
if (cwd) {
|
|
99
228
|
resolved = path.join(cwd, resolved);
|
|
@@ -115,17 +244,42 @@ const path = {
|
|
|
115
244
|
};
|
|
116
245
|
|
|
117
246
|
// --- Crypto ---
|
|
247
|
+
/** Cryptography module */
|
|
118
248
|
const crypto = {
|
|
119
249
|
hash: (algo, data) => native_crypto_hash ? native_crypto_hash(algo, data) : "",
|
|
120
250
|
randomBytes: (size) => native_crypto_random_bytes ? native_crypto_random_bytes(size) : "",
|
|
121
251
|
uuid: () => native_crypto_uuid ? native_crypto_uuid() : "",
|
|
122
252
|
base64: {
|
|
123
|
-
encode: (str) =>
|
|
124
|
-
decode: (str) =>
|
|
253
|
+
encode: (str) => local_btoa(str),
|
|
254
|
+
decode: (str) => local_atob(str),
|
|
255
|
+
},
|
|
256
|
+
// Extended API
|
|
257
|
+
/** Encrypts data using AES-256-GCM. Returns Base64 string. */
|
|
258
|
+
encrypt: (algorithm, key, plaintext) => {
|
|
259
|
+
if (!native_crypto_encrypt) throw new Error("Native crypto_encrypt not found");
|
|
260
|
+
const res = native_crypto_encrypt(algorithm, JSON.stringify({ key, plaintext }));
|
|
261
|
+
if (res.startsWith("ERROR:")) throw new Error(res.substring(6));
|
|
262
|
+
return res;
|
|
263
|
+
},
|
|
264
|
+
/** Decrypts data using AES-256-GCM. Returns plaintext string. */
|
|
265
|
+
decrypt: (algorithm, key, ciphertext) => {
|
|
266
|
+
if (!native_crypto_decrypt) throw new Error("Native crypto_decrypt not found");
|
|
267
|
+
const res = native_crypto_decrypt(algorithm, JSON.stringify({ key, ciphertext }));
|
|
268
|
+
if (res.startsWith("ERROR:")) throw new Error(res.substring(6));
|
|
269
|
+
return res;
|
|
125
270
|
},
|
|
271
|
+
/** Computes HMAC-SHA256/512. Returns Hex string. */
|
|
272
|
+
hashKeyed: (algorithm, key, message) => {
|
|
273
|
+
if (!native_crypto_hash_keyed) throw new Error("Native crypto_hash_keyed not found");
|
|
274
|
+
const res = native_crypto_hash_keyed(algorithm, JSON.stringify({ key, message }));
|
|
275
|
+
if (res.startsWith("ERROR:")) throw new Error(res.substring(6));
|
|
276
|
+
return res;
|
|
277
|
+
},
|
|
278
|
+
/** Constant-time string comparison */
|
|
126
279
|
compare: (a, b) => {
|
|
280
|
+
if (native_crypto_compare) return native_crypto_compare(a, b);
|
|
281
|
+
// Fallback insecure
|
|
127
282
|
if (a.length !== b.length) return false;
|
|
128
|
-
// Constant time comparison not guaranteed here in JS easily without specialized tricks
|
|
129
283
|
let mismatch = 0;
|
|
130
284
|
for (let i = 0; i < a.length; ++i) {
|
|
131
285
|
mismatch |= (a.charCodeAt(i) ^ b.charCodeAt(i));
|
|
@@ -134,6 +288,181 @@ const crypto = {
|
|
|
134
288
|
}
|
|
135
289
|
};
|
|
136
290
|
|
|
291
|
+
// --- Buffer ---
|
|
292
|
+
// Helper for hex
|
|
293
|
+
function hexToBytes(hex) {
|
|
294
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
295
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
296
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
297
|
+
}
|
|
298
|
+
return bytes;
|
|
299
|
+
}
|
|
300
|
+
function bytesToHex(bytes) {
|
|
301
|
+
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Buffer utility module */
|
|
305
|
+
const buffer = {
|
|
306
|
+
/** Creates Uint8Array from Base64 string */
|
|
307
|
+
fromBase64: (str) => {
|
|
308
|
+
const binary = local_atob(str);
|
|
309
|
+
const bytes = new Uint8Array(binary.length);
|
|
310
|
+
for (let i = 0; i < binary.length; i++) {
|
|
311
|
+
bytes[i] = binary.charCodeAt(i);
|
|
312
|
+
}
|
|
313
|
+
return bytes;
|
|
314
|
+
},
|
|
315
|
+
/** encoded Uint8Array or String to Base64 string */
|
|
316
|
+
toBase64: (bytes) => {
|
|
317
|
+
let binary = '';
|
|
318
|
+
if (typeof bytes === 'string') {
|
|
319
|
+
return local_btoa(bytes);
|
|
320
|
+
}
|
|
321
|
+
// Uint8Array
|
|
322
|
+
const len = bytes.byteLength;
|
|
323
|
+
for (let i = 0; i < len; i++) {
|
|
324
|
+
binary += String.fromCharCode(bytes[i]);
|
|
325
|
+
}
|
|
326
|
+
return local_btoa(binary);
|
|
327
|
+
},
|
|
328
|
+
/** Creates Uint8Array from Hex string */
|
|
329
|
+
fromHex: (str) => hexToBytes(str),
|
|
330
|
+
/** Encodes bytes to Hex string */
|
|
331
|
+
toHex: (bytes) => {
|
|
332
|
+
if (typeof bytes === 'string') {
|
|
333
|
+
return bytesToHex(local_utf8_encode(bytes));
|
|
334
|
+
}
|
|
335
|
+
return bytesToHex(bytes);
|
|
336
|
+
},
|
|
337
|
+
/** Creates Uint8Array from UTF-8 string */
|
|
338
|
+
fromUtf8: (str) => local_utf8_encode(str),
|
|
339
|
+
/** Decodes bytes to UTF-8 string */
|
|
340
|
+
toUtf8: (bytes) => local_utf8_decode(bytes)
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// --- Local Storage ---
|
|
344
|
+
/** Persistent Local Storage */
|
|
345
|
+
const ls = {
|
|
346
|
+
get: (key) => {
|
|
347
|
+
try {
|
|
348
|
+
const content = fs.readFile("titan_storage.json");
|
|
349
|
+
const db = JSON.parse(content || "{}");
|
|
350
|
+
return db[key] || null;
|
|
351
|
+
} catch (e) { return null; }
|
|
352
|
+
},
|
|
353
|
+
set: (key, value) => {
|
|
354
|
+
try {
|
|
355
|
+
let db = {};
|
|
356
|
+
try { db = JSON.parse(fs.readFile("titan_storage.json") || "{}"); } catch (e) { }
|
|
357
|
+
db[key] = String(value);
|
|
358
|
+
fs.writeFile("titan_storage.json", JSON.stringify(db));
|
|
359
|
+
} catch (e) { }
|
|
360
|
+
},
|
|
361
|
+
remove: (key) => {
|
|
362
|
+
try {
|
|
363
|
+
let db = {};
|
|
364
|
+
try { db = JSON.parse(fs.readFile("titan_storage.json") || "{}"); } catch (e) { }
|
|
365
|
+
delete db[key];
|
|
366
|
+
fs.writeFile("titan_storage.json", JSON.stringify(db));
|
|
367
|
+
} catch (e) { }
|
|
368
|
+
},
|
|
369
|
+
clear: () => {
|
|
370
|
+
try {
|
|
371
|
+
fs.writeFile("titan_storage.json", "{}");
|
|
372
|
+
} catch (e) { }
|
|
373
|
+
},
|
|
374
|
+
keys: () => {
|
|
375
|
+
try {
|
|
376
|
+
let db = JSON.parse(fs.readFile("titan_storage.json") || "{}");
|
|
377
|
+
return Object.keys(db);
|
|
378
|
+
} catch (e) { return []; }
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// --- Sessions ---
|
|
383
|
+
/** Server-side Session Management */
|
|
384
|
+
const session = {
|
|
385
|
+
get: (sessionId, key) => {
|
|
386
|
+
try {
|
|
387
|
+
const content = fs.readFile("titan_sessions.json");
|
|
388
|
+
const db = JSON.parse(content || "{}");
|
|
389
|
+
const sessionData = db[sessionId] || {};
|
|
390
|
+
return sessionData[key] || null;
|
|
391
|
+
} catch (e) { return null; }
|
|
392
|
+
},
|
|
393
|
+
set: (sessionId, key, value) => {
|
|
394
|
+
try {
|
|
395
|
+
let db = {};
|
|
396
|
+
try { db = JSON.parse(fs.readFile("titan_sessions.json") || "{}"); } catch (e) { }
|
|
397
|
+
if (!db[sessionId]) db[sessionId] = {};
|
|
398
|
+
db[sessionId][key] = String(value);
|
|
399
|
+
fs.writeFile("titan_sessions.json", JSON.stringify(db));
|
|
400
|
+
} catch (e) { }
|
|
401
|
+
},
|
|
402
|
+
delete: (sessionId, key) => {
|
|
403
|
+
try {
|
|
404
|
+
let db = {};
|
|
405
|
+
try { db = JSON.parse(fs.readFile("titan_sessions.json") || "{}"); } catch (e) { }
|
|
406
|
+
if (db[sessionId]) {
|
|
407
|
+
delete db[sessionId][key];
|
|
408
|
+
fs.writeFile("titan_sessions.json", JSON.stringify(db));
|
|
409
|
+
}
|
|
410
|
+
} catch (e) { }
|
|
411
|
+
},
|
|
412
|
+
clear: (sessionId) => {
|
|
413
|
+
try {
|
|
414
|
+
let db = {};
|
|
415
|
+
try { db = JSON.parse(fs.readFile("titan_sessions.json") || "{}"); } catch (e) { }
|
|
416
|
+
delete db[sessionId];
|
|
417
|
+
fs.writeFile("titan_sessions.json", JSON.stringify(db));
|
|
418
|
+
} catch (e) { }
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
// --- Cookies ---
|
|
424
|
+
/** HTTP Cookie Utilities */
|
|
425
|
+
const cookies = {
|
|
426
|
+
/** Parses cookie from request headers */
|
|
427
|
+
get: (req, name) => {
|
|
428
|
+
if (!req || !req.headers) return null;
|
|
429
|
+
const cookieHeader = req.headers.cookie;
|
|
430
|
+
if (!cookieHeader) return null;
|
|
431
|
+
const cookies = cookieHeader.split(';');
|
|
432
|
+
for (let c of cookies) {
|
|
433
|
+
const [k, v] = c.trim().split('=');
|
|
434
|
+
if (k === name) return decodeURIComponent(v);
|
|
435
|
+
}
|
|
436
|
+
return null;
|
|
437
|
+
},
|
|
438
|
+
/** Sets Set-Cookie header on response */
|
|
439
|
+
set: (res, name, value, options = {}) => {
|
|
440
|
+
if (!res || !res.setHeader) return;
|
|
441
|
+
let cookie = `${name}=${encodeURIComponent(value)}`;
|
|
442
|
+
if (options.maxAge) cookie += `; Max-Age=${options.maxAge}`;
|
|
443
|
+
if (options.path) cookie += `; Path=${options.path}`;
|
|
444
|
+
if (options.httpOnly) cookie += `; HttpOnly`;
|
|
445
|
+
if (options.secure) cookie += `; Secure`;
|
|
446
|
+
if (options.sameSite) cookie += `; SameSite=${options.sameSite}`;
|
|
447
|
+
|
|
448
|
+
let prev = res.getHeader ? res.getHeader('Set-Cookie') : null;
|
|
449
|
+
if (prev) {
|
|
450
|
+
if (Array.isArray(prev)) {
|
|
451
|
+
prev.push(cookie);
|
|
452
|
+
res.setHeader('Set-Cookie', prev);
|
|
453
|
+
} else {
|
|
454
|
+
res.setHeader('Set-Cookie', [prev, cookie]);
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
res.setHeader('Set-Cookie', cookie);
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
/** Deletes cookie by setting maxAge=0 */
|
|
461
|
+
delete: (res, name) => {
|
|
462
|
+
cookies.set(res, name, "", { maxAge: 0, path: '/' });
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
137
466
|
// --- OS ---
|
|
138
467
|
const os = {
|
|
139
468
|
platform: () => {
|
|
@@ -156,7 +485,7 @@ const os = {
|
|
|
156
485
|
const info = JSON.parse(native_os_info());
|
|
157
486
|
return info.freeMemory;
|
|
158
487
|
},
|
|
159
|
-
tmpdir: () => '/tmp'
|
|
488
|
+
tmpdir: () => '/tmp'
|
|
160
489
|
};
|
|
161
490
|
|
|
162
491
|
// --- Net ---
|
|
@@ -166,14 +495,10 @@ const net = {
|
|
|
166
495
|
return JSON.parse(native_net_resolve(hostname));
|
|
167
496
|
},
|
|
168
497
|
ip: () => native_net_ip ? native_net_ip() : "127.0.0.1",
|
|
169
|
-
ping: (host) =>
|
|
170
|
-
// Mock ping or simple verify
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
498
|
+
ping: (host) => true
|
|
173
499
|
};
|
|
174
500
|
|
|
175
501
|
// --- Proc ---
|
|
176
|
-
// Memoize static info if needed, but here we call native
|
|
177
502
|
const proc = {
|
|
178
503
|
pid: () => {
|
|
179
504
|
if (!native_proc_info) return 0;
|
|
@@ -185,27 +510,19 @@ const proc = {
|
|
|
185
510
|
const info = JSON.parse(native_proc_info());
|
|
186
511
|
return info.uptime;
|
|
187
512
|
},
|
|
188
|
-
memory: () => {
|
|
189
|
-
// Optional: return full memory usage if possible
|
|
190
|
-
return {};
|
|
191
|
-
}
|
|
513
|
+
memory: () => ({})
|
|
192
514
|
};
|
|
193
515
|
|
|
194
516
|
// --- Time ---
|
|
195
517
|
const time = {
|
|
196
518
|
sleep: (ms) => {
|
|
197
|
-
if (native_time_sleep)
|
|
198
|
-
native_time_sleep(ms);
|
|
199
|
-
} else {
|
|
200
|
-
console.log("[TitanCore] Warn: native_time_sleep missing");
|
|
201
|
-
}
|
|
519
|
+
if (native_time_sleep) native_time_sleep(ms);
|
|
202
520
|
},
|
|
203
521
|
now: () => Date.now(),
|
|
204
522
|
timestamp: () => new Date().toISOString()
|
|
205
523
|
};
|
|
206
524
|
|
|
207
525
|
// --- URL ---
|
|
208
|
-
// Simple URLSearchParams polyfill for V8 runtime
|
|
209
526
|
class TitanURLSearchParams {
|
|
210
527
|
constructor(init = '') {
|
|
211
528
|
this._params = {};
|
|
@@ -235,11 +552,9 @@ class TitanURLSearchParams {
|
|
|
235
552
|
|
|
236
553
|
const url = {
|
|
237
554
|
parse: (str) => {
|
|
238
|
-
// Basic URL parsing if native URL is available
|
|
239
555
|
if (typeof URL !== 'undefined') {
|
|
240
556
|
return new URL(str);
|
|
241
557
|
}
|
|
242
|
-
// Simple fallback parser
|
|
243
558
|
const match = str.match(/^(https?:)\/\/([^/:]+)(?::(\d+))?(\/[^?#]*)?(\?[^#]*)?(#.*)?$/);
|
|
244
559
|
if (!match) throw new Error('Invalid URL');
|
|
245
560
|
return {
|
|
@@ -255,7 +570,6 @@ const url = {
|
|
|
255
570
|
SearchParams: TitanURLSearchParams
|
|
256
571
|
};
|
|
257
572
|
|
|
258
|
-
|
|
259
573
|
// Create the main core export object (following titan-valid pattern)
|
|
260
574
|
const core = {
|
|
261
575
|
fs,
|
|
@@ -265,10 +579,13 @@ const core = {
|
|
|
265
579
|
net,
|
|
266
580
|
proc,
|
|
267
581
|
time,
|
|
268
|
-
url
|
|
582
|
+
url,
|
|
583
|
+
buffer, // t.core.buffer
|
|
584
|
+
ls,
|
|
585
|
+
session,
|
|
586
|
+
cookies
|
|
269
587
|
};
|
|
270
588
|
|
|
271
|
-
|
|
272
589
|
t.fs = fs;
|
|
273
590
|
t.path = path;
|
|
274
591
|
t.crypto = crypto;
|
|
@@ -278,6 +595,13 @@ t.proc = proc;
|
|
|
278
595
|
t.time = time;
|
|
279
596
|
t.url = url;
|
|
280
597
|
|
|
598
|
+
// New Global Modules
|
|
599
|
+
t.buffer = buffer;
|
|
600
|
+
t.ls = ls;
|
|
601
|
+
t.localStorage = ls;
|
|
602
|
+
t.session = session;
|
|
603
|
+
t.cookies = cookies;
|
|
604
|
+
|
|
281
605
|
// Attach core as unified namespace (main access point)
|
|
282
606
|
t.core = core;
|
|
283
607
|
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@titanpl/core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "The official Core Standard Library for Titan Planet - provides fs, path, crypto, os, net, proc, time, and url modules",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "globals.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build:native": "cd native && cargo build --release",
|
|
9
|
-
"test": "
|
|
9
|
+
"test": "titan run ext"
|
|
10
10
|
},
|
|
11
11
|
"keywords": [
|
|
12
12
|
"titan",
|
|
@@ -41,4 +41,4 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@titanpl/valid": "^1.1.1"
|
|
43
43
|
}
|
|
44
|
-
}
|
|
44
|
+
}
|
package/titan.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@titanpl/core",
|
|
3
3
|
"description": "The official Core Standard Library for Titan Planet",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.2",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"native": {
|
|
7
7
|
"path": "native/target/release/titan_core.dll",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"symbol": "fs_read_file",
|
|
11
11
|
"parameters": [
|
|
12
12
|
"string"
|
|
13
|
-
],
|
|
13
|
+
],
|
|
14
14
|
"result": "string"
|
|
15
15
|
},
|
|
16
16
|
"fs_write_file": {
|
|
@@ -109,6 +109,101 @@
|
|
|
109
109
|
"f64"
|
|
110
110
|
],
|
|
111
111
|
"result": "void"
|
|
112
|
+
},
|
|
113
|
+
"crypto_encrypt": {
|
|
114
|
+
"symbol": "crypto_encrypt",
|
|
115
|
+
"parameters": [
|
|
116
|
+
"string",
|
|
117
|
+
"string"
|
|
118
|
+
],
|
|
119
|
+
"result": "string"
|
|
120
|
+
},
|
|
121
|
+
"crypto_decrypt": {
|
|
122
|
+
"symbol": "crypto_decrypt",
|
|
123
|
+
"parameters": [
|
|
124
|
+
"string",
|
|
125
|
+
"string"
|
|
126
|
+
],
|
|
127
|
+
"result": "string"
|
|
128
|
+
},
|
|
129
|
+
"crypto_hash_keyed": {
|
|
130
|
+
"symbol": "crypto_hash_keyed",
|
|
131
|
+
"parameters": [
|
|
132
|
+
"string",
|
|
133
|
+
"string"
|
|
134
|
+
],
|
|
135
|
+
"result": "string"
|
|
136
|
+
},
|
|
137
|
+
"crypto_compare": {
|
|
138
|
+
"symbol": "crypto_compare",
|
|
139
|
+
"parameters": [
|
|
140
|
+
"string",
|
|
141
|
+
"string"
|
|
142
|
+
],
|
|
143
|
+
"result": "bool"
|
|
144
|
+
},
|
|
145
|
+
"ls_get": {
|
|
146
|
+
"symbol": "ls_get",
|
|
147
|
+
"parameters": [
|
|
148
|
+
"string"
|
|
149
|
+
],
|
|
150
|
+
"result": "string"
|
|
151
|
+
},
|
|
152
|
+
"ls_set": {
|
|
153
|
+
"symbol": "ls_set",
|
|
154
|
+
"parameters": [
|
|
155
|
+
"string",
|
|
156
|
+
"string"
|
|
157
|
+
],
|
|
158
|
+
"result": "void"
|
|
159
|
+
},
|
|
160
|
+
"ls_remove": {
|
|
161
|
+
"symbol": "ls_remove",
|
|
162
|
+
"parameters": [
|
|
163
|
+
"string"
|
|
164
|
+
],
|
|
165
|
+
"result": "void"
|
|
166
|
+
},
|
|
167
|
+
"ls_clear": {
|
|
168
|
+
"symbol": "ls_clear",
|
|
169
|
+
"parameters": [],
|
|
170
|
+
"result": "void"
|
|
171
|
+
},
|
|
172
|
+
"ls_keys": {
|
|
173
|
+
"symbol": "ls_keys",
|
|
174
|
+
"parameters": [],
|
|
175
|
+
"result": "string"
|
|
176
|
+
},
|
|
177
|
+
"session_get": {
|
|
178
|
+
"symbol": "session_get",
|
|
179
|
+
"parameters": [
|
|
180
|
+
"string",
|
|
181
|
+
"string"
|
|
182
|
+
],
|
|
183
|
+
"result": "string"
|
|
184
|
+
},
|
|
185
|
+
"session_set": {
|
|
186
|
+
"symbol": "session_set",
|
|
187
|
+
"parameters": [
|
|
188
|
+
"string",
|
|
189
|
+
"string"
|
|
190
|
+
],
|
|
191
|
+
"result": "void"
|
|
192
|
+
},
|
|
193
|
+
"session_delete": {
|
|
194
|
+
"symbol": "session_delete",
|
|
195
|
+
"parameters": [
|
|
196
|
+
"string",
|
|
197
|
+
"string"
|
|
198
|
+
],
|
|
199
|
+
"result": "void"
|
|
200
|
+
},
|
|
201
|
+
"session_clear": {
|
|
202
|
+
"symbol": "session_clear",
|
|
203
|
+
"parameters": [
|
|
204
|
+
"string"
|
|
205
|
+
],
|
|
206
|
+
"result": "void"
|
|
112
207
|
}
|
|
113
208
|
}
|
|
114
209
|
}
|