@nanoforge-dev/cli 1.2.0 → 1.3.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.3.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: {
@@ -204,6 +207,16 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
204
207
  INSTALL_SUCCESS: success("Installation completed!"),
205
208
  INSTALL_FAILED: failure("Installation failed!"),
206
209
  INSTALL_NAMES_QUESTION: "Which libraries do you want to install?",
210
+ INSTALL_PACKAGES_IN_PROGRESS: "Install Nanoforge Packages",
211
+ // --- Login ---
212
+ LOGIN_START: "NanoForge Login",
213
+ LOGIN_SUCCESS: success("Login completed!"),
214
+ LOGIN_FAILED: failure("Login failed!"),
215
+ LOGIN_API_KEY_QUESTION: "What is your registry api key?",
216
+ // --- Logout ---
217
+ LOGOUT_START: "NanoForge Logout",
218
+ LOGOUT_SUCCESS: success("Logout completed!"),
219
+ LOGOUT_FAILED: failure("Logout failed!"),
207
220
  // --- New Project ---
208
221
  NEW_START: "NanoForge Project Creation",
209
222
  NEW_SUCCESS: success("Project successfully created!"),
@@ -231,6 +244,16 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
231
244
  START_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Starting ${part}...`, "START_PART_IN_PROGRESS"),
232
245
  START_PART_SUCCESS: /* @__PURE__ */ __name((part) => success(`${part} terminated.`), "START_PART_SUCCESS"),
233
246
  START_PART_FAILED: /* @__PURE__ */ __name((part) => failure(`${part} failed!`), "START_PART_FAILED"),
247
+ // --- Publish ---
248
+ PUBLISH_START: "NanoForge Publish",
249
+ PUBLISH_SUCCESS: success("Publish completed!"),
250
+ PUBLISH_FAILED: failure("Publish failed!"),
251
+ PUBLISH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Publishing ${name}...`, "PUBLISH_IN_PROGRESS"),
252
+ // --- Unpublish ---
253
+ UNPUBLISH_START: "NanoForge Unpublish",
254
+ UNPUBLISH_SUCCESS: success("Unpublish completed!"),
255
+ UNPUBLISH_FAILED: failure("Unpublish failed!"),
256
+ UNPUBLISH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Unpublishing ${name}...`, "UNPUBLISH_IN_PROGRESS"),
234
257
  // --- Schematics ---
235
258
  SCHEMATICS_START: "Running schematics",
236
259
  SCHEMATIC_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Generating ${name}...`, "SCHEMATIC_IN_PROGRESS"),
@@ -320,16 +343,28 @@ var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
320
343
  return getBooleanInputWithDefault(inputs, "generate", false);
321
344
  }, "getDevGenerateInput");
322
345
 
346
+ // src/lib/input/inputs/install/lib.input.ts
347
+ function getInstallLibInput(inputs) {
348
+ return getBooleanInputWithDefault(inputs, "lib", false);
349
+ }
350
+ __name(getInstallLibInput, "getInstallLibInput");
351
+
323
352
  // src/lib/question/questions/confirm.question.ts
324
353
  import { confirm } from "@inquirer/prompts";
325
354
 
326
355
  // src/lib/utils/errors.ts
327
356
  import { red } from "ansis";
328
357
  var getErrorMessage = /* @__PURE__ */ __name((error) => {
329
- if (error instanceof Error) return error.message;
358
+ if (error instanceof Error) return getErrorString(error);
330
359
  if (typeof error === "string") return error;
331
360
  return void 0;
332
361
  }, "getErrorMessage");
362
+ var getErrorString = /* @__PURE__ */ __name((error) => {
363
+ const stack = error.stack ? error.stack : error.message;
364
+ const cause = error.cause && typeof error.cause === "object" ? JSON.stringify(error.cause, null, 2) : error.cause;
365
+ return `${stack}${cause ? `
366
+ ${cause}` : ""}`;
367
+ }, "getErrorString");
333
368
  var handleActionError = /* @__PURE__ */ __name((context, error) => {
334
369
  console.error();
335
370
  console.error(red(context));
@@ -413,6 +448,134 @@ var getInstallNamesInputOrAsk = /* @__PURE__ */ __name((inputs) => {
413
448
  );
414
449
  }, "getInstallNamesInputOrAsk");
415
450
 
451
+ // src/lib/input/inputs/install/server.input.ts
452
+ function getInstallServerInput(inputs) {
453
+ return getBooleanInputWithDefault(inputs, "server", false);
454
+ }
455
+ __name(getInstallServerInput, "getInstallServerInput");
456
+
457
+ // src/lib/input/inputs/login-out/api-key.input.ts
458
+ var getApiKeyInput = /* @__PURE__ */ __name((inputs) => {
459
+ return getStringInput(inputs, "apiKey");
460
+ }, "getApiKeyInput");
461
+ var getLoginApiKeyInputOrAsk = /* @__PURE__ */ __name((inputs) => {
462
+ return getInputOrAsk(
463
+ getApiKeyInput(inputs),
464
+ () => askInput(Messages.LOGIN_API_KEY_QUESTION, {
465
+ required: true
466
+ })
467
+ );
468
+ }, "getLoginApiKeyInputOrAsk");
469
+
470
+ // src/lib/input/inputs/login-out/local.input.ts
471
+ function getLocalInput(inputs) {
472
+ return getBooleanInputWithDefault(inputs, "local", false);
473
+ }
474
+ __name(getLocalInput, "getLocalInput");
475
+
476
+ // src/lib/input/inputs/new/docker.input.ts
477
+ var getNewDockerInput = /* @__PURE__ */ __name((inputs) => {
478
+ return getBooleanInput(inputs, "docker");
479
+ }, "getNewDockerInput");
480
+ var getNewDockerOrAsk = /* @__PURE__ */ __name((inputs) => {
481
+ return getInputOrAsk(
482
+ getNewDockerInput(inputs),
483
+ () => askConfirm(Messages.NEW_DOCKER_QUESTION, { default: true })
484
+ );
485
+ }, "getNewDockerOrAsk");
486
+
487
+ // src/lib/input/inputs/new/init-functions.input.ts
488
+ var getNewInitFunctionsWithDefault = /* @__PURE__ */ __name((inputs) => {
489
+ return getBooleanInputWithDefault(inputs, "initFunctions", false);
490
+ }, "getNewInitFunctionsWithDefault");
491
+
492
+ // src/lib/input/inputs/new/language.input.ts
493
+ var getLanguageInput = /* @__PURE__ */ __name((inputs) => {
494
+ return getStringInput(inputs, "language");
495
+ }, "getLanguageInput");
496
+ var getNewLanguageInputOrAsk = /* @__PURE__ */ __name((inputs) => {
497
+ return getInputOrAsk(
498
+ getLanguageInput(inputs),
499
+ () => askSelect(Messages.NEW_LANGUAGE_QUESTION, [{ value: "ts" }, { value: "js" }], {
500
+ default: "ts"
501
+ })
502
+ );
503
+ }, "getNewLanguageInputOrAsk");
504
+
505
+ // src/lib/input/inputs/new/lint.input.ts
506
+ var getNewLintInput = /* @__PURE__ */ __name((inputs) => {
507
+ return getBooleanInputWithDefault(inputs, "lint", true);
508
+ }, "getNewLintInput");
509
+
510
+ // src/lib/input/inputs/new/name.input.ts
511
+ var getNameInput = /* @__PURE__ */ __name((inputs) => {
512
+ return getStringInput(inputs, "name");
513
+ }, "getNameInput");
514
+ var getNewNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
515
+ return getInputOrAsk(
516
+ getNameInput(inputs),
517
+ () => askInput(Messages.NEW_NAME_QUESTION, {
518
+ required: true,
519
+ default: "nanoforge-app"
520
+ })
521
+ );
522
+ }, "getNewNameInputOrAsk");
523
+
524
+ // src/lib/input/inputs/new/package-manager.input.ts
525
+ var getPackageManagerInput = /* @__PURE__ */ __name((inputs) => {
526
+ return getStringInput(inputs, "packageManager");
527
+ }, "getPackageManagerInput");
528
+ var getNewPackageManagerInputOrAsk = /* @__PURE__ */ __name((inputs) => {
529
+ return getInputOrAsk(
530
+ getPackageManagerInput(inputs),
531
+ () => askSelect(
532
+ Messages.NEW_PACKAGE_MANAGER_QUESTION,
533
+ [{ value: "npm" }, { value: "yarn" }, { value: "pnpm" }, { value: "bun" }],
534
+ {
535
+ default: "npm"
536
+ }
537
+ )
538
+ );
539
+ }, "getNewPackageManagerInputOrAsk");
540
+
541
+ // src/lib/input/inputs/new/path.input.ts
542
+ var getNewPathInput = /* @__PURE__ */ __name((inputs) => {
543
+ return getStringInput(inputs, "path");
544
+ }, "getNewPathInput");
545
+
546
+ // src/lib/input/inputs/new/server.input.ts
547
+ var getNewServerInput = /* @__PURE__ */ __name((inputs) => {
548
+ return getBooleanInput(inputs, "server");
549
+ }, "getNewServerInput");
550
+ var getNewServerOrAsk = /* @__PURE__ */ __name((inputs) => {
551
+ return getInputOrAsk(
552
+ getNewServerInput(inputs),
553
+ () => askConfirm(Messages.NEW_SERVER_QUESTION, { default: false })
554
+ );
555
+ }, "getNewServerOrAsk");
556
+
557
+ // src/lib/input/inputs/new/skip-install.input.ts
558
+ var getNewSkipInstallInput = /* @__PURE__ */ __name((inputs) => {
559
+ return getBooleanInput(inputs, "skipInstall");
560
+ }, "getNewSkipInstallInput");
561
+ var getNewSkipInstallOrAsk = /* @__PURE__ */ __name((inputs) => {
562
+ return getInputOrAsk(
563
+ getNewSkipInstallInput(inputs),
564
+ () => askConfirm(Messages.NEW_SKIP_INSTALL_QUESTION, { default: false })
565
+ );
566
+ }, "getNewSkipInstallOrAsk");
567
+
568
+ // src/lib/input/inputs/new/strict.input.ts
569
+ var getNewStrictInput = /* @__PURE__ */ __name((inputs) => {
570
+ return getBooleanInput(inputs, "strict");
571
+ }, "getNewStrictInput");
572
+ var getNewStrictOrAsk = /* @__PURE__ */ __name((inputs) => {
573
+ return getInputOrAsk(
574
+ getNewStrictInput(inputs),
575
+ () => askConfirm(Messages.NEW_STRICT_QUESTION, { default: true })
576
+ );
577
+ }, "getNewStrictOrAsk");
578
+
416
579
  // src/lib/package-manager/package-manager.ts
417
580
  import { bold, red as red3 } from "ansis";
418
581
 
@@ -460,6 +623,21 @@ var resolveCLINodeBinaryPath = /* @__PURE__ */ __name((name) => {
460
623
  throw new Error("Could not find module path");
461
624
  }, "resolveCLINodeBinaryPath");
462
625
 
626
+ // src/lib/utils/spinner.ts
627
+ var withSpinner = /* @__PURE__ */ __name(async (message, task, onError) => {
628
+ const spinner = getSpinner(message);
629
+ spinner.start();
630
+ try {
631
+ const value = await task(spinner);
632
+ spinner.succeed();
633
+ return { success: true, value };
634
+ } catch (error) {
635
+ spinner.fail();
636
+ if (onError) onError();
637
+ return { success: false, error };
638
+ }
639
+ }, "withSpinner");
640
+
463
641
  // src/lib/package-manager/package-manager.ts
464
642
  var PackageManager = class {
465
643
  constructor(name, commands, runner) {
@@ -472,7 +650,7 @@ var PackageManager = class {
472
650
  }
473
651
  async install(directory) {
474
652
  const args = [this.commands.install, this.commands.silentFlag];
475
- const result = await this.withSpinner(
653
+ const result = await withSpinner(
476
654
  Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
477
655
  async (spinner) => {
478
656
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -500,7 +678,7 @@ var PackageManager = class {
500
678
  output,
501
679
  ...flags
502
680
  ];
503
- const result = await this.withSpinner(
681
+ const result = await withSpinner(
504
682
  message,
505
683
  async (spinner) => {
506
684
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -509,10 +687,10 @@ var PackageManager = class {
509
687
  );
510
688
  return result.success;
511
689
  }
512
- async run(name, directory, script, env2 = {}, flags = [], silent = false) {
690
+ async run(name, directory, script, params, env2 = {}, flags = [], silent = false) {
513
691
  console.info(Messages.START_PART_IN_PROGRESS(name));
514
692
  try {
515
- const args = this.buildRunArgs(script, flags, silent);
693
+ const args = this.buildRunArgs(script, params, flags, silent);
516
694
  await this.exec(args, directory, {
517
695
  env: env2,
518
696
  listeners: {
@@ -529,32 +707,21 @@ var PackageManager = class {
529
707
  }
530
708
  async runDev(directory, command, env2 = {}, flags = [], collect = true) {
531
709
  try {
532
- await this.exec([this.commands.run, command, ...flags], directory, { collect, env: env2 });
710
+ const base = [this.commands.exec, command];
711
+ if (this.commands.runArgsFlag) base.push(this.commands.runArgsFlag);
712
+ await this.exec([...base, ...flags], directory, { collect, env: env2 });
533
713
  return true;
534
714
  } catch {
535
715
  return false;
536
716
  }
537
717
  }
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
718
  async addDependencies(saveFlag, directory, dependencies) {
552
719
  if (!dependencies.length) {
553
720
  this.logEmpty(Messages.PACKAGE_MANAGER_INSTALLATION_NOTHING);
554
721
  return true;
555
722
  }
556
723
  const args = [this.commands.add, saveFlag, ...dependencies];
557
- const result = await this.withSpinner(
724
+ const result = await withSpinner(
558
725
  Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
559
726
  async (spinner) => {
560
727
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -569,11 +736,13 @@ var PackageManager = class {
569
736
  throw new Error(`Package manager "${this.name}" does not support "${feature}"`);
570
737
  }
571
738
  }
572
- buildRunArgs(script, flags, silent) {
739
+ buildRunArgs(script, params, flags, silent) {
573
740
  const args = [...flags, this.commands.run];
574
741
  if (silent) args.push(this.commands.silentFlag);
575
742
  args.push(script);
576
- return args;
743
+ if (params.length === 0) return args;
744
+ if (this.commands.runArgsFlag) args.push(this.commands.runArgsFlag);
745
+ return args.concat(params);
577
746
  }
578
747
  exec(args, directory, options = {}) {
579
748
  return this.runner.run(args, {
@@ -626,12 +795,12 @@ var Runner = class {
626
795
  const { collect = false, cwd: cwd2 = process2.cwd(), env: env2, listeners, onFail } = options;
627
796
  const spawnOpts = this.buildSpawnOptions(collect, cwd2, env2);
628
797
  const fullArgs = [...this.baseArgs, ...args];
629
- return new Promise((resolve3, reject) => {
798
+ return new Promise((resolve4, reject) => {
630
799
  const child = spawn(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
631
800
  const output = this.attachOutputHandlers(child, listeners);
632
801
  child.on("close", (code) => {
633
802
  if (code === 0) {
634
- resolve3(this.formatOutput(output, collect));
803
+ resolve4(this.formatOutput(output, collect));
635
804
  } else {
636
805
  this.handleFailure(output, fullArgs, onFail);
637
806
  reject(this.createError(fullArgs, code));
@@ -746,6 +915,7 @@ var PM_CONFIGS = {
746
915
  remove: "uninstall",
747
916
  exec: "exec",
748
917
  run: "run",
918
+ runArgsFlag: "--",
749
919
  saveFlag: "--save",
750
920
  saveDevFlag: "--save-dev",
751
921
  silentFlag: "--silent"
@@ -870,7 +1040,6 @@ var ClientConfig = class {
870
1040
  __name(this, "ClientConfig");
871
1041
  }
872
1042
  port;
873
- gameExposurePort;
874
1043
  build;
875
1044
  runtime;
876
1045
  };
@@ -878,10 +1047,6 @@ __decorateClass([
878
1047
  Expose(),
879
1048
  IsPort()
880
1049
  ], ClientConfig.prototype, "port", 2);
881
- __decorateClass([
882
- Expose(),
883
- IsPort()
884
- ], ClientConfig.prototype, "gameExposurePort", 2);
885
1050
  __decorateClass([
886
1051
  Expose(),
887
1052
  Type(() => BuildConfig),
@@ -897,7 +1062,6 @@ var ServerConfig = class {
897
1062
  __name(this, "ServerConfig");
898
1063
  }
899
1064
  enable;
900
- port;
901
1065
  build;
902
1066
  runtime;
903
1067
  };
@@ -905,10 +1069,6 @@ __decorateClass([
905
1069
  Expose(),
906
1070
  IsBoolean()
907
1071
  ], ServerConfig.prototype, "enable", 2);
908
- __decorateClass([
909
- Expose(),
910
- IsPort()
911
- ], ServerConfig.prototype, "port", 2);
912
1072
  __decorateClass([
913
1073
  Expose(),
914
1074
  Type(() => BuildConfig),
@@ -961,7 +1121,10 @@ import { join as join3 } from "path";
961
1121
 
962
1122
  // src/lib/constants.ts
963
1123
  var CONFIG_FILE_NAME = "nanoforge.config.json";
1124
+ var MANIFEST_FILE_NAME = "nanoforge.manifest.json";
1125
+ var GLOBAL_CONFIG_FILE_NAME = ".nanoforgerc";
964
1126
  var NANOFORGE_DIR = ".nanoforge";
1127
+ var REGISTRY_URL = "https://api.nanoforge.dev";
965
1128
 
966
1129
  // src/lib/utils/object.ts
967
1130
  var isObject = /* @__PURE__ */ __name((item) => {
@@ -982,6 +1145,7 @@ var deepMerge = /* @__PURE__ */ __name((target, ...sources) => {
982
1145
  }
983
1146
  return deepMerge(target, ...sources);
984
1147
  }, "deepMerge");
1148
+ var isEmpty = /* @__PURE__ */ __name((target) => Object.keys(target).length === 0, "isEmpty");
985
1149
 
986
1150
  // src/lib/config/config-defaults.ts
987
1151
  var CONFIG_DEFAULTS = {
@@ -990,7 +1154,6 @@ var CONFIG_DEFAULTS = {
990
1154
  initFunctions: true,
991
1155
  client: {
992
1156
  port: "3000",
993
- gameExposurePort: "3001",
994
1157
  build: {
995
1158
  entryFile: "client/main.ts",
996
1159
  outDir: ".nanoforge/client"
@@ -1001,7 +1164,6 @@ var CONFIG_DEFAULTS = {
1001
1164
  },
1002
1165
  server: {
1003
1166
  enable: false,
1004
- port: "3002",
1005
1167
  build: {
1006
1168
  entryFile: "server/main.ts",
1007
1169
  outDir: ".nanoforge/server"
@@ -1076,7 +1238,7 @@ var AbstractAction = class {
1076
1238
  if (keepAlive) return;
1077
1239
  console.info();
1078
1240
  if (!success2) {
1079
- if (this.failureMessage) console.error(this.failureMessage);
1241
+ handleActionError(this.failureMessage, result.error);
1080
1242
  process.exit(1);
1081
1243
  }
1082
1244
  if (this.successMessage) console.info(this.successMessage);
@@ -1449,6 +1611,310 @@ var GenerateAction = class extends AbstractAction {
1449
1611
  }
1450
1612
  };
1451
1613
 
1614
+ // src/action/actions/install.action.ts
1615
+ import { join as join7 } from "path";
1616
+
1617
+ // src/lib/global-config/global-config-handler.ts
1618
+ import { read, readUser, write, writeUser } from "rc9";
1619
+
1620
+ // src/lib/global-config/global-config-defaults.ts
1621
+ var GLOBAL_CONFIG_DEFAULTS = {};
1622
+
1623
+ // src/lib/global-config/global-config-handler.ts
1624
+ var GlobalConfigHandler = class {
1625
+ static {
1626
+ __name(this, "GlobalConfigHandler");
1627
+ }
1628
+ static read(dir) {
1629
+ const localConfig = this._readConfig(read, false, dir);
1630
+ if (localConfig) return localConfig;
1631
+ return this._readConfig(readUser, true);
1632
+ }
1633
+ static write(config2, local = false, dir) {
1634
+ const options = {
1635
+ name: GLOBAL_CONFIG_FILE_NAME,
1636
+ dir
1637
+ };
1638
+ if (local) write(config2, options);
1639
+ else writeUser(config2, options);
1640
+ }
1641
+ static _readConfig(func, force, dir) {
1642
+ const res = func({
1643
+ name: GLOBAL_CONFIG_FILE_NAME,
1644
+ dir
1645
+ });
1646
+ if (!force) {
1647
+ if (isEmpty(res)) return null;
1648
+ }
1649
+ return deepMerge(GLOBAL_CONFIG_DEFAULTS, res);
1650
+ }
1651
+ };
1652
+
1653
+ // src/lib/http/http-client.ts
1654
+ var HttpClient = class {
1655
+ static {
1656
+ __name(this, "HttpClient");
1657
+ }
1658
+ _baseUrl;
1659
+ _baseOptions;
1660
+ _middlewares;
1661
+ constructor(baseUrl, options) {
1662
+ this._baseUrl = baseUrl;
1663
+ this._baseOptions = options ?? {
1664
+ headers: {
1665
+ "Content-Type": "application/json"
1666
+ }
1667
+ };
1668
+ this._middlewares = [];
1669
+ }
1670
+ get(path, options) {
1671
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1672
+ return this._request(newPath, {
1673
+ ...newOptions,
1674
+ method: "GET"
1675
+ });
1676
+ });
1677
+ }
1678
+ post(path, body, options) {
1679
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1680
+ return this._request(newPath, {
1681
+ ...newOptions,
1682
+ method: "POST",
1683
+ body
1684
+ });
1685
+ });
1686
+ }
1687
+ put(path, body, options) {
1688
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1689
+ return this._request(newPath, {
1690
+ ...newOptions,
1691
+ method: "PUT",
1692
+ body
1693
+ });
1694
+ });
1695
+ }
1696
+ patch(path, body, options) {
1697
+ return this._applyMiddlewares(path, options, async (newPath, newOptions) => {
1698
+ return this._request(newPath, {
1699
+ ...newOptions,
1700
+ method: "PATCH",
1701
+ body
1702
+ });
1703
+ });
1704
+ }
1705
+ delete(path, options) {
1706
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1707
+ return this._request(newPath, {
1708
+ ...newOptions,
1709
+ method: "DELETE",
1710
+ body: "{}"
1711
+ });
1712
+ });
1713
+ }
1714
+ useMiddlewares(...middlewares) {
1715
+ for (const middleware of middlewares) this._middlewares.push(middleware);
1716
+ return this;
1717
+ }
1718
+ async _request(path, request) {
1719
+ const res = await fetch(path, request);
1720
+ res.content = null;
1721
+ return res;
1722
+ }
1723
+ _applyMiddlewares(path, options, callback) {
1724
+ const baseParams = {
1725
+ path,
1726
+ fullPath: this._getUrl(path),
1727
+ options: {
1728
+ ...this._baseOptions,
1729
+ ...options,
1730
+ headers: {
1731
+ ...this._baseOptions.headers,
1732
+ ...options?.headers
1733
+ }
1734
+ }
1735
+ };
1736
+ const middlewares = this._middlewares.slice();
1737
+ let response;
1738
+ const execution = /* @__PURE__ */ __name(async (params) => {
1739
+ if (!params) params = baseParams;
1740
+ const middleware = middlewares.shift();
1741
+ if (!middleware) response = await callback(params.fullPath, params.options);
1742
+ else response = await middleware(params, execution) ?? response;
1743
+ return response;
1744
+ }, "execution");
1745
+ return execution(baseParams);
1746
+ }
1747
+ _getUrl(path) {
1748
+ return `${this._baseUrl}${path}`;
1749
+ }
1750
+ };
1751
+
1752
+ // src/lib/http/repository.ts
1753
+ var Repository = class {
1754
+ static {
1755
+ __name(this, "Repository");
1756
+ }
1757
+ _client;
1758
+ constructor(client2) {
1759
+ this._client = client2;
1760
+ }
1761
+ get(path, options) {
1762
+ return this.runRequest("get", path, options);
1763
+ }
1764
+ getFile(path, options) {
1765
+ return this.runFileRequest("get", path, options);
1766
+ }
1767
+ post(path, body, options) {
1768
+ return this.runRequestBody("post", path, body ?? {}, options);
1769
+ }
1770
+ put(path, body, options) {
1771
+ return this.runRequestBody("put", path, body ?? {}, options);
1772
+ }
1773
+ patch(path, body, options) {
1774
+ return this.runRequestBody("patch", path, body ?? {}, options);
1775
+ }
1776
+ delete(path, options) {
1777
+ return this.runRequest("delete", path, options);
1778
+ }
1779
+ async runRequest(request, path, options) {
1780
+ const res = await this._client[request](path, options);
1781
+ const data = await res.json();
1782
+ if (!res.ok)
1783
+ throw new Error(`Request failed with status code ${res.status}`, {
1784
+ cause: data["error"]
1785
+ });
1786
+ return data;
1787
+ }
1788
+ async runFileRequest(request, path, options) {
1789
+ const res = await this._client[request](path, options);
1790
+ if (!res.ok)
1791
+ throw new Error(`Request failed with status code ${res.status}`, {
1792
+ cause: (await res.json())["error"]
1793
+ });
1794
+ return await res.blob();
1795
+ }
1796
+ async runRequestBody(request, path, body, options) {
1797
+ const res = await this._client[request](
1798
+ path,
1799
+ body === void 0 ? void 0 : body instanceof FormData ? body : JSON.stringify(body),
1800
+ options
1801
+ );
1802
+ const data = await res.json();
1803
+ if (!res.ok)
1804
+ throw new Error(`Request failed with status code ${res.status}`, {
1805
+ cause: data["error"]
1806
+ });
1807
+ return data;
1808
+ }
1809
+ };
1810
+
1811
+ // src/lib/http/client.ts
1812
+ var client = new HttpClient(REGISTRY_URL ?? "");
1813
+ var api = new Repository(client);
1814
+ var withAuth = /* @__PURE__ */ __name((apiKey, force = false, headers = {
1815
+ "Content-Type": "application/json"
1816
+ }) => {
1817
+ if (!apiKey && force) {
1818
+ console.error("No registry key found. Please use `nf login` to login");
1819
+ throw new Error("No apikey found. Please use `nf login` to login");
1820
+ }
1821
+ return new Repository(
1822
+ new HttpClient(REGISTRY_URL ?? "", {
1823
+ headers: {
1824
+ Authorization: apiKey,
1825
+ ...headers
1826
+ }
1827
+ })
1828
+ );
1829
+ }, "withAuth");
1830
+
1831
+ // src/lib/manifest/manifest-resolver.ts
1832
+ var resolveManifestDependencies = /* @__PURE__ */ __name(async (names, dir) => {
1833
+ const client2 = withAuth(GlobalConfigHandler.read(dir).apiKey, false);
1834
+ return concatDeps(await Promise.all(names.map(async (d) => resolveDeps(d, client2))));
1835
+ }, "resolveManifestDependencies");
1836
+ var resolveManifest = /* @__PURE__ */ __name(async (name, client2) => {
1837
+ return await client2.get(`/registry/${name}`);
1838
+ }, "resolveManifest");
1839
+ var resolveDeps = /* @__PURE__ */ __name(async (name, client2) => {
1840
+ const manifest = await resolveManifest(name, client2);
1841
+ const baseDeps = manifest.dependencies ?? [];
1842
+ const deps = await Promise.all(baseDeps.map(async (d) => resolveDeps(d, client2)));
1843
+ return concatDeps(
1844
+ [{ nf: { [manifest.name]: manifest }, npm: getNpmDeps(manifest) }].concat(deps)
1845
+ );
1846
+ }, "resolveDeps");
1847
+ var getNpmDeps = /* @__PURE__ */ __name((manifest) => {
1848
+ return Object.entries(manifest.npmDependencies ?? {});
1849
+ }, "getNpmDeps");
1850
+ var concatDeps = /* @__PURE__ */ __name((deps) => {
1851
+ return {
1852
+ npm: deps.map(({ npm }) => npm).flat(),
1853
+ nf: Object.fromEntries(deps.map(({ nf }) => Object.entries(nf)).flat())
1854
+ };
1855
+ }, "concatDeps");
1856
+
1857
+ // src/lib/registry/registry.ts
1858
+ import fs3 from "fs";
1859
+ import { join as join6 } from "path";
1860
+ var Registry = class {
1861
+ static {
1862
+ __name(this, "Registry");
1863
+ }
1864
+ static async publish(manifest, dir) {
1865
+ const client2 = this._getClient(dir, true, false);
1866
+ const filename = manifest.publish?.paths?.package ?? "index.ts";
1867
+ const file = await this._getPackageFile(filename, dir);
1868
+ const data = new FormData();
1869
+ for (const key of Object.keys(manifest)) {
1870
+ const value = manifest[key];
1871
+ if (!value) continue;
1872
+ data.append(key, typeof value === "string" ? value : JSON.stringify(value));
1873
+ }
1874
+ data.append("_packageFile", file, filename);
1875
+ await client2.put(`/registry/${manifest.name}`, data);
1876
+ }
1877
+ static async unpublish(manifest, dir) {
1878
+ const client2 = this._getClient(dir, true);
1879
+ await client2.delete(`/registry/${manifest.name}`);
1880
+ }
1881
+ static async install(manifests, dir) {
1882
+ const cwd2 = getCwd(dir);
1883
+ const client2 = this._getClient(dir, false);
1884
+ for (const manifest of manifests) {
1885
+ await this.installPackage(client2, manifest, cwd2);
1886
+ }
1887
+ }
1888
+ static async installPackage(client2, manifest, dir) {
1889
+ const file = await client2.getFile(`/registry/${manifest.name}/-/${manifest._file}`);
1890
+ const path = join6(dir, this.getTypeSubFolder(manifest.type));
1891
+ fs3.mkdirSync(path, { recursive: true });
1892
+ fs3.writeFileSync(join6(path, manifest._file), await file.bytes());
1893
+ }
1894
+ static getTypeSubFolder(type) {
1895
+ if (type === "component") return "components";
1896
+ if (type === "system") return "systems";
1897
+ return ".";
1898
+ }
1899
+ static _getClient(dir, force, headers = true) {
1900
+ const config2 = GlobalConfigHandler.read(dir);
1901
+ return withAuth(config2.apiKey, force, !headers ? {} : void 0);
1902
+ }
1903
+ static _getPackageFile(filename, dir) {
1904
+ const path = join6(getCwd(dir ?? "."), filename);
1905
+ if (!fs3.existsSync(path))
1906
+ throw new Error(
1907
+ "Package not found, please specify path in the nanoforge.manifest.json : `publish.paths.components`!"
1908
+ );
1909
+ try {
1910
+ fs3.accessSync(path, fs3.constants.R_OK);
1911
+ return fs3.openAsBlob(path);
1912
+ } catch {
1913
+ throw new Error("Cannot read package file, please verify your file permissions!");
1914
+ }
1915
+ }
1916
+ };
1917
+
1452
1918
  // src/action/actions/install.action.ts
1453
1919
  var InstallAction = class extends AbstractAction {
1454
1920
  static {
@@ -1460,114 +1926,78 @@ var InstallAction = class extends AbstractAction {
1460
1926
  async handle(args, options) {
1461
1927
  const names = await getInstallNamesInputOrAsk(args);
1462
1928
  const directory = getDirectoryInput(options);
1929
+ const isLib = getInstallLibInput(options);
1930
+ const isServer = getInstallServerInput(options);
1931
+ return isLib ? this._installLibs(directory, names) : this._installNfPackages(directory, names, isServer);
1932
+ }
1933
+ async _installLibs(directory, names) {
1463
1934
  const packageManager = await PackageManagerFactory.find(directory);
1464
- const success2 = await packageManager.addProduction(directory, names);
1465
- return { success: success2 };
1935
+ return { success: await packageManager.addDevelopment(directory, names) };
1936
+ }
1937
+ async _installNfPackages(directory, names, isServer) {
1938
+ const deps = await resolveManifestDependencies(names, directory);
1939
+ const libSuccess = await this._installLibs(
1940
+ directory,
1941
+ deps.npm.map(([name, version]) => `${name}@${version}`)
1942
+ );
1943
+ if (!libSuccess) return { success: false };
1944
+ return withSpinner(Messages.INSTALL_PACKAGES_IN_PROGRESS, async () => {
1945
+ await Registry.install(
1946
+ Object.values(deps.nf),
1947
+ join7(directory, isServer ? "server" : "client")
1948
+ );
1949
+ });
1466
1950
  }
1467
1951
  };
1468
1952
 
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" }],
1953
+ // src/action/actions/login.action.ts
1954
+ var LoginAction = class extends AbstractAction {
1955
+ static {
1956
+ __name(this, "LoginAction");
1957
+ }
1958
+ startMessage = Messages.LOGIN_START;
1959
+ successMessage = Messages.LOGIN_SUCCESS;
1960
+ failureMessage = Messages.LOGIN_FAILED;
1961
+ async handle(_args, options) {
1962
+ const directory = getDirectoryInput(options);
1963
+ const isLocal = getLocalInput(options);
1964
+ const apiKey = await getLoginApiKeyInputOrAsk(options);
1965
+ await withAuth(apiKey, true).post("/registry-key/verify");
1966
+ GlobalConfigHandler.write(
1525
1967
  {
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");
1968
+ apiKey
1969
+ },
1970
+ isLocal,
1971
+ directory
1972
+ );
1973
+ return { success: true };
1974
+ }
1975
+ };
1558
1976
 
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");
1977
+ // src/action/actions/logout.action.ts
1978
+ var LogoutAction = class extends AbstractAction {
1979
+ static {
1980
+ __name(this, "LogoutAction");
1981
+ }
1982
+ startMessage = Messages.LOGOUT_START;
1983
+ successMessage = Messages.LOGOUT_SUCCESS;
1984
+ failureMessage = Messages.LOGOUT_FAILED;
1985
+ async handle(_args, options) {
1986
+ const directory = getDirectoryInput(options);
1987
+ const isLocal = getLocalInput(options);
1988
+ GlobalConfigHandler.write(
1989
+ {
1990
+ apiKey: void 0
1991
+ },
1992
+ isLocal,
1993
+ directory
1994
+ );
1995
+ return { success: true };
1996
+ }
1997
+ };
1569
1998
 
1570
1999
  // src/action/actions/new.action.ts
2000
+ import { join as join8 } from "path";
1571
2001
  var NewAction = class extends AbstractAction {
1572
2002
  static {
1573
2003
  __name(this, "NewAction");
@@ -1576,12 +2006,15 @@ var NewAction = class extends AbstractAction {
1576
2006
  successMessage = Messages.NEW_SUCCESS;
1577
2007
  failureMessage = Messages.NEW_FAILED;
1578
2008
  async handle(_args, options) {
1579
- const directory = getDirectoryInput(options);
2009
+ const cwdDirectory = getDirectoryInput(options);
1580
2010
  const values = await this.collectValues(options);
1581
- await this.scaffold(values, directory);
2011
+ await this.scaffold(values, cwdDirectory);
1582
2012
  let res = true;
1583
2013
  if (!values.skipInstall) {
1584
- res = await this.installDependencies(values.packageManager, join6(directory, values.name));
2014
+ res = await this.installDependencies(
2015
+ values.packageManager,
2016
+ join8(cwdDirectory, values.directory ?? values.name)
2017
+ );
1585
2018
  }
1586
2019
  return { success: res };
1587
2020
  }
@@ -1595,7 +2028,8 @@ var NewAction = class extends AbstractAction {
1595
2028
  server: await getNewServerOrAsk(inputs),
1596
2029
  initFunctions: getNewInitFunctionsWithDefault(inputs),
1597
2030
  skipInstall: await getNewSkipInstallOrAsk(inputs),
1598
- docker: await getDockerOrAsk(inputs)
2031
+ docker: await getNewDockerOrAsk(inputs),
2032
+ lint: getNewLintInput(inputs)
1599
2033
  };
1600
2034
  }
1601
2035
  async scaffold(values, directory) {
@@ -1617,7 +2051,8 @@ var NewAction = class extends AbstractAction {
1617
2051
  packageManager: values.packageManager,
1618
2052
  language: values.language,
1619
2053
  strict: values.strict,
1620
- server: values.server
2054
+ server: values.server,
2055
+ lint: values.lint
1621
2056
  });
1622
2057
  }
1623
2058
  generateConfiguration(collection, values) {
@@ -1665,8 +2100,143 @@ var NewAction = class extends AbstractAction {
1665
2100
  }
1666
2101
  };
1667
2102
 
2103
+ // src/lib/manifest/manifest.type.ts
2104
+ import { Expose as Expose2, Type as Type2 } from "class-transformer";
2105
+ import {
2106
+ IsEnum as IsEnum2,
2107
+ IsNotEmpty as IsNotEmpty2,
2108
+ IsObject,
2109
+ IsOptional,
2110
+ IsString as IsString2,
2111
+ Matches,
2112
+ ValidateNested as ValidateNested2
2113
+ } from "class-validator";
2114
+ var ManifestPackageTypeEnum = /* @__PURE__ */ ((ManifestPackageTypeEnum2) => {
2115
+ ManifestPackageTypeEnum2["COMPONENT"] = "component";
2116
+ ManifestPackageTypeEnum2["SYSTEM"] = "system";
2117
+ return ManifestPackageTypeEnum2;
2118
+ })(ManifestPackageTypeEnum || {});
2119
+ var PathsPublishManifest = class {
2120
+ static {
2121
+ __name(this, "PathsPublishManifest");
2122
+ }
2123
+ package;
2124
+ };
2125
+ __decorateClass([
2126
+ Expose2(),
2127
+ IsString2(),
2128
+ IsNotEmpty2(),
2129
+ IsOptional()
2130
+ ], PathsPublishManifest.prototype, "package", 2);
2131
+ var PublishManifest = class {
2132
+ static {
2133
+ __name(this, "PublishManifest");
2134
+ }
2135
+ paths;
2136
+ };
2137
+ __decorateClass([
2138
+ Expose2(),
2139
+ IsOptional(),
2140
+ ValidateNested2(),
2141
+ Type2(() => PathsPublishManifest)
2142
+ ], PublishManifest.prototype, "paths", 2);
2143
+ var Manifest = class {
2144
+ static {
2145
+ __name(this, "Manifest");
2146
+ }
2147
+ name;
2148
+ type;
2149
+ description;
2150
+ dependencies;
2151
+ publish;
2152
+ npmDependencies;
2153
+ };
2154
+ __decorateClass([
2155
+ Expose2(),
2156
+ IsString2(),
2157
+ Matches(/^[A-Za-z0-9-]+\/[A-Za-z0-9-]+$/),
2158
+ IsNotEmpty2()
2159
+ ], Manifest.prototype, "name", 2);
2160
+ __decorateClass([
2161
+ Expose2(),
2162
+ IsString2(),
2163
+ IsEnum2(ManifestPackageTypeEnum),
2164
+ IsNotEmpty2()
2165
+ ], Manifest.prototype, "type", 2);
2166
+ __decorateClass([
2167
+ Expose2(),
2168
+ IsString2(),
2169
+ IsOptional()
2170
+ ], Manifest.prototype, "description", 2);
2171
+ __decorateClass([
2172
+ Expose2(),
2173
+ IsString2({ each: true }),
2174
+ IsNotEmpty2({ each: true }),
2175
+ IsOptional()
2176
+ ], Manifest.prototype, "dependencies", 2);
2177
+ __decorateClass([
2178
+ Expose2(),
2179
+ IsOptional(),
2180
+ ValidateNested2(),
2181
+ Type2(() => PublishManifest)
2182
+ ], Manifest.prototype, "publish", 2);
2183
+ __decorateClass([
2184
+ Expose2(),
2185
+ IsObject(),
2186
+ IsOptional()
2187
+ ], Manifest.prototype, "npmDependencies", 2);
2188
+
2189
+ // src/lib/manifest/manifest-loader.ts
2190
+ import { plainToInstance as plainToInstance2 } from "class-transformer";
2191
+ import { validate as validate2 } from "class-validator";
2192
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2193
+ import { join as join9 } from "path";
2194
+ var getManifestPath = /* @__PURE__ */ __name((directory) => {
2195
+ for (const n of [MANIFEST_FILE_NAME]) {
2196
+ const path = join9(directory, n);
2197
+ if (existsSync3(path)) return path;
2198
+ }
2199
+ throw new Error(`No manifest file found in directory: ${directory}`);
2200
+ }, "getManifestPath");
2201
+ var loadManifest = /* @__PURE__ */ __name(async (directory) => {
2202
+ let rawData;
2203
+ const path = getManifestPath(directory);
2204
+ try {
2205
+ rawData = deepMerge({}, JSON.parse(readFileSync2(path, "utf-8")));
2206
+ } catch {
2207
+ rawData = null;
2208
+ }
2209
+ if (!rawData) throw new Error(`Not able to read manifest file : ${path}`);
2210
+ const data = plainToInstance2(Manifest, rawData, {
2211
+ excludeExtraneousValues: true
2212
+ });
2213
+ const errors = await validate2(data);
2214
+ if (errors.length > 0)
2215
+ throw new Error(`Invalid manifest
2216
+ ${errors.toString().replace(/,/g, "\n")}`);
2217
+ return data;
2218
+ }, "loadManifest");
2219
+
2220
+ // src/action/actions/publish.action.ts
2221
+ var PublishAction = class extends AbstractAction {
2222
+ static {
2223
+ __name(this, "PublishAction");
2224
+ }
2225
+ startMessage = Messages.PUBLISH_START;
2226
+ successMessage = Messages.PUBLISH_SUCCESS;
2227
+ failureMessage = Messages.PUBLISH_FAILED;
2228
+ async handle(_args, options) {
2229
+ const directory = getDirectoryInput(options);
2230
+ const manifest = await loadManifest(directory);
2231
+ return withSpinner(Messages.PUBLISH_IN_PROGRESS(manifest.name), async () => {
2232
+ await Registry.publish(manifest, directory);
2233
+ });
2234
+ }
2235
+ };
2236
+
1668
2237
  // src/action/actions/start.action.ts
1669
- import { join as join7 } from "path";
2238
+ import dotenv from "dotenv";
2239
+ import { join as join10, resolve as resolve3 } from "path";
1670
2240
  var StartAction = class extends AbstractAction {
1671
2241
  static {
1672
2242
  __name(this, "StartAction");
@@ -1678,19 +2248,12 @@ var StartAction = class extends AbstractAction {
1678
2248
  const directory = getDirectoryInput(options);
1679
2249
  const config2 = await getConfig(options, directory);
1680
2250
  const watch3 = getWatchInput(options);
1681
- const ports = this.resolvePorts(options, config2);
2251
+ const port = getStringInputWithDefault(options, "port", config2.client.port);
1682
2252
  const ssl = this.resolveSSL(options);
1683
- const tasks = this.buildStartTasks(config2, directory, watch3, ports, ssl);
2253
+ const tasks = this.buildStartTasks(config2, directory, watch3, port, ssl);
1684
2254
  await Promise.all(tasks);
1685
2255
  return { keepAlive: true };
1686
2256
  }
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
2257
  resolveSSL(options) {
1695
2258
  const cert = getStringInput(options, "cert");
1696
2259
  const key = getStringInput(options, "key");
@@ -1702,53 +2265,97 @@ var StartAction = class extends AbstractAction {
1702
2265
  key
1703
2266
  };
1704
2267
  }
1705
- buildStartTasks(config2, directory, watch3, ports, ssl) {
2268
+ buildStartTasks(config2, directory, watch3, port, ssl) {
2269
+ const env2 = this.parseEnv(directory);
1706
2270
  const tasks = [];
1707
2271
  if (config2.server.enable) {
1708
- tasks.push(this.startServer(directory, config2.server.runtime.dir, watch3, ports.serverPort));
2272
+ tasks.push(this.startServer(directory, config2, { watch: watch3 }, env2));
1709
2273
  }
1710
- tasks.push(this.startClient(directory, config2, watch3, ports, ssl));
2274
+ tasks.push(this.startClient(directory, config2, { watch: watch3, port, ssl }, env2));
1711
2275
  return tasks;
1712
2276
  }
1713
- async startClient(directory, config2, watch3, ports, ssl) {
2277
+ async startClient(directory, config2, options, env2) {
1714
2278
  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))
2279
+ const params = this.buildClientParams(directory, config2, options);
2280
+ await this.runLoader("Client", loaderPath, params, env2.client);
2281
+ }
2282
+ async startServer(directory, config2, options, env2) {
2283
+ const loaderPath = getModulePath("@nanoforge-dev/loader-server/package.json", true);
2284
+ const params = this.buildServerParams(directory, config2, options);
2285
+ await this.runLoader("Server", loaderPath, params, env2.server);
2286
+ }
2287
+ buildClientParams(directory, config2, options) {
2288
+ const params = {
2289
+ "-d": getCwd(join10(directory, config2.client.runtime.dir)),
2290
+ "-p": options.port
1723
2291
  };
1724
- if (ports.gameExposurePort) {
1725
- env2["GAME_EXPOSURE_PORT"] = ports.gameExposurePort;
1726
- }
1727
- if (watch3) {
1728
- env2["WATCH"] = "true";
2292
+ if (options.watch) params["--watch"] = true;
2293
+ if (options.watch) {
2294
+ params["--watch"] = true;
1729
2295
  if (config2.server.enable) {
1730
- env2["WATCH_SERVER_GAME_DIR"] = getCwd(join7(directory, config2.server.runtime.dir));
2296
+ params["--watch-server-dir"] = getCwd(join10(directory, config2.server.runtime.dir));
1731
2297
  }
1732
2298
  }
1733
- if (ssl) {
1734
- env2["CERT"] = ssl.cert;
1735
- env2["KEY"] = ssl.key;
2299
+ if (options.ssl) {
2300
+ params["--cert"] = options.ssl.cert;
2301
+ params["--key"] = options.ssl.key;
1736
2302
  }
1737
- return env2;
2303
+ return this.buildParams(params);
1738
2304
  }
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))
2305
+ buildServerParams(directory, config2, options) {
2306
+ const params = {
2307
+ "-d": getCwd(join10(directory, config2.server.runtime.dir))
2308
+ };
2309
+ if (options.watch) params["--watch"] = true;
2310
+ return this.buildParams(params);
2311
+ }
2312
+ buildParams(params) {
2313
+ return Object.entries(params).map(([key, value]) => typeof value === "string" ? [key, value] : [key]).flat();
2314
+ }
2315
+ parseEnv(dir) {
2316
+ const prefix = "NANOFORGE_";
2317
+ const clientPrefix = `${prefix}CLIENT_`;
2318
+ const serverPrefix = `${prefix}SERVER_`;
2319
+ const rawEnv = {
2320
+ ...process.env
2321
+ };
2322
+ dotenv.config({
2323
+ path: resolve3(getCwd(join10(dir, ".env"))),
2324
+ processEnv: rawEnv
2325
+ });
2326
+ const baseEnv = Object.entries(rawEnv).filter(
2327
+ ([key, value]) => key.startsWith(prefix) && !!value
2328
+ );
2329
+ return {
2330
+ client: Object.fromEntries(
2331
+ baseEnv.filter(([key]) => !key.startsWith(serverPrefix)).map(([key, value]) => [key.replace(clientPrefix, prefix), value])
2332
+ ),
2333
+ server: Object.fromEntries(
2334
+ baseEnv.filter(([key]) => !key.startsWith(clientPrefix)).map(([key, value]) => [key.replace(serverPrefix, prefix), value])
2335
+ )
1743
2336
  };
1744
- if (port) env2["PORT"] = port;
1745
- if (watch3) env2["WATCH"] = "true";
1746
- await this.runLoader("Server", loaderPath, env2);
1747
2337
  }
1748
- async runLoader(name, directory, env2) {
2338
+ async runLoader(name, directory, params, env2) {
1749
2339
  await runSafe(async () => {
1750
2340
  const packageManager = await PackageManagerFactory.find(directory);
1751
- await packageManager.run(name, directory, "start", env2, [], true);
2341
+ await packageManager.run(name, directory, "start", params, env2, [], true);
2342
+ });
2343
+ }
2344
+ };
2345
+
2346
+ // src/action/actions/unpublish.action.ts
2347
+ var UnpublishAction = class extends AbstractAction {
2348
+ static {
2349
+ __name(this, "UnpublishAction");
2350
+ }
2351
+ startMessage = Messages.UNPUBLISH_START;
2352
+ successMessage = Messages.UNPUBLISH_SUCCESS;
2353
+ failureMessage = Messages.UNPUBLISH_FAILED;
2354
+ async handle(_args, options) {
2355
+ const directory = getDirectoryInput(options);
2356
+ const manifest = await loadManifest(directory);
2357
+ return withSpinner(Messages.UNPUBLISH_IN_PROGRESS(manifest.name), async () => {
2358
+ await Registry.unpublish(manifest, directory);
1752
2359
  });
1753
2360
  }
1754
2361
  };
@@ -1776,7 +2383,7 @@ var BuildCommand = class extends AbstractCommand {
1776
2383
  __name(this, "BuildCommand");
1777
2384
  }
1778
2385
  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) => {
2386
+ program2.command("build").description("build your game").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--client-outDir [clientDirectory]", "specify the output directory of the client").option("--server-outDir [serverDirectory]", "specify the output directory of the server").option("--watch", "build app in watching mode", false).action(async (rawOptions) => {
1780
2387
  const options = AbstractCommand.mapToInput({
1781
2388
  directory: rawOptions.directory,
1782
2389
  config: rawOptions.config,
@@ -1795,7 +2402,7 @@ var DevCommand = class extends AbstractCommand {
1795
2402
  __name(this, "DevCommand");
1796
2403
  }
1797
2404
  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) => {
2405
+ 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
2406
  const options = AbstractCommand.mapToInput({
1800
2407
  directory: rawOptions.directory,
1801
2408
  config: rawOptions.config,
@@ -1812,7 +2419,7 @@ var GenerateCommand = class extends AbstractCommand {
1812
2419
  __name(this, "GenerateCommand");
1813
2420
  }
1814
2421
  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) => {
2422
+ program2.command("generate").description("generate nanoforge files from config").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--watch", "generate app in watching mode", false).action(async (rawOptions) => {
1816
2423
  const options = AbstractCommand.mapToInput({
1817
2424
  directory: rawOptions.directory,
1818
2425
  config: rawOptions.config,
@@ -1829,9 +2436,15 @@ var InstallCommand = class extends AbstractCommand {
1829
2436
  __name(this, "InstallCommand");
1830
2437
  }
1831
2438
  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) => {
2439
+ 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(
2440
+ "-s, --server",
2441
+ "install components/systems on server (default install on client)",
2442
+ false
2443
+ ).action(async (names, rawOptions) => {
1833
2444
  const options = AbstractCommand.mapToInput({
1834
- directory: rawOptions.directory
2445
+ directory: rawOptions.directory,
2446
+ lib: rawOptions.lib,
2447
+ server: rawOptions.server
1835
2448
  });
1836
2449
  const args = AbstractCommand.mapToInput({
1837
2450
  names: names.length ? names : void 0
@@ -1841,13 +2454,49 @@ var InstallCommand = class extends AbstractCommand {
1841
2454
  }
1842
2455
  };
1843
2456
 
2457
+ // src/command/commands/login.command.ts
2458
+ var LoginCommand = class extends AbstractCommand {
2459
+ static {
2460
+ __name(this, "LoginCommand");
2461
+ }
2462
+ load(program2) {
2463
+ 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) => {
2464
+ const options = AbstractCommand.mapToInput({
2465
+ directory: rawOptions.directory,
2466
+ local: rawOptions.local,
2467
+ apiKey: rawOptions.apiKey
2468
+ });
2469
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2470
+ });
2471
+ }
2472
+ };
2473
+
2474
+ // src/command/commands/logout.command.ts
2475
+ var LogoutCommand = class extends AbstractCommand {
2476
+ static {
2477
+ __name(this, "LogoutCommand");
2478
+ }
2479
+ load(program2) {
2480
+ 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) => {
2481
+ const options = AbstractCommand.mapToInput({
2482
+ directory: rawOptions.directory,
2483
+ local: rawOptions.local
2484
+ });
2485
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2486
+ });
2487
+ }
2488
+ };
2489
+
1844
2490
  // src/command/commands/new.command.ts
1845
2491
  var NewCommand = class extends AbstractCommand {
1846
2492
  static {
1847
2493
  __name(this, "NewCommand");
1848
2494
  }
1849
2495
  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) => {
2496
+ program2.command("new").description("create a new nanoforge project").option("-d, --directory [directory]", "specify the working directory of the command").option("--name [name]", "specify the name of your project").option(
2497
+ "--path [path]",
2498
+ "specify the relative path where your project will be created (default: name of the project)"
2499
+ ).option("--package-manager [packageManager]", "specify the package manager of your project").option("--language [language]", "specify the language of your project").option("--strict", "use strict mode").option("--no-strict", "do not use strict mode").option("--server", "create a server").option("--no-server", "do not create a server").option("--init-functions", "initialize functions").option("--no-init-functions", "do not initialize functions").option("--skip-install", "skip installing dependencies").option("--no-skip-install", "do not skip installing dependencies").option("--docker", "generate docker files").option("--no-docker", "do not generate docker files").option("--no-lint", "do not generate lint files").action(async (rawOptions) => {
1851
2500
  const options = AbstractCommand.mapToInput({
1852
2501
  directory: rawOptions.directory,
1853
2502
  name: rawOptions.name,
@@ -1858,7 +2507,23 @@ var NewCommand = class extends AbstractCommand {
1858
2507
  server: rawOptions.server,
1859
2508
  initFunctions: rawOptions.initFunctions,
1860
2509
  skipInstall: rawOptions.skipInstall,
1861
- docker: rawOptions.docker
2510
+ docker: rawOptions.docker,
2511
+ lint: rawOptions.lint
2512
+ });
2513
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2514
+ });
2515
+ }
2516
+ };
2517
+
2518
+ // src/command/commands/publish.command.ts
2519
+ var PublishCommand = class extends AbstractCommand {
2520
+ static {
2521
+ __name(this, "PublishCommand");
2522
+ }
2523
+ load(program2) {
2524
+ program2.command("publish").description("publish package to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").action(async (rawOptions) => {
2525
+ const options = AbstractCommand.mapToInput({
2526
+ directory: rawOptions.directory
1862
2527
  });
1863
2528
  await this.action.run(/* @__PURE__ */ new Map(), options);
1864
2529
  });
@@ -1871,16 +2536,11 @@ var StartCommand = class extends AbstractCommand {
1871
2536
  __name(this, "StartCommand");
1872
2537
  }
1873
2538
  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) => {
2539
+ program2.command("start").description("start your game").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("-p, --port [port]", "specify the port of the loader (the website to load the game)").option("--watch", "run app in watching mode", false).option("--cert [cert]", "path to the SSL certificate for HTTPS").option("--key [key]", "path to the SSL key for HTTPS").action(async (rawOptions) => {
1878
2540
  const options = AbstractCommand.mapToInput({
1879
2541
  directory: rawOptions.directory,
1880
2542
  config: rawOptions.config,
1881
- clientPort: rawOptions.clientPort,
1882
- gameExposurePort: rawOptions.gameExposurePort,
1883
- serverPort: rawOptions.serverPort,
2543
+ port: rawOptions.port,
1884
2544
  watch: rawOptions.watch,
1885
2545
  cert: rawOptions.cert,
1886
2546
  key: rawOptions.key
@@ -1890,18 +2550,37 @@ var StartCommand = class extends AbstractCommand {
1890
2550
  }
1891
2551
  };
1892
2552
 
2553
+ // src/command/commands/unpublish.command.ts
2554
+ var UnpublishCommand = class extends AbstractCommand {
2555
+ static {
2556
+ __name(this, "UnpublishCommand");
2557
+ }
2558
+ load(program2) {
2559
+ program2.command("unpublish").description("unpublish package to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").action(async (rawOptions) => {
2560
+ const options = AbstractCommand.mapToInput({
2561
+ directory: rawOptions.directory
2562
+ });
2563
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2564
+ });
2565
+ }
2566
+ };
2567
+
1893
2568
  // src/command/command.loader.ts
1894
2569
  var CommandLoader = class {
1895
2570
  static {
1896
2571
  __name(this, "CommandLoader");
1897
2572
  }
1898
2573
  static async load(program2) {
2574
+ new NewCommand(new NewAction()).load(program2);
2575
+ new InstallCommand(new InstallAction()).load(program2);
1899
2576
  new BuildCommand(new BuildAction()).load(program2);
2577
+ new StartCommand(new StartAction()).load(program2);
1900
2578
  new DevCommand(new DevAction()).load(program2);
1901
2579
  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);
2580
+ new LoginCommand(new LoginAction()).load(program2);
2581
+ new LogoutCommand(new LogoutAction()).load(program2);
2582
+ new PublishCommand(new PublishAction()).load(program2);
2583
+ new UnpublishCommand(new UnpublishAction()).load(program2);
1905
2584
  this.handleInvalidCommand(program2);
1906
2585
  }
1907
2586
  static handleInvalidCommand(program2) {