@titanpl/packet 1.0.1 → 1.5.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 ADDED
@@ -0,0 +1,11 @@
1
+ # @titanpl/packet
2
+
3
+ The internal bundler and module packer for Titan Planet.
4
+
5
+ ## What it works (What it does)
6
+ This library parses all your app routes, components, middleware, and logic paths, then packages them into a static `build` output layer consisting of metadata files and JavaScript assets.
7
+
8
+ ## How it works
9
+ This package is triggered whenever you run `titan init` or `titan build` from the `@titanpl/cli`. It hooks into tools like `esbuild` to optimize your scripts efficiently. It strips unnecessary pieces out so that only explicit route actions make their way aggressively over to the Titan runtime engine.
10
+
11
+ **Important Note:** Currently, Titan Planet and its entire package ecosystem are only for Windows. The Linux version is in development (dev only) for the new architecture and will be launched later.
package/index.js CHANGED
@@ -1,9 +1,18 @@
1
1
  import path from "path";
2
2
  import fs from "fs";
3
3
 
4
- import { bundle as bundleActions } from "./js/titan/bundle.js";
5
- import { dev as devServer } from "./js/titan/dev.js";
6
- import { buildMetadata } from "./js/titan/builder.js";
4
+ /**
5
+ * Get project type (js or ts)
6
+ */
7
+ function getProjectType(root) {
8
+ try {
9
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
10
+ if (pkg.titan && pkg.titan.template && pkg.titan.template.includes("ts")) return "ts";
11
+ } catch (e) { }
12
+
13
+ if (fs.existsSync(path.join(root, "tsconfig.json"))) return "ts";
14
+ return "js";
15
+ }
7
16
 
8
17
  /**
9
18
  * Ensure dist directory exists
@@ -27,6 +36,10 @@ function ensureDist(root) {
27
36
  * Production build
28
37
  */
29
38
  export async function build(root = process.cwd()) {
39
+ const type = getProjectType(root);
40
+ const { bundle: bundleActions } = await import(`./${type}/titan/bundle.js`);
41
+ const { buildMetadata } = await import(`./${type}/titan/builder.js`);
42
+
30
43
  const dist = ensureDist(root);
31
44
 
32
45
  await buildMetadata(root, dist);
@@ -39,10 +52,13 @@ export async function build(root = process.cwd()) {
39
52
  }
40
53
 
41
54
  /**
42
- * Dev mode build (watch mode handled inside dev.js)
55
+ * Dev mode build
43
56
  */
44
57
  export async function dev(options = {}) {
45
58
  const root = typeof options === 'string' ? options : (options.root || process.cwd());
59
+ const type = getProjectType(root);
60
+ const { dev: devServer } = await import(`./${type}/titan/dev.js`);
61
+
46
62
  const dist = ensureDist(root);
47
63
 
48
64
  await devServer({
@@ -55,9 +71,14 @@ export async function dev(options = {}) {
55
71
  }
56
72
 
57
73
  /**
58
- * Direct export of lower-level tools (optional)
74
+ * Direct export of current project tools
59
75
  */
60
- export {
61
- bundleActions,
62
- buildMetadata
63
- };
76
+ export async function getTools(root = process.cwd()) {
77
+ const type = getProjectType(root);
78
+ const bundleModule = await import(`./${type}/titan/bundle.js`);
79
+ const builderModule = await import(`./${type}/titan/builder.js`);
80
+ return {
81
+ bundleActions: bundleModule.bundle,
82
+ buildMetadata: builderModule.buildMetadata
83
+ };
84
+ }
@@ -3,40 +3,17 @@ import path from "path";
3
3
  import { pathToFileURL } from "url";
4
4
 
5
5
  export async function buildMetadata(root, dist) {
6
- const appJsPath = path.join(root, "app", "app.js");
7
- const appTsPath = path.join(root, "app", "app.ts");
6
+ const appDir = path.join(root, "app");
7
+ const appFile = "app.js";
8
+ const appPath = path.join(appDir, appFile);
8
9
 
9
- if (!fs.existsSync(appJsPath) && !fs.existsSync(appTsPath)) {
10
- console.error("❌ app/app.js or app/app.ts not found.");
10
+ if (!fs.existsSync(appPath)) {
11
+ console.error(`\x1b[31m❌ app/${appFile} not found.\x1b[0m`);
11
12
  process.exit(1);
12
13
  }
13
14
 
14
- // Load the app file to populate globalThis.__TITAN_ROUTES_MAP__
15
15
  try {
16
- let appUrl;
17
- if (fs.existsSync(appTsPath)) {
18
- const dotTitan = path.join(root, ".titan");
19
- const compiledApp = path.join(dotTitan, "app.js");
20
-
21
- if (!fs.existsSync(dotTitan)) {
22
- fs.mkdirSync(dotTitan, { recursive: true });
23
- }
24
-
25
- const esbuild = await import("esbuild");
26
- await esbuild.build({
27
- entryPoints: [appTsPath],
28
- outfile: compiledApp,
29
- bundle: true,
30
- platform: "node",
31
- format: "esm",
32
- packages: "external",
33
- logLevel: "silent"
34
- });
35
- appUrl = pathToFileURL(compiledApp).href;
36
- } else {
37
- appUrl = pathToFileURL(appJsPath).href;
38
- }
39
-
16
+ const appUrl = pathToFileURL(appPath).href;
40
17
  const cacheBuster = `?t=${Date.now()}`;
41
18
  await import(appUrl + cacheBuster);
42
19
 
@@ -67,7 +44,7 @@ export async function buildMetadata(root, dist) {
67
44
  );
68
45
 
69
46
  } catch (err) {
70
- console.error("❌ Failed to parse routes from app.js", err);
47
+ console.error("\x1b[31m❌ Failed to parse routes from app.js\x1b[0m", err);
71
48
  process.exit(1);
72
49
  }
73
50
  }
@@ -1,7 +1,6 @@
1
1
  /**
2
- * Bundle.js
3
- * Handles esbuild bundling with comprehensive error reporting
4
- * RULE: This file handles ALL esbuild errors and prints error boxes directly
2
+ * Bundle.js (JavaScript Version)
3
+ * Handles esbuild bundling with error reporting
5
4
  */
6
5
 
7
6
  import esbuild from 'esbuild';
@@ -19,32 +18,21 @@ const require = createRequire(import.meta.url);
19
18
 
20
19
  /**
21
20
  * Titan Node Builtin Rewrite Map
22
- * Rewrites Node builtins to @titanpl/node shims
23
21
  */
24
22
  const NODE_BUILTIN_MAP = {
25
23
  "fs": "@titanpl/node/fs",
26
24
  "node:fs": "@titanpl/node/fs",
27
-
28
25
  "path": "@titanpl/node/path",
29
26
  "node:path": "@titanpl/node/path",
30
-
31
27
  "os": "@titanpl/node/os",
32
28
  "node:os": "@titanpl/node/os",
33
-
34
29
  "crypto": "@titanpl/node/crypto",
35
30
  "node:crypto": "@titanpl/node/crypto",
36
-
37
31
  "process": "@titanpl/node/process",
38
-
39
32
  "util": "@titanpl/node/util",
40
33
  "node:util": "@titanpl/node/util",
41
34
  };
42
35
 
43
- /**
44
- * Titan Node Compatibility Plugin
45
- * Rewrites require/import of Node builtins
46
- * Returns absolute paths (required by esbuild)
47
- */
48
36
  const titanNodeCompatPlugin = {
49
37
  name: "titan-node-compat",
50
38
  setup(build) {
@@ -54,30 +42,22 @@ const titanNodeCompatPlugin = {
54
42
  const resolved = require.resolve(NODE_BUILTIN_MAP[args.path]);
55
43
  return { path: resolved };
56
44
  } catch (e) {
57
- throw new Error(
58
- `[Titan] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`
59
- );
45
+ throw new Error(`[TitanPL] Failed to resolve Node shim: ${NODE_BUILTIN_MAP[args.path]}`);
60
46
  }
61
47
  }
62
48
  });
63
49
  }
64
50
  };
65
51
 
66
- /**
67
- * Get Titan version for error branding
68
- */
69
52
  function getTitanVersion() {
70
53
  try {
71
- const pkgPath = require.resolve("@ezetgalaxy/titan/package.json");
54
+ const pkgPath = require.resolve("@titanpl/cli/package.json");
72
55
  return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
73
56
  } catch (e) {
74
- return "0.1.0";
57
+ return "1.0.0";
75
58
  }
76
59
  }
77
60
 
78
- /**
79
- * Custom error class for bundle errors
80
- */
81
61
  export class BundleError extends Error {
82
62
  constructor(message, errors = [], warnings = []) {
83
63
  super(message);
@@ -88,26 +68,10 @@ export class BundleError extends Error {
88
68
  }
89
69
  }
90
70
 
91
- /**
92
- * Validate entry file exists
93
- */
94
71
  async function validateEntryPoint(entryPoint) {
95
72
  const absPath = path.resolve(entryPoint);
96
-
97
73
  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
- );
74
+ throw new BundleError(`Entry point does not exist: ${entryPoint}`, [{ text: `Cannot find file: ${absPath}`, location: { file: entryPoint } }]);
111
75
  }
112
76
  }
113
77
 
@@ -115,23 +79,12 @@ async function validateEntryPoint(entryPoint) {
115
79
  * Bundles a single file
116
80
  */
117
81
  export async function bundleFile(options) {
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;
82
+ const { entryPoint, outfile, format = 'iife', globalName = '__titan_exports', target = 'es2020' } = options;
130
83
 
131
84
  await validateEntryPoint(entryPoint);
132
85
 
133
86
  const outDir = path.dirname(outfile);
134
- await fs.promises.mkdir(outDir, { recursive: true });
87
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
135
88
 
136
89
  try {
137
90
  const result = await esbuild.build({
@@ -140,63 +93,40 @@ export async function bundleFile(options) {
140
93
  outfile,
141
94
  format,
142
95
  globalName,
143
- platform,
96
+ platform: 'node',
144
97
  target,
145
- banner,
146
- footer,
147
- minify,
148
- sourcemap,
149
98
  logLevel: 'silent',
150
- logLimit: 0,
151
- write: true,
152
- metafile: false,
153
99
  plugins: [titanNodeCompatPlugin],
100
+ banner: { js: "var Titan = t;" },
101
+ footer: options.footer || {}
154
102
  });
155
103
 
156
104
  if (result.errors?.length) {
157
- throw new BundleError(
158
- `Build failed with ${result.errors.length} error(s)`,
159
- result.errors,
160
- result.warnings || []
161
- );
105
+ throw new BundleError(`Build failed`, result.errors);
162
106
  }
163
-
164
107
  } 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
- );
171
- }
172
-
173
- throw new BundleError(
174
- `Unexpected build error: ${err.message}`,
175
- [{ text: err.message, location: { file: entryPoint } }]
176
- );
108
+ if (err.errors) throw new BundleError(`Build failed`, err.errors);
109
+ throw err;
177
110
  }
178
111
  }
179
112
 
180
113
  /**
181
- * Main bundler
114
+ * Main JS Bundler
182
115
  */
183
116
  export async function bundle(options = {}) {
184
117
  const root = options.root || process.cwd();
185
118
  const outDir = options.outDir || path.join(root, 'dist');
119
+ const titanVersion = getTitanVersion();
186
120
 
187
121
  const actionsDir = path.join(root, 'app', 'actions');
188
122
  const bundleDir = path.join(outDir, 'actions');
189
123
 
190
- if (fs.existsSync(bundleDir)) {
191
- fs.rmSync(bundleDir, { recursive: true, force: true });
192
- }
193
- await fs.promises.mkdir(bundleDir, { recursive: true });
124
+ if (fs.existsSync(bundleDir)) fs.rmSync(bundleDir, { recursive: true, force: true });
125
+ fs.mkdirSync(bundleDir, { recursive: true });
194
126
 
195
127
  if (!fs.existsSync(actionsDir)) return;
196
128
 
197
- const files = fs.readdirSync(actionsDir).filter(f =>
198
- (f.endsWith('.js') || f.endsWith('.ts')) && !f.endsWith('.d.ts')
199
- );
129
+ const files = fs.readdirSync(actionsDir).filter(f => f.endsWith('.js'));
200
130
 
201
131
  for (const file of files) {
202
132
  const actionName = path.basename(file, path.extname(file));
@@ -207,54 +137,27 @@ export async function bundle(options = {}) {
207
137
  await bundleFile({
208
138
  entryPoint,
209
139
  outfile,
210
- format: 'iife',
211
- globalName: '__titan_exports',
212
- platform: 'node',
213
- target: 'es2020',
214
- banner: { js: "var Titan = t;" },
215
140
  footer: {
216
141
  js: `
217
142
  (function () {
218
- const fn =
219
- __titan_exports["${actionName}"] ||
220
- __titan_exports.default;
221
-
222
- if (typeof fn !== "function") {
223
- throw new Error("[Titan] Action '${actionName}' not found or not a function");
224
- }
225
-
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");
226
145
  globalThis["${actionName}"] = globalThis.defineAction(fn);
227
- })();
228
- `
146
+ })();`
229
147
  }
230
148
  });
231
-
232
149
  } catch (error) {
233
-
234
150
  console.error();
235
-
236
- const titanVersion = getTitanVersion();
237
-
238
151
  if (error.isBundleError && error.errors?.length) {
239
- for (let i = 0; i < error.errors.length; i++) {
240
- const errorInfo = parseEsbuildError(error.errors[i]);
241
- if (error.errors.length > 1) {
242
- errorInfo.title = `Build Error ${i + 1}/${error.errors.length}`;
243
- }
152
+ for (const err of error.errors) {
153
+ const errorInfo = parseEsbuildError(err);
244
154
  errorInfo.titanVersion = titanVersion;
245
155
  console.error(renderErrorBox(errorInfo));
246
156
  console.error();
247
157
  }
248
158
  } else {
249
- const errorInfo = {
250
- title: 'Build Error',
251
- file: entryPoint,
252
- message: error.message || 'Unknown error',
253
- titanVersion
254
- };
255
- console.error(renderErrorBox(errorInfo));
159
+ console.error(renderErrorBox({ title: 'Build Error', message: error.message, titanVersion }));
256
160
  }
257
-
258
161
  throw new Error('__TITAN_BUNDLE_FAILED__');
259
162
  }
260
163
  }
package/js/titan/dev.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import chokidar from "chokidar";
2
+ import { spawn, execSync } from "child_process";
2
3
  import path from "path";
3
4
  import fs from "fs";
4
5
  import { bundle } from "./bundle.js";
5
6
  import { buildMetadata } from "./builder.js";
6
7
  import { createRequire } from "module";
8
+ import os from "os";
7
9
 
8
10
  const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
9
11
  const green = (t) => `\x1b[32m${t}\x1b[0m`;
@@ -22,31 +24,94 @@ function getTitanVersion() {
22
24
  }
23
25
  }
24
26
 
25
- export async function dev(options) {
26
- const root = options.root || process.cwd();
27
- const outDir = options.outDir || path.join(root, "dist");
28
- const onRebuild = options.onRebuild || (() => { });
27
+ function getEngineBinaryPath(root) {
28
+ const platform = os.platform();
29
+ const arch = os.arch();
30
+ const binName = platform === 'win32' ? 'titan-server.exe' : 'titan-server';
31
+ const pkgName = `@titanpl/engine-${platform}-${arch}`;
29
32
 
30
- const isTs = fs.existsSync(path.join(root, "tsconfig.json")) || fs.existsSync(path.join(root, "app", "app.ts"));
31
- const actionsDir = path.join(root, "app", "actions");
32
- let hasRust = false;
33
- if (fs.existsSync(actionsDir)) {
34
- hasRust = fs.readdirSync(actionsDir).some(f => f.endsWith(".rs"));
33
+ // Monorepo search
34
+ let current = root;
35
+ for (let i = 0; i < 5; i++) {
36
+ const potential = path.join(current, 'engine', 'target', 'release', binName);
37
+ if (fs.existsSync(potential)) return potential;
38
+ current = path.dirname(current);
35
39
  }
36
40
 
37
- let mode = "";
38
- if (hasRust) {
39
- mode = isTs ? "Rust + TS Actions" : "Rust + JS Actions";
40
- } else {
41
- mode = isTs ? "TS Actions" : "JS Actions";
41
+ // Node modules search
42
+ try {
43
+ const require = createRequire(import.meta.url);
44
+ const pkgPath = require.resolve(`${pkgName}/package.json`);
45
+ const binPath = path.join(path.dirname(pkgPath), 'bin', binName);
46
+ if (fs.existsSync(binPath)) return binPath;
47
+ } catch (e) { }
48
+
49
+ return null;
50
+ }
51
+
52
+ let serverProcess = null;
53
+
54
+ async function killServer() {
55
+ if (!serverProcess) return;
56
+
57
+ return new Promise((resolve) => {
58
+ const onExit = () => {
59
+ serverProcess = null;
60
+ resolve();
61
+ };
62
+
63
+ serverProcess.on('exit', onExit);
64
+
65
+ if (os.platform() === 'win32') {
66
+ try {
67
+ execSync(`taskkill /pid ${serverProcess.pid} /f /t`, { stdio: 'ignore' });
68
+ } catch (e) { }
69
+ } else {
70
+ serverProcess.kill('SIGKILL');
71
+ }
72
+
73
+ setTimeout(onExit, 500);
74
+ });
75
+ }
76
+
77
+ function startServer(root, outDir) {
78
+ const binaryPath = getEngineBinaryPath(root);
79
+ if (!binaryPath) {
80
+ console.error(red("[TitanPL] Error: Could not find engine binary. Ensure you have installed the correct engine package."));
81
+ return;
42
82
  }
83
+
84
+ const distPath = path.resolve(root, "dist");
85
+
86
+ serverProcess = spawn(binaryPath, ['run', distPath, '--watch'], {
87
+ stdio: 'inherit',
88
+ env: {
89
+ ...process.env,
90
+ TITAN_ENV: 'development',
91
+ Titan_Dev: '1'
92
+ }
93
+ });
94
+
95
+ serverProcess.on('error', (err) => {
96
+ if (err.code === 'ENOENT') {
97
+ console.error(red("[TitanPL] Failed to start engine: Binary not found."));
98
+ } else {
99
+ console.error(red(`[TitanPL] Engine error: ${err.message}`));
100
+ }
101
+ });
102
+ }
103
+
104
+ export async function dev(options) {
105
+ const root = options.root || process.cwd();
106
+ const outDir = options.outDir || path.join(root, "dist");
107
+
43
108
  const version = getTitanVersion();
44
109
 
45
110
  console.clear();
46
111
  console.log("");
47
112
  console.log(` ${bold(cyan("⏣ Titan Planet"))} ${gray("v" + version)} ${yellow("[ Dev Mode ]")}`);
48
113
  console.log("");
49
- console.log(` ${gray("Type: ")} ${mode}`);
114
+ console.log(` ${gray("Type: ")} JS Actions`);
50
115
  console.log(` ${gray("Hot Reload: ")} ${green("Enabled")}`);
51
116
 
52
117
  if (fs.existsSync(path.join(root, ".env"))) {
@@ -54,9 +119,25 @@ export async function dev(options) {
54
119
  }
55
120
  console.log("");
56
121
 
122
+ const runBuildCycle = async () => {
123
+ try {
124
+ await killServer();
125
+
126
+ await buildMetadata(root, outDir);
127
+ await bundle({ root, outDir });
128
+
129
+ console.log(cyan(`\n[TitanPL] Starting Engine...`));
130
+ startServer(root, outDir);
131
+ } catch (err) {
132
+ if (err.message !== '__TITAN_BUNDLE_FAILED__') {
133
+ console.error(red(`[TitanPL] Build failed: ${err.message}`));
134
+ }
135
+ console.log(yellow("\n[TitanPL] Waiting for fixes before restarting orbit..."));
136
+ }
137
+ };
138
+
57
139
  // Initial build
58
- await buildMetadata(root, outDir);
59
- await bundle({ root, outDir });
140
+ await runBuildCycle();
60
141
 
61
142
  // Watch for changes inside app/
62
143
  const appDir = path.join(root, "app");
@@ -64,21 +145,27 @@ export async function dev(options) {
64
145
 
65
146
  const watcher = chokidar.watch([appDir, envFile], {
66
147
  ignoreInitial: true,
67
- awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 }
148
+ awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
68
149
  });
69
150
 
151
+ let buildTimer = null;
70
152
  watcher.on("all", async (event, file) => {
71
153
  if (!file) return;
72
154
  const relPath = path.relative(root, file);
73
- if (relPath.startsWith("dist") || relPath.startsWith("server")) return;
155
+ if (relPath.startsWith("dist") || relPath.startsWith(".titan") || relPath.startsWith("server") || relPath.startsWith("node_modules")) return;
74
156
 
75
- try {
76
- await buildMetadata(root, outDir);
77
- await bundle({ root, outDir });
78
- onRebuild();
79
- } catch (err) {
80
- console.error(red(`[Titan] Build failed, awaiting changes...`));
81
- }
157
+ if (buildTimer) clearTimeout(buildTimer);
158
+ buildTimer = setTimeout(() => {
159
+ console.clear();
160
+ console.log(cyan(`[TitanPL] File changed: ${relPath}. Rebuilding...`));
161
+ runBuildCycle();
162
+ }, 300); // Debounce
163
+ });
164
+
165
+ // Cleanup on exit
166
+ process.on('SIGINT', async () => {
167
+ await killServer();
168
+ process.exit(0);
82
169
  });
83
170
 
84
171
  return watcher;
@@ -25,7 +25,7 @@ const red = (text) => `\x1b[31m${text}\x1b[0m`;
25
25
  function getTitanVersion() {
26
26
  try {
27
27
  const require = createRequire(import.meta.url);
28
- const pkgPath = require.resolve("@ezetgalaxy/titan/package.json");
28
+ const pkgPath = require.resolve("titanpl/package.json");
29
29
  return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
30
30
  } catch (e) {
31
31
  try {
@@ -34,7 +34,7 @@ function getTitanVersion() {
34
34
  const pkgPath = path.join(cur, "package.json");
35
35
  if (fs.existsSync(pkgPath)) {
36
36
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
37
- if (pkg.name === "@ezetgalaxy/titan") return pkg.version;
37
+ if (pkg.name === "titanpl") return pkg.version;
38
38
  }
39
39
  cur = path.join(cur, "..");
40
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@titanpl/packet",
3
- "version": "1.0.1",
3
+ "version": "1.5.0",
4
4
  "description": "The bundler for TitanPl servers.",
5
5
  "keywords": [
6
6
  "bundler",
@@ -14,5 +14,10 @@
14
14
  "main": "index.js",
15
15
  "scripts": {
16
16
  "test": "echo \"Error: no test specified\" && exit 1"
17
+ },
18
+ "dependencies": {
19
+ "esbuild": "^0.27.2",
20
+ "chokidar": "^5.0.0",
21
+ "typescript": "^5.0.0"
17
22
  }
18
- }
23
+ }