titanpl 2.0.1 → 2.0.3

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.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/packages/cli/README.md +17 -17
  4. package/packages/cli/index.js +160 -160
  5. package/packages/cli/package.json +5 -5
  6. package/packages/cli/src/commands/build.js +5 -5
  7. package/packages/cli/src/commands/dev.js +12 -12
  8. package/packages/cli/src/commands/init.js +196 -174
  9. package/packages/cli/src/commands/migrate.js +124 -124
  10. package/packages/cli/src/commands/start.js +4 -4
  11. package/packages/cli/src/commands/update.js +90 -90
  12. package/packages/cli/src/engine.js +169 -152
  13. package/packages/engine-darwin-arm64/README.md +13 -13
  14. package/packages/engine-darwin-arm64/package.json +1 -1
  15. package/packages/engine-linux-x64/README.md +11 -11
  16. package/packages/engine-linux-x64/package.json +1 -1
  17. package/packages/engine-win32-x64/README.md +11 -11
  18. package/packages/engine-win32-x64/package.json +1 -1
  19. package/packages/native/README.md +11 -11
  20. package/packages/native/index.d.ts +41 -41
  21. package/packages/native/index.js +39 -39
  22. package/packages/native/package.json +1 -1
  23. package/packages/packet/README.md +11 -11
  24. package/packages/packet/index.js +83 -83
  25. package/packages/packet/js/titan/builder.js +50 -50
  26. package/packages/packet/js/titan/bundle.js +164 -164
  27. package/packages/packet/js/titan/dev.js +53 -19
  28. package/packages/packet/js/titan/error-box.js +277 -277
  29. package/packages/packet/package.json +1 -1
  30. package/packages/packet/ts/titan/bundle.js +227 -227
  31. package/packages/packet/ts/titan/dev.js +53 -20
  32. package/packages/packet/ts/titan/error-box.js +277 -277
  33. package/packages/route/README.md +24 -24
  34. package/packages/route/index.d.ts +16 -16
  35. package/packages/route/index.js +52 -52
  36. package/packages/route/package.json +1 -1
  37. package/templates/common/_gitignore +1 -1
  38. package/templates/extension/package.json +2 -2
  39. package/templates/js/package.json +5 -5
  40. package/templates/rust-js/package.json +4 -4
  41. package/templates/rust-ts/package.json +4 -4
  42. package/templates/ts/package.json +5 -5
  43. package/titanpl-sdk/package.json +1 -1
@@ -1,164 +1,164 @@
1
- /**
2
- * Bundle.js (JavaScript Version)
3
- * Handles esbuild bundling with error reporting
4
- */
5
-
6
- import esbuild from 'esbuild';
7
- import path from 'path';
8
- import fs from 'fs';
9
- import { fileURLToPath } from 'url';
10
- import { createRequire } from 'module';
11
- import { renderErrorBox, parseEsbuildError } from './error-box.js';
12
-
13
- const __filename = fileURLToPath(import.meta.url);
14
- const __dirname = path.dirname(__filename);
15
-
16
- // Required for resolving node_modules inside ESM
17
- const require = createRequire(import.meta.url);
18
-
19
- /**
20
- * Titan Node Builtin Rewrite Map
21
- */
22
- const NODE_BUILTIN_MAP = {
23
- "fs": "@titanpl/node/fs",
24
- "node:fs": "@titanpl/node/fs",
25
- "path": "@titanpl/node/path",
26
- "node:path": "@titanpl/node/path",
27
- "os": "@titanpl/node/os",
28
- "node:os": "@titanpl/node/os",
29
- "crypto": "@titanpl/node/crypto",
30
- "node:crypto": "@titanpl/node/crypto",
31
- "process": "@titanpl/node/process",
32
- "util": "@titanpl/node/util",
33
- "node:util": "@titanpl/node/util",
34
- };
35
-
36
- const titanNodeCompatPlugin = {
37
- name: "titan-node-compat",
38
- setup(build) {
39
- build.onResolve({ filter: /.*/ }, args => {
40
- if (NODE_BUILTIN_MAP[args.path]) {
41
- try {
42
- const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
43
- return { path: resolved };
44
- } catch (e) {
45
- throw new Error(`[TitanPL] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`);
46
- }
47
- }
48
- });
49
- }
50
- };
51
-
52
- function getTitanVersion() {
53
- try {
54
- const pkgPath = require.resolve("@titanpl/cli/package.json");
55
- return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
56
- } catch (e) {
57
- return "1.0.0";
58
- }
59
- }
60
-
61
- export class BundleError extends Error {
62
- constructor(message, errors = [], warnings = []) {
63
- super(message);
64
- this.name = 'BundleError';
65
- this.errors = errors;
66
- this.warnings = warnings;
67
- this.isBundleError = true;
68
- }
69
- }
70
-
71
- async function validateEntryPoint(entryPoint) {
72
- const absPath = path.resolve(entryPoint);
73
- if (!fs.existsSync(absPath)) {
74
- throw new BundleError(`Entry point does not exist: ${entryPoint}`, [{ text: `Cannot find file: ${absPath}`, location: { file: entryPoint } }]);
75
- }
76
- }
77
-
78
- /**
79
- * Bundles a single file
80
- */
81
- export async function bundleFile(options) {
82
- const { entryPoint, outfile, format = 'iife', globalName = '__titan_exports', target = 'es2020' } = options;
83
-
84
- await validateEntryPoint(entryPoint);
85
-
86
- const outDir = path.dirname(outfile);
87
- if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
88
-
89
- try {
90
- const result = await esbuild.build({
91
- entryPoints: [entryPoint],
92
- bundle: true,
93
- outfile,
94
- format,
95
- globalName,
96
- platform: 'node',
97
- target,
98
- logLevel: 'silent',
99
- plugins: [titanNodeCompatPlugin],
100
- banner: { js: "var Titan = t;" },
101
- footer: options.footer || {}
102
- });
103
-
104
- if (result.errors?.length) {
105
- throw new BundleError(`Build failed`, result.errors);
106
- }
107
- } catch (err) {
108
- if (err.errors) throw new BundleError(`Build failed`, err.errors);
109
- throw err;
110
- }
111
- }
112
-
113
- /**
114
- * Main JS Bundler
115
- */
116
- export async function bundle(options = {}) {
117
- const root = options.root || process.cwd();
118
- const outDir = options.outDir || path.join(root, 'dist');
119
- const titanVersion = getTitanVersion();
120
-
121
- const actionsDir = path.join(root, 'app', 'actions');
122
- const bundleDir = path.join(outDir, 'actions');
123
-
124
- if (fs.existsSync(bundleDir)) fs.rmSync(bundleDir, { recursive: true, force: true });
125
- fs.mkdirSync(bundleDir, { recursive: true });
126
-
127
- if (!fs.existsSync(actionsDir)) return;
128
-
129
- const files = fs.readdirSync(actionsDir).filter(f => f.endsWith('.js'));
130
-
131
- for (const file of files) {
132
- const actionName = path.basename(file, path.extname(file));
133
- const entryPoint = path.join(actionsDir, file);
134
- const outfile = path.join(bundleDir, actionName + ".jsbundle");
135
-
136
- try {
137
- await bundleFile({
138
- entryPoint,
139
- outfile,
140
- footer: {
141
- js: `
142
- (function () {
143
- const fn = __titan_exports["${actionName}"] || __titan_exports.default;
144
- if (typeof fn !== "function") throw new Error("[TitanPL] Action '${actionName}' not found or not a function");
145
- globalThis["${actionName}"] = globalThis.defineAction(fn);
146
- })();`
147
- }
148
- });
149
- } catch (error) {
150
- console.error();
151
- if (error.isBundleError && error.errors?.length) {
152
- for (const err of error.errors) {
153
- const errorInfo = parseEsbuildError(err);
154
- errorInfo.titanVersion = titanVersion;
155
- console.error(renderErrorBox(errorInfo));
156
- console.error();
157
- }
158
- } else {
159
- console.error(renderErrorBox({ title: 'Build Error', message: error.message, titanVersion }));
160
- }
161
- throw new Error('__TITAN_BUNDLE_FAILED__');
162
- }
163
- }
164
- }
1
+ /**
2
+ * Bundle.js (JavaScript Version)
3
+ * Handles esbuild bundling with error reporting
4
+ */
5
+
6
+ import esbuild from 'esbuild';
7
+ import path from 'path';
8
+ import fs from 'fs';
9
+ import { fileURLToPath } from 'url';
10
+ import { createRequire } from 'module';
11
+ import { renderErrorBox, parseEsbuildError } from './error-box.js';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ // Required for resolving node_modules inside ESM
17
+ const require = createRequire(import.meta.url);
18
+
19
+ /**
20
+ * Titan Node Builtin Rewrite Map
21
+ */
22
+ const NODE_BUILTIN_MAP = {
23
+ "fs": "@titanpl/node/fs",
24
+ "node:fs": "@titanpl/node/fs",
25
+ "path": "@titanpl/node/path",
26
+ "node:path": "@titanpl/node/path",
27
+ "os": "@titanpl/node/os",
28
+ "node:os": "@titanpl/node/os",
29
+ "crypto": "@titanpl/node/crypto",
30
+ "node:crypto": "@titanpl/node/crypto",
31
+ "process": "@titanpl/node/process",
32
+ "util": "@titanpl/node/util",
33
+ "node:util": "@titanpl/node/util",
34
+ };
35
+
36
+ const titanNodeCompatPlugin = {
37
+ name: "titan-node-compat",
38
+ setup(build) {
39
+ build.onResolve({ filter: /.*/ }, args => {
40
+ if (NODE_BUILTIN_MAP[args.path]) {
41
+ try {
42
+ const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
43
+ return { path: resolved };
44
+ } catch (e) {
45
+ throw new Error(`[TitanPL] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`);
46
+ }
47
+ }
48
+ });
49
+ }
50
+ };
51
+
52
+ function getTitanVersion() {
53
+ try {
54
+ const pkgPath = require.resolve("@titanpl/cli/package.json");
55
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
56
+ } catch (e) {
57
+ return "1.0.0";
58
+ }
59
+ }
60
+
61
+ export class BundleError extends Error {
62
+ constructor(message, errors = [], warnings = []) {
63
+ super(message);
64
+ this.name = 'BundleError';
65
+ this.errors = errors;
66
+ this.warnings = warnings;
67
+ this.isBundleError = true;
68
+ }
69
+ }
70
+
71
+ async function validateEntryPoint(entryPoint) {
72
+ const absPath = path.resolve(entryPoint);
73
+ if (!fs.existsSync(absPath)) {
74
+ throw new BundleError(`Entry point does not exist: ${entryPoint}`, [{ text: `Cannot find file: ${absPath}`, location: { file: entryPoint } }]);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Bundles a single file
80
+ */
81
+ export async function bundleFile(options) {
82
+ const { entryPoint, outfile, format = 'iife', globalName = '__titan_exports', target = 'es2020' } = options;
83
+
84
+ await validateEntryPoint(entryPoint);
85
+
86
+ const outDir = path.dirname(outfile);
87
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
88
+
89
+ try {
90
+ const result = await esbuild.build({
91
+ entryPoints: [entryPoint],
92
+ bundle: true,
93
+ outfile,
94
+ format,
95
+ globalName,
96
+ platform: 'node',
97
+ target,
98
+ logLevel: 'silent',
99
+ plugins: [titanNodeCompatPlugin],
100
+ banner: { js: "var Titan = t;" },
101
+ footer: options.footer || {}
102
+ });
103
+
104
+ if (result.errors?.length) {
105
+ throw new BundleError(`Build failed`, result.errors);
106
+ }
107
+ } catch (err) {
108
+ if (err.errors) throw new BundleError(`Build failed`, err.errors);
109
+ throw err;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Main JS Bundler
115
+ */
116
+ export async function bundle(options = {}) {
117
+ const root = options.root || process.cwd();
118
+ const outDir = options.outDir || path.join(root, 'dist');
119
+ const titanVersion = getTitanVersion();
120
+
121
+ const actionsDir = path.join(root, 'app', 'actions');
122
+ const bundleDir = path.join(outDir, 'actions');
123
+
124
+ if (fs.existsSync(bundleDir)) fs.rmSync(bundleDir, { recursive: true, force: true });
125
+ fs.mkdirSync(bundleDir, { recursive: true });
126
+
127
+ if (!fs.existsSync(actionsDir)) return;
128
+
129
+ const files = fs.readdirSync(actionsDir).filter(f => f.endsWith('.js'));
130
+
131
+ for (const file of files) {
132
+ const actionName = path.basename(file, path.extname(file));
133
+ const entryPoint = path.join(actionsDir, file);
134
+ const outfile = path.join(bundleDir, actionName + ".jsbundle");
135
+
136
+ try {
137
+ await bundleFile({
138
+ entryPoint,
139
+ outfile,
140
+ footer: {
141
+ js: `
142
+ (function () {
143
+ const fn = __titan_exports["${actionName}"] || __titan_exports.default;
144
+ if (typeof fn !== "function") throw new Error("[TitanPL] Action '${actionName}' not found or not a function");
145
+ globalThis["${actionName}"] = globalThis.defineAction(fn);
146
+ })();`
147
+ }
148
+ });
149
+ } catch (error) {
150
+ console.error();
151
+ if (error.isBundleError && error.errors?.length) {
152
+ for (const err of error.errors) {
153
+ const errorInfo = parseEsbuildError(err);
154
+ errorInfo.titanVersion = titanVersion;
155
+ console.error(renderErrorBox(errorInfo));
156
+ console.error();
157
+ }
158
+ } else {
159
+ console.error(renderErrorBox({ title: 'Build Error', message: error.message, titanVersion }));
160
+ }
161
+ throw new Error('__TITAN_BUNDLE_FAILED__');
162
+ }
163
+ }
164
+ }
@@ -38,33 +38,53 @@ function getEngineBinaryPath(root) {
38
38
  // 1. Monorepo search (dev environment)
39
39
  let current = root;
40
40
  for (let i = 0; i < 5; i++) {
41
- const potential = path.join(current, 'engine', 'target', 'release', binName);
42
- if (fs.existsSync(potential)) return potential;
41
+ const potentialRelease = path.join(current, 'engine', 'target', 'release', binName);
42
+ if (fs.existsSync(potentialRelease)) return potentialRelease;
43
+ const potentialDebug = path.join(current, 'engine', 'target', 'debug', binName);
44
+ if (fs.existsSync(potentialDebug)) return potentialDebug;
45
+
46
+ // Check sibling monorepo folder for test-apps
47
+ const siblingRelease = path.join(current, 'titanpl', 'engine', 'target', 'release', binName);
48
+ if (fs.existsSync(siblingRelease)) return siblingRelease;
49
+ const siblingDebug = path.join(current, 'titanpl', 'engine', 'target', 'debug', binName);
50
+ if (fs.existsSync(siblingDebug)) return siblingDebug;
51
+ const siblingPkg = path.join(current, 'titanpl', 'packages', pkgName.replace('@titanpl/', ''), 'bin', binName);
52
+ if (fs.existsSync(siblingPkg)) return siblingPkg;
53
+
43
54
  current = path.dirname(current);
44
55
  }
45
56
 
46
57
  // 2. Search relative to @titanpl/cli (where optionalDependencies are installed)
47
- // This is the primary path for globally-installed CLI users.
48
58
  try {
49
- const require = createRequire(import.meta.url);
50
- const cliPkgPath = require.resolve('@titanpl/cli/package.json');
59
+ const req = createRequire(import.meta.url);
60
+ const cliPkgPath = req.resolve('@titanpl/cli/package.json');
51
61
  const cliDir = path.dirname(cliPkgPath);
52
- // Check cli's own node_modules (global install sibling)
53
62
  const cliNodeModulesBin = path.join(cliDir, 'node_modules', pkgName, 'bin', binName);
54
63
  if (fs.existsSync(cliNodeModulesBin)) return cliNodeModulesBin;
55
- // Check parent node_modules (hoisted global install)
56
- const parentNodeModulesBin = path.join(path.dirname(cliDir), pkgName, 'bin', binName);
64
+
65
+ const nodeModulesDir = path.dirname(path.dirname(cliDir));
66
+ const parentNodeModulesBin = path.join(nodeModulesDir, pkgName, 'bin', binName);
57
67
  if (fs.existsSync(parentNodeModulesBin)) return parentNodeModulesBin;
58
68
  } catch (e) { }
59
69
 
60
- // 3. Search in the project's own node_modules
70
+ // 3. Search in the project's own node_modules directly
61
71
  try {
62
- const require = createRequire(import.meta.url);
63
- const pkgPath = require.resolve(`${pkgName}/package.json`);
72
+ const req = createRequire(import.meta.url);
73
+ const pkgPath = req.resolve(`${pkgName}/package.json`);
64
74
  const binPath = path.join(path.dirname(pkgPath), 'bin', binName);
65
75
  if (fs.existsSync(binPath)) return binPath;
66
76
  } catch (e) { }
67
77
 
78
+ // Walk upwards from current dir searching for node_modules/@titanpl/engine-...
79
+ let searchDir = process.cwd();
80
+ for (let i = 0; i < 5; i++) {
81
+ const nmBin = path.join(searchDir, 'node_modules', pkgName, 'bin', binName);
82
+ if (fs.existsSync(nmBin)) return nmBin;
83
+ const parent = path.dirname(searchDir);
84
+ if (parent === searchDir) break;
85
+ searchDir = parent;
86
+ }
87
+
68
88
  // 4. Fallback: check common global npm paths
69
89
  const globalSearchRoots = [
70
90
  process.env.npm_config_prefix,
@@ -76,8 +96,16 @@ function getEngineBinaryPath(root) {
76
96
  for (const gRoot of globalSearchRoots) {
77
97
  const gBin = path.join(gRoot, 'node_modules', pkgName, 'bin', binName);
78
98
  if (fs.existsSync(gBin)) return gBin;
99
+ const libNodeModulesBin = path.join(gRoot, 'lib', 'node_modules', pkgName, 'bin', binName);
100
+ if (fs.existsSync(libNodeModulesBin)) return libNodeModulesBin;
79
101
  }
80
102
 
103
+ try {
104
+ const globalModules = execSync('npm root -g').toString().trim();
105
+ const globalBin = path.join(globalModules, pkgName, 'bin', binName);
106
+ if (fs.existsSync(globalBin)) return globalBin;
107
+ } catch (e) { }
108
+
81
109
  return null;
82
110
  }
83
111
 
@@ -87,22 +115,28 @@ async function killServer() {
87
115
  if (!serverProcess) return;
88
116
 
89
117
  return new Promise((resolve) => {
90
- const onExit = () => {
118
+ if (serverProcess.killed || serverProcess.exitCode !== null) {
91
119
  serverProcess = null;
92
120
  resolve();
121
+ return;
122
+ }
123
+
124
+ let isDone = false;
125
+ const onExit = () => {
126
+ if (isDone) return;
127
+ isDone = true;
128
+ serverProcess = null;
129
+ setTimeout(resolve, 300); // Grace period for OS socket release
93
130
  };
94
131
 
95
132
  serverProcess.on('exit', onExit);
133
+ serverProcess.on('error', onExit);
96
134
 
97
- if (os.platform() === 'win32') {
98
- try {
99
- execSync(`taskkill /pid ${serverProcess.pid} /f /t`, { stdio: 'ignore' });
100
- } catch (e) { }
101
- } else {
135
+ try {
102
136
  serverProcess.kill('SIGKILL');
103
- }
137
+ } catch (e) { }
104
138
 
105
- setTimeout(onExit, 500);
139
+ setTimeout(onExit, 800); // Fallback
106
140
  });
107
141
  }
108
142