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