@ezetgalaxy/titan 26.15.0 → 26.15.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezetgalaxy/titan",
3
- "version": "26.15.0",
3
+ "version": "26.15.1",
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",
@@ -65,7 +65,8 @@
65
65
  "test:all": "vitest run && vitest run --config vitest.config.e2e.ts"
66
66
  },
67
67
  "dependencies": {
68
- "@titanpl/core": "^1.0.1",
68
+ "@titanpl/core": "latest",
69
+ "@titanpl/node": "latest",
69
70
  "chokidar": "^5.0.0",
70
71
  "esbuild": "^0.27.2",
71
72
  "prompts": "^2.4.2"
@@ -46,7 +46,7 @@ RUN mkdir -p /app/.ext && \
46
46
  rm -rf "/app/.ext/$pkg_name/node_modules"; \
47
47
  done
48
48
 
49
- RUN cd server && cargo build --release
49
+ RUN titan build
50
50
 
51
51
 
52
52
 
@@ -1058,6 +1058,8 @@ declare global {
1058
1058
  * @see https://titan-docs-ez.vercel.app/docs/10-extensions — Extensions documentation
1059
1059
  */
1060
1060
  [key: string]: any;
1061
+
1062
+
1061
1063
  }
1062
1064
 
1063
1065
  /**
@@ -1978,6 +1980,64 @@ declare global {
1978
1980
  }
1979
1981
 
1980
1982
  }
1983
+
1984
+ /**
1985
+ * Node-compatible `process` global (Titan Shim).
1986
+ *
1987
+ * This is a lightweight compatibility layer intended
1988
+ * for supporting common Node libraries.
1989
+ *
1990
+ * Internally backed by:
1991
+ * - t.proc
1992
+ * - t.os
1993
+ * - t.time
1994
+ */
1995
+ const process: {
1996
+ /** Process ID */
1997
+ pid: number;
1998
+
1999
+ /** Platform (linux, win32, darwin) */
2000
+ platform: string;
2001
+
2002
+ /** CPU architecture */
2003
+ arch: string;
2004
+
2005
+ /** Node version string (shimmed) */
2006
+ version: string;
2007
+
2008
+ /** Version object */
2009
+ versions: {
2010
+ node: string;
2011
+ titan: string;
2012
+ };
2013
+
2014
+ /** Environment variables */
2015
+ env: Record<string, string | undefined>;
2016
+
2017
+ /** CLI arguments */
2018
+ argv: string[];
2019
+
2020
+ /** Current working directory */
2021
+ cwd(): string;
2022
+
2023
+ /** Uptime in seconds */
2024
+ uptime(): number;
2025
+
2026
+ /** High resolution time */
2027
+ hrtime: {
2028
+ (time?: [number, number]): [number, number];
2029
+ bigint(): bigint;
2030
+ };
2031
+
2032
+ /** Memory usage info */
2033
+ memoryUsage(): Record<string, any>;
2034
+
2035
+ /** No-op event listener (compat only) */
2036
+ on(event: string, listener: (...args: any[]) => void): void;
2037
+
2038
+ /** Exit stub (throws in Titan runtime) */
2039
+ exit(code?: number): never;
2040
+ };
1981
2041
  }
1982
2042
 
1983
2043
  export { };
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@titanpl/core": "latest",
11
+ "@titanpl/node": "latest",
11
12
  "chokidar": "^5.0.0",
12
13
  "esbuild": "^0.27.2"
13
14
  },
@@ -14,193 +14,204 @@ import { renderErrorBox, parseEsbuildError } from './error-box.js';
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = path.dirname(__filename);
16
16
 
17
+ // Required for resolving node_modules inside ESM
18
+ const require = createRequire(import.meta.url);
19
+
20
+ /**
21
+ * Titan Node Builtin Rewrite Map
22
+ * Rewrites Node builtins to @titanpl/node shims
23
+ */
24
+ const NODE_BUILTIN_MAP = {
25
+ "fs": "@titanpl/node/fs",
26
+ "node:fs": "@titanpl/node/fs",
27
+
28
+ "path": "@titanpl/node/path",
29
+ "node:path": "@titanpl/node/path",
30
+
31
+ "os": "@titanpl/node/os",
32
+ "node:os": "@titanpl/node/os",
33
+
34
+ "crypto": "@titanpl/node/crypto",
35
+ "node:crypto": "@titanpl/node/crypto",
36
+
37
+ "process": "@titanpl/node/process",
38
+
39
+ "util": "@titanpl/node/util",
40
+ "node:util": "@titanpl/node/util",
41
+ };
42
+
43
+ /**
44
+ * Titan Node Compatibility Plugin
45
+ * Rewrites require/import of Node builtins
46
+ * Returns absolute paths (required by esbuild)
47
+ */
48
+ const titanNodeCompatPlugin = {
49
+ name: "titan-node-compat",
50
+ setup(build) {
51
+ build.onResolve({ filter: /.*/ }, args => {
52
+ if (NODE_BUILTIN_MAP[args.path]) {
53
+ try {
54
+ const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
55
+ return { path: resolved };
56
+ } catch (e) {
57
+ throw new Error(
58
+ `[Titan] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`
59
+ );
60
+ }
61
+ }
62
+ });
63
+ }
64
+ };
65
+
17
66
  /**
18
67
  * Get Titan version for error branding
19
68
  */
20
69
  function getTitanVersion() {
21
- try {
22
- const require = createRequire(import.meta.url);
23
- const pkgPath = require.resolve("@ezetgalaxy/titan/package.json");
24
- return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
25
- } catch (e) {
26
- return "0.1.0";
27
- }
70
+ try {
71
+ const pkgPath = require.resolve("@ezetgalaxy/titan/package.json");
72
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
73
+ } catch (e) {
74
+ return "0.1.0";
75
+ }
28
76
  }
29
77
 
30
78
  /**
31
79
  * Custom error class for bundle errors
32
80
  */
33
81
  export class BundleError extends Error {
34
- constructor(message, errors = [], warnings = []) {
35
- super(message);
36
- this.name = 'BundleError';
37
- this.errors = errors;
38
- this.warnings = warnings;
39
- this.isBundleError = true;
40
- }
82
+ constructor(message, errors = [], warnings = []) {
83
+ super(message);
84
+ this.name = 'BundleError';
85
+ this.errors = errors;
86
+ this.warnings = warnings;
87
+ this.isBundleError = true;
88
+ }
41
89
  }
42
90
 
43
91
  /**
44
- * Validates that the entry point exists and is readable
45
- * @param {string} entryPoint - Entry file path
46
- * @throws {BundleError} If file doesn't exist or isn't readable
92
+ * Validate entry file exists
47
93
  */
48
94
  async function validateEntryPoint(entryPoint) {
49
- const absPath = path.resolve(entryPoint);
50
-
51
- if (!fs.existsSync(absPath)) {
52
- throw new BundleError(
53
- `Entry point does not exist: ${entryPoint}`,
54
- [{
55
- text: `Cannot find file: ${absPath}`,
56
- location: { file: entryPoint }
57
- }]
58
- );
59
- }
95
+ const absPath = path.resolve(entryPoint);
60
96
 
61
- try {
62
- await fs.promises.access(absPath, fs.constants.R_OK);
63
- } catch (err) {
64
- throw new BundleError(
65
- `Entry point is not readable: ${entryPoint}`,
66
- [{
67
- text: `Cannot read file: ${absPath}`,
68
- location: { file: entryPoint }
69
- }]
70
- );
71
- }
97
+ if (!fs.existsSync(absPath)) {
98
+ throw new BundleError(
99
+ `Entry point does not exist: ${entryPoint}`,
100
+ [{ text: `Cannot find file: ${absPath}`, location: { file: entryPoint } }]
101
+ );
102
+ }
103
+
104
+ try {
105
+ await fs.promises.access(absPath, fs.constants.R_OK);
106
+ } catch {
107
+ throw new BundleError(
108
+ `Entry point is not readable: ${entryPoint}`,
109
+ [{ text: `Cannot read file: ${absPath}`, location: { file: entryPoint } }]
110
+ );
111
+ }
72
112
  }
73
113
 
74
114
  /**
75
- * Bundles a single JavaScript/TypeScript file using esbuild
76
- * @param {Object} options - Bundle options
77
- * @returns {Promise<void>}
78
- * @throws {BundleError} If bundling fails
115
+ * Bundles a single file
79
116
  */
80
117
  export async function bundleFile(options) {
81
- const {
82
- entryPoint,
83
- outfile,
84
- format = 'iife',
85
- minify = false,
86
- sourcemap = false,
87
- platform = 'neutral',
88
- globalName = '__titan_exports',
89
- target = 'es2020',
90
- banner = {},
91
- footer = {}
92
- } = options;
93
-
94
- // Validate entry point exists
95
- await validateEntryPoint(entryPoint);
96
-
97
- // Ensure output directory exists
98
- const outDir = path.dirname(outfile);
99
- await fs.promises.mkdir(outDir, { recursive: true });
118
+ const {
119
+ entryPoint,
120
+ outfile,
121
+ format = 'iife',
122
+ minify = false,
123
+ sourcemap = false,
124
+ platform = 'neutral',
125
+ globalName = '__titan_exports',
126
+ target = 'es2020',
127
+ banner = {},
128
+ footer = {}
129
+ } = options;
100
130
 
101
- try {
102
- // Run esbuild with error logging enabled
103
- const result = await esbuild.build({
104
- entryPoints: [entryPoint],
105
- bundle: true,
106
- outfile,
107
- format,
108
- globalName,
109
- platform,
110
- target,
111
- banner,
112
- footer,
113
- minify,
114
- sourcemap,
115
- logLevel: 'silent', // We handle all errors ourselves
116
- logLimit: 0,
117
- color: false,
118
- write: true,
119
- metafile: false,
120
- });
121
-
122
- // Check for errors in the result
123
- if (result.errors && result.errors.length > 0) {
124
- throw new BundleError(
125
- `Build failed with ${result.errors.length} error(s)`,
126
- result.errors,
127
- result.warnings || []
128
- );
129
- }
131
+ await validateEntryPoint(entryPoint);
130
132
 
131
- } catch (err) {
132
- if (err.errors && err.errors.length > 0) {
133
- // This is an esbuild error with detailed error information
134
- throw new BundleError(
135
- `Build failed with ${err.errors.length} error(s)`,
136
- err.errors,
137
- err.warnings || []
138
- );
139
- }
133
+ const outDir = path.dirname(outfile);
134
+ await fs.promises.mkdir(outDir, { recursive: true });
135
+
136
+ try {
137
+ const result = await esbuild.build({
138
+ entryPoints: [entryPoint],
139
+ bundle: true,
140
+ outfile,
141
+ format,
142
+ globalName,
143
+ platform,
144
+ target,
145
+ banner,
146
+ footer,
147
+ minify,
148
+ sourcemap,
149
+ logLevel: 'silent',
150
+ logLimit: 0,
151
+ write: true,
152
+ metafile: false,
153
+ plugins: [titanNodeCompatPlugin],
154
+ });
155
+
156
+ if (result.errors?.length) {
157
+ throw new BundleError(
158
+ `Build failed with ${result.errors.length} error(s)`,
159
+ result.errors,
160
+ result.warnings || []
161
+ );
162
+ }
140
163
 
141
- // Other unexpected errors
142
- throw new BundleError(
143
- `Unexpected build error: ${err.message}`,
144
- [{
145
- text: err.message,
146
- location: { file: entryPoint }
147
- }]
148
- );
164
+ } catch (err) {
165
+ if (err.errors?.length) {
166
+ throw new BundleError(
167
+ `Build failed with ${err.errors.length} error(s)`,
168
+ err.errors,
169
+ err.warnings || []
170
+ );
149
171
  }
172
+
173
+ throw new BundleError(
174
+ `Unexpected build error: ${err.message}`,
175
+ [{ text: err.message, location: { file: entryPoint } }]
176
+ );
177
+ }
150
178
  }
151
179
 
152
180
  /**
153
- * Main bundle function - scans app/actions and bundles all files
154
- * RULE: This function handles ALL esbuild errors and prints error boxes directly
155
- * RULE: After printing error box, throws Error("__TITAN_BUNDLE_FAILED__")
156
- * @returns {Promise<void>}
181
+ * Main bundler
157
182
  */
158
183
  export async function bundle() {
159
- const root = process.cwd();
160
- const actionsDir = path.join(root, 'app', 'actions');
161
- const bundleDir = path.join(root, 'server', 'src', 'actions');
184
+ const root = process.cwd();
185
+ const actionsDir = path.join(root, 'app', 'actions');
186
+ const bundleDir = path.join(root, 'server', 'src', 'actions');
162
187
 
163
- // Ensure bundle directory exists and is clean
164
- if (fs.existsSync(bundleDir)) {
165
- fs.rmSync(bundleDir, { recursive: true, force: true });
166
- }
167
- await fs.promises.mkdir(bundleDir, { recursive: true });
168
-
169
- // Check if actions directory exists
170
- if (!fs.existsSync(actionsDir)) {
171
- return; // No actions to bundle
172
- }
188
+ if (fs.existsSync(bundleDir)) {
189
+ fs.rmSync(bundleDir, { recursive: true, force: true });
190
+ }
191
+ await fs.promises.mkdir(bundleDir, { recursive: true });
173
192
 
174
- // Get all JS/TS files in actions directory
175
- const files = fs.readdirSync(actionsDir).filter(f =>
176
- (f.endsWith('.js') || f.endsWith('.ts')) && !f.endsWith('.d.ts')
177
- );
193
+ if (!fs.existsSync(actionsDir)) return;
178
194
 
179
- if (files.length === 0) {
180
- return; // No action files
181
- }
195
+ const files = fs.readdirSync(actionsDir).filter(f =>
196
+ (f.endsWith('.js') || f.endsWith('.ts')) && !f.endsWith('.d.ts')
197
+ );
182
198
 
183
- // Bundle each action file
184
- for (const file of files) {
185
- const actionName = path.basename(file, path.extname(file));
186
- const entryPoint = path.join(actionsDir, file);
187
- const outfile = path.join(bundleDir, actionName + ".jsbundle");
199
+ for (const file of files) {
200
+ const actionName = path.basename(file, path.extname(file));
201
+ const entryPoint = path.join(actionsDir, file);
202
+ const outfile = path.join(bundleDir, actionName + ".jsbundle");
188
203
 
189
- try {
190
- await bundleFile({
191
- entryPoint,
192
- outfile,
193
- format: 'iife',
194
- globalName: '__titan_exports',
195
- platform: 'neutral',
196
- target: 'es2020',
197
- minify: false,
198
- sourcemap: false,
199
- banner: {
200
- js: "var Titan = t;"
201
- },
202
- footer: {
203
- js: `
204
+ try {
205
+ await bundleFile({
206
+ entryPoint,
207
+ outfile,
208
+ format: 'iife',
209
+ globalName: '__titan_exports',
210
+ platform: 'node',
211
+ target: 'es2020',
212
+ banner: { js: "var Titan = t;" },
213
+ footer: {
214
+ js: `
204
215
  (function () {
205
216
  const fn =
206
217
  __titan_exports["${actionName}"] ||
@@ -213,52 +224,36 @@ export async function bundle() {
213
224
  globalThis["${actionName}"] = globalThis.defineAction(fn);
214
225
  })();
215
226
  `
216
- }
217
- });
218
- } catch (error) {
219
- // RULE: Handle esbuild errors HERE and print error boxes
220
- if (error.isBundleError && error.errors && error.errors.length > 0) {
221
- // Print error box for each esbuild error
222
- console.error(); // Empty line for spacing
223
-
224
- const titanVersion = getTitanVersion();
225
-
226
- for (let i = 0; i < error.errors.length; i++) {
227
- const esbuildError = error.errors[i];
228
- const errorInfo = parseEsbuildError(esbuildError);
229
-
230
- // Add error number to title if multiple errors
231
- if (error.errors.length > 1) {
232
- errorInfo.title = `Build Error ${i + 1}/${error.errors.length}`;
233
- }
234
-
235
- // Add Titan version
236
- errorInfo.titanVersion = titanVersion;
237
-
238
- // Print the error box
239
- console.error(renderErrorBox(errorInfo));
240
-
241
- if (i < error.errors.length - 1) {
242
- console.error(); // Empty line between errors
243
- }
244
- }
245
-
246
- console.error(); // Empty line after all errors
247
- } else {
248
- // Other errors
249
- console.error();
250
- const errorInfo = {
251
- title: 'Build Error',
252
- file: entryPoint,
253
- message: error.message || 'Unknown error',
254
- titanVersion: getTitanVersion()
255
- };
256
- console.error(renderErrorBox(errorInfo));
257
- console.error();
258
- }
259
-
260
- // RULE: Throw special error to signal bundle failure
261
- throw new Error('__TITAN_BUNDLE_FAILED__');
262
227
  }
228
+ });
229
+
230
+ } catch (error) {
231
+
232
+ console.error();
233
+
234
+ const titanVersion = getTitanVersion();
235
+
236
+ if (error.isBundleError && error.errors?.length) {
237
+ for (let i = 0; i < error.errors.length; i++) {
238
+ const errorInfo = parseEsbuildError(error.errors[i]);
239
+ if (error.errors.length > 1) {
240
+ errorInfo.title = `Build Error ${i + 1}/${error.errors.length}`;
241
+ }
242
+ errorInfo.titanVersion = titanVersion;
243
+ console.error(renderErrorBox(errorInfo));
244
+ console.error();
245
+ }
246
+ } else {
247
+ const errorInfo = {
248
+ title: 'Build Error',
249
+ file: entryPoint,
250
+ message: error.message || 'Unknown error',
251
+ titanVersion
252
+ };
253
+ console.error(renderErrorBox(errorInfo));
254
+ }
255
+
256
+ throw new Error('__TITAN_BUNDLE_FAILED__');
263
257
  }
264
- }
258
+ }
259
+ }
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@titanpl/core": "latest",
11
+ "@titanpl/node": "latest",
11
12
  "chokidar": "^5.0.0",
12
13
  "esbuild": "^0.27.2",
13
14
  "typescript": "^5.0.0"