@axiomify/cli 5.0.0 → 6.0.0-rc.2

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/index.mjs CHANGED
@@ -1,44 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import { __commonJS, __require, __esm, __glob, __toESM } from './chunk-YZPZCUKZ.mjs';
2
3
  import * as esbuild from 'esbuild';
3
- import path4 from 'path';
4
+ import path7 from 'path';
4
5
  import fs, { existsSync } from 'fs';
6
+ import pc from 'picocolors';
7
+ import fs5 from 'fs/promises';
5
8
  import { spawn } from 'child_process';
9
+ import { createServer } from 'net';
6
10
  import { prompt } from 'enquirer';
7
11
  import { execa } from 'execa';
8
- import fs2 from 'fs/promises';
9
- import pc from 'picocolors';
10
-
11
- var __create = Object.create;
12
- var __defProp = Object.defineProperty;
13
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
- var __getOwnPropNames = Object.getOwnPropertyNames;
15
- var __getProtoOf = Object.getPrototypeOf;
16
- var __hasOwnProp = Object.prototype.hasOwnProperty;
17
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
18
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
19
- }) : x)(function(x) {
20
- if (typeof require !== "undefined") return require.apply(this, arguments);
21
- throw Error('Dynamic require of "' + x + '" is not supported');
22
- });
23
- var __commonJS = (cb, mod) => function __require2() {
24
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
25
- };
26
- var __copyProps = (to, from, except, desc) => {
27
- if (from && typeof from === "object" || typeof from === "function") {
28
- for (let key of __getOwnPropNames(from))
29
- if (!__hasOwnProp.call(to, key) && key !== except)
30
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
31
- }
32
- return to;
33
- };
34
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
35
- // If the importer is in node compatibility mode or this is not an ESM
36
- // file that has been converted to a CommonJS file using a Babel-
37
- // compatible transform (i.e. "__esModule" has not been set), then set
38
- // "default" to the CommonJS "module.exports" for node compatibility.
39
- __defProp(target, "default", { value: mod, enumerable: true }) ,
40
- mod
41
- ));
42
12
 
43
13
  // node_modules/commander/lib/error.js
44
14
  var require_error = __commonJS({
@@ -929,8 +899,8 @@ var require_command = __commonJS({
929
899
  "node_modules/commander/lib/command.js"(exports) {
930
900
  var EventEmitter = __require("events").EventEmitter;
931
901
  var childProcess = __require("child_process");
932
- var path6 = __require("path");
933
- var fs4 = __require("fs");
902
+ var path11 = __require("path");
903
+ var fs9 = __require("fs");
934
904
  var process2 = __require("process");
935
905
  var { Argument: Argument2, humanReadableArgName } = require_argument();
936
906
  var { CommanderError: CommanderError2 } = require_error();
@@ -1813,10 +1783,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1813
1783
  let launchWithNode = false;
1814
1784
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1815
1785
  function findFile(baseDir, baseName) {
1816
- const localBin = path6.resolve(baseDir, baseName);
1817
- if (fs4.existsSync(localBin)) return localBin;
1818
- if (sourceExt.includes(path6.extname(baseName))) return void 0;
1819
- const foundExt = sourceExt.find((ext) => fs4.existsSync(`${localBin}${ext}`));
1786
+ const localBin = path11.resolve(baseDir, baseName);
1787
+ if (fs9.existsSync(localBin)) return localBin;
1788
+ if (sourceExt.includes(path11.extname(baseName))) return void 0;
1789
+ const foundExt = sourceExt.find((ext) => fs9.existsSync(`${localBin}${ext}`));
1820
1790
  if (foundExt) return `${localBin}${foundExt}`;
1821
1791
  return void 0;
1822
1792
  }
@@ -1827,23 +1797,23 @@ Expecting one of '${allowedValues.join("', '")}'`);
1827
1797
  if (this._scriptPath) {
1828
1798
  let resolvedScriptPath;
1829
1799
  try {
1830
- resolvedScriptPath = fs4.realpathSync(this._scriptPath);
1800
+ resolvedScriptPath = fs9.realpathSync(this._scriptPath);
1831
1801
  } catch (err) {
1832
1802
  resolvedScriptPath = this._scriptPath;
1833
1803
  }
1834
- executableDir = path6.resolve(path6.dirname(resolvedScriptPath), executableDir);
1804
+ executableDir = path11.resolve(path11.dirname(resolvedScriptPath), executableDir);
1835
1805
  }
1836
1806
  if (executableDir) {
1837
1807
  let localFile = findFile(executableDir, executableFile);
1838
1808
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1839
- const legacyName = path6.basename(this._scriptPath, path6.extname(this._scriptPath));
1809
+ const legacyName = path11.basename(this._scriptPath, path11.extname(this._scriptPath));
1840
1810
  if (legacyName !== this._name) {
1841
1811
  localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
1842
1812
  }
1843
1813
  }
1844
1814
  executableFile = localFile || executableFile;
1845
1815
  }
1846
- launchWithNode = sourceExt.includes(path6.extname(executableFile));
1816
+ launchWithNode = sourceExt.includes(path11.extname(executableFile));
1847
1817
  let proc;
1848
1818
  if (process2.platform !== "win32") {
1849
1819
  if (launchWithNode) {
@@ -2632,7 +2602,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2632
2602
  * @return {Command}
2633
2603
  */
2634
2604
  nameFromFilename(filename) {
2635
- this._name = path6.basename(filename, path6.extname(filename));
2605
+ this._name = path11.basename(filename, path11.extname(filename));
2636
2606
  return this;
2637
2607
  }
2638
2608
  /**
@@ -2646,9 +2616,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2646
2616
  * @param {string} [path]
2647
2617
  * @return {(string|null|Command)}
2648
2618
  */
2649
- executableDir(path7) {
2650
- if (path7 === void 0) return this._executableDir;
2651
- this._executableDir = path7;
2619
+ executableDir(path12) {
2620
+ if (path12 === void 0) return this._executableDir;
2621
+ this._executableDir = path12;
2652
2622
  return this;
2653
2623
  }
2654
2624
  /**
@@ -2876,8 +2846,267 @@ var require_commander = __commonJS({
2876
2846
  }
2877
2847
  });
2878
2848
 
2849
+ // ../../node_modules/uWebSockets.js/uws_darwin_arm64_108.node
2850
+ var require_uws_darwin_arm64_108 = __commonJS({
2851
+ "../../node_modules/uWebSockets.js/uws_darwin_arm64_108.node"(exports, module) {
2852
+ module.exports = "./uws_darwin_arm64_108-CLFXMYPI.node";
2853
+ }
2854
+ });
2855
+
2856
+ // ../../node_modules/uWebSockets.js/uws_darwin_arm64_115.node
2857
+ var require_uws_darwin_arm64_115 = __commonJS({
2858
+ "../../node_modules/uWebSockets.js/uws_darwin_arm64_115.node"(exports, module) {
2859
+ module.exports = "./uws_darwin_arm64_115-7FFEG3YF.node";
2860
+ }
2861
+ });
2862
+
2863
+ // ../../node_modules/uWebSockets.js/uws_darwin_arm64_120.node
2864
+ var require_uws_darwin_arm64_120 = __commonJS({
2865
+ "../../node_modules/uWebSockets.js/uws_darwin_arm64_120.node"(exports, module) {
2866
+ module.exports = "./uws_darwin_arm64_120-GFZT7CLS.node";
2867
+ }
2868
+ });
2869
+
2870
+ // ../../node_modules/uWebSockets.js/uws_darwin_arm64_127.node
2871
+ var require_uws_darwin_arm64_127 = __commonJS({
2872
+ "../../node_modules/uWebSockets.js/uws_darwin_arm64_127.node"(exports, module) {
2873
+ module.exports = "./uws_darwin_arm64_127-KHC2FVAM.node";
2874
+ }
2875
+ });
2876
+
2877
+ // ../../node_modules/uWebSockets.js/uws_darwin_x64_108.node
2878
+ var require_uws_darwin_x64_108 = __commonJS({
2879
+ "../../node_modules/uWebSockets.js/uws_darwin_x64_108.node"(exports, module) {
2880
+ module.exports = "./uws_darwin_x64_108-BRGT45AT.node";
2881
+ }
2882
+ });
2883
+
2884
+ // ../../node_modules/uWebSockets.js/uws_darwin_x64_115.node
2885
+ var require_uws_darwin_x64_115 = __commonJS({
2886
+ "../../node_modules/uWebSockets.js/uws_darwin_x64_115.node"(exports, module) {
2887
+ module.exports = "./uws_darwin_x64_115-4HGPQGDD.node";
2888
+ }
2889
+ });
2890
+
2891
+ // ../../node_modules/uWebSockets.js/uws_darwin_x64_120.node
2892
+ var require_uws_darwin_x64_120 = __commonJS({
2893
+ "../../node_modules/uWebSockets.js/uws_darwin_x64_120.node"(exports, module) {
2894
+ module.exports = "./uws_darwin_x64_120-C2SGUHP4.node";
2895
+ }
2896
+ });
2897
+
2898
+ // ../../node_modules/uWebSockets.js/uws_darwin_x64_127.node
2899
+ var require_uws_darwin_x64_127 = __commonJS({
2900
+ "../../node_modules/uWebSockets.js/uws_darwin_x64_127.node"(exports, module) {
2901
+ module.exports = "./uws_darwin_x64_127-NHKQMMST.node";
2902
+ }
2903
+ });
2904
+
2905
+ // ../../node_modules/uWebSockets.js/uws_linux_arm64_108.node
2906
+ var require_uws_linux_arm64_108 = __commonJS({
2907
+ "../../node_modules/uWebSockets.js/uws_linux_arm64_108.node"(exports, module) {
2908
+ module.exports = "./uws_linux_arm64_108-YHK7ACON.node";
2909
+ }
2910
+ });
2911
+
2912
+ // ../../node_modules/uWebSockets.js/uws_linux_arm64_115.node
2913
+ var require_uws_linux_arm64_115 = __commonJS({
2914
+ "../../node_modules/uWebSockets.js/uws_linux_arm64_115.node"(exports, module) {
2915
+ module.exports = "./uws_linux_arm64_115-EIAAY4WO.node";
2916
+ }
2917
+ });
2918
+
2919
+ // ../../node_modules/uWebSockets.js/uws_linux_arm64_120.node
2920
+ var require_uws_linux_arm64_120 = __commonJS({
2921
+ "../../node_modules/uWebSockets.js/uws_linux_arm64_120.node"(exports, module) {
2922
+ module.exports = "./uws_linux_arm64_120-OADY5FIN.node";
2923
+ }
2924
+ });
2925
+
2926
+ // ../../node_modules/uWebSockets.js/uws_linux_arm64_127.node
2927
+ var require_uws_linux_arm64_127 = __commonJS({
2928
+ "../../node_modules/uWebSockets.js/uws_linux_arm64_127.node"(exports, module) {
2929
+ module.exports = "./uws_linux_arm64_127-U2SRLYQM.node";
2930
+ }
2931
+ });
2932
+
2933
+ // ../../node_modules/uWebSockets.js/uws_linux_arm_108.node
2934
+ var require_uws_linux_arm_108 = __commonJS({
2935
+ "../../node_modules/uWebSockets.js/uws_linux_arm_108.node"(exports, module) {
2936
+ module.exports = "./uws_linux_arm_108-BKVITVZA.node";
2937
+ }
2938
+ });
2939
+
2940
+ // ../../node_modules/uWebSockets.js/uws_linux_arm_115.node
2941
+ var require_uws_linux_arm_115 = __commonJS({
2942
+ "../../node_modules/uWebSockets.js/uws_linux_arm_115.node"(exports, module) {
2943
+ module.exports = "./uws_linux_arm_115-7IORQF77.node";
2944
+ }
2945
+ });
2946
+
2947
+ // ../../node_modules/uWebSockets.js/uws_linux_arm_120.node
2948
+ var require_uws_linux_arm_120 = __commonJS({
2949
+ "../../node_modules/uWebSockets.js/uws_linux_arm_120.node"(exports, module) {
2950
+ module.exports = "./uws_linux_arm_120-LCX4ED5F.node";
2951
+ }
2952
+ });
2953
+
2954
+ // ../../node_modules/uWebSockets.js/uws_linux_arm_127.node
2955
+ var require_uws_linux_arm_127 = __commonJS({
2956
+ "../../node_modules/uWebSockets.js/uws_linux_arm_127.node"(exports, module) {
2957
+ module.exports = "./uws_linux_arm_127-6UTO7TL6.node";
2958
+ }
2959
+ });
2960
+
2961
+ // ../../node_modules/uWebSockets.js/uws_linux_x64_108.node
2962
+ var require_uws_linux_x64_108 = __commonJS({
2963
+ "../../node_modules/uWebSockets.js/uws_linux_x64_108.node"(exports, module) {
2964
+ module.exports = "./uws_linux_x64_108-QSNE6XWU.node";
2965
+ }
2966
+ });
2967
+
2968
+ // ../../node_modules/uWebSockets.js/uws_linux_x64_115.node
2969
+ var require_uws_linux_x64_115 = __commonJS({
2970
+ "../../node_modules/uWebSockets.js/uws_linux_x64_115.node"(exports, module) {
2971
+ module.exports = "./uws_linux_x64_115-7AAZWMWE.node";
2972
+ }
2973
+ });
2974
+
2975
+ // ../../node_modules/uWebSockets.js/uws_linux_x64_120.node
2976
+ var require_uws_linux_x64_120 = __commonJS({
2977
+ "../../node_modules/uWebSockets.js/uws_linux_x64_120.node"(exports, module) {
2978
+ module.exports = "./uws_linux_x64_120-AIZ6RIW2.node";
2979
+ }
2980
+ });
2981
+
2982
+ // ../../node_modules/uWebSockets.js/uws_linux_x64_127.node
2983
+ var require_uws_linux_x64_127 = __commonJS({
2984
+ "../../node_modules/uWebSockets.js/uws_linux_x64_127.node"(exports, module) {
2985
+ module.exports = "./uws_linux_x64_127-HBA6RNSU.node";
2986
+ }
2987
+ });
2988
+
2989
+ // ../../node_modules/uWebSockets.js/uws_win32_x64_108.node
2990
+ var require_uws_win32_x64_108 = __commonJS({
2991
+ "../../node_modules/uWebSockets.js/uws_win32_x64_108.node"(exports, module) {
2992
+ module.exports = "./uws_win32_x64_108-J6KONPDM.node";
2993
+ }
2994
+ });
2995
+
2996
+ // ../../node_modules/uWebSockets.js/uws_win32_x64_115.node
2997
+ var require_uws_win32_x64_115 = __commonJS({
2998
+ "../../node_modules/uWebSockets.js/uws_win32_x64_115.node"(exports, module) {
2999
+ module.exports = "./uws_win32_x64_115-V5N4NHJ5.node";
3000
+ }
3001
+ });
3002
+
3003
+ // ../../node_modules/uWebSockets.js/uws_win32_x64_120.node
3004
+ var require_uws_win32_x64_120 = __commonJS({
3005
+ "../../node_modules/uWebSockets.js/uws_win32_x64_120.node"(exports, module) {
3006
+ module.exports = "./uws_win32_x64_120-XH4MVJGN.node";
3007
+ }
3008
+ });
3009
+
3010
+ // ../../node_modules/uWebSockets.js/uws_win32_x64_127.node
3011
+ var require_uws_win32_x64_127 = __commonJS({
3012
+ "../../node_modules/uWebSockets.js/uws_win32_x64_127.node"(exports, module) {
3013
+ module.exports = "./uws_win32_x64_127-JBAEKR4X.node";
3014
+ }
3015
+ });
3016
+
3017
+ // require("./uws_*_*_*.node") in ../../node_modules/uWebSockets.js/uws.js
3018
+ var globRequire_uws______node;
3019
+ var init_ = __esm({
3020
+ 'require("./uws_*_*_*.node") in ../../node_modules/uWebSockets.js/uws.js'() {
3021
+ globRequire_uws______node = __glob({
3022
+ "./uws_darwin_arm64_108.node": () => require_uws_darwin_arm64_108(),
3023
+ "./uws_darwin_arm64_115.node": () => require_uws_darwin_arm64_115(),
3024
+ "./uws_darwin_arm64_120.node": () => require_uws_darwin_arm64_120(),
3025
+ "./uws_darwin_arm64_127.node": () => require_uws_darwin_arm64_127(),
3026
+ "./uws_darwin_x64_108.node": () => require_uws_darwin_x64_108(),
3027
+ "./uws_darwin_x64_115.node": () => require_uws_darwin_x64_115(),
3028
+ "./uws_darwin_x64_120.node": () => require_uws_darwin_x64_120(),
3029
+ "./uws_darwin_x64_127.node": () => require_uws_darwin_x64_127(),
3030
+ "./uws_linux_arm64_108.node": () => require_uws_linux_arm64_108(),
3031
+ "./uws_linux_arm64_115.node": () => require_uws_linux_arm64_115(),
3032
+ "./uws_linux_arm64_120.node": () => require_uws_linux_arm64_120(),
3033
+ "./uws_linux_arm64_127.node": () => require_uws_linux_arm64_127(),
3034
+ "./uws_linux_arm_108.node": () => require_uws_linux_arm_108(),
3035
+ "./uws_linux_arm_115.node": () => require_uws_linux_arm_115(),
3036
+ "./uws_linux_arm_120.node": () => require_uws_linux_arm_120(),
3037
+ "./uws_linux_arm_127.node": () => require_uws_linux_arm_127(),
3038
+ "./uws_linux_x64_108.node": () => require_uws_linux_x64_108(),
3039
+ "./uws_linux_x64_115.node": () => require_uws_linux_x64_115(),
3040
+ "./uws_linux_x64_120.node": () => require_uws_linux_x64_120(),
3041
+ "./uws_linux_x64_127.node": () => require_uws_linux_x64_127(),
3042
+ "./uws_win32_x64_108.node": () => require_uws_win32_x64_108(),
3043
+ "./uws_win32_x64_115.node": () => require_uws_win32_x64_115(),
3044
+ "./uws_win32_x64_120.node": () => require_uws_win32_x64_120(),
3045
+ "./uws_win32_x64_127.node": () => require_uws_win32_x64_127()
3046
+ });
3047
+ }
3048
+ });
3049
+
3050
+ // ../../node_modules/uWebSockets.js/uws.js
3051
+ var require_uws = __commonJS({
3052
+ "../../node_modules/uWebSockets.js/uws.js"(exports, module) {
3053
+ init_();
3054
+ module.exports = (() => {
3055
+ try {
3056
+ return globRequire_uws______node("./uws_" + process.platform + "_" + process.arch + "_" + process.versions.modules + ".node");
3057
+ } catch (e) {
3058
+ throw new Error("This version of uWS.js supports only Node.js versions 18, 20, 21 and 22 on (glibc) Linux, macOS and Windows, on Tier 1 platforms (https://github.com/nodejs/node/blob/master/BUILDING.md#platform-list).\n\n" + e.toString());
3059
+ }
3060
+ })();
3061
+ module.exports.DeclarativeResponse = class DeclarativeResponse {
3062
+ constructor() {
3063
+ this.instructions = [];
3064
+ }
3065
+ // Utility method to encode text and append instruction
3066
+ _appendInstruction(opcode, ...text) {
3067
+ this.instructions.push(opcode);
3068
+ text.forEach((str) => {
3069
+ const bytes = typeof str === "string" ? new TextEncoder().encode(str) : str;
3070
+ this.instructions.push(bytes.length, ...bytes);
3071
+ });
3072
+ }
3073
+ // Utility method to append 2-byte length text in little-endian format
3074
+ _appendInstructionWithLength(opcode, text) {
3075
+ this.instructions.push(opcode);
3076
+ const bytes = new TextEncoder().encode(text);
3077
+ const length = bytes.length;
3078
+ this.instructions.push(length & 255, length >> 8 & 255, ...bytes);
3079
+ }
3080
+ writeHeader(key, value) {
3081
+ return this._appendInstruction(1, key, value), this;
3082
+ }
3083
+ writeBody() {
3084
+ return this.instructions.push(2), this;
3085
+ }
3086
+ writeQueryValue(key) {
3087
+ return this._appendInstruction(3, key), this;
3088
+ }
3089
+ writeHeaderValue(key) {
3090
+ return this._appendInstruction(4, key), this;
3091
+ }
3092
+ write(value) {
3093
+ return this._appendInstructionWithLength(5, value), this;
3094
+ }
3095
+ writeParameterValue(key) {
3096
+ return this._appendInstruction(6, key), this;
3097
+ }
3098
+ end(value) {
3099
+ const bytes = new TextEncoder().encode(value);
3100
+ const length = bytes.length;
3101
+ this.instructions.push(0, length & 255, length >> 8 & 255, ...bytes);
3102
+ return new Uint8Array(this.instructions).buffer;
3103
+ }
3104
+ };
3105
+ }
3106
+ });
3107
+
2879
3108
  // node_modules/commander/esm.mjs
2880
- var import_index = __toESM(require_commander());
3109
+ var import_index = __toESM(require_commander(), 1);
2881
3110
  var {
2882
3111
  program,
2883
3112
  createCommand,
@@ -2895,12 +3124,12 @@ var {
2895
3124
 
2896
3125
  // package.json
2897
3126
  var package_default = {
2898
- version: "5.0.0"};
3127
+ version: "6.0.0-rc.2"};
2899
3128
  var ALWAYS_EXTERNAL = ["uWebSockets.js"];
2900
3129
  function getUserExternals(cwd) {
2901
3130
  let pkgExternals = [];
2902
3131
  try {
2903
- const pkgPath = path4.join(cwd, "package.json");
3132
+ const pkgPath = path7.join(cwd, "package.json");
2904
3133
  if (fs.existsSync(pkgPath)) {
2905
3134
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
2906
3135
  const deps = Object.keys(pkg.dependencies || {});
@@ -2918,8 +3147,8 @@ function getUserExternals(cwd) {
2918
3147
 
2919
3148
  // src/commands/build.ts
2920
3149
  async function buildProject(entry) {
2921
- const entryPath = path4.resolve(process.cwd(), entry);
2922
- const outPath = path4.resolve(process.cwd(), "dist/index.js");
3150
+ const entryPath = path7.resolve(process.cwd(), entry);
3151
+ const outPath = path7.resolve(process.cwd(), "dist/index.js");
2923
3152
  const userExternals = getUserExternals(process.cwd());
2924
3153
  console.log(`\u{1F528} Building production bundle from ${entry}...`);
2925
3154
  try {
@@ -2939,9 +3168,379 @@ async function buildProject(entry) {
2939
3168
  process.exit(1);
2940
3169
  }
2941
3170
  }
3171
+ function colourMethod(method) {
3172
+ const m = method.toUpperCase();
3173
+ const padded = m.padEnd(7);
3174
+ switch (m) {
3175
+ case "GET":
3176
+ return pc.bold(pc.green(padded));
3177
+ case "POST":
3178
+ return pc.bold(pc.blue(padded));
3179
+ case "PUT":
3180
+ return pc.bold(pc.yellow(padded));
3181
+ case "PATCH":
3182
+ return pc.bold(pc.magenta(padded));
3183
+ case "DELETE":
3184
+ return pc.bold(pc.red(padded));
3185
+ case "HEAD":
3186
+ return pc.dim(padded);
3187
+ case "OPTIONS":
3188
+ return pc.dim(padded);
3189
+ case "WS":
3190
+ return pc.bold(pc.cyan(padded));
3191
+ default:
3192
+ return padded;
3193
+ }
3194
+ }
3195
+ var badge = {
3196
+ validation(label) {
3197
+ return pc.cyan(label);
3198
+ },
3199
+ deprecated() {
3200
+ return pc.bold(pc.red("\u2298 DEPRECATED"));
3201
+ },
3202
+ timeout(ms) {
3203
+ return pc.dim(`${ms}ms`);
3204
+ },
3205
+ tags(tags) {
3206
+ if (!tags.length) return "";
3207
+ return tags.map((t) => pc.dim(`#${t}`)).join(" ");
3208
+ }
3209
+ };
3210
+ function visibleLength(s) {
3211
+ return s.replace(/\x1b\[[0-9;]*m/g, "").length;
3212
+ }
3213
+ function pad(s, width, align) {
3214
+ const v = visibleLength(s);
3215
+ if (v >= width) return s;
3216
+ const filler = " ".repeat(width - v);
3217
+ return align === "right" ? filler + s : s + filler;
3218
+ }
3219
+ function truncate(s, max) {
3220
+ if (visibleLength(s) <= max) return s;
3221
+ const stripped = s.replace(/\x1b\[[0-9;]*m/g, "");
3222
+ return stripped.slice(0, max - 1) + pc.dim("\u2026");
3223
+ }
3224
+ function renderTable(columns, rows, terminalWidth = process.stdout.columns || 100) {
3225
+ const widths = columns.map((col, i) => {
3226
+ const headerW = visibleLength(col.header);
3227
+ const cellW = Math.max(
3228
+ headerW,
3229
+ ...rows.map((r) => visibleLength(r[i] ?? ""))
3230
+ );
3231
+ let w = Math.max(headerW, cellW);
3232
+ if (col.minWidth) w = Math.max(w, col.minWidth);
3233
+ if (col.maxWidth) w = Math.min(w, col.maxWidth);
3234
+ return w;
3235
+ });
3236
+ const overheadPerSep = 3;
3237
+ const overhead = (columns.length + 1) * 2 + overheadPerSep * (columns.length - 1);
3238
+ let total = widths.reduce((a, b) => a + b, 0) + overhead;
3239
+ if (total > terminalWidth) {
3240
+ const flexIdx = columns.map((c, i) => ({ c, i })).filter(({ c }) => !c.minWidth).map(({ i }) => i);
3241
+ let excess = total - terminalWidth;
3242
+ for (let pass = 0; pass < 8 && excess > 0; pass++) {
3243
+ for (const i of flexIdx) {
3244
+ if (excess <= 0) break;
3245
+ if (widths[i] > 8) {
3246
+ widths[i]--;
3247
+ excess--;
3248
+ total--;
3249
+ }
3250
+ }
3251
+ }
3252
+ }
3253
+ const bar = (l, m, r) => l + widths.map((w) => "\u2500".repeat(w + 2)).join(m) + r;
3254
+ const lines = [];
3255
+ lines.push(pc.dim(bar("\u250C", "\u252C", "\u2510")));
3256
+ lines.push(
3257
+ pc.dim("\u2502 ") + columns.map((c, i) => pc.bold(pad(c.header, widths[i], c.align ?? "left"))).join(pc.dim(" \u2502 ")) + pc.dim(" \u2502")
3258
+ );
3259
+ lines.push(pc.dim(bar("\u251C", "\u253C", "\u2524")));
3260
+ for (const row of rows) {
3261
+ lines.push(
3262
+ pc.dim("\u2502 ") + columns.map((c, i) => {
3263
+ const cell = row[i] ?? "";
3264
+ const truncated = c.maxWidth ? truncate(cell, widths[i]) : cell;
3265
+ return pad(truncated, widths[i], c.align ?? "left");
3266
+ }).join(pc.dim(" \u2502 ")) + pc.dim(" \u2502")
3267
+ );
3268
+ }
3269
+ lines.push(pc.dim(bar("\u2514", "\u2534", "\u2518")));
3270
+ return lines.join("\n");
3271
+ }
3272
+ function pluralise(n, singular, plural) {
3273
+ return n === 1 ? `${n} ${singular}` : `${n} ${plural ?? singular + "s"}`;
3274
+ }
3275
+ var symbols = {
3276
+ ok: pc.green("\u2713"),
3277
+ warn: pc.yellow("\u26A0"),
3278
+ fail: pc.red("\u2717"),
3279
+ info: pc.cyan("\u2139"),
3280
+ bullet: pc.dim("\u2022"),
3281
+ arrow: pc.dim("\u2192")
3282
+ };
3283
+ async function loadApp(entry) {
3284
+ const entryPath = path7.resolve(process.cwd(), entry);
3285
+ const tempDir = path7.resolve(process.cwd(), ".axiomify");
3286
+ const tempPath = path7.join(tempDir, "inspect.cjs");
3287
+ const userExternals = getUserExternals(process.cwd());
3288
+ await esbuild.build({
3289
+ entryPoints: [entryPath],
3290
+ bundle: true,
3291
+ platform: "node",
3292
+ format: "cjs",
3293
+ outfile: tempPath,
3294
+ external: [.../* @__PURE__ */ new Set([...userExternals, "node:*"])],
3295
+ // Silence esbuild's own progress chatter — the CLI command above is
3296
+ // responsible for its own user-facing output.
3297
+ logLevel: "error"
3298
+ });
3299
+ try {
3300
+ delete __require.cache[__require.resolve(tempPath)];
3301
+ } catch {
3302
+ }
3303
+ const hangTimer = setTimeout(() => {
3304
+ process.stderr.write(
3305
+ "\n" + pc.yellow("\u26A0 CLI inspection is taking longer than expected.") + "\n Your entry file may be starting a server unconditionally.\n Wrap the listen() call in " + pc.cyan("if (require.main === module) { ... }") + " so it only runs when executed directly.\n\n"
3306
+ );
3307
+ }, 5e3);
3308
+ hangTimer.unref();
3309
+ let mod;
3310
+ try {
3311
+ mod = __require(tempPath);
3312
+ } finally {
3313
+ clearTimeout(hangTimer);
3314
+ }
3315
+ const app = mod.app ?? mod.default;
3316
+ if (!app || typeof app.registeredRoutes === "undefined") {
3317
+ await fs5.rm(tempDir, { recursive: true, force: true }).catch(() => {
3318
+ });
3319
+ throw new Error(
3320
+ "Could not find an exported Axiomify instance.\nEnsure your entry file exports the app:\n export const app = new Axiomify();\nor:\n export default app;"
3321
+ );
3322
+ }
3323
+ const cleanup = async () => {
3324
+ await fs5.rm(tempDir, { recursive: true, force: true }).catch(() => {
3325
+ });
3326
+ };
3327
+ return { app, cleanup };
3328
+ }
3329
+
3330
+ // src/commands/check.ts
3331
+ function add(ctx, f) {
3332
+ ctx.findings.push(f);
3333
+ }
3334
+ function checkRequestId(ctx) {
3335
+ const onRequest = ctx.app.hooks?.hooks?.onRequest ?? [];
3336
+ if (onRequest.length === 0) {
3337
+ add(ctx, {
3338
+ severity: "warn",
3339
+ area: "observability",
3340
+ message: "`app.enableRequestId()` has not been called",
3341
+ hint: "Distributed-trace correlation will be impossible without an X-Request-Id header. Add `app.enableRequestId()` after construction unless you handle this elsewhere."
3342
+ });
3343
+ return;
3344
+ }
3345
+ add(ctx, {
3346
+ severity: "ok",
3347
+ area: "observability",
3348
+ message: `onRequest hooks: ${onRequest.length} registered`
3349
+ });
3350
+ }
3351
+ function checkEnvVars(ctx) {
3352
+ const expectedInProd = ["JWT_SECRET", "NODE_ENV"];
3353
+ for (const key of expectedInProd) {
3354
+ if (!ctx.envKeys.has(key)) continue;
3355
+ if (process.env[key]) {
3356
+ add(ctx, { severity: "ok", area: "env", message: `${key} is set` });
3357
+ } else {
3358
+ add(ctx, {
3359
+ severity: "warn",
3360
+ area: "env",
3361
+ message: `${key} referenced in source but not set in environment`,
3362
+ hint: key === "JWT_SECRET" ? "Set this before deploying. Generate one via `node -e \"console.log(require('crypto').randomBytes(48).toString('base64'))\"`" : `Ensure ${key} is set in your deployment environment.`
3363
+ });
3364
+ }
3365
+ }
3366
+ }
3367
+ function checkResponseSchemas(ctx) {
3368
+ const routes = ctx.app.registeredRoutes ?? [];
3369
+ const missing = routes.filter(
3370
+ (r) => r.schema?.body && !r.schema?.response
3371
+ );
3372
+ if (missing.length > 0) {
3373
+ add(ctx, {
3374
+ severity: "warn",
3375
+ area: "validation",
3376
+ message: `${missing.length} route${missing.length === 1 ? "" : "s"} with body schema but no response schema`,
3377
+ hint: `Declaring \`schema.response\` pins the API contract \u2014 without it, internal model field names leak to clients and breaking changes go undetected. First offender: ${missing[0].method} ${missing[0].path}`
3378
+ });
3379
+ } else if (routes.length > 0) {
3380
+ add(ctx, {
3381
+ severity: "ok",
3382
+ area: "validation",
3383
+ message: "every route with a body schema also declares a response schema"
3384
+ });
3385
+ }
3386
+ }
3387
+ function checkOpenApiNaming(ctx) {
3388
+ const routes = ctx.app.registeredRoutes ?? [];
3389
+ const usingMeta = routes.filter((r) => r.meta);
3390
+ if (usingMeta.length > 0) {
3391
+ add(ctx, {
3392
+ severity: "fail",
3393
+ area: "api",
3394
+ message: `${usingMeta.length} route${usingMeta.length === 1 ? "" : "s"} use the removed \`meta:\` field`,
3395
+ hint: `The \`meta:\` field was removed in 6.0. Rename to \`openapi:\` \u2014 shape is identical. First offender: ${usingMeta[0].method} ${usingMeta[0].path}`
3396
+ });
3397
+ }
3398
+ }
3399
+ function checkHealthCheck(ctx) {
3400
+ const routes = ctx.app.registeredRoutes ?? [];
3401
+ const hasHealth = routes.some(
3402
+ (r) => r.method === "GET" && ["/health", "/healthz", "/-/health", "/ping", "/live", "/ready"].some(
3403
+ (p) => r.path === p
3404
+ )
3405
+ );
3406
+ if (hasHealth) {
3407
+ add(ctx, { severity: "ok", area: "ops", message: "health-check route registered" });
3408
+ } else {
3409
+ add(ctx, {
3410
+ severity: "warn",
3411
+ area: "ops",
3412
+ message: "no health-check route detected",
3413
+ hint: 'Kubernetes, ECS, and load balancers expect a `/health` (or similar) endpoint. Register one via `app.healthCheck("/health")` from `@axiomify/core`.'
3414
+ });
3415
+ }
3416
+ }
3417
+ function checkOpenApiExposure(ctx) {
3418
+ const routes = ctx.app.registeredRoutes ?? [];
3419
+ const docsRoute = routes.find(
3420
+ (r) => r.method === "GET" && (r.path === "/docs" || r.path.endsWith("/docs"))
3421
+ );
3422
+ const specRoute = routes.find(
3423
+ (r) => r.method === "GET" && r.path.endsWith("/openapi.json")
3424
+ );
3425
+ if (docsRoute || specRoute) {
3426
+ add(ctx, {
3427
+ severity: "warn",
3428
+ area: "security",
3429
+ message: "OpenAPI docs endpoint is registered",
3430
+ hint: "In production, supply `protect: (req) => ...` to `useOpenAPI()` (or set `allowPublicInProduction: true` if exposure is intentional). The runtime warning is logged on the first request."
3431
+ });
3432
+ }
3433
+ }
3434
+ function checkRoutesLockState(ctx) {
3435
+ if (ctx.app._routesLocked) {
3436
+ add(ctx, {
3437
+ severity: "warn",
3438
+ area: "config",
3439
+ message: "app.lockRoutes() has already been called",
3440
+ hint: "The entry file appears to construct an adapter at the top level. Wrap adapter construction in `if (require.main === module) { ... }` so the CLI can introspect the app without triggering it."
3441
+ });
3442
+ }
3443
+ }
3444
+ function checkSecurityPlugins(ctx) {
3445
+ const onRequest = ctx.app.hooks?.hooks?.onRequest ?? [];
3446
+ if (onRequest.length < 2) {
3447
+ add(ctx, {
3448
+ severity: "warn",
3449
+ area: "security",
3450
+ message: "few onRequest hooks detected \u2014 security plugins may not be registered",
3451
+ hint: "`@axiomify/helmet`, `@axiomify/cors`, and `@axiomify/security` each install onRequest hooks. If you are not using these, ensure equivalent defences are in place elsewhere."
3452
+ });
3453
+ } else {
3454
+ add(ctx, {
3455
+ severity: "ok",
3456
+ area: "security",
3457
+ message: `${onRequest.length} onRequest hooks registered (security plugins likely active)`
3458
+ });
3459
+ }
3460
+ }
3461
+ function collectEnvKeysFromBundle(bundlePath) {
3462
+ const keys = /* @__PURE__ */ new Set();
3463
+ try {
3464
+ const src = fs.readFileSync(bundlePath, "utf8");
3465
+ const re = /process\.env\.([A-Z][A-Z0-9_]*)|process\.env\[(['"])([A-Z][A-Z0-9_]*)\2\]/g;
3466
+ let m;
3467
+ while ((m = re.exec(src)) !== null) {
3468
+ keys.add(m[1] ?? m[3]);
3469
+ }
3470
+ } catch {
3471
+ }
3472
+ return keys;
3473
+ }
3474
+ function loadPkgJson(cwd) {
3475
+ try {
3476
+ return JSON.parse(fs.readFileSync(path7.join(cwd, "package.json"), "utf8"));
3477
+ } catch {
3478
+ return null;
3479
+ }
3480
+ }
3481
+ async function runCheck(entry) {
3482
+ let app;
3483
+ let cleanup = async () => {
3484
+ };
3485
+ let bundlePath = "";
3486
+ try {
3487
+ const loaded = await loadApp(entry);
3488
+ app = loaded.app;
3489
+ cleanup = loaded.cleanup;
3490
+ bundlePath = path7.resolve(process.cwd(), ".axiomify/inspect.cjs");
3491
+ } catch (err) {
3492
+ console.error(pc.red("\u2717 Failed to load app:"));
3493
+ console.error(err.message);
3494
+ process.exit(1);
3495
+ }
3496
+ const ctx = {
3497
+ app,
3498
+ cwd: process.cwd(),
3499
+ findings: [],
3500
+ pkgJson: loadPkgJson(process.cwd()),
3501
+ envKeys: collectEnvKeysFromBundle(bundlePath)
3502
+ };
3503
+ checkEnvVars(ctx);
3504
+ checkRequestId(ctx);
3505
+ checkRoutesLockState(ctx);
3506
+ checkOpenApiNaming(ctx);
3507
+ checkSecurityPlugins(ctx);
3508
+ checkOpenApiExposure(ctx);
3509
+ checkResponseSchemas(ctx);
3510
+ checkHealthCheck(ctx);
3511
+ await cleanup();
3512
+ console.log();
3513
+ console.log(pc.bold(" \u{1F50D} Production-readiness check"));
3514
+ console.log();
3515
+ const sevOrder = { fail: 0, warn: 1, ok: 2 };
3516
+ ctx.findings.sort(
3517
+ (a, b) => sevOrder[a.severity] - sevOrder[b.severity] || a.area.localeCompare(b.area)
3518
+ );
3519
+ const sym = {
3520
+ ok: symbols.ok,
3521
+ warn: symbols.warn,
3522
+ fail: symbols.fail
3523
+ };
3524
+ for (const f of ctx.findings) {
3525
+ const tag = pc.dim(`[${f.area}]`);
3526
+ console.log(` ${sym[f.severity]} ${tag} ${f.message}`);
3527
+ if (f.hint && f.severity !== "ok") {
3528
+ const wrapped = f.hint.replace(/(.{1,80})(\s+|$)/g, "\n " + pc.dim("$1"));
3529
+ console.log(wrapped);
3530
+ }
3531
+ }
3532
+ const fails = ctx.findings.filter((f) => f.severity === "fail").length;
3533
+ const warns = ctx.findings.filter((f) => f.severity === "warn").length;
3534
+ const oks = ctx.findings.filter((f) => f.severity === "ok").length;
3535
+ console.log();
3536
+ const summary = ` ${symbols.ok} ${pluralise(oks, "pass", "passes")}` + pc.dim(" \xB7 ") + (warns > 0 ? `${symbols.warn} ${pluralise(warns, "warning")}` : pc.dim("0 warnings")) + pc.dim(" \xB7 ") + (fails > 0 ? `${symbols.fail} ${pluralise(fails, "failure")}` : pc.dim("0 failures"));
3537
+ console.log(summary);
3538
+ console.log();
3539
+ if (fails > 0) process.exit(1);
3540
+ }
2942
3541
  async function devServer(entry) {
2943
- const entryPath = path4.resolve(process.cwd(), entry);
2944
- const outPath = path4.resolve(process.cwd(), ".axiomify/dev.js");
3542
+ const entryPath = path7.resolve(process.cwd(), entry);
3543
+ const outPath = path7.resolve(process.cwd(), ".axiomify/dev.js");
2945
3544
  let child = null;
2946
3545
  let firstBuild = true;
2947
3546
  const startChild = () => {
@@ -3021,6 +3620,213 @@ async function devServer(entry) {
3021
3620
  console.log(`\u{1F440} Axiomify Dev Engine watching for changes...`);
3022
3621
  await ctx.watch();
3023
3622
  }
3623
+ function add2(findings, f) {
3624
+ findings.push(f);
3625
+ }
3626
+ function probePort(port) {
3627
+ return new Promise((resolve) => {
3628
+ const server = createServer();
3629
+ server.once("error", (err) => {
3630
+ if (err.code === "EADDRINUSE") resolve("busy");
3631
+ else if (err.code === "EACCES") resolve("denied");
3632
+ else resolve("busy");
3633
+ });
3634
+ server.once("listening", () => {
3635
+ server.close(() => resolve("free"));
3636
+ });
3637
+ server.listen(port, "127.0.0.1");
3638
+ });
3639
+ }
3640
+ function checkNodeVersion(findings) {
3641
+ const major = parseInt(process.versions.node.split(".")[0], 10);
3642
+ if (major < 18) {
3643
+ add2(findings, {
3644
+ severity: "fail",
3645
+ area: "node",
3646
+ message: `Node ${process.versions.node} is below the supported minimum`,
3647
+ hint: "Axiomify requires Node 18 or later. Upgrade via nvm: `nvm install 22 && nvm use 22`."
3648
+ });
3649
+ } else if (major > 22) {
3650
+ add2(findings, {
3651
+ severity: "warn",
3652
+ area: "node",
3653
+ message: `Node ${process.versions.node} \u2014 uWebSockets.js has no prebuilt binary for this version`,
3654
+ hint: "Tests and benchmarks that need a real uWS listener will skip; the framework still builds. For a runnable production deploy, downgrade to Node 22 LTS."
3655
+ });
3656
+ } else {
3657
+ add2(findings, {
3658
+ severity: "ok",
3659
+ area: "node",
3660
+ message: `Node ${process.versions.node} (uWS prebuilt available)`
3661
+ });
3662
+ }
3663
+ }
3664
+ function checkPlatform(findings) {
3665
+ if (process.platform === "linux") {
3666
+ add2(findings, {
3667
+ severity: "ok",
3668
+ area: "platform",
3669
+ message: "Linux \u2014 SO_REUSEPORT clustering supported natively"
3670
+ });
3671
+ } else {
3672
+ add2(findings, {
3673
+ severity: "warn",
3674
+ area: "platform",
3675
+ message: `${process.platform} \u2014 \`listenClustered()\` requires \`allowUserspaceProxy: true\``,
3676
+ hint: "Clustering on non-Linux falls back to a userspace L4 proxy that adds two event-loop hops per byte. Single-process `listen()` is the recommended path on this OS."
3677
+ });
3678
+ }
3679
+ }
3680
+ function checkDependencyDrift(findings) {
3681
+ try {
3682
+ const pkgPath = path7.join(process.cwd(), "package.json");
3683
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
3684
+ const all = { ...pkg.dependencies, ...pkg.devDependencies };
3685
+ const axiomifyDeps = Object.entries(all).filter(
3686
+ ([k]) => k.startsWith("@axiomify/")
3687
+ );
3688
+ if (axiomifyDeps.length === 0) {
3689
+ add2(findings, {
3690
+ severity: "warn",
3691
+ area: "deps",
3692
+ message: "No @axiomify/* packages found in package.json",
3693
+ hint: "Run this from the root of a project that uses Axiomify."
3694
+ });
3695
+ return;
3696
+ }
3697
+ const versions = new Set(
3698
+ axiomifyDeps.map(([, v]) => v.replace(/^[\^~]/, "").replace(/^\*$/, "workspace"))
3699
+ );
3700
+ if (versions.size > 1) {
3701
+ add2(findings, {
3702
+ severity: "warn",
3703
+ area: "deps",
3704
+ message: `@axiomify/* packages are on ${versions.size} different versions`,
3705
+ hint: "Mixed @axiomify/* versions can cause subtle compat issues. Pin them all to the same version: " + [...versions].join(", ")
3706
+ });
3707
+ } else {
3708
+ add2(findings, {
3709
+ severity: "ok",
3710
+ area: "deps",
3711
+ message: `${axiomifyDeps.length} @axiomify/* packages aligned (${[...versions][0]})`
3712
+ });
3713
+ }
3714
+ } catch {
3715
+ add2(findings, {
3716
+ severity: "warn",
3717
+ area: "deps",
3718
+ message: "Could not read package.json",
3719
+ hint: "Run `axiomify doctor` from a project root."
3720
+ });
3721
+ }
3722
+ }
3723
+ function checkUwsLoads(findings) {
3724
+ try {
3725
+ require_uws();
3726
+ add2(findings, {
3727
+ severity: "ok",
3728
+ area: "uws",
3729
+ message: "uWebSockets.js loads successfully"
3730
+ });
3731
+ } catch (err) {
3732
+ const msg = String(err.message ?? err);
3733
+ if (msg.includes("Cannot find module")) {
3734
+ add2(findings, {
3735
+ severity: "warn",
3736
+ area: "uws",
3737
+ message: "uWebSockets.js is not installed in this project",
3738
+ hint: "It is a peer dependency of `@axiomify/native`. Install via `npm install --save-optional uWebSockets.js` (the package handles platform selection)."
3739
+ });
3740
+ } else {
3741
+ add2(findings, {
3742
+ severity: "fail",
3743
+ area: "uws",
3744
+ message: "uWebSockets.js native binding failed to load",
3745
+ hint: msg.length > 200 ? msg.slice(0, 200) + "\u2026" : msg
3746
+ });
3747
+ }
3748
+ }
3749
+ }
3750
+ async function checkPortAvailability(findings) {
3751
+ const port = parseInt(process.env.PORT ?? "3000", 10);
3752
+ const state = await probePort(port);
3753
+ if (state === "free") {
3754
+ add2(findings, {
3755
+ severity: "ok",
3756
+ area: "port",
3757
+ message: `Port ${port} is available on 127.0.0.1`
3758
+ });
3759
+ } else if (state === "busy") {
3760
+ add2(findings, {
3761
+ severity: "warn",
3762
+ area: "port",
3763
+ message: `Port ${port} is already in use`,
3764
+ hint: "Stop the conflicting process or set `PORT` to a different value."
3765
+ });
3766
+ } else {
3767
+ add2(findings, {
3768
+ severity: "warn",
3769
+ area: "port",
3770
+ message: `Port ${port}: bind denied (permission)`,
3771
+ hint: "Ports below 1024 typically require root. Use a port \u2265 1024 in development."
3772
+ });
3773
+ }
3774
+ }
3775
+ function checkBuildArtifacts(findings) {
3776
+ const dist = path7.join(process.cwd(), "dist");
3777
+ if (fs.existsSync(dist)) {
3778
+ add2(findings, {
3779
+ severity: "ok",
3780
+ area: "build",
3781
+ message: "dist/ exists (recent `axiomify build`)"
3782
+ });
3783
+ } else {
3784
+ add2(findings, {
3785
+ severity: "warn",
3786
+ area: "build",
3787
+ message: "No dist/ directory \u2014 production build has not been run",
3788
+ hint: "Run `axiomify build` before deploying."
3789
+ });
3790
+ }
3791
+ }
3792
+ async function runDoctor() {
3793
+ const findings = [];
3794
+ checkNodeVersion(findings);
3795
+ checkPlatform(findings);
3796
+ checkDependencyDrift(findings);
3797
+ checkUwsLoads(findings);
3798
+ checkBuildArtifacts(findings);
3799
+ await checkPortAvailability(findings);
3800
+ console.log();
3801
+ console.log(pc.bold(" \u{1FA7A} Axiomify doctor"));
3802
+ console.log();
3803
+ const sevOrder = { fail: 0, warn: 1, ok: 2 };
3804
+ findings.sort(
3805
+ (a, b) => sevOrder[a.severity] - sevOrder[b.severity] || a.area.localeCompare(b.area)
3806
+ );
3807
+ const sym = {
3808
+ ok: symbols.ok,
3809
+ warn: symbols.warn,
3810
+ fail: symbols.fail
3811
+ };
3812
+ for (const f of findings) {
3813
+ const tag = pc.dim(`[${f.area}]`);
3814
+ console.log(` ${sym[f.severity]} ${tag} ${f.message}`);
3815
+ if (f.hint && f.severity !== "ok") {
3816
+ const wrapped = f.hint.replace(/(.{1,80})(\s+|$)/g, "\n " + pc.dim("$1"));
3817
+ console.log(wrapped);
3818
+ }
3819
+ }
3820
+ const fails = findings.filter((f) => f.severity === "fail").length;
3821
+ const warns = findings.filter((f) => f.severity === "warn").length;
3822
+ const oks = findings.filter((f) => f.severity === "ok").length;
3823
+ console.log();
3824
+ console.log(
3825
+ ` ${symbols.ok} ${pluralise(oks, "pass", "passes")}` + pc.dim(" \xB7 ") + (warns > 0 ? `${symbols.warn} ${pluralise(warns, "warning")}` : pc.dim("0 warnings")) + pc.dim(" \xB7 ") + (fails > 0 ? `${symbols.fail} ${pluralise(fails, "failure")}` : pc.dim("0 failures"))
3826
+ );
3827
+ console.log();
3828
+ if (fails > 0) process.exit(1);
3829
+ }
3024
3830
  var DEV_COMMAND_BY_PM = {
3025
3831
  npm: "npm run dev",
3026
3832
  pnpm: "pnpm dev",
@@ -3038,20 +3844,6 @@ async function initProject(targetDir, options = {}) {
3038
3844
  });
3039
3845
  }
3040
3846
  questions.push(
3041
- {
3042
- type: "select",
3043
- name: "adapter",
3044
- message: "Which HTTP adapter do you want to use?",
3045
- choices: [
3046
- "Native (uWS \u2014 Fastest, 50k+ req/s)",
3047
- "Fastify (High-throughput, recommended)",
3048
- "Express (Max ecosystem compatibility)",
3049
- "Hapi (Enterprise, plugin-first)",
3050
- "Node HTTP (Zero dependency)"
3051
- ],
3052
- initial: 0
3053
- // Native is the recommended default
3054
- },
3055
3847
  {
3056
3848
  type: "input",
3057
3849
  name: "description",
@@ -3105,12 +3897,12 @@ async function initProject(targetDir, options = {}) {
3105
3897
  );
3106
3898
  process.exit(1);
3107
3899
  }
3108
- const dir = path4.resolve(process.cwd(), projectName);
3900
+ const dir = path7.resolve(process.cwd(), projectName);
3109
3901
  if (existsSync(dir) && !options.force && targetDir) {
3110
3902
  const targets = [
3111
- path4.join(dir, "package.json"),
3112
- path4.join(dir, "tsconfig.json"),
3113
- path4.join(dir, "src", "index.ts")
3903
+ path7.join(dir, "package.json"),
3904
+ path7.join(dir, "tsconfig.json"),
3905
+ path7.join(dir, "src", "index.ts")
3114
3906
  ];
3115
3907
  const collisions = targets.filter((p) => existsSync(p));
3116
3908
  if (collisions.length > 0) {
@@ -3122,28 +3914,11 @@ async function initProject(targetDir, options = {}) {
3122
3914
  process.exit(1);
3123
3915
  }
3124
3916
  }
3125
- await fs2.mkdir(path4.join(dir, "src"), { recursive: true });
3917
+ await fs5.mkdir(path7.join(dir, "src"), { recursive: true });
3126
3918
  const AXIOMIFY_VERSION = `^${package_default.version}`;
3127
- let adapterPackage = "@axiomify/native";
3128
- let adapterImport = "import { NativeAdapter } from '@axiomify/native';";
3129
- let adapterInit = "const server = new NativeAdapter(app, { port: 3000 });\n server.listen(() => console.log(' Axiomify Native on :3000'));";
3130
- if (answers.adapter.includes("Fastify")) {
3131
- adapterPackage = "@axiomify/fastify";
3132
- adapterImport = "import { FastifyAdapter } from '@axiomify/fastify';";
3133
- adapterInit = "const server = new FastifyAdapter(app);\n await server.listen(3000);\n console.log(' Axiomify Fastify on :3000');";
3134
- } else if (answers.adapter.includes("Express")) {
3135
- adapterPackage = "@axiomify/express";
3136
- adapterImport = "import { ExpressAdapter } from '@axiomify/express';";
3137
- adapterInit = "const server = new ExpressAdapter(app);\n server.listen(3000, () => console.log(' Axiomify Express on :3000'));";
3138
- } else if (answers.adapter.includes("Hapi")) {
3139
- adapterPackage = "@axiomify/hapi";
3140
- adapterImport = "import { HapiAdapter } from '@axiomify/hapi';";
3141
- adapterInit = "const server = new HapiAdapter(app);\n await server.listen(3000);\n console.log(' Axiomify Hapi on :3000');";
3142
- } else if (answers.adapter.includes("HTTP")) {
3143
- adapterPackage = "@axiomify/http";
3144
- adapterImport = "import { HttpAdapter } from '@axiomify/http';";
3145
- adapterInit = "const server = new HttpAdapter(app);\n server.listen(3000, () => console.log(' Axiomify HTTP on :3000'));";
3146
- }
3919
+ const adapterPackage = "@axiomify/native";
3920
+ const adapterImport = "import { NativeAdapter } from '@axiomify/native';";
3921
+ const adapterInit = "const server = new NativeAdapter(app, { port: 3000 });\n server.listen(() => console.log(' Axiomify Native on :3000'));";
3147
3922
  const pkgJson = {
3148
3923
  name: projectName,
3149
3924
  version: "1.0.0",
@@ -3211,14 +3986,14 @@ async function initProject(targetDir, options = {}) {
3211
3986
  "printWidth": 100,
3212
3987
  "tabWidth": 2
3213
3988
  }`;
3214
- await fs2.writeFile(path4.join(dir, ".eslintrc.cjs"), eslintConfig);
3215
- await fs2.writeFile(path4.join(dir, ".prettierrc"), prettierConfig);
3216
- await fs2.writeFile(
3217
- path4.join(dir, ".prettierignore"),
3989
+ await fs5.writeFile(path7.join(dir, ".eslintrc.cjs"), eslintConfig);
3990
+ await fs5.writeFile(path7.join(dir, ".prettierrc"), prettierConfig);
3991
+ await fs5.writeFile(
3992
+ path7.join(dir, ".prettierignore"),
3218
3993
  "dist\nnode_modules\ncoverage\n"
3219
3994
  );
3220
- await fs2.writeFile(
3221
- path4.join(dir, ".editorconfig"),
3995
+ await fs5.writeFile(
3996
+ path7.join(dir, ".editorconfig"),
3222
3997
  "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n"
3223
3998
  );
3224
3999
  }
@@ -3279,19 +4054,19 @@ async function initProject(targetDir, options = {}) {
3279
4054
  "coverage",
3280
4055
  "*.log"
3281
4056
  ].join("\n") + "\n";
3282
- await fs2.writeFile(
3283
- path4.join(dir, "package.json"),
4057
+ await fs5.writeFile(
4058
+ path7.join(dir, "package.json"),
3284
4059
  JSON.stringify(pkgJson, null, 2)
3285
4060
  );
3286
- await fs2.writeFile(
3287
- path4.join(dir, "tsconfig.json"),
4061
+ await fs5.writeFile(
4062
+ path7.join(dir, "tsconfig.json"),
3288
4063
  JSON.stringify(tsConfig, null, 2)
3289
4064
  );
3290
- await fs2.writeFile(path4.join(dir, "src", "index.ts"), indexTs);
3291
- await fs2.writeFile(path4.join(dir, ".gitignore"), gitignore);
4065
+ await fs5.writeFile(path7.join(dir, "src", "index.ts"), indexTs);
4066
+ await fs5.writeFile(path7.join(dir, ".gitignore"), gitignore);
3292
4067
  console.log(pc.green(`
3293
4068
  \u2705 Axiomify project initialized in ${pc.bold(dir)}`));
3294
- if (answers.useGit && !existsSync(path4.join(dir, ".git"))) {
4069
+ if (answers.useGit && !existsSync(path7.join(dir, ".git"))) {
3295
4070
  try {
3296
4071
  await execa("git", ["init"], { cwd: dir });
3297
4072
  console.log(pc.green("\u2705 Git repository initialized"));
@@ -3325,63 +4100,640 @@ async function initProject(targetDir, options = {}) {
3325
4100
  \u{1F525} Run "${devCommand}" to start your development server!`)
3326
4101
  );
3327
4102
  }
3328
- async function inspectRoutes(entry) {
3329
- const entryPath = path4.resolve(process.cwd(), entry);
3330
- const tempPath = path4.resolve(process.cwd(), ".axiomify/inspect.cjs");
3331
- const userExternals = getUserExternals(process.cwd());
3332
- try {
3333
- await esbuild.build({
3334
- entryPoints: [entryPath],
3335
- bundle: true,
3336
- platform: "node",
3337
- format: "cjs",
3338
- outfile: tempPath,
3339
- external: [.../* @__PURE__ */ new Set([...userExternals, "node:*"])]
3340
- });
4103
+ var RULES = [
4104
+ {
4105
+ id: "meta-to-openapi",
4106
+ description: "`meta:` route field renamed to `openapi:` (OpenAPI 3.0.3 Operation Object terminology)",
4107
+ // Match `meta: {` or `meta:{` after whitespace/comma — avoid matching
4108
+ // unrelated `meta:` fields in other object literals by requiring an
4109
+ // adjacent route-definition cue (method, path, schema, handler nearby).
4110
+ // To stay simple and conservative, only rewrite when the line starts
4111
+ // with optional whitespace + `meta:` (the common formatting).
4112
+ match: /^(\s*)meta:(\s*\{)/gm,
4113
+ replace: "$1openapi:$2"
4114
+ },
4115
+ {
4116
+ id: "useSwagger-import",
4117
+ description: "`useSwagger` import \u2192 `useOpenAPI` (the function was never named `useSwagger` in shipped code \u2014 docs were wrong)",
4118
+ match: /\buseSwagger\b/g,
4119
+ replace: "useOpenAPI"
4120
+ },
4121
+ {
4122
+ id: "routePrefix-option",
4123
+ description: "`routePrefix:` \u2192 `prefix:` on `useOpenAPI()` options",
4124
+ match: /(\buseOpenAPI\s*\([\s\S]*?)\brouteprefix(\s*:)/gi,
4125
+ // Naive: just rename the property when it appears inside a useOpenAPI() call.
4126
+ // The capture-group lookbehind avoids touching unrelated `routePrefix`
4127
+ // properties in other contexts.
4128
+ replace: (_match, before, suffix) => `${before}prefix${suffix}`
4129
+ },
4130
+ {
4131
+ id: "RouteMeta-type",
4132
+ description: "`RouteMeta` type \u2192 `OpenApiOperation` (alias kept through 5.x, removed in 6.0)",
4133
+ // Match `RouteMeta` only as a type position (after `:` or `<` or
4134
+ // `as`) — avoids hitting an unrelated variable named `RouteMeta`.
4135
+ match: /(:\s*|<\s*|\bas\s+)RouteMeta\b/g,
4136
+ replace: "$1OpenApiOperation"
4137
+ },
4138
+ {
4139
+ id: "AppPlugin-type",
4140
+ description: "`AppPlugin` type alias \u2192 `AppConfigurator` (removed in 5.0; runtime accepts 1-arg fns identically)",
4141
+ match: /(:\s*|<\s*|\bas\s+)AppPlugin\b/g,
4142
+ replace: "$1AppConfigurator"
4143
+ }
4144
+ ];
4145
+ async function listSourceFiles(rootAbs) {
4146
+ const out = [];
4147
+ const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".axiomify", "coverage"]);
4148
+ const walk = async (dir) => {
4149
+ let entries;
3341
4150
  try {
3342
- delete __require.cache[__require.resolve(tempPath)];
3343
- } catch (e) {
4151
+ entries = await fs5.readdir(dir, { withFileTypes: true });
4152
+ } catch {
4153
+ return;
3344
4154
  }
3345
- const inspectionTimeout = setTimeout(() => {
3346
- console.warn(
3347
- "\n\u26A0\uFE0F Route inspection is taking longer than expected.\n Your entry file may be starting a server unconditionally.\n Wrap the listen() call in `if (require.main === module) { ... }`\n so it only runs when the file is executed directly.\n"
3348
- );
3349
- }, 5e3);
3350
- inspectionTimeout.unref();
3351
- const mod = __require(tempPath);
3352
- const app = mod.app || mod.default;
3353
- if (!app || typeof app.registeredRoutes === "undefined") {
3354
- console.error("\u274C Error: Could not find an exported Axiomify instance.");
4155
+ for (const e of entries) {
4156
+ if (e.isDirectory()) {
4157
+ if (skip.has(e.name) || e.name.startsWith(".")) continue;
4158
+ await walk(path7.join(dir, e.name));
4159
+ } else if (e.isFile()) {
4160
+ const ext = path7.extname(e.name);
4161
+ if ([".ts", ".tsx", ".js", ".mjs", ".cjs"].includes(ext)) {
4162
+ out.push(path7.join(dir, e.name));
4163
+ }
4164
+ }
4165
+ }
4166
+ };
4167
+ await walk(rootAbs);
4168
+ return out;
4169
+ }
4170
+ function applyRules(src) {
4171
+ let updated = src;
4172
+ const counts = {};
4173
+ for (const rule of RULES) {
4174
+ const before = updated;
4175
+ if (typeof rule.replace === "string") {
4176
+ updated = updated.replace(rule.match, rule.replace);
4177
+ } else {
4178
+ updated = updated.replace(rule.match, rule.replace);
4179
+ }
4180
+ if (updated !== before) {
4181
+ const matches = before.match(rule.match);
4182
+ counts[rule.id] = matches ? matches.length : 1;
4183
+ }
4184
+ }
4185
+ return { updated, counts };
4186
+ }
4187
+ function renderUnifiedDiff(file, original, updated) {
4188
+ if (original === updated) return "";
4189
+ const origLines = original.split("\n");
4190
+ const updatedLines = updated.split("\n");
4191
+ const out = [];
4192
+ out.push(pc.bold(`--- ${file}`));
4193
+ out.push(pc.bold(`+++ ${file}`));
4194
+ const max = Math.max(origLines.length, updatedLines.length);
4195
+ for (let i = 0; i < max; i++) {
4196
+ const a = origLines[i];
4197
+ const b = updatedLines[i];
4198
+ if (a === b) continue;
4199
+ if (a !== void 0) out.push(pc.red("- " + a));
4200
+ if (b !== void 0) out.push(pc.green("+ " + b));
4201
+ }
4202
+ return out.join("\n");
4203
+ }
4204
+ async function runMigrate(opts = {}) {
4205
+ const dir = opts.dir ?? "src";
4206
+ const rootAbs = path7.resolve(process.cwd(), dir);
4207
+ try {
4208
+ const stat = await fs5.stat(rootAbs);
4209
+ if (!stat.isDirectory()) throw new Error("not a directory");
4210
+ } catch {
4211
+ console.error(
4212
+ pc.red(`\u2717 ${dir} does not exist or is not a directory.`),
4213
+ `
4214
+ Run from a project root that contains the directory you want to migrate.`
4215
+ );
4216
+ process.exit(1);
4217
+ }
4218
+ const files = await listSourceFiles(rootAbs);
4219
+ if (files.length === 0) {
4220
+ console.log(`${symbols.info} No source files found under ${pc.cyan(dir)}.`);
4221
+ return;
4222
+ }
4223
+ const results = [];
4224
+ for (const f of files) {
4225
+ const original = await fs5.readFile(f, "utf8");
4226
+ const { updated, counts } = applyRules(original);
4227
+ if (Object.keys(counts).length > 0) {
4228
+ results.push({ file: f, counts, original, updated });
4229
+ }
4230
+ }
4231
+ console.log();
4232
+ console.log(pc.bold(" \u{1F504} Axiomify v4 \u2192 v5 migration"));
4233
+ console.log(
4234
+ pc.dim(
4235
+ ` Scanned ${pluralise(files.length, "file")} under ${path7.relative(process.cwd(), rootAbs) || "."}/.
4236
+ `
4237
+ )
4238
+ );
4239
+ if (results.length === 0) {
4240
+ console.log(` ${symbols.ok} Nothing to migrate \u2014 looks like you're already on the v5 shape.`);
4241
+ console.log();
4242
+ return;
4243
+ }
4244
+ const totalsByRule = {};
4245
+ for (const r of results) {
4246
+ for (const [rule, n] of Object.entries(r.counts)) {
4247
+ totalsByRule[rule] = (totalsByRule[rule] ?? 0) + n;
4248
+ }
4249
+ }
4250
+ for (const rule of RULES) {
4251
+ const count = totalsByRule[rule.id];
4252
+ if (!count) continue;
4253
+ console.log(
4254
+ ` ${pc.cyan(rule.id)} ${pc.dim("\u2014")} ${rule.description}`
4255
+ );
4256
+ console.log(
4257
+ ` ${symbols.bullet} ${pluralise(count, "change")} across ${pluralise(
4258
+ results.filter((r) => r.counts[rule.id]).length,
4259
+ "file"
4260
+ )}`
4261
+ );
4262
+ }
4263
+ console.log();
4264
+ if (opts.reportOnly) {
4265
+ console.log(pc.dim(" --report-only: no files were modified."));
4266
+ console.log();
4267
+ return;
4268
+ }
4269
+ if (opts.dryRun) {
4270
+ for (const r of results) {
4271
+ const rel = path7.relative(process.cwd(), r.file);
4272
+ console.log(renderUnifiedDiff(rel, r.original, r.updated));
4273
+ console.log();
4274
+ }
4275
+ console.log(
4276
+ `${symbols.info} ${pluralise(results.length, "file")} would be modified. Re-run without ${pc.cyan("--dry-run")} to apply.`
4277
+ );
4278
+ console.log();
4279
+ return;
4280
+ }
4281
+ for (const r of results) {
4282
+ await fs5.writeFile(r.file, r.updated, "utf8");
4283
+ console.log(` ${symbols.ok} ${pc.green("Updated")} ${path7.relative(process.cwd(), r.file)}`);
4284
+ }
4285
+ console.log();
4286
+ console.log(
4287
+ ` ${symbols.ok} ${pluralise(results.length, "file")} migrated, ${pluralise(
4288
+ Object.values(totalsByRule).reduce((a, b) => a + b, 0),
4289
+ "total change"
4290
+ )} applied.`
4291
+ );
4292
+ console.log();
4293
+ console.log(pc.dim(" Manual review needed for:"));
4294
+ console.log(
4295
+ pc.dim(
4296
+ ` ${symbols.bullet} Dangling \`AppPlugin\` / \`RouteMeta\` in import statements \u2014 the codemod renames`
4297
+ )
4298
+ );
4299
+ console.log(
4300
+ pc.dim(
4301
+ ` type USAGES but not the import bindings themselves. TypeScript flags the unused`
4302
+ )
4303
+ );
4304
+ console.log(
4305
+ pc.dim(
4306
+ ` import; remove it (or run \`tsc --noUnusedLocals\` once and let your editor clean up).`
4307
+ )
4308
+ );
4309
+ console.log(
4310
+ pc.dim(
4311
+ ` ${symbols.bullet} 5-arg positional \`SerializerFn\` signatures \u2014 the function body needs by-hand updates`
4312
+ )
4313
+ );
4314
+ console.log(
4315
+ pc.dim(
4316
+ ` ${symbols.bullet} \`new Axiomify()\` callers that relied on automatic \`X-Request-Id\` injection`
4317
+ )
4318
+ );
4319
+ console.log(
4320
+ pc.dim(
4321
+ ` ${symbols.bullet} JWT secrets \u2014 verify they are \u2265 32 BYTES (not chars) per RFC 7518 \xA73.2`
4322
+ )
4323
+ );
4324
+ console.log();
4325
+ console.log(
4326
+ ` See ${pc.cyan("docs/migration-v4-to-v5.md")} for the full guide and ` + pc.cyan("axiomify check") + " to verify the migrated app."
4327
+ );
4328
+ console.log();
4329
+ }
4330
+ async function jsonToYaml(obj) {
4331
+ const emit = (v, indent) => {
4332
+ const pad2 = " ".repeat(indent);
4333
+ if (v === null) return "null";
4334
+ if (typeof v === "boolean" || typeof v === "number") return String(v);
4335
+ if (typeof v === "string") {
4336
+ if (/[:#\n\r\t"'{}[\]&*!|>%@`]|^\s|\s$/.test(v) || v === "") {
4337
+ return JSON.stringify(v);
4338
+ }
4339
+ return v;
4340
+ }
4341
+ if (Array.isArray(v)) {
4342
+ if (v.length === 0) return "[]";
4343
+ return v.map((item) => `
4344
+ ${pad2}- ${emit(item, indent + 1).replace(/^/gm, " ").trimStart()}`).join("");
4345
+ }
4346
+ if (typeof v === "object") {
4347
+ const keys = Object.keys(v);
4348
+ if (keys.length === 0) return "{}";
4349
+ return keys.map((k) => {
4350
+ const val = v[k];
4351
+ const rendered = emit(val, indent + 1);
4352
+ if (rendered.startsWith("\n")) return `
4353
+ ${pad2}${k}:${rendered}`;
4354
+ return `
4355
+ ${pad2}${k}: ${rendered}`;
4356
+ }).join("");
4357
+ }
4358
+ return JSON.stringify(v);
4359
+ };
4360
+ return emit(obj, 0).trimStart();
4361
+ }
4362
+ async function emitOpenApi(entry, opts = {}) {
4363
+ let app;
4364
+ let cleanup = async () => {
4365
+ };
4366
+ try {
4367
+ const loaded = await loadApp(entry);
4368
+ app = loaded.app;
4369
+ cleanup = loaded.cleanup;
4370
+ } catch (err) {
4371
+ console.error(pc.red("\u2717 Failed to load app:"));
4372
+ console.error(err.message);
4373
+ process.exit(1);
4374
+ }
4375
+ try {
4376
+ let OpenApiGenerator;
4377
+ try {
4378
+ ({ OpenApiGenerator } = await import('./dist-PKSGYMK7.mjs'));
4379
+ } catch {
3355
4380
  console.error(
3356
- "Ensure your entry file exports the app: `export const app = new Axiomify();`"
4381
+ pc.red("\u2717 @axiomify/openapi is not installed."),
4382
+ "\n Install it:",
4383
+ pc.cyan("npm install @axiomify/openapi")
3357
4384
  );
3358
4385
  process.exit(1);
3359
4386
  }
3360
- console.log("\n\u{1F9ED} Registered Axiomify Routes:");
3361
- console.log("----------------------------------------------------");
3362
- console.log(`${"METHOD".padEnd(10)} | ${"PATH".padEnd(30)} | VALIDATION`);
3363
- console.log("----------------------------------------------------");
3364
- app.registeredRoutes.forEach((route) => {
3365
- const schemas = [];
3366
- if (route.schema?.body) schemas.push("Body");
3367
- if (route.schema?.query) schemas.push("Query");
3368
- if (route.schema?.params) schemas.push("Params");
3369
- if (route.schema?.response) schemas.push("Response");
3370
- if (route.schema?.files) schemas.push("Files");
3371
- const validationStr = schemas.length > 0 ? schemas.join(", ") : "None";
4387
+ const info = {
4388
+ title: opts.title ?? "API",
4389
+ version: opts.version ?? "1.0.0"
4390
+ };
4391
+ const generator = new OpenApiGenerator(app, { info });
4392
+ const spec = generator.generate();
4393
+ if (opts.title) spec.info.title = opts.title;
4394
+ if (opts.version) spec.info.version = opts.version;
4395
+ const format = opts.format ?? "json";
4396
+ const serialised = format === "yaml" ? await jsonToYaml(spec) : opts.minify ? JSON.stringify(spec) : JSON.stringify(spec, null, 2);
4397
+ if (opts.output) {
4398
+ const outPath = path7.resolve(process.cwd(), opts.output);
4399
+ await fs5.mkdir(path7.dirname(outPath), { recursive: true });
4400
+ await fs5.writeFile(outPath, serialised + "\n", "utf8");
4401
+ const routeCount = (app.registeredRoutes ?? []).length;
3372
4402
  console.log(
3373
- `${route.method.padEnd(10)} | ${route.path.padEnd(
3374
- 30
3375
- )} | ${validationStr}`
4403
+ `${pc.green("\u2713")} OpenAPI spec written to ${pc.cyan(opts.output)} ` + pc.dim(`(${routeCount} route${routeCount === 1 ? "" : "s"}, ${format})`)
3376
4404
  );
3377
- });
3378
- console.log("----------------------------------------------------\n");
4405
+ } else {
4406
+ process.stdout.write(serialised + "\n");
4407
+ }
3379
4408
  } catch (error) {
3380
- console.error("\u274C Failed to inspect routes:", error);
4409
+ console.error(pc.red("\u2717 Failed to generate spec:"), error);
4410
+ process.exit(1);
3381
4411
  } finally {
3382
- await fs2.rm(path4.dirname(tempPath), { recursive: true, force: true }).catch(() => {
4412
+ await cleanup();
4413
+ }
4414
+ }
4415
+ function normalise(raw, isWs) {
4416
+ const validation = [];
4417
+ if (raw.schema?.body) validation.push("Body");
4418
+ if (raw.schema?.query) validation.push("Query");
4419
+ if (raw.schema?.params) validation.push("Params");
4420
+ if (raw.schema?.response) validation.push("Response");
4421
+ if (raw.schema?.files) validation.push("Files");
4422
+ if (raw.schema?.message) validation.push("Message");
4423
+ const op = raw.openapi ?? {};
4424
+ return {
4425
+ method: isWs ? "WS" : raw.method,
4426
+ path: raw.path,
4427
+ validation,
4428
+ tags: Array.isArray(op.tags) ? op.tags : [],
4429
+ operationId: typeof op.operationId === "string" ? op.operationId : void 0,
4430
+ deprecated: op.deprecated === true,
4431
+ timeout: typeof raw.timeout === "number" && raw.timeout > 0 ? raw.timeout : void 0,
4432
+ plugins: Array.isArray(raw.plugins) ? raw.plugins.length : 0,
4433
+ isWs
4434
+ };
4435
+ }
4436
+ function matchesFilter(route, opts) {
4437
+ if (opts.method) {
4438
+ const wanted = opts.method.split(",").map((m) => m.trim().toUpperCase()).filter(Boolean);
4439
+ if (wanted.length && !wanted.includes(route.method)) return false;
4440
+ }
4441
+ if (opts.filter) {
4442
+ const pat = opts.filter;
4443
+ if (pat.includes("*")) {
4444
+ const re = new RegExp(
4445
+ "^" + pat.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
4446
+ );
4447
+ if (!re.test(route.path)) return false;
4448
+ } else if (!route.path.includes(pat)) {
4449
+ return false;
4450
+ }
4451
+ }
4452
+ return true;
4453
+ }
4454
+ function sortRoutes(routes, by) {
4455
+ const methodOrder = {
4456
+ GET: 0,
4457
+ POST: 1,
4458
+ PUT: 2,
4459
+ PATCH: 3,
4460
+ DELETE: 4,
4461
+ HEAD: 5,
4462
+ OPTIONS: 6,
4463
+ WS: 7
4464
+ };
4465
+ return [...routes].sort((a, b) => {
4466
+ if (by === "method") {
4467
+ const am = methodOrder[a.method] ?? 99;
4468
+ const bm = methodOrder[b.method] ?? 99;
4469
+ if (am !== bm) return am - bm;
4470
+ return a.path.localeCompare(b.path);
4471
+ }
4472
+ if (a.path !== b.path) return a.path.localeCompare(b.path);
4473
+ return (methodOrder[a.method] ?? 99) - (methodOrder[b.method] ?? 99);
4474
+ });
4475
+ }
4476
+ async function inspectRoutes(entry, opts = {}) {
4477
+ let app;
4478
+ let cleanup = async () => {
4479
+ };
4480
+ try {
4481
+ const loaded = await loadApp(entry);
4482
+ app = loaded.app;
4483
+ cleanup = loaded.cleanup;
4484
+ } catch (err) {
4485
+ console.error(pc.red("\u2717 Failed to load app:"));
4486
+ console.error(err.message);
4487
+ process.exit(1);
4488
+ }
4489
+ try {
4490
+ const httpRoutes = (app.registeredRoutes ?? []).map(
4491
+ (r) => normalise(r, false)
4492
+ );
4493
+ const wsRoutes = (app.registeredWsRoutes ?? []).map(
4494
+ (r) => normalise(r, true)
4495
+ );
4496
+ const all = sortRoutes([...httpRoutes, ...wsRoutes], opts.sort ?? "path");
4497
+ const filtered = all.filter((r) => matchesFilter(r, opts));
4498
+ if (opts.json) {
4499
+ process.stdout.write(JSON.stringify(filtered, null, 2) + "\n");
4500
+ return;
4501
+ }
4502
+ if (filtered.length === 0) {
4503
+ console.log(
4504
+ "\n" + symbols.info + pc.dim(
4505
+ ` No routes match the current filter (${all.length} total registered).
4506
+ `
4507
+ )
4508
+ );
4509
+ return;
4510
+ }
4511
+ console.log();
4512
+ console.log(pc.bold(" \u{1F9ED} Axiomify routes"));
4513
+ console.log();
4514
+ const columns = [
4515
+ { header: "METHOD", minWidth: 7 },
4516
+ { header: "PATH", minWidth: 20, maxWidth: 60 },
4517
+ { header: "VALIDATION", minWidth: 10, maxWidth: 32 },
4518
+ { header: "META", maxWidth: 40 }
4519
+ ];
4520
+ const rows = filtered.map((r) => {
4521
+ const method = colourMethod(r.method);
4522
+ const path11 = r.deprecated ? pc.strikethrough(r.path) + " " + badge.deprecated() : r.path;
4523
+ const validation = r.validation.length > 0 ? r.validation.map(badge.validation).join(pc.dim(",")) : pc.dim("\u2014");
4524
+ const metaBits = [];
4525
+ if (r.operationId) metaBits.push(pc.dim(`op:`) + r.operationId);
4526
+ if (r.tags.length) metaBits.push(badge.tags(r.tags));
4527
+ if (r.timeout !== void 0) metaBits.push(badge.timeout(r.timeout));
4528
+ if (r.plugins > 0) metaBits.push(pc.dim(`+${r.plugins} plugin${r.plugins === 1 ? "" : "s"}`));
4529
+ const meta = metaBits.length ? metaBits.join(" ") : pc.dim("\u2014");
4530
+ return [method, path11, validation, meta];
3383
4531
  });
4532
+ console.log(renderTable(columns, rows));
4533
+ const byMethod = filtered.reduce((acc, r) => {
4534
+ acc[r.method] = (acc[r.method] ?? 0) + 1;
4535
+ return acc;
4536
+ }, {});
4537
+ const summaryParts = Object.entries(byMethod).sort(([a], [b]) => a.localeCompare(b)).map(([m, n]) => `${colourMethod(m).trimEnd()} ${pc.bold(String(n))}`);
4538
+ console.log();
4539
+ console.log(
4540
+ ` ${symbols.ok} ${pluralise(filtered.length, "route")}` + (filtered.length < all.length ? pc.dim(` (filtered from ${all.length})`) : "") + " " + summaryParts.join(pc.dim(" \xB7 "))
4541
+ );
4542
+ const filteredWs = filtered.filter((r) => r.isWs).length;
4543
+ if (filteredWs > 0) {
4544
+ console.log(
4545
+ pc.dim(` \u2514 ${pluralise(filteredWs, "WebSocket route")} included`)
4546
+ );
4547
+ }
4548
+ console.log();
4549
+ } catch (error) {
4550
+ console.error(pc.red("\u2717 Failed to inspect routes:"), error);
4551
+ process.exit(1);
4552
+ } finally {
4553
+ await cleanup();
4554
+ }
4555
+ }
4556
+ var VALID_METHODS = /* @__PURE__ */ new Set([
4557
+ "GET",
4558
+ "POST",
4559
+ "PUT",
4560
+ "PATCH",
4561
+ "DELETE",
4562
+ "OPTIONS",
4563
+ "HEAD",
4564
+ "WS"
4565
+ ]);
4566
+ function pathToFilename(routePath) {
4567
+ const cleaned = routePath.split("/").filter(Boolean).map((seg) => seg.startsWith(":") ? `by-${seg.slice(1)}` : seg).join("-");
4568
+ return cleaned || "root";
4569
+ }
4570
+ function generateRouteSource(method, routePath, opts) {
4571
+ const isWs = method === "WS";
4572
+ const params = routePath.match(/:[a-zA-Z_][a-zA-Z0-9_]*/g) ?? [];
4573
+ const paramKeys = params.map((p) => p.slice(1));
4574
+ const plugins = [];
4575
+ const extraImports = [];
4576
+ if (opts.auth) {
4577
+ plugins.push("requireAuth");
4578
+ extraImports.push(
4579
+ `import { createAuthPlugin } from '@axiomify/auth';
4580
+
4581
+ const requireAuth = createAuthPlugin({
4582
+ secret: process.env.JWT_SECRET!,
4583
+ });`
4584
+ );
4585
+ }
4586
+ if (opts.rateLimit) {
4587
+ plugins.push("limiter");
4588
+ extraImports.push(
4589
+ `import { createRateLimitPlugin, MemoryStore } from '@axiomify/rate-limit';
4590
+
4591
+ // Replace MemoryStore with RedisStore for multi-process / multi-host.
4592
+ const limiter = createRateLimitPlugin({
4593
+ windowMs: 60_000,
4594
+ max: 100,
4595
+ store: new MemoryStore(),
4596
+ });`
4597
+ );
4598
+ }
4599
+ if (isWs) {
4600
+ return [
4601
+ `import type { Axiomify } from '@axiomify/core';`,
4602
+ `import { z } from 'zod';`,
4603
+ extraImports.length ? extraImports.join("\n\n") + "\n" : "",
4604
+ `/**`,
4605
+ ` * Registers WebSocket route ${routePath}.`,
4606
+ ` * Wire this into your entry file:`,
4607
+ ` * import { registerRoute } from './routes/${pathToFilename(routePath)}';`,
4608
+ ` * registerRoute(app);`,
4609
+ ` */`,
4610
+ `export function registerRoute(app: Axiomify): void {`,
4611
+ ` app.ws({`,
4612
+ ` path: '${routePath}',`,
4613
+ paramKeys.length > 0 ? ` schema: {
4614
+ params: z.object({ ${paramKeys.map((k) => `${k}: z.string()`).join(", ")} }),
4615
+ // Define your message shape here \u2014 runtime-validated on every incoming frame.
4616
+ message: z.object({
4617
+ text: z.string(),
4618
+ }),
4619
+ },` : ` schema: {
4620
+ // Define your message shape here \u2014 runtime-validated on every incoming frame.
4621
+ message: z.object({
4622
+ text: z.string(),
4623
+ }),
4624
+ },`,
4625
+ plugins.length > 0 ? ` plugins: [${plugins.join(", ")}],` : "",
4626
+ ` open: (client, _req) => {`,
4627
+ ` client.send({ type: 'welcome' });`,
4628
+ ` },`,
4629
+ ` message: (client, data) => {`,
4630
+ ` // \`data\` is typed and validated from the schema above.`,
4631
+ ` client.send({ echo: data.text });`,
4632
+ ` },`,
4633
+ ` close: (_client, code, reason) => {`,
4634
+ ` console.log('connection closed', code, reason);`,
4635
+ ` },`,
4636
+ ` });`,
4637
+ `}`,
4638
+ ``
4639
+ ].filter(Boolean).join("\n");
4640
+ }
4641
+ return [
4642
+ `import type { Axiomify } from '@axiomify/core';`,
4643
+ `import { z } from 'zod';`,
4644
+ extraImports.length ? extraImports.join("\n\n") + "\n" : "",
4645
+ `/**`,
4646
+ ` * Registers ${method} ${routePath}.`,
4647
+ ` * Wire this into your entry file:`,
4648
+ ` * import { registerRoute } from './routes/${pathToFilename(routePath)}';`,
4649
+ ` * registerRoute(app);`,
4650
+ ` */`,
4651
+ `export function registerRoute(app: Axiomify): void {`,
4652
+ ` app.route({`,
4653
+ ` method: '${method}',`,
4654
+ ` path: '${routePath}',`,
4655
+ ` schema: {`,
4656
+ paramKeys.length > 0 ? ` params: z.object({ ${paramKeys.map((k) => `${k}: z.string()`).join(", ")} }),` : "",
4657
+ method === "POST" || method === "PUT" || method === "PATCH" ? ` body: z.object({
4658
+ // TODO \u2014 define request body shape
4659
+ }),` : "",
4660
+ ` // response: z.object({ /* response shape */ }),`,
4661
+ ` },`,
4662
+ ` openapi: {`,
4663
+ ` tags: ['${pathToFilename(routePath).split("-")[0] || "general"}'],`,
4664
+ ` summary: '${method} ${routePath}',`,
4665
+ ` },`,
4666
+ plugins.length > 0 ? ` plugins: [${plugins.join(", ")}],` : "",
4667
+ ` handler: async (${paramKeys.length > 0 || method !== "GET" ? "req" : "_req"}, res) => {`,
4668
+ method === "POST" ? ` // TODO \u2014 handler logic
4669
+ res.status(201).send({ ok: true });` : method === "DELETE" ? ` // TODO \u2014 handler logic
4670
+ res.status(204).send(null);` : ` // TODO \u2014 handler logic
4671
+ res.send({ ok: true });`,
4672
+ ` },`,
4673
+ ` });`,
4674
+ `}`,
4675
+ ``
4676
+ ].filter(Boolean).join("\n");
4677
+ }
4678
+ async function scaffoldRoute(method, routePath, opts = {}) {
4679
+ const upperMethod = method.toUpperCase();
4680
+ if (!VALID_METHODS.has(upperMethod)) {
4681
+ console.error(
4682
+ pc.red(`\u2717 Invalid method "${method}".`),
4683
+ `Expected one of: ${[...VALID_METHODS].join(", ")}.`
4684
+ );
4685
+ process.exit(1);
4686
+ }
4687
+ if (!routePath.startsWith("/")) {
4688
+ console.error(
4689
+ pc.red('\u2717 Path must start with "/".'),
4690
+ `Got: ${routePath}`
4691
+ );
4692
+ process.exit(1);
4693
+ }
4694
+ const dir = opts.dir ?? "src/routes";
4695
+ const filename = pathToFilename(routePath) + ".ts";
4696
+ const fileAbs = path7.resolve(process.cwd(), dir, filename);
4697
+ const source = generateRouteSource(upperMethod, routePath, opts);
4698
+ if (opts.dryRun) {
4699
+ console.log(pc.dim(`# would write ${path7.relative(process.cwd(), fileAbs)}
4700
+ `));
4701
+ console.log(source);
4702
+ return;
4703
+ }
4704
+ let exists = false;
4705
+ try {
4706
+ await fs5.access(fileAbs);
4707
+ exists = true;
4708
+ } catch {
4709
+ }
4710
+ if (exists && !opts.force) {
4711
+ console.log(
4712
+ `${symbols.warn} ${pc.yellow("Already exists:")} ${path7.relative(process.cwd(), fileAbs)}
4713
+ Pass ${pc.cyan("--force")} to overwrite, or pick a different path.`
4714
+ );
4715
+ return;
3384
4716
  }
4717
+ await fs5.mkdir(path7.dirname(fileAbs), { recursive: true });
4718
+ await fs5.writeFile(fileAbs, source, "utf8");
4719
+ console.log();
4720
+ console.log(
4721
+ `${symbols.ok} ${pc.green("Created")} ${pc.cyan(path7.relative(process.cwd(), fileAbs))}`
4722
+ );
4723
+ console.log();
4724
+ console.log(pc.dim(" Next steps:"));
4725
+ console.log(
4726
+ ` 1. Wire it into your entry file:
4727
+ ` + pc.dim(` import { registerRoute } from './routes/${pathToFilename(routePath)}';
4728
+ `) + pc.dim(` registerRoute(app);`)
4729
+ );
4730
+ console.log(
4731
+ ` 2. Fill in the TODOs in ${pc.cyan(path7.relative(process.cwd(), fileAbs))}.`
4732
+ );
4733
+ console.log(
4734
+ ` 3. Verify with ${pc.cyan("npx axiomify routes")} and ${pc.cyan("npx axiomify check")}.`
4735
+ );
4736
+ console.log();
3385
4737
  }
3386
4738
 
3387
4739
  // src/index.ts
@@ -3392,5 +4744,37 @@ program2.command("init").description("Bootstrap a new Axiomify project").argumen
3392
4744
  );
3393
4745
  program2.command("dev").description("Start the development server with hot-reload").argument("[entry]", "Entry file", "src/index.ts").action(devServer);
3394
4746
  program2.command("build").description("Compile the application for production").argument("[entry]", "Entry file", "src/index.ts").action(buildProject);
3395
- program2.command("routes").description("Inspect and list all registered routes in the application").argument("[entry]", "Entry file", "src/index.ts").action(inspectRoutes);
4747
+ program2.command("routes").description("Inspect and list all registered HTTP + WebSocket routes").argument("[entry]", "Entry file", "src/index.ts").option("--json", "Emit machine-readable JSON instead of the formatted table", false).option(
4748
+ "-m, --method <list>",
4749
+ "Comma-separated list of methods to include (e.g. GET,POST,WS)"
4750
+ ).option(
4751
+ "-f, --filter <pattern>",
4752
+ 'Path filter \u2014 substring match, or glob with "*" (e.g. /api/v1/*)'
4753
+ ).option(
4754
+ "-s, --sort <by>",
4755
+ 'Sort routes by "method" or "path"',
4756
+ "path"
4757
+ ).action(
4758
+ (entry, options) => inspectRoutes(entry, options)
4759
+ );
4760
+ program2.command("openapi").description("Generate the OpenAPI spec from the app and emit it").argument("[entry]", "Entry file", "src/index.ts").option(
4761
+ "-o, --output <file>",
4762
+ "Write the spec to this file path instead of stdout"
4763
+ ).option("--format <fmt>", 'Output format: "json" (default) or "yaml"', "json").option("--minify", "Minified JSON (single line). Ignored for yaml.", false).option("--title <title>", "Override info.title in the generated spec").option("--spec-version <version>", "Override info.version in the generated spec").action(
4764
+ (entry, options) => emitOpenApi(entry, {
4765
+ ...options,
4766
+ // Map the CLI flag name (`spec-version` → camel `specVersion`) onto
4767
+ // the internal `version` field that emitOpenApi() expects.
4768
+ version: options.specVersion
4769
+ })
4770
+ );
4771
+ program2.command("check").description("Run a static production-readiness audit against the app").argument("[entry]", "Entry file", "src/index.ts").action(runCheck);
4772
+ program2.command("doctor").description("Diagnose the host environment (Node, uWS, ports, dep drift)").action(runDoctor);
4773
+ var scaffold = program2.command("scaffold").description("Generate boilerplate (routes, modules, plugins)");
4774
+ scaffold.command("route <method> <path>").description("Create a new route file under src/routes/").option("--auth", "Include `requireAuth` plugin and import", false).option("--rate-limit", "Include a default rate-limit plugin", false).option("--dry-run", "Print the generated source without writing", false).option("--force", "Overwrite the file if it already exists", false).option("--dir <dir>", "Directory under cwd to create the file in", "src/routes").action(
4775
+ (method, routePath, options) => scaffoldRoute(method, routePath, options)
4776
+ );
4777
+ program2.command("migrate").description("v4 \u2192 v5 codemod: rename meta\u2192openapi, useSwagger\u2192useOpenAPI, etc").option("--dry-run", "Show the unified diff without writing", false).option("--report-only", "Print a migration report and exit; do not write", false).option("--dir <dir>", "Directory to scan recursively", "src").action(
4778
+ (options) => runMigrate(options)
4779
+ );
3396
4780
  program2.parse(process.argv);