@nanoforge-dev/cli 1.3.0 → 1.4.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/dist/nf.js CHANGED
@@ -40,7 +40,7 @@ var require_package = __commonJS({
40
40
  module.exports = {
41
41
  $schema: "https://json.schemastore.org/package.json",
42
42
  name: "@nanoforge-dev/cli",
43
- version: "1.3.0",
43
+ version: "1.4.0",
44
44
  description: "NanoForge CLI",
45
45
  keywords: [
46
46
  "nanoforge",
@@ -149,6 +149,110 @@ var require_package = __commonJS({
149
149
  import { program } from "commander";
150
150
  import "reflect-metadata";
151
151
 
152
+ // src/lib/tree-kill/tree-kill.ts
153
+ import { execSync, spawn } from "child_process";
154
+ var treeKill = /* @__PURE__ */ __name((pid, signal) => {
155
+ function extracted(resolve4) {
156
+ const tree = {};
157
+ const pidsToProcess = {};
158
+ tree[pid] = [];
159
+ pidsToProcess[pid] = 1;
160
+ switch (process.platform) {
161
+ case "win32":
162
+ execSync("taskkill /pid " + pid + " /T /F");
163
+ resolve4();
164
+ break;
165
+ case "darwin":
166
+ buildProcessTree(
167
+ pid,
168
+ tree,
169
+ pidsToProcess,
170
+ function(parentPid) {
171
+ return spawn("pgrep", ["-P", parentPid]);
172
+ },
173
+ function() {
174
+ killAll(tree, signal);
175
+ resolve4();
176
+ }
177
+ );
178
+ break;
179
+ default:
180
+ buildProcessTree(
181
+ pid,
182
+ tree,
183
+ pidsToProcess,
184
+ function(parentPid) {
185
+ return spawn("ps", ["-o", "pid", "--no-headers", "--ppid", parentPid]);
186
+ },
187
+ function() {
188
+ killAll(tree, signal);
189
+ resolve4();
190
+ }
191
+ );
192
+ break;
193
+ }
194
+ }
195
+ __name(extracted, "extracted");
196
+ return new Promise((resolve4) => {
197
+ extracted(resolve4);
198
+ });
199
+ }, "treeKill");
200
+ function killAll(tree, signal) {
201
+ const killed = {};
202
+ Object.keys(tree).reverse().forEach(function(pidStr) {
203
+ const pid = Number(pidStr);
204
+ tree[pid]?.forEach(function(pidpid) {
205
+ if (!killed[pidpid]) {
206
+ killPid(pidpid, signal);
207
+ killed[pidpid] = true;
208
+ }
209
+ });
210
+ if (!killed[pid]) {
211
+ killPid(pid, signal);
212
+ killed[pid] = true;
213
+ }
214
+ });
215
+ }
216
+ __name(killAll, "killAll");
217
+ function killPid(pid, signal) {
218
+ try {
219
+ process.kill(pid, signal);
220
+ } catch (err) {
221
+ const e = err;
222
+ if (e.code !== "ESRCH") throw err;
223
+ }
224
+ }
225
+ __name(killPid, "killPid");
226
+ function buildProcessTree(parentPid, tree, pidsToProcess, spawnChildProcessesList, cb) {
227
+ const ps = spawnChildProcessesList(parentPid.toString());
228
+ let allData = "";
229
+ ps.stdout.on("data", function(data) {
230
+ allData += data.toString("ascii");
231
+ });
232
+ const onClose = /* @__PURE__ */ __name(function(code) {
233
+ delete pidsToProcess[parentPid];
234
+ if (code != 0) {
235
+ if (Object.keys(pidsToProcess).length == 0) {
236
+ cb();
237
+ }
238
+ return;
239
+ }
240
+ if (allData)
241
+ allData.match(/\d+/g)?.forEach(function(pid) {
242
+ const intPid = Number(pid);
243
+ if (tree[parentPid] === void 0) {
244
+ tree[parentPid] = [];
245
+ }
246
+ tree[parentPid].push(intPid);
247
+ tree[intPid] = [];
248
+ pidsToProcess[intPid] = 1;
249
+ buildProcessTree(intPid, tree, pidsToProcess, spawnChildProcessesList, cb);
250
+ });
251
+ }, "onClose");
252
+ ps.on("close", onClose);
253
+ }
254
+ __name(buildProcessTree, "buildProcessTree");
255
+
152
256
  // src/lib/utils/local-binaries.ts
153
257
  import { existsSync } from "fs";
154
258
  import { join, posix } from "path";
@@ -228,6 +332,11 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
228
332
  NEW_SERVER_QUESTION: "Do you want to generate a server for multiplayer?",
229
333
  NEW_SKIP_INSTALL_QUESTION: "Do you want to skip dependency installation?",
230
334
  NEW_DOCKER_QUESTION: "Do you want to add a Dockerfile for containerization?",
335
+ // --- Create ---
336
+ CREATE_START: "NanoForge Component/System Creation",
337
+ CREATE_SUCCESS: success("Element successfully created!"),
338
+ CREATE_FAILED: failure("Creation failed!"),
339
+ CREATE_NAME_QUESTION: "What is the name of your component/system?",
231
340
  // --- Generate ---
232
341
  GENERATE_START: "NanoForge Generate",
233
342
  GENERATE_SUCCESS: success("Generation succeeded!"),
@@ -284,7 +393,7 @@ var getSpinner = /* @__PURE__ */ __name((message) => ora({ text: message }), "ge
284
393
 
285
394
  // src/action/actions/build.action.ts
286
395
  import { watch } from "chokidar";
287
- import { dirname, join as join4 } from "path";
396
+ import { dirname, join as join5 } from "path";
288
397
 
289
398
  // src/lib/input/base-inputs.ts
290
399
  var getStringInput = /* @__PURE__ */ __name((input2, field) => {
@@ -333,21 +442,10 @@ var getConfigInput = /* @__PURE__ */ __name((inputs) => {
333
442
  return getStringInputWithDefault(inputs, "config", ".");
334
443
  }, "getConfigInput");
335
444
 
336
- // src/lib/input/inputs/watch.input.ts
337
- var getWatchInput = /* @__PURE__ */ __name((inputs) => {
338
- return getBooleanInputWithDefault(inputs, "watch", false);
339
- }, "getWatchInput");
340
-
341
- // src/lib/input/inputs/dev/generate.input.ts
342
- var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
343
- return getBooleanInputWithDefault(inputs, "generate", false);
344
- }, "getDevGenerateInput");
345
-
346
- // src/lib/input/inputs/install/lib.input.ts
347
- function getInstallLibInput(inputs) {
348
- return getBooleanInputWithDefault(inputs, "lib", false);
349
- }
350
- __name(getInstallLibInput, "getInstallLibInput");
445
+ // src/lib/input/inputs/editor.input.ts
446
+ var getEditorInput = /* @__PURE__ */ __name((inputs) => {
447
+ return getBooleanInputWithDefault(inputs, "editor", false);
448
+ }, "getEditorInput");
351
449
 
352
450
  // src/lib/question/questions/confirm.question.ts
353
451
  import { confirm } from "@inquirer/prompts";
@@ -437,6 +535,66 @@ var askSelect = /* @__PURE__ */ __name(async (question, choices, baseOptions) =>
437
535
  }).catch(promptError);
438
536
  }, "askSelect");
439
537
 
538
+ // src/lib/input/inputs/name.input.ts
539
+ var getNameInput = /* @__PURE__ */ __name((inputs) => {
540
+ return getStringInput(inputs, "name");
541
+ }, "getNameInput");
542
+ var getNewNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
543
+ return getInputOrAsk(
544
+ getNameInput(inputs),
545
+ () => askInput(Messages.NEW_NAME_QUESTION, {
546
+ required: true,
547
+ default: "nanoforge-app"
548
+ })
549
+ );
550
+ }, "getNewNameInputOrAsk");
551
+ var getCreateNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
552
+ return getInputOrAsk(
553
+ getNameInput(inputs),
554
+ () => askInput(Messages.CREATE_NAME_QUESTION, {
555
+ required: true,
556
+ default: "example"
557
+ })
558
+ );
559
+ }, "getCreateNameInputOrAsk");
560
+
561
+ // src/lib/input/inputs/path.input.ts
562
+ var getPathInput = /* @__PURE__ */ __name((inputs) => {
563
+ return getStringInput(inputs, "path");
564
+ }, "getPathInput");
565
+ var getPathInputWithDefault = /* @__PURE__ */ __name((inputs, defaultValue) => {
566
+ return getStringInputWithDefault(inputs, "path", defaultValue);
567
+ }, "getPathInputWithDefault");
568
+
569
+ // src/lib/input/inputs/server.input.ts
570
+ function getServerInput(inputs) {
571
+ return getBooleanInputWithDefault(inputs, "server", false);
572
+ }
573
+ __name(getServerInput, "getServerInput");
574
+
575
+ // src/lib/input/inputs/watch.input.ts
576
+ var getWatchInput = /* @__PURE__ */ __name((inputs) => {
577
+ return getBooleanInputWithDefault(inputs, "watch", false);
578
+ }, "getWatchInput");
579
+
580
+ // src/lib/input/inputs/create/type.input.ts
581
+ var getCreateTypeInput = /* @__PURE__ */ __name((inputs) => {
582
+ const res = getStringInput(inputs, "type");
583
+ if (res && ["component", "system"].includes(res)) return res;
584
+ throw new Error("Invalid type. Please enter 'component' or 'system'.");
585
+ }, "getCreateTypeInput");
586
+
587
+ // src/lib/input/inputs/dev/generate.input.ts
588
+ var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
589
+ return getBooleanInputWithDefault(inputs, "generate", false);
590
+ }, "getDevGenerateInput");
591
+
592
+ // src/lib/input/inputs/install/lib.input.ts
593
+ function getInstallLibInput(inputs) {
594
+ return getBooleanInputWithDefault(inputs, "lib", false);
595
+ }
596
+ __name(getInstallLibInput, "getInstallLibInput");
597
+
440
598
  // src/lib/input/inputs/install/names.input.ts
441
599
  var getNamesInput = /* @__PURE__ */ __name((inputs) => {
442
600
  return getArrayInput(inputs, "names");
@@ -448,12 +606,6 @@ var getInstallNamesInputOrAsk = /* @__PURE__ */ __name((inputs) => {
448
606
  );
449
607
  }, "getInstallNamesInputOrAsk");
450
608
 
451
- // src/lib/input/inputs/install/server.input.ts
452
- function getInstallServerInput(inputs) {
453
- return getBooleanInputWithDefault(inputs, "server", false);
454
- }
455
- __name(getInstallServerInput, "getInstallServerInput");
456
-
457
609
  // src/lib/input/inputs/login-out/api-key.input.ts
458
610
  var getApiKeyInput = /* @__PURE__ */ __name((inputs) => {
459
611
  return getStringInput(inputs, "apiKey");
@@ -507,20 +659,6 @@ var getNewLintInput = /* @__PURE__ */ __name((inputs) => {
507
659
  return getBooleanInputWithDefault(inputs, "lint", true);
508
660
  }, "getNewLintInput");
509
661
 
510
- // src/lib/input/inputs/new/name.input.ts
511
- var getNameInput = /* @__PURE__ */ __name((inputs) => {
512
- return getStringInput(inputs, "name");
513
- }, "getNameInput");
514
- var getNewNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
515
- return getInputOrAsk(
516
- getNameInput(inputs),
517
- () => askInput(Messages.NEW_NAME_QUESTION, {
518
- required: true,
519
- default: "nanoforge-app"
520
- })
521
- );
522
- }, "getNewNameInputOrAsk");
523
-
524
662
  // src/lib/input/inputs/new/package-manager.input.ts
525
663
  var getPackageManagerInput = /* @__PURE__ */ __name((inputs) => {
526
664
  return getStringInput(inputs, "packageManager");
@@ -538,11 +676,6 @@ var getNewPackageManagerInputOrAsk = /* @__PURE__ */ __name((inputs) => {
538
676
  );
539
677
  }, "getNewPackageManagerInputOrAsk");
540
678
 
541
- // src/lib/input/inputs/new/path.input.ts
542
- var getNewPathInput = /* @__PURE__ */ __name((inputs) => {
543
- return getStringInput(inputs, "path");
544
- }, "getNewPathInput");
545
-
546
679
  // src/lib/input/inputs/new/server.input.ts
547
680
  var getNewServerInput = /* @__PURE__ */ __name((inputs) => {
548
681
  return getBooleanInput(inputs, "server");
@@ -645,6 +778,9 @@ var PackageManager = class {
645
778
  this.commands = commands;
646
779
  this.runner = runner;
647
780
  }
781
+ name;
782
+ commands;
783
+ runner;
648
784
  static {
649
785
  __name(this, "PackageManager");
650
786
  }
@@ -781,13 +917,15 @@ import { resolve as resolve2 } from "path";
781
917
 
782
918
  // src/lib/runner/runner.ts
783
919
  import { red as red4 } from "ansis";
784
- import { spawn } from "child_process";
920
+ import { spawn as spawn2 } from "child_process";
785
921
  import * as process2 from "process";
786
922
  var Runner = class {
787
923
  constructor(binary, baseArgs = []) {
788
924
  this.binary = binary;
789
925
  this.baseArgs = baseArgs;
790
926
  }
927
+ binary;
928
+ baseArgs;
791
929
  static {
792
930
  __name(this, "Runner");
793
931
  }
@@ -796,7 +934,7 @@ var Runner = class {
796
934
  const spawnOpts = this.buildSpawnOptions(collect, cwd2, env2);
797
935
  const fullArgs = [...this.baseArgs, ...args];
798
936
  return new Promise((resolve4, reject) => {
799
- const child = spawn(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
937
+ const child = spawn2(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
800
938
  const output = this.attachOutputHandlers(child, listeners);
801
939
  child.on("close", (code) => {
802
940
  if (code === 0) {
@@ -992,6 +1130,21 @@ var PackageManagerFactory = class {
992
1130
  }
993
1131
  };
994
1132
 
1133
+ // src/lib/utils/files.ts
1134
+ import fs3 from "fs";
1135
+ import { join as join3 } from "path";
1136
+ var copyFiles = /* @__PURE__ */ __name((from, to) => {
1137
+ if (!fs3.existsSync(from)) return;
1138
+ if (!fs3.existsSync(to)) throw new Error(`Directory ${to} does not exist`);
1139
+ fs3.readdirSync(from, { recursive: true }).forEach((file) => {
1140
+ fs3.copyFileSync(join3(from, file.toString()), join3(to, file.toString()));
1141
+ });
1142
+ }, "copyFiles");
1143
+ var resetFolder = /* @__PURE__ */ __name((folder) => {
1144
+ if (fs3.existsSync(folder)) fs3.rmSync(folder, { recursive: true, force: true });
1145
+ fs3.mkdirSync(folder);
1146
+ }, "resetFolder");
1147
+
995
1148
  // src/lib/utils/run-safe.ts
996
1149
  import { red as red5 } from "ansis";
997
1150
  var runSafe = /* @__PURE__ */ __name(async (fn, fallback) => {
@@ -1011,42 +1164,77 @@ var BuildConfig = class {
1011
1164
  static {
1012
1165
  __name(this, "BuildConfig");
1013
1166
  }
1014
- entryFile;
1015
- outDir;
1167
+ entry;
1168
+ staticDir;
1169
+ };
1170
+ __decorateClass([
1171
+ Expose(),
1172
+ IsString(),
1173
+ IsNotEmpty()
1174
+ ], BuildConfig.prototype, "entry", 2);
1175
+ __decorateClass([
1176
+ Expose(),
1177
+ IsString(),
1178
+ IsNotEmpty()
1179
+ ], BuildConfig.prototype, "staticDir", 2);
1180
+ var EditorConfig = class {
1181
+ static {
1182
+ __name(this, "EditorConfig");
1183
+ }
1184
+ entry;
1185
+ save;
1016
1186
  };
1017
1187
  __decorateClass([
1018
1188
  Expose(),
1019
1189
  IsString(),
1020
1190
  IsNotEmpty()
1021
- ], BuildConfig.prototype, "entryFile", 2);
1191
+ ], EditorConfig.prototype, "entry", 2);
1022
1192
  __decorateClass([
1023
1193
  Expose(),
1024
1194
  IsString(),
1025
1195
  IsNotEmpty()
1026
- ], BuildConfig.prototype, "outDir", 2);
1027
- var RunConfig = class {
1196
+ ], EditorConfig.prototype, "save", 2);
1197
+ var DirsConfig = class {
1028
1198
  static {
1029
- __name(this, "RunConfig");
1199
+ __name(this, "DirsConfig");
1030
1200
  }
1031
- dir;
1201
+ components;
1202
+ systems;
1032
1203
  };
1033
1204
  __decorateClass([
1034
1205
  Expose(),
1035
1206
  IsString(),
1036
1207
  IsNotEmpty()
1037
- ], RunConfig.prototype, "dir", 2);
1208
+ ], DirsConfig.prototype, "components", 2);
1209
+ __decorateClass([
1210
+ Expose(),
1211
+ IsString(),
1212
+ IsNotEmpty()
1213
+ ], DirsConfig.prototype, "systems", 2);
1038
1214
  var ClientConfig = class {
1039
1215
  static {
1040
1216
  __name(this, "ClientConfig");
1041
1217
  }
1218
+ enable;
1042
1219
  port;
1220
+ outDir;
1043
1221
  build;
1044
- runtime;
1222
+ editor;
1223
+ dirs;
1045
1224
  };
1225
+ __decorateClass([
1226
+ Expose(),
1227
+ IsBoolean()
1228
+ ], ClientConfig.prototype, "enable", 2);
1046
1229
  __decorateClass([
1047
1230
  Expose(),
1048
1231
  IsPort()
1049
1232
  ], ClientConfig.prototype, "port", 2);
1233
+ __decorateClass([
1234
+ Expose(),
1235
+ IsString(),
1236
+ IsNotEmpty()
1237
+ ], ClientConfig.prototype, "outDir", 2);
1050
1238
  __decorateClass([
1051
1239
  Expose(),
1052
1240
  Type(() => BuildConfig),
@@ -1054,21 +1242,33 @@ __decorateClass([
1054
1242
  ], ClientConfig.prototype, "build", 2);
1055
1243
  __decorateClass([
1056
1244
  Expose(),
1057
- Type(() => RunConfig),
1245
+ Type(() => EditorConfig),
1246
+ ValidateNested()
1247
+ ], ClientConfig.prototype, "editor", 2);
1248
+ __decorateClass([
1249
+ Expose(),
1250
+ Type(() => DirsConfig),
1058
1251
  ValidateNested()
1059
- ], ClientConfig.prototype, "runtime", 2);
1252
+ ], ClientConfig.prototype, "dirs", 2);
1060
1253
  var ServerConfig = class {
1061
1254
  static {
1062
1255
  __name(this, "ServerConfig");
1063
1256
  }
1064
1257
  enable;
1258
+ outDir;
1065
1259
  build;
1066
- runtime;
1260
+ editor;
1261
+ dirs;
1067
1262
  };
1068
1263
  __decorateClass([
1069
1264
  Expose(),
1070
1265
  IsBoolean()
1071
1266
  ], ServerConfig.prototype, "enable", 2);
1267
+ __decorateClass([
1268
+ Expose(),
1269
+ IsString(),
1270
+ IsNotEmpty()
1271
+ ], ServerConfig.prototype, "outDir", 2);
1072
1272
  __decorateClass([
1073
1273
  Expose(),
1074
1274
  Type(() => BuildConfig),
@@ -1076,9 +1276,14 @@ __decorateClass([
1076
1276
  ], ServerConfig.prototype, "build", 2);
1077
1277
  __decorateClass([
1078
1278
  Expose(),
1079
- Type(() => RunConfig),
1279
+ Type(() => EditorConfig),
1080
1280
  ValidateNested()
1081
- ], ServerConfig.prototype, "runtime", 2);
1281
+ ], ServerConfig.prototype, "editor", 2);
1282
+ __decorateClass([
1283
+ Expose(),
1284
+ Type(() => DirsConfig),
1285
+ ValidateNested()
1286
+ ], ServerConfig.prototype, "dirs", 2);
1082
1287
  var Config = class {
1083
1288
  static {
1084
1289
  __name(this, "Config");
@@ -1117,13 +1322,12 @@ __decorateClass([
1117
1322
  import { plainToInstance } from "class-transformer";
1118
1323
  import { validate } from "class-validator";
1119
1324
  import { existsSync as existsSync2, readFileSync } from "fs";
1120
- import { join as join3 } from "path";
1325
+ import { join as join4 } from "path";
1121
1326
 
1122
1327
  // src/lib/constants.ts
1123
1328
  var CONFIG_FILE_NAME = "nanoforge.config.json";
1124
1329
  var MANIFEST_FILE_NAME = "nanoforge.manifest.json";
1125
1330
  var GLOBAL_CONFIG_FILE_NAME = ".nanoforgerc";
1126
- var NANOFORGE_DIR = ".nanoforge";
1127
1331
  var REGISTRY_URL = "https://api.nanoforge.dev";
1128
1332
 
1129
1333
  // src/lib/utils/object.ts
@@ -1153,23 +1357,36 @@ var CONFIG_DEFAULTS = {
1153
1357
  language: "ts",
1154
1358
  initFunctions: true,
1155
1359
  client: {
1360
+ enable: true,
1156
1361
  port: "3000",
1362
+ outDir: ".nanoforge/client",
1157
1363
  build: {
1158
- entryFile: "client/main.ts",
1159
- outDir: ".nanoforge/client"
1364
+ entry: "client/main.ts",
1365
+ staticDir: "client/static"
1160
1366
  },
1161
- runtime: {
1162
- dir: ".nanoforge/client"
1367
+ editor: {
1368
+ entry: ".nanoforge/editor/client/main.ts",
1369
+ save: ".nanoforge/client.save.json"
1370
+ },
1371
+ dirs: {
1372
+ components: "client/components",
1373
+ systems: "client/systems"
1163
1374
  }
1164
1375
  },
1165
1376
  server: {
1166
1377
  enable: false,
1378
+ outDir: ".nanoforge/server",
1167
1379
  build: {
1168
- entryFile: "server/main.ts",
1169
- outDir: ".nanoforge/server"
1380
+ entry: "server/main.ts",
1381
+ staticDir: "server/static"
1382
+ },
1383
+ editor: {
1384
+ entry: ".nanoforge/editor/server/main.ts",
1385
+ save: ".nanoforge/server.save.json"
1170
1386
  },
1171
- runtime: {
1172
- dir: ".nanoforge/server"
1387
+ dirs: {
1388
+ components: "server/components",
1389
+ systems: "server/systems"
1173
1390
  }
1174
1391
  }
1175
1392
  };
@@ -1178,23 +1395,23 @@ var CONFIG_DEFAULTS = {
1178
1395
  var config;
1179
1396
  var getConfigPath = /* @__PURE__ */ __name((directory, name) => {
1180
1397
  if (name) {
1181
- return join3(directory, name);
1398
+ return join4(directory, name);
1182
1399
  } else {
1183
1400
  for (const n of [CONFIG_FILE_NAME]) {
1184
- const path = join3(directory, n);
1401
+ const path = join4(directory, n);
1185
1402
  if (existsSync2(path)) return path;
1186
1403
  }
1187
1404
  throw new Error(`No config file found in directory: ${directory}`);
1188
1405
  }
1189
1406
  }, "getConfigPath");
1190
- var loadConfig = /* @__PURE__ */ __name(async (directory, name) => {
1407
+ var loadConfig = /* @__PURE__ */ __name(async (directory, name, noThrow = false) => {
1191
1408
  if (config) return config;
1192
1409
  let rawData;
1193
1410
  const path = getConfigPath(directory, name);
1194
1411
  try {
1195
1412
  rawData = deepMerge(CONFIG_DEFAULTS, JSON.parse(readFileSync(path, "utf-8")));
1196
1413
  } catch {
1197
- rawData = null;
1414
+ rawData = noThrow ? CONFIG_DEFAULTS : null;
1198
1415
  }
1199
1416
  if (!rawData) throw new Error(`Not able to read config file : ${path}`);
1200
1417
  const data = plainToInstance(Config, rawData, {
@@ -1209,8 +1426,8 @@ ${errors.toString().replace(/,/g, "\n")}`);
1209
1426
  }, "loadConfig");
1210
1427
 
1211
1428
  // src/action/common/config.ts
1212
- var getConfig = /* @__PURE__ */ __name((inputs, dir) => {
1213
- return loadConfig(dir, getConfigInput(inputs));
1429
+ var getConfig = /* @__PURE__ */ __name((inputs, dir, noThrow) => {
1430
+ return loadConfig(dir, getConfigInput(inputs), noThrow);
1214
1431
  }, "getConfig");
1215
1432
 
1216
1433
  // src/action/abstract.action.ts
@@ -1257,40 +1474,53 @@ var BuildAction = class extends AbstractAction {
1257
1474
  async handle(_args, options) {
1258
1475
  const directory = getDirectoryInput(options);
1259
1476
  const config2 = await getConfig(options, directory);
1477
+ const isEditor = getEditorInput(options);
1260
1478
  const isWatch = getWatchInput(options);
1261
- const targets = this.resolveTargets(config2, options);
1479
+ const targets = this.resolveTargets(config2, options, isEditor);
1262
1480
  const results = await this.buildAll(targets, directory, isWatch);
1263
1481
  if (isWatch) {
1264
1482
  return this.enterWatchMode();
1265
1483
  }
1266
1484
  return { success: results.every(Boolean) };
1267
1485
  }
1268
- resolveTargets(config2, options) {
1269
- const targets = [
1270
- this.createTarget(
1271
- "Client",
1272
- config2.client.build,
1273
- "browser",
1274
- getStringInput(options, "clientDirectory")
1275
- )
1276
- ];
1277
- if (config2.server.enable) {
1486
+ resolveTargets(config2, options, isEditor) {
1487
+ const targets = [];
1488
+ if (config2.client.enable)
1489
+ targets.push(
1490
+ this.createTarget(
1491
+ "Client",
1492
+ "browser",
1493
+ getStringInputWithDefault(
1494
+ options,
1495
+ "clientEntry",
1496
+ !isEditor ? config2.client.build.entry : config2.client.editor.entry
1497
+ ),
1498
+ getStringInputWithDefault(options, "clientStaticDir", config2.client.build.staticDir),
1499
+ getStringInputWithDefault(options, "clientOutDir", config2.client.outDir)
1500
+ )
1501
+ );
1502
+ if (config2.server.enable)
1278
1503
  targets.push(
1279
1504
  this.createTarget(
1280
1505
  "Server",
1281
- config2.server.build,
1282
1506
  "node",
1283
- getStringInput(options, "serverDirectory")
1507
+ getStringInputWithDefault(
1508
+ options,
1509
+ "serverEntry",
1510
+ !isEditor ? config2.server.build.entry : config2.server.editor.entry
1511
+ ),
1512
+ getStringInputWithDefault(options, "serverStaticDir", config2.server.build.staticDir),
1513
+ getStringInputWithDefault(options, "serverOutDir", config2.server.outDir)
1284
1514
  )
1285
1515
  );
1286
- }
1287
1516
  return targets;
1288
1517
  }
1289
- createTarget(name, config2, platform, outDirOverride) {
1518
+ createTarget(name, platform, entryFile, staticDir, outDir) {
1290
1519
  return {
1291
1520
  name,
1292
- entry: config2.entryFile,
1293
- output: outDirOverride || config2.outDir,
1521
+ entry: entryFile,
1522
+ static: staticDir,
1523
+ output: outDir,
1294
1524
  platform
1295
1525
  };
1296
1526
  }
@@ -1304,17 +1534,18 @@ var BuildAction = class extends AbstractAction {
1304
1534
  }
1305
1535
  async buildTarget(target, directory, isWatch) {
1306
1536
  const packageManager = PackageManagerFactory.create("local_bun" /* LOCAL_BUN */);
1307
- const executeBuild = /* @__PURE__ */ __name((rebuild = false) => runSafe(
1308
- () => packageManager.build(
1537
+ const executeBuild = /* @__PURE__ */ __name((rebuild = false) => runSafe(() => {
1538
+ this.resetOut(target.output, directory);
1539
+ this.copyFiles(target, directory);
1540
+ return packageManager.build(
1309
1541
  target.name,
1310
1542
  directory,
1311
1543
  target.entry,
1312
1544
  target.output,
1313
1545
  ["--asset-naming", "[name].[ext]", "--target", target.platform],
1314
1546
  rebuild
1315
- ),
1316
- false
1317
- ), "executeBuild");
1547
+ );
1548
+ }, false), "executeBuild");
1318
1549
  if (isWatch) {
1319
1550
  this.watchDirectory(directory, target.entry, () => executeBuild(true));
1320
1551
  }
@@ -1322,7 +1553,7 @@ var BuildAction = class extends AbstractAction {
1322
1553
  return result !== false;
1323
1554
  }
1324
1555
  watchDirectory(directory, entry, onChange) {
1325
- const watchPath = dirname(join4(getCwd(directory), entry));
1556
+ const watchPath = dirname(join5(getCwd(directory), entry));
1326
1557
  watch(watchPath).on("change", onChange);
1327
1558
  }
1328
1559
  enterWatchMode() {
@@ -1331,43 +1562,16 @@ var BuildAction = class extends AbstractAction {
1331
1562
  console.info();
1332
1563
  return { keepAlive: true };
1333
1564
  }
1334
- };
1335
-
1336
- // src/action/actions/dev.action.ts
1337
- var DevAction = class extends AbstractAction {
1338
- static {
1339
- __name(this, "DevAction");
1565
+ resetOut(outDir, directory) {
1566
+ resetFolder(getCwd(join5(directory, outDir)));
1340
1567
  }
1341
- startMessage = Messages.DEV_START;
1342
- successMessage = Messages.DEV_SUCCESS;
1343
- failureMessage = Messages.DEV_FAILED;
1344
- async handle(_args, options) {
1345
- const directory = getDirectoryInput(options);
1346
- const generate = getDevGenerateInput(options);
1347
- const tasks = this.buildTaskList(directory, generate);
1348
- await Promise.all(tasks);
1349
- return { keepAlive: true };
1350
- }
1351
- buildTaskList(directory, generate) {
1352
- const tasks = [];
1353
- if (generate) {
1354
- tasks.push(this.runSubCommand("generate", directory, { silent: true }));
1355
- }
1356
- tasks.push(this.runSubCommand("build", directory, { silent: true }));
1357
- tasks.push(this.runSubCommand("start", directory, { silent: false }));
1358
- return tasks;
1359
- }
1360
- async runSubCommand(command, directory, options) {
1361
- await runSafe(async () => {
1362
- const packageManager = await PackageManagerFactory.find(directory);
1363
- await packageManager.runDev(directory, "nf", {}, [command, "--watch"], options.silent);
1364
- });
1568
+ copyFiles(target, directory) {
1569
+ const from = getCwd(join5(directory, target.static));
1570
+ const to = getCwd(join5(directory, target.output));
1571
+ copyFiles(from, to);
1365
1572
  }
1366
1573
  };
1367
1574
 
1368
- // src/action/actions/generate.action.ts
1369
- import { join as join5 } from "path";
1370
-
1371
1575
  // src/lib/schematics/abstract.collection.ts
1372
1576
  var AbstractCollection = class {
1373
1577
  constructor(collection, runner, cwd2) {
@@ -1375,6 +1579,9 @@ var AbstractCollection = class {
1375
1579
  this.runner = runner;
1376
1580
  this.cwd = cwd2;
1377
1581
  }
1582
+ collection;
1583
+ runner;
1584
+ cwd;
1378
1585
  static {
1379
1586
  __name(this, "AbstractCollection");
1380
1587
  }
@@ -1424,6 +1631,16 @@ var NanoforgeCollection = class _NanoforgeCollection extends AbstractCollection
1424
1631
  name: "docker",
1425
1632
  alias: "docker",
1426
1633
  description: "Generate a Dockerfile for the application"
1634
+ },
1635
+ {
1636
+ name: "component",
1637
+ alias: "component",
1638
+ description: "Generate a Component for an application"
1639
+ },
1640
+ {
1641
+ name: "system",
1642
+ alias: "system",
1643
+ description: "Generate a System for an application"
1427
1644
  }
1428
1645
  ];
1429
1646
  constructor(runner, cwd2) {
@@ -1467,6 +1684,7 @@ var CollectionFactory = class {
1467
1684
  var toKebabCase = /* @__PURE__ */ __name((str) => {
1468
1685
  return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
1469
1686
  }, "toKebabCase");
1687
+ var capitalize = /* @__PURE__ */ __name((str) => str.charAt(0).toUpperCase() + str.slice(1), "capitalize");
1470
1688
 
1471
1689
  // src/lib/schematics/schematic.option.ts
1472
1690
  var SchematicOption = class {
@@ -1474,6 +1692,8 @@ var SchematicOption = class {
1474
1692
  this.name = name;
1475
1693
  this.value = value;
1476
1694
  }
1695
+ name;
1696
+ value;
1477
1697
  static {
1478
1698
  __name(this, "SchematicOption");
1479
1699
  }
@@ -1544,7 +1764,72 @@ var mapSchematicOptions = /* @__PURE__ */ __name((inputs) => {
1544
1764
  }, []);
1545
1765
  }, "mapSchematicOptions");
1546
1766
 
1767
+ // src/action/actions/create.action.ts
1768
+ var CreateAction = class extends AbstractAction {
1769
+ static {
1770
+ __name(this, "CreateAction");
1771
+ }
1772
+ startMessage = Messages.CREATE_START;
1773
+ successMessage = Messages.CREATE_SUCCESS;
1774
+ failureMessage = Messages.CREATE_FAILED;
1775
+ async handle(args, options) {
1776
+ const directory = getDirectoryInput(options);
1777
+ const config2 = await getConfig(options, directory, true);
1778
+ const type = getCreateTypeInput(args);
1779
+ const name = await getCreateNameInputOrAsk(options);
1780
+ const isServer = getServerInput(options);
1781
+ const path = getPathInputWithDefault(
1782
+ options,
1783
+ config2[isServer ? "server" : "client"].dirs[type === "component" ? "components" : "systems"]
1784
+ );
1785
+ await this.generateElement(directory, type, {
1786
+ name,
1787
+ directory: path,
1788
+ part: isServer ? "server" : "client",
1789
+ language: config2.language
1790
+ });
1791
+ return {};
1792
+ }
1793
+ async generateElement(directory, type, values) {
1794
+ const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1795
+ await executeSchematic(capitalize(type), collection, type, values);
1796
+ }
1797
+ };
1798
+
1799
+ // src/action/actions/dev.action.ts
1800
+ var DevAction = class extends AbstractAction {
1801
+ static {
1802
+ __name(this, "DevAction");
1803
+ }
1804
+ startMessage = Messages.DEV_START;
1805
+ successMessage = Messages.DEV_SUCCESS;
1806
+ failureMessage = Messages.DEV_FAILED;
1807
+ async handle(_args, options) {
1808
+ const directory = getDirectoryInput(options);
1809
+ const generate = getDevGenerateInput(options);
1810
+ const tasks = this.buildTaskList(directory, generate);
1811
+ await Promise.all(tasks);
1812
+ return { keepAlive: true };
1813
+ }
1814
+ buildTaskList(directory, generate) {
1815
+ const tasks = [];
1816
+ if (generate) {
1817
+ tasks.push(this.runSubCommand("generate", directory, { silent: true }));
1818
+ }
1819
+ tasks.push(this.runSubCommand("build", directory, { silent: true }));
1820
+ tasks.push(this.runSubCommand("start", directory, { silent: false }));
1821
+ return tasks;
1822
+ }
1823
+ async runSubCommand(command, directory, options) {
1824
+ await runSafe(async () => {
1825
+ const packageManager = await PackageManagerFactory.find(directory);
1826
+ await packageManager.runDev(directory, "nf", {}, [command, "--watch"], options.silent);
1827
+ });
1828
+ }
1829
+ };
1830
+
1547
1831
  // src/action/actions/generate.action.ts
1832
+ import { join as join6 } from "path";
1548
1833
  var GenerateAction = class extends AbstractAction {
1549
1834
  static {
1550
1835
  __name(this, "GenerateAction");
@@ -1555,9 +1840,9 @@ var GenerateAction = class extends AbstractAction {
1555
1840
  async handle(_args, options) {
1556
1841
  const directory = getDirectoryInput(options);
1557
1842
  const config2 = await getConfig(options, directory);
1843
+ const isEditor = getEditorInput(options);
1558
1844
  const isWatch = getWatchInput(options);
1559
- const values = this.extractValues(config2);
1560
- await this.generateParts(values, directory, isWatch);
1845
+ await this.generateParts(config2, directory, isEditor, isWatch);
1561
1846
  if (isWatch) {
1562
1847
  return this.enterWatchMode();
1563
1848
  }
@@ -1565,43 +1850,45 @@ var GenerateAction = class extends AbstractAction {
1565
1850
  }
1566
1851
  extractValues(config2) {
1567
1852
  return {
1568
- name: config2.name,
1569
1853
  directory: ".",
1570
1854
  language: config2.language,
1571
- server: config2.server.enable,
1572
1855
  initFunctions: config2.initFunctions
1573
1856
  };
1574
1857
  }
1575
- async generateParts(values, directory, watch3) {
1858
+ async generateParts(config2, directory, isEditor, watch3) {
1576
1859
  const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1577
- const baseOptions = this.baseSchematicOptions(values);
1578
- await executeSchematic(
1579
- "Client main file",
1580
- collection,
1581
- "part-main",
1582
- { ...baseOptions, part: "client" },
1583
- watch3 ? this.watchPath(directory, values.directory, "client") : void 0
1584
- );
1585
- if (values.server) {
1860
+ const values = this.extractValues(config2);
1861
+ if (config2.client.enable)
1862
+ await executeSchematic(
1863
+ "Client main file",
1864
+ collection,
1865
+ "part-main",
1866
+ {
1867
+ ...values,
1868
+ part: "client",
1869
+ outFile: !isEditor ? config2.client.build.entry : config2.client.editor.entry,
1870
+ saveFile: config2.client.editor.save,
1871
+ editor: isEditor
1872
+ },
1873
+ watch3 ? this.watchPath(directory, values.directory, config2.client.editor.save) : void 0
1874
+ );
1875
+ if (config2.server.enable)
1586
1876
  await executeSchematic(
1587
1877
  "Server main file",
1588
1878
  collection,
1589
1879
  "part-main",
1590
- { ...baseOptions, part: "server" },
1591
- this.watchPath(directory, values.directory, "server")
1880
+ {
1881
+ ...values,
1882
+ part: "server",
1883
+ outFile: !isEditor ? config2.server.build.entry : config2.server.editor.entry,
1884
+ saveFile: config2.server.editor.save,
1885
+ editor: isEditor
1886
+ },
1887
+ this.watchPath(directory, values.directory, config2.server.editor.save)
1592
1888
  );
1593
- }
1594
1889
  }
1595
- baseSchematicOptions(values) {
1596
- return {
1597
- name: values.name,
1598
- directory: values.directory,
1599
- language: values.language,
1600
- initFunctions: values.initFunctions
1601
- };
1602
- }
1603
- watchPath(directory, subDir, part) {
1604
- return join5(getCwd(directory), subDir, NANOFORGE_DIR, `${part}.save.json`);
1890
+ watchPath(directory, subDir, saveFile) {
1891
+ return join6(getCwd(directory), subDir, saveFile);
1605
1892
  }
1606
1893
  enterWatchMode() {
1607
1894
  console.info();
@@ -1612,7 +1899,7 @@ var GenerateAction = class extends AbstractAction {
1612
1899
  };
1613
1900
 
1614
1901
  // src/action/actions/install.action.ts
1615
- import { join as join7 } from "path";
1902
+ import { join as join8 } from "path";
1616
1903
 
1617
1904
  // src/lib/global-config/global-config-handler.ts
1618
1905
  import { read, readUser, write, writeUser } from "rc9";
@@ -1855,8 +2142,8 @@ var concatDeps = /* @__PURE__ */ __name((deps) => {
1855
2142
  }, "concatDeps");
1856
2143
 
1857
2144
  // src/lib/registry/registry.ts
1858
- import fs3 from "fs";
1859
- import { join as join6 } from "path";
2145
+ import fs4 from "fs";
2146
+ import { join as join7 } from "path";
1860
2147
  var Registry = class {
1861
2148
  static {
1862
2149
  __name(this, "Registry");
@@ -1887,9 +2174,9 @@ var Registry = class {
1887
2174
  }
1888
2175
  static async installPackage(client2, manifest, dir) {
1889
2176
  const file = await client2.getFile(`/registry/${manifest.name}/-/${manifest._file}`);
1890
- const path = join6(dir, this.getTypeSubFolder(manifest.type));
1891
- fs3.mkdirSync(path, { recursive: true });
1892
- fs3.writeFileSync(join6(path, manifest._file), await file.bytes());
2177
+ const path = join7(dir, this.getTypeSubFolder(manifest.type));
2178
+ fs4.mkdirSync(path, { recursive: true });
2179
+ fs4.writeFileSync(join7(path, manifest._file), await file.bytes());
1893
2180
  }
1894
2181
  static getTypeSubFolder(type) {
1895
2182
  if (type === "component") return "components";
@@ -1901,14 +2188,14 @@ var Registry = class {
1901
2188
  return withAuth(config2.apiKey, force, !headers ? {} : void 0);
1902
2189
  }
1903
2190
  static _getPackageFile(filename, dir) {
1904
- const path = join6(getCwd(dir ?? "."), filename);
1905
- if (!fs3.existsSync(path))
2191
+ const path = join7(getCwd(dir ?? "."), filename);
2192
+ if (!fs4.existsSync(path))
1906
2193
  throw new Error(
1907
- "Package not found, please specify path in the nanoforge.manifest.json : `publish.paths.components`!"
2194
+ "Package not found, please specify path in the nanoforge.manifest.json : `publish.paths.package`!"
1908
2195
  );
1909
2196
  try {
1910
- fs3.accessSync(path, fs3.constants.R_OK);
1911
- return fs3.openAsBlob(path);
2197
+ fs4.accessSync(path, fs4.constants.R_OK);
2198
+ return fs4.openAsBlob(path);
1912
2199
  } catch {
1913
2200
  throw new Error("Cannot read package file, please verify your file permissions!");
1914
2201
  }
@@ -1927,7 +2214,7 @@ var InstallAction = class extends AbstractAction {
1927
2214
  const names = await getInstallNamesInputOrAsk(args);
1928
2215
  const directory = getDirectoryInput(options);
1929
2216
  const isLib = getInstallLibInput(options);
1930
- const isServer = getInstallServerInput(options);
2217
+ const isServer = getServerInput(options);
1931
2218
  return isLib ? this._installLibs(directory, names) : this._installNfPackages(directory, names, isServer);
1932
2219
  }
1933
2220
  async _installLibs(directory, names) {
@@ -1944,7 +2231,7 @@ var InstallAction = class extends AbstractAction {
1944
2231
  return withSpinner(Messages.INSTALL_PACKAGES_IN_PROGRESS, async () => {
1945
2232
  await Registry.install(
1946
2233
  Object.values(deps.nf),
1947
- join7(directory, isServer ? "server" : "client")
2234
+ join8(directory, isServer ? "server" : "client")
1948
2235
  );
1949
2236
  });
1950
2237
  }
@@ -1997,7 +2284,7 @@ var LogoutAction = class extends AbstractAction {
1997
2284
  };
1998
2285
 
1999
2286
  // src/action/actions/new.action.ts
2000
- import { join as join8 } from "path";
2287
+ import { join as join9 } from "path";
2001
2288
  var NewAction = class extends AbstractAction {
2002
2289
  static {
2003
2290
  __name(this, "NewAction");
@@ -2013,7 +2300,7 @@ var NewAction = class extends AbstractAction {
2013
2300
  if (!values.skipInstall) {
2014
2301
  res = await this.installDependencies(
2015
2302
  values.packageManager,
2016
- join8(cwdDirectory, values.directory ?? values.name)
2303
+ join9(cwdDirectory, values.directory ?? values.name)
2017
2304
  );
2018
2305
  }
2019
2306
  return { success: res };
@@ -2021,7 +2308,7 @@ var NewAction = class extends AbstractAction {
2021
2308
  async collectValues(inputs) {
2022
2309
  return {
2023
2310
  name: await getNewNameInputOrAsk(inputs),
2024
- directory: getNewPathInput(inputs),
2311
+ directory: getPathInput(inputs),
2025
2312
  packageManager: await getNewPackageManagerInputOrAsk(inputs),
2026
2313
  language: await getNewLanguageInputOrAsk(inputs),
2027
2314
  strict: await getNewStrictOrAsk(inputs),
@@ -2029,7 +2316,8 @@ var NewAction = class extends AbstractAction {
2029
2316
  initFunctions: getNewInitFunctionsWithDefault(inputs),
2030
2317
  skipInstall: await getNewSkipInstallOrAsk(inputs),
2031
2318
  docker: await getNewDockerOrAsk(inputs),
2032
- lint: getNewLintInput(inputs)
2319
+ lint: getNewLintInput(inputs),
2320
+ editor: getEditorInput(inputs)
2033
2321
  };
2034
2322
  }
2035
2323
  async scaffold(values, directory) {
@@ -2052,14 +2340,17 @@ var NewAction = class extends AbstractAction {
2052
2340
  language: values.language,
2053
2341
  strict: values.strict,
2054
2342
  server: values.server,
2055
- lint: values.lint
2343
+ lint: values.lint,
2344
+ editor: values.editor
2056
2345
  });
2057
2346
  }
2058
2347
  generateConfiguration(collection, values) {
2059
2348
  return executeSchematic("Configuration", collection, "configuration", {
2060
2349
  name: values.name,
2061
- directory: values.directory,
2062
- server: values.server
2350
+ directory: values.directory ?? values.name,
2351
+ server: values.server,
2352
+ language: values.language,
2353
+ initFunctions: values.initFunctions
2063
2354
  });
2064
2355
  }
2065
2356
  async generateClientParts(collection, values) {
@@ -2068,7 +2359,9 @@ var NewAction = class extends AbstractAction {
2068
2359
  ...partOptions,
2069
2360
  server: values.server
2070
2361
  });
2071
- await executeSchematic("Client main file", collection, "part-main", partOptions);
2362
+ await executeSchematic("Client main file", collection, "part-main", {
2363
+ ...partOptions
2364
+ });
2072
2365
  }
2073
2366
  async generateServerParts(collection, values) {
2074
2367
  const partOptions = this.partOptions(values, "server");
@@ -2076,20 +2369,20 @@ var NewAction = class extends AbstractAction {
2076
2369
  ...partOptions,
2077
2370
  server: values.server
2078
2371
  });
2079
- await executeSchematic("Server main file", collection, "part-main", partOptions);
2372
+ await executeSchematic("Server main file", collection, "part-main", {
2373
+ ...partOptions
2374
+ });
2080
2375
  }
2081
2376
  async generateDocker(collection, values) {
2082
2377
  await executeSchematic("Docker", collection, "docker", {
2083
- name: values.name,
2084
- directory: values.directory,
2378
+ directory: values.directory ?? values.name,
2085
2379
  packageManager: values.packageManager
2086
2380
  });
2087
2381
  }
2088
2382
  partOptions(values, part) {
2089
2383
  return {
2090
- name: values.name,
2091
2384
  part,
2092
- directory: values.directory,
2385
+ directory: values.directory ?? values.name,
2093
2386
  language: values.language,
2094
2387
  initFunctions: values.initFunctions
2095
2388
  };
@@ -2147,6 +2440,7 @@ var Manifest = class {
2147
2440
  name;
2148
2441
  type;
2149
2442
  description;
2443
+ tags;
2150
2444
  dependencies;
2151
2445
  publish;
2152
2446
  npmDependencies;
@@ -2168,6 +2462,11 @@ __decorateClass([
2168
2462
  IsString2(),
2169
2463
  IsOptional()
2170
2464
  ], Manifest.prototype, "description", 2);
2465
+ __decorateClass([
2466
+ Expose2(),
2467
+ IsString2({ each: true }),
2468
+ IsOptional()
2469
+ ], Manifest.prototype, "tags", 2);
2171
2470
  __decorateClass([
2172
2471
  Expose2(),
2173
2472
  IsString2({ each: true }),
@@ -2190,10 +2489,10 @@ __decorateClass([
2190
2489
  import { plainToInstance as plainToInstance2 } from "class-transformer";
2191
2490
  import { validate as validate2 } from "class-validator";
2192
2491
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2193
- import { join as join9 } from "path";
2492
+ import { join as join10 } from "path";
2194
2493
  var getManifestPath = /* @__PURE__ */ __name((directory) => {
2195
2494
  for (const n of [MANIFEST_FILE_NAME]) {
2196
- const path = join9(directory, n);
2495
+ const path = join10(directory, n);
2197
2496
  if (existsSync3(path)) return path;
2198
2497
  }
2199
2498
  throw new Error(`No manifest file found in directory: ${directory}`);
@@ -2236,7 +2535,7 @@ var PublishAction = class extends AbstractAction {
2236
2535
 
2237
2536
  // src/action/actions/start.action.ts
2238
2537
  import dotenv from "dotenv";
2239
- import { join as join10, resolve as resolve3 } from "path";
2538
+ import { join as join11, resolve as resolve3 } from "path";
2240
2539
  var StartAction = class extends AbstractAction {
2241
2540
  static {
2242
2541
  __name(this, "StartAction");
@@ -2247,10 +2546,18 @@ var StartAction = class extends AbstractAction {
2247
2546
  async handle(_args, options) {
2248
2547
  const directory = getDirectoryInput(options);
2249
2548
  const config2 = await getConfig(options, directory);
2549
+ const clientDir = getStringInputWithDefault(options, "clientDir", config2.client.outDir);
2550
+ const serverDir = getStringInputWithDefault(options, "serverDir", config2.server.outDir);
2250
2551
  const watch3 = getWatchInput(options);
2251
2552
  const port = getStringInputWithDefault(options, "port", config2.client.port);
2252
2553
  const ssl = this.resolveSSL(options);
2253
- const tasks = this.buildStartTasks(config2, directory, watch3, port, ssl);
2554
+ const tasks = this.buildStartTasks(config2, directory, {
2555
+ clientDir,
2556
+ serverDir,
2557
+ watch: watch3,
2558
+ port,
2559
+ ssl
2560
+ });
2254
2561
  await Promise.all(tasks);
2255
2562
  return { keepAlive: true };
2256
2563
  }
@@ -2265,13 +2572,16 @@ var StartAction = class extends AbstractAction {
2265
2572
  key
2266
2573
  };
2267
2574
  }
2268
- buildStartTasks(config2, directory, watch3, port, ssl) {
2575
+ buildStartTasks(config2, directory, options) {
2269
2576
  const env2 = this.parseEnv(directory);
2270
2577
  const tasks = [];
2271
- if (config2.server.enable) {
2272
- tasks.push(this.startServer(directory, config2, { watch: watch3 }, env2));
2273
- }
2274
- tasks.push(this.startClient(directory, config2, { watch: watch3, port, ssl }, env2));
2578
+ const { clientDir, serverDir, watch: watch3, port, ssl } = options;
2579
+ if (config2.server.enable)
2580
+ tasks.push(this.startServer(directory, config2, { serverDir, watch: watch3 }, env2));
2581
+ if (config2.client.enable)
2582
+ tasks.push(
2583
+ this.startClient(directory, config2, { clientDir, serverDir, watch: watch3, port, ssl }, env2)
2584
+ );
2275
2585
  return tasks;
2276
2586
  }
2277
2587
  async startClient(directory, config2, options, env2) {
@@ -2286,14 +2596,14 @@ var StartAction = class extends AbstractAction {
2286
2596
  }
2287
2597
  buildClientParams(directory, config2, options) {
2288
2598
  const params = {
2289
- "-d": getCwd(join10(directory, config2.client.runtime.dir)),
2599
+ "-d": getCwd(join11(directory, options.clientDir)),
2290
2600
  "-p": options.port
2291
2601
  };
2292
2602
  if (options.watch) params["--watch"] = true;
2293
2603
  if (options.watch) {
2294
2604
  params["--watch"] = true;
2295
2605
  if (config2.server.enable) {
2296
- params["--watch-server-dir"] = getCwd(join10(directory, config2.server.runtime.dir));
2606
+ params["--watch-server-dir"] = getCwd(join11(directory, options.serverDir));
2297
2607
  }
2298
2608
  }
2299
2609
  if (options.ssl) {
@@ -2302,9 +2612,9 @@ var StartAction = class extends AbstractAction {
2302
2612
  }
2303
2613
  return this.buildParams(params);
2304
2614
  }
2305
- buildServerParams(directory, config2, options) {
2615
+ buildServerParams(directory, _config, options) {
2306
2616
  const params = {
2307
- "-d": getCwd(join10(directory, config2.server.runtime.dir))
2617
+ "-d": getCwd(join11(directory, options.serverDir))
2308
2618
  };
2309
2619
  if (options.watch) params["--watch"] = true;
2310
2620
  return this.buildParams(params);
@@ -2320,7 +2630,7 @@ var StartAction = class extends AbstractAction {
2320
2630
  ...process.env
2321
2631
  };
2322
2632
  dotenv.config({
2323
- path: resolve3(getCwd(join10(dir, ".env"))),
2633
+ path: resolve3(getCwd(join11(dir, ".env"))),
2324
2634
  processEnv: rawEnv
2325
2635
  });
2326
2636
  const baseEnv = Object.entries(rawEnv).filter(
@@ -2365,6 +2675,7 @@ var AbstractCommand = class {
2365
2675
  constructor(action) {
2366
2676
  this.action = action;
2367
2677
  }
2678
+ action;
2368
2679
  static {
2369
2680
  __name(this, "AbstractCommand");
2370
2681
  }
@@ -2383,12 +2694,17 @@ var BuildCommand = class extends AbstractCommand {
2383
2694
  __name(this, "BuildCommand");
2384
2695
  }
2385
2696
  load(program2) {
2386
- program2.command("build").description("build your game").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--client-outDir [clientDirectory]", "specify the output directory of the client").option("--server-outDir [serverDirectory]", "specify the output directory of the server").option("--watch", "build app in watching mode", false).action(async (rawOptions) => {
2697
+ program2.command("build").description("build your game").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--client-entry [clientEntry]", "specify the entry file of the client").option("--server-entry [serverEntry]", "specify the entry file of the server").option("--client-static-dir [clientStaticDir]", "specify the static directory of the client").option("--server-static-dir [serverStaticDir]", "specify the static directory of the server").option("--client-out-dir [clientOutDir]", "specify the output directory of the client").option("--server-out-dir [serverOutDir]", "specify the output directory of the server").option("--editor", "specify if the project must build with editor config").option("--watch", "build app in watching mode", false).action(async (rawOptions) => {
2387
2698
  const options = AbstractCommand.mapToInput({
2388
2699
  directory: rawOptions.directory,
2389
2700
  config: rawOptions.config,
2390
- clientDirectory: rawOptions.clientOutDir,
2391
- serverDirectory: rawOptions.serverOutDir,
2701
+ clientEntry: rawOptions.clientEntry,
2702
+ serverEntry: rawOptions.serverEntry,
2703
+ clientStaticDir: rawOptions.clientStaticDir,
2704
+ serverStaticDir: rawOptions.serverStaticDir,
2705
+ clientOutDir: rawOptions.clientOutDir,
2706
+ serverOutDir: rawOptions.serverOutDir,
2707
+ editor: rawOptions.editor,
2392
2708
  watch: rawOptions.watch
2393
2709
  });
2394
2710
  await this.action.run(/* @__PURE__ */ new Map(), options);
@@ -2396,6 +2712,35 @@ var BuildCommand = class extends AbstractCommand {
2396
2712
  }
2397
2713
  };
2398
2714
 
2715
+ // src/command/commands/create.command.ts
2716
+ var CreateCommand = class extends AbstractCommand {
2717
+ static {
2718
+ __name(this, "CreateCommand");
2719
+ }
2720
+ load(program2) {
2721
+ program2.command("create [type]").description("create nanoforge components or systems").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("-n, --name [name]", "name of the component/system").option(
2722
+ "-s, --server",
2723
+ "install components/systems on server (default install on client)",
2724
+ false
2725
+ ).option(
2726
+ "-p, --path [path]",
2727
+ "path to the component/system folder (default: [part]/<components|systems>)"
2728
+ ).action(async (type, rawOptions) => {
2729
+ const args = AbstractCommand.mapToInput({
2730
+ type
2731
+ });
2732
+ const options = AbstractCommand.mapToInput({
2733
+ directory: rawOptions.directory,
2734
+ config: rawOptions.config,
2735
+ name: rawOptions.name,
2736
+ server: rawOptions.server,
2737
+ path: rawOptions.path
2738
+ });
2739
+ await this.action.run(args, options);
2740
+ });
2741
+ }
2742
+ };
2743
+
2399
2744
  // src/command/commands/dev.command.ts
2400
2745
  var DevCommand = class extends AbstractCommand {
2401
2746
  static {
@@ -2419,10 +2764,11 @@ var GenerateCommand = class extends AbstractCommand {
2419
2764
  __name(this, "GenerateCommand");
2420
2765
  }
2421
2766
  load(program2) {
2422
- program2.command("generate").description("generate nanoforge files from config").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--watch", "generate app in watching mode", false).action(async (rawOptions) => {
2767
+ program2.command("generate").description("generate nanoforge files from config").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--editor", "specify if the project must generate editor main file").option("--watch", "generate app in watching mode", false).action(async (rawOptions) => {
2423
2768
  const options = AbstractCommand.mapToInput({
2424
2769
  directory: rawOptions.directory,
2425
2770
  config: rawOptions.config,
2771
+ editor: rawOptions.editor,
2426
2772
  watch: rawOptions.watch
2427
2773
  });
2428
2774
  await this.action.run(/* @__PURE__ */ new Map(), options);
@@ -2496,7 +2842,7 @@ var NewCommand = class extends AbstractCommand {
2496
2842
  program2.command("new").description("create a new nanoforge project").option("-d, --directory [directory]", "specify the working directory of the command").option("--name [name]", "specify the name of your project").option(
2497
2843
  "--path [path]",
2498
2844
  "specify the relative path where your project will be created (default: name of the project)"
2499
- ).option("--package-manager [packageManager]", "specify the package manager of your project").option("--language [language]", "specify the language of your project").option("--strict", "use strict mode").option("--no-strict", "do not use strict mode").option("--server", "create a server").option("--no-server", "do not create a server").option("--init-functions", "initialize functions").option("--no-init-functions", "do not initialize functions").option("--skip-install", "skip installing dependencies").option("--no-skip-install", "do not skip installing dependencies").option("--docker", "generate docker files").option("--no-docker", "do not generate docker files").option("--no-lint", "do not generate lint files").action(async (rawOptions) => {
2845
+ ).option("--package-manager [packageManager]", "specify the package manager of your project").option("--language [language]", "specify the language of your project").option("--strict", "use strict mode").option("--no-strict", "do not use strict mode").option("--server", "create a server").option("--no-server", "do not create a server").option("--init-functions", "initialize functions").option("--no-init-functions", "do not initialize functions").option("--skip-install", "skip installing dependencies").option("--no-skip-install", "do not skip installing dependencies").option("--docker", "generate docker files").option("--no-docker", "do not generate docker files").option("--no-lint", "do not generate lint files").option("--editor", "do add editor dependencies").action(async (rawOptions) => {
2500
2846
  const options = AbstractCommand.mapToInput({
2501
2847
  directory: rawOptions.directory,
2502
2848
  name: rawOptions.name,
@@ -2508,7 +2854,8 @@ var NewCommand = class extends AbstractCommand {
2508
2854
  initFunctions: rawOptions.initFunctions,
2509
2855
  skipInstall: rawOptions.skipInstall,
2510
2856
  docker: rawOptions.docker,
2511
- lint: rawOptions.lint
2857
+ lint: rawOptions.lint,
2858
+ editor: rawOptions.editor
2512
2859
  });
2513
2860
  await this.action.run(/* @__PURE__ */ new Map(), options);
2514
2861
  });
@@ -2536,11 +2883,13 @@ var StartCommand = class extends AbstractCommand {
2536
2883
  __name(this, "StartCommand");
2537
2884
  }
2538
2885
  load(program2) {
2539
- program2.command("start").description("start your game").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("-p, --port [port]", "specify the port of the loader (the website to load the game)").option("--watch", "run app in watching mode", false).option("--cert [cert]", "path to the SSL certificate for HTTPS").option("--key [key]", "path to the SSL key for HTTPS").action(async (rawOptions) => {
2886
+ program2.command("start").description("start your game").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("-p, --port [port]", "specify the port of the loader (the website to load the game)").option("--client-dir [clientDirectory]", "specify the directory of the client").option("--server-dir [serverDirectory]", "specify the directory of the server").option("--watch", "run app in watching mode", false).option("--cert [cert]", "path to the SSL certificate for HTTPS").option("--key [key]", "path to the SSL key for HTTPS").action(async (rawOptions) => {
2540
2887
  const options = AbstractCommand.mapToInput({
2541
2888
  directory: rawOptions.directory,
2542
2889
  config: rawOptions.config,
2543
2890
  port: rawOptions.port,
2891
+ clientDir: rawOptions.clientDir,
2892
+ serverDir: rawOptions.serverDir,
2544
2893
  watch: rawOptions.watch,
2545
2894
  cert: rawOptions.cert,
2546
2895
  key: rawOptions.key
@@ -2577,6 +2926,7 @@ var CommandLoader = class {
2577
2926
  new StartCommand(new StartAction()).load(program2);
2578
2927
  new DevCommand(new DevAction()).load(program2);
2579
2928
  new GenerateCommand(new GenerateAction()).load(program2);
2929
+ new CreateCommand(new CreateAction()).load(program2);
2580
2930
  new LoginCommand(new LoginAction()).load(program2);
2581
2931
  new LogoutCommand(new LogoutAction()).load(program2);
2582
2932
  new PublishCommand(new PublishAction()).load(program2);
@@ -2596,6 +2946,14 @@ ${Prefixes.ERROR} Invalid command: ${red6`%s`}`, program2.args.join(" "));
2596
2946
 
2597
2947
  // src/bin/nf.ts
2598
2948
  var bootstrap = /* @__PURE__ */ __name(async () => {
2949
+ const signals = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT", "SIGBREAK"];
2950
+ signals.forEach((signal) => {
2951
+ const listener = /* @__PURE__ */ __name(async () => {
2952
+ process.off(signal, listener);
2953
+ await treeKill(process.pid, signal);
2954
+ }, "listener");
2955
+ process.on(signal, listener);
2956
+ });
2599
2957
  program.version(
2600
2958
  (await Promise.resolve().then(() => __toESM(require_package(), 1))).version ?? "unknown",
2601
2959
  "-v, --version",