@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/README.md +115 -52
- package/dist/chunk-YZPZCUKZ.mjs +41 -0
- package/dist/dist-PKSGYMK7.mjs +357 -0
- package/dist/index.js +1917 -124
- package/dist/index.mjs +1538 -154
- package/dist/uws_darwin_arm64_108-CLFXMYPI.node +0 -0
- package/dist/uws_darwin_arm64_115-7FFEG3YF.node +0 -0
- package/dist/uws_darwin_arm64_120-GFZT7CLS.node +0 -0
- package/dist/uws_darwin_arm64_127-KHC2FVAM.node +0 -0
- package/dist/uws_darwin_x64_108-BRGT45AT.node +0 -0
- package/dist/uws_darwin_x64_115-4HGPQGDD.node +0 -0
- package/dist/uws_darwin_x64_120-C2SGUHP4.node +0 -0
- package/dist/uws_darwin_x64_127-NHKQMMST.node +0 -0
- package/dist/uws_linux_arm64_108-YHK7ACON.node +0 -0
- package/dist/uws_linux_arm64_115-EIAAY4WO.node +0 -0
- package/dist/uws_linux_arm64_120-OADY5FIN.node +0 -0
- package/dist/uws_linux_arm64_127-U2SRLYQM.node +0 -0
- package/dist/uws_linux_arm_108-BKVITVZA.node +0 -0
- package/dist/uws_linux_arm_115-7IORQF77.node +0 -0
- package/dist/uws_linux_arm_120-LCX4ED5F.node +0 -0
- package/dist/uws_linux_arm_127-6UTO7TL6.node +0 -0
- package/dist/uws_linux_x64_108-QSNE6XWU.node +0 -0
- package/dist/uws_linux_x64_115-7AAZWMWE.node +0 -0
- package/dist/uws_linux_x64_120-AIZ6RIW2.node +0 -0
- package/dist/uws_linux_x64_127-HBA6RNSU.node +0 -0
- package/dist/uws_win32_x64_108-J6KONPDM.node +0 -0
- package/dist/uws_win32_x64_115-V5N4NHJ5.node +0 -0
- package/dist/uws_win32_x64_120-XH4MVJGN.node +0 -0
- package/dist/uws_win32_x64_127-JBAEKR4X.node +0 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var esbuild = require('esbuild');
|
|
5
|
-
var
|
|
5
|
+
var path7 = require('path');
|
|
6
6
|
var fs = require('fs');
|
|
7
|
+
var pc = require('picocolors');
|
|
8
|
+
var fs5 = require('fs/promises');
|
|
7
9
|
var child_process = require('child_process');
|
|
10
|
+
var net = require('net');
|
|
8
11
|
var enquirer = require('enquirer');
|
|
9
12
|
var execa = require('execa');
|
|
10
|
-
var fs2 = require('fs/promises');
|
|
11
|
-
var pc = require('picocolors');
|
|
12
13
|
|
|
13
14
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
15
|
|
|
@@ -31,10 +32,10 @@ function _interopNamespace(e) {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
var esbuild__namespace = /*#__PURE__*/_interopNamespace(esbuild);
|
|
34
|
-
var
|
|
35
|
+
var path7__default = /*#__PURE__*/_interopDefault(path7);
|
|
35
36
|
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
36
|
-
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
37
37
|
var pc__default = /*#__PURE__*/_interopDefault(pc);
|
|
38
|
+
var fs5__default = /*#__PURE__*/_interopDefault(fs5);
|
|
38
39
|
|
|
39
40
|
var __create = Object.create;
|
|
40
41
|
var __defProp = Object.defineProperty;
|
|
@@ -48,9 +49,21 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
48
49
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
49
50
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
50
51
|
});
|
|
51
|
-
var
|
|
52
|
+
var __glob = (map) => (path11) => {
|
|
53
|
+
var fn = map[path11];
|
|
54
|
+
if (fn) return fn();
|
|
55
|
+
throw new Error("Module not found in bundle: " + path11);
|
|
56
|
+
};
|
|
57
|
+
var __esm = (fn, res) => function __init() {
|
|
58
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
59
|
+
};
|
|
60
|
+
var __commonJS = (cb, mod) => function __require3() {
|
|
52
61
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
53
62
|
};
|
|
63
|
+
var __export = (target, all) => {
|
|
64
|
+
for (var name in all)
|
|
65
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
66
|
+
};
|
|
54
67
|
var __copyProps = (to, from, except, desc) => {
|
|
55
68
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
56
69
|
for (let key of __getOwnPropNames(from))
|
|
@@ -957,8 +970,8 @@ var require_command = __commonJS({
|
|
|
957
970
|
"node_modules/commander/lib/command.js"(exports) {
|
|
958
971
|
var EventEmitter = __require("events").EventEmitter;
|
|
959
972
|
var childProcess = __require("child_process");
|
|
960
|
-
var
|
|
961
|
-
var
|
|
973
|
+
var path11 = __require("path");
|
|
974
|
+
var fs9 = __require("fs");
|
|
962
975
|
var process2 = __require("process");
|
|
963
976
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
964
977
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1841,10 +1854,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1841
1854
|
let launchWithNode = false;
|
|
1842
1855
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1843
1856
|
function findFile(baseDir, baseName) {
|
|
1844
|
-
const localBin =
|
|
1845
|
-
if (
|
|
1846
|
-
if (sourceExt.includes(
|
|
1847
|
-
const foundExt = sourceExt.find((ext) =>
|
|
1857
|
+
const localBin = path11.resolve(baseDir, baseName);
|
|
1858
|
+
if (fs9.existsSync(localBin)) return localBin;
|
|
1859
|
+
if (sourceExt.includes(path11.extname(baseName))) return void 0;
|
|
1860
|
+
const foundExt = sourceExt.find((ext) => fs9.existsSync(`${localBin}${ext}`));
|
|
1848
1861
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1849
1862
|
return void 0;
|
|
1850
1863
|
}
|
|
@@ -1855,23 +1868,23 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1855
1868
|
if (this._scriptPath) {
|
|
1856
1869
|
let resolvedScriptPath;
|
|
1857
1870
|
try {
|
|
1858
|
-
resolvedScriptPath =
|
|
1871
|
+
resolvedScriptPath = fs9.realpathSync(this._scriptPath);
|
|
1859
1872
|
} catch (err) {
|
|
1860
1873
|
resolvedScriptPath = this._scriptPath;
|
|
1861
1874
|
}
|
|
1862
|
-
executableDir =
|
|
1875
|
+
executableDir = path11.resolve(path11.dirname(resolvedScriptPath), executableDir);
|
|
1863
1876
|
}
|
|
1864
1877
|
if (executableDir) {
|
|
1865
1878
|
let localFile = findFile(executableDir, executableFile);
|
|
1866
1879
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1867
|
-
const legacyName =
|
|
1880
|
+
const legacyName = path11.basename(this._scriptPath, path11.extname(this._scriptPath));
|
|
1868
1881
|
if (legacyName !== this._name) {
|
|
1869
1882
|
localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
|
|
1870
1883
|
}
|
|
1871
1884
|
}
|
|
1872
1885
|
executableFile = localFile || executableFile;
|
|
1873
1886
|
}
|
|
1874
|
-
launchWithNode = sourceExt.includes(
|
|
1887
|
+
launchWithNode = sourceExt.includes(path11.extname(executableFile));
|
|
1875
1888
|
let proc;
|
|
1876
1889
|
if (process2.platform !== "win32") {
|
|
1877
1890
|
if (launchWithNode) {
|
|
@@ -2660,7 +2673,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2660
2673
|
* @return {Command}
|
|
2661
2674
|
*/
|
|
2662
2675
|
nameFromFilename(filename) {
|
|
2663
|
-
this._name =
|
|
2676
|
+
this._name = path11.basename(filename, path11.extname(filename));
|
|
2664
2677
|
return this;
|
|
2665
2678
|
}
|
|
2666
2679
|
/**
|
|
@@ -2674,9 +2687,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2674
2687
|
* @param {string} [path]
|
|
2675
2688
|
* @return {(string|null|Command)}
|
|
2676
2689
|
*/
|
|
2677
|
-
executableDir(
|
|
2678
|
-
if (
|
|
2679
|
-
this._executableDir =
|
|
2690
|
+
executableDir(path12) {
|
|
2691
|
+
if (path12 === void 0) return this._executableDir;
|
|
2692
|
+
this._executableDir = path12;
|
|
2680
2693
|
return this;
|
|
2681
2694
|
}
|
|
2682
2695
|
/**
|
|
@@ -2904,6 +2917,631 @@ var require_commander = __commonJS({
|
|
|
2904
2917
|
}
|
|
2905
2918
|
});
|
|
2906
2919
|
|
|
2920
|
+
// ../../node_modules/uWebSockets.js/uws_darwin_arm64_108.node
|
|
2921
|
+
var require_uws_darwin_arm64_108 = __commonJS({
|
|
2922
|
+
"../../node_modules/uWebSockets.js/uws_darwin_arm64_108.node"(exports, module) {
|
|
2923
|
+
module.exports = "./uws_darwin_arm64_108-CLFXMYPI.node";
|
|
2924
|
+
}
|
|
2925
|
+
});
|
|
2926
|
+
|
|
2927
|
+
// ../../node_modules/uWebSockets.js/uws_darwin_arm64_115.node
|
|
2928
|
+
var require_uws_darwin_arm64_115 = __commonJS({
|
|
2929
|
+
"../../node_modules/uWebSockets.js/uws_darwin_arm64_115.node"(exports, module) {
|
|
2930
|
+
module.exports = "./uws_darwin_arm64_115-7FFEG3YF.node";
|
|
2931
|
+
}
|
|
2932
|
+
});
|
|
2933
|
+
|
|
2934
|
+
// ../../node_modules/uWebSockets.js/uws_darwin_arm64_120.node
|
|
2935
|
+
var require_uws_darwin_arm64_120 = __commonJS({
|
|
2936
|
+
"../../node_modules/uWebSockets.js/uws_darwin_arm64_120.node"(exports, module) {
|
|
2937
|
+
module.exports = "./uws_darwin_arm64_120-GFZT7CLS.node";
|
|
2938
|
+
}
|
|
2939
|
+
});
|
|
2940
|
+
|
|
2941
|
+
// ../../node_modules/uWebSockets.js/uws_darwin_arm64_127.node
|
|
2942
|
+
var require_uws_darwin_arm64_127 = __commonJS({
|
|
2943
|
+
"../../node_modules/uWebSockets.js/uws_darwin_arm64_127.node"(exports, module) {
|
|
2944
|
+
module.exports = "./uws_darwin_arm64_127-KHC2FVAM.node";
|
|
2945
|
+
}
|
|
2946
|
+
});
|
|
2947
|
+
|
|
2948
|
+
// ../../node_modules/uWebSockets.js/uws_darwin_x64_108.node
|
|
2949
|
+
var require_uws_darwin_x64_108 = __commonJS({
|
|
2950
|
+
"../../node_modules/uWebSockets.js/uws_darwin_x64_108.node"(exports, module) {
|
|
2951
|
+
module.exports = "./uws_darwin_x64_108-BRGT45AT.node";
|
|
2952
|
+
}
|
|
2953
|
+
});
|
|
2954
|
+
|
|
2955
|
+
// ../../node_modules/uWebSockets.js/uws_darwin_x64_115.node
|
|
2956
|
+
var require_uws_darwin_x64_115 = __commonJS({
|
|
2957
|
+
"../../node_modules/uWebSockets.js/uws_darwin_x64_115.node"(exports, module) {
|
|
2958
|
+
module.exports = "./uws_darwin_x64_115-4HGPQGDD.node";
|
|
2959
|
+
}
|
|
2960
|
+
});
|
|
2961
|
+
|
|
2962
|
+
// ../../node_modules/uWebSockets.js/uws_darwin_x64_120.node
|
|
2963
|
+
var require_uws_darwin_x64_120 = __commonJS({
|
|
2964
|
+
"../../node_modules/uWebSockets.js/uws_darwin_x64_120.node"(exports, module) {
|
|
2965
|
+
module.exports = "./uws_darwin_x64_120-C2SGUHP4.node";
|
|
2966
|
+
}
|
|
2967
|
+
});
|
|
2968
|
+
|
|
2969
|
+
// ../../node_modules/uWebSockets.js/uws_darwin_x64_127.node
|
|
2970
|
+
var require_uws_darwin_x64_127 = __commonJS({
|
|
2971
|
+
"../../node_modules/uWebSockets.js/uws_darwin_x64_127.node"(exports, module) {
|
|
2972
|
+
module.exports = "./uws_darwin_x64_127-NHKQMMST.node";
|
|
2973
|
+
}
|
|
2974
|
+
});
|
|
2975
|
+
|
|
2976
|
+
// ../../node_modules/uWebSockets.js/uws_linux_arm64_108.node
|
|
2977
|
+
var require_uws_linux_arm64_108 = __commonJS({
|
|
2978
|
+
"../../node_modules/uWebSockets.js/uws_linux_arm64_108.node"(exports, module) {
|
|
2979
|
+
module.exports = "./uws_linux_arm64_108-YHK7ACON.node";
|
|
2980
|
+
}
|
|
2981
|
+
});
|
|
2982
|
+
|
|
2983
|
+
// ../../node_modules/uWebSockets.js/uws_linux_arm64_115.node
|
|
2984
|
+
var require_uws_linux_arm64_115 = __commonJS({
|
|
2985
|
+
"../../node_modules/uWebSockets.js/uws_linux_arm64_115.node"(exports, module) {
|
|
2986
|
+
module.exports = "./uws_linux_arm64_115-EIAAY4WO.node";
|
|
2987
|
+
}
|
|
2988
|
+
});
|
|
2989
|
+
|
|
2990
|
+
// ../../node_modules/uWebSockets.js/uws_linux_arm64_120.node
|
|
2991
|
+
var require_uws_linux_arm64_120 = __commonJS({
|
|
2992
|
+
"../../node_modules/uWebSockets.js/uws_linux_arm64_120.node"(exports, module) {
|
|
2993
|
+
module.exports = "./uws_linux_arm64_120-OADY5FIN.node";
|
|
2994
|
+
}
|
|
2995
|
+
});
|
|
2996
|
+
|
|
2997
|
+
// ../../node_modules/uWebSockets.js/uws_linux_arm64_127.node
|
|
2998
|
+
var require_uws_linux_arm64_127 = __commonJS({
|
|
2999
|
+
"../../node_modules/uWebSockets.js/uws_linux_arm64_127.node"(exports, module) {
|
|
3000
|
+
module.exports = "./uws_linux_arm64_127-U2SRLYQM.node";
|
|
3001
|
+
}
|
|
3002
|
+
});
|
|
3003
|
+
|
|
3004
|
+
// ../../node_modules/uWebSockets.js/uws_linux_arm_108.node
|
|
3005
|
+
var require_uws_linux_arm_108 = __commonJS({
|
|
3006
|
+
"../../node_modules/uWebSockets.js/uws_linux_arm_108.node"(exports, module) {
|
|
3007
|
+
module.exports = "./uws_linux_arm_108-BKVITVZA.node";
|
|
3008
|
+
}
|
|
3009
|
+
});
|
|
3010
|
+
|
|
3011
|
+
// ../../node_modules/uWebSockets.js/uws_linux_arm_115.node
|
|
3012
|
+
var require_uws_linux_arm_115 = __commonJS({
|
|
3013
|
+
"../../node_modules/uWebSockets.js/uws_linux_arm_115.node"(exports, module) {
|
|
3014
|
+
module.exports = "./uws_linux_arm_115-7IORQF77.node";
|
|
3015
|
+
}
|
|
3016
|
+
});
|
|
3017
|
+
|
|
3018
|
+
// ../../node_modules/uWebSockets.js/uws_linux_arm_120.node
|
|
3019
|
+
var require_uws_linux_arm_120 = __commonJS({
|
|
3020
|
+
"../../node_modules/uWebSockets.js/uws_linux_arm_120.node"(exports, module) {
|
|
3021
|
+
module.exports = "./uws_linux_arm_120-LCX4ED5F.node";
|
|
3022
|
+
}
|
|
3023
|
+
});
|
|
3024
|
+
|
|
3025
|
+
// ../../node_modules/uWebSockets.js/uws_linux_arm_127.node
|
|
3026
|
+
var require_uws_linux_arm_127 = __commonJS({
|
|
3027
|
+
"../../node_modules/uWebSockets.js/uws_linux_arm_127.node"(exports, module) {
|
|
3028
|
+
module.exports = "./uws_linux_arm_127-6UTO7TL6.node";
|
|
3029
|
+
}
|
|
3030
|
+
});
|
|
3031
|
+
|
|
3032
|
+
// ../../node_modules/uWebSockets.js/uws_linux_x64_108.node
|
|
3033
|
+
var require_uws_linux_x64_108 = __commonJS({
|
|
3034
|
+
"../../node_modules/uWebSockets.js/uws_linux_x64_108.node"(exports, module) {
|
|
3035
|
+
module.exports = "./uws_linux_x64_108-QSNE6XWU.node";
|
|
3036
|
+
}
|
|
3037
|
+
});
|
|
3038
|
+
|
|
3039
|
+
// ../../node_modules/uWebSockets.js/uws_linux_x64_115.node
|
|
3040
|
+
var require_uws_linux_x64_115 = __commonJS({
|
|
3041
|
+
"../../node_modules/uWebSockets.js/uws_linux_x64_115.node"(exports, module) {
|
|
3042
|
+
module.exports = "./uws_linux_x64_115-7AAZWMWE.node";
|
|
3043
|
+
}
|
|
3044
|
+
});
|
|
3045
|
+
|
|
3046
|
+
// ../../node_modules/uWebSockets.js/uws_linux_x64_120.node
|
|
3047
|
+
var require_uws_linux_x64_120 = __commonJS({
|
|
3048
|
+
"../../node_modules/uWebSockets.js/uws_linux_x64_120.node"(exports, module) {
|
|
3049
|
+
module.exports = "./uws_linux_x64_120-AIZ6RIW2.node";
|
|
3050
|
+
}
|
|
3051
|
+
});
|
|
3052
|
+
|
|
3053
|
+
// ../../node_modules/uWebSockets.js/uws_linux_x64_127.node
|
|
3054
|
+
var require_uws_linux_x64_127 = __commonJS({
|
|
3055
|
+
"../../node_modules/uWebSockets.js/uws_linux_x64_127.node"(exports, module) {
|
|
3056
|
+
module.exports = "./uws_linux_x64_127-HBA6RNSU.node";
|
|
3057
|
+
}
|
|
3058
|
+
});
|
|
3059
|
+
|
|
3060
|
+
// ../../node_modules/uWebSockets.js/uws_win32_x64_108.node
|
|
3061
|
+
var require_uws_win32_x64_108 = __commonJS({
|
|
3062
|
+
"../../node_modules/uWebSockets.js/uws_win32_x64_108.node"(exports, module) {
|
|
3063
|
+
module.exports = "./uws_win32_x64_108-J6KONPDM.node";
|
|
3064
|
+
}
|
|
3065
|
+
});
|
|
3066
|
+
|
|
3067
|
+
// ../../node_modules/uWebSockets.js/uws_win32_x64_115.node
|
|
3068
|
+
var require_uws_win32_x64_115 = __commonJS({
|
|
3069
|
+
"../../node_modules/uWebSockets.js/uws_win32_x64_115.node"(exports, module) {
|
|
3070
|
+
module.exports = "./uws_win32_x64_115-V5N4NHJ5.node";
|
|
3071
|
+
}
|
|
3072
|
+
});
|
|
3073
|
+
|
|
3074
|
+
// ../../node_modules/uWebSockets.js/uws_win32_x64_120.node
|
|
3075
|
+
var require_uws_win32_x64_120 = __commonJS({
|
|
3076
|
+
"../../node_modules/uWebSockets.js/uws_win32_x64_120.node"(exports, module) {
|
|
3077
|
+
module.exports = "./uws_win32_x64_120-XH4MVJGN.node";
|
|
3078
|
+
}
|
|
3079
|
+
});
|
|
3080
|
+
|
|
3081
|
+
// ../../node_modules/uWebSockets.js/uws_win32_x64_127.node
|
|
3082
|
+
var require_uws_win32_x64_127 = __commonJS({
|
|
3083
|
+
"../../node_modules/uWebSockets.js/uws_win32_x64_127.node"(exports, module) {
|
|
3084
|
+
module.exports = "./uws_win32_x64_127-JBAEKR4X.node";
|
|
3085
|
+
}
|
|
3086
|
+
});
|
|
3087
|
+
|
|
3088
|
+
// require("./uws_*_*_*.node") in ../../node_modules/uWebSockets.js/uws.js
|
|
3089
|
+
var globRequire_uws______node;
|
|
3090
|
+
var init_ = __esm({
|
|
3091
|
+
'require("./uws_*_*_*.node") in ../../node_modules/uWebSockets.js/uws.js'() {
|
|
3092
|
+
globRequire_uws______node = __glob({
|
|
3093
|
+
"./uws_darwin_arm64_108.node": () => require_uws_darwin_arm64_108(),
|
|
3094
|
+
"./uws_darwin_arm64_115.node": () => require_uws_darwin_arm64_115(),
|
|
3095
|
+
"./uws_darwin_arm64_120.node": () => require_uws_darwin_arm64_120(),
|
|
3096
|
+
"./uws_darwin_arm64_127.node": () => require_uws_darwin_arm64_127(),
|
|
3097
|
+
"./uws_darwin_x64_108.node": () => require_uws_darwin_x64_108(),
|
|
3098
|
+
"./uws_darwin_x64_115.node": () => require_uws_darwin_x64_115(),
|
|
3099
|
+
"./uws_darwin_x64_120.node": () => require_uws_darwin_x64_120(),
|
|
3100
|
+
"./uws_darwin_x64_127.node": () => require_uws_darwin_x64_127(),
|
|
3101
|
+
"./uws_linux_arm64_108.node": () => require_uws_linux_arm64_108(),
|
|
3102
|
+
"./uws_linux_arm64_115.node": () => require_uws_linux_arm64_115(),
|
|
3103
|
+
"./uws_linux_arm64_120.node": () => require_uws_linux_arm64_120(),
|
|
3104
|
+
"./uws_linux_arm64_127.node": () => require_uws_linux_arm64_127(),
|
|
3105
|
+
"./uws_linux_arm_108.node": () => require_uws_linux_arm_108(),
|
|
3106
|
+
"./uws_linux_arm_115.node": () => require_uws_linux_arm_115(),
|
|
3107
|
+
"./uws_linux_arm_120.node": () => require_uws_linux_arm_120(),
|
|
3108
|
+
"./uws_linux_arm_127.node": () => require_uws_linux_arm_127(),
|
|
3109
|
+
"./uws_linux_x64_108.node": () => require_uws_linux_x64_108(),
|
|
3110
|
+
"./uws_linux_x64_115.node": () => require_uws_linux_x64_115(),
|
|
3111
|
+
"./uws_linux_x64_120.node": () => require_uws_linux_x64_120(),
|
|
3112
|
+
"./uws_linux_x64_127.node": () => require_uws_linux_x64_127(),
|
|
3113
|
+
"./uws_win32_x64_108.node": () => require_uws_win32_x64_108(),
|
|
3114
|
+
"./uws_win32_x64_115.node": () => require_uws_win32_x64_115(),
|
|
3115
|
+
"./uws_win32_x64_120.node": () => require_uws_win32_x64_120(),
|
|
3116
|
+
"./uws_win32_x64_127.node": () => require_uws_win32_x64_127()
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
});
|
|
3120
|
+
|
|
3121
|
+
// ../../node_modules/uWebSockets.js/uws.js
|
|
3122
|
+
var require_uws = __commonJS({
|
|
3123
|
+
"../../node_modules/uWebSockets.js/uws.js"(exports, module) {
|
|
3124
|
+
init_();
|
|
3125
|
+
module.exports = (() => {
|
|
3126
|
+
try {
|
|
3127
|
+
return globRequire_uws______node("./uws_" + process.platform + "_" + process.arch + "_" + process.versions.modules + ".node");
|
|
3128
|
+
} catch (e) {
|
|
3129
|
+
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());
|
|
3130
|
+
}
|
|
3131
|
+
})();
|
|
3132
|
+
module.exports.DeclarativeResponse = class DeclarativeResponse {
|
|
3133
|
+
constructor() {
|
|
3134
|
+
this.instructions = [];
|
|
3135
|
+
}
|
|
3136
|
+
// Utility method to encode text and append instruction
|
|
3137
|
+
_appendInstruction(opcode, ...text) {
|
|
3138
|
+
this.instructions.push(opcode);
|
|
3139
|
+
text.forEach((str) => {
|
|
3140
|
+
const bytes = typeof str === "string" ? new TextEncoder().encode(str) : str;
|
|
3141
|
+
this.instructions.push(bytes.length, ...bytes);
|
|
3142
|
+
});
|
|
3143
|
+
}
|
|
3144
|
+
// Utility method to append 2-byte length text in little-endian format
|
|
3145
|
+
_appendInstructionWithLength(opcode, text) {
|
|
3146
|
+
this.instructions.push(opcode);
|
|
3147
|
+
const bytes = new TextEncoder().encode(text);
|
|
3148
|
+
const length = bytes.length;
|
|
3149
|
+
this.instructions.push(length & 255, length >> 8 & 255, ...bytes);
|
|
3150
|
+
}
|
|
3151
|
+
writeHeader(key, value) {
|
|
3152
|
+
return this._appendInstruction(1, key, value), this;
|
|
3153
|
+
}
|
|
3154
|
+
writeBody() {
|
|
3155
|
+
return this.instructions.push(2), this;
|
|
3156
|
+
}
|
|
3157
|
+
writeQueryValue(key) {
|
|
3158
|
+
return this._appendInstruction(3, key), this;
|
|
3159
|
+
}
|
|
3160
|
+
writeHeaderValue(key) {
|
|
3161
|
+
return this._appendInstruction(4, key), this;
|
|
3162
|
+
}
|
|
3163
|
+
write(value) {
|
|
3164
|
+
return this._appendInstructionWithLength(5, value), this;
|
|
3165
|
+
}
|
|
3166
|
+
writeParameterValue(key) {
|
|
3167
|
+
return this._appendInstruction(6, key), this;
|
|
3168
|
+
}
|
|
3169
|
+
end(value) {
|
|
3170
|
+
const bytes = new TextEncoder().encode(value);
|
|
3171
|
+
const length = bytes.length;
|
|
3172
|
+
this.instructions.push(0, length & 255, length >> 8 & 255, ...bytes);
|
|
3173
|
+
return new Uint8Array(this.instructions).buffer;
|
|
3174
|
+
}
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
});
|
|
3178
|
+
|
|
3179
|
+
// ../openapi/dist/index.mjs
|
|
3180
|
+
var dist_exports = {};
|
|
3181
|
+
__export(dist_exports, {
|
|
3182
|
+
OpenApiGenerator: () => OpenApiGenerator,
|
|
3183
|
+
Security: () => Security,
|
|
3184
|
+
defineSecuritySchemes: () => defineSecuritySchemes,
|
|
3185
|
+
useOpenAPI: () => useOpenAPI
|
|
3186
|
+
});
|
|
3187
|
+
function zodSchemaToOpenApi(schema) {
|
|
3188
|
+
const s = schema;
|
|
3189
|
+
if (typeof s.toJSONSchema === "function") {
|
|
3190
|
+
const full = s.toJSONSchema({ target: "openApi3_1" });
|
|
3191
|
+
const { $schema: _dropped, ...rest } = full;
|
|
3192
|
+
return rest;
|
|
3193
|
+
}
|
|
3194
|
+
try {
|
|
3195
|
+
const { zodToJsonSchema } = __require2("zod-to-json-schema");
|
|
3196
|
+
return zodToJsonSchema(schema, { target: "openApi3" });
|
|
3197
|
+
} catch {
|
|
3198
|
+
return { type: "object" };
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
function isZodSchema(value) {
|
|
3202
|
+
return typeof value === "object" && value !== null && typeof value.safeParse === "function";
|
|
3203
|
+
}
|
|
3204
|
+
function escapeHtml(s) {
|
|
3205
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3206
|
+
}
|
|
3207
|
+
function escapeJsString(s) {
|
|
3208
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
3209
|
+
}
|
|
3210
|
+
function defineSecuritySchemes(schemes) {
|
|
3211
|
+
return {
|
|
3212
|
+
schemes,
|
|
3213
|
+
require: (name, scopes = []) => [{ [name]: scopes }],
|
|
3214
|
+
requireMultiple: (requirements) => {
|
|
3215
|
+
const combined = {};
|
|
3216
|
+
requirements.forEach((req) => {
|
|
3217
|
+
combined[req] = [];
|
|
3218
|
+
});
|
|
3219
|
+
return [combined];
|
|
3220
|
+
}
|
|
3221
|
+
};
|
|
3222
|
+
}
|
|
3223
|
+
function inferSchemaFromPayload(data, depth = 0) {
|
|
3224
|
+
if (depth > 32) return { type: "object" };
|
|
3225
|
+
if (data === null) return { type: "null" };
|
|
3226
|
+
if (Array.isArray(data)) {
|
|
3227
|
+
return {
|
|
3228
|
+
type: "array",
|
|
3229
|
+
items: data.length > 0 ? inferSchemaFromPayload(data[0], depth + 1) : { type: "object" }
|
|
3230
|
+
};
|
|
3231
|
+
}
|
|
3232
|
+
if (typeof data === "object") {
|
|
3233
|
+
const properties = {};
|
|
3234
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3235
|
+
properties[key] = inferSchemaFromPayload(value, depth + 1);
|
|
3236
|
+
}
|
|
3237
|
+
return { type: "object", properties };
|
|
3238
|
+
}
|
|
3239
|
+
return { type: typeof data };
|
|
3240
|
+
}
|
|
3241
|
+
function useOpenAPI(app, options) {
|
|
3242
|
+
const rawPrefix = options.prefix ?? "/docs";
|
|
3243
|
+
const normalizedPrefix = rawPrefix.startsWith("/") ? rawPrefix : `/${rawPrefix}`;
|
|
3244
|
+
const prefix = normalizedPrefix === "/" ? "/" : normalizedPrefix.endsWith("/") ? normalizedPrefix.slice(0, -1) : normalizedPrefix;
|
|
3245
|
+
const docsPaths = prefix === "/" ? ["/"] : [prefix, `${prefix}/`];
|
|
3246
|
+
const docsPathSet = new Set(docsPaths);
|
|
3247
|
+
const specPath = prefix === "/" ? "/openapi.json" : `${prefix}/openapi.json`;
|
|
3248
|
+
const generator = new OpenApiGenerator(app, options);
|
|
3249
|
+
let cachedSpec = null;
|
|
3250
|
+
let cachedSpecJson = null;
|
|
3251
|
+
let emittedPublicDocsWarning = false;
|
|
3252
|
+
if (options.autoInferResponses) {
|
|
3253
|
+
app.addHook("onPostHandler", (req, res, match) => {
|
|
3254
|
+
if (!match?.route) return;
|
|
3255
|
+
if (req.path === specPath || docsPathSet.has(req.path)) {
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
const payload = res.payload;
|
|
3259
|
+
if (payload === void 0) return;
|
|
3260
|
+
if (!cachedSpec) cachedSpec = generator.generate();
|
|
3261
|
+
const path11 = generator.formatPath(match.route.path);
|
|
3262
|
+
const method = match.route.method.toLowerCase();
|
|
3263
|
+
const statusCode = String(res.statusCode);
|
|
3264
|
+
const existingResponse = cachedSpec.paths[path11]?.[method]?.responses?.[statusCode];
|
|
3265
|
+
const isDefault = existingResponse?.description === "Successful response" && existingResponse?.content?.["application/json"]?.schema?.type === "object";
|
|
3266
|
+
if (existingResponse && !isDefault) return;
|
|
3267
|
+
let parsedData = payload;
|
|
3268
|
+
if (typeof payload === "string") {
|
|
3269
|
+
try {
|
|
3270
|
+
parsedData = JSON.parse(payload);
|
|
3271
|
+
} catch {
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
if (cachedSpec.paths[path11]?.[method]) {
|
|
3275
|
+
cachedSpec.paths[path11][method].responses[statusCode] = {
|
|
3276
|
+
description: "Auto-inferred response",
|
|
3277
|
+
content: {
|
|
3278
|
+
"application/json": { schema: inferSchemaFromPayload(parsedData) }
|
|
3279
|
+
}
|
|
3280
|
+
};
|
|
3281
|
+
cachedSpecJson = null;
|
|
3282
|
+
}
|
|
3283
|
+
});
|
|
3284
|
+
}
|
|
3285
|
+
const guard = async (req) => {
|
|
3286
|
+
if (!options.protect) {
|
|
3287
|
+
if (process.env.NODE_ENV === "production") {
|
|
3288
|
+
if (!emittedPublicDocsWarning) {
|
|
3289
|
+
emittedPublicDocsWarning = true;
|
|
3290
|
+
console.warn(
|
|
3291
|
+
"[axiomify/openapi] OpenAPI endpoints are not protected. Production access is denied by default. Provide a `protect` function or set `allowPublicInProduction: true` explicitly."
|
|
3292
|
+
);
|
|
3293
|
+
}
|
|
3294
|
+
return options.allowPublicInProduction === true;
|
|
3295
|
+
}
|
|
3296
|
+
return true;
|
|
3297
|
+
}
|
|
3298
|
+
return Boolean(await options.protect(req));
|
|
3299
|
+
};
|
|
3300
|
+
app.route({
|
|
3301
|
+
method: "GET",
|
|
3302
|
+
path: specPath,
|
|
3303
|
+
handler: async (req, res) => {
|
|
3304
|
+
if (!await guard(req)) return res.status(403).send(null, "Forbidden");
|
|
3305
|
+
if (!cachedSpec) cachedSpec = generator.generate();
|
|
3306
|
+
if (!cachedSpecJson) cachedSpecJson = JSON.stringify(cachedSpec);
|
|
3307
|
+
res.status(200).sendRaw(cachedSpecJson, "application/json");
|
|
3308
|
+
}
|
|
3309
|
+
});
|
|
3310
|
+
const docsHandler = async (req, res) => {
|
|
3311
|
+
if (!await guard(req)) return res.status(403).send(null, "Forbidden");
|
|
3312
|
+
if (typeof res.setHeader === "function") {
|
|
3313
|
+
res.setHeader("Content-Security-Policy", DOCS_CSP);
|
|
3314
|
+
} else if (typeof res.header === "function") {
|
|
3315
|
+
res.header("Content-Security-Policy", DOCS_CSP);
|
|
3316
|
+
}
|
|
3317
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
3318
|
+
const specUrl = isDev ? `${specPath}?t=${Date.now()}` : specPath;
|
|
3319
|
+
const html = `<!DOCTYPE html>
|
|
3320
|
+
<html lang="en">
|
|
3321
|
+
<head>
|
|
3322
|
+
<meta charset="utf-8" />
|
|
3323
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
3324
|
+
<title>${escapeHtml(options.info.title)} - API Docs</title>
|
|
3325
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.11.0/swagger-ui.min.css" integrity="sha384-bIuUyBV7i6P7z/kPAs1oeBIf8PMIqVkPVDzzaOL+QH7kWmvCT9HDTWwGVs0L4/9Q" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
|
3326
|
+
</head>
|
|
3327
|
+
<body style="margin: 0; padding: 0;">
|
|
3328
|
+
<div id="swagger-ui"></div>
|
|
3329
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.11.0/swagger-ui-bundle.min.js" integrity="sha384-XHDYRdiHvBq7oL4CtkiJKfdVVA5PydxYtssHVtRrvPlha1m+zz8kboiyx/MAsyl3" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
3330
|
+
<script>
|
|
3331
|
+
window.onload = () => {
|
|
3332
|
+
window.ui = SwaggerUIBundle({
|
|
3333
|
+
url: '${escapeJsString(specUrl)}',
|
|
3334
|
+
dom_id: '#swagger-ui',
|
|
3335
|
+
});
|
|
3336
|
+
};
|
|
3337
|
+
</script>
|
|
3338
|
+
</body>
|
|
3339
|
+
</html>`;
|
|
3340
|
+
res.status(200).sendRaw(html, "text/html");
|
|
3341
|
+
};
|
|
3342
|
+
app.route({
|
|
3343
|
+
method: "GET",
|
|
3344
|
+
path: prefix,
|
|
3345
|
+
handler: docsHandler
|
|
3346
|
+
});
|
|
3347
|
+
}
|
|
3348
|
+
var __require2, OpenApiGenerator, DOCS_CSP, Security;
|
|
3349
|
+
var init_dist = __esm({
|
|
3350
|
+
"../openapi/dist/index.mjs"() {
|
|
3351
|
+
__require2 = /* @__PURE__ */ ((x) => typeof __require !== "undefined" ? __require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3352
|
+
get: (a, b) => (typeof __require !== "undefined" ? __require : a)[b]
|
|
3353
|
+
}) : x)(function(x) {
|
|
3354
|
+
if (typeof __require !== "undefined") return __require.apply(this, arguments);
|
|
3355
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
3356
|
+
});
|
|
3357
|
+
OpenApiGenerator = class {
|
|
3358
|
+
constructor(app, options) {
|
|
3359
|
+
this.app = app;
|
|
3360
|
+
this.options = options;
|
|
3361
|
+
}
|
|
3362
|
+
app;
|
|
3363
|
+
options;
|
|
3364
|
+
generate() {
|
|
3365
|
+
const spec = {
|
|
3366
|
+
openapi: "3.0.3",
|
|
3367
|
+
info: this.options.info,
|
|
3368
|
+
paths: {}
|
|
3369
|
+
};
|
|
3370
|
+
if (this.options.components) spec.components = this.options.components;
|
|
3371
|
+
if (this.options.security) spec.security = this.options.security;
|
|
3372
|
+
for (const route of this.app.registeredRoutes) {
|
|
3373
|
+
const openApiPath = this.formatPath(route.path);
|
|
3374
|
+
const method = route.method.toLowerCase();
|
|
3375
|
+
const paths = spec.paths;
|
|
3376
|
+
if (!paths[openApiPath]) paths[openApiPath] = {};
|
|
3377
|
+
const op = route.openapi ?? void 0;
|
|
3378
|
+
const operation = {
|
|
3379
|
+
// OAS §4.7.10.2 — summary. Default to `${method} ${path}` so the
|
|
3380
|
+
// docs UI always has a human-readable title even without user input.
|
|
3381
|
+
summary: op?.summary ?? `${route.method} ${route.path}`,
|
|
3382
|
+
// OAS §4.7.10.5 — operationId. Client codegen tools
|
|
3383
|
+
// (openapi-typescript, openapi-generator) use this to name the
|
|
3384
|
+
// generated function. When the user doesn't supply one we
|
|
3385
|
+
// synthesise a stable name from method+path:
|
|
3386
|
+
// GET /users/:id → "getUsersById"
|
|
3387
|
+
// POST /users → "postUsers"
|
|
3388
|
+
// Determinism matters here — codegen output should not drift
|
|
3389
|
+
// between releases unless method+path actually change.
|
|
3390
|
+
operationId: op?.operationId ?? this.synthesiseOperationId(route.method, route.path),
|
|
3391
|
+
parameters: this.extractParameters(route),
|
|
3392
|
+
responses: this.extractResponses(route)
|
|
3393
|
+
};
|
|
3394
|
+
const legacySchema = route.schema ?? {};
|
|
3395
|
+
const description = op?.description ?? legacySchema.description;
|
|
3396
|
+
const tags = op?.tags ?? legacySchema.tags;
|
|
3397
|
+
const security = op?.security ?? legacySchema.security;
|
|
3398
|
+
if (description) operation.description = description;
|
|
3399
|
+
if (tags) operation.tags = tags;
|
|
3400
|
+
if (security !== void 0) operation.security = security;
|
|
3401
|
+
if (op?.deprecated) operation.deprecated = true;
|
|
3402
|
+
if (op?.externalDocs) operation.externalDocs = op.externalDocs;
|
|
3403
|
+
if (op?.servers) operation.servers = op.servers;
|
|
3404
|
+
if (op?.callbacks) operation.callbacks = op.callbacks;
|
|
3405
|
+
const body = this.extractBody(route);
|
|
3406
|
+
if (body) {
|
|
3407
|
+
if (op?.requestBodyDescription) {
|
|
3408
|
+
body.description = op.requestBodyDescription;
|
|
3409
|
+
}
|
|
3410
|
+
operation.requestBody = body;
|
|
3411
|
+
}
|
|
3412
|
+
paths[openApiPath][method] = operation;
|
|
3413
|
+
}
|
|
3414
|
+
return spec;
|
|
3415
|
+
}
|
|
3416
|
+
/** Translates Axiomify path syntax to OpenAPI: `/users/:id` → `/users/{id}` */
|
|
3417
|
+
formatPath(path11) {
|
|
3418
|
+
return path11.replace(/:([a-zA-Z0-9_]+)/g, "{$1}");
|
|
3419
|
+
}
|
|
3420
|
+
extractParameters(route) {
|
|
3421
|
+
const parameters = [];
|
|
3422
|
+
if (route.schema?.params) {
|
|
3423
|
+
const paramSchema = zodSchemaToOpenApi(route.schema.params);
|
|
3424
|
+
const properties = paramSchema.properties ?? {};
|
|
3425
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
3426
|
+
parameters.push({ name: key, in: "path", required: true, schema: prop });
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
if (route.schema?.query) {
|
|
3430
|
+
const querySchema = zodSchemaToOpenApi(route.schema.query);
|
|
3431
|
+
const properties = querySchema.properties ?? {};
|
|
3432
|
+
const required = querySchema.required ?? [];
|
|
3433
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
3434
|
+
parameters.push({
|
|
3435
|
+
name: key,
|
|
3436
|
+
in: "query",
|
|
3437
|
+
required: required.includes(key),
|
|
3438
|
+
schema: prop
|
|
3439
|
+
});
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
return parameters;
|
|
3443
|
+
}
|
|
3444
|
+
/**
|
|
3445
|
+
* Synthesise a stable, codegen-friendly operationId from method+path
|
|
3446
|
+
* when the route definition doesn't supply one. Example outputs:
|
|
3447
|
+
* GET /users/:id → getUsersById
|
|
3448
|
+
* POST /users → postUsers
|
|
3449
|
+
* GET /users/:id/posts/:pid → getUsersByIdPostsByPid
|
|
3450
|
+
*
|
|
3451
|
+
* Determinism matters here — client codegen produces the same function
|
|
3452
|
+
* names on every run as long as method+path are stable.
|
|
3453
|
+
*/
|
|
3454
|
+
synthesiseOperationId(method, path11) {
|
|
3455
|
+
const verb = method.toLowerCase();
|
|
3456
|
+
const parts = [];
|
|
3457
|
+
for (const seg of path11.split("/")) {
|
|
3458
|
+
if (!seg) continue;
|
|
3459
|
+
if (seg.startsWith(":")) {
|
|
3460
|
+
const name = seg.slice(1);
|
|
3461
|
+
parts.push("By", name.charAt(0).toUpperCase() + name.slice(1));
|
|
3462
|
+
} else if (seg === "*") {
|
|
3463
|
+
parts.push("All");
|
|
3464
|
+
} else {
|
|
3465
|
+
parts.push(seg.charAt(0).toUpperCase() + seg.slice(1));
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
return verb + parts.join("");
|
|
3469
|
+
}
|
|
3470
|
+
extractBody(route) {
|
|
3471
|
+
if (!route.schema?.body && !route.schema?.files) return void 0;
|
|
3472
|
+
const hasFiles = !!route.schema.files;
|
|
3473
|
+
const contentType = hasFiles ? "multipart/form-data" : "application/json";
|
|
3474
|
+
let finalSchema = { type: "object", properties: {} };
|
|
3475
|
+
if (route.schema.body) {
|
|
3476
|
+
const bodySchema = zodSchemaToOpenApi(route.schema.body);
|
|
3477
|
+
if (bodySchema.type === "object") {
|
|
3478
|
+
finalSchema.properties = { ...bodySchema.properties };
|
|
3479
|
+
if (bodySchema.required) finalSchema.required = bodySchema.required;
|
|
3480
|
+
if (bodySchema.additionalProperties !== void 0) {
|
|
3481
|
+
finalSchema.additionalProperties = bodySchema.additionalProperties;
|
|
3482
|
+
}
|
|
3483
|
+
} else {
|
|
3484
|
+
finalSchema = hasFiles ? { type: "object", properties: { payload: bodySchema } } : bodySchema;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
if (hasFiles) {
|
|
3488
|
+
const files = route.schema.files;
|
|
3489
|
+
const props = finalSchema.properties ?? {};
|
|
3490
|
+
for (const [fieldName, config] of Object.entries(files)) {
|
|
3491
|
+
props[fieldName] = {
|
|
3492
|
+
type: "string",
|
|
3493
|
+
format: "binary",
|
|
3494
|
+
...config.description ? { description: config.description } : {},
|
|
3495
|
+
...config.maxSize ? { description: `Max size: ${config.maxSize} bytes` } : {}
|
|
3496
|
+
};
|
|
3497
|
+
}
|
|
3498
|
+
finalSchema.properties = props;
|
|
3499
|
+
}
|
|
3500
|
+
return { required: true, content: { [contentType]: { schema: finalSchema } } };
|
|
3501
|
+
}
|
|
3502
|
+
extractResponses(route) {
|
|
3503
|
+
const op = route.openapi;
|
|
3504
|
+
const descriptions = op?.responseDescriptions ?? {};
|
|
3505
|
+
const defaultResponse = {
|
|
3506
|
+
"200": {
|
|
3507
|
+
description: descriptions["200"] ?? "Successful response",
|
|
3508
|
+
content: { "application/json": { schema: { type: "object" } } }
|
|
3509
|
+
}
|
|
3510
|
+
};
|
|
3511
|
+
if (!route.schema?.response) return defaultResponse;
|
|
3512
|
+
const responseSchema = route.schema.response;
|
|
3513
|
+
const responses = {};
|
|
3514
|
+
if (isZodSchema(responseSchema)) {
|
|
3515
|
+
responses["200"] = {
|
|
3516
|
+
description: descriptions["200"] ?? "Successful response",
|
|
3517
|
+
content: {
|
|
3518
|
+
"application/json": { schema: zodSchemaToOpenApi(responseSchema) }
|
|
3519
|
+
}
|
|
3520
|
+
};
|
|
3521
|
+
} else if (typeof responseSchema === "object" && responseSchema !== null) {
|
|
3522
|
+
for (const [code, schema] of Object.entries(
|
|
3523
|
+
responseSchema
|
|
3524
|
+
)) {
|
|
3525
|
+
responses[code] = {
|
|
3526
|
+
description: descriptions[code] ?? `Response ${code}`,
|
|
3527
|
+
content: {
|
|
3528
|
+
"application/json": { schema: zodSchemaToOpenApi(schema) }
|
|
3529
|
+
}
|
|
3530
|
+
};
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
return Object.keys(responses).length > 0 ? responses : defaultResponse;
|
|
3534
|
+
}
|
|
3535
|
+
};
|
|
3536
|
+
DOCS_CSP = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: https://validator.swagger.io; worker-src 'self' blob:;";
|
|
3537
|
+
Security = defineSecuritySchemes({
|
|
3538
|
+
bearerAuth: { type: "http", scheme: "bearer" },
|
|
3539
|
+
apiKey: { type: "apiKey", in: "header", name: "X-API-KEY" },
|
|
3540
|
+
basicAuth: { type: "http", scheme: "basic" }
|
|
3541
|
+
});
|
|
3542
|
+
}
|
|
3543
|
+
});
|
|
3544
|
+
|
|
2907
3545
|
// node_modules/commander/esm.mjs
|
|
2908
3546
|
var import_index = __toESM(require_commander());
|
|
2909
3547
|
var {
|
|
@@ -2923,12 +3561,12 @@ var {
|
|
|
2923
3561
|
|
|
2924
3562
|
// package.json
|
|
2925
3563
|
var package_default = {
|
|
2926
|
-
version: "
|
|
3564
|
+
version: "6.0.0-rc.2"};
|
|
2927
3565
|
var ALWAYS_EXTERNAL = ["uWebSockets.js"];
|
|
2928
3566
|
function getUserExternals(cwd) {
|
|
2929
3567
|
let pkgExternals = [];
|
|
2930
3568
|
try {
|
|
2931
|
-
const pkgPath =
|
|
3569
|
+
const pkgPath = path7__default.default.join(cwd, "package.json");
|
|
2932
3570
|
if (fs__default.default.existsSync(pkgPath)) {
|
|
2933
3571
|
const pkg = JSON.parse(fs__default.default.readFileSync(pkgPath, "utf8"));
|
|
2934
3572
|
const deps = Object.keys(pkg.dependencies || {});
|
|
@@ -2946,8 +3584,8 @@ function getUserExternals(cwd) {
|
|
|
2946
3584
|
|
|
2947
3585
|
// src/commands/build.ts
|
|
2948
3586
|
async function buildProject(entry) {
|
|
2949
|
-
const entryPath =
|
|
2950
|
-
const outPath =
|
|
3587
|
+
const entryPath = path7__default.default.resolve(process.cwd(), entry);
|
|
3588
|
+
const outPath = path7__default.default.resolve(process.cwd(), "dist/index.js");
|
|
2951
3589
|
const userExternals = getUserExternals(process.cwd());
|
|
2952
3590
|
console.log(`\u{1F528} Building production bundle from ${entry}...`);
|
|
2953
3591
|
try {
|
|
@@ -2967,9 +3605,379 @@ async function buildProject(entry) {
|
|
|
2967
3605
|
process.exit(1);
|
|
2968
3606
|
}
|
|
2969
3607
|
}
|
|
3608
|
+
function colourMethod(method) {
|
|
3609
|
+
const m = method.toUpperCase();
|
|
3610
|
+
const padded = m.padEnd(7);
|
|
3611
|
+
switch (m) {
|
|
3612
|
+
case "GET":
|
|
3613
|
+
return pc__default.default.bold(pc__default.default.green(padded));
|
|
3614
|
+
case "POST":
|
|
3615
|
+
return pc__default.default.bold(pc__default.default.blue(padded));
|
|
3616
|
+
case "PUT":
|
|
3617
|
+
return pc__default.default.bold(pc__default.default.yellow(padded));
|
|
3618
|
+
case "PATCH":
|
|
3619
|
+
return pc__default.default.bold(pc__default.default.magenta(padded));
|
|
3620
|
+
case "DELETE":
|
|
3621
|
+
return pc__default.default.bold(pc__default.default.red(padded));
|
|
3622
|
+
case "HEAD":
|
|
3623
|
+
return pc__default.default.dim(padded);
|
|
3624
|
+
case "OPTIONS":
|
|
3625
|
+
return pc__default.default.dim(padded);
|
|
3626
|
+
case "WS":
|
|
3627
|
+
return pc__default.default.bold(pc__default.default.cyan(padded));
|
|
3628
|
+
default:
|
|
3629
|
+
return padded;
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
var badge = {
|
|
3633
|
+
validation(label) {
|
|
3634
|
+
return pc__default.default.cyan(label);
|
|
3635
|
+
},
|
|
3636
|
+
deprecated() {
|
|
3637
|
+
return pc__default.default.bold(pc__default.default.red("\u2298 DEPRECATED"));
|
|
3638
|
+
},
|
|
3639
|
+
timeout(ms) {
|
|
3640
|
+
return pc__default.default.dim(`${ms}ms`);
|
|
3641
|
+
},
|
|
3642
|
+
tags(tags) {
|
|
3643
|
+
if (!tags.length) return "";
|
|
3644
|
+
return tags.map((t) => pc__default.default.dim(`#${t}`)).join(" ");
|
|
3645
|
+
}
|
|
3646
|
+
};
|
|
3647
|
+
function visibleLength(s) {
|
|
3648
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
3649
|
+
}
|
|
3650
|
+
function pad(s, width, align) {
|
|
3651
|
+
const v = visibleLength(s);
|
|
3652
|
+
if (v >= width) return s;
|
|
3653
|
+
const filler = " ".repeat(width - v);
|
|
3654
|
+
return align === "right" ? filler + s : s + filler;
|
|
3655
|
+
}
|
|
3656
|
+
function truncate(s, max) {
|
|
3657
|
+
if (visibleLength(s) <= max) return s;
|
|
3658
|
+
const stripped = s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
3659
|
+
return stripped.slice(0, max - 1) + pc__default.default.dim("\u2026");
|
|
3660
|
+
}
|
|
3661
|
+
function renderTable(columns, rows, terminalWidth = process.stdout.columns || 100) {
|
|
3662
|
+
const widths = columns.map((col, i) => {
|
|
3663
|
+
const headerW = visibleLength(col.header);
|
|
3664
|
+
const cellW = Math.max(
|
|
3665
|
+
headerW,
|
|
3666
|
+
...rows.map((r) => visibleLength(r[i] ?? ""))
|
|
3667
|
+
);
|
|
3668
|
+
let w = Math.max(headerW, cellW);
|
|
3669
|
+
if (col.minWidth) w = Math.max(w, col.minWidth);
|
|
3670
|
+
if (col.maxWidth) w = Math.min(w, col.maxWidth);
|
|
3671
|
+
return w;
|
|
3672
|
+
});
|
|
3673
|
+
const overheadPerSep = 3;
|
|
3674
|
+
const overhead = (columns.length + 1) * 2 + overheadPerSep * (columns.length - 1);
|
|
3675
|
+
let total = widths.reduce((a, b) => a + b, 0) + overhead;
|
|
3676
|
+
if (total > terminalWidth) {
|
|
3677
|
+
const flexIdx = columns.map((c, i) => ({ c, i })).filter(({ c }) => !c.minWidth).map(({ i }) => i);
|
|
3678
|
+
let excess = total - terminalWidth;
|
|
3679
|
+
for (let pass = 0; pass < 8 && excess > 0; pass++) {
|
|
3680
|
+
for (const i of flexIdx) {
|
|
3681
|
+
if (excess <= 0) break;
|
|
3682
|
+
if (widths[i] > 8) {
|
|
3683
|
+
widths[i]--;
|
|
3684
|
+
excess--;
|
|
3685
|
+
total--;
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
const bar = (l, m, r) => l + widths.map((w) => "\u2500".repeat(w + 2)).join(m) + r;
|
|
3691
|
+
const lines = [];
|
|
3692
|
+
lines.push(pc__default.default.dim(bar("\u250C", "\u252C", "\u2510")));
|
|
3693
|
+
lines.push(
|
|
3694
|
+
pc__default.default.dim("\u2502 ") + columns.map((c, i) => pc__default.default.bold(pad(c.header, widths[i], c.align ?? "left"))).join(pc__default.default.dim(" \u2502 ")) + pc__default.default.dim(" \u2502")
|
|
3695
|
+
);
|
|
3696
|
+
lines.push(pc__default.default.dim(bar("\u251C", "\u253C", "\u2524")));
|
|
3697
|
+
for (const row of rows) {
|
|
3698
|
+
lines.push(
|
|
3699
|
+
pc__default.default.dim("\u2502 ") + columns.map((c, i) => {
|
|
3700
|
+
const cell = row[i] ?? "";
|
|
3701
|
+
const truncated = c.maxWidth ? truncate(cell, widths[i]) : cell;
|
|
3702
|
+
return pad(truncated, widths[i], c.align ?? "left");
|
|
3703
|
+
}).join(pc__default.default.dim(" \u2502 ")) + pc__default.default.dim(" \u2502")
|
|
3704
|
+
);
|
|
3705
|
+
}
|
|
3706
|
+
lines.push(pc__default.default.dim(bar("\u2514", "\u2534", "\u2518")));
|
|
3707
|
+
return lines.join("\n");
|
|
3708
|
+
}
|
|
3709
|
+
function pluralise(n, singular, plural) {
|
|
3710
|
+
return n === 1 ? `${n} ${singular}` : `${n} ${plural ?? singular + "s"}`;
|
|
3711
|
+
}
|
|
3712
|
+
var symbols = {
|
|
3713
|
+
ok: pc__default.default.green("\u2713"),
|
|
3714
|
+
warn: pc__default.default.yellow("\u26A0"),
|
|
3715
|
+
fail: pc__default.default.red("\u2717"),
|
|
3716
|
+
info: pc__default.default.cyan("\u2139"),
|
|
3717
|
+
bullet: pc__default.default.dim("\u2022"),
|
|
3718
|
+
arrow: pc__default.default.dim("\u2192")
|
|
3719
|
+
};
|
|
3720
|
+
async function loadApp(entry) {
|
|
3721
|
+
const entryPath = path7__default.default.resolve(process.cwd(), entry);
|
|
3722
|
+
const tempDir = path7__default.default.resolve(process.cwd(), ".axiomify");
|
|
3723
|
+
const tempPath = path7__default.default.join(tempDir, "inspect.cjs");
|
|
3724
|
+
const userExternals = getUserExternals(process.cwd());
|
|
3725
|
+
await esbuild__namespace.build({
|
|
3726
|
+
entryPoints: [entryPath],
|
|
3727
|
+
bundle: true,
|
|
3728
|
+
platform: "node",
|
|
3729
|
+
format: "cjs",
|
|
3730
|
+
outfile: tempPath,
|
|
3731
|
+
external: [.../* @__PURE__ */ new Set([...userExternals, "node:*"])],
|
|
3732
|
+
// Silence esbuild's own progress chatter — the CLI command above is
|
|
3733
|
+
// responsible for its own user-facing output.
|
|
3734
|
+
logLevel: "error"
|
|
3735
|
+
});
|
|
3736
|
+
try {
|
|
3737
|
+
delete __require.cache[__require.resolve(tempPath)];
|
|
3738
|
+
} catch {
|
|
3739
|
+
}
|
|
3740
|
+
const hangTimer = setTimeout(() => {
|
|
3741
|
+
process.stderr.write(
|
|
3742
|
+
"\n" + pc__default.default.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__default.default.cyan("if (require.main === module) { ... }") + " so it only runs when executed directly.\n\n"
|
|
3743
|
+
);
|
|
3744
|
+
}, 5e3);
|
|
3745
|
+
hangTimer.unref();
|
|
3746
|
+
let mod;
|
|
3747
|
+
try {
|
|
3748
|
+
mod = __require(tempPath);
|
|
3749
|
+
} finally {
|
|
3750
|
+
clearTimeout(hangTimer);
|
|
3751
|
+
}
|
|
3752
|
+
const app = mod.app ?? mod.default;
|
|
3753
|
+
if (!app || typeof app.registeredRoutes === "undefined") {
|
|
3754
|
+
await fs5__default.default.rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
3755
|
+
});
|
|
3756
|
+
throw new Error(
|
|
3757
|
+
"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;"
|
|
3758
|
+
);
|
|
3759
|
+
}
|
|
3760
|
+
const cleanup = async () => {
|
|
3761
|
+
await fs5__default.default.rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
3762
|
+
});
|
|
3763
|
+
};
|
|
3764
|
+
return { app, cleanup };
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
// src/commands/check.ts
|
|
3768
|
+
function add(ctx, f) {
|
|
3769
|
+
ctx.findings.push(f);
|
|
3770
|
+
}
|
|
3771
|
+
function checkRequestId(ctx) {
|
|
3772
|
+
const onRequest = ctx.app.hooks?.hooks?.onRequest ?? [];
|
|
3773
|
+
if (onRequest.length === 0) {
|
|
3774
|
+
add(ctx, {
|
|
3775
|
+
severity: "warn",
|
|
3776
|
+
area: "observability",
|
|
3777
|
+
message: "`app.enableRequestId()` has not been called",
|
|
3778
|
+
hint: "Distributed-trace correlation will be impossible without an X-Request-Id header. Add `app.enableRequestId()` after construction unless you handle this elsewhere."
|
|
3779
|
+
});
|
|
3780
|
+
return;
|
|
3781
|
+
}
|
|
3782
|
+
add(ctx, {
|
|
3783
|
+
severity: "ok",
|
|
3784
|
+
area: "observability",
|
|
3785
|
+
message: `onRequest hooks: ${onRequest.length} registered`
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
function checkEnvVars(ctx) {
|
|
3789
|
+
const expectedInProd = ["JWT_SECRET", "NODE_ENV"];
|
|
3790
|
+
for (const key of expectedInProd) {
|
|
3791
|
+
if (!ctx.envKeys.has(key)) continue;
|
|
3792
|
+
if (process.env[key]) {
|
|
3793
|
+
add(ctx, { severity: "ok", area: "env", message: `${key} is set` });
|
|
3794
|
+
} else {
|
|
3795
|
+
add(ctx, {
|
|
3796
|
+
severity: "warn",
|
|
3797
|
+
area: "env",
|
|
3798
|
+
message: `${key} referenced in source but not set in environment`,
|
|
3799
|
+
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.`
|
|
3800
|
+
});
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
function checkResponseSchemas(ctx) {
|
|
3805
|
+
const routes = ctx.app.registeredRoutes ?? [];
|
|
3806
|
+
const missing = routes.filter(
|
|
3807
|
+
(r) => r.schema?.body && !r.schema?.response
|
|
3808
|
+
);
|
|
3809
|
+
if (missing.length > 0) {
|
|
3810
|
+
add(ctx, {
|
|
3811
|
+
severity: "warn",
|
|
3812
|
+
area: "validation",
|
|
3813
|
+
message: `${missing.length} route${missing.length === 1 ? "" : "s"} with body schema but no response schema`,
|
|
3814
|
+
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}`
|
|
3815
|
+
});
|
|
3816
|
+
} else if (routes.length > 0) {
|
|
3817
|
+
add(ctx, {
|
|
3818
|
+
severity: "ok",
|
|
3819
|
+
area: "validation",
|
|
3820
|
+
message: "every route with a body schema also declares a response schema"
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
function checkOpenApiNaming(ctx) {
|
|
3825
|
+
const routes = ctx.app.registeredRoutes ?? [];
|
|
3826
|
+
const usingMeta = routes.filter((r) => r.meta);
|
|
3827
|
+
if (usingMeta.length > 0) {
|
|
3828
|
+
add(ctx, {
|
|
3829
|
+
severity: "fail",
|
|
3830
|
+
area: "api",
|
|
3831
|
+
message: `${usingMeta.length} route${usingMeta.length === 1 ? "" : "s"} use the removed \`meta:\` field`,
|
|
3832
|
+
hint: `The \`meta:\` field was removed in 6.0. Rename to \`openapi:\` \u2014 shape is identical. First offender: ${usingMeta[0].method} ${usingMeta[0].path}`
|
|
3833
|
+
});
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
function checkHealthCheck(ctx) {
|
|
3837
|
+
const routes = ctx.app.registeredRoutes ?? [];
|
|
3838
|
+
const hasHealth = routes.some(
|
|
3839
|
+
(r) => r.method === "GET" && ["/health", "/healthz", "/-/health", "/ping", "/live", "/ready"].some(
|
|
3840
|
+
(p) => r.path === p
|
|
3841
|
+
)
|
|
3842
|
+
);
|
|
3843
|
+
if (hasHealth) {
|
|
3844
|
+
add(ctx, { severity: "ok", area: "ops", message: "health-check route registered" });
|
|
3845
|
+
} else {
|
|
3846
|
+
add(ctx, {
|
|
3847
|
+
severity: "warn",
|
|
3848
|
+
area: "ops",
|
|
3849
|
+
message: "no health-check route detected",
|
|
3850
|
+
hint: 'Kubernetes, ECS, and load balancers expect a `/health` (or similar) endpoint. Register one via `app.healthCheck("/health")` from `@axiomify/core`.'
|
|
3851
|
+
});
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
function checkOpenApiExposure(ctx) {
|
|
3855
|
+
const routes = ctx.app.registeredRoutes ?? [];
|
|
3856
|
+
const docsRoute = routes.find(
|
|
3857
|
+
(r) => r.method === "GET" && (r.path === "/docs" || r.path.endsWith("/docs"))
|
|
3858
|
+
);
|
|
3859
|
+
const specRoute = routes.find(
|
|
3860
|
+
(r) => r.method === "GET" && r.path.endsWith("/openapi.json")
|
|
3861
|
+
);
|
|
3862
|
+
if (docsRoute || specRoute) {
|
|
3863
|
+
add(ctx, {
|
|
3864
|
+
severity: "warn",
|
|
3865
|
+
area: "security",
|
|
3866
|
+
message: "OpenAPI docs endpoint is registered",
|
|
3867
|
+
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."
|
|
3868
|
+
});
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
function checkRoutesLockState(ctx) {
|
|
3872
|
+
if (ctx.app._routesLocked) {
|
|
3873
|
+
add(ctx, {
|
|
3874
|
+
severity: "warn",
|
|
3875
|
+
area: "config",
|
|
3876
|
+
message: "app.lockRoutes() has already been called",
|
|
3877
|
+
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."
|
|
3878
|
+
});
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
function checkSecurityPlugins(ctx) {
|
|
3882
|
+
const onRequest = ctx.app.hooks?.hooks?.onRequest ?? [];
|
|
3883
|
+
if (onRequest.length < 2) {
|
|
3884
|
+
add(ctx, {
|
|
3885
|
+
severity: "warn",
|
|
3886
|
+
area: "security",
|
|
3887
|
+
message: "few onRequest hooks detected \u2014 security plugins may not be registered",
|
|
3888
|
+
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."
|
|
3889
|
+
});
|
|
3890
|
+
} else {
|
|
3891
|
+
add(ctx, {
|
|
3892
|
+
severity: "ok",
|
|
3893
|
+
area: "security",
|
|
3894
|
+
message: `${onRequest.length} onRequest hooks registered (security plugins likely active)`
|
|
3895
|
+
});
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
function collectEnvKeysFromBundle(bundlePath) {
|
|
3899
|
+
const keys = /* @__PURE__ */ new Set();
|
|
3900
|
+
try {
|
|
3901
|
+
const src = fs__default.default.readFileSync(bundlePath, "utf8");
|
|
3902
|
+
const re = /process\.env\.([A-Z][A-Z0-9_]*)|process\.env\[(['"])([A-Z][A-Z0-9_]*)\2\]/g;
|
|
3903
|
+
let m;
|
|
3904
|
+
while ((m = re.exec(src)) !== null) {
|
|
3905
|
+
keys.add(m[1] ?? m[3]);
|
|
3906
|
+
}
|
|
3907
|
+
} catch {
|
|
3908
|
+
}
|
|
3909
|
+
return keys;
|
|
3910
|
+
}
|
|
3911
|
+
function loadPkgJson(cwd) {
|
|
3912
|
+
try {
|
|
3913
|
+
return JSON.parse(fs__default.default.readFileSync(path7__default.default.join(cwd, "package.json"), "utf8"));
|
|
3914
|
+
} catch {
|
|
3915
|
+
return null;
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
async function runCheck(entry) {
|
|
3919
|
+
let app;
|
|
3920
|
+
let cleanup = async () => {
|
|
3921
|
+
};
|
|
3922
|
+
let bundlePath = "";
|
|
3923
|
+
try {
|
|
3924
|
+
const loaded = await loadApp(entry);
|
|
3925
|
+
app = loaded.app;
|
|
3926
|
+
cleanup = loaded.cleanup;
|
|
3927
|
+
bundlePath = path7__default.default.resolve(process.cwd(), ".axiomify/inspect.cjs");
|
|
3928
|
+
} catch (err) {
|
|
3929
|
+
console.error(pc__default.default.red("\u2717 Failed to load app:"));
|
|
3930
|
+
console.error(err.message);
|
|
3931
|
+
process.exit(1);
|
|
3932
|
+
}
|
|
3933
|
+
const ctx = {
|
|
3934
|
+
app,
|
|
3935
|
+
cwd: process.cwd(),
|
|
3936
|
+
findings: [],
|
|
3937
|
+
pkgJson: loadPkgJson(process.cwd()),
|
|
3938
|
+
envKeys: collectEnvKeysFromBundle(bundlePath)
|
|
3939
|
+
};
|
|
3940
|
+
checkEnvVars(ctx);
|
|
3941
|
+
checkRequestId(ctx);
|
|
3942
|
+
checkRoutesLockState(ctx);
|
|
3943
|
+
checkOpenApiNaming(ctx);
|
|
3944
|
+
checkSecurityPlugins(ctx);
|
|
3945
|
+
checkOpenApiExposure(ctx);
|
|
3946
|
+
checkResponseSchemas(ctx);
|
|
3947
|
+
checkHealthCheck(ctx);
|
|
3948
|
+
await cleanup();
|
|
3949
|
+
console.log();
|
|
3950
|
+
console.log(pc__default.default.bold(" \u{1F50D} Production-readiness check"));
|
|
3951
|
+
console.log();
|
|
3952
|
+
const sevOrder = { fail: 0, warn: 1, ok: 2 };
|
|
3953
|
+
ctx.findings.sort(
|
|
3954
|
+
(a, b) => sevOrder[a.severity] - sevOrder[b.severity] || a.area.localeCompare(b.area)
|
|
3955
|
+
);
|
|
3956
|
+
const sym = {
|
|
3957
|
+
ok: symbols.ok,
|
|
3958
|
+
warn: symbols.warn,
|
|
3959
|
+
fail: symbols.fail
|
|
3960
|
+
};
|
|
3961
|
+
for (const f of ctx.findings) {
|
|
3962
|
+
const tag = pc__default.default.dim(`[${f.area}]`);
|
|
3963
|
+
console.log(` ${sym[f.severity]} ${tag} ${f.message}`);
|
|
3964
|
+
if (f.hint && f.severity !== "ok") {
|
|
3965
|
+
const wrapped = f.hint.replace(/(.{1,80})(\s+|$)/g, "\n " + pc__default.default.dim("$1"));
|
|
3966
|
+
console.log(wrapped);
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
const fails = ctx.findings.filter((f) => f.severity === "fail").length;
|
|
3970
|
+
const warns = ctx.findings.filter((f) => f.severity === "warn").length;
|
|
3971
|
+
const oks = ctx.findings.filter((f) => f.severity === "ok").length;
|
|
3972
|
+
console.log();
|
|
3973
|
+
const summary = ` ${symbols.ok} ${pluralise(oks, "pass", "passes")}` + pc__default.default.dim(" \xB7 ") + (warns > 0 ? `${symbols.warn} ${pluralise(warns, "warning")}` : pc__default.default.dim("0 warnings")) + pc__default.default.dim(" \xB7 ") + (fails > 0 ? `${symbols.fail} ${pluralise(fails, "failure")}` : pc__default.default.dim("0 failures"));
|
|
3974
|
+
console.log(summary);
|
|
3975
|
+
console.log();
|
|
3976
|
+
if (fails > 0) process.exit(1);
|
|
3977
|
+
}
|
|
2970
3978
|
async function devServer(entry) {
|
|
2971
|
-
const entryPath =
|
|
2972
|
-
const outPath =
|
|
3979
|
+
const entryPath = path7__default.default.resolve(process.cwd(), entry);
|
|
3980
|
+
const outPath = path7__default.default.resolve(process.cwd(), ".axiomify/dev.js");
|
|
2973
3981
|
let child = null;
|
|
2974
3982
|
let firstBuild = true;
|
|
2975
3983
|
const startChild = () => {
|
|
@@ -3049,6 +4057,213 @@ async function devServer(entry) {
|
|
|
3049
4057
|
console.log(`\u{1F440} Axiomify Dev Engine watching for changes...`);
|
|
3050
4058
|
await ctx.watch();
|
|
3051
4059
|
}
|
|
4060
|
+
function add2(findings, f) {
|
|
4061
|
+
findings.push(f);
|
|
4062
|
+
}
|
|
4063
|
+
function probePort(port) {
|
|
4064
|
+
return new Promise((resolve) => {
|
|
4065
|
+
const server = net.createServer();
|
|
4066
|
+
server.once("error", (err) => {
|
|
4067
|
+
if (err.code === "EADDRINUSE") resolve("busy");
|
|
4068
|
+
else if (err.code === "EACCES") resolve("denied");
|
|
4069
|
+
else resolve("busy");
|
|
4070
|
+
});
|
|
4071
|
+
server.once("listening", () => {
|
|
4072
|
+
server.close(() => resolve("free"));
|
|
4073
|
+
});
|
|
4074
|
+
server.listen(port, "127.0.0.1");
|
|
4075
|
+
});
|
|
4076
|
+
}
|
|
4077
|
+
function checkNodeVersion(findings) {
|
|
4078
|
+
const major = parseInt(process.versions.node.split(".")[0], 10);
|
|
4079
|
+
if (major < 18) {
|
|
4080
|
+
add2(findings, {
|
|
4081
|
+
severity: "fail",
|
|
4082
|
+
area: "node",
|
|
4083
|
+
message: `Node ${process.versions.node} is below the supported minimum`,
|
|
4084
|
+
hint: "Axiomify requires Node 18 or later. Upgrade via nvm: `nvm install 22 && nvm use 22`."
|
|
4085
|
+
});
|
|
4086
|
+
} else if (major > 22) {
|
|
4087
|
+
add2(findings, {
|
|
4088
|
+
severity: "warn",
|
|
4089
|
+
area: "node",
|
|
4090
|
+
message: `Node ${process.versions.node} \u2014 uWebSockets.js has no prebuilt binary for this version`,
|
|
4091
|
+
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."
|
|
4092
|
+
});
|
|
4093
|
+
} else {
|
|
4094
|
+
add2(findings, {
|
|
4095
|
+
severity: "ok",
|
|
4096
|
+
area: "node",
|
|
4097
|
+
message: `Node ${process.versions.node} (uWS prebuilt available)`
|
|
4098
|
+
});
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
function checkPlatform(findings) {
|
|
4102
|
+
if (process.platform === "linux") {
|
|
4103
|
+
add2(findings, {
|
|
4104
|
+
severity: "ok",
|
|
4105
|
+
area: "platform",
|
|
4106
|
+
message: "Linux \u2014 SO_REUSEPORT clustering supported natively"
|
|
4107
|
+
});
|
|
4108
|
+
} else {
|
|
4109
|
+
add2(findings, {
|
|
4110
|
+
severity: "warn",
|
|
4111
|
+
area: "platform",
|
|
4112
|
+
message: `${process.platform} \u2014 \`listenClustered()\` requires \`allowUserspaceProxy: true\``,
|
|
4113
|
+
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."
|
|
4114
|
+
});
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
function checkDependencyDrift(findings) {
|
|
4118
|
+
try {
|
|
4119
|
+
const pkgPath = path7__default.default.join(process.cwd(), "package.json");
|
|
4120
|
+
const pkg = JSON.parse(fs__default.default.readFileSync(pkgPath, "utf8"));
|
|
4121
|
+
const all = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4122
|
+
const axiomifyDeps = Object.entries(all).filter(
|
|
4123
|
+
([k]) => k.startsWith("@axiomify/")
|
|
4124
|
+
);
|
|
4125
|
+
if (axiomifyDeps.length === 0) {
|
|
4126
|
+
add2(findings, {
|
|
4127
|
+
severity: "warn",
|
|
4128
|
+
area: "deps",
|
|
4129
|
+
message: "No @axiomify/* packages found in package.json",
|
|
4130
|
+
hint: "Run this from the root of a project that uses Axiomify."
|
|
4131
|
+
});
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
const versions = new Set(
|
|
4135
|
+
axiomifyDeps.map(([, v]) => v.replace(/^[\^~]/, "").replace(/^\*$/, "workspace"))
|
|
4136
|
+
);
|
|
4137
|
+
if (versions.size > 1) {
|
|
4138
|
+
add2(findings, {
|
|
4139
|
+
severity: "warn",
|
|
4140
|
+
area: "deps",
|
|
4141
|
+
message: `@axiomify/* packages are on ${versions.size} different versions`,
|
|
4142
|
+
hint: "Mixed @axiomify/* versions can cause subtle compat issues. Pin them all to the same version: " + [...versions].join(", ")
|
|
4143
|
+
});
|
|
4144
|
+
} else {
|
|
4145
|
+
add2(findings, {
|
|
4146
|
+
severity: "ok",
|
|
4147
|
+
area: "deps",
|
|
4148
|
+
message: `${axiomifyDeps.length} @axiomify/* packages aligned (${[...versions][0]})`
|
|
4149
|
+
});
|
|
4150
|
+
}
|
|
4151
|
+
} catch {
|
|
4152
|
+
add2(findings, {
|
|
4153
|
+
severity: "warn",
|
|
4154
|
+
area: "deps",
|
|
4155
|
+
message: "Could not read package.json",
|
|
4156
|
+
hint: "Run `axiomify doctor` from a project root."
|
|
4157
|
+
});
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
function checkUwsLoads(findings) {
|
|
4161
|
+
try {
|
|
4162
|
+
require_uws();
|
|
4163
|
+
add2(findings, {
|
|
4164
|
+
severity: "ok",
|
|
4165
|
+
area: "uws",
|
|
4166
|
+
message: "uWebSockets.js loads successfully"
|
|
4167
|
+
});
|
|
4168
|
+
} catch (err) {
|
|
4169
|
+
const msg = String(err.message ?? err);
|
|
4170
|
+
if (msg.includes("Cannot find module")) {
|
|
4171
|
+
add2(findings, {
|
|
4172
|
+
severity: "warn",
|
|
4173
|
+
area: "uws",
|
|
4174
|
+
message: "uWebSockets.js is not installed in this project",
|
|
4175
|
+
hint: "It is a peer dependency of `@axiomify/native`. Install via `npm install --save-optional uWebSockets.js` (the package handles platform selection)."
|
|
4176
|
+
});
|
|
4177
|
+
} else {
|
|
4178
|
+
add2(findings, {
|
|
4179
|
+
severity: "fail",
|
|
4180
|
+
area: "uws",
|
|
4181
|
+
message: "uWebSockets.js native binding failed to load",
|
|
4182
|
+
hint: msg.length > 200 ? msg.slice(0, 200) + "\u2026" : msg
|
|
4183
|
+
});
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
async function checkPortAvailability(findings) {
|
|
4188
|
+
const port = parseInt(process.env.PORT ?? "3000", 10);
|
|
4189
|
+
const state = await probePort(port);
|
|
4190
|
+
if (state === "free") {
|
|
4191
|
+
add2(findings, {
|
|
4192
|
+
severity: "ok",
|
|
4193
|
+
area: "port",
|
|
4194
|
+
message: `Port ${port} is available on 127.0.0.1`
|
|
4195
|
+
});
|
|
4196
|
+
} else if (state === "busy") {
|
|
4197
|
+
add2(findings, {
|
|
4198
|
+
severity: "warn",
|
|
4199
|
+
area: "port",
|
|
4200
|
+
message: `Port ${port} is already in use`,
|
|
4201
|
+
hint: "Stop the conflicting process or set `PORT` to a different value."
|
|
4202
|
+
});
|
|
4203
|
+
} else {
|
|
4204
|
+
add2(findings, {
|
|
4205
|
+
severity: "warn",
|
|
4206
|
+
area: "port",
|
|
4207
|
+
message: `Port ${port}: bind denied (permission)`,
|
|
4208
|
+
hint: "Ports below 1024 typically require root. Use a port \u2265 1024 in development."
|
|
4209
|
+
});
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
function checkBuildArtifacts(findings) {
|
|
4213
|
+
const dist = path7__default.default.join(process.cwd(), "dist");
|
|
4214
|
+
if (fs__default.default.existsSync(dist)) {
|
|
4215
|
+
add2(findings, {
|
|
4216
|
+
severity: "ok",
|
|
4217
|
+
area: "build",
|
|
4218
|
+
message: "dist/ exists (recent `axiomify build`)"
|
|
4219
|
+
});
|
|
4220
|
+
} else {
|
|
4221
|
+
add2(findings, {
|
|
4222
|
+
severity: "warn",
|
|
4223
|
+
area: "build",
|
|
4224
|
+
message: "No dist/ directory \u2014 production build has not been run",
|
|
4225
|
+
hint: "Run `axiomify build` before deploying."
|
|
4226
|
+
});
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
async function runDoctor() {
|
|
4230
|
+
const findings = [];
|
|
4231
|
+
checkNodeVersion(findings);
|
|
4232
|
+
checkPlatform(findings);
|
|
4233
|
+
checkDependencyDrift(findings);
|
|
4234
|
+
checkUwsLoads(findings);
|
|
4235
|
+
checkBuildArtifacts(findings);
|
|
4236
|
+
await checkPortAvailability(findings);
|
|
4237
|
+
console.log();
|
|
4238
|
+
console.log(pc__default.default.bold(" \u{1FA7A} Axiomify doctor"));
|
|
4239
|
+
console.log();
|
|
4240
|
+
const sevOrder = { fail: 0, warn: 1, ok: 2 };
|
|
4241
|
+
findings.sort(
|
|
4242
|
+
(a, b) => sevOrder[a.severity] - sevOrder[b.severity] || a.area.localeCompare(b.area)
|
|
4243
|
+
);
|
|
4244
|
+
const sym = {
|
|
4245
|
+
ok: symbols.ok,
|
|
4246
|
+
warn: symbols.warn,
|
|
4247
|
+
fail: symbols.fail
|
|
4248
|
+
};
|
|
4249
|
+
for (const f of findings) {
|
|
4250
|
+
const tag = pc__default.default.dim(`[${f.area}]`);
|
|
4251
|
+
console.log(` ${sym[f.severity]} ${tag} ${f.message}`);
|
|
4252
|
+
if (f.hint && f.severity !== "ok") {
|
|
4253
|
+
const wrapped = f.hint.replace(/(.{1,80})(\s+|$)/g, "\n " + pc__default.default.dim("$1"));
|
|
4254
|
+
console.log(wrapped);
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
const fails = findings.filter((f) => f.severity === "fail").length;
|
|
4258
|
+
const warns = findings.filter((f) => f.severity === "warn").length;
|
|
4259
|
+
const oks = findings.filter((f) => f.severity === "ok").length;
|
|
4260
|
+
console.log();
|
|
4261
|
+
console.log(
|
|
4262
|
+
` ${symbols.ok} ${pluralise(oks, "pass", "passes")}` + pc__default.default.dim(" \xB7 ") + (warns > 0 ? `${symbols.warn} ${pluralise(warns, "warning")}` : pc__default.default.dim("0 warnings")) + pc__default.default.dim(" \xB7 ") + (fails > 0 ? `${symbols.fail} ${pluralise(fails, "failure")}` : pc__default.default.dim("0 failures"))
|
|
4263
|
+
);
|
|
4264
|
+
console.log();
|
|
4265
|
+
if (fails > 0) process.exit(1);
|
|
4266
|
+
}
|
|
3052
4267
|
var DEV_COMMAND_BY_PM = {
|
|
3053
4268
|
npm: "npm run dev",
|
|
3054
4269
|
pnpm: "pnpm dev",
|
|
@@ -3066,20 +4281,6 @@ async function initProject(targetDir, options = {}) {
|
|
|
3066
4281
|
});
|
|
3067
4282
|
}
|
|
3068
4283
|
questions.push(
|
|
3069
|
-
{
|
|
3070
|
-
type: "select",
|
|
3071
|
-
name: "adapter",
|
|
3072
|
-
message: "Which HTTP adapter do you want to use?",
|
|
3073
|
-
choices: [
|
|
3074
|
-
"Native (uWS \u2014 Fastest, 50k+ req/s)",
|
|
3075
|
-
"Fastify (High-throughput, recommended)",
|
|
3076
|
-
"Express (Max ecosystem compatibility)",
|
|
3077
|
-
"Hapi (Enterprise, plugin-first)",
|
|
3078
|
-
"Node HTTP (Zero dependency)"
|
|
3079
|
-
],
|
|
3080
|
-
initial: 0
|
|
3081
|
-
// Native is the recommended default
|
|
3082
|
-
},
|
|
3083
4284
|
{
|
|
3084
4285
|
type: "input",
|
|
3085
4286
|
name: "description",
|
|
@@ -3133,12 +4334,12 @@ async function initProject(targetDir, options = {}) {
|
|
|
3133
4334
|
);
|
|
3134
4335
|
process.exit(1);
|
|
3135
4336
|
}
|
|
3136
|
-
const dir =
|
|
4337
|
+
const dir = path7__default.default.resolve(process.cwd(), projectName);
|
|
3137
4338
|
if (fs.existsSync(dir) && !options.force && targetDir) {
|
|
3138
4339
|
const targets = [
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
4340
|
+
path7__default.default.join(dir, "package.json"),
|
|
4341
|
+
path7__default.default.join(dir, "tsconfig.json"),
|
|
4342
|
+
path7__default.default.join(dir, "src", "index.ts")
|
|
3142
4343
|
];
|
|
3143
4344
|
const collisions = targets.filter((p) => fs.existsSync(p));
|
|
3144
4345
|
if (collisions.length > 0) {
|
|
@@ -3150,28 +4351,11 @@ async function initProject(targetDir, options = {}) {
|
|
|
3150
4351
|
process.exit(1);
|
|
3151
4352
|
}
|
|
3152
4353
|
}
|
|
3153
|
-
await
|
|
4354
|
+
await fs5__default.default.mkdir(path7__default.default.join(dir, "src"), { recursive: true });
|
|
3154
4355
|
const AXIOMIFY_VERSION = `^${package_default.version}`;
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
if (answers.adapter.includes("Fastify")) {
|
|
3159
|
-
adapterPackage = "@axiomify/fastify";
|
|
3160
|
-
adapterImport = "import { FastifyAdapter } from '@axiomify/fastify';";
|
|
3161
|
-
adapterInit = "const server = new FastifyAdapter(app);\n await server.listen(3000);\n console.log(' Axiomify Fastify on :3000');";
|
|
3162
|
-
} else if (answers.adapter.includes("Express")) {
|
|
3163
|
-
adapterPackage = "@axiomify/express";
|
|
3164
|
-
adapterImport = "import { ExpressAdapter } from '@axiomify/express';";
|
|
3165
|
-
adapterInit = "const server = new ExpressAdapter(app);\n server.listen(3000, () => console.log(' Axiomify Express on :3000'));";
|
|
3166
|
-
} else if (answers.adapter.includes("Hapi")) {
|
|
3167
|
-
adapterPackage = "@axiomify/hapi";
|
|
3168
|
-
adapterImport = "import { HapiAdapter } from '@axiomify/hapi';";
|
|
3169
|
-
adapterInit = "const server = new HapiAdapter(app);\n await server.listen(3000);\n console.log(' Axiomify Hapi on :3000');";
|
|
3170
|
-
} else if (answers.adapter.includes("HTTP")) {
|
|
3171
|
-
adapterPackage = "@axiomify/http";
|
|
3172
|
-
adapterImport = "import { HttpAdapter } from '@axiomify/http';";
|
|
3173
|
-
adapterInit = "const server = new HttpAdapter(app);\n server.listen(3000, () => console.log(' Axiomify HTTP on :3000'));";
|
|
3174
|
-
}
|
|
4356
|
+
const adapterPackage = "@axiomify/native";
|
|
4357
|
+
const adapterImport = "import { NativeAdapter } from '@axiomify/native';";
|
|
4358
|
+
const adapterInit = "const server = new NativeAdapter(app, { port: 3000 });\n server.listen(() => console.log(' Axiomify Native on :3000'));";
|
|
3175
4359
|
const pkgJson = {
|
|
3176
4360
|
name: projectName,
|
|
3177
4361
|
version: "1.0.0",
|
|
@@ -3239,14 +4423,14 @@ async function initProject(targetDir, options = {}) {
|
|
|
3239
4423
|
"printWidth": 100,
|
|
3240
4424
|
"tabWidth": 2
|
|
3241
4425
|
}`;
|
|
3242
|
-
await
|
|
3243
|
-
await
|
|
3244
|
-
await
|
|
3245
|
-
|
|
4426
|
+
await fs5__default.default.writeFile(path7__default.default.join(dir, ".eslintrc.cjs"), eslintConfig);
|
|
4427
|
+
await fs5__default.default.writeFile(path7__default.default.join(dir, ".prettierrc"), prettierConfig);
|
|
4428
|
+
await fs5__default.default.writeFile(
|
|
4429
|
+
path7__default.default.join(dir, ".prettierignore"),
|
|
3246
4430
|
"dist\nnode_modules\ncoverage\n"
|
|
3247
4431
|
);
|
|
3248
|
-
await
|
|
3249
|
-
|
|
4432
|
+
await fs5__default.default.writeFile(
|
|
4433
|
+
path7__default.default.join(dir, ".editorconfig"),
|
|
3250
4434
|
"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"
|
|
3251
4435
|
);
|
|
3252
4436
|
}
|
|
@@ -3307,19 +4491,19 @@ async function initProject(targetDir, options = {}) {
|
|
|
3307
4491
|
"coverage",
|
|
3308
4492
|
"*.log"
|
|
3309
4493
|
].join("\n") + "\n";
|
|
3310
|
-
await
|
|
3311
|
-
|
|
4494
|
+
await fs5__default.default.writeFile(
|
|
4495
|
+
path7__default.default.join(dir, "package.json"),
|
|
3312
4496
|
JSON.stringify(pkgJson, null, 2)
|
|
3313
4497
|
);
|
|
3314
|
-
await
|
|
3315
|
-
|
|
4498
|
+
await fs5__default.default.writeFile(
|
|
4499
|
+
path7__default.default.join(dir, "tsconfig.json"),
|
|
3316
4500
|
JSON.stringify(tsConfig, null, 2)
|
|
3317
4501
|
);
|
|
3318
|
-
await
|
|
3319
|
-
await
|
|
4502
|
+
await fs5__default.default.writeFile(path7__default.default.join(dir, "src", "index.ts"), indexTs);
|
|
4503
|
+
await fs5__default.default.writeFile(path7__default.default.join(dir, ".gitignore"), gitignore);
|
|
3320
4504
|
console.log(pc__default.default.green(`
|
|
3321
4505
|
\u2705 Axiomify project initialized in ${pc__default.default.bold(dir)}`));
|
|
3322
|
-
if (answers.useGit && !fs.existsSync(
|
|
4506
|
+
if (answers.useGit && !fs.existsSync(path7__default.default.join(dir, ".git"))) {
|
|
3323
4507
|
try {
|
|
3324
4508
|
await execa.execa("git", ["init"], { cwd: dir });
|
|
3325
4509
|
console.log(pc__default.default.green("\u2705 Git repository initialized"));
|
|
@@ -3353,64 +4537,641 @@ async function initProject(targetDir, options = {}) {
|
|
|
3353
4537
|
\u{1F525} Run "${devCommand}" to start your development server!`)
|
|
3354
4538
|
);
|
|
3355
4539
|
}
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
4540
|
+
var RULES = [
|
|
4541
|
+
{
|
|
4542
|
+
id: "meta-to-openapi",
|
|
4543
|
+
description: "`meta:` route field renamed to `openapi:` (OpenAPI 3.0.3 Operation Object terminology)",
|
|
4544
|
+
// Match `meta: {` or `meta:{` after whitespace/comma — avoid matching
|
|
4545
|
+
// unrelated `meta:` fields in other object literals by requiring an
|
|
4546
|
+
// adjacent route-definition cue (method, path, schema, handler nearby).
|
|
4547
|
+
// To stay simple and conservative, only rewrite when the line starts
|
|
4548
|
+
// with optional whitespace + `meta:` (the common formatting).
|
|
4549
|
+
match: /^(\s*)meta:(\s*\{)/gm,
|
|
4550
|
+
replace: "$1openapi:$2"
|
|
4551
|
+
},
|
|
4552
|
+
{
|
|
4553
|
+
id: "useSwagger-import",
|
|
4554
|
+
description: "`useSwagger` import \u2192 `useOpenAPI` (the function was never named `useSwagger` in shipped code \u2014 docs were wrong)",
|
|
4555
|
+
match: /\buseSwagger\b/g,
|
|
4556
|
+
replace: "useOpenAPI"
|
|
4557
|
+
},
|
|
4558
|
+
{
|
|
4559
|
+
id: "routePrefix-option",
|
|
4560
|
+
description: "`routePrefix:` \u2192 `prefix:` on `useOpenAPI()` options",
|
|
4561
|
+
match: /(\buseOpenAPI\s*\([\s\S]*?)\brouteprefix(\s*:)/gi,
|
|
4562
|
+
// Naive: just rename the property when it appears inside a useOpenAPI() call.
|
|
4563
|
+
// The capture-group lookbehind avoids touching unrelated `routePrefix`
|
|
4564
|
+
// properties in other contexts.
|
|
4565
|
+
replace: (_match, before, suffix) => `${before}prefix${suffix}`
|
|
4566
|
+
},
|
|
4567
|
+
{
|
|
4568
|
+
id: "RouteMeta-type",
|
|
4569
|
+
description: "`RouteMeta` type \u2192 `OpenApiOperation` (alias kept through 5.x, removed in 6.0)",
|
|
4570
|
+
// Match `RouteMeta` only as a type position (after `:` or `<` or
|
|
4571
|
+
// `as`) — avoids hitting an unrelated variable named `RouteMeta`.
|
|
4572
|
+
match: /(:\s*|<\s*|\bas\s+)RouteMeta\b/g,
|
|
4573
|
+
replace: "$1OpenApiOperation"
|
|
4574
|
+
},
|
|
4575
|
+
{
|
|
4576
|
+
id: "AppPlugin-type",
|
|
4577
|
+
description: "`AppPlugin` type alias \u2192 `AppConfigurator` (removed in 5.0; runtime accepts 1-arg fns identically)",
|
|
4578
|
+
match: /(:\s*|<\s*|\bas\s+)AppPlugin\b/g,
|
|
4579
|
+
replace: "$1AppConfigurator"
|
|
4580
|
+
}
|
|
4581
|
+
];
|
|
4582
|
+
async function listSourceFiles(rootAbs) {
|
|
4583
|
+
const out = [];
|
|
4584
|
+
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".axiomify", "coverage"]);
|
|
4585
|
+
const walk = async (dir) => {
|
|
4586
|
+
let entries;
|
|
3369
4587
|
try {
|
|
3370
|
-
|
|
3371
|
-
} catch
|
|
4588
|
+
entries = await fs5__default.default.readdir(dir, { withFileTypes: true });
|
|
4589
|
+
} catch {
|
|
4590
|
+
return;
|
|
3372
4591
|
}
|
|
3373
|
-
const
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
4592
|
+
for (const e of entries) {
|
|
4593
|
+
if (e.isDirectory()) {
|
|
4594
|
+
if (skip.has(e.name) || e.name.startsWith(".")) continue;
|
|
4595
|
+
await walk(path7__default.default.join(dir, e.name));
|
|
4596
|
+
} else if (e.isFile()) {
|
|
4597
|
+
const ext = path7__default.default.extname(e.name);
|
|
4598
|
+
if ([".ts", ".tsx", ".js", ".mjs", ".cjs"].includes(ext)) {
|
|
4599
|
+
out.push(path7__default.default.join(dir, e.name));
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
};
|
|
4604
|
+
await walk(rootAbs);
|
|
4605
|
+
return out;
|
|
4606
|
+
}
|
|
4607
|
+
function applyRules(src) {
|
|
4608
|
+
let updated = src;
|
|
4609
|
+
const counts = {};
|
|
4610
|
+
for (const rule of RULES) {
|
|
4611
|
+
const before = updated;
|
|
4612
|
+
if (typeof rule.replace === "string") {
|
|
4613
|
+
updated = updated.replace(rule.match, rule.replace);
|
|
4614
|
+
} else {
|
|
4615
|
+
updated = updated.replace(rule.match, rule.replace);
|
|
4616
|
+
}
|
|
4617
|
+
if (updated !== before) {
|
|
4618
|
+
const matches = before.match(rule.match);
|
|
4619
|
+
counts[rule.id] = matches ? matches.length : 1;
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
return { updated, counts };
|
|
4623
|
+
}
|
|
4624
|
+
function renderUnifiedDiff(file, original, updated) {
|
|
4625
|
+
if (original === updated) return "";
|
|
4626
|
+
const origLines = original.split("\n");
|
|
4627
|
+
const updatedLines = updated.split("\n");
|
|
4628
|
+
const out = [];
|
|
4629
|
+
out.push(pc__default.default.bold(`--- ${file}`));
|
|
4630
|
+
out.push(pc__default.default.bold(`+++ ${file}`));
|
|
4631
|
+
const max = Math.max(origLines.length, updatedLines.length);
|
|
4632
|
+
for (let i = 0; i < max; i++) {
|
|
4633
|
+
const a = origLines[i];
|
|
4634
|
+
const b = updatedLines[i];
|
|
4635
|
+
if (a === b) continue;
|
|
4636
|
+
if (a !== void 0) out.push(pc__default.default.red("- " + a));
|
|
4637
|
+
if (b !== void 0) out.push(pc__default.default.green("+ " + b));
|
|
4638
|
+
}
|
|
4639
|
+
return out.join("\n");
|
|
4640
|
+
}
|
|
4641
|
+
async function runMigrate(opts = {}) {
|
|
4642
|
+
const dir = opts.dir ?? "src";
|
|
4643
|
+
const rootAbs = path7__default.default.resolve(process.cwd(), dir);
|
|
4644
|
+
try {
|
|
4645
|
+
const stat = await fs5__default.default.stat(rootAbs);
|
|
4646
|
+
if (!stat.isDirectory()) throw new Error("not a directory");
|
|
4647
|
+
} catch {
|
|
4648
|
+
console.error(
|
|
4649
|
+
pc__default.default.red(`\u2717 ${dir} does not exist or is not a directory.`),
|
|
4650
|
+
`
|
|
4651
|
+
Run from a project root that contains the directory you want to migrate.`
|
|
4652
|
+
);
|
|
4653
|
+
process.exit(1);
|
|
4654
|
+
}
|
|
4655
|
+
const files = await listSourceFiles(rootAbs);
|
|
4656
|
+
if (files.length === 0) {
|
|
4657
|
+
console.log(`${symbols.info} No source files found under ${pc__default.default.cyan(dir)}.`);
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4660
|
+
const results = [];
|
|
4661
|
+
for (const f of files) {
|
|
4662
|
+
const original = await fs5__default.default.readFile(f, "utf8");
|
|
4663
|
+
const { updated, counts } = applyRules(original);
|
|
4664
|
+
if (Object.keys(counts).length > 0) {
|
|
4665
|
+
results.push({ file: f, counts, original, updated });
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
console.log();
|
|
4669
|
+
console.log(pc__default.default.bold(" \u{1F504} Axiomify v4 \u2192 v5 migration"));
|
|
4670
|
+
console.log(
|
|
4671
|
+
pc__default.default.dim(
|
|
4672
|
+
` Scanned ${pluralise(files.length, "file")} under ${path7__default.default.relative(process.cwd(), rootAbs) || "."}/.
|
|
4673
|
+
`
|
|
4674
|
+
)
|
|
4675
|
+
);
|
|
4676
|
+
if (results.length === 0) {
|
|
4677
|
+
console.log(` ${symbols.ok} Nothing to migrate \u2014 looks like you're already on the v5 shape.`);
|
|
4678
|
+
console.log();
|
|
4679
|
+
return;
|
|
4680
|
+
}
|
|
4681
|
+
const totalsByRule = {};
|
|
4682
|
+
for (const r of results) {
|
|
4683
|
+
for (const [rule, n] of Object.entries(r.counts)) {
|
|
4684
|
+
totalsByRule[rule] = (totalsByRule[rule] ?? 0) + n;
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
for (const rule of RULES) {
|
|
4688
|
+
const count = totalsByRule[rule.id];
|
|
4689
|
+
if (!count) continue;
|
|
4690
|
+
console.log(
|
|
4691
|
+
` ${pc__default.default.cyan(rule.id)} ${pc__default.default.dim("\u2014")} ${rule.description}`
|
|
4692
|
+
);
|
|
4693
|
+
console.log(
|
|
4694
|
+
` ${symbols.bullet} ${pluralise(count, "change")} across ${pluralise(
|
|
4695
|
+
results.filter((r) => r.counts[rule.id]).length,
|
|
4696
|
+
"file"
|
|
4697
|
+
)}`
|
|
4698
|
+
);
|
|
4699
|
+
}
|
|
4700
|
+
console.log();
|
|
4701
|
+
if (opts.reportOnly) {
|
|
4702
|
+
console.log(pc__default.default.dim(" --report-only: no files were modified."));
|
|
4703
|
+
console.log();
|
|
4704
|
+
return;
|
|
4705
|
+
}
|
|
4706
|
+
if (opts.dryRun) {
|
|
4707
|
+
for (const r of results) {
|
|
4708
|
+
const rel = path7__default.default.relative(process.cwd(), r.file);
|
|
4709
|
+
console.log(renderUnifiedDiff(rel, r.original, r.updated));
|
|
4710
|
+
console.log();
|
|
4711
|
+
}
|
|
4712
|
+
console.log(
|
|
4713
|
+
`${symbols.info} ${pluralise(results.length, "file")} would be modified. Re-run without ${pc__default.default.cyan("--dry-run")} to apply.`
|
|
4714
|
+
);
|
|
4715
|
+
console.log();
|
|
4716
|
+
return;
|
|
4717
|
+
}
|
|
4718
|
+
for (const r of results) {
|
|
4719
|
+
await fs5__default.default.writeFile(r.file, r.updated, "utf8");
|
|
4720
|
+
console.log(` ${symbols.ok} ${pc__default.default.green("Updated")} ${path7__default.default.relative(process.cwd(), r.file)}`);
|
|
4721
|
+
}
|
|
4722
|
+
console.log();
|
|
4723
|
+
console.log(
|
|
4724
|
+
` ${symbols.ok} ${pluralise(results.length, "file")} migrated, ${pluralise(
|
|
4725
|
+
Object.values(totalsByRule).reduce((a, b) => a + b, 0),
|
|
4726
|
+
"total change"
|
|
4727
|
+
)} applied.`
|
|
4728
|
+
);
|
|
4729
|
+
console.log();
|
|
4730
|
+
console.log(pc__default.default.dim(" Manual review needed for:"));
|
|
4731
|
+
console.log(
|
|
4732
|
+
pc__default.default.dim(
|
|
4733
|
+
` ${symbols.bullet} Dangling \`AppPlugin\` / \`RouteMeta\` in import statements \u2014 the codemod renames`
|
|
4734
|
+
)
|
|
4735
|
+
);
|
|
4736
|
+
console.log(
|
|
4737
|
+
pc__default.default.dim(
|
|
4738
|
+
` type USAGES but not the import bindings themselves. TypeScript flags the unused`
|
|
4739
|
+
)
|
|
4740
|
+
);
|
|
4741
|
+
console.log(
|
|
4742
|
+
pc__default.default.dim(
|
|
4743
|
+
` import; remove it (or run \`tsc --noUnusedLocals\` once and let your editor clean up).`
|
|
4744
|
+
)
|
|
4745
|
+
);
|
|
4746
|
+
console.log(
|
|
4747
|
+
pc__default.default.dim(
|
|
4748
|
+
` ${symbols.bullet} 5-arg positional \`SerializerFn\` signatures \u2014 the function body needs by-hand updates`
|
|
4749
|
+
)
|
|
4750
|
+
);
|
|
4751
|
+
console.log(
|
|
4752
|
+
pc__default.default.dim(
|
|
4753
|
+
` ${symbols.bullet} \`new Axiomify()\` callers that relied on automatic \`X-Request-Id\` injection`
|
|
4754
|
+
)
|
|
4755
|
+
);
|
|
4756
|
+
console.log(
|
|
4757
|
+
pc__default.default.dim(
|
|
4758
|
+
` ${symbols.bullet} JWT secrets \u2014 verify they are \u2265 32 BYTES (not chars) per RFC 7518 \xA73.2`
|
|
4759
|
+
)
|
|
4760
|
+
);
|
|
4761
|
+
console.log();
|
|
4762
|
+
console.log(
|
|
4763
|
+
` See ${pc__default.default.cyan("docs/migration-v4-to-v5.md")} for the full guide and ` + pc__default.default.cyan("axiomify check") + " to verify the migrated app."
|
|
4764
|
+
);
|
|
4765
|
+
console.log();
|
|
4766
|
+
}
|
|
4767
|
+
async function jsonToYaml(obj) {
|
|
4768
|
+
const emit = (v, indent) => {
|
|
4769
|
+
const pad2 = " ".repeat(indent);
|
|
4770
|
+
if (v === null) return "null";
|
|
4771
|
+
if (typeof v === "boolean" || typeof v === "number") return String(v);
|
|
4772
|
+
if (typeof v === "string") {
|
|
4773
|
+
if (/[:#\n\r\t"'{}[\]&*!|>%@`]|^\s|\s$/.test(v) || v === "") {
|
|
4774
|
+
return JSON.stringify(v);
|
|
4775
|
+
}
|
|
4776
|
+
return v;
|
|
4777
|
+
}
|
|
4778
|
+
if (Array.isArray(v)) {
|
|
4779
|
+
if (v.length === 0) return "[]";
|
|
4780
|
+
return v.map((item) => `
|
|
4781
|
+
${pad2}- ${emit(item, indent + 1).replace(/^/gm, " ").trimStart()}`).join("");
|
|
4782
|
+
}
|
|
4783
|
+
if (typeof v === "object") {
|
|
4784
|
+
const keys = Object.keys(v);
|
|
4785
|
+
if (keys.length === 0) return "{}";
|
|
4786
|
+
return keys.map((k) => {
|
|
4787
|
+
const val = v[k];
|
|
4788
|
+
const rendered = emit(val, indent + 1);
|
|
4789
|
+
if (rendered.startsWith("\n")) return `
|
|
4790
|
+
${pad2}${k}:${rendered}`;
|
|
4791
|
+
return `
|
|
4792
|
+
${pad2}${k}: ${rendered}`;
|
|
4793
|
+
}).join("");
|
|
4794
|
+
}
|
|
4795
|
+
return JSON.stringify(v);
|
|
4796
|
+
};
|
|
4797
|
+
return emit(obj, 0).trimStart();
|
|
4798
|
+
}
|
|
4799
|
+
async function emitOpenApi(entry, opts = {}) {
|
|
4800
|
+
let app;
|
|
4801
|
+
let cleanup = async () => {
|
|
4802
|
+
};
|
|
4803
|
+
try {
|
|
4804
|
+
const loaded = await loadApp(entry);
|
|
4805
|
+
app = loaded.app;
|
|
4806
|
+
cleanup = loaded.cleanup;
|
|
4807
|
+
} catch (err) {
|
|
4808
|
+
console.error(pc__default.default.red("\u2717 Failed to load app:"));
|
|
4809
|
+
console.error(err.message);
|
|
4810
|
+
process.exit(1);
|
|
4811
|
+
}
|
|
4812
|
+
try {
|
|
4813
|
+
let OpenApiGenerator2;
|
|
4814
|
+
try {
|
|
4815
|
+
({ OpenApiGenerator: OpenApiGenerator2 } = await Promise.resolve().then(() => (init_dist(), dist_exports)));
|
|
4816
|
+
} catch {
|
|
3383
4817
|
console.error(
|
|
3384
|
-
"
|
|
4818
|
+
pc__default.default.red("\u2717 @axiomify/openapi is not installed."),
|
|
4819
|
+
"\n Install it:",
|
|
4820
|
+
pc__default.default.cyan("npm install @axiomify/openapi")
|
|
3385
4821
|
);
|
|
3386
4822
|
process.exit(1);
|
|
3387
4823
|
}
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
app
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
const
|
|
4824
|
+
const info = {
|
|
4825
|
+
title: opts.title ?? "API",
|
|
4826
|
+
version: opts.version ?? "1.0.0"
|
|
4827
|
+
};
|
|
4828
|
+
const generator = new OpenApiGenerator2(app, { info });
|
|
4829
|
+
const spec = generator.generate();
|
|
4830
|
+
if (opts.title) spec.info.title = opts.title;
|
|
4831
|
+
if (opts.version) spec.info.version = opts.version;
|
|
4832
|
+
const format = opts.format ?? "json";
|
|
4833
|
+
const serialised = format === "yaml" ? await jsonToYaml(spec) : opts.minify ? JSON.stringify(spec) : JSON.stringify(spec, null, 2);
|
|
4834
|
+
if (opts.output) {
|
|
4835
|
+
const outPath = path7__default.default.resolve(process.cwd(), opts.output);
|
|
4836
|
+
await fs5__default.default.mkdir(path7__default.default.dirname(outPath), { recursive: true });
|
|
4837
|
+
await fs5__default.default.writeFile(outPath, serialised + "\n", "utf8");
|
|
4838
|
+
const routeCount = (app.registeredRoutes ?? []).length;
|
|
3400
4839
|
console.log(
|
|
3401
|
-
`${
|
|
3402
|
-
30
|
|
3403
|
-
)} | ${validationStr}`
|
|
4840
|
+
`${pc__default.default.green("\u2713")} OpenAPI spec written to ${pc__default.default.cyan(opts.output)} ` + pc__default.default.dim(`(${routeCount} route${routeCount === 1 ? "" : "s"}, ${format})`)
|
|
3404
4841
|
);
|
|
3405
|
-
}
|
|
3406
|
-
|
|
4842
|
+
} else {
|
|
4843
|
+
process.stdout.write(serialised + "\n");
|
|
4844
|
+
}
|
|
3407
4845
|
} catch (error) {
|
|
3408
|
-
console.error("\
|
|
4846
|
+
console.error(pc__default.default.red("\u2717 Failed to generate spec:"), error);
|
|
4847
|
+
process.exit(1);
|
|
3409
4848
|
} finally {
|
|
3410
|
-
await
|
|
4849
|
+
await cleanup();
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
function normalise(raw, isWs) {
|
|
4853
|
+
const validation = [];
|
|
4854
|
+
if (raw.schema?.body) validation.push("Body");
|
|
4855
|
+
if (raw.schema?.query) validation.push("Query");
|
|
4856
|
+
if (raw.schema?.params) validation.push("Params");
|
|
4857
|
+
if (raw.schema?.response) validation.push("Response");
|
|
4858
|
+
if (raw.schema?.files) validation.push("Files");
|
|
4859
|
+
if (raw.schema?.message) validation.push("Message");
|
|
4860
|
+
const op = raw.openapi ?? {};
|
|
4861
|
+
return {
|
|
4862
|
+
method: isWs ? "WS" : raw.method,
|
|
4863
|
+
path: raw.path,
|
|
4864
|
+
validation,
|
|
4865
|
+
tags: Array.isArray(op.tags) ? op.tags : [],
|
|
4866
|
+
operationId: typeof op.operationId === "string" ? op.operationId : void 0,
|
|
4867
|
+
deprecated: op.deprecated === true,
|
|
4868
|
+
timeout: typeof raw.timeout === "number" && raw.timeout > 0 ? raw.timeout : void 0,
|
|
4869
|
+
plugins: Array.isArray(raw.plugins) ? raw.plugins.length : 0,
|
|
4870
|
+
isWs
|
|
4871
|
+
};
|
|
4872
|
+
}
|
|
4873
|
+
function matchesFilter(route, opts) {
|
|
4874
|
+
if (opts.method) {
|
|
4875
|
+
const wanted = opts.method.split(",").map((m) => m.trim().toUpperCase()).filter(Boolean);
|
|
4876
|
+
if (wanted.length && !wanted.includes(route.method)) return false;
|
|
4877
|
+
}
|
|
4878
|
+
if (opts.filter) {
|
|
4879
|
+
const pat = opts.filter;
|
|
4880
|
+
if (pat.includes("*")) {
|
|
4881
|
+
const re = new RegExp(
|
|
4882
|
+
"^" + pat.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
|
|
4883
|
+
);
|
|
4884
|
+
if (!re.test(route.path)) return false;
|
|
4885
|
+
} else if (!route.path.includes(pat)) {
|
|
4886
|
+
return false;
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
return true;
|
|
4890
|
+
}
|
|
4891
|
+
function sortRoutes(routes, by) {
|
|
4892
|
+
const methodOrder = {
|
|
4893
|
+
GET: 0,
|
|
4894
|
+
POST: 1,
|
|
4895
|
+
PUT: 2,
|
|
4896
|
+
PATCH: 3,
|
|
4897
|
+
DELETE: 4,
|
|
4898
|
+
HEAD: 5,
|
|
4899
|
+
OPTIONS: 6,
|
|
4900
|
+
WS: 7
|
|
4901
|
+
};
|
|
4902
|
+
return [...routes].sort((a, b) => {
|
|
4903
|
+
if (by === "method") {
|
|
4904
|
+
const am = methodOrder[a.method] ?? 99;
|
|
4905
|
+
const bm = methodOrder[b.method] ?? 99;
|
|
4906
|
+
if (am !== bm) return am - bm;
|
|
4907
|
+
return a.path.localeCompare(b.path);
|
|
4908
|
+
}
|
|
4909
|
+
if (a.path !== b.path) return a.path.localeCompare(b.path);
|
|
4910
|
+
return (methodOrder[a.method] ?? 99) - (methodOrder[b.method] ?? 99);
|
|
4911
|
+
});
|
|
4912
|
+
}
|
|
4913
|
+
async function inspectRoutes(entry, opts = {}) {
|
|
4914
|
+
let app;
|
|
4915
|
+
let cleanup = async () => {
|
|
4916
|
+
};
|
|
4917
|
+
try {
|
|
4918
|
+
const loaded = await loadApp(entry);
|
|
4919
|
+
app = loaded.app;
|
|
4920
|
+
cleanup = loaded.cleanup;
|
|
4921
|
+
} catch (err) {
|
|
4922
|
+
console.error(pc__default.default.red("\u2717 Failed to load app:"));
|
|
4923
|
+
console.error(err.message);
|
|
4924
|
+
process.exit(1);
|
|
4925
|
+
}
|
|
4926
|
+
try {
|
|
4927
|
+
const httpRoutes = (app.registeredRoutes ?? []).map(
|
|
4928
|
+
(r) => normalise(r, false)
|
|
4929
|
+
);
|
|
4930
|
+
const wsRoutes = (app.registeredWsRoutes ?? []).map(
|
|
4931
|
+
(r) => normalise(r, true)
|
|
4932
|
+
);
|
|
4933
|
+
const all = sortRoutes([...httpRoutes, ...wsRoutes], opts.sort ?? "path");
|
|
4934
|
+
const filtered = all.filter((r) => matchesFilter(r, opts));
|
|
4935
|
+
if (opts.json) {
|
|
4936
|
+
process.stdout.write(JSON.stringify(filtered, null, 2) + "\n");
|
|
4937
|
+
return;
|
|
4938
|
+
}
|
|
4939
|
+
if (filtered.length === 0) {
|
|
4940
|
+
console.log(
|
|
4941
|
+
"\n" + symbols.info + pc__default.default.dim(
|
|
4942
|
+
` No routes match the current filter (${all.length} total registered).
|
|
4943
|
+
`
|
|
4944
|
+
)
|
|
4945
|
+
);
|
|
4946
|
+
return;
|
|
4947
|
+
}
|
|
4948
|
+
console.log();
|
|
4949
|
+
console.log(pc__default.default.bold(" \u{1F9ED} Axiomify routes"));
|
|
4950
|
+
console.log();
|
|
4951
|
+
const columns = [
|
|
4952
|
+
{ header: "METHOD", minWidth: 7 },
|
|
4953
|
+
{ header: "PATH", minWidth: 20, maxWidth: 60 },
|
|
4954
|
+
{ header: "VALIDATION", minWidth: 10, maxWidth: 32 },
|
|
4955
|
+
{ header: "META", maxWidth: 40 }
|
|
4956
|
+
];
|
|
4957
|
+
const rows = filtered.map((r) => {
|
|
4958
|
+
const method = colourMethod(r.method);
|
|
4959
|
+
const path11 = r.deprecated ? pc__default.default.strikethrough(r.path) + " " + badge.deprecated() : r.path;
|
|
4960
|
+
const validation = r.validation.length > 0 ? r.validation.map(badge.validation).join(pc__default.default.dim(",")) : pc__default.default.dim("\u2014");
|
|
4961
|
+
const metaBits = [];
|
|
4962
|
+
if (r.operationId) metaBits.push(pc__default.default.dim(`op:`) + r.operationId);
|
|
4963
|
+
if (r.tags.length) metaBits.push(badge.tags(r.tags));
|
|
4964
|
+
if (r.timeout !== void 0) metaBits.push(badge.timeout(r.timeout));
|
|
4965
|
+
if (r.plugins > 0) metaBits.push(pc__default.default.dim(`+${r.plugins} plugin${r.plugins === 1 ? "" : "s"}`));
|
|
4966
|
+
const meta = metaBits.length ? metaBits.join(" ") : pc__default.default.dim("\u2014");
|
|
4967
|
+
return [method, path11, validation, meta];
|
|
3411
4968
|
});
|
|
4969
|
+
console.log(renderTable(columns, rows));
|
|
4970
|
+
const byMethod = filtered.reduce((acc, r) => {
|
|
4971
|
+
acc[r.method] = (acc[r.method] ?? 0) + 1;
|
|
4972
|
+
return acc;
|
|
4973
|
+
}, {});
|
|
4974
|
+
const summaryParts = Object.entries(byMethod).sort(([a], [b]) => a.localeCompare(b)).map(([m, n]) => `${colourMethod(m).trimEnd()} ${pc__default.default.bold(String(n))}`);
|
|
4975
|
+
console.log();
|
|
4976
|
+
console.log(
|
|
4977
|
+
` ${symbols.ok} ${pluralise(filtered.length, "route")}` + (filtered.length < all.length ? pc__default.default.dim(` (filtered from ${all.length})`) : "") + " " + summaryParts.join(pc__default.default.dim(" \xB7 "))
|
|
4978
|
+
);
|
|
4979
|
+
const filteredWs = filtered.filter((r) => r.isWs).length;
|
|
4980
|
+
if (filteredWs > 0) {
|
|
4981
|
+
console.log(
|
|
4982
|
+
pc__default.default.dim(` \u2514 ${pluralise(filteredWs, "WebSocket route")} included`)
|
|
4983
|
+
);
|
|
4984
|
+
}
|
|
4985
|
+
console.log();
|
|
4986
|
+
} catch (error) {
|
|
4987
|
+
console.error(pc__default.default.red("\u2717 Failed to inspect routes:"), error);
|
|
4988
|
+
process.exit(1);
|
|
4989
|
+
} finally {
|
|
4990
|
+
await cleanup();
|
|
3412
4991
|
}
|
|
3413
4992
|
}
|
|
4993
|
+
var VALID_METHODS = /* @__PURE__ */ new Set([
|
|
4994
|
+
"GET",
|
|
4995
|
+
"POST",
|
|
4996
|
+
"PUT",
|
|
4997
|
+
"PATCH",
|
|
4998
|
+
"DELETE",
|
|
4999
|
+
"OPTIONS",
|
|
5000
|
+
"HEAD",
|
|
5001
|
+
"WS"
|
|
5002
|
+
]);
|
|
5003
|
+
function pathToFilename(routePath) {
|
|
5004
|
+
const cleaned = routePath.split("/").filter(Boolean).map((seg) => seg.startsWith(":") ? `by-${seg.slice(1)}` : seg).join("-");
|
|
5005
|
+
return cleaned || "root";
|
|
5006
|
+
}
|
|
5007
|
+
function generateRouteSource(method, routePath, opts) {
|
|
5008
|
+
const isWs = method === "WS";
|
|
5009
|
+
const params = routePath.match(/:[a-zA-Z_][a-zA-Z0-9_]*/g) ?? [];
|
|
5010
|
+
const paramKeys = params.map((p) => p.slice(1));
|
|
5011
|
+
const plugins = [];
|
|
5012
|
+
const extraImports = [];
|
|
5013
|
+
if (opts.auth) {
|
|
5014
|
+
plugins.push("requireAuth");
|
|
5015
|
+
extraImports.push(
|
|
5016
|
+
`import { createAuthPlugin } from '@axiomify/auth';
|
|
5017
|
+
|
|
5018
|
+
const requireAuth = createAuthPlugin({
|
|
5019
|
+
secret: process.env.JWT_SECRET!,
|
|
5020
|
+
});`
|
|
5021
|
+
);
|
|
5022
|
+
}
|
|
5023
|
+
if (opts.rateLimit) {
|
|
5024
|
+
plugins.push("limiter");
|
|
5025
|
+
extraImports.push(
|
|
5026
|
+
`import { createRateLimitPlugin, MemoryStore } from '@axiomify/rate-limit';
|
|
5027
|
+
|
|
5028
|
+
// Replace MemoryStore with RedisStore for multi-process / multi-host.
|
|
5029
|
+
const limiter = createRateLimitPlugin({
|
|
5030
|
+
windowMs: 60_000,
|
|
5031
|
+
max: 100,
|
|
5032
|
+
store: new MemoryStore(),
|
|
5033
|
+
});`
|
|
5034
|
+
);
|
|
5035
|
+
}
|
|
5036
|
+
if (isWs) {
|
|
5037
|
+
return [
|
|
5038
|
+
`import type { Axiomify } from '@axiomify/core';`,
|
|
5039
|
+
`import { z } from 'zod';`,
|
|
5040
|
+
extraImports.length ? extraImports.join("\n\n") + "\n" : "",
|
|
5041
|
+
`/**`,
|
|
5042
|
+
` * Registers WebSocket route ${routePath}.`,
|
|
5043
|
+
` * Wire this into your entry file:`,
|
|
5044
|
+
` * import { registerRoute } from './routes/${pathToFilename(routePath)}';`,
|
|
5045
|
+
` * registerRoute(app);`,
|
|
5046
|
+
` */`,
|
|
5047
|
+
`export function registerRoute(app: Axiomify): void {`,
|
|
5048
|
+
` app.ws({`,
|
|
5049
|
+
` path: '${routePath}',`,
|
|
5050
|
+
paramKeys.length > 0 ? ` schema: {
|
|
5051
|
+
params: z.object({ ${paramKeys.map((k) => `${k}: z.string()`).join(", ")} }),
|
|
5052
|
+
// Define your message shape here \u2014 runtime-validated on every incoming frame.
|
|
5053
|
+
message: z.object({
|
|
5054
|
+
text: z.string(),
|
|
5055
|
+
}),
|
|
5056
|
+
},` : ` schema: {
|
|
5057
|
+
// Define your message shape here \u2014 runtime-validated on every incoming frame.
|
|
5058
|
+
message: z.object({
|
|
5059
|
+
text: z.string(),
|
|
5060
|
+
}),
|
|
5061
|
+
},`,
|
|
5062
|
+
plugins.length > 0 ? ` plugins: [${plugins.join(", ")}],` : "",
|
|
5063
|
+
` open: (client, _req) => {`,
|
|
5064
|
+
` client.send({ type: 'welcome' });`,
|
|
5065
|
+
` },`,
|
|
5066
|
+
` message: (client, data) => {`,
|
|
5067
|
+
` // \`data\` is typed and validated from the schema above.`,
|
|
5068
|
+
` client.send({ echo: data.text });`,
|
|
5069
|
+
` },`,
|
|
5070
|
+
` close: (_client, code, reason) => {`,
|
|
5071
|
+
` console.log('connection closed', code, reason);`,
|
|
5072
|
+
` },`,
|
|
5073
|
+
` });`,
|
|
5074
|
+
`}`,
|
|
5075
|
+
``
|
|
5076
|
+
].filter(Boolean).join("\n");
|
|
5077
|
+
}
|
|
5078
|
+
return [
|
|
5079
|
+
`import type { Axiomify } from '@axiomify/core';`,
|
|
5080
|
+
`import { z } from 'zod';`,
|
|
5081
|
+
extraImports.length ? extraImports.join("\n\n") + "\n" : "",
|
|
5082
|
+
`/**`,
|
|
5083
|
+
` * Registers ${method} ${routePath}.`,
|
|
5084
|
+
` * Wire this into your entry file:`,
|
|
5085
|
+
` * import { registerRoute } from './routes/${pathToFilename(routePath)}';`,
|
|
5086
|
+
` * registerRoute(app);`,
|
|
5087
|
+
` */`,
|
|
5088
|
+
`export function registerRoute(app: Axiomify): void {`,
|
|
5089
|
+
` app.route({`,
|
|
5090
|
+
` method: '${method}',`,
|
|
5091
|
+
` path: '${routePath}',`,
|
|
5092
|
+
` schema: {`,
|
|
5093
|
+
paramKeys.length > 0 ? ` params: z.object({ ${paramKeys.map((k) => `${k}: z.string()`).join(", ")} }),` : "",
|
|
5094
|
+
method === "POST" || method === "PUT" || method === "PATCH" ? ` body: z.object({
|
|
5095
|
+
// TODO \u2014 define request body shape
|
|
5096
|
+
}),` : "",
|
|
5097
|
+
` // response: z.object({ /* response shape */ }),`,
|
|
5098
|
+
` },`,
|
|
5099
|
+
` openapi: {`,
|
|
5100
|
+
` tags: ['${pathToFilename(routePath).split("-")[0] || "general"}'],`,
|
|
5101
|
+
` summary: '${method} ${routePath}',`,
|
|
5102
|
+
` },`,
|
|
5103
|
+
plugins.length > 0 ? ` plugins: [${plugins.join(", ")}],` : "",
|
|
5104
|
+
` handler: async (${paramKeys.length > 0 || method !== "GET" ? "req" : "_req"}, res) => {`,
|
|
5105
|
+
method === "POST" ? ` // TODO \u2014 handler logic
|
|
5106
|
+
res.status(201).send({ ok: true });` : method === "DELETE" ? ` // TODO \u2014 handler logic
|
|
5107
|
+
res.status(204).send(null);` : ` // TODO \u2014 handler logic
|
|
5108
|
+
res.send({ ok: true });`,
|
|
5109
|
+
` },`,
|
|
5110
|
+
` });`,
|
|
5111
|
+
`}`,
|
|
5112
|
+
``
|
|
5113
|
+
].filter(Boolean).join("\n");
|
|
5114
|
+
}
|
|
5115
|
+
async function scaffoldRoute(method, routePath, opts = {}) {
|
|
5116
|
+
const upperMethod = method.toUpperCase();
|
|
5117
|
+
if (!VALID_METHODS.has(upperMethod)) {
|
|
5118
|
+
console.error(
|
|
5119
|
+
pc__default.default.red(`\u2717 Invalid method "${method}".`),
|
|
5120
|
+
`Expected one of: ${[...VALID_METHODS].join(", ")}.`
|
|
5121
|
+
);
|
|
5122
|
+
process.exit(1);
|
|
5123
|
+
}
|
|
5124
|
+
if (!routePath.startsWith("/")) {
|
|
5125
|
+
console.error(
|
|
5126
|
+
pc__default.default.red('\u2717 Path must start with "/".'),
|
|
5127
|
+
`Got: ${routePath}`
|
|
5128
|
+
);
|
|
5129
|
+
process.exit(1);
|
|
5130
|
+
}
|
|
5131
|
+
const dir = opts.dir ?? "src/routes";
|
|
5132
|
+
const filename = pathToFilename(routePath) + ".ts";
|
|
5133
|
+
const fileAbs = path7__default.default.resolve(process.cwd(), dir, filename);
|
|
5134
|
+
const source = generateRouteSource(upperMethod, routePath, opts);
|
|
5135
|
+
if (opts.dryRun) {
|
|
5136
|
+
console.log(pc__default.default.dim(`# would write ${path7__default.default.relative(process.cwd(), fileAbs)}
|
|
5137
|
+
`));
|
|
5138
|
+
console.log(source);
|
|
5139
|
+
return;
|
|
5140
|
+
}
|
|
5141
|
+
let exists = false;
|
|
5142
|
+
try {
|
|
5143
|
+
await fs5__default.default.access(fileAbs);
|
|
5144
|
+
exists = true;
|
|
5145
|
+
} catch {
|
|
5146
|
+
}
|
|
5147
|
+
if (exists && !opts.force) {
|
|
5148
|
+
console.log(
|
|
5149
|
+
`${symbols.warn} ${pc__default.default.yellow("Already exists:")} ${path7__default.default.relative(process.cwd(), fileAbs)}
|
|
5150
|
+
Pass ${pc__default.default.cyan("--force")} to overwrite, or pick a different path.`
|
|
5151
|
+
);
|
|
5152
|
+
return;
|
|
5153
|
+
}
|
|
5154
|
+
await fs5__default.default.mkdir(path7__default.default.dirname(fileAbs), { recursive: true });
|
|
5155
|
+
await fs5__default.default.writeFile(fileAbs, source, "utf8");
|
|
5156
|
+
console.log();
|
|
5157
|
+
console.log(
|
|
5158
|
+
`${symbols.ok} ${pc__default.default.green("Created")} ${pc__default.default.cyan(path7__default.default.relative(process.cwd(), fileAbs))}`
|
|
5159
|
+
);
|
|
5160
|
+
console.log();
|
|
5161
|
+
console.log(pc__default.default.dim(" Next steps:"));
|
|
5162
|
+
console.log(
|
|
5163
|
+
` 1. Wire it into your entry file:
|
|
5164
|
+
` + pc__default.default.dim(` import { registerRoute } from './routes/${pathToFilename(routePath)}';
|
|
5165
|
+
`) + pc__default.default.dim(` registerRoute(app);`)
|
|
5166
|
+
);
|
|
5167
|
+
console.log(
|
|
5168
|
+
` 2. Fill in the TODOs in ${pc__default.default.cyan(path7__default.default.relative(process.cwd(), fileAbs))}.`
|
|
5169
|
+
);
|
|
5170
|
+
console.log(
|
|
5171
|
+
` 3. Verify with ${pc__default.default.cyan("npx axiomify routes")} and ${pc__default.default.cyan("npx axiomify check")}.`
|
|
5172
|
+
);
|
|
5173
|
+
console.log();
|
|
5174
|
+
}
|
|
3414
5175
|
|
|
3415
5176
|
// src/index.ts
|
|
3416
5177
|
var program2 = new Command();
|
|
@@ -3420,5 +5181,37 @@ program2.command("init").description("Bootstrap a new Axiomify project").argumen
|
|
|
3420
5181
|
);
|
|
3421
5182
|
program2.command("dev").description("Start the development server with hot-reload").argument("[entry]", "Entry file", "src/index.ts").action(devServer);
|
|
3422
5183
|
program2.command("build").description("Compile the application for production").argument("[entry]", "Entry file", "src/index.ts").action(buildProject);
|
|
3423
|
-
program2.command("routes").description("Inspect and list all registered
|
|
5184
|
+
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(
|
|
5185
|
+
"-m, --method <list>",
|
|
5186
|
+
"Comma-separated list of methods to include (e.g. GET,POST,WS)"
|
|
5187
|
+
).option(
|
|
5188
|
+
"-f, --filter <pattern>",
|
|
5189
|
+
'Path filter \u2014 substring match, or glob with "*" (e.g. /api/v1/*)'
|
|
5190
|
+
).option(
|
|
5191
|
+
"-s, --sort <by>",
|
|
5192
|
+
'Sort routes by "method" or "path"',
|
|
5193
|
+
"path"
|
|
5194
|
+
).action(
|
|
5195
|
+
(entry, options) => inspectRoutes(entry, options)
|
|
5196
|
+
);
|
|
5197
|
+
program2.command("openapi").description("Generate the OpenAPI spec from the app and emit it").argument("[entry]", "Entry file", "src/index.ts").option(
|
|
5198
|
+
"-o, --output <file>",
|
|
5199
|
+
"Write the spec to this file path instead of stdout"
|
|
5200
|
+
).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(
|
|
5201
|
+
(entry, options) => emitOpenApi(entry, {
|
|
5202
|
+
...options,
|
|
5203
|
+
// Map the CLI flag name (`spec-version` → camel `specVersion`) onto
|
|
5204
|
+
// the internal `version` field that emitOpenApi() expects.
|
|
5205
|
+
version: options.specVersion
|
|
5206
|
+
})
|
|
5207
|
+
);
|
|
5208
|
+
program2.command("check").description("Run a static production-readiness audit against the app").argument("[entry]", "Entry file", "src/index.ts").action(runCheck);
|
|
5209
|
+
program2.command("doctor").description("Diagnose the host environment (Node, uWS, ports, dep drift)").action(runDoctor);
|
|
5210
|
+
var scaffold = program2.command("scaffold").description("Generate boilerplate (routes, modules, plugins)");
|
|
5211
|
+
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(
|
|
5212
|
+
(method, routePath, options) => scaffoldRoute(method, routePath, options)
|
|
5213
|
+
);
|
|
5214
|
+
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(
|
|
5215
|
+
(options) => runMigrate(options)
|
|
5216
|
+
);
|
|
3424
5217
|
program2.parse(process.argv);
|