@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.
@@ -56,6 +56,16 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
56
56
  INSTALL_SUCCESS: success("Installation completed!"),
57
57
  INSTALL_FAILED: failure("Installation failed!"),
58
58
  INSTALL_NAMES_QUESTION: "Which libraries do you want to install?",
59
+ INSTALL_PACKAGES_IN_PROGRESS: "Install Nanoforge Packages",
60
+ // --- Login ---
61
+ LOGIN_START: "NanoForge Login",
62
+ LOGIN_SUCCESS: success("Login completed!"),
63
+ LOGIN_FAILED: failure("Login failed!"),
64
+ LOGIN_API_KEY_QUESTION: "What is your registry api key?",
65
+ // --- Logout ---
66
+ LOGOUT_START: "NanoForge Logout",
67
+ LOGOUT_SUCCESS: success("Logout completed!"),
68
+ LOGOUT_FAILED: failure("Logout failed!"),
59
69
  // --- New Project ---
60
70
  NEW_START: "NanoForge Project Creation",
61
71
  NEW_SUCCESS: success("Project successfully created!"),
@@ -83,6 +93,16 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
83
93
  START_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Starting ${part}...`, "START_PART_IN_PROGRESS"),
84
94
  START_PART_SUCCESS: /* @__PURE__ */ __name((part) => success(`${part} terminated.`), "START_PART_SUCCESS"),
85
95
  START_PART_FAILED: /* @__PURE__ */ __name((part) => failure(`${part} failed!`), "START_PART_FAILED"),
96
+ // --- Publish ---
97
+ PUBLISH_START: "NanoForge Publish",
98
+ PUBLISH_SUCCESS: success("Publish completed!"),
99
+ PUBLISH_FAILED: failure("Publish failed!"),
100
+ PUBLISH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Publishing ${name}...`, "PUBLISH_IN_PROGRESS"),
101
+ // --- Unpublish ---
102
+ UNPUBLISH_START: "NanoForge Unpublish",
103
+ UNPUBLISH_SUCCESS: success("Unpublish completed!"),
104
+ UNPUBLISH_FAILED: failure("Unpublish failed!"),
105
+ UNPUBLISH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Unpublishing ${name}...`, "UNPUBLISH_IN_PROGRESS"),
86
106
  // --- Schematics ---
87
107
  SCHEMATICS_START: "Running schematics",
88
108
  SCHEMATIC_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Generating ${name}...`, "SCHEMATIC_IN_PROGRESS"),
@@ -172,16 +192,28 @@ var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
172
192
  return getBooleanInputWithDefault(inputs, "generate", false);
173
193
  }, "getDevGenerateInput");
174
194
 
195
+ // src/lib/input/inputs/install/lib.input.ts
196
+ function getInstallLibInput(inputs) {
197
+ return getBooleanInputWithDefault(inputs, "lib", false);
198
+ }
199
+ __name(getInstallLibInput, "getInstallLibInput");
200
+
175
201
  // src/lib/question/questions/confirm.question.ts
176
202
  import { confirm } from "@inquirer/prompts";
177
203
 
178
204
  // src/lib/utils/errors.ts
179
205
  import { red } from "ansis";
180
206
  var getErrorMessage = /* @__PURE__ */ __name((error) => {
181
- if (error instanceof Error) return error.message;
207
+ if (error instanceof Error) return getErrorString(error);
182
208
  if (typeof error === "string") return error;
183
209
  return void 0;
184
210
  }, "getErrorMessage");
211
+ var getErrorString = /* @__PURE__ */ __name((error) => {
212
+ const stack = error.stack ? error.stack : error.message;
213
+ const cause = error.cause && typeof error.cause === "object" ? JSON.stringify(error.cause, null, 2) : error.cause;
214
+ return `${stack}${cause ? `
215
+ ${cause}` : ""}`;
216
+ }, "getErrorString");
185
217
  var handleActionError = /* @__PURE__ */ __name((context, error) => {
186
218
  console.error();
187
219
  console.error(red(context));
@@ -265,6 +297,134 @@ var getInstallNamesInputOrAsk = /* @__PURE__ */ __name((inputs) => {
265
297
  );
266
298
  }, "getInstallNamesInputOrAsk");
267
299
 
300
+ // src/lib/input/inputs/install/server.input.ts
301
+ function getInstallServerInput(inputs) {
302
+ return getBooleanInputWithDefault(inputs, "server", false);
303
+ }
304
+ __name(getInstallServerInput, "getInstallServerInput");
305
+
306
+ // src/lib/input/inputs/login-out/api-key.input.ts
307
+ var getApiKeyInput = /* @__PURE__ */ __name((inputs) => {
308
+ return getStringInput(inputs, "apiKey");
309
+ }, "getApiKeyInput");
310
+ var getLoginApiKeyInputOrAsk = /* @__PURE__ */ __name((inputs) => {
311
+ return getInputOrAsk(
312
+ getApiKeyInput(inputs),
313
+ () => askInput(Messages.LOGIN_API_KEY_QUESTION, {
314
+ required: true
315
+ })
316
+ );
317
+ }, "getLoginApiKeyInputOrAsk");
318
+
319
+ // src/lib/input/inputs/login-out/local.input.ts
320
+ function getLocalInput(inputs) {
321
+ return getBooleanInputWithDefault(inputs, "local", false);
322
+ }
323
+ __name(getLocalInput, "getLocalInput");
324
+
325
+ // src/lib/input/inputs/new/docker.input.ts
326
+ var getNewDockerInput = /* @__PURE__ */ __name((inputs) => {
327
+ return getBooleanInput(inputs, "docker");
328
+ }, "getNewDockerInput");
329
+ var getNewDockerOrAsk = /* @__PURE__ */ __name((inputs) => {
330
+ return getInputOrAsk(
331
+ getNewDockerInput(inputs),
332
+ () => askConfirm(Messages.NEW_DOCKER_QUESTION, { default: true })
333
+ );
334
+ }, "getNewDockerOrAsk");
335
+
336
+ // src/lib/input/inputs/new/init-functions.input.ts
337
+ var getNewInitFunctionsWithDefault = /* @__PURE__ */ __name((inputs) => {
338
+ return getBooleanInputWithDefault(inputs, "initFunctions", false);
339
+ }, "getNewInitFunctionsWithDefault");
340
+
341
+ // src/lib/input/inputs/new/language.input.ts
342
+ var getLanguageInput = /* @__PURE__ */ __name((inputs) => {
343
+ return getStringInput(inputs, "language");
344
+ }, "getLanguageInput");
345
+ var getNewLanguageInputOrAsk = /* @__PURE__ */ __name((inputs) => {
346
+ return getInputOrAsk(
347
+ getLanguageInput(inputs),
348
+ () => askSelect(Messages.NEW_LANGUAGE_QUESTION, [{ value: "ts" }, { value: "js" }], {
349
+ default: "ts"
350
+ })
351
+ );
352
+ }, "getNewLanguageInputOrAsk");
353
+
354
+ // src/lib/input/inputs/new/lint.input.ts
355
+ var getNewLintInput = /* @__PURE__ */ __name((inputs) => {
356
+ return getBooleanInputWithDefault(inputs, "lint", true);
357
+ }, "getNewLintInput");
358
+
359
+ // src/lib/input/inputs/new/name.input.ts
360
+ var getNameInput = /* @__PURE__ */ __name((inputs) => {
361
+ return getStringInput(inputs, "name");
362
+ }, "getNameInput");
363
+ var getNewNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
364
+ return getInputOrAsk(
365
+ getNameInput(inputs),
366
+ () => askInput(Messages.NEW_NAME_QUESTION, {
367
+ required: true,
368
+ default: "nanoforge-app"
369
+ })
370
+ );
371
+ }, "getNewNameInputOrAsk");
372
+
373
+ // src/lib/input/inputs/new/package-manager.input.ts
374
+ var getPackageManagerInput = /* @__PURE__ */ __name((inputs) => {
375
+ return getStringInput(inputs, "packageManager");
376
+ }, "getPackageManagerInput");
377
+ var getNewPackageManagerInputOrAsk = /* @__PURE__ */ __name((inputs) => {
378
+ return getInputOrAsk(
379
+ getPackageManagerInput(inputs),
380
+ () => askSelect(
381
+ Messages.NEW_PACKAGE_MANAGER_QUESTION,
382
+ [{ value: "npm" }, { value: "yarn" }, { value: "pnpm" }, { value: "bun" }],
383
+ {
384
+ default: "npm"
385
+ }
386
+ )
387
+ );
388
+ }, "getNewPackageManagerInputOrAsk");
389
+
390
+ // src/lib/input/inputs/new/path.input.ts
391
+ var getNewPathInput = /* @__PURE__ */ __name((inputs) => {
392
+ return getStringInput(inputs, "path");
393
+ }, "getNewPathInput");
394
+
395
+ // src/lib/input/inputs/new/server.input.ts
396
+ var getNewServerInput = /* @__PURE__ */ __name((inputs) => {
397
+ return getBooleanInput(inputs, "server");
398
+ }, "getNewServerInput");
399
+ var getNewServerOrAsk = /* @__PURE__ */ __name((inputs) => {
400
+ return getInputOrAsk(
401
+ getNewServerInput(inputs),
402
+ () => askConfirm(Messages.NEW_SERVER_QUESTION, { default: false })
403
+ );
404
+ }, "getNewServerOrAsk");
405
+
406
+ // src/lib/input/inputs/new/skip-install.input.ts
407
+ var getNewSkipInstallInput = /* @__PURE__ */ __name((inputs) => {
408
+ return getBooleanInput(inputs, "skipInstall");
409
+ }, "getNewSkipInstallInput");
410
+ var getNewSkipInstallOrAsk = /* @__PURE__ */ __name((inputs) => {
411
+ return getInputOrAsk(
412
+ getNewSkipInstallInput(inputs),
413
+ () => askConfirm(Messages.NEW_SKIP_INSTALL_QUESTION, { default: false })
414
+ );
415
+ }, "getNewSkipInstallOrAsk");
416
+
417
+ // src/lib/input/inputs/new/strict.input.ts
418
+ var getNewStrictInput = /* @__PURE__ */ __name((inputs) => {
419
+ return getBooleanInput(inputs, "strict");
420
+ }, "getNewStrictInput");
421
+ var getNewStrictOrAsk = /* @__PURE__ */ __name((inputs) => {
422
+ return getInputOrAsk(
423
+ getNewStrictInput(inputs),
424
+ () => askConfirm(Messages.NEW_STRICT_QUESTION, { default: true })
425
+ );
426
+ }, "getNewStrictOrAsk");
427
+
268
428
  // src/lib/package-manager/package-manager.ts
269
429
  import { bold, red as red3 } from "ansis";
270
430
 
@@ -312,6 +472,21 @@ var resolveCLINodeBinaryPath = /* @__PURE__ */ __name((name) => {
312
472
  throw new Error("Could not find module path");
313
473
  }, "resolveCLINodeBinaryPath");
314
474
 
475
+ // src/lib/utils/spinner.ts
476
+ var withSpinner = /* @__PURE__ */ __name(async (message, task, onError) => {
477
+ const spinner = getSpinner(message);
478
+ spinner.start();
479
+ try {
480
+ const value = await task(spinner);
481
+ spinner.succeed();
482
+ return { success: true, value };
483
+ } catch (error) {
484
+ spinner.fail();
485
+ if (onError) onError();
486
+ return { success: false, error };
487
+ }
488
+ }, "withSpinner");
489
+
315
490
  // src/lib/package-manager/package-manager.ts
316
491
  var PackageManager = class {
317
492
  constructor(name, commands, runner) {
@@ -324,7 +499,7 @@ var PackageManager = class {
324
499
  }
325
500
  async install(directory) {
326
501
  const args = [this.commands.install, this.commands.silentFlag];
327
- const result = await this.withSpinner(
502
+ const result = await withSpinner(
328
503
  Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
329
504
  async (spinner) => {
330
505
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -352,7 +527,7 @@ var PackageManager = class {
352
527
  output,
353
528
  ...flags
354
529
  ];
355
- const result = await this.withSpinner(
530
+ const result = await withSpinner(
356
531
  message,
357
532
  async (spinner) => {
358
533
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -361,10 +536,10 @@ var PackageManager = class {
361
536
  );
362
537
  return result.success;
363
538
  }
364
- async run(name, directory, script, env2 = {}, flags = [], silent = false) {
539
+ async run(name, directory, script, params, env2 = {}, flags = [], silent = false) {
365
540
  console.info(Messages.START_PART_IN_PROGRESS(name));
366
541
  try {
367
- const args = this.buildRunArgs(script, flags, silent);
542
+ const args = this.buildRunArgs(script, params, flags, silent);
368
543
  await this.exec(args, directory, {
369
544
  env: env2,
370
545
  listeners: {
@@ -381,32 +556,21 @@ var PackageManager = class {
381
556
  }
382
557
  async runDev(directory, command, env2 = {}, flags = [], collect = true) {
383
558
  try {
384
- await this.exec([this.commands.run, command, ...flags], directory, { collect, env: env2 });
559
+ const base = [this.commands.exec, command];
560
+ if (this.commands.runArgsFlag) base.push(this.commands.runArgsFlag);
561
+ await this.exec([...base, ...flags], directory, { collect, env: env2 });
385
562
  return true;
386
563
  } catch {
387
564
  return false;
388
565
  }
389
566
  }
390
- async withSpinner(message, task, onError) {
391
- const spinner = getSpinner(message);
392
- spinner.start();
393
- try {
394
- const value = await task(spinner);
395
- spinner.succeed();
396
- return { success: true, value };
397
- } catch {
398
- spinner.fail();
399
- if (onError) onError();
400
- return { success: false };
401
- }
402
- }
403
567
  async addDependencies(saveFlag, directory, dependencies) {
404
568
  if (!dependencies.length) {
405
569
  this.logEmpty(Messages.PACKAGE_MANAGER_INSTALLATION_NOTHING);
406
570
  return true;
407
571
  }
408
572
  const args = [this.commands.add, saveFlag, ...dependencies];
409
- const result = await this.withSpinner(
573
+ const result = await withSpinner(
410
574
  Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
411
575
  async (spinner) => {
412
576
  await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
@@ -421,11 +585,13 @@ var PackageManager = class {
421
585
  throw new Error(`Package manager "${this.name}" does not support "${feature}"`);
422
586
  }
423
587
  }
424
- buildRunArgs(script, flags, silent) {
588
+ buildRunArgs(script, params, flags, silent) {
425
589
  const args = [...flags, this.commands.run];
426
590
  if (silent) args.push(this.commands.silentFlag);
427
591
  args.push(script);
428
- return args;
592
+ if (params.length === 0) return args;
593
+ if (this.commands.runArgsFlag) args.push(this.commands.runArgsFlag);
594
+ return args.concat(params);
429
595
  }
430
596
  exec(args, directory, options = {}) {
431
597
  return this.runner.run(args, {
@@ -478,12 +644,12 @@ var Runner = class {
478
644
  const { collect = false, cwd: cwd2 = process2.cwd(), env: env2, listeners, onFail } = options;
479
645
  const spawnOpts = this.buildSpawnOptions(collect, cwd2, env2);
480
646
  const fullArgs = [...this.baseArgs, ...args];
481
- return new Promise((resolve3, reject) => {
647
+ return new Promise((resolve4, reject) => {
482
648
  const child = spawn(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
483
649
  const output = this.attachOutputHandlers(child, listeners);
484
650
  child.on("close", (code) => {
485
651
  if (code === 0) {
486
- resolve3(this.formatOutput(output, collect));
652
+ resolve4(this.formatOutput(output, collect));
487
653
  } else {
488
654
  this.handleFailure(output, fullArgs, onFail);
489
655
  reject(this.createError(fullArgs, code));
@@ -598,6 +764,7 @@ var PM_CONFIGS = {
598
764
  remove: "uninstall",
599
765
  exec: "exec",
600
766
  run: "run",
767
+ runArgsFlag: "--",
601
768
  saveFlag: "--save",
602
769
  saveDevFlag: "--save-dev",
603
770
  silentFlag: "--silent"
@@ -722,7 +889,6 @@ var ClientConfig = class {
722
889
  __name(this, "ClientConfig");
723
890
  }
724
891
  port;
725
- gameExposurePort;
726
892
  build;
727
893
  runtime;
728
894
  };
@@ -730,10 +896,6 @@ __decorateClass([
730
896
  Expose(),
731
897
  IsPort()
732
898
  ], ClientConfig.prototype, "port", 2);
733
- __decorateClass([
734
- Expose(),
735
- IsPort()
736
- ], ClientConfig.prototype, "gameExposurePort", 2);
737
899
  __decorateClass([
738
900
  Expose(),
739
901
  Type(() => BuildConfig),
@@ -749,7 +911,6 @@ var ServerConfig = class {
749
911
  __name(this, "ServerConfig");
750
912
  }
751
913
  enable;
752
- port;
753
914
  build;
754
915
  runtime;
755
916
  };
@@ -757,10 +918,6 @@ __decorateClass([
757
918
  Expose(),
758
919
  IsBoolean()
759
920
  ], ServerConfig.prototype, "enable", 2);
760
- __decorateClass([
761
- Expose(),
762
- IsPort()
763
- ], ServerConfig.prototype, "port", 2);
764
921
  __decorateClass([
765
922
  Expose(),
766
923
  Type(() => BuildConfig),
@@ -813,7 +970,10 @@ import { join as join2 } from "path";
813
970
 
814
971
  // src/lib/constants.ts
815
972
  var CONFIG_FILE_NAME = "nanoforge.config.json";
973
+ var MANIFEST_FILE_NAME = "nanoforge.manifest.json";
974
+ var GLOBAL_CONFIG_FILE_NAME = ".nanoforgerc";
816
975
  var NANOFORGE_DIR = ".nanoforge";
976
+ var REGISTRY_URL = "https://api.nanoforge.dev";
817
977
 
818
978
  // src/lib/utils/object.ts
819
979
  var isObject = /* @__PURE__ */ __name((item) => {
@@ -834,6 +994,7 @@ var deepMerge = /* @__PURE__ */ __name((target, ...sources) => {
834
994
  }
835
995
  return deepMerge(target, ...sources);
836
996
  }, "deepMerge");
997
+ var isEmpty = /* @__PURE__ */ __name((target) => Object.keys(target).length === 0, "isEmpty");
837
998
 
838
999
  // src/lib/config/config-defaults.ts
839
1000
  var CONFIG_DEFAULTS = {
@@ -842,7 +1003,6 @@ var CONFIG_DEFAULTS = {
842
1003
  initFunctions: true,
843
1004
  client: {
844
1005
  port: "3000",
845
- gameExposurePort: "3001",
846
1006
  build: {
847
1007
  entryFile: "client/main.ts",
848
1008
  outDir: ".nanoforge/client"
@@ -853,7 +1013,6 @@ var CONFIG_DEFAULTS = {
853
1013
  },
854
1014
  server: {
855
1015
  enable: false,
856
- port: "3002",
857
1016
  build: {
858
1017
  entryFile: "server/main.ts",
859
1018
  outDir: ".nanoforge/server"
@@ -928,7 +1087,7 @@ var AbstractAction = class {
928
1087
  if (keepAlive) return;
929
1088
  console.info();
930
1089
  if (!success2) {
931
- if (this.failureMessage) console.error(this.failureMessage);
1090
+ handleActionError(this.failureMessage, result.error);
932
1091
  process.exit(1);
933
1092
  }
934
1093
  if (this.successMessage) console.info(this.successMessage);
@@ -1301,6 +1460,310 @@ var GenerateAction = class extends AbstractAction {
1301
1460
  }
1302
1461
  };
1303
1462
 
1463
+ // src/action/actions/install.action.ts
1464
+ import { join as join6 } from "path";
1465
+
1466
+ // src/lib/global-config/global-config-handler.ts
1467
+ import { read, readUser, write, writeUser } from "rc9";
1468
+
1469
+ // src/lib/global-config/global-config-defaults.ts
1470
+ var GLOBAL_CONFIG_DEFAULTS = {};
1471
+
1472
+ // src/lib/global-config/global-config-handler.ts
1473
+ var GlobalConfigHandler = class {
1474
+ static {
1475
+ __name(this, "GlobalConfigHandler");
1476
+ }
1477
+ static read(dir) {
1478
+ const localConfig = this._readConfig(read, false, dir);
1479
+ if (localConfig) return localConfig;
1480
+ return this._readConfig(readUser, true);
1481
+ }
1482
+ static write(config2, local = false, dir) {
1483
+ const options = {
1484
+ name: GLOBAL_CONFIG_FILE_NAME,
1485
+ dir
1486
+ };
1487
+ if (local) write(config2, options);
1488
+ else writeUser(config2, options);
1489
+ }
1490
+ static _readConfig(func, force, dir) {
1491
+ const res = func({
1492
+ name: GLOBAL_CONFIG_FILE_NAME,
1493
+ dir
1494
+ });
1495
+ if (!force) {
1496
+ if (isEmpty(res)) return null;
1497
+ }
1498
+ return deepMerge(GLOBAL_CONFIG_DEFAULTS, res);
1499
+ }
1500
+ };
1501
+
1502
+ // src/lib/http/http-client.ts
1503
+ var HttpClient = class {
1504
+ static {
1505
+ __name(this, "HttpClient");
1506
+ }
1507
+ _baseUrl;
1508
+ _baseOptions;
1509
+ _middlewares;
1510
+ constructor(baseUrl, options) {
1511
+ this._baseUrl = baseUrl;
1512
+ this._baseOptions = options ?? {
1513
+ headers: {
1514
+ "Content-Type": "application/json"
1515
+ }
1516
+ };
1517
+ this._middlewares = [];
1518
+ }
1519
+ get(path, options) {
1520
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1521
+ return this._request(newPath, {
1522
+ ...newOptions,
1523
+ method: "GET"
1524
+ });
1525
+ });
1526
+ }
1527
+ post(path, body, options) {
1528
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1529
+ return this._request(newPath, {
1530
+ ...newOptions,
1531
+ method: "POST",
1532
+ body
1533
+ });
1534
+ });
1535
+ }
1536
+ put(path, body, options) {
1537
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1538
+ return this._request(newPath, {
1539
+ ...newOptions,
1540
+ method: "PUT",
1541
+ body
1542
+ });
1543
+ });
1544
+ }
1545
+ patch(path, body, options) {
1546
+ return this._applyMiddlewares(path, options, async (newPath, newOptions) => {
1547
+ return this._request(newPath, {
1548
+ ...newOptions,
1549
+ method: "PATCH",
1550
+ body
1551
+ });
1552
+ });
1553
+ }
1554
+ delete(path, options) {
1555
+ return this._applyMiddlewares(path, options, (newPath, newOptions) => {
1556
+ return this._request(newPath, {
1557
+ ...newOptions,
1558
+ method: "DELETE",
1559
+ body: "{}"
1560
+ });
1561
+ });
1562
+ }
1563
+ useMiddlewares(...middlewares) {
1564
+ for (const middleware of middlewares) this._middlewares.push(middleware);
1565
+ return this;
1566
+ }
1567
+ async _request(path, request) {
1568
+ const res = await fetch(path, request);
1569
+ res.content = null;
1570
+ return res;
1571
+ }
1572
+ _applyMiddlewares(path, options, callback) {
1573
+ const baseParams = {
1574
+ path,
1575
+ fullPath: this._getUrl(path),
1576
+ options: {
1577
+ ...this._baseOptions,
1578
+ ...options,
1579
+ headers: {
1580
+ ...this._baseOptions.headers,
1581
+ ...options?.headers
1582
+ }
1583
+ }
1584
+ };
1585
+ const middlewares = this._middlewares.slice();
1586
+ let response;
1587
+ const execution = /* @__PURE__ */ __name(async (params) => {
1588
+ if (!params) params = baseParams;
1589
+ const middleware = middlewares.shift();
1590
+ if (!middleware) response = await callback(params.fullPath, params.options);
1591
+ else response = await middleware(params, execution) ?? response;
1592
+ return response;
1593
+ }, "execution");
1594
+ return execution(baseParams);
1595
+ }
1596
+ _getUrl(path) {
1597
+ return `${this._baseUrl}${path}`;
1598
+ }
1599
+ };
1600
+
1601
+ // src/lib/http/repository.ts
1602
+ var Repository = class {
1603
+ static {
1604
+ __name(this, "Repository");
1605
+ }
1606
+ _client;
1607
+ constructor(client2) {
1608
+ this._client = client2;
1609
+ }
1610
+ get(path, options) {
1611
+ return this.runRequest("get", path, options);
1612
+ }
1613
+ getFile(path, options) {
1614
+ return this.runFileRequest("get", path, options);
1615
+ }
1616
+ post(path, body, options) {
1617
+ return this.runRequestBody("post", path, body ?? {}, options);
1618
+ }
1619
+ put(path, body, options) {
1620
+ return this.runRequestBody("put", path, body ?? {}, options);
1621
+ }
1622
+ patch(path, body, options) {
1623
+ return this.runRequestBody("patch", path, body ?? {}, options);
1624
+ }
1625
+ delete(path, options) {
1626
+ return this.runRequest("delete", path, options);
1627
+ }
1628
+ async runRequest(request, path, options) {
1629
+ const res = await this._client[request](path, options);
1630
+ const data = await res.json();
1631
+ if (!res.ok)
1632
+ throw new Error(`Request failed with status code ${res.status}`, {
1633
+ cause: data["error"]
1634
+ });
1635
+ return data;
1636
+ }
1637
+ async runFileRequest(request, path, options) {
1638
+ const res = await this._client[request](path, options);
1639
+ if (!res.ok)
1640
+ throw new Error(`Request failed with status code ${res.status}`, {
1641
+ cause: (await res.json())["error"]
1642
+ });
1643
+ return await res.blob();
1644
+ }
1645
+ async runRequestBody(request, path, body, options) {
1646
+ const res = await this._client[request](
1647
+ path,
1648
+ body === void 0 ? void 0 : body instanceof FormData ? body : JSON.stringify(body),
1649
+ options
1650
+ );
1651
+ const data = await res.json();
1652
+ if (!res.ok)
1653
+ throw new Error(`Request failed with status code ${res.status}`, {
1654
+ cause: data["error"]
1655
+ });
1656
+ return data;
1657
+ }
1658
+ };
1659
+
1660
+ // src/lib/http/client.ts
1661
+ var client = new HttpClient(REGISTRY_URL ?? "");
1662
+ var api = new Repository(client);
1663
+ var withAuth = /* @__PURE__ */ __name((apiKey, force = false, headers = {
1664
+ "Content-Type": "application/json"
1665
+ }) => {
1666
+ if (!apiKey && force) {
1667
+ console.error("No registry key found. Please use `nf login` to login");
1668
+ throw new Error("No apikey found. Please use `nf login` to login");
1669
+ }
1670
+ return new Repository(
1671
+ new HttpClient(REGISTRY_URL ?? "", {
1672
+ headers: {
1673
+ Authorization: apiKey,
1674
+ ...headers
1675
+ }
1676
+ })
1677
+ );
1678
+ }, "withAuth");
1679
+
1680
+ // src/lib/manifest/manifest-resolver.ts
1681
+ var resolveManifestDependencies = /* @__PURE__ */ __name(async (names, dir) => {
1682
+ const client2 = withAuth(GlobalConfigHandler.read(dir).apiKey, false);
1683
+ return concatDeps(await Promise.all(names.map(async (d) => resolveDeps(d, client2))));
1684
+ }, "resolveManifestDependencies");
1685
+ var resolveManifest = /* @__PURE__ */ __name(async (name, client2) => {
1686
+ return await client2.get(`/registry/${name}`);
1687
+ }, "resolveManifest");
1688
+ var resolveDeps = /* @__PURE__ */ __name(async (name, client2) => {
1689
+ const manifest = await resolveManifest(name, client2);
1690
+ const baseDeps = manifest.dependencies ?? [];
1691
+ const deps = await Promise.all(baseDeps.map(async (d) => resolveDeps(d, client2)));
1692
+ return concatDeps(
1693
+ [{ nf: { [manifest.name]: manifest }, npm: getNpmDeps(manifest) }].concat(deps)
1694
+ );
1695
+ }, "resolveDeps");
1696
+ var getNpmDeps = /* @__PURE__ */ __name((manifest) => {
1697
+ return Object.entries(manifest.npmDependencies ?? {});
1698
+ }, "getNpmDeps");
1699
+ var concatDeps = /* @__PURE__ */ __name((deps) => {
1700
+ return {
1701
+ npm: deps.map(({ npm }) => npm).flat(),
1702
+ nf: Object.fromEntries(deps.map(({ nf }) => Object.entries(nf)).flat())
1703
+ };
1704
+ }, "concatDeps");
1705
+
1706
+ // src/lib/registry/registry.ts
1707
+ import fs3 from "fs";
1708
+ import { join as join5 } from "path";
1709
+ var Registry = class {
1710
+ static {
1711
+ __name(this, "Registry");
1712
+ }
1713
+ static async publish(manifest, dir) {
1714
+ const client2 = this._getClient(dir, true, false);
1715
+ const filename = manifest.publish?.paths?.package ?? "index.ts";
1716
+ const file = await this._getPackageFile(filename, dir);
1717
+ const data = new FormData();
1718
+ for (const key of Object.keys(manifest)) {
1719
+ const value = manifest[key];
1720
+ if (!value) continue;
1721
+ data.append(key, typeof value === "string" ? value : JSON.stringify(value));
1722
+ }
1723
+ data.append("_packageFile", file, filename);
1724
+ await client2.put(`/registry/${manifest.name}`, data);
1725
+ }
1726
+ static async unpublish(manifest, dir) {
1727
+ const client2 = this._getClient(dir, true);
1728
+ await client2.delete(`/registry/${manifest.name}`);
1729
+ }
1730
+ static async install(manifests, dir) {
1731
+ const cwd2 = getCwd(dir);
1732
+ const client2 = this._getClient(dir, false);
1733
+ for (const manifest of manifests) {
1734
+ await this.installPackage(client2, manifest, cwd2);
1735
+ }
1736
+ }
1737
+ static async installPackage(client2, manifest, dir) {
1738
+ const file = await client2.getFile(`/registry/${manifest.name}/-/${manifest._file}`);
1739
+ const path = join5(dir, this.getTypeSubFolder(manifest.type));
1740
+ fs3.mkdirSync(path, { recursive: true });
1741
+ fs3.writeFileSync(join5(path, manifest._file), await file.bytes());
1742
+ }
1743
+ static getTypeSubFolder(type) {
1744
+ if (type === "component") return "components";
1745
+ if (type === "system") return "systems";
1746
+ return ".";
1747
+ }
1748
+ static _getClient(dir, force, headers = true) {
1749
+ const config2 = GlobalConfigHandler.read(dir);
1750
+ return withAuth(config2.apiKey, force, !headers ? {} : void 0);
1751
+ }
1752
+ static _getPackageFile(filename, dir) {
1753
+ const path = join5(getCwd(dir ?? "."), filename);
1754
+ if (!fs3.existsSync(path))
1755
+ throw new Error(
1756
+ "Package not found, please specify path in the nanoforge.manifest.json : `publish.paths.components`!"
1757
+ );
1758
+ try {
1759
+ fs3.accessSync(path, fs3.constants.R_OK);
1760
+ return fs3.openAsBlob(path);
1761
+ } catch {
1762
+ throw new Error("Cannot read package file, please verify your file permissions!");
1763
+ }
1764
+ }
1765
+ };
1766
+
1304
1767
  // src/action/actions/install.action.ts
1305
1768
  var InstallAction = class extends AbstractAction {
1306
1769
  static {
@@ -1312,114 +1775,78 @@ var InstallAction = class extends AbstractAction {
1312
1775
  async handle(args, options) {
1313
1776
  const names = await getInstallNamesInputOrAsk(args);
1314
1777
  const directory = getDirectoryInput(options);
1778
+ const isLib = getInstallLibInput(options);
1779
+ const isServer = getInstallServerInput(options);
1780
+ return isLib ? this._installLibs(directory, names) : this._installNfPackages(directory, names, isServer);
1781
+ }
1782
+ async _installLibs(directory, names) {
1315
1783
  const packageManager = await PackageManagerFactory.find(directory);
1316
- const success2 = await packageManager.addProduction(directory, names);
1317
- return { success: success2 };
1784
+ return { success: await packageManager.addDevelopment(directory, names) };
1785
+ }
1786
+ async _installNfPackages(directory, names, isServer) {
1787
+ const deps = await resolveManifestDependencies(names, directory);
1788
+ const libSuccess = await this._installLibs(
1789
+ directory,
1790
+ deps.npm.map(([name, version]) => `${name}@${version}`)
1791
+ );
1792
+ if (!libSuccess) return { success: false };
1793
+ return withSpinner(Messages.INSTALL_PACKAGES_IN_PROGRESS, async () => {
1794
+ await Registry.install(
1795
+ Object.values(deps.nf),
1796
+ join6(directory, isServer ? "server" : "client")
1797
+ );
1798
+ });
1318
1799
  }
1319
1800
  };
1320
1801
 
1321
- // src/action/actions/new.action.ts
1322
- import { join as join5 } from "path";
1323
-
1324
- // src/lib/input/inputs/new/docker.input.ts
1325
- var getDockerInput = /* @__PURE__ */ __name((inputs) => {
1326
- return getBooleanInput(inputs, "docker");
1327
- }, "getDockerInput");
1328
- var getDockerOrAsk = /* @__PURE__ */ __name((inputs) => {
1329
- return getInputOrAsk(
1330
- getDockerInput(inputs),
1331
- () => askConfirm(Messages.NEW_DOCKER_QUESTION, { default: true })
1332
- );
1333
- }, "getDockerOrAsk");
1334
-
1335
- // src/lib/input/inputs/new/init-functions.input.ts
1336
- var getNewInitFunctionsWithDefault = /* @__PURE__ */ __name((inputs) => {
1337
- return getBooleanInputWithDefault(inputs, "initFunctions", false);
1338
- }, "getNewInitFunctionsWithDefault");
1339
-
1340
- // src/lib/input/inputs/new/language.input.ts
1341
- var getLanguageInput = /* @__PURE__ */ __name((inputs) => {
1342
- return getStringInput(inputs, "language");
1343
- }, "getLanguageInput");
1344
- var getNewLanguageInputOrAsk = /* @__PURE__ */ __name((inputs) => {
1345
- return getInputOrAsk(
1346
- getLanguageInput(inputs),
1347
- () => askSelect(Messages.NEW_LANGUAGE_QUESTION, [{ value: "ts" }, { value: "js" }], {
1348
- default: "ts"
1349
- })
1350
- );
1351
- }, "getNewLanguageInputOrAsk");
1352
-
1353
- // src/lib/input/inputs/new/name.input.ts
1354
- var getNameInput = /* @__PURE__ */ __name((inputs) => {
1355
- return getStringInput(inputs, "name");
1356
- }, "getNameInput");
1357
- var getNewNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
1358
- return getInputOrAsk(
1359
- getNameInput(inputs),
1360
- () => askInput(Messages.NEW_NAME_QUESTION, {
1361
- required: true,
1362
- default: "nanoforge-app"
1363
- })
1364
- );
1365
- }, "getNewNameInputOrAsk");
1366
-
1367
- // src/lib/input/inputs/new/package-manager.input.ts
1368
- var getPackageManagerInput = /* @__PURE__ */ __name((inputs) => {
1369
- return getStringInput(inputs, "packageManager");
1370
- }, "getPackageManagerInput");
1371
- var getNewPackageManagerInputOrAsk = /* @__PURE__ */ __name((inputs) => {
1372
- return getInputOrAsk(
1373
- getPackageManagerInput(inputs),
1374
- () => askSelect(
1375
- Messages.NEW_PACKAGE_MANAGER_QUESTION,
1376
- [{ value: "npm" }, { value: "yarn" }, { value: "pnpm" }, { value: "bun" }],
1802
+ // src/action/actions/login.action.ts
1803
+ var LoginAction = class extends AbstractAction {
1804
+ static {
1805
+ __name(this, "LoginAction");
1806
+ }
1807
+ startMessage = Messages.LOGIN_START;
1808
+ successMessage = Messages.LOGIN_SUCCESS;
1809
+ failureMessage = Messages.LOGIN_FAILED;
1810
+ async handle(_args, options) {
1811
+ const directory = getDirectoryInput(options);
1812
+ const isLocal = getLocalInput(options);
1813
+ const apiKey = await getLoginApiKeyInputOrAsk(options);
1814
+ await withAuth(apiKey, true).post("/registry-key/verify");
1815
+ GlobalConfigHandler.write(
1377
1816
  {
1378
- default: "npm"
1379
- }
1380
- )
1381
- );
1382
- }, "getNewPackageManagerInputOrAsk");
1383
-
1384
- // src/lib/input/inputs/new/path.input.ts
1385
- var getNewPathInput = /* @__PURE__ */ __name((inputs) => {
1386
- return getStringInput(inputs, "path");
1387
- }, "getNewPathInput");
1388
-
1389
- // src/lib/input/inputs/new/server.input.ts
1390
- var getNewServerInput = /* @__PURE__ */ __name((inputs) => {
1391
- return getBooleanInput(inputs, "server");
1392
- }, "getNewServerInput");
1393
- var getNewServerOrAsk = /* @__PURE__ */ __name((inputs) => {
1394
- return getInputOrAsk(
1395
- getNewServerInput(inputs),
1396
- () => askConfirm(Messages.NEW_SERVER_QUESTION, { default: false })
1397
- );
1398
- }, "getNewServerOrAsk");
1399
-
1400
- // src/lib/input/inputs/new/skip-install.input.ts
1401
- var getNewSkipInstallInput = /* @__PURE__ */ __name((inputs) => {
1402
- return getBooleanInput(inputs, "skipInstall");
1403
- }, "getNewSkipInstallInput");
1404
- var getNewSkipInstallOrAsk = /* @__PURE__ */ __name((inputs) => {
1405
- return getInputOrAsk(
1406
- getNewSkipInstallInput(inputs),
1407
- () => askConfirm(Messages.NEW_SKIP_INSTALL_QUESTION, { default: false })
1408
- );
1409
- }, "getNewSkipInstallOrAsk");
1817
+ apiKey
1818
+ },
1819
+ isLocal,
1820
+ directory
1821
+ );
1822
+ return { success: true };
1823
+ }
1824
+ };
1410
1825
 
1411
- // src/lib/input/inputs/new/strict.input.ts
1412
- var getNewStrictInput = /* @__PURE__ */ __name((inputs) => {
1413
- return getBooleanInput(inputs, "strict");
1414
- }, "getNewStrictInput");
1415
- var getNewStrictOrAsk = /* @__PURE__ */ __name((inputs) => {
1416
- return getInputOrAsk(
1417
- getNewStrictInput(inputs),
1418
- () => askConfirm(Messages.NEW_STRICT_QUESTION, { default: true })
1419
- );
1420
- }, "getNewStrictOrAsk");
1826
+ // src/action/actions/logout.action.ts
1827
+ var LogoutAction = class extends AbstractAction {
1828
+ static {
1829
+ __name(this, "LogoutAction");
1830
+ }
1831
+ startMessage = Messages.LOGOUT_START;
1832
+ successMessage = Messages.LOGOUT_SUCCESS;
1833
+ failureMessage = Messages.LOGOUT_FAILED;
1834
+ async handle(_args, options) {
1835
+ const directory = getDirectoryInput(options);
1836
+ const isLocal = getLocalInput(options);
1837
+ GlobalConfigHandler.write(
1838
+ {
1839
+ apiKey: void 0
1840
+ },
1841
+ isLocal,
1842
+ directory
1843
+ );
1844
+ return { success: true };
1845
+ }
1846
+ };
1421
1847
 
1422
1848
  // src/action/actions/new.action.ts
1849
+ import { join as join7 } from "path";
1423
1850
  var NewAction = class extends AbstractAction {
1424
1851
  static {
1425
1852
  __name(this, "NewAction");
@@ -1428,12 +1855,15 @@ var NewAction = class extends AbstractAction {
1428
1855
  successMessage = Messages.NEW_SUCCESS;
1429
1856
  failureMessage = Messages.NEW_FAILED;
1430
1857
  async handle(_args, options) {
1431
- const directory = getDirectoryInput(options);
1858
+ const cwdDirectory = getDirectoryInput(options);
1432
1859
  const values = await this.collectValues(options);
1433
- await this.scaffold(values, directory);
1860
+ await this.scaffold(values, cwdDirectory);
1434
1861
  let res = true;
1435
1862
  if (!values.skipInstall) {
1436
- res = await this.installDependencies(values.packageManager, join5(directory, values.name));
1863
+ res = await this.installDependencies(
1864
+ values.packageManager,
1865
+ join7(cwdDirectory, values.directory ?? values.name)
1866
+ );
1437
1867
  }
1438
1868
  return { success: res };
1439
1869
  }
@@ -1447,7 +1877,8 @@ var NewAction = class extends AbstractAction {
1447
1877
  server: await getNewServerOrAsk(inputs),
1448
1878
  initFunctions: getNewInitFunctionsWithDefault(inputs),
1449
1879
  skipInstall: await getNewSkipInstallOrAsk(inputs),
1450
- docker: await getDockerOrAsk(inputs)
1880
+ docker: await getNewDockerOrAsk(inputs),
1881
+ lint: getNewLintInput(inputs)
1451
1882
  };
1452
1883
  }
1453
1884
  async scaffold(values, directory) {
@@ -1469,7 +1900,8 @@ var NewAction = class extends AbstractAction {
1469
1900
  packageManager: values.packageManager,
1470
1901
  language: values.language,
1471
1902
  strict: values.strict,
1472
- server: values.server
1903
+ server: values.server,
1904
+ lint: values.lint
1473
1905
  });
1474
1906
  }
1475
1907
  generateConfiguration(collection, values) {
@@ -1517,8 +1949,143 @@ var NewAction = class extends AbstractAction {
1517
1949
  }
1518
1950
  };
1519
1951
 
1952
+ // src/lib/manifest/manifest.type.ts
1953
+ import { Expose as Expose2, Type as Type2 } from "class-transformer";
1954
+ import {
1955
+ IsEnum as IsEnum2,
1956
+ IsNotEmpty as IsNotEmpty2,
1957
+ IsObject,
1958
+ IsOptional,
1959
+ IsString as IsString2,
1960
+ Matches,
1961
+ ValidateNested as ValidateNested2
1962
+ } from "class-validator";
1963
+ var ManifestPackageTypeEnum = /* @__PURE__ */ ((ManifestPackageTypeEnum2) => {
1964
+ ManifestPackageTypeEnum2["COMPONENT"] = "component";
1965
+ ManifestPackageTypeEnum2["SYSTEM"] = "system";
1966
+ return ManifestPackageTypeEnum2;
1967
+ })(ManifestPackageTypeEnum || {});
1968
+ var PathsPublishManifest = class {
1969
+ static {
1970
+ __name(this, "PathsPublishManifest");
1971
+ }
1972
+ package;
1973
+ };
1974
+ __decorateClass([
1975
+ Expose2(),
1976
+ IsString2(),
1977
+ IsNotEmpty2(),
1978
+ IsOptional()
1979
+ ], PathsPublishManifest.prototype, "package", 2);
1980
+ var PublishManifest = class {
1981
+ static {
1982
+ __name(this, "PublishManifest");
1983
+ }
1984
+ paths;
1985
+ };
1986
+ __decorateClass([
1987
+ Expose2(),
1988
+ IsOptional(),
1989
+ ValidateNested2(),
1990
+ Type2(() => PathsPublishManifest)
1991
+ ], PublishManifest.prototype, "paths", 2);
1992
+ var Manifest = class {
1993
+ static {
1994
+ __name(this, "Manifest");
1995
+ }
1996
+ name;
1997
+ type;
1998
+ description;
1999
+ dependencies;
2000
+ publish;
2001
+ npmDependencies;
2002
+ };
2003
+ __decorateClass([
2004
+ Expose2(),
2005
+ IsString2(),
2006
+ Matches(/^[A-Za-z0-9-]+\/[A-Za-z0-9-]+$/),
2007
+ IsNotEmpty2()
2008
+ ], Manifest.prototype, "name", 2);
2009
+ __decorateClass([
2010
+ Expose2(),
2011
+ IsString2(),
2012
+ IsEnum2(ManifestPackageTypeEnum),
2013
+ IsNotEmpty2()
2014
+ ], Manifest.prototype, "type", 2);
2015
+ __decorateClass([
2016
+ Expose2(),
2017
+ IsString2(),
2018
+ IsOptional()
2019
+ ], Manifest.prototype, "description", 2);
2020
+ __decorateClass([
2021
+ Expose2(),
2022
+ IsString2({ each: true }),
2023
+ IsNotEmpty2({ each: true }),
2024
+ IsOptional()
2025
+ ], Manifest.prototype, "dependencies", 2);
2026
+ __decorateClass([
2027
+ Expose2(),
2028
+ IsOptional(),
2029
+ ValidateNested2(),
2030
+ Type2(() => PublishManifest)
2031
+ ], Manifest.prototype, "publish", 2);
2032
+ __decorateClass([
2033
+ Expose2(),
2034
+ IsObject(),
2035
+ IsOptional()
2036
+ ], Manifest.prototype, "npmDependencies", 2);
2037
+
2038
+ // src/lib/manifest/manifest-loader.ts
2039
+ import { plainToInstance as plainToInstance2 } from "class-transformer";
2040
+ import { validate as validate2 } from "class-validator";
2041
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
2042
+ import { join as join8 } from "path";
2043
+ var getManifestPath = /* @__PURE__ */ __name((directory) => {
2044
+ for (const n of [MANIFEST_FILE_NAME]) {
2045
+ const path = join8(directory, n);
2046
+ if (existsSync2(path)) return path;
2047
+ }
2048
+ throw new Error(`No manifest file found in directory: ${directory}`);
2049
+ }, "getManifestPath");
2050
+ var loadManifest = /* @__PURE__ */ __name(async (directory) => {
2051
+ let rawData;
2052
+ const path = getManifestPath(directory);
2053
+ try {
2054
+ rawData = deepMerge({}, JSON.parse(readFileSync2(path, "utf-8")));
2055
+ } catch {
2056
+ rawData = null;
2057
+ }
2058
+ if (!rawData) throw new Error(`Not able to read manifest file : ${path}`);
2059
+ const data = plainToInstance2(Manifest, rawData, {
2060
+ excludeExtraneousValues: true
2061
+ });
2062
+ const errors = await validate2(data);
2063
+ if (errors.length > 0)
2064
+ throw new Error(`Invalid manifest
2065
+ ${errors.toString().replace(/,/g, "\n")}`);
2066
+ return data;
2067
+ }, "loadManifest");
2068
+
2069
+ // src/action/actions/publish.action.ts
2070
+ var PublishAction = class extends AbstractAction {
2071
+ static {
2072
+ __name(this, "PublishAction");
2073
+ }
2074
+ startMessage = Messages.PUBLISH_START;
2075
+ successMessage = Messages.PUBLISH_SUCCESS;
2076
+ failureMessage = Messages.PUBLISH_FAILED;
2077
+ async handle(_args, options) {
2078
+ const directory = getDirectoryInput(options);
2079
+ const manifest = await loadManifest(directory);
2080
+ return withSpinner(Messages.PUBLISH_IN_PROGRESS(manifest.name), async () => {
2081
+ await Registry.publish(manifest, directory);
2082
+ });
2083
+ }
2084
+ };
2085
+
1520
2086
  // src/action/actions/start.action.ts
1521
- import { join as join6 } from "path";
2087
+ import dotenv from "dotenv";
2088
+ import { join as join9, resolve as resolve3 } from "path";
1522
2089
  var StartAction = class extends AbstractAction {
1523
2090
  static {
1524
2091
  __name(this, "StartAction");
@@ -1530,19 +2097,12 @@ var StartAction = class extends AbstractAction {
1530
2097
  const directory = getDirectoryInput(options);
1531
2098
  const config2 = await getConfig(options, directory);
1532
2099
  const watch3 = getWatchInput(options);
1533
- const ports = this.resolvePorts(options, config2);
2100
+ const port = getStringInputWithDefault(options, "port", config2.client.port);
1534
2101
  const ssl = this.resolveSSL(options);
1535
- const tasks = this.buildStartTasks(config2, directory, watch3, ports, ssl);
2102
+ const tasks = this.buildStartTasks(config2, directory, watch3, port, ssl);
1536
2103
  await Promise.all(tasks);
1537
2104
  return { keepAlive: true };
1538
2105
  }
1539
- resolvePorts(options, config2) {
1540
- return {
1541
- clientPort: getStringInputWithDefault(options, "clientPort", config2.client.port),
1542
- gameExposurePort: getStringInput(options, "gameExposurePort"),
1543
- serverPort: getStringInput(options, "serverPort")
1544
- };
1545
- }
1546
2106
  resolveSSL(options) {
1547
2107
  const cert = getStringInput(options, "cert");
1548
2108
  const key = getStringInput(options, "key");
@@ -1554,53 +2114,97 @@ var StartAction = class extends AbstractAction {
1554
2114
  key
1555
2115
  };
1556
2116
  }
1557
- buildStartTasks(config2, directory, watch3, ports, ssl) {
2117
+ buildStartTasks(config2, directory, watch3, port, ssl) {
2118
+ const env2 = this.parseEnv(directory);
1558
2119
  const tasks = [];
1559
2120
  if (config2.server.enable) {
1560
- tasks.push(this.startServer(directory, config2.server.runtime.dir, watch3, ports.serverPort));
2121
+ tasks.push(this.startServer(directory, config2, { watch: watch3 }, env2));
1561
2122
  }
1562
- tasks.push(this.startClient(directory, config2, watch3, ports, ssl));
2123
+ tasks.push(this.startClient(directory, config2, { watch: watch3, port, ssl }, env2));
1563
2124
  return tasks;
1564
2125
  }
1565
- async startClient(directory, config2, watch3, ports, ssl) {
2126
+ async startClient(directory, config2, options, env2) {
1566
2127
  const loaderPath = getModulePath("@nanoforge-dev/loader-client/package.json", true);
1567
- const gameDir = config2.client.runtime.dir;
1568
- const env2 = this.buildClientEnv(directory, gameDir, watch3, config2, ports, ssl);
1569
- await this.runLoader("Client", loaderPath, env2);
1570
- }
1571
- buildClientEnv(directory, gameDir, watch3, config2, ports, ssl) {
1572
- const env2 = {
1573
- PORT: ports.clientPort,
1574
- GAME_DIR: getCwd(join6(directory, gameDir))
2128
+ const params = this.buildClientParams(directory, config2, options);
2129
+ await this.runLoader("Client", loaderPath, params, env2.client);
2130
+ }
2131
+ async startServer(directory, config2, options, env2) {
2132
+ const loaderPath = getModulePath("@nanoforge-dev/loader-server/package.json", true);
2133
+ const params = this.buildServerParams(directory, config2, options);
2134
+ await this.runLoader("Server", loaderPath, params, env2.server);
2135
+ }
2136
+ buildClientParams(directory, config2, options) {
2137
+ const params = {
2138
+ "-d": getCwd(join9(directory, config2.client.runtime.dir)),
2139
+ "-p": options.port
1575
2140
  };
1576
- if (ports.gameExposurePort) {
1577
- env2["GAME_EXPOSURE_PORT"] = ports.gameExposurePort;
1578
- }
1579
- if (watch3) {
1580
- env2["WATCH"] = "true";
2141
+ if (options.watch) params["--watch"] = true;
2142
+ if (options.watch) {
2143
+ params["--watch"] = true;
1581
2144
  if (config2.server.enable) {
1582
- env2["WATCH_SERVER_GAME_DIR"] = getCwd(join6(directory, config2.server.runtime.dir));
2145
+ params["--watch-server-dir"] = getCwd(join9(directory, config2.server.runtime.dir));
1583
2146
  }
1584
2147
  }
1585
- if (ssl) {
1586
- env2["CERT"] = ssl.cert;
1587
- env2["KEY"] = ssl.key;
2148
+ if (options.ssl) {
2149
+ params["--cert"] = options.ssl.cert;
2150
+ params["--key"] = options.ssl.key;
1588
2151
  }
1589
- return env2;
2152
+ return this.buildParams(params);
1590
2153
  }
1591
- async startServer(directory, gameDir, watch3, port) {
1592
- const loaderPath = getModulePath("@nanoforge-dev/loader-server/package.json", true);
1593
- const env2 = {
1594
- GAME_DIR: getCwd(join6(directory, gameDir))
2154
+ buildServerParams(directory, config2, options) {
2155
+ const params = {
2156
+ "-d": getCwd(join9(directory, config2.server.runtime.dir))
2157
+ };
2158
+ if (options.watch) params["--watch"] = true;
2159
+ return this.buildParams(params);
2160
+ }
2161
+ buildParams(params) {
2162
+ return Object.entries(params).map(([key, value]) => typeof value === "string" ? [key, value] : [key]).flat();
2163
+ }
2164
+ parseEnv(dir) {
2165
+ const prefix = "NANOFORGE_";
2166
+ const clientPrefix = `${prefix}CLIENT_`;
2167
+ const serverPrefix = `${prefix}SERVER_`;
2168
+ const rawEnv = {
2169
+ ...process.env
2170
+ };
2171
+ dotenv.config({
2172
+ path: resolve3(getCwd(join9(dir, ".env"))),
2173
+ processEnv: rawEnv
2174
+ });
2175
+ const baseEnv = Object.entries(rawEnv).filter(
2176
+ ([key, value]) => key.startsWith(prefix) && !!value
2177
+ );
2178
+ return {
2179
+ client: Object.fromEntries(
2180
+ baseEnv.filter(([key]) => !key.startsWith(serverPrefix)).map(([key, value]) => [key.replace(clientPrefix, prefix), value])
2181
+ ),
2182
+ server: Object.fromEntries(
2183
+ baseEnv.filter(([key]) => !key.startsWith(clientPrefix)).map(([key, value]) => [key.replace(serverPrefix, prefix), value])
2184
+ )
1595
2185
  };
1596
- if (port) env2["PORT"] = port;
1597
- if (watch3) env2["WATCH"] = "true";
1598
- await this.runLoader("Server", loaderPath, env2);
1599
2186
  }
1600
- async runLoader(name, directory, env2) {
2187
+ async runLoader(name, directory, params, env2) {
1601
2188
  await runSafe(async () => {
1602
2189
  const packageManager = await PackageManagerFactory.find(directory);
1603
- await packageManager.run(name, directory, "start", env2, [], true);
2190
+ await packageManager.run(name, directory, "start", params, env2, [], true);
2191
+ });
2192
+ }
2193
+ };
2194
+
2195
+ // src/action/actions/unpublish.action.ts
2196
+ var UnpublishAction = class extends AbstractAction {
2197
+ static {
2198
+ __name(this, "UnpublishAction");
2199
+ }
2200
+ startMessage = Messages.UNPUBLISH_START;
2201
+ successMessage = Messages.UNPUBLISH_SUCCESS;
2202
+ failureMessage = Messages.UNPUBLISH_FAILED;
2203
+ async handle(_args, options) {
2204
+ const directory = getDirectoryInput(options);
2205
+ const manifest = await loadManifest(directory);
2206
+ return withSpinner(Messages.UNPUBLISH_IN_PROGRESS(manifest.name), async () => {
2207
+ await Registry.unpublish(manifest, directory);
1604
2208
  });
1605
2209
  }
1606
2210
  };
@@ -1628,7 +2232,7 @@ var BuildCommand = class extends AbstractCommand {
1628
2232
  __name(this, "BuildCommand");
1629
2233
  }
1630
2234
  load(program) {
1631
- program.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) => {
2235
+ program.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) => {
1632
2236
  const options = AbstractCommand.mapToInput({
1633
2237
  directory: rawOptions.directory,
1634
2238
  config: rawOptions.config,
@@ -1647,7 +2251,7 @@ var DevCommand = class extends AbstractCommand {
1647
2251
  __name(this, "DevCommand");
1648
2252
  }
1649
2253
  load(program) {
1650
- program.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) => {
2254
+ program.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) => {
1651
2255
  const options = AbstractCommand.mapToInput({
1652
2256
  directory: rawOptions.directory,
1653
2257
  config: rawOptions.config,
@@ -1664,7 +2268,7 @@ var GenerateCommand = class extends AbstractCommand {
1664
2268
  __name(this, "GenerateCommand");
1665
2269
  }
1666
2270
  load(program) {
1667
- program.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) => {
2271
+ program.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) => {
1668
2272
  const options = AbstractCommand.mapToInput({
1669
2273
  directory: rawOptions.directory,
1670
2274
  config: rawOptions.config,
@@ -1681,9 +2285,15 @@ var InstallCommand = class extends AbstractCommand {
1681
2285
  __name(this, "InstallCommand");
1682
2286
  }
1683
2287
  load(program) {
1684
- program.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) => {
2288
+ program.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(
2289
+ "-s, --server",
2290
+ "install components/systems on server (default install on client)",
2291
+ false
2292
+ ).action(async (names, rawOptions) => {
1685
2293
  const options = AbstractCommand.mapToInput({
1686
- directory: rawOptions.directory
2294
+ directory: rawOptions.directory,
2295
+ lib: rawOptions.lib,
2296
+ server: rawOptions.server
1687
2297
  });
1688
2298
  const args = AbstractCommand.mapToInput({
1689
2299
  names: names.length ? names : void 0
@@ -1693,13 +2303,49 @@ var InstallCommand = class extends AbstractCommand {
1693
2303
  }
1694
2304
  };
1695
2305
 
2306
+ // src/command/commands/login.command.ts
2307
+ var LoginCommand = class extends AbstractCommand {
2308
+ static {
2309
+ __name(this, "LoginCommand");
2310
+ }
2311
+ load(program) {
2312
+ program.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) => {
2313
+ const options = AbstractCommand.mapToInput({
2314
+ directory: rawOptions.directory,
2315
+ local: rawOptions.local,
2316
+ apiKey: rawOptions.apiKey
2317
+ });
2318
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2319
+ });
2320
+ }
2321
+ };
2322
+
2323
+ // src/command/commands/logout.command.ts
2324
+ var LogoutCommand = class extends AbstractCommand {
2325
+ static {
2326
+ __name(this, "LogoutCommand");
2327
+ }
2328
+ load(program) {
2329
+ program.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) => {
2330
+ const options = AbstractCommand.mapToInput({
2331
+ directory: rawOptions.directory,
2332
+ local: rawOptions.local
2333
+ });
2334
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2335
+ });
2336
+ }
2337
+ };
2338
+
1696
2339
  // src/command/commands/new.command.ts
1697
2340
  var NewCommand = class extends AbstractCommand {
1698
2341
  static {
1699
2342
  __name(this, "NewCommand");
1700
2343
  }
1701
2344
  load(program) {
1702
- program.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) => {
2345
+ program.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(
2346
+ "--path [path]",
2347
+ "specify the relative path where your project will be created (default: name of the project)"
2348
+ ).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) => {
1703
2349
  const options = AbstractCommand.mapToInput({
1704
2350
  directory: rawOptions.directory,
1705
2351
  name: rawOptions.name,
@@ -1710,7 +2356,23 @@ var NewCommand = class extends AbstractCommand {
1710
2356
  server: rawOptions.server,
1711
2357
  initFunctions: rawOptions.initFunctions,
1712
2358
  skipInstall: rawOptions.skipInstall,
1713
- docker: rawOptions.docker
2359
+ docker: rawOptions.docker,
2360
+ lint: rawOptions.lint
2361
+ });
2362
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2363
+ });
2364
+ }
2365
+ };
2366
+
2367
+ // src/command/commands/publish.command.ts
2368
+ var PublishCommand = class extends AbstractCommand {
2369
+ static {
2370
+ __name(this, "PublishCommand");
2371
+ }
2372
+ load(program) {
2373
+ program.command("publish").description("publish package to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").action(async (rawOptions) => {
2374
+ const options = AbstractCommand.mapToInput({
2375
+ directory: rawOptions.directory
1714
2376
  });
1715
2377
  await this.action.run(/* @__PURE__ */ new Map(), options);
1716
2378
  });
@@ -1723,16 +2385,11 @@ var StartCommand = class extends AbstractCommand {
1723
2385
  __name(this, "StartCommand");
1724
2386
  }
1725
2387
  load(program) {
1726
- program.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(
1727
- "-p, --client-port [clientPort]",
1728
- "specify the port of the loader (the website to load the game)"
1729
- ).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) => {
2388
+ program.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) => {
1730
2389
  const options = AbstractCommand.mapToInput({
1731
2390
  directory: rawOptions.directory,
1732
2391
  config: rawOptions.config,
1733
- clientPort: rawOptions.clientPort,
1734
- gameExposurePort: rawOptions.gameExposurePort,
1735
- serverPort: rawOptions.serverPort,
2392
+ port: rawOptions.port,
1736
2393
  watch: rawOptions.watch,
1737
2394
  cert: rawOptions.cert,
1738
2395
  key: rawOptions.key
@@ -1742,18 +2399,37 @@ var StartCommand = class extends AbstractCommand {
1742
2399
  }
1743
2400
  };
1744
2401
 
2402
+ // src/command/commands/unpublish.command.ts
2403
+ var UnpublishCommand = class extends AbstractCommand {
2404
+ static {
2405
+ __name(this, "UnpublishCommand");
2406
+ }
2407
+ load(program) {
2408
+ program.command("unpublish").description("unpublish package to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").action(async (rawOptions) => {
2409
+ const options = AbstractCommand.mapToInput({
2410
+ directory: rawOptions.directory
2411
+ });
2412
+ await this.action.run(/* @__PURE__ */ new Map(), options);
2413
+ });
2414
+ }
2415
+ };
2416
+
1745
2417
  // src/command/command.loader.ts
1746
2418
  var CommandLoader = class {
1747
2419
  static {
1748
2420
  __name(this, "CommandLoader");
1749
2421
  }
1750
2422
  static async load(program) {
2423
+ new NewCommand(new NewAction()).load(program);
2424
+ new InstallCommand(new InstallAction()).load(program);
1751
2425
  new BuildCommand(new BuildAction()).load(program);
2426
+ new StartCommand(new StartAction()).load(program);
1752
2427
  new DevCommand(new DevAction()).load(program);
1753
2428
  new GenerateCommand(new GenerateAction()).load(program);
1754
- new InstallCommand(new InstallAction()).load(program);
1755
- new NewCommand(new NewAction()).load(program);
1756
- new StartCommand(new StartAction()).load(program);
2429
+ new LoginCommand(new LoginAction()).load(program);
2430
+ new LogoutCommand(new LogoutAction()).load(program);
2431
+ new PublishCommand(new PublishAction()).load(program);
2432
+ new UnpublishCommand(new UnpublishAction()).load(program);
1757
2433
  this.handleInvalidCommand(program);
1758
2434
  }
1759
2435
  static handleInvalidCommand(program) {