@nanoforge-dev/cli 1.2.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.2.0",
43
+ version: "1.4.0",
44
44
  description: "NanoForge CLI",
45
45
  keywords: [
46
46
  "nanoforge",
@@ -73,6 +73,7 @@ var require_package = __commonJS({
73
73
  funding: "",
74
74
  scripts: {
75
75
  build: "tsc --noEmit && tsup",
76
+ "build:dev": "tsc --noEmit && NODE_ENV=development tsup",
76
77
  start: "node dist/nf.js",
77
78
  lint: "prettier --check . && eslint --format=pretty src",
78
79
  format: "prettier --write . && eslint --fix --format=pretty src",
@@ -100,8 +101,10 @@ var require_package = __commonJS({
100
101
  "class-transformer": "catalog:libs",
101
102
  "class-validator": "catalog:libs",
102
103
  commander: "catalog:cli",
104
+ dotenv: "catalog:libs",
103
105
  "node-emoji": "catalog:cli",
104
106
  ora: "catalog:cli",
107
+ rc9: "catalog:libs",
105
108
  "reflect-metadata": "catalog:libs"
106
109
  },
107
110
  devDependencies: {
@@ -146,6 +149,110 @@ var require_package = __commonJS({
146
149
  import { program } from "commander";
147
150
  import "reflect-metadata";
148
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
+
149
256
  // src/lib/utils/local-binaries.ts
150
257
  import { existsSync } from "fs";
151
258
  import { join, posix } from "path";
@@ -204,6 +311,16 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
204
311
  INSTALL_SUCCESS: success("Installation completed!"),
205
312
  INSTALL_FAILED: failure("Installation failed!"),
206
313
  INSTALL_NAMES_QUESTION: "Which libraries do you want to install?",
314
+ INSTALL_PACKAGES_IN_PROGRESS: "Install Nanoforge Packages",
315
+ // --- Login ---
316
+ LOGIN_START: "NanoForge Login",
317
+ LOGIN_SUCCESS: success("Login completed!"),
318
+ LOGIN_FAILED: failure("Login failed!"),
319
+ LOGIN_API_KEY_QUESTION: "What is your registry api key?",
320
+ // --- Logout ---
321
+ LOGOUT_START: "NanoForge Logout",
322
+ LOGOUT_SUCCESS: success("Logout completed!"),
323
+ LOGOUT_FAILED: failure("Logout failed!"),
207
324
  // --- New Project ---
208
325
  NEW_START: "NanoForge Project Creation",
209
326
  NEW_SUCCESS: success("Project successfully created!"),
@@ -215,6 +332,11 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
215
332
  NEW_SERVER_QUESTION: "Do you want to generate a server for multiplayer?",
216
333
  NEW_SKIP_INSTALL_QUESTION: "Do you want to skip dependency installation?",
217
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?",
218
340
  // --- Generate ---
219
341
  GENERATE_START: "NanoForge Generate",
220
342
  GENERATE_SUCCESS: success("Generation succeeded!"),
@@ -231,6 +353,16 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
231
353
  START_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Starting ${part}...`, "START_PART_IN_PROGRESS"),
232
354
  START_PART_SUCCESS: /* @__PURE__ */ __name((part) => success(`${part} terminated.`), "START_PART_SUCCESS"),
233
355
  START_PART_FAILED: /* @__PURE__ */ __name((part) => failure(`${part} failed!`), "START_PART_FAILED"),
356
+ // --- Publish ---
357
+ PUBLISH_START: "NanoForge Publish",
358
+ PUBLISH_SUCCESS: success("Publish completed!"),
359
+ PUBLISH_FAILED: failure("Publish failed!"),
360
+ PUBLISH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Publishing ${name}...`, "PUBLISH_IN_PROGRESS"),
361
+ // --- Unpublish ---
362
+ UNPUBLISH_START: "NanoForge Unpublish",
363
+ UNPUBLISH_SUCCESS: success("Unpublish completed!"),
364
+ UNPUBLISH_FAILED: failure("Unpublish failed!"),
365
+ UNPUBLISH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Unpublishing ${name}...`, "UNPUBLISH_IN_PROGRESS"),
234
366
  // --- Schematics ---
235
367
  SCHEMATICS_START: "Running schematics",
236
368
  SCHEMATIC_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Generating ${name}...`, "SCHEMATIC_IN_PROGRESS"),
@@ -261,7 +393,7 @@ var getSpinner = /* @__PURE__ */ __name((message) => ora({ text: message }), "ge
261
393
 
262
394
  // src/action/actions/build.action.ts
263
395
  import { watch } from "chokidar";
264
- import { dirname, join as join4 } from "path";
396
+ import { dirname, join as join5 } from "path";
265
397
 
266
398
  // src/lib/input/base-inputs.ts
267
399
  var getStringInput = /* @__PURE__ */ __name((input2, field) => {
@@ -310,15 +442,10 @@ var getConfigInput = /* @__PURE__ */ __name((inputs) => {
310
442
  return getStringInputWithDefault(inputs, "config", ".");
311
443
  }, "getConfigInput");
312
444
 
313
- // src/lib/input/inputs/watch.input.ts
314
- var getWatchInput = /* @__PURE__ */ __name((inputs) => {
315
- return getBooleanInputWithDefault(inputs, "watch", false);
316
- }, "getWatchInput");
317
-
318
- // src/lib/input/inputs/dev/generate.input.ts
319
- var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
320
- return getBooleanInputWithDefault(inputs, "generate", false);
321
- }, "getDevGenerateInput");
445
+ // src/lib/input/inputs/editor.input.ts
446
+ var getEditorInput = /* @__PURE__ */ __name((inputs) => {
447
+ return getBooleanInputWithDefault(inputs, "editor", false);
448
+ }, "getEditorInput");
322
449
 
323
450
  // src/lib/question/questions/confirm.question.ts
324
451
  import { confirm } from "@inquirer/prompts";
@@ -326,10 +453,16 @@ import { confirm } from "@inquirer/prompts";
326
453
  // src/lib/utils/errors.ts
327
454
  import { red } from "ansis";
328
455
  var getErrorMessage = /* @__PURE__ */ __name((error) => {
329
- if (error instanceof Error) return error.message;
456
+ if (error instanceof Error) return getErrorString(error);
330
457
  if (typeof error === "string") return error;
331
458
  return void 0;
332
459
  }, "getErrorMessage");
460
+ var getErrorString = /* @__PURE__ */ __name((error) => {
461
+ const stack = error.stack ? error.stack : error.message;
462
+ const cause = error.cause && typeof error.cause === "object" ? JSON.stringify(error.cause, null, 2) : error.cause;
463
+ return `${stack}${cause ? `
464
+ ${cause}` : ""}`;
465
+ }, "getErrorString");
333
466
  var handleActionError = /* @__PURE__ */ __name((context, error) => {
334
467
  console.error();
335
468
  console.error(red(context));
@@ -402,6 +535,66 @@ var askSelect = /* @__PURE__ */ __name(async (question, choices, baseOptions) =>
402
535
  }).catch(promptError);
403
536
  }, "askSelect");
404
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
+
405
598
  // src/lib/input/inputs/install/names.input.ts
406
599
  var getNamesInput = /* @__PURE__ */ __name((inputs) => {
407
600
  return getArrayInput(inputs, "names");
@@ -413,6 +606,109 @@ var getInstallNamesInputOrAsk = /* @__PURE__ */ __name((inputs) => {
413
606
  );
414
607
  }, "getInstallNamesInputOrAsk");
415
608
 
609
+ // src/lib/input/inputs/login-out/api-key.input.ts
610
+ var getApiKeyInput = /* @__PURE__ */ __name((inputs) => {
611
+ return getStringInput(inputs, "apiKey");
612
+ }, "getApiKeyInput");
613
+ var getLoginApiKeyInputOrAsk = /* @__PURE__ */ __name((inputs) => {
614
+ return getInputOrAsk(
615
+ getApiKeyInput(inputs),
616
+ () => askInput(Messages.LOGIN_API_KEY_QUESTION, {
617
+ required: true
618
+ })
619
+ );
620
+ }, "getLoginApiKeyInputOrAsk");
621
+
622
+ // src/lib/input/inputs/login-out/local.input.ts
623
+ function getLocalInput(inputs) {
624
+ return getBooleanInputWithDefault(inputs, "local", false);
625
+ }
626
+ __name(getLocalInput, "getLocalInput");
627
+
628
+ // src/lib/input/inputs/new/docker.input.ts
629
+ var getNewDockerInput = /* @__PURE__ */ __name((inputs) => {
630
+ return getBooleanInput(inputs, "docker");
631
+ }, "getNewDockerInput");
632
+ var getNewDockerOrAsk = /* @__PURE__ */ __name((inputs) => {
633
+ return getInputOrAsk(
634
+ getNewDockerInput(inputs),
635
+ () => askConfirm(Messages.NEW_DOCKER_QUESTION, { default: true })
636
+ );
637
+ }, "getNewDockerOrAsk");
638
+
639
+ // src/lib/input/inputs/new/init-functions.input.ts
640
+ var getNewInitFunctionsWithDefault = /* @__PURE__ */ __name((inputs) => {
641
+ return getBooleanInputWithDefault(inputs, "initFunctions", false);
642
+ }, "getNewInitFunctionsWithDefault");
643
+
644
+ // src/lib/input/inputs/new/language.input.ts
645
+ var getLanguageInput = /* @__PURE__ */ __name((inputs) => {
646
+ return getStringInput(inputs, "language");
647
+ }, "getLanguageInput");
648
+ var getNewLanguageInputOrAsk = /* @__PURE__ */ __name((inputs) => {
649
+ return getInputOrAsk(
650
+ getLanguageInput(inputs),
651
+ () => askSelect(Messages.NEW_LANGUAGE_QUESTION, [{ value: "ts" }, { value: "js" }], {
652
+ default: "ts"
653
+ })
654
+ );
655
+ }, "getNewLanguageInputOrAsk");
656
+
657
+ // src/lib/input/inputs/new/lint.input.ts
658
+ var getNewLintInput = /* @__PURE__ */ __name((inputs) => {
659
+ return getBooleanInputWithDefault(inputs, "lint", true);
660
+ }, "getNewLintInput");
661
+
662
+ // src/lib/input/inputs/new/package-manager.input.ts
663
+ var getPackageManagerInput = /* @__PURE__ */ __name((inputs) => {
664
+ return getStringInput(inputs, "packageManager");
665
+ }, "getPackageManagerInput");
666
+ var getNewPackageManagerInputOrAsk = /* @__PURE__ */ __name((inputs) => {
667
+ return getInputOrAsk(
668
+ getPackageManagerInput(inputs),
669
+ () => askSelect(
670
+ Messages.NEW_PACKAGE_MANAGER_QUESTION,
671
+ [{ value: "npm" }, { value: "yarn" }, { value: "pnpm" }, { value: "bun" }],
672
+ {
673
+ default: "npm"
674
+ }
675
+ )
676
+ );
677
+ }, "getNewPackageManagerInputOrAsk");
678
+
679
+ // src/lib/input/inputs/new/server.input.ts
680
+ var getNewServerInput = /* @__PURE__ */ __name((inputs) => {
681
+ return getBooleanInput(inputs, "server");
682
+ }, "getNewServerInput");
683
+ var getNewServerOrAsk = /* @__PURE__ */ __name((inputs) => {
684
+ return getInputOrAsk(
685
+ getNewServerInput(inputs),
686
+ () => askConfirm(Messages.NEW_SERVER_QUESTION, { default: false })
687
+ );
688
+ }, "getNewServerOrAsk");
689
+
690
+ // src/lib/input/inputs/new/skip-install.input.ts
691
+ var getNewSkipInstallInput = /* @__PURE__ */ __name((inputs) => {
692
+ return getBooleanInput(inputs, "skipInstall");
693
+ }, "getNewSkipInstallInput");
694
+ var getNewSkipInstallOrAsk = /* @__PURE__ */ __name((inputs) => {
695
+ return getInputOrAsk(
696
+ getNewSkipInstallInput(inputs),
697
+ () => askConfirm(Messages.NEW_SKIP_INSTALL_QUESTION, { default: false })
698
+ );
699
+ }, "getNewSkipInstallOrAsk");
700
+
701
+ // src/lib/input/inputs/new/strict.input.ts
702
+ var getNewStrictInput = /* @__PURE__ */ __name((inputs) => {
703
+ return getBooleanInput(inputs, "strict");
704
+ }, "getNewStrictInput");
705
+ var getNewStrictOrAsk = /* @__PURE__ */ __name((inputs) => {
706
+ return getInputOrAsk(
707
+ getNewStrictInput(inputs),
708
+ () => askConfirm(Messages.NEW_STRICT_QUESTION, { default: true })
709
+ );
710
+ }, "getNewStrictOrAsk");
711
+
416
712
  // src/lib/package-manager/package-manager.ts
417
713
  import { bold, red as red3 } from "ansis";
418
714
 
@@ -460,6 +756,21 @@ var resolveCLINodeBinaryPath = /* @__PURE__ */ __name((name) => {
460
756
  throw new Error("Could not find module path");
461
757
  }, "resolveCLINodeBinaryPath");
462
758
 
759
+ // src/lib/utils/spinner.ts
760
+ var withSpinner = /* @__PURE__ */ __name(async (message, task, onError) => {
761
+ const spinner = getSpinner(message);
762
+ spinner.start();
763
+ try {
764
+ const value = await task(spinner);
765
+ spinner.succeed();
766
+ return { success: true, value };
767
+ } catch (error) {
768
+ spinner.fail();
769
+ if (onError) onError();
770
+ return { success: false, error };
771
+ }
772
+ }, "withSpinner");
773
+
463
774
  // src/lib/package-manager/package-manager.ts
464
775
  var PackageManager = class {
465
776
  constructor(name, commands, runner) {
@@ -467,12 +778,15 @@ var PackageManager = class {
467
778
  this.commands = commands;
468
779
  this.runner = runner;
469
780
  }
781
+ name;
782
+ commands;
783
+ runner;
470
784
  static {
471
785
  __name(this, "PackageManager");
472
786
  }
473
787
  async install(directory) {
474
788
  const args = [this.commands.install, this.commands.silentFlag];
475
- const result = await this.withSpinner(
789
+ const result = await withSpinner(
476
790
  Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
477
791
  async (spinner) => {
478
792
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -500,7 +814,7 @@ var PackageManager = class {
500
814
  output,
501
815
  ...flags
502
816
  ];
503
- const result = await this.withSpinner(
817
+ const result = await withSpinner(
504
818
  message,
505
819
  async (spinner) => {
506
820
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -509,10 +823,10 @@ var PackageManager = class {
509
823
  );
510
824
  return result.success;
511
825
  }
512
- async run(name, directory, script, env2 = {}, flags = [], silent = false) {
826
+ async run(name, directory, script, params, env2 = {}, flags = [], silent = false) {
513
827
  console.info(Messages.START_PART_IN_PROGRESS(name));
514
828
  try {
515
- const args = this.buildRunArgs(script, flags, silent);
829
+ const args = this.buildRunArgs(script, params, flags, silent);
516
830
  await this.exec(args, directory, {
517
831
  env: env2,
518
832
  listeners: {
@@ -529,32 +843,21 @@ var PackageManager = class {
529
843
  }
530
844
  async runDev(directory, command, env2 = {}, flags = [], collect = true) {
531
845
  try {
532
- await this.exec([this.commands.run, command, ...flags], directory, { collect, env: env2 });
846
+ const base = [this.commands.exec, command];
847
+ if (this.commands.runArgsFlag) base.push(this.commands.runArgsFlag);
848
+ await this.exec([...base, ...flags], directory, { collect, env: env2 });
533
849
  return true;
534
850
  } catch {
535
851
  return false;
536
852
  }
537
853
  }
538
- async withSpinner(message, task, onError) {
539
- const spinner = getSpinner(message);
540
- spinner.start();
541
- try {
542
- const value = await task(spinner);
543
- spinner.succeed();
544
- return { success: true, value };
545
- } catch {
546
- spinner.fail();
547
- if (onError) onError();
548
- return { success: false };
549
- }
550
- }
551
854
  async addDependencies(saveFlag, directory, dependencies) {
552
855
  if (!dependencies.length) {
553
856
  this.logEmpty(Messages.PACKAGE_MANAGER_INSTALLATION_NOTHING);
554
857
  return true;
555
858
  }
556
859
  const args = [this.commands.add, saveFlag, ...dependencies];
557
- const result = await this.withSpinner(
860
+ const result = await withSpinner(
558
861
  Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
559
862
  async (spinner) => {
560
863
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -569,11 +872,13 @@ var PackageManager = class {
569
872
  throw new Error(`Package manager "${this.name}" does not support "${feature}"`);
570
873
  }
571
874
  }
572
- buildRunArgs(script, flags, silent) {
875
+ buildRunArgs(script, params, flags, silent) {
573
876
  const args = [...flags, this.commands.run];
574
877
  if (silent) args.push(this.commands.silentFlag);
575
878
  args.push(script);
576
- return args;
879
+ if (params.length === 0) return args;
880
+ if (this.commands.runArgsFlag) args.push(this.commands.runArgsFlag);
881
+ return args.concat(params);
577
882
  }
578
883
  exec(args, directory, options = {}) {
579
884
  return this.runner.run(args, {
@@ -612,13 +917,15 @@ import { resolve as resolve2 } from "path";
612
917
 
613
918
  // src/lib/runner/runner.ts
614
919
  import { red as red4 } from "ansis";
615
- import { spawn } from "child_process";
920
+ import { spawn as spawn2 } from "child_process";
616
921
  import * as process2 from "process";
617
922
  var Runner = class {
618
923
  constructor(binary, baseArgs = []) {
619
924
  this.binary = binary;
620
925
  this.baseArgs = baseArgs;
621
926
  }
927
+ binary;
928
+ baseArgs;
622
929
  static {
623
930
  __name(this, "Runner");
624
931
  }
@@ -626,12 +933,12 @@ var Runner = class {
626
933
  const { collect = false, cwd: cwd2 = process2.cwd(), env: env2, listeners, onFail } = options;
627
934
  const spawnOpts = this.buildSpawnOptions(collect, cwd2, env2);
628
935
  const fullArgs = [...this.baseArgs, ...args];
629
- return new Promise((resolve3, reject) => {
630
- const child = spawn(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
936
+ return new Promise((resolve4, reject) => {
937
+ const child = spawn2(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
631
938
  const output = this.attachOutputHandlers(child, listeners);
632
939
  child.on("close", (code) => {
633
940
  if (code === 0) {
634
- resolve3(this.formatOutput(output, collect));
941
+ resolve4(this.formatOutput(output, collect));
635
942
  } else {
636
943
  this.handleFailure(output, fullArgs, onFail);
637
944
  reject(this.createError(fullArgs, code));
@@ -746,6 +1053,7 @@ var PM_CONFIGS = {
746
1053
  remove: "uninstall",
747
1054
  exec: "exec",
748
1055
  run: "run",
1056
+ runArgsFlag: "--",
749
1057
  saveFlag: "--save",
750
1058
  saveDevFlag: "--save-dev",
751
1059
  silentFlag: "--silent"
@@ -822,6 +1130,21 @@ var PackageManagerFactory = class {
822
1130
  }
823
1131
  };
824
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
+
825
1148
  // src/lib/utils/run-safe.ts
826
1149
  import { red as red5 } from "ansis";
827
1150
  var runSafe = /* @__PURE__ */ __name(async (fn, fallback) => {
@@ -841,47 +1164,77 @@ var BuildConfig = class {
841
1164
  static {
842
1165
  __name(this, "BuildConfig");
843
1166
  }
844
- entryFile;
845
- 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;
846
1186
  };
847
1187
  __decorateClass([
848
1188
  Expose(),
849
1189
  IsString(),
850
1190
  IsNotEmpty()
851
- ], BuildConfig.prototype, "entryFile", 2);
1191
+ ], EditorConfig.prototype, "entry", 2);
852
1192
  __decorateClass([
853
1193
  Expose(),
854
1194
  IsString(),
855
1195
  IsNotEmpty()
856
- ], BuildConfig.prototype, "outDir", 2);
857
- var RunConfig = class {
1196
+ ], EditorConfig.prototype, "save", 2);
1197
+ var DirsConfig = class {
858
1198
  static {
859
- __name(this, "RunConfig");
1199
+ __name(this, "DirsConfig");
860
1200
  }
861
- dir;
1201
+ components;
1202
+ systems;
862
1203
  };
863
1204
  __decorateClass([
864
1205
  Expose(),
865
1206
  IsString(),
866
1207
  IsNotEmpty()
867
- ], RunConfig.prototype, "dir", 2);
1208
+ ], DirsConfig.prototype, "components", 2);
1209
+ __decorateClass([
1210
+ Expose(),
1211
+ IsString(),
1212
+ IsNotEmpty()
1213
+ ], DirsConfig.prototype, "systems", 2);
868
1214
  var ClientConfig = class {
869
1215
  static {
870
1216
  __name(this, "ClientConfig");
871
1217
  }
1218
+ enable;
872
1219
  port;
873
- gameExposurePort;
1220
+ outDir;
874
1221
  build;
875
- runtime;
1222
+ editor;
1223
+ dirs;
876
1224
  };
1225
+ __decorateClass([
1226
+ Expose(),
1227
+ IsBoolean()
1228
+ ], ClientConfig.prototype, "enable", 2);
877
1229
  __decorateClass([
878
1230
  Expose(),
879
1231
  IsPort()
880
1232
  ], ClientConfig.prototype, "port", 2);
881
1233
  __decorateClass([
882
1234
  Expose(),
883
- IsPort()
884
- ], ClientConfig.prototype, "gameExposurePort", 2);
1235
+ IsString(),
1236
+ IsNotEmpty()
1237
+ ], ClientConfig.prototype, "outDir", 2);
885
1238
  __decorateClass([
886
1239
  Expose(),
887
1240
  Type(() => BuildConfig),
@@ -889,17 +1242,23 @@ __decorateClass([
889
1242
  ], ClientConfig.prototype, "build", 2);
890
1243
  __decorateClass([
891
1244
  Expose(),
892
- Type(() => RunConfig),
1245
+ Type(() => EditorConfig),
893
1246
  ValidateNested()
894
- ], ClientConfig.prototype, "runtime", 2);
1247
+ ], ClientConfig.prototype, "editor", 2);
1248
+ __decorateClass([
1249
+ Expose(),
1250
+ Type(() => DirsConfig),
1251
+ ValidateNested()
1252
+ ], ClientConfig.prototype, "dirs", 2);
895
1253
  var ServerConfig = class {
896
1254
  static {
897
1255
  __name(this, "ServerConfig");
898
1256
  }
899
1257
  enable;
900
- port;
1258
+ outDir;
901
1259
  build;
902
- runtime;
1260
+ editor;
1261
+ dirs;
903
1262
  };
904
1263
  __decorateClass([
905
1264
  Expose(),
@@ -907,8 +1266,9 @@ __decorateClass([
907
1266
  ], ServerConfig.prototype, "enable", 2);
908
1267
  __decorateClass([
909
1268
  Expose(),
910
- IsPort()
911
- ], ServerConfig.prototype, "port", 2);
1269
+ IsString(),
1270
+ IsNotEmpty()
1271
+ ], ServerConfig.prototype, "outDir", 2);
912
1272
  __decorateClass([
913
1273
  Expose(),
914
1274
  Type(() => BuildConfig),
@@ -916,9 +1276,14 @@ __decorateClass([
916
1276
  ], ServerConfig.prototype, "build", 2);
917
1277
  __decorateClass([
918
1278
  Expose(),
919
- Type(() => RunConfig),
1279
+ Type(() => EditorConfig),
920
1280
  ValidateNested()
921
- ], ServerConfig.prototype, "runtime", 2);
1281
+ ], ServerConfig.prototype, "editor", 2);
1282
+ __decorateClass([
1283
+ Expose(),
1284
+ Type(() => DirsConfig),
1285
+ ValidateNested()
1286
+ ], ServerConfig.prototype, "dirs", 2);
922
1287
  var Config = class {
923
1288
  static {
924
1289
  __name(this, "Config");
@@ -957,11 +1322,13 @@ __decorateClass([
957
1322
  import { plainToInstance } from "class-transformer";
958
1323
  import { validate } from "class-validator";
959
1324
  import { existsSync as existsSync2, readFileSync } from "fs";
960
- import { join as join3 } from "path";
1325
+ import { join as join4 } from "path";
961
1326
 
962
1327
  // src/lib/constants.ts
963
1328
  var CONFIG_FILE_NAME = "nanoforge.config.json";
964
- var NANOFORGE_DIR = ".nanoforge";
1329
+ var MANIFEST_FILE_NAME = "nanoforge.manifest.json";
1330
+ var GLOBAL_CONFIG_FILE_NAME = ".nanoforgerc";
1331
+ var REGISTRY_URL = "https://api.nanoforge.dev";
965
1332
 
966
1333
  // src/lib/utils/object.ts
967
1334
  var isObject = /* @__PURE__ */ __name((item) => {
@@ -982,6 +1349,7 @@ var deepMerge = /* @__PURE__ */ __name((target, ...sources) => {
982
1349
  }
983
1350
  return deepMerge(target, ...sources);
984
1351
  }, "deepMerge");
1352
+ var isEmpty = /* @__PURE__ */ __name((target) => Object.keys(target).length === 0, "isEmpty");
985
1353
 
986
1354
  // src/lib/config/config-defaults.ts
987
1355
  var CONFIG_DEFAULTS = {
@@ -989,25 +1357,36 @@ var CONFIG_DEFAULTS = {
989
1357
  language: "ts",
990
1358
  initFunctions: true,
991
1359
  client: {
1360
+ enable: true,
992
1361
  port: "3000",
993
- gameExposurePort: "3001",
1362
+ outDir: ".nanoforge/client",
994
1363
  build: {
995
- entryFile: "client/main.ts",
996
- outDir: ".nanoforge/client"
1364
+ entry: "client/main.ts",
1365
+ staticDir: "client/static"
997
1366
  },
998
- runtime: {
999
- 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"
1000
1374
  }
1001
1375
  },
1002
1376
  server: {
1003
1377
  enable: false,
1004
- port: "3002",
1378
+ outDir: ".nanoforge/server",
1005
1379
  build: {
1006
- entryFile: "server/main.ts",
1007
- 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"
1008
1386
  },
1009
- runtime: {
1010
- dir: ".nanoforge/server"
1387
+ dirs: {
1388
+ components: "server/components",
1389
+ systems: "server/systems"
1011
1390
  }
1012
1391
  }
1013
1392
  };
@@ -1016,23 +1395,23 @@ var CONFIG_DEFAULTS = {
1016
1395
  var config;
1017
1396
  var getConfigPath = /* @__PURE__ */ __name((directory, name) => {
1018
1397
  if (name) {
1019
- return join3(directory, name);
1398
+ return join4(directory, name);
1020
1399
  } else {
1021
1400
  for (const n of [CONFIG_FILE_NAME]) {
1022
- const path = join3(directory, n);
1401
+ const path = join4(directory, n);
1023
1402
  if (existsSync2(path)) return path;
1024
1403
  }
1025
1404
  throw new Error(`No config file found in directory: ${directory}`);
1026
1405
  }
1027
1406
  }, "getConfigPath");
1028
- var loadConfig = /* @__PURE__ */ __name(async (directory, name) => {
1407
+ var loadConfig = /* @__PURE__ */ __name(async (directory, name, noThrow = false) => {
1029
1408
  if (config) return config;
1030
1409
  let rawData;
1031
1410
  const path = getConfigPath(directory, name);
1032
1411
  try {
1033
1412
  rawData = deepMerge(CONFIG_DEFAULTS, JSON.parse(readFileSync(path, "utf-8")));
1034
1413
  } catch {
1035
- rawData = null;
1414
+ rawData = noThrow ? CONFIG_DEFAULTS : null;
1036
1415
  }
1037
1416
  if (!rawData) throw new Error(`Not able to read config file : ${path}`);
1038
1417
  const data = plainToInstance(Config, rawData, {
@@ -1047,8 +1426,8 @@ ${errors.toString().replace(/,/g, "\n")}`);
1047
1426
  }, "loadConfig");
1048
1427
 
1049
1428
  // src/action/common/config.ts
1050
- var getConfig = /* @__PURE__ */ __name((inputs, dir) => {
1051
- return loadConfig(dir, getConfigInput(inputs));
1429
+ var getConfig = /* @__PURE__ */ __name((inputs, dir, noThrow) => {
1430
+ return loadConfig(dir, getConfigInput(inputs), noThrow);
1052
1431
  }, "getConfig");
1053
1432
 
1054
1433
  // src/action/abstract.action.ts
@@ -1076,7 +1455,7 @@ var AbstractAction = class {
1076
1455
  if (keepAlive) return;
1077
1456
  console.info();
1078
1457
  if (!success2) {
1079
- if (this.failureMessage) console.error(this.failureMessage);
1458
+ handleActionError(this.failureMessage, result.error);
1080
1459
  process.exit(1);
1081
1460
  }
1082
1461
  if (this.successMessage) console.info(this.successMessage);
@@ -1095,40 +1474,53 @@ var BuildAction = class extends AbstractAction {
1095
1474
  async handle(_args, options) {
1096
1475
  const directory = getDirectoryInput(options);
1097
1476
  const config2 = await getConfig(options, directory);
1477
+ const isEditor = getEditorInput(options);
1098
1478
  const isWatch = getWatchInput(options);
1099
- const targets = this.resolveTargets(config2, options);
1479
+ const targets = this.resolveTargets(config2, options, isEditor);
1100
1480
  const results = await this.buildAll(targets, directory, isWatch);
1101
1481
  if (isWatch) {
1102
1482
  return this.enterWatchMode();
1103
1483
  }
1104
1484
  return { success: results.every(Boolean) };
1105
1485
  }
1106
- resolveTargets(config2, options) {
1107
- const targets = [
1108
- this.createTarget(
1109
- "Client",
1110
- config2.client.build,
1111
- "browser",
1112
- getStringInput(options, "clientDirectory")
1113
- )
1114
- ];
1115
- 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)
1116
1503
  targets.push(
1117
1504
  this.createTarget(
1118
1505
  "Server",
1119
- config2.server.build,
1120
1506
  "node",
1121
- 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)
1122
1514
  )
1123
1515
  );
1124
- }
1125
1516
  return targets;
1126
1517
  }
1127
- createTarget(name, config2, platform, outDirOverride) {
1518
+ createTarget(name, platform, entryFile, staticDir, outDir) {
1128
1519
  return {
1129
1520
  name,
1130
- entry: config2.entryFile,
1131
- output: outDirOverride || config2.outDir,
1521
+ entry: entryFile,
1522
+ static: staticDir,
1523
+ output: outDir,
1132
1524
  platform
1133
1525
  };
1134
1526
  }
@@ -1142,17 +1534,18 @@ var BuildAction = class extends AbstractAction {
1142
1534
  }
1143
1535
  async buildTarget(target, directory, isWatch) {
1144
1536
  const packageManager = PackageManagerFactory.create("local_bun" /* LOCAL_BUN */);
1145
- const executeBuild = /* @__PURE__ */ __name((rebuild = false) => runSafe(
1146
- () => 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(
1147
1541
  target.name,
1148
1542
  directory,
1149
1543
  target.entry,
1150
1544
  target.output,
1151
1545
  ["--asset-naming", "[name].[ext]", "--target", target.platform],
1152
1546
  rebuild
1153
- ),
1154
- false
1155
- ), "executeBuild");
1547
+ );
1548
+ }, false), "executeBuild");
1156
1549
  if (isWatch) {
1157
1550
  this.watchDirectory(directory, target.entry, () => executeBuild(true));
1158
1551
  }
@@ -1160,7 +1553,7 @@ var BuildAction = class extends AbstractAction {
1160
1553
  return result !== false;
1161
1554
  }
1162
1555
  watchDirectory(directory, entry, onChange) {
1163
- const watchPath = dirname(join4(getCwd(directory), entry));
1556
+ const watchPath = dirname(join5(getCwd(directory), entry));
1164
1557
  watch(watchPath).on("change", onChange);
1165
1558
  }
1166
1559
  enterWatchMode() {
@@ -1169,43 +1562,16 @@ var BuildAction = class extends AbstractAction {
1169
1562
  console.info();
1170
1563
  return { keepAlive: true };
1171
1564
  }
1172
- };
1173
-
1174
- // src/action/actions/dev.action.ts
1175
- var DevAction = class extends AbstractAction {
1176
- static {
1177
- __name(this, "DevAction");
1178
- }
1179
- startMessage = Messages.DEV_START;
1180
- successMessage = Messages.DEV_SUCCESS;
1181
- failureMessage = Messages.DEV_FAILED;
1182
- async handle(_args, options) {
1183
- const directory = getDirectoryInput(options);
1184
- const generate = getDevGenerateInput(options);
1185
- const tasks = this.buildTaskList(directory, generate);
1186
- await Promise.all(tasks);
1187
- return { keepAlive: true };
1188
- }
1189
- buildTaskList(directory, generate) {
1190
- const tasks = [];
1191
- if (generate) {
1192
- tasks.push(this.runSubCommand("generate", directory, { silent: true }));
1193
- }
1194
- tasks.push(this.runSubCommand("build", directory, { silent: true }));
1195
- tasks.push(this.runSubCommand("start", directory, { silent: false }));
1196
- return tasks;
1565
+ resetOut(outDir, directory) {
1566
+ resetFolder(getCwd(join5(directory, outDir)));
1197
1567
  }
1198
- async runSubCommand(command, directory, options) {
1199
- await runSafe(async () => {
1200
- const packageManager = await PackageManagerFactory.find(directory);
1201
- await packageManager.runDev(directory, "nf", {}, [command, "--watch"], options.silent);
1202
- });
1568
+ copyFiles(target, directory) {
1569
+ const from = getCwd(join5(directory, target.static));
1570
+ const to = getCwd(join5(directory, target.output));
1571
+ copyFiles(from, to);
1203
1572
  }
1204
1573
  };
1205
1574
 
1206
- // src/action/actions/generate.action.ts
1207
- import { join as join5 } from "path";
1208
-
1209
1575
  // src/lib/schematics/abstract.collection.ts
1210
1576
  var AbstractCollection = class {
1211
1577
  constructor(collection, runner, cwd2) {
@@ -1213,6 +1579,9 @@ var AbstractCollection = class {
1213
1579
  this.runner = runner;
1214
1580
  this.cwd = cwd2;
1215
1581
  }
1582
+ collection;
1583
+ runner;
1584
+ cwd;
1216
1585
  static {
1217
1586
  __name(this, "AbstractCollection");
1218
1587
  }
@@ -1262,6 +1631,16 @@ var NanoforgeCollection = class _NanoforgeCollection extends AbstractCollection
1262
1631
  name: "docker",
1263
1632
  alias: "docker",
1264
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"
1265
1644
  }
1266
1645
  ];
1267
1646
  constructor(runner, cwd2) {
@@ -1305,6 +1684,7 @@ var CollectionFactory = class {
1305
1684
  var toKebabCase = /* @__PURE__ */ __name((str) => {
1306
1685
  return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
1307
1686
  }, "toKebabCase");
1687
+ var capitalize = /* @__PURE__ */ __name((str) => str.charAt(0).toUpperCase() + str.slice(1), "capitalize");
1308
1688
 
1309
1689
  // src/lib/schematics/schematic.option.ts
1310
1690
  var SchematicOption = class {
@@ -1312,6 +1692,8 @@ var SchematicOption = class {
1312
1692
  this.name = name;
1313
1693
  this.value = value;
1314
1694
  }
1695
+ name;
1696
+ value;
1315
1697
  static {
1316
1698
  __name(this, "SchematicOption");
1317
1699
  }
@@ -1382,64 +1764,131 @@ var mapSchematicOptions = /* @__PURE__ */ __name((inputs) => {
1382
1764
  }, []);
1383
1765
  }, "mapSchematicOptions");
1384
1766
 
1385
- // src/action/actions/generate.action.ts
1386
- var GenerateAction = class extends AbstractAction {
1767
+ // src/action/actions/create.action.ts
1768
+ var CreateAction = class extends AbstractAction {
1387
1769
  static {
1388
- __name(this, "GenerateAction");
1770
+ __name(this, "CreateAction");
1389
1771
  }
1390
- startMessage = Messages.GENERATE_START;
1391
- successMessage = Messages.GENERATE_SUCCESS;
1392
- failureMessage = Messages.GENERATE_FAILED;
1393
- async handle(_args, options) {
1772
+ startMessage = Messages.CREATE_START;
1773
+ successMessage = Messages.CREATE_SUCCESS;
1774
+ failureMessage = Messages.CREATE_FAILED;
1775
+ async handle(args, options) {
1394
1776
  const directory = getDirectoryInput(options);
1395
- const config2 = await getConfig(options, directory);
1396
- const isWatch = getWatchInput(options);
1397
- const values = this.extractValues(config2);
1398
- await this.generateParts(values, directory, isWatch);
1399
- if (isWatch) {
1400
- return this.enterWatchMode();
1401
- }
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
+
1831
+ // src/action/actions/generate.action.ts
1832
+ import { join as join6 } from "path";
1833
+ var GenerateAction = class extends AbstractAction {
1834
+ static {
1835
+ __name(this, "GenerateAction");
1836
+ }
1837
+ startMessage = Messages.GENERATE_START;
1838
+ successMessage = Messages.GENERATE_SUCCESS;
1839
+ failureMessage = Messages.GENERATE_FAILED;
1840
+ async handle(_args, options) {
1841
+ const directory = getDirectoryInput(options);
1842
+ const config2 = await getConfig(options, directory);
1843
+ const isEditor = getEditorInput(options);
1844
+ const isWatch = getWatchInput(options);
1845
+ await this.generateParts(config2, directory, isEditor, isWatch);
1846
+ if (isWatch) {
1847
+ return this.enterWatchMode();
1848
+ }
1402
1849
  return {};
1403
1850
  }
1404
1851
  extractValues(config2) {
1405
1852
  return {
1406
- name: config2.name,
1407
1853
  directory: ".",
1408
1854
  language: config2.language,
1409
- server: config2.server.enable,
1410
1855
  initFunctions: config2.initFunctions
1411
1856
  };
1412
1857
  }
1413
- async generateParts(values, directory, watch3) {
1858
+ async generateParts(config2, directory, isEditor, watch3) {
1414
1859
  const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1415
- const baseOptions = this.baseSchematicOptions(values);
1416
- await executeSchematic(
1417
- "Client main file",
1418
- collection,
1419
- "part-main",
1420
- { ...baseOptions, part: "client" },
1421
- watch3 ? this.watchPath(directory, values.directory, "client") : void 0
1422
- );
1423
- 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)
1424
1876
  await executeSchematic(
1425
1877
  "Server main file",
1426
1878
  collection,
1427
1879
  "part-main",
1428
- { ...baseOptions, part: "server" },
1429
- 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)
1430
1888
  );
1431
- }
1432
1889
  }
1433
- baseSchematicOptions(values) {
1434
- return {
1435
- name: values.name,
1436
- directory: values.directory,
1437
- language: values.language,
1438
- initFunctions: values.initFunctions
1439
- };
1440
- }
1441
- watchPath(directory, subDir, part) {
1442
- return join5(getCwd(directory), subDir, NANOFORGE_DIR, `${part}.save.json`);
1890
+ watchPath(directory, subDir, saveFile) {
1891
+ return join6(getCwd(directory), subDir, saveFile);
1443
1892
  }
1444
1893
  enterWatchMode() {
1445
1894
  console.info();
@@ -1449,6 +1898,310 @@ var GenerateAction = class extends AbstractAction {
1449
1898
  }
1450
1899
  };
1451
1900
 
1901
+ // src/action/actions/install.action.ts
1902
+ import { join as join8 } from "path";
1903
+
1904
+ // src/lib/global-config/global-config-handler.ts
1905
+ import { read, readUser, write, writeUser } from "rc9";
1906
+
1907
+ // src/lib/global-config/global-config-defaults.ts
1908
+ var GLOBAL_CONFIG_DEFAULTS = {};
1909
+
1910
+ // src/lib/global-config/global-config-handler.ts
1911
+ var GlobalConfigHandler = class {
1912
+ static {
1913
+ __name(this, "GlobalConfigHandler");
1914
+ }
1915
+ static read(dir) {
1916
+ const localConfig = this._readConfig(read, false, dir);
1917
+ if (localConfig) return localConfig;
1918
+ return this._readConfig(readUser, true);
1919
+ }
1920
+ static write(config2, local = false, dir) {
1921
+ const options = {
1922
+ name: GLOBAL_CONFIG_FILE_NAME,
1923
+ dir
1924
+ };
1925
+ if (local) write(config2, options);
1926
+ else writeUser(config2, options);
1927
+ }
1928
+ static _readConfig(func, force, dir) {
1929
+ const res = func({
1930
+ name: GLOBAL_CONFIG_FILE_NAME,
1931
+ dir
1932
+ });
1933
+ if (!force) {
1934
+ if (isEmpty(res)) return null;
1935
+ }
1936
+ return deepMerge(GLOBAL_CONFIG_DEFAULTS, res);
1937
+ }
1938
+ };
1939
+
1940
+ // src/lib/http/http-client.ts
1941
+ var HttpClient = class {
1942
+ static {
1943
+ __name(this, "HttpClient");
1944
+ }
1945
+ _baseUrl;
1946
+ _baseOptions;
1947
+ _middlewares;
1948
+ constructor(baseUrl, options) {
1949
+ this._baseUrl = baseUrl;
1950
+ this._baseOptions = options ?? {
1951
+ headers: {
1952
+ "Content-Type": "application/json"
1953
+ }
1954
+ };
1955
+ this._middlewares = [];
1956
+ }
1957
+ get(path, options) {
1958
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1959
+ return this._request(newPath, {
1960
+ ...newOptions,
1961
+ method: "GET"
1962
+ });
1963
+ });
1964
+ }
1965
+ post(path, body, options) {
1966
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1967
+ return this._request(newPath, {
1968
+ ...newOptions,
1969
+ method: "POST",
1970
+ body
1971
+ });
1972
+ });
1973
+ }
1974
+ put(path, body, options) {
1975
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1976
+ return this._request(newPath, {
1977
+ ...newOptions,
1978
+ method: "PUT",
1979
+ body
1980
+ });
1981
+ });
1982
+ }
1983
+ patch(path, body, options) {
1984
+ return this._applyMiddlewares(path, options, async (newPath, newOptions) => {
1985
+ return this._request(newPath, {
1986
+ ...newOptions,
1987
+ method: "PATCH",
1988
+ body
1989
+ });
1990
+ });
1991
+ }
1992
+ delete(path, options) {
1993
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1994
+ return this._request(newPath, {
1995
+ ...newOptions,
1996
+ method: "DELETE",
1997
+ body: "{}"
1998
+ });
1999
+ });
2000
+ }
2001
+ useMiddlewares(...middlewares) {
2002
+ for (const middleware of middlewares) this._middlewares.push(middleware);
2003
+ return this;
2004
+ }
2005
+ async _request(path, request) {
2006
+ const res = await fetch(path, request);
2007
+ res.content = null;
2008
+ return res;
2009
+ }
2010
+ _applyMiddlewares(path, options, callback) {
2011
+ const baseParams = {
2012
+ path,
2013
+ fullPath: this._getUrl(path),
2014
+ options: {
2015
+ ...this._baseOptions,
2016
+ ...options,
2017
+ headers: {
2018
+ ...this._baseOptions.headers,
2019
+ ...options?.headers
2020
+ }
2021
+ }
2022
+ };
2023
+ const middlewares = this._middlewares.slice();
2024
+ let response;
2025
+ const execution = /* @__PURE__ */ __name(async (params) => {
2026
+ if (!params) params = baseParams;
2027
+ const middleware = middlewares.shift();
2028
+ if (!middleware) response = await callback(params.fullPath, params.options);
2029
+ else response = await middleware(params, execution) ?? response;
2030
+ return response;
2031
+ }, "execution");
2032
+ return execution(baseParams);
2033
+ }
2034
+ _getUrl(path) {
2035
+ return `${this._baseUrl}${path}`;
2036
+ }
2037
+ };
2038
+
2039
+ // src/lib/http/repository.ts
2040
+ var Repository = class {
2041
+ static {
2042
+ __name(this, "Repository");
2043
+ }
2044
+ _client;
2045
+ constructor(client2) {
2046
+ this._client = client2;
2047
+ }
2048
+ get(path, options) {
2049
+ return this.runRequest("get", path, options);
2050
+ }
2051
+ getFile(path, options) {
2052
+ return this.runFileRequest("get", path, options);
2053
+ }
2054
+ post(path, body, options) {
2055
+ return this.runRequestBody("post", path, body ?? {}, options);
2056
+ }
2057
+ put(path, body, options) {
2058
+ return this.runRequestBody("put", path, body ?? {}, options);
2059
+ }
2060
+ patch(path, body, options) {
2061
+ return this.runRequestBody("patch", path, body ?? {}, options);
2062
+ }
2063
+ delete(path, options) {
2064
+ return this.runRequest("delete", path, options);
2065
+ }
2066
+ async runRequest(request, path, options) {
2067
+ const res = await this._client[request](path, options);
2068
+ const data = await res.json();
2069
+ if (!res.ok)
2070
+ throw new Error(`Request failed with status code ${res.status}`, {
2071
+ cause: data["error"]
2072
+ });
2073
+ return data;
2074
+ }
2075
+ async runFileRequest(request, path, options) {
2076
+ const res = await this._client[request](path, options);
2077
+ if (!res.ok)
2078
+ throw new Error(`Request failed with status code ${res.status}`, {
2079
+ cause: (await res.json())["error"]
2080
+ });
2081
+ return await res.blob();
2082
+ }
2083
+ async runRequestBody(request, path, body, options) {
2084
+ const res = await this._client[request](
2085
+ path,
2086
+ body === void 0 ? void 0 : body instanceof FormData ? body : JSON.stringify(body),
2087
+ options
2088
+ );
2089
+ const data = await res.json();
2090
+ if (!res.ok)
2091
+ throw new Error(`Request failed with status code ${res.status}`, {
2092
+ cause: data["error"]
2093
+ });
2094
+ return data;
2095
+ }
2096
+ };
2097
+
2098
+ // src/lib/http/client.ts
2099
+ var client = new HttpClient(REGISTRY_URL ?? "");
2100
+ var api = new Repository(client);
2101
+ var withAuth = /* @__PURE__ */ __name((apiKey, force = false, headers = {
2102
+ "Content-Type": "application/json"
2103
+ }) => {
2104
+ if (!apiKey && force) {
2105
+ console.error("No registry key found. Please use `nf login` to login");
2106
+ throw new Error("No apikey found. Please use `nf login` to login");
2107
+ }
2108
+ return new Repository(
2109
+ new HttpClient(REGISTRY_URL ?? "", {
2110
+ headers: {
2111
+ Authorization: apiKey,
2112
+ ...headers
2113
+ }
2114
+ })
2115
+ );
2116
+ }, "withAuth");
2117
+
2118
+ // src/lib/manifest/manifest-resolver.ts
2119
+ var resolveManifestDependencies = /* @__PURE__ */ __name(async (names, dir) => {
2120
+ const client2 = withAuth(GlobalConfigHandler.read(dir).apiKey, false);
2121
+ return concatDeps(await Promise.all(names.map(async (d) => resolveDeps(d, client2))));
2122
+ }, "resolveManifestDependencies");
2123
+ var resolveManifest = /* @__PURE__ */ __name(async (name, client2) => {
2124
+ return await client2.get(`/registry/${name}`);
2125
+ }, "resolveManifest");
2126
+ var resolveDeps = /* @__PURE__ */ __name(async (name, client2) => {
2127
+ const manifest = await resolveManifest(name, client2);
2128
+ const baseDeps = manifest.dependencies ?? [];
2129
+ const deps = await Promise.all(baseDeps.map(async (d) => resolveDeps(d, client2)));
2130
+ return concatDeps(
2131
+ [{ nf: { [manifest.name]: manifest }, npm: getNpmDeps(manifest) }].concat(deps)
2132
+ );
2133
+ }, "resolveDeps");
2134
+ var getNpmDeps = /* @__PURE__ */ __name((manifest) => {
2135
+ return Object.entries(manifest.npmDependencies ?? {});
2136
+ }, "getNpmDeps");
2137
+ var concatDeps = /* @__PURE__ */ __name((deps) => {
2138
+ return {
2139
+ npm: deps.map(({ npm }) => npm).flat(),
2140
+ nf: Object.fromEntries(deps.map(({ nf }) => Object.entries(nf)).flat())
2141
+ };
2142
+ }, "concatDeps");
2143
+
2144
+ // src/lib/registry/registry.ts
2145
+ import fs4 from "fs";
2146
+ import { join as join7 } from "path";
2147
+ var Registry = class {
2148
+ static {
2149
+ __name(this, "Registry");
2150
+ }
2151
+ static async publish(manifest, dir) {
2152
+ const client2 = this._getClient(dir, true, false);
2153
+ const filename = manifest.publish?.paths?.package ?? "index.ts";
2154
+ const file = await this._getPackageFile(filename, dir);
2155
+ const data = new FormData();
2156
+ for (const key of Object.keys(manifest)) {
2157
+ const value = manifest[key];
2158
+ if (!value) continue;
2159
+ data.append(key, typeof value === "string" ? value : JSON.stringify(value));
2160
+ }
2161
+ data.append("_packageFile", file, filename);
2162
+ await client2.put(`/registry/${manifest.name}`, data);
2163
+ }
2164
+ static async unpublish(manifest, dir) {
2165
+ const client2 = this._getClient(dir, true);
2166
+ await client2.delete(`/registry/${manifest.name}`);
2167
+ }
2168
+ static async install(manifests, dir) {
2169
+ const cwd2 = getCwd(dir);
2170
+ const client2 = this._getClient(dir, false);
2171
+ for (const manifest of manifests) {
2172
+ await this.installPackage(client2, manifest, cwd2);
2173
+ }
2174
+ }
2175
+ static async installPackage(client2, manifest, dir) {
2176
+ const file = await client2.getFile(`/registry/${manifest.name}/-/${manifest._file}`);
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());
2180
+ }
2181
+ static getTypeSubFolder(type) {
2182
+ if (type === "component") return "components";
2183
+ if (type === "system") return "systems";
2184
+ return ".";
2185
+ }
2186
+ static _getClient(dir, force, headers = true) {
2187
+ const config2 = GlobalConfigHandler.read(dir);
2188
+ return withAuth(config2.apiKey, force, !headers ? {} : void 0);
2189
+ }
2190
+ static _getPackageFile(filename, dir) {
2191
+ const path = join7(getCwd(dir ?? "."), filename);
2192
+ if (!fs4.existsSync(path))
2193
+ throw new Error(
2194
+ "Package not found, please specify path in the nanoforge.manifest.json : `publish.paths.package`!"
2195
+ );
2196
+ try {
2197
+ fs4.accessSync(path, fs4.constants.R_OK);
2198
+ return fs4.openAsBlob(path);
2199
+ } catch {
2200
+ throw new Error("Cannot read package file, please verify your file permissions!");
2201
+ }
2202
+ }
2203
+ };
2204
+
1452
2205
  // src/action/actions/install.action.ts
1453
2206
  var InstallAction = class extends AbstractAction {
1454
2207
  static {
@@ -1460,114 +2213,78 @@ var InstallAction = class extends AbstractAction {
1460
2213
  async handle(args, options) {
1461
2214
  const names = await getInstallNamesInputOrAsk(args);
1462
2215
  const directory = getDirectoryInput(options);
2216
+ const isLib = getInstallLibInput(options);
2217
+ const isServer = getServerInput(options);
2218
+ return isLib ? this._installLibs(directory, names) : this._installNfPackages(directory, names, isServer);
2219
+ }
2220
+ async _installLibs(directory, names) {
1463
2221
  const packageManager = await PackageManagerFactory.find(directory);
1464
- const success2 = await packageManager.addProduction(directory, names);
1465
- return { success: success2 };
2222
+ return { success: await packageManager.addDevelopment(directory, names) };
2223
+ }
2224
+ async _installNfPackages(directory, names, isServer) {
2225
+ const deps = await resolveManifestDependencies(names, directory);
2226
+ const libSuccess = await this._installLibs(
2227
+ directory,
2228
+ deps.npm.map(([name, version]) => `${name}@${version}`)
2229
+ );
2230
+ if (!libSuccess) return { success: false };
2231
+ return withSpinner(Messages.INSTALL_PACKAGES_IN_PROGRESS, async () => {
2232
+ await Registry.install(
2233
+ Object.values(deps.nf),
2234
+ join8(directory, isServer ? "server" : "client")
2235
+ );
2236
+ });
1466
2237
  }
1467
2238
  };
1468
2239
 
1469
- // src/action/actions/new.action.ts
1470
- import { join as join6 } from "path";
1471
-
1472
- // src/lib/input/inputs/new/docker.input.ts
1473
- var getDockerInput = /* @__PURE__ */ __name((inputs) => {
1474
- return getBooleanInput(inputs, "docker");
1475
- }, "getDockerInput");
1476
- var getDockerOrAsk = /* @__PURE__ */ __name((inputs) => {
1477
- return getInputOrAsk(
1478
- getDockerInput(inputs),
1479
- () => askConfirm(Messages.NEW_DOCKER_QUESTION, { default: true })
1480
- );
1481
- }, "getDockerOrAsk");
1482
-
1483
- // src/lib/input/inputs/new/init-functions.input.ts
1484
- var getNewInitFunctionsWithDefault = /* @__PURE__ */ __name((inputs) => {
1485
- return getBooleanInputWithDefault(inputs, "initFunctions", false);
1486
- }, "getNewInitFunctionsWithDefault");
1487
-
1488
- // src/lib/input/inputs/new/language.input.ts
1489
- var getLanguageInput = /* @__PURE__ */ __name((inputs) => {
1490
- return getStringInput(inputs, "language");
1491
- }, "getLanguageInput");
1492
- var getNewLanguageInputOrAsk = /* @__PURE__ */ __name((inputs) => {
1493
- return getInputOrAsk(
1494
- getLanguageInput(inputs),
1495
- () => askSelect(Messages.NEW_LANGUAGE_QUESTION, [{ value: "ts" }, { value: "js" }], {
1496
- default: "ts"
1497
- })
1498
- );
1499
- }, "getNewLanguageInputOrAsk");
1500
-
1501
- // src/lib/input/inputs/new/name.input.ts
1502
- var getNameInput = /* @__PURE__ */ __name((inputs) => {
1503
- return getStringInput(inputs, "name");
1504
- }, "getNameInput");
1505
- var getNewNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
1506
- return getInputOrAsk(
1507
- getNameInput(inputs),
1508
- () => askInput(Messages.NEW_NAME_QUESTION, {
1509
- required: true,
1510
- default: "nanoforge-app"
1511
- })
1512
- );
1513
- }, "getNewNameInputOrAsk");
1514
-
1515
- // src/lib/input/inputs/new/package-manager.input.ts
1516
- var getPackageManagerInput = /* @__PURE__ */ __name((inputs) => {
1517
- return getStringInput(inputs, "packageManager");
1518
- }, "getPackageManagerInput");
1519
- var getNewPackageManagerInputOrAsk = /* @__PURE__ */ __name((inputs) => {
1520
- return getInputOrAsk(
1521
- getPackageManagerInput(inputs),
1522
- () => askSelect(
1523
- Messages.NEW_PACKAGE_MANAGER_QUESTION,
1524
- [{ value: "npm" }, { value: "yarn" }, { value: "pnpm" }, { value: "bun" }],
2240
+ // src/action/actions/login.action.ts
2241
+ var LoginAction = class extends AbstractAction {
2242
+ static {
2243
+ __name(this, "LoginAction");
2244
+ }
2245
+ startMessage = Messages.LOGIN_START;
2246
+ successMessage = Messages.LOGIN_SUCCESS;
2247
+ failureMessage = Messages.LOGIN_FAILED;
2248
+ async handle(_args, options) {
2249
+ const directory = getDirectoryInput(options);
2250
+ const isLocal = getLocalInput(options);
2251
+ const apiKey = await getLoginApiKeyInputOrAsk(options);
2252
+ await withAuth(apiKey, true).post("/registry-key/verify");
2253
+ GlobalConfigHandler.write(
1525
2254
  {
1526
- default: "npm"
1527
- }
1528
- )
1529
- );
1530
- }, "getNewPackageManagerInputOrAsk");
1531
-
1532
- // src/lib/input/inputs/new/path.input.ts
1533
- var getNewPathInput = /* @__PURE__ */ __name((inputs) => {
1534
- return getStringInput(inputs, "path");
1535
- }, "getNewPathInput");
1536
-
1537
- // src/lib/input/inputs/new/server.input.ts
1538
- var getNewServerInput = /* @__PURE__ */ __name((inputs) => {
1539
- return getBooleanInput(inputs, "server");
1540
- }, "getNewServerInput");
1541
- var getNewServerOrAsk = /* @__PURE__ */ __name((inputs) => {
1542
- return getInputOrAsk(
1543
- getNewServerInput(inputs),
1544
- () => askConfirm(Messages.NEW_SERVER_QUESTION, { default: false })
1545
- );
1546
- }, "getNewServerOrAsk");
1547
-
1548
- // src/lib/input/inputs/new/skip-install.input.ts
1549
- var getNewSkipInstallInput = /* @__PURE__ */ __name((inputs) => {
1550
- return getBooleanInput(inputs, "skipInstall");
1551
- }, "getNewSkipInstallInput");
1552
- var getNewSkipInstallOrAsk = /* @__PURE__ */ __name((inputs) => {
1553
- return getInputOrAsk(
1554
- getNewSkipInstallInput(inputs),
1555
- () => askConfirm(Messages.NEW_SKIP_INSTALL_QUESTION, { default: false })
1556
- );
1557
- }, "getNewSkipInstallOrAsk");
2255
+ apiKey
2256
+ },
2257
+ isLocal,
2258
+ directory
2259
+ );
2260
+ return { success: true };
2261
+ }
2262
+ };
1558
2263
 
1559
- // src/lib/input/inputs/new/strict.input.ts
1560
- var getNewStrictInput = /* @__PURE__ */ __name((inputs) => {
1561
- return getBooleanInput(inputs, "strict");
1562
- }, "getNewStrictInput");
1563
- var getNewStrictOrAsk = /* @__PURE__ */ __name((inputs) => {
1564
- return getInputOrAsk(
1565
- getNewStrictInput(inputs),
1566
- () => askConfirm(Messages.NEW_STRICT_QUESTION, { default: true })
1567
- );
1568
- }, "getNewStrictOrAsk");
2264
+ // src/action/actions/logout.action.ts
2265
+ var LogoutAction = class extends AbstractAction {
2266
+ static {
2267
+ __name(this, "LogoutAction");
2268
+ }
2269
+ startMessage = Messages.LOGOUT_START;
2270
+ successMessage = Messages.LOGOUT_SUCCESS;
2271
+ failureMessage = Messages.LOGOUT_FAILED;
2272
+ async handle(_args, options) {
2273
+ const directory = getDirectoryInput(options);
2274
+ const isLocal = getLocalInput(options);
2275
+ GlobalConfigHandler.write(
2276
+ {
2277
+ apiKey: void 0
2278
+ },
2279
+ isLocal,
2280
+ directory
2281
+ );
2282
+ return { success: true };
2283
+ }
2284
+ };
1569
2285
 
1570
2286
  // src/action/actions/new.action.ts
2287
+ import { join as join9 } from "path";
1571
2288
  var NewAction = class extends AbstractAction {
1572
2289
  static {
1573
2290
  __name(this, "NewAction");
@@ -1576,26 +2293,31 @@ var NewAction = class extends AbstractAction {
1576
2293
  successMessage = Messages.NEW_SUCCESS;
1577
2294
  failureMessage = Messages.NEW_FAILED;
1578
2295
  async handle(_args, options) {
1579
- const directory = getDirectoryInput(options);
2296
+ const cwdDirectory = getDirectoryInput(options);
1580
2297
  const values = await this.collectValues(options);
1581
- await this.scaffold(values, directory);
2298
+ await this.scaffold(values, cwdDirectory);
1582
2299
  let res = true;
1583
2300
  if (!values.skipInstall) {
1584
- res = await this.installDependencies(values.packageManager, join6(directory, values.name));
2301
+ res = await this.installDependencies(
2302
+ values.packageManager,
2303
+ join9(cwdDirectory, values.directory ?? values.name)
2304
+ );
1585
2305
  }
1586
2306
  return { success: res };
1587
2307
  }
1588
2308
  async collectValues(inputs) {
1589
2309
  return {
1590
2310
  name: await getNewNameInputOrAsk(inputs),
1591
- directory: getNewPathInput(inputs),
2311
+ directory: getPathInput(inputs),
1592
2312
  packageManager: await getNewPackageManagerInputOrAsk(inputs),
1593
2313
  language: await getNewLanguageInputOrAsk(inputs),
1594
2314
  strict: await getNewStrictOrAsk(inputs),
1595
2315
  server: await getNewServerOrAsk(inputs),
1596
2316
  initFunctions: getNewInitFunctionsWithDefault(inputs),
1597
2317
  skipInstall: await getNewSkipInstallOrAsk(inputs),
1598
- docker: await getDockerOrAsk(inputs)
2318
+ docker: await getNewDockerOrAsk(inputs),
2319
+ lint: getNewLintInput(inputs),
2320
+ editor: getEditorInput(inputs)
1599
2321
  };
1600
2322
  }
1601
2323
  async scaffold(values, directory) {
@@ -1617,14 +2339,18 @@ var NewAction = class extends AbstractAction {
1617
2339
  packageManager: values.packageManager,
1618
2340
  language: values.language,
1619
2341
  strict: values.strict,
1620
- server: values.server
2342
+ server: values.server,
2343
+ lint: values.lint,
2344
+ editor: values.editor
1621
2345
  });
1622
2346
  }
1623
2347
  generateConfiguration(collection, values) {
1624
2348
  return executeSchematic("Configuration", collection, "configuration", {
1625
2349
  name: values.name,
1626
- directory: values.directory,
1627
- server: values.server
2350
+ directory: values.directory ?? values.name,
2351
+ server: values.server,
2352
+ language: values.language,
2353
+ initFunctions: values.initFunctions
1628
2354
  });
1629
2355
  }
1630
2356
  async generateClientParts(collection, values) {
@@ -1633,7 +2359,9 @@ var NewAction = class extends AbstractAction {
1633
2359
  ...partOptions,
1634
2360
  server: values.server
1635
2361
  });
1636
- await executeSchematic("Client main file", collection, "part-main", partOptions);
2362
+ await executeSchematic("Client main file", collection, "part-main", {
2363
+ ...partOptions
2364
+ });
1637
2365
  }
1638
2366
  async generateServerParts(collection, values) {
1639
2367
  const partOptions = this.partOptions(values, "server");
@@ -1641,20 +2369,20 @@ var NewAction = class extends AbstractAction {
1641
2369
  ...partOptions,
1642
2370
  server: values.server
1643
2371
  });
1644
- await executeSchematic("Server main file", collection, "part-main", partOptions);
2372
+ await executeSchematic("Server main file", collection, "part-main", {
2373
+ ...partOptions
2374
+ });
1645
2375
  }
1646
2376
  async generateDocker(collection, values) {
1647
2377
  await executeSchematic("Docker", collection, "docker", {
1648
- name: values.name,
1649
- directory: values.directory,
2378
+ directory: values.directory ?? values.name,
1650
2379
  packageManager: values.packageManager
1651
2380
  });
1652
2381
  }
1653
2382
  partOptions(values, part) {
1654
2383
  return {
1655
- name: values.name,
1656
2384
  part,
1657
- directory: values.directory,
2385
+ directory: values.directory ?? values.name,
1658
2386
  language: values.language,
1659
2387
  initFunctions: values.initFunctions
1660
2388
  };
@@ -1665,8 +2393,149 @@ var NewAction = class extends AbstractAction {
1665
2393
  }
1666
2394
  };
1667
2395
 
2396
+ // src/lib/manifest/manifest.type.ts
2397
+ import { Expose as Expose2, Type as Type2 } from "class-transformer";
2398
+ import {
2399
+ IsEnum as IsEnum2,
2400
+ IsNotEmpty as IsNotEmpty2,
2401
+ IsObject,
2402
+ IsOptional,
2403
+ IsString as IsString2,
2404
+ Matches,
2405
+ ValidateNested as ValidateNested2
2406
+ } from "class-validator";
2407
+ var ManifestPackageTypeEnum = /* @__PURE__ */ ((ManifestPackageTypeEnum2) => {
2408
+ ManifestPackageTypeEnum2["COMPONENT"] = "component";
2409
+ ManifestPackageTypeEnum2["SYSTEM"] = "system";
2410
+ return ManifestPackageTypeEnum2;
2411
+ })(ManifestPackageTypeEnum || {});
2412
+ var PathsPublishManifest = class {
2413
+ static {
2414
+ __name(this, "PathsPublishManifest");
2415
+ }
2416
+ package;
2417
+ };
2418
+ __decorateClass([
2419
+ Expose2(),
2420
+ IsString2(),
2421
+ IsNotEmpty2(),
2422
+ IsOptional()
2423
+ ], PathsPublishManifest.prototype, "package", 2);
2424
+ var PublishManifest = class {
2425
+ static {
2426
+ __name(this, "PublishManifest");
2427
+ }
2428
+ paths;
2429
+ };
2430
+ __decorateClass([
2431
+ Expose2(),
2432
+ IsOptional(),
2433
+ ValidateNested2(),
2434
+ Type2(() => PathsPublishManifest)
2435
+ ], PublishManifest.prototype, "paths", 2);
2436
+ var Manifest = class {
2437
+ static {
2438
+ __name(this, "Manifest");
2439
+ }
2440
+ name;
2441
+ type;
2442
+ description;
2443
+ tags;
2444
+ dependencies;
2445
+ publish;
2446
+ npmDependencies;
2447
+ };
2448
+ __decorateClass([
2449
+ Expose2(),
2450
+ IsString2(),
2451
+ Matches(/^[A-Za-z0-9-]+\/[A-Za-z0-9-]+$/),
2452
+ IsNotEmpty2()
2453
+ ], Manifest.prototype, "name", 2);
2454
+ __decorateClass([
2455
+ Expose2(),
2456
+ IsString2(),
2457
+ IsEnum2(ManifestPackageTypeEnum),
2458
+ IsNotEmpty2()
2459
+ ], Manifest.prototype, "type", 2);
2460
+ __decorateClass([
2461
+ Expose2(),
2462
+ IsString2(),
2463
+ IsOptional()
2464
+ ], Manifest.prototype, "description", 2);
2465
+ __decorateClass([
2466
+ Expose2(),
2467
+ IsString2({ each: true }),
2468
+ IsOptional()
2469
+ ], Manifest.prototype, "tags", 2);
2470
+ __decorateClass([
2471
+ Expose2(),
2472
+ IsString2({ each: true }),
2473
+ IsNotEmpty2({ each: true }),
2474
+ IsOptional()
2475
+ ], Manifest.prototype, "dependencies", 2);
2476
+ __decorateClass([
2477
+ Expose2(),
2478
+ IsOptional(),
2479
+ ValidateNested2(),
2480
+ Type2(() => PublishManifest)
2481
+ ], Manifest.prototype, "publish", 2);
2482
+ __decorateClass([
2483
+ Expose2(),
2484
+ IsObject(),
2485
+ IsOptional()
2486
+ ], Manifest.prototype, "npmDependencies", 2);
2487
+
2488
+ // src/lib/manifest/manifest-loader.ts
2489
+ import { plainToInstance as plainToInstance2 } from "class-transformer";
2490
+ import { validate as validate2 } from "class-validator";
2491
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2492
+ import { join as join10 } from "path";
2493
+ var getManifestPath = /* @__PURE__ */ __name((directory) => {
2494
+ for (const n of [MANIFEST_FILE_NAME]) {
2495
+ const path = join10(directory, n);
2496
+ if (existsSync3(path)) return path;
2497
+ }
2498
+ throw new Error(`No manifest file found in directory: ${directory}`);
2499
+ }, "getManifestPath");
2500
+ var loadManifest = /* @__PURE__ */ __name(async (directory) => {
2501
+ let rawData;
2502
+ const path = getManifestPath(directory);
2503
+ try {
2504
+ rawData = deepMerge({}, JSON.parse(readFileSync2(path, "utf-8")));
2505
+ } catch {
2506
+ rawData = null;
2507
+ }
2508
+ if (!rawData) throw new Error(`Not able to read manifest file : ${path}`);
2509
+ const data = plainToInstance2(Manifest, rawData, {
2510
+ excludeExtraneousValues: true
2511
+ });
2512
+ const errors = await validate2(data);
2513
+ if (errors.length > 0)
2514
+ throw new Error(`Invalid manifest
2515
+ ${errors.toString().replace(/,/g, "\n")}`);
2516
+ return data;
2517
+ }, "loadManifest");
2518
+
2519
+ // src/action/actions/publish.action.ts
2520
+ var PublishAction = class extends AbstractAction {
2521
+ static {
2522
+ __name(this, "PublishAction");
2523
+ }
2524
+ startMessage = Messages.PUBLISH_START;
2525
+ successMessage = Messages.PUBLISH_SUCCESS;
2526
+ failureMessage = Messages.PUBLISH_FAILED;
2527
+ async handle(_args, options) {
2528
+ const directory = getDirectoryInput(options);
2529
+ const manifest = await loadManifest(directory);
2530
+ return withSpinner(Messages.PUBLISH_IN_PROGRESS(manifest.name), async () => {
2531
+ await Registry.publish(manifest, directory);
2532
+ });
2533
+ }
2534
+ };
2535
+
1668
2536
  // src/action/actions/start.action.ts
1669
- import { join as join7 } from "path";
2537
+ import dotenv from "dotenv";
2538
+ import { join as join11, resolve as resolve3 } from "path";
1670
2539
  var StartAction = class extends AbstractAction {
1671
2540
  static {
1672
2541
  __name(this, "StartAction");
@@ -1677,20 +2546,21 @@ var StartAction = class extends AbstractAction {
1677
2546
  async handle(_args, options) {
1678
2547
  const directory = getDirectoryInput(options);
1679
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);
1680
2551
  const watch3 = getWatchInput(options);
1681
- const ports = this.resolvePorts(options, config2);
2552
+ const port = getStringInputWithDefault(options, "port", config2.client.port);
1682
2553
  const ssl = this.resolveSSL(options);
1683
- const tasks = this.buildStartTasks(config2, directory, watch3, ports, ssl);
2554
+ const tasks = this.buildStartTasks(config2, directory, {
2555
+ clientDir,
2556
+ serverDir,
2557
+ watch: watch3,
2558
+ port,
2559
+ ssl
2560
+ });
1684
2561
  await Promise.all(tasks);
1685
2562
  return { keepAlive: true };
1686
2563
  }
1687
- resolvePorts(options, config2) {
1688
- return {
1689
- clientPort: getStringInputWithDefault(options, "clientPort", config2.client.port),
1690
- gameExposurePort: getStringInput(options, "gameExposurePort"),
1691
- serverPort: getStringInput(options, "serverPort")
1692
- };
1693
- }
1694
2564
  resolveSSL(options) {
1695
2565
  const cert = getStringInput(options, "cert");
1696
2566
  const key = getStringInput(options, "key");
@@ -1702,53 +2572,100 @@ var StartAction = class extends AbstractAction {
1702
2572
  key
1703
2573
  };
1704
2574
  }
1705
- buildStartTasks(config2, directory, watch3, ports, ssl) {
2575
+ buildStartTasks(config2, directory, options) {
2576
+ const env2 = this.parseEnv(directory);
1706
2577
  const tasks = [];
1707
- if (config2.server.enable) {
1708
- tasks.push(this.startServer(directory, config2.server.runtime.dir, watch3, ports.serverPort));
1709
- }
1710
- tasks.push(this.startClient(directory, config2, watch3, ports, ssl));
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
+ );
1711
2585
  return tasks;
1712
2586
  }
1713
- async startClient(directory, config2, watch3, ports, ssl) {
2587
+ async startClient(directory, config2, options, env2) {
1714
2588
  const loaderPath = getModulePath("@nanoforge-dev/loader-client/package.json", true);
1715
- const gameDir = config2.client.runtime.dir;
1716
- const env2 = this.buildClientEnv(directory, gameDir, watch3, config2, ports, ssl);
1717
- await this.runLoader("Client", loaderPath, env2);
1718
- }
1719
- buildClientEnv(directory, gameDir, watch3, config2, ports, ssl) {
1720
- const env2 = {
1721
- PORT: ports.clientPort,
1722
- GAME_DIR: getCwd(join7(directory, gameDir))
2589
+ const params = this.buildClientParams(directory, config2, options);
2590
+ await this.runLoader("Client", loaderPath, params, env2.client);
2591
+ }
2592
+ async startServer(directory, config2, options, env2) {
2593
+ const loaderPath = getModulePath("@nanoforge-dev/loader-server/package.json", true);
2594
+ const params = this.buildServerParams(directory, config2, options);
2595
+ await this.runLoader("Server", loaderPath, params, env2.server);
2596
+ }
2597
+ buildClientParams(directory, config2, options) {
2598
+ const params = {
2599
+ "-d": getCwd(join11(directory, options.clientDir)),
2600
+ "-p": options.port
1723
2601
  };
1724
- if (ports.gameExposurePort) {
1725
- env2["GAME_EXPOSURE_PORT"] = ports.gameExposurePort;
1726
- }
1727
- if (watch3) {
1728
- env2["WATCH"] = "true";
2602
+ if (options.watch) params["--watch"] = true;
2603
+ if (options.watch) {
2604
+ params["--watch"] = true;
1729
2605
  if (config2.server.enable) {
1730
- env2["WATCH_SERVER_GAME_DIR"] = getCwd(join7(directory, config2.server.runtime.dir));
2606
+ params["--watch-server-dir"] = getCwd(join11(directory, options.serverDir));
1731
2607
  }
1732
2608
  }
1733
- if (ssl) {
1734
- env2["CERT"] = ssl.cert;
1735
- env2["KEY"] = ssl.key;
2609
+ if (options.ssl) {
2610
+ params["--cert"] = options.ssl.cert;
2611
+ params["--key"] = options.ssl.key;
1736
2612
  }
1737
- return env2;
2613
+ return this.buildParams(params);
1738
2614
  }
1739
- async startServer(directory, gameDir, watch3, port) {
1740
- const loaderPath = getModulePath("@nanoforge-dev/loader-server/package.json", true);
1741
- const env2 = {
1742
- GAME_DIR: getCwd(join7(directory, gameDir))
2615
+ buildServerParams(directory, _config, options) {
2616
+ const params = {
2617
+ "-d": getCwd(join11(directory, options.serverDir))
2618
+ };
2619
+ if (options.watch) params["--watch"] = true;
2620
+ return this.buildParams(params);
2621
+ }
2622
+ buildParams(params) {
2623
+ return Object.entries(params).map(([key, value]) => typeof value === "string" ? [key, value] : [key]).flat();
2624
+ }
2625
+ parseEnv(dir) {
2626
+ const prefix = "NANOFORGE_";
2627
+ const clientPrefix = `${prefix}CLIENT_`;
2628
+ const serverPrefix = `${prefix}SERVER_`;
2629
+ const rawEnv = {
2630
+ ...process.env
2631
+ };
2632
+ dotenv.config({
2633
+ path: resolve3(getCwd(join11(dir, ".env"))),
2634
+ processEnv: rawEnv
2635
+ });
2636
+ const baseEnv = Object.entries(rawEnv).filter(
2637
+ ([key, value]) => key.startsWith(prefix) && !!value
2638
+ );
2639
+ return {
2640
+ client: Object.fromEntries(
2641
+ baseEnv.filter(([key]) => !key.startsWith(serverPrefix)).map(([key, value]) => [key.replace(clientPrefix, prefix), value])
2642
+ ),
2643
+ server: Object.fromEntries(
2644
+ baseEnv.filter(([key]) => !key.startsWith(clientPrefix)).map(([key, value]) => [key.replace(serverPrefix, prefix), value])
2645
+ )
1743
2646
  };
1744
- if (port) env2["PORT"] = port;
1745
- if (watch3) env2["WATCH"] = "true";
1746
- await this.runLoader("Server", loaderPath, env2);
1747
2647
  }
1748
- async runLoader(name, directory, env2) {
2648
+ async runLoader(name, directory, params, env2) {
1749
2649
  await runSafe(async () => {
1750
2650
  const packageManager = await PackageManagerFactory.find(directory);
1751
- await packageManager.run(name, directory, "start", env2, [], true);
2651
+ await packageManager.run(name, directory, "start", params, env2, [], true);
2652
+ });
2653
+ }
2654
+ };
2655
+
2656
+ // src/action/actions/unpublish.action.ts
2657
+ var UnpublishAction = class extends AbstractAction {
2658
+ static {
2659
+ __name(this, "UnpublishAction");
2660
+ }
2661
+ startMessage = Messages.UNPUBLISH_START;
2662
+ successMessage = Messages.UNPUBLISH_SUCCESS;
2663
+ failureMessage = Messages.UNPUBLISH_FAILED;
2664
+ async handle(_args, options) {
2665
+ const directory = getDirectoryInput(options);
2666
+ const manifest = await loadManifest(directory);
2667
+ return withSpinner(Messages.UNPUBLISH_IN_PROGRESS(manifest.name), async () => {
2668
+ await Registry.unpublish(manifest, directory);
1752
2669
  });
1753
2670
  }
1754
2671
  };
@@ -1758,6 +2675,7 @@ var AbstractCommand = class {
1758
2675
  constructor(action) {
1759
2676
  this.action = action;
1760
2677
  }
2678
+ action;
1761
2679
  static {
1762
2680
  __name(this, "AbstractCommand");
1763
2681
  }
@@ -1776,12 +2694,17 @@ var BuildCommand = class extends AbstractCommand {
1776
2694
  __name(this, "BuildCommand");
1777
2695
  }
1778
2696
  load(program2) {
1779
- program2.command("build").description("build your game").option("-d, --directory [directory]", "specify the directory of your project").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) => {
1780
2698
  const options = AbstractCommand.mapToInput({
1781
2699
  directory: rawOptions.directory,
1782
2700
  config: rawOptions.config,
1783
- clientDirectory: rawOptions.clientOutDir,
1784
- 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,
1785
2708
  watch: rawOptions.watch
1786
2709
  });
1787
2710
  await this.action.run(/* @__PURE__ */ new Map(), options);
@@ -1789,13 +2712,42 @@ var BuildCommand = class extends AbstractCommand {
1789
2712
  }
1790
2713
  };
1791
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
+
1792
2744
  // src/command/commands/dev.command.ts
1793
2745
  var DevCommand = class extends AbstractCommand {
1794
2746
  static {
1795
2747
  __name(this, "DevCommand");
1796
2748
  }
1797
2749
  load(program2) {
1798
- program2.command("dev").description("run your game in dev mode").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--generate", "generate app from config", false).action(async (rawOptions) => {
2750
+ program2.command("dev").description("run your game in dev mode").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--generate", "generate app from config", false).action(async (rawOptions) => {
1799
2751
  const options = AbstractCommand.mapToInput({
1800
2752
  directory: rawOptions.directory,
1801
2753
  config: rawOptions.config,
@@ -1812,10 +2764,11 @@ var GenerateCommand = class extends AbstractCommand {
1812
2764
  __name(this, "GenerateCommand");
1813
2765
  }
1814
2766
  load(program2) {
1815
- program2.command("generate").description("generate nanoforge files from config").option("-d, --directory [directory]", "specify the directory of your project").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) => {
1816
2768
  const options = AbstractCommand.mapToInput({
1817
2769
  directory: rawOptions.directory,
1818
2770
  config: rawOptions.config,
2771
+ editor: rawOptions.editor,
1819
2772
  watch: rawOptions.watch
1820
2773
  });
1821
2774
  await this.action.run(/* @__PURE__ */ new Map(), options);
@@ -1829,9 +2782,15 @@ var InstallCommand = class extends AbstractCommand {
1829
2782
  __name(this, "InstallCommand");
1830
2783
  }
1831
2784
  load(program2) {
1832
- program2.command("install [names...]").alias("add").description("add NanoForge library to your project").option("-d, --directory [directory]", "specify the directory of your project").action(async (names, rawOptions) => {
2785
+ program2.command("install [names...]").alias("add").description("add Nanoforge components and systems to your project").option("-d, --directory [directory]", "specify the working directory of the command").option("-l, --lib", "install library instead of component/system", false).option(
2786
+ "-s, --server",
2787
+ "install components/systems on server (default install on client)",
2788
+ false
2789
+ ).action(async (names, rawOptions) => {
1833
2790
  const options = AbstractCommand.mapToInput({
1834
- directory: rawOptions.directory
2791
+ directory: rawOptions.directory,
2792
+ lib: rawOptions.lib,
2793
+ server: rawOptions.server
1835
2794
  });
1836
2795
  const args = AbstractCommand.mapToInput({
1837
2796
  names: names.length ? names : void 0
@@ -1841,13 +2800,49 @@ var InstallCommand = class extends AbstractCommand {
1841
2800
  }
1842
2801
  };
1843
2802
 
2803
+ // src/command/commands/login.command.ts
2804
+ var LoginCommand = class extends AbstractCommand {
2805
+ static {
2806
+ __name(this, "LoginCommand");
2807
+ }
2808
+ load(program2) {
2809
+ program2.command("login").description("login to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").option("-l, --local", "login only for the project", false).option("-k, --api-key <key>", "api key for Nanoforge registry").action(async (rawOptions) => {
2810
+ const options = AbstractCommand.mapToInput({
2811
+ directory: rawOptions.directory,
2812
+ local: rawOptions.local,
2813
+ apiKey: rawOptions.apiKey
2814
+ });
2815
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2816
+ });
2817
+ }
2818
+ };
2819
+
2820
+ // src/command/commands/logout.command.ts
2821
+ var LogoutCommand = class extends AbstractCommand {
2822
+ static {
2823
+ __name(this, "LogoutCommand");
2824
+ }
2825
+ load(program2) {
2826
+ program2.command("logout").description("logout from Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").option("-l, --local", "logout only for the project").action(async (rawOptions) => {
2827
+ const options = AbstractCommand.mapToInput({
2828
+ directory: rawOptions.directory,
2829
+ local: rawOptions.local
2830
+ });
2831
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2832
+ });
2833
+ }
2834
+ };
2835
+
1844
2836
  // src/command/commands/new.command.ts
1845
2837
  var NewCommand = class extends AbstractCommand {
1846
2838
  static {
1847
2839
  __name(this, "NewCommand");
1848
2840
  }
1849
2841
  load(program2) {
1850
- program2.command("new").description("create a new nanoforge project").option("-d, --directory [directory]", "specify the directory of your project").option("--name [name]", "specify the name of your project").option("--path [path]", "specify the path of your project").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").action(async (rawOptions) => {
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(
2843
+ "--path [path]",
2844
+ "specify the relative path where your project will be created (default: name of the project)"
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) => {
1851
2846
  const options = AbstractCommand.mapToInput({
1852
2847
  directory: rawOptions.directory,
1853
2848
  name: rawOptions.name,
@@ -1858,7 +2853,24 @@ var NewCommand = class extends AbstractCommand {
1858
2853
  server: rawOptions.server,
1859
2854
  initFunctions: rawOptions.initFunctions,
1860
2855
  skipInstall: rawOptions.skipInstall,
1861
- docker: rawOptions.docker
2856
+ docker: rawOptions.docker,
2857
+ lint: rawOptions.lint,
2858
+ editor: rawOptions.editor
2859
+ });
2860
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2861
+ });
2862
+ }
2863
+ };
2864
+
2865
+ // src/command/commands/publish.command.ts
2866
+ var PublishCommand = class extends AbstractCommand {
2867
+ static {
2868
+ __name(this, "PublishCommand");
2869
+ }
2870
+ load(program2) {
2871
+ program2.command("publish").description("publish package to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").action(async (rawOptions) => {
2872
+ const options = AbstractCommand.mapToInput({
2873
+ directory: rawOptions.directory
1862
2874
  });
1863
2875
  await this.action.run(/* @__PURE__ */ new Map(), options);
1864
2876
  });
@@ -1871,16 +2883,13 @@ var StartCommand = class extends AbstractCommand {
1871
2883
  __name(this, "StartCommand");
1872
2884
  }
1873
2885
  load(program2) {
1874
- program2.command("start").description("start your game").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option(
1875
- "-p, --client-port [clientPort]",
1876
- "specify the port of the loader (the website to load the game)"
1877
- ).option("--game-exposure-port [gameExposurePort]", "specify the port of the game exposure").option("--server-port [serverPort]", "specify the port 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) => {
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) => {
1878
2887
  const options = AbstractCommand.mapToInput({
1879
2888
  directory: rawOptions.directory,
1880
2889
  config: rawOptions.config,
1881
- clientPort: rawOptions.clientPort,
1882
- gameExposurePort: rawOptions.gameExposurePort,
1883
- serverPort: rawOptions.serverPort,
2890
+ port: rawOptions.port,
2891
+ clientDir: rawOptions.clientDir,
2892
+ serverDir: rawOptions.serverDir,
1884
2893
  watch: rawOptions.watch,
1885
2894
  cert: rawOptions.cert,
1886
2895
  key: rawOptions.key
@@ -1890,18 +2899,38 @@ var StartCommand = class extends AbstractCommand {
1890
2899
  }
1891
2900
  };
1892
2901
 
2902
+ // src/command/commands/unpublish.command.ts
2903
+ var UnpublishCommand = class extends AbstractCommand {
2904
+ static {
2905
+ __name(this, "UnpublishCommand");
2906
+ }
2907
+ load(program2) {
2908
+ program2.command("unpublish").description("unpublish package to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").action(async (rawOptions) => {
2909
+ const options = AbstractCommand.mapToInput({
2910
+ directory: rawOptions.directory
2911
+ });
2912
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2913
+ });
2914
+ }
2915
+ };
2916
+
1893
2917
  // src/command/command.loader.ts
1894
2918
  var CommandLoader = class {
1895
2919
  static {
1896
2920
  __name(this, "CommandLoader");
1897
2921
  }
1898
2922
  static async load(program2) {
2923
+ new NewCommand(new NewAction()).load(program2);
2924
+ new InstallCommand(new InstallAction()).load(program2);
1899
2925
  new BuildCommand(new BuildAction()).load(program2);
2926
+ new StartCommand(new StartAction()).load(program2);
1900
2927
  new DevCommand(new DevAction()).load(program2);
1901
2928
  new GenerateCommand(new GenerateAction()).load(program2);
1902
- new InstallCommand(new InstallAction()).load(program2);
1903
- new NewCommand(new NewAction()).load(program2);
1904
- new StartCommand(new StartAction()).load(program2);
2929
+ new CreateCommand(new CreateAction()).load(program2);
2930
+ new LoginCommand(new LoginAction()).load(program2);
2931
+ new LogoutCommand(new LogoutAction()).load(program2);
2932
+ new PublishCommand(new PublishAction()).load(program2);
2933
+ new UnpublishCommand(new UnpublishAction()).load(program2);
1905
2934
  this.handleInvalidCommand(program2);
1906
2935
  }
1907
2936
  static handleInvalidCommand(program2) {
@@ -1917,6 +2946,14 @@ ${Prefixes.ERROR} Invalid command: ${red6`%s`}`, program2.args.join(" "));
1917
2946
 
1918
2947
  // src/bin/nf.ts
1919
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
+ });
1920
2957
  program.version(
1921
2958
  (await Promise.resolve().then(() => __toESM(require_package(), 1))).version ?? "unknown",
1922
2959
  "-v, --version",