anl 26.806.0 → 26.921.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/config.d.ts CHANGED
@@ -3,6 +3,9 @@ export type TDatalevel = 'data' | 'serve' | 'axios';
3
3
  /** 终端日志输出级别 */
4
4
  export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'verbose';
5
5
 
6
+ /** 请求模板类型 —— 决定生成到项目 `<saveApiListFolderPath>/config/` 下的底层请求实现 */
7
+ export type RequestTemplate = 'axios' | 'fetch' | 'wx' | 'uniapp' | 'taro';
8
+
6
9
  /** 响应模型转换配置 */
7
10
  export interface IResponseModelTransform {
8
11
  /** 转换类型:unwrap-剔除响应模型,wrap-添加响应模型,replace-替换响应模型 */
@@ -79,6 +82,14 @@ export interface ConfigType {
79
82
  apiListFileName?: string;
80
83
  /** 请求方法导入路径 */
81
84
  requestMethodsImportPath: string;
85
+ /** 请求模板类型(决定生成到 `<saveApiListFolderPath>/config/` 下的底层 `dio.ts` 实现),默认 `'axios'`。
86
+ * - `axios` 基于 axios(浏览器/Node,需 `npm i axios`)
87
+ * - `fetch` 基于原生 fetch(浏览器/现代 Node,无额外依赖)
88
+ * - `wx` 基于 `wx.request`(微信小程序原生)
89
+ * - `uniapp` 基于 `uni.request`(uni-app 跨端)
90
+ * - `taro` 基于 `Taro.request`(Taro 3+ 跨端)
91
+ * 注意:切换模板时需手动删除 `<saveApiListFolderPath>/config/` 目录,CLI 才会重新写入对应模板文件。 */
92
+ requestTemplate?: RequestTemplate;
82
93
  /** 追加请求头 */
83
94
  headers?: Record<string, string>;
84
95
  /** 公共前缀(仅为兼容旧配置,最终以服务级配置为准) */
package/lib/index.cjs CHANGED
@@ -1 +1 @@
1
- const e=require(`./_virtual/rolldown_runtime.cjs`),t=require(`./package.cjs`),n=require(`./git-local-config/index.cjs`),r=require(`./skill-init/index.cjs`),i=require(`./standard/lint-init.cjs`),a=require(`./swagger-codegen/index.cjs`);let o=require(`commander`),s=require(`inquirer`);s=e.__toESM(s),o.program.version(`${t.version}`,`-v --version`).usage(`<command> [options]`),o.program.command(`type`).description(`auto interface`).option(`-s, --show <what>`,`show interface list after generation: miss | gen`).option(`-f, --format [config]`,`enable prettier formatting after generation; optionally specify a prettier config file path (e.g. --format .prettierrc.mjs)`).option(`-l, --log-level <level>`,`set log output level: silent | error | warn | info | verbose`).option(`-S, --service <names>`,"only regenerate the specified swagger service(s); comma-separated. Match by `name` (preferred) or `apiListFileName` without extension. Other services are kept untouched.").action(e=>{let t=(e.show??``).toLowerCase().trim(),n=t===`miss`||t===`missing`||t===`m`||t===`exclude`||t===`x`?`miss`:t===`gen`||t===`generated`||t===`g`||t===`include`||t===`i`?`gen`:void 0,r=e.service?e.service.split(`,`).map(e=>e.trim()).filter(Boolean):void 0;new a.Main().initialize(n,e.format,e.logLevel,r).catch(e=>{console.error(e)})}),o.program.command(`lint`).description(`install linting tools (eslint, stylelint, prettier, commitlint, vscode)`).action(()=>i.lintHandle()),o.program.command(`git`).description(`config git Local custom command`).action(async()=>{let{features:e}=await s.default.prompt([{type:`checkbox`,name:`features`,message:`Select the required Git features (multi-select):`,choices:[{name:`gitflow standard branch creation`,value:`gitflow`},{name:`automatically set commit subject`,value:`commitSubject`},{name:`custom git command`,value:`customGitCommand`}],pageSize:10}]);n.gitHandle(e).catch(e=>{console.error(e)})}),o.program.command(`skill`).description(`initialize an agent skill into the current project`).action(()=>{r.skillHandle().catch(e=>{console.error(e)})}),o.program.parse(process.argv);
1
+ const e=require(`./_virtual/rolldown_runtime.cjs`),t=require(`./package.cjs`),n=require(`./git-local-config/index.cjs`),r=require(`./skill-init/index.cjs`),i=require(`./standard/lint-init.cjs`),a=require(`./swagger-codegen/index.cjs`);let o=require(`commander`),s=require(`inquirer`);s=e.__toESM(s),o.program.version(`${t.version}`,`-v --version`).usage(`<command> [options]`),o.program.command(`type`).description(`auto interface`).option(`-s, --show <what>`,`show interface list after generation: miss | gen`).option(`-f, --format [config]`,`enable prettier formatting after generation; optionally specify a prettier config file path (e.g. --format .prettierrc.mjs)`).option(`-l, --log-level <level>`,`set log output level: silent | error | warn | info | verbose`).option(`-S, --service <names>`,"only regenerate the specified swagger service(s); comma-separated. Match by `name` (preferred) or `apiListFileName` without extension. Other services are kept untouched.").option(`-t, --template <name>`,`request template written to <saveApiListFolderPath>/config: axios | fetch | wx | uniapp | taro. Overrides an.config.ts. Only takes effect when the config folder does not exist yet.`).action(e=>{let t=(e.show??``).toLowerCase().trim(),n=t===`miss`||t===`missing`||t===`m`||t===`exclude`||t===`x`?`miss`:t===`gen`||t===`generated`||t===`g`||t===`include`||t===`i`?`gen`:void 0,r=e.service?e.service.split(`,`).map(e=>e.trim()).filter(Boolean):void 0;new a.Main().initialize(n,e.format,e.logLevel,r,e.template).catch(e=>{console.error(e)})}),o.program.command(`lint`).description(`install linting tools (eslint, stylelint, prettier, commitlint, vscode)`).action(()=>i.lintHandle()),o.program.command(`git`).description(`config git Local custom command`).action(async()=>{let{features:e}=await s.default.prompt([{type:`checkbox`,name:`features`,message:`Select the required Git features (multi-select):`,choices:[{name:`gitflow standard branch creation`,value:`gitflow`},{name:`automatically set commit subject`,value:`commitSubject`},{name:`custom git command`,value:`customGitCommand`}],pageSize:10}]);n.gitHandle(e).catch(e=>{console.error(e)})}),o.program.command(`skill`).description(`initialize an agent skill into the current project`).action(()=>{r.skillHandle().catch(e=>{console.error(e)})}),o.program.parse(process.argv);
package/lib/package.cjs CHANGED
@@ -1 +1 @@
1
- var e=`26.806.0`;Object.defineProperty(exports,`version`,{enumerable:!0,get:function(){return e}});
1
+ var e=`26.921.0`;Object.defineProperty(exports,`version`,{enumerable:!0,get:function(){return e}});
@@ -0,0 +1,52 @@
1
+ /**
2
+ * datalevel 说明:
3
+ * - 'data' 返回业务数据本体(ResponseModel.data)
4
+ * - 'serve' 返回完整业务响应体 ResponseModel<T>
5
+ * - 'axios' 返回底层适配器的原始响应对象 DioResponse<ResponseModel<T>>
6
+ * (命名沿用历史值以保持代码生成器一致,语义为"最底层原始响应")
7
+ */
8
+ type TDatalevel = 'data' | 'serve' | 'axios';
9
+
10
+ type RServe<T> = Promise<ResponseModel<T>>;
11
+ type RAxios<T> = Promise<DioResponse<ResponseModel<T>>>;
12
+
13
+ interface ResponseModel<T> {
14
+ code: number;
15
+ message: string;
16
+ data: T;
17
+ success: boolean;
18
+ }
19
+
20
+ /** 底层适配器统一响应结构(axios/fetch/wx/uni/taro 的共同抽象) */
21
+ interface DioResponse<T = unknown> {
22
+ data: T;
23
+ status: number;
24
+ statusText?: string;
25
+ headers?: Record<string, string>;
26
+ }
27
+
28
+ /** 底层适配器统一请求配置(axios/fetch/wx/uni/taro 的共同抽象) */
29
+ interface DioRequestConfig {
30
+ url?: string;
31
+ method?: 'GET' | 'DELETE' | 'PUT' | 'POST' | 'PATCH' | 'OPTIONS' | 'HEAD' | 'SEARCH' | Lowercase<'GET' | 'DELETE' | 'PUT' | 'POST' | 'PATCH' | 'OPTIONS' | 'HEAD' | 'SEARCH'>;
32
+ headers?: Record<string, string>;
33
+ timeout?: number;
34
+ signal?: AbortSignal;
35
+ responseType?: 'json' | 'text' | 'blob' | 'arrayBuffer';
36
+ [key: string]: unknown;
37
+ }
38
+
39
+ type IRequestFnParams = DioRequestConfig & {
40
+ query?: unknown;
41
+ body?: unknown;
42
+ };
43
+
44
+ interface IRequestFnRestParams {
45
+ config?: DioRequestConfig;
46
+ [key: string]: unknown;
47
+ }
48
+
49
+ interface ErrorPayload {
50
+ errors?: { message?: string }[];
51
+ [key: string]: unknown;
52
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 提示组件请按项目实际替换:
3
+ * - Web:可换成 antd / element-plus / naive-ui 的 message
4
+ * - 小程序/uni-app/Taro:可换成 wx.showToast / uni.showToast / Taro.showToast
5
+ * 这里保留一个最小实现(console.warn),避免默认强依赖任何提示库。
6
+ */
7
+ const message = {
8
+ info: (msg: string) => console.info('[tip]', msg),
9
+ warning: (msg: string) => console.warn('[warn]', msg),
10
+ error: (msg: string) => console.error('[error]', msg),
11
+ };
12
+
13
+ /** 跳过提示的 code 码 */
14
+ const skipTipCode: number[] = [];
15
+
16
+ /** 需要重写提示的 code 码 */
17
+ const rewriteCodeMessage = ({ code = 10000, message = '网络错误,稍后重试!' }: ResponseModel<unknown>) => {
18
+ switch (code) {
19
+ case 10000:
20
+ return { code, message: '系统错误,稍后重试!' };
21
+ default:
22
+ return { code, message };
23
+ }
24
+ };
25
+
26
+ export const messageTip = ({ data }: DioResponse<ResponseModel<unknown>>) => {
27
+ if (Object.prototype.toString.call(data) !== '[object Object]') return;
28
+ if (!('message' in data && 'code' in data)) return;
29
+
30
+ const { code, message: msg } = rewriteCodeMessage(data);
31
+
32
+ if (skipTipCode.includes(code)) return;
33
+
34
+ // 以下的代码是根据 code 码范围来显示不同的提示信息,请根据实际需求修改
35
+
36
+ /** 信息提示范围 */
37
+ if (code >= 2000 && code <= 3999) {
38
+ message.info(msg);
39
+ }
40
+
41
+ /** 警告提示范围 */
42
+ if (code > 3999 && code <= 4999) {
43
+ message.warning(msg);
44
+ }
45
+
46
+ /** 错误提示范围 */
47
+ if (code > 4999 && code <= 5999) {
48
+ message.error(msg);
49
+ }
50
+
51
+ /** 系统提示范围 */
52
+ if (code >= 9000) {
53
+ message.info(msg);
54
+ }
55
+ };
@@ -0,0 +1,208 @@
1
+ /**
2
+ * 只需要导出 GET DELETE PUT POST PATCH OPTIONS HEAD SEARCH 这些请求方法即可,
3
+ * 至于方法内使用 axios / fetch / wx / uni / taro 由 dio.ts 中的 dio 实例决定。
4
+ * 请注意方法的参数和返回值类型,请参考 dio.ts 中 dio 的类型定义;实现函数重载。
5
+ */
6
+
7
+ import { dio } from './dio';
8
+ import { messageTip } from './error-message';
9
+
10
+ function GET<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
11
+ function GET<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
12
+ function GET<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
13
+ function GET<R = unknown>(url: string, params: IRequestFnParams, datalevel: TDatalevel = 'serve') {
14
+ return dio
15
+ .request<ResponseModel<R>>({
16
+ ...params,
17
+ url,
18
+ query: params.query,
19
+ method: 'GET',
20
+ })
21
+ .then((res) => {
22
+ messageTip(res);
23
+ switch (datalevel) {
24
+ case 'data':
25
+ return res.data.data;
26
+ case 'serve':
27
+ return res.data;
28
+ case 'axios':
29
+ return res;
30
+ }
31
+ });
32
+ }
33
+
34
+ function DELETE<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
35
+ function DELETE<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
36
+ function DELETE<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
37
+ function DELETE<R = unknown>(url: string, params: IRequestFnParams, datalevel: TDatalevel = 'serve') {
38
+ return dio
39
+ .request<ResponseModel<R>>({
40
+ ...params,
41
+ url,
42
+ query: params.query,
43
+ method: 'DELETE',
44
+ })
45
+ .then((res) => {
46
+ messageTip(res);
47
+ switch (datalevel) {
48
+ case 'data':
49
+ return res.data.data;
50
+ case 'serve':
51
+ return res.data;
52
+ case 'axios':
53
+ return res;
54
+ }
55
+ });
56
+ }
57
+
58
+ function PUT<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
59
+ function PUT<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
60
+ function PUT<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
61
+ function PUT<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
62
+ return dio
63
+ .request<ResponseModel<R>>({
64
+ ...rest,
65
+ url,
66
+ query,
67
+ body,
68
+ method: 'PUT',
69
+ })
70
+ .then((res) => {
71
+ messageTip(res);
72
+ switch (datalevel) {
73
+ case 'data':
74
+ return res.data.data;
75
+ case 'serve':
76
+ return res.data;
77
+ case 'axios':
78
+ return res;
79
+ }
80
+ });
81
+ }
82
+
83
+ function POST<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
84
+ function POST<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
85
+ function POST<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
86
+ function POST<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
87
+ return dio
88
+ .request<ResponseModel<R>>({
89
+ ...rest,
90
+ url,
91
+ query,
92
+ body,
93
+ method: 'POST',
94
+ })
95
+ .then((res) => {
96
+ messageTip(res);
97
+ switch (datalevel) {
98
+ case 'data':
99
+ return res.data.data;
100
+ case 'serve':
101
+ return res.data;
102
+ case 'axios':
103
+ return res;
104
+ }
105
+ });
106
+ }
107
+
108
+ function PATCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
109
+ function PATCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
110
+ function PATCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
111
+ function PATCH<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
112
+ return dio
113
+ .request<ResponseModel<R>>({
114
+ ...rest,
115
+ url,
116
+ query,
117
+ body,
118
+ method: 'PATCH',
119
+ })
120
+ .then((res) => {
121
+ messageTip(res);
122
+ switch (datalevel) {
123
+ case 'data':
124
+ return res.data.data;
125
+ case 'serve':
126
+ return res.data;
127
+ case 'axios':
128
+ return res;
129
+ }
130
+ });
131
+ }
132
+
133
+ function OPTIONS<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
134
+ function OPTIONS<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
135
+ function OPTIONS<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
136
+ function OPTIONS<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
137
+ return dio
138
+ .request<ResponseModel<R>>({
139
+ ...rest,
140
+ url,
141
+ query,
142
+ body,
143
+ method: 'OPTIONS',
144
+ })
145
+ .then((res) => {
146
+ messageTip(res);
147
+ switch (datalevel) {
148
+ case 'data':
149
+ return res.data.data;
150
+ case 'serve':
151
+ return res.data;
152
+ case 'axios':
153
+ return res;
154
+ }
155
+ });
156
+ }
157
+
158
+ function HEAD<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
159
+ function HEAD<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
160
+ function HEAD<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
161
+ function HEAD<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
162
+ return dio
163
+ .request<ResponseModel<R>>({
164
+ ...rest,
165
+ url,
166
+ query,
167
+ body,
168
+ method: 'HEAD',
169
+ })
170
+ .then((res) => {
171
+ messageTip(res);
172
+ switch (datalevel) {
173
+ case 'data':
174
+ return res.data.data;
175
+ case 'serve':
176
+ return res.data;
177
+ case 'axios':
178
+ return res;
179
+ }
180
+ });
181
+ }
182
+
183
+ function SEARCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
184
+ function SEARCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
185
+ function SEARCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
186
+ function SEARCH<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
187
+ return dio
188
+ .request<ResponseModel<R>>({
189
+ ...rest,
190
+ url,
191
+ query,
192
+ body,
193
+ method: 'SEARCH',
194
+ })
195
+ .then((res) => {
196
+ messageTip(res);
197
+ switch (datalevel) {
198
+ case 'data':
199
+ return res.data.data;
200
+ case 'serve':
201
+ return res.data;
202
+ case 'axios':
203
+ return res;
204
+ }
205
+ });
206
+ }
207
+
208
+ export { DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, SEARCH };
@@ -0,0 +1,233 @@
1
+ /**
2
+ * fetch 版 dio 实例:基于 Web 原生 fetch,无外部依赖。
3
+ * 与 axios 版保持相同的对外形态:`dio.request(config)` 与 `dio.interceptors.request|response`。
4
+ */
5
+
6
+ // 环境变量参考(Vite):
7
+ // const { VITE_API_URL, VITE_API_PROXY, PROD } = import.meta.env;
8
+ // export const BASE_URL = PROD ? VITE_API_URL : VITE_API_PROXY;
9
+ export const BASE_URL = '';
10
+
11
+ type Interceptor<V> = {
12
+ onFulfilled?: (value: V) => V | Promise<V>;
13
+ onRejected?: (error: unknown) => unknown;
14
+ };
15
+
16
+ class InterceptorManager<V> {
17
+ private handlers: Interceptor<V>[] = [];
18
+ use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: unknown) => unknown): number {
19
+ this.handlers.push({ onFulfilled, onRejected });
20
+ return this.handlers.length - 1;
21
+ }
22
+ eject(id: number): void {
23
+ if (this.handlers[id]) this.handlers[id] = {};
24
+ }
25
+ forEach(cb: (h: Interceptor<V>) => void): void {
26
+ this.handlers.forEach((h) => h && cb(h));
27
+ }
28
+ }
29
+
30
+ const DEFAULT_TIMEOUT = 600000; // 10 分钟
31
+
32
+ function buildURL(base: string, url: string, query?: unknown): string {
33
+ const full = /^https?:\/\//i.test(url) ? url : `${base}${url}`;
34
+ if (!query || typeof query !== 'object') return full;
35
+ const usp = new URLSearchParams();
36
+ Object.entries(query as Record<string, unknown>).forEach(([k, v]) => {
37
+ if (v === undefined || v === null) return;
38
+ if (Array.isArray(v)) v.forEach((item) => usp.append(k, String(item)));
39
+ else usp.append(k, String(v));
40
+ });
41
+ const qs = usp.toString();
42
+ if (!qs) return full;
43
+ return full.includes('?') ? `${full}&${qs}` : `${full}?${qs}`;
44
+ }
45
+
46
+ function normalizeHeaders(h?: Record<string, string>): Record<string, string> {
47
+ return { 'Content-Type': 'application/json', ...(h ?? {}) };
48
+ }
49
+
50
+ async function coreRequest<T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
51
+ const { url = '', method = 'GET', query, body, headers, timeout = DEFAULT_TIMEOUT, signal, responseType = 'json' } = config;
52
+ const finalHeaders = normalizeHeaders(headers);
53
+
54
+ const controller = new AbortController();
55
+ const timerId = setTimeout(() => controller.abort(), timeout);
56
+ // 外部传入的 signal 优先,被 abort 时也要中断 fetch
57
+ if (signal) signal.addEventListener('abort', () => controller.abort(), { once: true });
58
+
59
+ const init: RequestInit = {
60
+ method: String(method).toUpperCase(),
61
+ headers: finalHeaders,
62
+ signal: controller.signal,
63
+ };
64
+
65
+ const upperMethod = init.method as string;
66
+ if (body !== undefined && upperMethod !== 'GET' && upperMethod !== 'HEAD') {
67
+ init.body = typeof body === 'string' || body instanceof FormData || body instanceof Blob ? (body as BodyInit) : JSON.stringify(body);
68
+ }
69
+
70
+ let response: Response;
71
+ try {
72
+ response = await fetch(buildURL(BASE_URL, url, query), init);
73
+ } finally {
74
+ clearTimeout(timerId);
75
+ }
76
+
77
+ let data: unknown;
78
+ if (responseType === 'text') data = await response.text();
79
+ else if (responseType === 'blob') data = await response.blob();
80
+ else if (responseType === 'arrayBuffer') data = await response.arrayBuffer();
81
+ else {
82
+ const text = await response.text();
83
+ try {
84
+ data = text ? JSON.parse(text) : null;
85
+ } catch {
86
+ data = text;
87
+ }
88
+ }
89
+
90
+ const respHeaders: Record<string, string> = {};
91
+ response.headers.forEach((v, k) => (respHeaders[k] = v));
92
+
93
+ const dioResponse: DioResponse<T> = {
94
+ data: data as T,
95
+ status: response.status,
96
+ statusText: response.statusText,
97
+ headers: respHeaders,
98
+ };
99
+
100
+ if (!response.ok) {
101
+ const err: Error & { response?: DioResponse<T>; config?: DioRequestConfig } = new Error(`Request failed with status ${response.status}`);
102
+ err.response = dioResponse;
103
+ err.config = config;
104
+ throw err;
105
+ }
106
+ return dioResponse;
107
+ }
108
+
109
+ const requestInterceptor = new InterceptorManager<DioRequestConfig & { query?: unknown; body?: unknown }>();
110
+ const responseInterceptor = new InterceptorManager<DioResponse<unknown>>();
111
+
112
+ async function runRequest<T>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
113
+ let current: DioRequestConfig & { query?: unknown; body?: unknown } = config;
114
+ const reqHandlers: Interceptor<typeof current>[] = [];
115
+ requestInterceptor.forEach((h) => reqHandlers.push(h));
116
+ for (const h of reqHandlers) {
117
+ try {
118
+ if (h.onFulfilled) current = await h.onFulfilled(current);
119
+ } catch (e) {
120
+ if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
121
+ throw e;
122
+ }
123
+ }
124
+
125
+ let value: DioResponse<T>;
126
+ try {
127
+ value = await coreRequest<T>(current);
128
+ } catch (e) {
129
+ let error: unknown = e;
130
+ const handlers: Interceptor<DioResponse<unknown>>[] = [];
131
+ responseInterceptor.forEach((h) => handlers.push(h));
132
+ for (const h of handlers) {
133
+ if (h.onRejected) {
134
+ try {
135
+ const recovered = await h.onRejected(error);
136
+ return recovered as DioResponse<T>;
137
+ } catch (next) {
138
+ error = next;
139
+ }
140
+ }
141
+ }
142
+ throw error;
143
+ }
144
+
145
+ let currentRes: DioResponse<unknown> = value;
146
+ const resHandlers: Interceptor<DioResponse<unknown>>[] = [];
147
+ responseInterceptor.forEach((h) => resHandlers.push(h));
148
+ for (const h of resHandlers) {
149
+ try {
150
+ if (h.onFulfilled) currentRes = await h.onFulfilled(currentRes);
151
+ } catch (e) {
152
+ if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
153
+ throw e;
154
+ }
155
+ }
156
+ return currentRes as DioResponse<T>;
157
+ }
158
+
159
+ export const dio = {
160
+ interceptors: {
161
+ request: requestInterceptor,
162
+ response: responseInterceptor,
163
+ },
164
+ request: <T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }) => runRequest<T>(config),
165
+ };
166
+
167
+ /**
168
+ * 请求拦截
169
+ */
170
+ dio.interceptors.request.use(
171
+ (config) => {
172
+ config.headers = { ...(config.headers ?? {}), Authorization: `Bearer 【auth -> replace】` };
173
+ return config;
174
+ },
175
+ (error) => {
176
+ console.error('request interceptors', error);
177
+ return error;
178
+ },
179
+ );
180
+
181
+ /**
182
+ * 响应拦截 —— 成功(2xx)
183
+ */
184
+ dio.interceptors.response.use(
185
+ (response) => response,
186
+ (rawError) => {
187
+ const error = rawError as Error & { response?: DioResponse<{ message?: string; data?: unknown }> };
188
+ if (!error.response) {
189
+ console.error('网络异常,请检查网络连接');
190
+ return Promise.reject(error);
191
+ }
192
+ const { status, data } = error.response;
193
+ const respData = (data ?? {}) as { message?: string; data?: unknown };
194
+ let errorMsg = respData.message ?? '请求失败,请稍后重试';
195
+ const payload = respData.data;
196
+ if (typeof payload === 'string') errorMsg = payload;
197
+ else if (payload && typeof payload === 'object' && 'errors' in payload) {
198
+ const errors = (payload as ErrorPayload).errors;
199
+ if (Array.isArray(errors)) {
200
+ const collected = errors.map((it) => it?.message).filter((m): m is string => Boolean(m));
201
+ if (collected.length) errorMsg = collected.join(';');
202
+ }
203
+ }
204
+
205
+ switch (status) {
206
+ case 400:
207
+ console.error(errorMsg);
208
+ break;
209
+ case 401:
210
+ console.error('登录过期,请重新登录');
211
+ if (typeof window !== 'undefined' && window.location.pathname !== '/login') window.location.href = '/login';
212
+ break;
213
+ case 403:
214
+ console.error('服务器拒绝请求');
215
+ break;
216
+ case 404:
217
+ console.error('资源未找到');
218
+ break;
219
+ case 500:
220
+ console.error('服务器内部错误');
221
+ break;
222
+ case 502:
223
+ console.error('网关或代理服务器收到无效响应');
224
+ break;
225
+ case 503:
226
+ console.error('服务器暂时无法处理请求');
227
+ break;
228
+ default:
229
+ console.error(errorMsg);
230
+ }
231
+ return Promise.reject(error);
232
+ },
233
+ );
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Taro 3+ 版 dio 实例:基于 Taro.request(跨端 H5/小程序/RN)。
3
+ * 与 axios 版保持相同的对外形态:`dio.request(config)` 与 `dio.interceptors.request|response`。
4
+ */
5
+
6
+ // Taro 环境变量:process.env.TARO_APP_XXX
7
+ export const BASE_URL = '';
8
+
9
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
10
+ // @ts-ignore -- 用户项目需安装 @tarojs/taro
11
+ import Taro from '@tarojs/taro';
12
+
13
+ type Interceptor<V> = {
14
+ onFulfilled?: (value: V) => V | Promise<V>;
15
+ onRejected?: (error: unknown) => unknown;
16
+ };
17
+
18
+ class InterceptorManager<V> {
19
+ private handlers: Interceptor<V>[] = [];
20
+ use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: unknown) => unknown): number {
21
+ this.handlers.push({ onFulfilled, onRejected });
22
+ return this.handlers.length - 1;
23
+ }
24
+ eject(id: number): void {
25
+ if (this.handlers[id]) this.handlers[id] = {};
26
+ }
27
+ forEach(cb: (h: Interceptor<V>) => void): void {
28
+ this.handlers.forEach((h) => h && cb(h));
29
+ }
30
+ }
31
+
32
+ const DEFAULT_TIMEOUT = 600000;
33
+
34
+ function buildURL(base: string, url: string, query?: unknown): string {
35
+ const full = /^https?:\/\//i.test(url) ? url : `${base}${url}`;
36
+ if (!query || typeof query !== 'object') return full;
37
+ const parts: string[] = [];
38
+ Object.entries(query as Record<string, unknown>).forEach(([k, v]) => {
39
+ if (v === undefined || v === null) return;
40
+ const enc = encodeURIComponent(k);
41
+ if (Array.isArray(v)) v.forEach((item) => parts.push(`${enc}=${encodeURIComponent(String(item))}`));
42
+ else parts.push(`${enc}=${encodeURIComponent(String(v))}`);
43
+ });
44
+ if (!parts.length) return full;
45
+ return full.includes('?') ? `${full}&${parts.join('&')}` : `${full}?${parts.join('&')}`;
46
+ }
47
+
48
+ async function coreRequest<T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
49
+ const { url = '', method = 'GET', query, body, headers, timeout = DEFAULT_TIMEOUT, responseType } = config;
50
+ const finalHeaders = { 'Content-Type': 'application/json', ...(headers ?? {}) };
51
+ const res = (await Taro.request({
52
+ url: buildURL(BASE_URL, url, query),
53
+ method: String(method).toUpperCase() as 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD',
54
+ data: body,
55
+ header: finalHeaders,
56
+ timeout,
57
+ responseType: responseType === 'arrayBuffer' ? 'arraybuffer' : undefined,
58
+ dataType: responseType && responseType !== 'json' ? '其他' : 'json',
59
+ })) as { data: unknown; statusCode: number; header: Record<string, string> };
60
+
61
+ const dioResponse: DioResponse<T> = {
62
+ data: res.data as T,
63
+ status: res.statusCode,
64
+ headers: res.header,
65
+ };
66
+ if (res.statusCode >= 200 && res.statusCode < 300) return dioResponse;
67
+
68
+ const err: Error & { response?: DioResponse<T>; config?: DioRequestConfig } = new Error(`Request failed with status ${res.statusCode}`);
69
+ err.response = dioResponse;
70
+ err.config = config;
71
+ throw err;
72
+ }
73
+
74
+ const requestInterceptor = new InterceptorManager<DioRequestConfig & { query?: unknown; body?: unknown }>();
75
+ const responseInterceptor = new InterceptorManager<DioResponse<unknown>>();
76
+
77
+ async function runRequest<T>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
78
+ let current: DioRequestConfig & { query?: unknown; body?: unknown } = config;
79
+ const reqHandlers: Interceptor<typeof current>[] = [];
80
+ requestInterceptor.forEach((h) => reqHandlers.push(h));
81
+ for (const h of reqHandlers) {
82
+ try {
83
+ if (h.onFulfilled) current = await h.onFulfilled(current);
84
+ } catch (e) {
85
+ if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
86
+ throw e;
87
+ }
88
+ }
89
+
90
+ let value: DioResponse<T>;
91
+ try {
92
+ value = await coreRequest<T>(current);
93
+ } catch (e) {
94
+ let error: unknown = e;
95
+ const handlers: Interceptor<DioResponse<unknown>>[] = [];
96
+ responseInterceptor.forEach((h) => handlers.push(h));
97
+ for (const h of handlers) {
98
+ if (h.onRejected) {
99
+ try {
100
+ const recovered = await h.onRejected(error);
101
+ return recovered as DioResponse<T>;
102
+ } catch (next) {
103
+ error = next;
104
+ }
105
+ }
106
+ }
107
+ throw error;
108
+ }
109
+
110
+ let currentRes: DioResponse<unknown> = value;
111
+ const resHandlers: Interceptor<DioResponse<unknown>>[] = [];
112
+ responseInterceptor.forEach((h) => resHandlers.push(h));
113
+ for (const h of resHandlers) {
114
+ try {
115
+ if (h.onFulfilled) currentRes = await h.onFulfilled(currentRes);
116
+ } catch (e) {
117
+ if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
118
+ throw e;
119
+ }
120
+ }
121
+ return currentRes as DioResponse<T>;
122
+ }
123
+
124
+ export const dio = {
125
+ interceptors: {
126
+ request: requestInterceptor,
127
+ response: responseInterceptor,
128
+ },
129
+ request: <T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }) => runRequest<T>(config),
130
+ };
131
+
132
+ /**
133
+ * 请求拦截
134
+ */
135
+ dio.interceptors.request.use(
136
+ (config) => {
137
+ config.headers = { ...(config.headers ?? {}), Authorization: `Bearer 【auth -> replace】` };
138
+ return config;
139
+ },
140
+ (error) => {
141
+ console.error('request interceptors', error);
142
+ return error;
143
+ },
144
+ );
145
+
146
+ /**
147
+ * 响应拦截 —— 成功(2xx)
148
+ */
149
+ dio.interceptors.response.use(
150
+ (response) => response,
151
+ (rawError) => {
152
+ const error = rawError as Error & { response?: DioResponse<{ message?: string; data?: unknown }> };
153
+ if (!error.response) {
154
+ console.error('网络异常,请检查网络连接');
155
+ return Promise.reject(error);
156
+ }
157
+ const { status, data } = error.response;
158
+ const respData = (data ?? {}) as { message?: string };
159
+ const errorMsg = respData.message ?? '请求失败,请稍后重试';
160
+
161
+ switch (status) {
162
+ case 400:
163
+ console.error(errorMsg);
164
+ break;
165
+ case 401:
166
+ console.error('登录过期,请重新登录');
167
+ Taro.reLaunch?.({ url: '/pages/login/index' });
168
+ break;
169
+ case 403:
170
+ console.error('服务器拒绝请求');
171
+ break;
172
+ case 404:
173
+ console.error('资源未找到');
174
+ break;
175
+ case 500:
176
+ console.error('服务器内部错误');
177
+ break;
178
+ default:
179
+ console.error(errorMsg);
180
+ }
181
+ return Promise.reject(error);
182
+ },
183
+ );
@@ -0,0 +1,201 @@
1
+ /**
2
+ * uni-app 版 dio 实例:基于 uni.request(跨端 H5/小程序/App)。
3
+ * 与 axios 版保持相同的对外形态:`dio.request(config)` 与 `dio.interceptors.request|response`。
4
+ * 若 TS 报 `Cannot find name 'uni'`,在项目执行:`npm i -D @dcloudio/types`,并在 tsconfig `types` 中加入 `@dcloudio/types`。
5
+ */
6
+
7
+ // uni-app 环境变量:process.env.VUE_APP_XXX / import.meta.env.VITE_XXX,根据你的构建配置选一种
8
+ export const BASE_URL = '';
9
+
10
+ type Interceptor<V> = {
11
+ onFulfilled?: (value: V) => V | Promise<V>;
12
+ onRejected?: (error: unknown) => unknown;
13
+ };
14
+
15
+ class InterceptorManager<V> {
16
+ private handlers: Interceptor<V>[] = [];
17
+ use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: unknown) => unknown): number {
18
+ this.handlers.push({ onFulfilled, onRejected });
19
+ return this.handlers.length - 1;
20
+ }
21
+ eject(id: number): void {
22
+ if (this.handlers[id]) this.handlers[id] = {};
23
+ }
24
+ forEach(cb: (h: Interceptor<V>) => void): void {
25
+ this.handlers.forEach((h) => h && cb(h));
26
+ }
27
+ }
28
+
29
+ const DEFAULT_TIMEOUT = 600000;
30
+
31
+ function buildURL(base: string, url: string, query?: unknown): string {
32
+ const full = /^https?:\/\//i.test(url) ? url : `${base}${url}`;
33
+ if (!query || typeof query !== 'object') return full;
34
+ const parts: string[] = [];
35
+ Object.entries(query as Record<string, unknown>).forEach(([k, v]) => {
36
+ if (v === undefined || v === null) return;
37
+ const enc = encodeURIComponent(k);
38
+ if (Array.isArray(v)) v.forEach((item) => parts.push(`${enc}=${encodeURIComponent(String(item))}`));
39
+ else parts.push(`${enc}=${encodeURIComponent(String(v))}`);
40
+ });
41
+ if (!parts.length) return full;
42
+ return full.includes('?') ? `${full}&${parts.join('&')}` : `${full}?${parts.join('&')}`;
43
+ }
44
+
45
+ declare const uni: {
46
+ request: (opts: {
47
+ url: string;
48
+ method?: string;
49
+ data?: unknown;
50
+ header?: Record<string, string>;
51
+ timeout?: number;
52
+ responseType?: string;
53
+ dataType?: string;
54
+ success?: (res: { data: unknown; statusCode: number; header: Record<string, string> }) => void;
55
+ fail?: (err: { errMsg: string }) => void;
56
+ }) => unknown;
57
+ reLaunch?: (opts: { url: string }) => void;
58
+ };
59
+
60
+ async function coreRequest<T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
61
+ const { url = '', method = 'GET', query, body, headers, timeout = DEFAULT_TIMEOUT, responseType } = config;
62
+ const finalHeaders = { 'Content-Type': 'application/json', ...(headers ?? {}) };
63
+ return new Promise<DioResponse<T>>((resolve, reject) => {
64
+ uni.request({
65
+ url: buildURL(BASE_URL, url, query),
66
+ method: String(method).toUpperCase(),
67
+ data: body as Record<string, unknown> | string | ArrayBuffer | undefined,
68
+ header: finalHeaders,
69
+ timeout,
70
+ responseType: responseType === 'arrayBuffer' ? 'arraybuffer' : undefined,
71
+ dataType: responseType && responseType !== 'json' ? '其他' : 'json',
72
+ success: (res) => {
73
+ const dioResponse: DioResponse<T> = {
74
+ data: res.data as T,
75
+ status: res.statusCode,
76
+ headers: res.header,
77
+ };
78
+ if (res.statusCode >= 200 && res.statusCode < 300) {
79
+ resolve(dioResponse);
80
+ } else {
81
+ const err: Error & { response?: DioResponse<T>; config?: DioRequestConfig } = new Error(`Request failed with status ${res.statusCode}`);
82
+ err.response = dioResponse;
83
+ err.config = config;
84
+ reject(err);
85
+ }
86
+ },
87
+ fail: (err) => reject(new Error(err.errMsg)),
88
+ });
89
+ });
90
+ }
91
+
92
+ const requestInterceptor = new InterceptorManager<DioRequestConfig & { query?: unknown; body?: unknown }>();
93
+ const responseInterceptor = new InterceptorManager<DioResponse<unknown>>();
94
+
95
+ async function runRequest<T>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
96
+ let current: DioRequestConfig & { query?: unknown; body?: unknown } = config;
97
+ const reqHandlers: Interceptor<typeof current>[] = [];
98
+ requestInterceptor.forEach((h) => reqHandlers.push(h));
99
+ for (const h of reqHandlers) {
100
+ try {
101
+ if (h.onFulfilled) current = await h.onFulfilled(current);
102
+ } catch (e) {
103
+ if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
104
+ throw e;
105
+ }
106
+ }
107
+
108
+ let value: DioResponse<T>;
109
+ try {
110
+ value = await coreRequest<T>(current);
111
+ } catch (e) {
112
+ let error: unknown = e;
113
+ const handlers: Interceptor<DioResponse<unknown>>[] = [];
114
+ responseInterceptor.forEach((h) => handlers.push(h));
115
+ for (const h of handlers) {
116
+ if (h.onRejected) {
117
+ try {
118
+ const recovered = await h.onRejected(error);
119
+ return recovered as DioResponse<T>;
120
+ } catch (next) {
121
+ error = next;
122
+ }
123
+ }
124
+ }
125
+ throw error;
126
+ }
127
+
128
+ let currentRes: DioResponse<unknown> = value;
129
+ const resHandlers: Interceptor<DioResponse<unknown>>[] = [];
130
+ responseInterceptor.forEach((h) => resHandlers.push(h));
131
+ for (const h of resHandlers) {
132
+ try {
133
+ if (h.onFulfilled) currentRes = await h.onFulfilled(currentRes);
134
+ } catch (e) {
135
+ if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
136
+ throw e;
137
+ }
138
+ }
139
+ return currentRes as DioResponse<T>;
140
+ }
141
+
142
+ export const dio = {
143
+ interceptors: {
144
+ request: requestInterceptor,
145
+ response: responseInterceptor,
146
+ },
147
+ request: <T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }) => runRequest<T>(config),
148
+ };
149
+
150
+ /**
151
+ * 请求拦截
152
+ */
153
+ dio.interceptors.request.use(
154
+ (config) => {
155
+ config.headers = { ...(config.headers ?? {}), Authorization: `Bearer 【auth -> replace】` };
156
+ return config;
157
+ },
158
+ (error) => {
159
+ console.error('request interceptors', error);
160
+ return error;
161
+ },
162
+ );
163
+
164
+ /**
165
+ * 响应拦截 —— 成功(2xx)
166
+ */
167
+ dio.interceptors.response.use(
168
+ (response) => response,
169
+ (rawError) => {
170
+ const error = rawError as Error & { response?: DioResponse<{ message?: string; data?: unknown }> };
171
+ if (!error.response) {
172
+ console.error('网络异常,请检查网络连接');
173
+ return Promise.reject(error);
174
+ }
175
+ const { status, data } = error.response;
176
+ const respData = (data ?? {}) as { message?: string };
177
+ const errorMsg = respData.message ?? '请求失败,请稍后重试';
178
+
179
+ switch (status) {
180
+ case 400:
181
+ console.error(errorMsg);
182
+ break;
183
+ case 401:
184
+ console.error('登录过期,请重新登录');
185
+ uni.reLaunch?.({ url: '/pages/login/index' });
186
+ break;
187
+ case 403:
188
+ console.error('服务器拒绝请求');
189
+ break;
190
+ case 404:
191
+ console.error('资源未找到');
192
+ break;
193
+ case 500:
194
+ console.error('服务器内部错误');
195
+ break;
196
+ default:
197
+ console.error(errorMsg);
198
+ }
199
+ return Promise.reject(error);
200
+ },
201
+ );
@@ -0,0 +1,201 @@
1
+ /**
2
+ * 微信小程序版 dio 实例:基于 wx.request,无外部依赖。
3
+ * 与 axios 版保持相同的对外形态:`dio.request(config)` 与 `dio.interceptors.request|response`。
4
+ * 若 TS 报 `Cannot find name 'wx'`,在项目执行:`npm i -D @types/wechat-miniprogram`,并在 tsconfig `types` 中加入 `wechat-miniprogram`。
5
+ */
6
+
7
+ // 小程序无环境变量惯例,可自行读取 __wxConfig 或在 getApp() 里注入
8
+ export const BASE_URL = '';
9
+
10
+ type Interceptor<V> = {
11
+ onFulfilled?: (value: V) => V | Promise<V>;
12
+ onRejected?: (error: unknown) => unknown;
13
+ };
14
+
15
+ class InterceptorManager<V> {
16
+ private handlers: Interceptor<V>[] = [];
17
+ use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: unknown) => unknown): number {
18
+ this.handlers.push({ onFulfilled, onRejected });
19
+ return this.handlers.length - 1;
20
+ }
21
+ eject(id: number): void {
22
+ if (this.handlers[id]) this.handlers[id] = {};
23
+ }
24
+ forEach(cb: (h: Interceptor<V>) => void): void {
25
+ this.handlers.forEach((h) => h && cb(h));
26
+ }
27
+ }
28
+
29
+ const DEFAULT_TIMEOUT = 600000;
30
+
31
+ function buildURL(base: string, url: string, query?: unknown): string {
32
+ const full = /^https?:\/\//i.test(url) ? url : `${base}${url}`;
33
+ if (!query || typeof query !== 'object') return full;
34
+ const parts: string[] = [];
35
+ Object.entries(query as Record<string, unknown>).forEach(([k, v]) => {
36
+ if (v === undefined || v === null) return;
37
+ const enc = encodeURIComponent(k);
38
+ if (Array.isArray(v)) v.forEach((item) => parts.push(`${enc}=${encodeURIComponent(String(item))}`));
39
+ else parts.push(`${enc}=${encodeURIComponent(String(v))}`);
40
+ });
41
+ if (!parts.length) return full;
42
+ return full.includes('?') ? `${full}&${parts.join('&')}` : `${full}?${parts.join('&')}`;
43
+ }
44
+
45
+ declare const wx: {
46
+ request: (opts: {
47
+ url: string;
48
+ method?: string;
49
+ data?: unknown;
50
+ header?: Record<string, string>;
51
+ timeout?: number;
52
+ responseType?: string;
53
+ dataType?: string;
54
+ success?: (res: { data: unknown; statusCode: number; header: Record<string, string> }) => void;
55
+ fail?: (err: { errMsg: string }) => void;
56
+ }) => unknown;
57
+ reLaunch?: (opts: { url: string }) => void;
58
+ };
59
+
60
+ async function coreRequest<T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
61
+ const { url = '', method = 'GET', query, body, headers, timeout = DEFAULT_TIMEOUT, responseType } = config;
62
+ const finalHeaders = { 'Content-Type': 'application/json', ...(headers ?? {}) };
63
+ return new Promise<DioResponse<T>>((resolve, reject) => {
64
+ wx.request({
65
+ url: buildURL(BASE_URL, url, query),
66
+ method: String(method).toUpperCase(),
67
+ data: body as Record<string, unknown> | string | ArrayBuffer | undefined,
68
+ header: finalHeaders,
69
+ timeout,
70
+ responseType: responseType === 'arrayBuffer' ? 'arraybuffer' : undefined,
71
+ dataType: responseType && responseType !== 'json' ? '其他' : 'json',
72
+ success: (res) => {
73
+ const dioResponse: DioResponse<T> = {
74
+ data: res.data as T,
75
+ status: res.statusCode,
76
+ headers: res.header,
77
+ };
78
+ if (res.statusCode >= 200 && res.statusCode < 300) {
79
+ resolve(dioResponse);
80
+ } else {
81
+ const err: Error & { response?: DioResponse<T>; config?: DioRequestConfig } = new Error(`Request failed with status ${res.statusCode}`);
82
+ err.response = dioResponse;
83
+ err.config = config;
84
+ reject(err);
85
+ }
86
+ },
87
+ fail: (err) => reject(new Error(err.errMsg)),
88
+ });
89
+ });
90
+ }
91
+
92
+ const requestInterceptor = new InterceptorManager<DioRequestConfig & { query?: unknown; body?: unknown }>();
93
+ const responseInterceptor = new InterceptorManager<DioResponse<unknown>>();
94
+
95
+ async function runRequest<T>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
96
+ let current: DioRequestConfig & { query?: unknown; body?: unknown } = config;
97
+ const reqHandlers: Interceptor<typeof current>[] = [];
98
+ requestInterceptor.forEach((h) => reqHandlers.push(h));
99
+ for (const h of reqHandlers) {
100
+ try {
101
+ if (h.onFulfilled) current = await h.onFulfilled(current);
102
+ } catch (e) {
103
+ if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
104
+ throw e;
105
+ }
106
+ }
107
+
108
+ let value: DioResponse<T>;
109
+ try {
110
+ value = await coreRequest<T>(current);
111
+ } catch (e) {
112
+ let error: unknown = e;
113
+ const handlers: Interceptor<DioResponse<unknown>>[] = [];
114
+ responseInterceptor.forEach((h) => handlers.push(h));
115
+ for (const h of handlers) {
116
+ if (h.onRejected) {
117
+ try {
118
+ const recovered = await h.onRejected(error);
119
+ return recovered as DioResponse<T>;
120
+ } catch (next) {
121
+ error = next;
122
+ }
123
+ }
124
+ }
125
+ throw error;
126
+ }
127
+
128
+ let currentRes: DioResponse<unknown> = value;
129
+ const resHandlers: Interceptor<DioResponse<unknown>>[] = [];
130
+ responseInterceptor.forEach((h) => resHandlers.push(h));
131
+ for (const h of resHandlers) {
132
+ try {
133
+ if (h.onFulfilled) currentRes = await h.onFulfilled(currentRes);
134
+ } catch (e) {
135
+ if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
136
+ throw e;
137
+ }
138
+ }
139
+ return currentRes as DioResponse<T>;
140
+ }
141
+
142
+ export const dio = {
143
+ interceptors: {
144
+ request: requestInterceptor,
145
+ response: responseInterceptor,
146
+ },
147
+ request: <T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }) => runRequest<T>(config),
148
+ };
149
+
150
+ /**
151
+ * 请求拦截
152
+ */
153
+ dio.interceptors.request.use(
154
+ (config) => {
155
+ config.headers = { ...(config.headers ?? {}), Authorization: `Bearer 【auth -> replace】` };
156
+ return config;
157
+ },
158
+ (error) => {
159
+ console.error('request interceptors', error);
160
+ return error;
161
+ },
162
+ );
163
+
164
+ /**
165
+ * 响应拦截 —— 成功(2xx)
166
+ */
167
+ dio.interceptors.response.use(
168
+ (response) => response,
169
+ (rawError) => {
170
+ const error = rawError as Error & { response?: DioResponse<{ message?: string; data?: unknown }> };
171
+ if (!error.response) {
172
+ console.error('网络异常,请检查网络连接');
173
+ return Promise.reject(error);
174
+ }
175
+ const { status, data } = error.response;
176
+ const respData = (data ?? {}) as { message?: string };
177
+ const errorMsg = respData.message ?? '请求失败,请稍后重试';
178
+
179
+ switch (status) {
180
+ case 400:
181
+ console.error(errorMsg);
182
+ break;
183
+ case 401:
184
+ console.error('登录过期,请重新登录');
185
+ wx.reLaunch?.({ url: '/pages/login/index' });
186
+ break;
187
+ case 403:
188
+ console.error('服务器拒绝请求');
189
+ break;
190
+ case 404:
191
+ console.error('资源未找到');
192
+ break;
193
+ case 500:
194
+ console.error('服务器内部错误');
195
+ break;
196
+ default:
197
+ console.error(errorMsg);
198
+ }
199
+ return Promise.reject(error);
200
+ },
201
+ );
@@ -1,10 +1,10 @@
1
- const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../utils/logger.cjs`),n=require(`../utils/index.cjs`),r=require(`./shared/naming.cjs`),i=require(`./components/index.cjs`),a=require(`./get-data.cjs`),o=require(`./path/index.cjs`);let s=require(`inquirer`);s=e.__toESM(s);let c=require(`fs`);c=e.__toESM(c);let l=require(`path`);l=e.__toESM(l);let u=require(`shelljs`),d=require(`chalk`);d=e.__toESM(d);let f=require(`jiti`),p;const m=process.env.NODE_ENV===`debug`,h={saveTypeFolderPath:m?`apps/types`:`src/types`,saveApiListFolderPath:m?`apps/types`:`src/apis`,saveEnumFolderPath:m?`apps/enums`:`src/enums`,importEnumPath:`../../../enums`,requestMethodsImportPath:`./config/fetch`,formatting:{indentation:` `,lineEnding:`
2
- `},swaggerConfig:{url:`https://generator3.swagger.io/openapi.json`,apiListFileName:`index.ts`,headers:{},dataLevel:`serve`,parameterSeparator:`_`,includeInterface:[],excludeInterface:[]},enmuConfig:{erasableSyntaxOnly:!1,varnames:`enum-varnames`,comment:`enum-descriptions`}};var g=class{schemas={};paths={};async handle(e,n,s){let c=r.getServiceTag(e);t.log.section(c||`service`,e.swaggerJsonUrl);try{t.spinner.start(`Fetching Swagger data...`);let r=await a.getSwaggerJson(e);if(!r)throw t.spinner.error(`Failed to fetch Swagger data`),Error(`无法获取 Swagger 数据`);t.spinner.success(`Swagger data fetched`),this.schemas=r.components?.schemas??{},this.paths=r.paths??{};let c=new i.default(this.schemas,e,{appendMode:n}),l=new o.default(this.paths,r.components?.parameters,this.schemas,e);return t.spinner.start(`Generating types and APIs...`),await c.handle(),await l.handle(),t.spinner.success(`Types and APIs generated`),s===`gen`?l.getGeneratedInterfacesForOutput():s===`miss`?l.getMissingInterfacesForOutput():null}catch(e){throw e instanceof Error?Error(`Handle Swagger data failed: ${e.message}`):Error(`Handle Swagger data failed: unknown error`)}}async resolvePrettierExecutable(){let e=process.platform===`win32`,n=l.default.join(process.cwd(),`node_modules`,`.bin`,e?`prettier.cmd`:`prettier`);try{return await c.default.promises.access(n,c.default.constants.X_OK),t.log.info(`Using local prettier: ${n}`),`"${n}"`}catch{return`npx prettier`}}async detectPrettierConfig(){for(let e of[`.prettierrc`,`.prettierrc.json`,`.prettierrc.json5`,`.prettierrc.yaml`,`.prettierrc.yml`,`.prettierrc.js`,`.prettierrc.cjs`,`.prettierrc.mjs`,`.prettierrc.ts`,`.prettierrc.cts`,`.prettierrc.mts`,`prettier.config.js`,`prettier.config.cjs`,`prettier.config.mjs`,`prettier.config.ts`,`prettier.config.cts`,`prettier.config.mts`]){let n=l.default.join(process.cwd(),e);try{return await c.default.promises.access(n),t.log.info(`Auto-detected prettier config: ${e}`),n}catch{}}try{let e=await c.default.promises.readFile(l.default.join(process.cwd(),`package.json`),`utf8`);if(JSON.parse(e).prettier)return t.log.info(`Using prettier config from package.json`),l.default.join(process.cwd(),`package.json`)}catch{}return null}async formatGeneratedFiles(e,n){let r=await this.resolvePrettierExecutable(),i=``;if(typeof n==`string`&&n.trim()){let e=l.default.resolve(process.cwd(),n.trim());try{await c.default.promises.access(e),i=` --config "${e}"`}catch{t.log.warning(`Prettier config file not found: ${n}, falling back to auto-detection...`);let e=await this.detectPrettierConfig();e&&(i=` --config "${e}"`)}}else{let e=await this.detectPrettierConfig();e&&(i=` --config "${e}"`)}let a=[],o=async(e,t)=>{try{await c.default.promises.access(e),a.push(`"${e}/${t}"`)}catch{}};if(await o(e.saveTypeFolderPath,`**/*.{ts,d.ts}`),await o(e.saveApiListFolderPath,`**/*.ts`),await o(e.saveEnumFolderPath,`**/*.ts`),a.length===0){t.log.warning(`No generated directories found to format.`);return}let s=`${r} --write ${a.join(` `)}${i}`;try{t.spinner.start(`Formatting generated files...`);let{stderr:e}=await new Promise((e,t)=>{(0,u.exec)(s,(n,r,i)=>{n?t(Error(String(n))):e({stdout:r,stderr:i})})});e&&(t.log.print(`
1
+ const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../utils/logger.cjs`),n=require(`../utils/index.cjs`),r=require(`./shared/naming.cjs`),i=require(`./components/index.cjs`),a=require(`./get-data.cjs`),o=require(`./path/index.cjs`);let s=require(`inquirer`);s=e.__toESM(s);let c=require(`fs`);c=e.__toESM(c);let l=require(`path`);l=e.__toESM(l);let u=require(`shelljs`),d=require(`chalk`);d=e.__toESM(d);let f=require(`jiti`),p;const m=process.env.NODE_ENV===`debug`,h=[`axios`,`fetch`,`wx`,`uniapp`,`taro`],g=`axios`,_=[`dio.ts`,`error-message.ts`,`fetch.ts`,`api-type.d.ts`],v=/@an-cli-request-template:\s*([a-z]+)/i;function y(e,t){if(e==null||e===``)return g;if(typeof e!=`string`)throw Error(`${t} 中的 requestTemplate 类型无效(期望字符串,实际 ${typeof e}),支持的值:${h.join(` | `)}`);let n=e.trim().toLowerCase();if(!h.includes(n))throw Error(`${t} 中的 requestTemplate="${e}" 无效,支持的值:${h.join(` | `)}`);return n}const b={saveTypeFolderPath:m?`apps/types`:`src/types`,saveApiListFolderPath:m?`apps/types`:`src/apis`,saveEnumFolderPath:m?`apps/enums`:`src/enums`,importEnumPath:`../../../enums`,requestMethodsImportPath:`./config/fetch`,requestTemplate:g,formatting:{indentation:` `,lineEnding:`
2
+ `},swaggerConfig:{url:`https://generator3.swagger.io/openapi.json`,apiListFileName:`index.ts`,headers:{},dataLevel:`serve`,parameterSeparator:`_`,includeInterface:[],excludeInterface:[]},enmuConfig:{erasableSyntaxOnly:!1,varnames:`enum-varnames`,comment:`enum-descriptions`}};var x=class{schemas={};paths={};async handle(e,n,s){let c=r.getServiceTag(e);t.log.section(c||`service`,e.swaggerJsonUrl);try{t.spinner.start(`Fetching Swagger data...`);let r=await a.getSwaggerJson(e);if(!r)throw t.spinner.error(`Failed to fetch Swagger data`),Error(`无法获取 Swagger 数据`);t.spinner.success(`Swagger data fetched`),this.schemas=r.components?.schemas??{},this.paths=r.paths??{};let c=new i.default(this.schemas,e,{appendMode:n}),l=new o.default(this.paths,r.components?.parameters,this.schemas,e);return t.spinner.start(`Generating types and APIs...`),await c.handle(),await l.handle(),t.spinner.success(`Types and APIs generated`),s===`gen`?l.getGeneratedInterfacesForOutput():s===`miss`?l.getMissingInterfacesForOutput():null}catch(e){throw e instanceof Error?Error(`Handle Swagger data failed: ${e.message}`):Error(`Handle Swagger data failed: unknown error`)}}async resolvePrettierExecutable(){let e=process.platform===`win32`,n=l.default.join(process.cwd(),`node_modules`,`.bin`,e?`prettier.cmd`:`prettier`);try{return await c.default.promises.access(n,c.default.constants.X_OK),t.log.info(`Using local prettier: ${n}`),`"${n}"`}catch{return`npx prettier`}}async detectPrettierConfig(){for(let e of[`.prettierrc`,`.prettierrc.json`,`.prettierrc.json5`,`.prettierrc.yaml`,`.prettierrc.yml`,`.prettierrc.js`,`.prettierrc.cjs`,`.prettierrc.mjs`,`.prettierrc.ts`,`.prettierrc.cts`,`.prettierrc.mts`,`prettier.config.js`,`prettier.config.cjs`,`prettier.config.mjs`,`prettier.config.ts`,`prettier.config.cts`,`prettier.config.mts`]){let n=l.default.join(process.cwd(),e);try{return await c.default.promises.access(n),t.log.info(`Auto-detected prettier config: ${e}`),n}catch{}}try{let e=await c.default.promises.readFile(l.default.join(process.cwd(),`package.json`),`utf8`);if(JSON.parse(e).prettier)return t.log.info(`Using prettier config from package.json`),l.default.join(process.cwd(),`package.json`)}catch{}return null}async formatGeneratedFiles(e,n){let r=await this.resolvePrettierExecutable(),i=``;if(typeof n==`string`&&n.trim()){let e=l.default.resolve(process.cwd(),n.trim());try{await c.default.promises.access(e),i=` --config "${e}"`}catch{t.log.warning(`Prettier config file not found: ${n}, falling back to auto-detection...`);let e=await this.detectPrettierConfig();e&&(i=` --config "${e}"`)}}else{let e=await this.detectPrettierConfig();e&&(i=` --config "${e}"`)}let a=[],o=async(e,t)=>{try{await c.default.promises.access(e),a.push(`"${e}/${t}"`)}catch{}};if(await o(e.saveTypeFolderPath,`**/*.{ts,d.ts}`),await o(e.saveApiListFolderPath,`**/*.ts`),await o(e.saveEnumFolderPath,`**/*.ts`),a.length===0){t.log.warning(`No generated directories found to format.`);return}let s=`${r} --write ${a.join(` `)}${i}`;try{t.spinner.start(`Formatting generated files...`);let{stderr:e}=await new Promise((e,t)=>{(0,u.exec)(s,(n,r,i)=>{n?t(Error(String(n))):e({stdout:r,stderr:i})})});e&&(t.log.print(`
3
3
  `),t.log.print(`$`,d.default.yellow(s)),t.log.print(`
4
4
  `)),t.spinner.success(`File formatting successful`),t.log.print(`
5
- `)}catch(e){t.spinner.error(`Format failed`),t.log.print(``),t.log.print(e),t.log.error(`Format failed, please manually execute the following command:`),t.log.print(`$`,d.default.yellow(s)),t.log.print(``)}}async copyAjaxConfigFiles(e){let n=[`dio.ts`,`error-message.ts`,`fetch.ts`,`api-type.d.ts`],r=m?l.default.join(__dirname,`..`,`..`,`postbuild-assets`,`ajax-config`):l.default.join(__dirname,`..`,`ajax-config`),i=l.default.join(e,`config`);try{await c.default.promises.access(i),t.log.info(`config folder already exists at ${i}, skipping generation.`);return}catch{await c.default.promises.mkdir(i,{recursive:!0})}for(let e of n){let n=l.default.join(r,e),a=l.default.join(i,e);try{await c.default.promises.access(n),await c.default.promises.copyFile(n,a),t.log.success(`${e} create done.`)}catch(e){throw e instanceof Error?(t.log.error(`Source file ${n} does not exist`),Error(`Source file ${n} does not exist: ${e.message}`)):Error(`Source file ${n} does not exist: unknown error`)}}}getSystemLocale(){try{return Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase()}catch{return(process.env.LANG??process.env.LC_ALL??process.env.LC_MESSAGES??``).toLowerCase()}}showLegacyConfigHint(e){let n={url:e.swaggerJsonUrl??`https://your.swagger.json`,publicPrefix:e.publicPrefix??``,apiListFileName:e.apiListFileName??`index.ts`,headers:e.headers??{}},r=this.getSystemLocale();r.startsWith(`zh`)||r.includes(`chinese`)?(t.log.print(`
5
+ `)}catch(e){t.spinner.error(`Format failed`),t.log.print(``),t.log.print(e),t.log.error(`Format failed, please manually execute the following command:`),t.log.print(`$`,d.default.yellow(s)),t.log.print(``)}}async copyAjaxConfigFiles(e,n){let r=m?l.default.join(__dirname,`..`,`..`,`postbuild-assets`,`request-templates`):l.default.join(__dirname,`..`,`request-templates`),i=l.default.join(r,n),a=l.default.join(r,`_shared`),o=l.default.join(e,`config`);try{await c.default.promises.access(o);let e=await this.readExistingTemplateMarker(o);e&&e!==n?t.log.warning(`config folder exists at ${o}, but its embedded marker "${e}" differs from configured requestTemplate="${n}". If you want to switch, delete the config folder first: rm -rf "${o}"`):(t.log.info(`config folder already exists at ${o}, skipping generation. (template=${n})`),t.log.info(` If you want to switch template, please delete the config folder first: rm -rf "${o}"`));return}catch{}let s=[];for(let e of _){let t=l.default.join(i,e),r=l.default.join(a,e),o=null;try{await c.default.promises.access(t),o=t}catch{try{await c.default.promises.access(r),o=r}catch{throw Error(`Source file not found for template "${n}": ${e} (looked in ${i} and ${a})`)}}s.push({file:e,sourceFile:o})}await c.default.promises.mkdir(o,{recursive:!0}),t.log.info(`Using request template: ${d.default.cyan(n)} (template dir: ${i}, shared dir: ${a})`);try{for(let{file:e,sourceFile:r}of s){let i=l.default.join(o,e);if(e===`dio.ts`){let e=await c.default.promises.readFile(r,`utf8`),t=v.test(e)?e:`// @an-cli-request-template: ${n} — 切换请求模板前请删除本 config/ 目录后重新运行 anl type\n${e}`;await c.default.promises.writeFile(i,t)}else await c.default.promises.copyFile(r,i);t.log.success(`${e} create done.`)}}catch(e){throw await c.default.promises.rm(o,{recursive:!0,force:!0}).catch(()=>void 0),e instanceof Error?e:Error(String(e))}}async readExistingTemplateMarker(e){try{let t=await c.default.promises.readFile(l.default.join(e,`dio.ts`),`utf8`),n=v.exec(t.slice(0,500));return n?n[1].toLowerCase():null}catch{return null}}getSystemLocale(){try{return Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase()}catch{return(process.env.LANG??process.env.LC_ALL??process.env.LC_MESSAGES??``).toLowerCase()}}showLegacyConfigHint(e){let n={url:e.swaggerJsonUrl??`https://your.swagger.json`,publicPrefix:e.publicPrefix??``,apiListFileName:e.apiListFileName??`index.ts`,headers:e.headers??{}},r=this.getSystemLocale();r.startsWith(`zh`)||r.includes(`chinese`)?(t.log.print(`
6
6
  检测到旧版配置,请更新 an.config.json:`),t.log.print(`1) 将 swaggerJsonUrl / publicPrefix / headers 移到 swaggerConfig 字段。`),t.log.print(`2) 单个服务可直接填写对象,多个服务请使用数组,并确保 apiListFileName 唯一。`),t.log.print(`示例:`),t.log.print(JSON.stringify({swaggerConfig:n},null,2)),t.log.print(``)):(t.log.print(`
7
- Legacy configuration detected, please update an.config.json:`),t.log.print(`1) Move swaggerJsonUrl / publicPrefix / headers to swaggerConfig field.`),t.log.print(`2) Single service can be an object directly, multiple services should use an array, and ensure apiListFileName is unique.`),t.log.print(`Example:`),t.log.print(JSON.stringify({swaggerConfig:n},null,2)),t.log.print(``))}normalizeswaggerConfig(e,t){let n=!1,r=t?e.swaggerConfig:void 0;r||=(n=!0,{url:e.swaggerJsonUrl??``,publicPrefix:e.publicPrefix??``,apiListFileName:e.apiListFileName??`index.ts`,headers:e.headers??{},modulePrefix:e.modulePrefix});let i=(t,r)=>{let i=t.url||e.swaggerJsonUrl;if(!i)throw Error(`swaggerConfig[${r}] 缺少 url,请补充后重试。`);let a=t.publicPrefix??e.publicPrefix??``;!t.url&&e.swaggerJsonUrl&&(n=!0);let o=(t.apiListFileName??e.apiListFileName??`index.ts`).trim()||`index.ts`;if(/[\\/]/.test(o)||o.includes(`..`))throw Error(`swaggerConfig[${r}].apiListFileName 非法:"${o}",请使用单个文件名(如 "bff.ts"),不要包含路径分隔符或 ".."。`);let s=t.headers??e.headers??{},c=t.dataLevel??e.dataLevel??`serve`,l=t.parameterSeparator??e.parameterSeparator??`_`,u=t.includeInterface??e.includeInterface??[],d=t.excludeInterface??e.excludeInterface??[],f=t.includeTags??e.includeTags,p=t.excludeTags??e.excludeTags,m={url:i,publicPrefix:a,apiListFileName:o,headers:s,dataLevel:c,parameterSeparator:l,includeInterface:u,excludeInterface:d,modulePrefix:t.modulePrefix??e.modulePrefix??``,responseModelTransform:t.responseModelTransform??e.responseModelTransform,timeout:t.timeout??e.timeout,namespaceIsolation:t.namespaceIsolation??e.namespaceIsolation??`segment`,enumIsolation:t.enumIsolation??e.enumIsolation??`segment`};return t.name?.trim()&&(m.name=t.name.trim()),f!==void 0&&(m.includeTags=f),p!==void 0&&(m.excludeTags=p),m},a=Array.isArray(r)?r.map((e,t)=>i(e,t)):[i(r,0)];if(a.length===0)throw Error(`swaggerConfig 不能为空,请至少配置一个 swagger 服务。`);if(a.length>1){let e=new Set;a.forEach(t=>{if(e.has(t.apiListFileName))throw Error(`swaggerConfig 中 apiListFileName 重复:${t.apiListFileName},请为每个服务设置唯一文件名。`);e.add(t.apiListFileName)});let t=new Set;a.forEach(e=>{if(e.name){if(t.has(e.name))throw Error(`swaggerConfig 中 name 重复:${e.name},请为每个服务设置唯一名称。`);t.add(e.name)}})}return n&&this.showLegacyConfigHint(e),a}buildServerConfig(e,t,n,r){return{...e,swaggerJsonUrl:t.url,publicPrefix:t.publicPrefix??e.publicPrefix,headers:t.headers,apiListFileName:t.apiListFileName,dataLevel:t.dataLevel,parameterSeparator:t.parameterSeparator,includeInterface:t.includeInterface,excludeInterface:t.excludeInterface,includeTags:t.includeTags,excludeTags:t.excludeTags,modulePrefix:t.modulePrefix,responseModelTransform:t.responseModelTransform??e.responseModelTransform,timeout:t.timeout??e.timeout,namespaceIsolation:t.namespaceIsolation??e.namespaceIsolation??`segment`,enumIsolation:t.enumIsolation??e.enumIsolation??`segment`,swaggerConfig:t,__segment:n,__namespacePrefix:r}}async loadTsConfig(e){try{let t=await(0,f.createJiti)(__filename,{interopDefault:!0}).import(e),n=`default`in t&&t.default?t.default:t;return p=!0,n}catch(e){p=!0;let t=e instanceof Error?e.message:String(e);throw Error(`配置文件加载失败,请检查 an.config.ts 文件: ${t}`)}}async loadJsonConfig(e){try{let t=await c.default.promises.readFile(e,`utf8`);p=!0;try{return JSON.parse(t)}catch(e){throw p=!0,Error(`配置文件格式错误,请检查 an.config.json 的 JSON 格式是否正确: ${e instanceof Error?e.message:String(e)}`)}}catch(e){throw e instanceof Error&&`code`in e&&e.code,e}}async getConfig(e){let r=l.default.join(e,`an.config.ts`),i=l.default.join(e,`an.config.json`);return c.default.existsSync(r)?(t.log.info(`检测到 an.config.ts 配置文件。`),this.loadTsConfig(r)):c.default.existsSync(i)?this.loadJsonConfig(i):(p=!1,t.log.warning(`配置文件不存在,将自动创建配置文件。`),await n.writeFileRecursive(r,this.generateTsConfigContent()),t.log.success(`配置文件已创建,请检查项目根目录下的 an.config.ts 文件并配置后重新运行。`),h)}generateTsConfigContent(){return`import { defineConfig } from 'anl/config';
7
+ Legacy configuration detected, please update an.config.json:`),t.log.print(`1) Move swaggerJsonUrl / publicPrefix / headers to swaggerConfig field.`),t.log.print(`2) Single service can be an object directly, multiple services should use an array, and ensure apiListFileName is unique.`),t.log.print(`Example:`),t.log.print(JSON.stringify({swaggerConfig:n},null,2)),t.log.print(``))}normalizeswaggerConfig(e,t){let n=!1,r=t?e.swaggerConfig:void 0;r||=(n=!0,{url:e.swaggerJsonUrl??``,publicPrefix:e.publicPrefix??``,apiListFileName:e.apiListFileName??`index.ts`,headers:e.headers??{},modulePrefix:e.modulePrefix});let i=(t,r)=>{let i=t.url||e.swaggerJsonUrl;if(!i)throw Error(`swaggerConfig[${r}] 缺少 url,请补充后重试。`);let a=t.publicPrefix??e.publicPrefix??``;!t.url&&e.swaggerJsonUrl&&(n=!0);let o=(t.apiListFileName??e.apiListFileName??`index.ts`).trim()||`index.ts`;if(/[\\/]/.test(o)||o.includes(`..`))throw Error(`swaggerConfig[${r}].apiListFileName 非法:"${o}",请使用单个文件名(如 "bff.ts"),不要包含路径分隔符或 ".."。`);let s=t.headers??e.headers??{},c=t.dataLevel??e.dataLevel??`serve`,l=t.parameterSeparator??e.parameterSeparator??`_`,u=t.includeInterface??e.includeInterface??[],d=t.excludeInterface??e.excludeInterface??[],f=t.includeTags??e.includeTags,p=t.excludeTags??e.excludeTags,m={url:i,publicPrefix:a,apiListFileName:o,headers:s,dataLevel:c,parameterSeparator:l,includeInterface:u,excludeInterface:d,modulePrefix:t.modulePrefix??e.modulePrefix??``,responseModelTransform:t.responseModelTransform??e.responseModelTransform,timeout:t.timeout??e.timeout,namespaceIsolation:t.namespaceIsolation??e.namespaceIsolation??`segment`,enumIsolation:t.enumIsolation??e.enumIsolation??`segment`};return t.name?.trim()&&(m.name=t.name.trim()),f!==void 0&&(m.includeTags=f),p!==void 0&&(m.excludeTags=p),m},a=Array.isArray(r)?r.map((e,t)=>i(e,t)):[i(r,0)];if(a.length===0)throw Error(`swaggerConfig 不能为空,请至少配置一个 swagger 服务。`);if(a.length>1){let e=new Set;a.forEach(t=>{if(e.has(t.apiListFileName))throw Error(`swaggerConfig 中 apiListFileName 重复:${t.apiListFileName},请为每个服务设置唯一文件名。`);e.add(t.apiListFileName)});let t=new Set;a.forEach(e=>{if(e.name){if(t.has(e.name))throw Error(`swaggerConfig 中 name 重复:${e.name},请为每个服务设置唯一名称。`);t.add(e.name)}})}return n&&this.showLegacyConfigHint(e),a}buildServerConfig(e,t,n,r){return{...e,swaggerJsonUrl:t.url,publicPrefix:t.publicPrefix??e.publicPrefix,headers:t.headers,apiListFileName:t.apiListFileName,dataLevel:t.dataLevel,parameterSeparator:t.parameterSeparator,includeInterface:t.includeInterface,excludeInterface:t.excludeInterface,includeTags:t.includeTags,excludeTags:t.excludeTags,modulePrefix:t.modulePrefix,responseModelTransform:t.responseModelTransform??e.responseModelTransform,timeout:t.timeout??e.timeout,namespaceIsolation:t.namespaceIsolation??e.namespaceIsolation??`segment`,enumIsolation:t.enumIsolation??e.enumIsolation??`segment`,swaggerConfig:t,__segment:n,__namespacePrefix:r}}async loadTsConfig(e){try{let t=await(0,f.createJiti)(__filename,{interopDefault:!0}).import(e),n=`default`in t&&t.default?t.default:t;return p=!0,n}catch(e){p=!0;let t=e instanceof Error?e.message:String(e);throw Error(`配置文件加载失败,请检查 an.config.ts 文件: ${t}`)}}async loadJsonConfig(e){try{let t=await c.default.promises.readFile(e,`utf8`);p=!0;try{return JSON.parse(t)}catch(e){throw p=!0,Error(`配置文件格式错误,请检查 an.config.json 的 JSON 格式是否正确: ${e instanceof Error?e.message:String(e)}`)}}catch(e){throw e instanceof Error&&`code`in e&&e.code,e}}async getConfig(e,r){let i=l.default.join(e,`an.config.ts`),a=l.default.join(e,`an.config.json`);if(c.default.existsSync(i))return t.log.info(`检测到 an.config.ts 配置文件。`),this.loadTsConfig(i);if(c.default.existsSync(a))return this.loadJsonConfig(a);p=!1,t.log.warning(`配置文件不存在,将自动创建配置文件。`);let o=g;if(r)o=y(r,`CLI --template`);else if(process.stdin.isTTY&&process.stdout.isTTY){let{picked:e}=await s.default.prompt([{type:`list`,name:`picked`,message:`请选择请求模板(决定 <saveApiListFolderPath>/config/ 下的底层实现):`,choices:[{name:`axios —— 基于 axios(浏览器/Node,需 npm i axios)`,value:`axios`},{name:`fetch —— 基于原生 fetch(浏览器/现代 Node,无额外依赖)`,value:`fetch`},{name:`wx —— 基于 wx.request(微信小程序)`,value:`wx`},{name:`uniapp —— 基于 uni.request(uni-app 跨端)`,value:`uniapp`},{name:`taro —— 基于 Taro.request(Taro 3+ 跨端)`,value:`taro`}],default:`axios`}]);o=e}else t.log.warning(`未指定 --template 且当前非交互终端,requestTemplate 默认为 "${g}"。可通过 --template 指定或修改 an.config.ts 后重跑。`);await n.writeFileRecursive(i,this.generateTsConfigContent(o));try{await c.default.promises.mkdir(b.saveApiListFolderPath,{recursive:!0}),await this.copyAjaxConfigFiles(b.saveApiListFolderPath,o),t.log.success(`配置文件已创建(requestTemplate=${o}),并已同步初始化 ${b.saveApiListFolderPath}/config/。请检查 an.config.ts 后重新运行以生成 API。`)}catch(e){t.log.warning(`初始化 ${b.saveApiListFolderPath}/config/ 失败:${e instanceof Error?e.message:String(e)}。配置文件已创建,请修正后重跑。`)}return b}generateTsConfigContent(e=g){return`import { defineConfig } from 'anl/config';
8
8
 
9
9
  export default defineConfig({
10
10
  saveTypeFolderPath: 'src/types',
@@ -12,6 +12,8 @@ export default defineConfig({
12
12
  saveEnumFolderPath: 'src/enums',
13
13
  importEnumPath: '../../../enums',
14
14
  requestMethodsImportPath: './config/fetch',
15
+ /** 请求模板:axios | fetch | wx | uniapp | taro,切换后需删除 <saveApiListFolderPath>/config 目录重新生成 */
16
+ requestTemplate: '${e}',
15
17
  formatting: {
16
18
  indentation: '\\t',
17
19
  lineEnding: '\\n',
@@ -33,8 +35,8 @@ export default defineConfig({
33
35
  comment: 'enum-descriptions',
34
36
  },
35
37
  });
36
- `}resolveSelectedServiceIndices(e,t){let n=new Map;e.forEach((e,t)=>{e.name&&n.set(e.name.toLowerCase(),t)}),e.forEach((e,t)=>{let i=r.computeSegment(e.apiListFileName).toLowerCase();i&&!n.has(i)&&n.set(i,t)});let i=new Set,a=[];for(let e of t){let t=e.toLowerCase(),r=n.get(t);r===void 0?a.push(e):i.add(r)}if(a.length){let t=e.map(e=>e.name??r.computeSegment(e.apiListFileName)).filter(Boolean).join(`, `);throw Error(`未找到匹配的 swagger 服务:${a.join(`, `)}。可用服务:${t||`<无>`}`)}return Array.from(i).sort((e,t)=>e-t)}async promptSelectServices(e){let t=e.map((e,t)=>{let n=(e.name??r.computeSegment(e.apiListFileName))||`#${t}`;return{name:`${n} (${e.apiListFileName} ← ${e.url})`,value:t,short:n}}),{picked:n}=await s.default.prompt([{type:`checkbox`,name:`picked`,message:`请选择需要重新生成的 swagger 服务(空选 = 全部):`,choices:t,pageSize:Math.min(20,t.length)}]);return!n||n.length===0?e.map((e,t)=>t):n.sort((e,t)=>e-t)}async initialize(e,i,a,o){let s=process.cwd();try{let l=await this.getConfig(s),u={...h,...l},f=a??u.logLevel;f&&t.setLogLevel(f);let m=Object.prototype.hasOwnProperty.call(l,`swaggerConfig`),g=this.normalizeswaggerConfig(u,m);if(!p)return;let _=g.length>1,v=_?g.map(e=>r.computeSegment(e.apiListFileName)):g.map(()=>``);if(_){let e=new Map;v.forEach((t,n)=>{if(!t)throw Error(`swaggerConfig[${n}].apiListFileName="${g[n].apiListFileName}" 无法派生有效 segment(清洗后为空),请使用包含字母/数字的文件名。`);if(t.toLowerCase()===`index`)throw Error(`swaggerConfig[${n}].apiListFileName="${g[n].apiListFileName}" 派生 segment 为 "index",会与顶层 models/index.ts 形成自引用,请改为其他文件名(如 "main.ts" / "default.ts")。`);if(e.has(t))throw Error(`swaggerConfig 多个服务的 apiListFileName 在派生 segment 时冲突:"${g[e.get(t)].apiListFileName}" 与 "${g[n].apiListFileName}" 都解析为 "${t}",请改名以避免目录覆盖。`);e.set(t,n)})}let y=g.map(e=>(e.namespaceIsolation??u.namespaceIsolation??`segment`)===`none`?``:r.segmentToNamespacePrefix(r.computeSegment(e.apiListFileName)));if(_){let e=g.map((e,t)=>({s:e,idx:t})).filter(({s:e})=>(e.namespaceIsolation??u.namespaceIsolation??`segment`)===`none`).map(({s:e})=>e.apiListFileName);e.length>0&&t.log.warning(`检测到多服务场景下以下服务关闭了 namespace 前缀(namespaceIsolation: 'none'):${e.join(`, `)}。 不同服务若存在同名 path,将会因 declare namespace 全局合并而互相污染类型,请确认是否符合预期。`);let n=new Map;y.forEach((e,t)=>{if(e){if(n.has(e))throw Error(`swaggerConfig 多个服务派生的 namespace 前缀冲突:"${g[n.get(e)].apiListFileName}" 与 "${g[t].apiListFileName}" 都解析为前缀 "${e}",请改名以避免类型同名合并。`);n.set(e,t)}})}let b,x;o?.length?(b=this.resolveSelectedServiceIndices(g,o),x=b.length!==g.length):g.length>1&&process.stdin.isTTY&&process.stdout.isTTY?(t.log.print(d.default.cyan(`
37
- 检测到多个 swagger 服务,请选择本次要重新生成的服务:`)),b=await this.promptSelectServices(g),x=b.length!==g.length):(b=g.map((e,t)=>t),x=!1),this.printExecutionPlan(g,b,x),await c.default.promises.mkdir(u.saveApiListFolderPath,{recursive:!0}),await this.copyAjaxConfigFiles(u.saveApiListFolderPath),x?await this.cleanSelectedTargets(u,g,b,v,_):(await n.clearDirExcept(u.saveApiListFolderPath,[`config`]),await n.clearDir(u.saveTypeFolderPath),await n.clearDir(u.saveEnumFolderPath));let S=[];for(let t=0;t<b.length;t++){let n=b[t],r=this.buildServerConfig(u,g[n],v[n],y[n]),i=x?!0:t>0,a=await this.handle(r,i,e);e&&a&&S.push({serverUrl:g[n].url,list:a})}if(_&&(await this.writeTopLevelModelsBarrel(u,v,b,x),b.filter(e=>(g[e].enumIsolation??`segment`)===`segment`).map(e=>v[e]).filter(Boolean).length>0&&await this.writeTopLevelEnumsBarrel(u,v,g,b,x),g.every(e=>(e.enumIsolation??`segment`)===`segment`)&&await this.warnLegacyTopLevelEnums(u)),i!==void 0&&i!==!1&&(x?await this.formatSelectedFiles(u,g,b,v,_,i):await this.formatGeneratedFiles(u,i)),t.log.banner(`All done — see you next time!`),e&&S.length>0){let n=e===`miss`?`excludeInterface`:`includeInterface`;for(let{serverUrl:e,list:r}of S)b.length>1?t.log.print(d.default.cyan(`\n[${n}] ${e}`)):t.log.print(d.default.cyan(`\n[${n}]`)),t.log.print(JSON.stringify(r,null,2));t.log.print(`
38
+ `}resolveSelectedServiceIndices(e,t){let n=new Map;e.forEach((e,t)=>{e.name&&n.set(e.name.toLowerCase(),t)}),e.forEach((e,t)=>{let i=r.computeSegment(e.apiListFileName).toLowerCase();i&&!n.has(i)&&n.set(i,t)});let i=new Set,a=[];for(let e of t){let t=e.toLowerCase(),r=n.get(t);r===void 0?a.push(e):i.add(r)}if(a.length){let t=e.map(e=>e.name??r.computeSegment(e.apiListFileName)).filter(Boolean).join(`, `);throw Error(`未找到匹配的 swagger 服务:${a.join(`, `)}。可用服务:${t||`<无>`}`)}return Array.from(i).sort((e,t)=>e-t)}async promptSelectServices(e){let t=e.map((e,t)=>{let n=(e.name??r.computeSegment(e.apiListFileName))||`#${t}`;return{name:`${n} (${e.apiListFileName} ← ${e.url})`,value:t,short:n}}),{picked:n}=await s.default.prompt([{type:`checkbox`,name:`picked`,message:`请选择需要重新生成的 swagger 服务(空选 = 全部):`,choices:t,pageSize:Math.min(20,t.length)}]);return!n||n.length===0?e.map((e,t)=>t):n.sort((e,t)=>e-t)}async initialize(e,i,a,o,s){let l=process.cwd();try{let u=await this.getConfig(l,s),f={...b,...u},m=a??f.logLevel;m&&t.setLogLevel(m);let h=Object.prototype.hasOwnProperty.call(u,`swaggerConfig`),g=this.normalizeswaggerConfig(f,h);if(!p)return;let _=g.length>1,v=_?g.map(e=>r.computeSegment(e.apiListFileName)):g.map(()=>``);if(_){let e=new Map;v.forEach((t,n)=>{if(!t)throw Error(`swaggerConfig[${n}].apiListFileName="${g[n].apiListFileName}" 无法派生有效 segment(清洗后为空),请使用包含字母/数字的文件名。`);if(t.toLowerCase()===`index`)throw Error(`swaggerConfig[${n}].apiListFileName="${g[n].apiListFileName}" 派生 segment 为 "index",会与顶层 models/index.ts 形成自引用,请改为其他文件名(如 "main.ts" / "default.ts")。`);if(e.has(t))throw Error(`swaggerConfig 多个服务的 apiListFileName 在派生 segment 时冲突:"${g[e.get(t)].apiListFileName}" 与 "${g[n].apiListFileName}" 都解析为 "${t}",请改名以避免目录覆盖。`);e.set(t,n)})}let x=g.map(e=>(e.namespaceIsolation??f.namespaceIsolation??`segment`)===`none`?``:r.segmentToNamespacePrefix(r.computeSegment(e.apiListFileName)));if(_){let e=g.map((e,t)=>({s:e,idx:t})).filter(({s:e})=>(e.namespaceIsolation??f.namespaceIsolation??`segment`)===`none`).map(({s:e})=>e.apiListFileName);e.length>0&&t.log.warning(`检测到多服务场景下以下服务关闭了 namespace 前缀(namespaceIsolation: 'none'):${e.join(`, `)}。 不同服务若存在同名 path,将会因 declare namespace 全局合并而互相污染类型,请确认是否符合预期。`);let n=new Map;x.forEach((e,t)=>{if(e){if(n.has(e))throw Error(`swaggerConfig 多个服务派生的 namespace 前缀冲突:"${g[n.get(e)].apiListFileName}" 与 "${g[t].apiListFileName}" 都解析为前缀 "${e}",请改名以避免类型同名合并。`);n.set(e,t)}})}let S,C;o?.length?(S=this.resolveSelectedServiceIndices(g,o),C=S.length!==g.length):g.length>1&&process.stdin.isTTY&&process.stdout.isTTY?(t.log.print(d.default.cyan(`
39
+ 检测到多个 swagger 服务,请选择本次要重新生成的服务:`)),S=await this.promptSelectServices(g),C=S.length!==g.length):(S=g.map((e,t)=>t),C=!1),this.printExecutionPlan(g,S,C),await c.default.promises.mkdir(f.saveApiListFolderPath,{recursive:!0});let w=s?y(s,`CLI --template`):y(f.requestTemplate,`an.config`);await this.copyAjaxConfigFiles(f.saveApiListFolderPath,w),C?await this.cleanSelectedTargets(f,g,S,v,_):(await n.clearDirExcept(f.saveApiListFolderPath,[`config`]),await n.clearDir(f.saveTypeFolderPath),await n.clearDir(f.saveEnumFolderPath));let T=[];for(let t=0;t<S.length;t++){let n=S[t],r=this.buildServerConfig(f,g[n],v[n],x[n]),i=C?!0:t>0,a=await this.handle(r,i,e);e&&a&&T.push({serverUrl:g[n].url,list:a})}if(_&&(await this.writeTopLevelModelsBarrel(f,v,S,C),S.filter(e=>(g[e].enumIsolation??`segment`)===`segment`).map(e=>v[e]).filter(Boolean).length>0&&await this.writeTopLevelEnumsBarrel(f,v,g,S,C),g.every(e=>(e.enumIsolation??`segment`)===`segment`)&&await this.warnLegacyTopLevelEnums(f)),i!==void 0&&i!==!1&&(C?await this.formatSelectedFiles(f,g,S,v,_,i):await this.formatGeneratedFiles(f,i)),t.log.banner(`All done — see you next time!`),e&&T.length>0){let n=e===`miss`?`excludeInterface`:`includeInterface`;for(let{serverUrl:e,list:r}of T)S.length>1?t.log.print(d.default.cyan(`\n[${n}] ${e}`)):t.log.print(d.default.cyan(`\n[${n}]`)),t.log.print(JSON.stringify(r,null,2));t.log.print(`
38
40
  `)}}catch(e){let n=e instanceof Error?e.message:`Unknown error`;t.log.error(`Initialization failed: ${n}`),process.exitCode=1}}printExecutionPlan(e,n,i){let a=new Set(n);t.log.print(d.default.cyan(i?`
39
41
  [anl type] 选择型生成,仅处理以下服务:`:`
40
42
  [anl type] 全量生成,将处理所有服务:`)),e.forEach((e,n)=>{let i=(e.name??r.computeSegment(e.apiListFileName))||`#${n}`;a.has(n)?t.log.print(` ${d.default.green(`●`)} ${i} (${e.apiListFileName}) ${d.default.gray(e.url)}`):t.log.print(` ${d.default.gray(`○ skip `)}${i} (${e.apiListFileName}) ${d.default.gray(e.url)}`)}),t.log.print(``)}async cleanSelectedTargets(e,t,r,i,a){for(let o of r)if(await n.clearDir(`${e.saveApiListFolderPath}/${t[o].apiListFileName}`),a){let r=i[o];r&&(await n.clearDir(`${e.saveTypeFolderPath}/connectors/${r}`),await n.clearDir(`${e.saveTypeFolderPath}/models/${r}`),(t[o].enumIsolation??`segment`)===`segment`&&await n.clearDir(`${e.saveEnumFolderPath}/${r}`))}else await n.clearDir(`${e.saveTypeFolderPath}/connectors`),await n.clearDir(`${e.saveTypeFolderPath}/models`)}async writeTopLevelModelsBarrel(e,i,a,o){let s=`${e.saveTypeFolderPath}/models/index.ts`,l=e=>{let t=r.segmentToNamespacePrefix(e);if(!t)throw Error(`无法为 segment "${e}" 生成 namespace 别名,请改用包含字母/数字的 apiListFileName。`);return`export * as ${t} from './${e}';`},u=o?a.map(e=>i[e]).filter(Boolean):i.filter(Boolean),d=u.map(e=>l(e)),f=e=>{let t=/from\s+['"]\.\/([^'"]+)['"]/.exec(e);return t?t[1]:null},p;if(o){let e=[];try{e=(await c.default.promises.readFile(s,`utf8`)).split(`
@@ -45,4 +47,4 @@ export default defineConfig({
45
47
  `).filter(e=>e.trim()!==``)}catch{e=[]}let t=new Set(p);g=[...e.filter(e=>{let n=h(e);return!(n&&t.has(n))}),...m]}await n.writeFileRecursive(l,g.join(`
46
48
  `)+`
47
49
  `),t.log.info(`${l} - Top-level enums barrel ${s?`merged`:`written`}.`)}async warnLegacyTopLevelEnums(e){let n=e.saveEnumFolderPath;try{let e=(await c.default.promises.readdir(n,{withFileTypes:!0})).filter(e=>e.isFile()&&e.name.endsWith(`.ts`)&&e.name!==`index.ts`).map(e=>e.name);e.length>0&&t.log.warning(`检测到 ${n} 顶层仍存在历史枚举文件(${e.slice(0,5).join(`, `)}${e.length>5?` ...`:``}),共 ${e.length} 个。\n若已切换到 enumIsolation: 'segment'(默认),建议执行 \`anl type\` 全量重生成或手动清理这些文件,避免与 ${n}/<segment>/ 下的隔离枚举混用。`)}catch{}}async formatSelectedFiles(e,n,r,i,a,o){let s=[];for(let t of r){let r=`${e.saveApiListFolderPath}/${n[t].apiListFileName}`;try{await c.default.promises.access(r),s.push(`"${r}"`)}catch{}let o=a&&i[t]?[`${e.saveTypeFolderPath}/connectors/${i[t]}`,`${e.saveTypeFolderPath}/models/${i[t]}`]:[`${e.saveTypeFolderPath}/connectors`,`${e.saveTypeFolderPath}/models`];for(let e of o)try{await c.default.promises.access(e),s.push(`"${e}/**/*.{ts,d.ts}"`)}catch{}if(a&&i[t]&&(n[t].enumIsolation??`segment`)===`segment`){let n=`${e.saveEnumFolderPath}/${i[t]}`;try{await c.default.promises.access(n),s.push(`"${n}/**/*.{ts,d.ts}"`)}catch{}}else{let t=e.saveEnumFolderPath;try{await c.default.promises.access(t),s.push(`"${t}/*.ts"`)}catch{}}}if(s.length===0){t.log.warning(`No files to format for the selected services.`);return}let f=await this.resolvePrettierExecutable(),p=``;if(typeof o==`string`&&o.trim()){let e=l.default.resolve(process.cwd(),o.trim());try{await c.default.promises.access(e),p=` --config "${e}"`}catch{let e=await this.detectPrettierConfig();e&&(p=` --config "${e}"`)}}else{let e=await this.detectPrettierConfig();e&&(p=` --config "${e}"`)}let m=`${f} --write ${s.join(` `)}${p}`;try{t.spinner.start(`Formatting selected files...`),await new Promise((e,t)=>{(0,u.exec)(m,n=>{n?t(Error(String(n))):e()})}),t.spinner.success(`File formatting successful`),t.log.print(`
48
- `)}catch(e){t.spinner.error(`Format failed`),t.log.print(e),t.log.error(`Format failed, please manually execute the following command:`),t.log.print(`$`,d.default.yellow(m))}}};m&&new g().initialize().catch(e=>{console.error(e)}),exports.Main=g;
50
+ `)}catch(e){t.spinner.error(`Format failed`),t.log.print(e),t.log.error(`Format failed, please manually execute the following command:`),t.log.print(`$`,d.default.yellow(m))}}};m&&new x().initialize().catch(e=>{console.error(e)}),exports.Main=x;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anl",
3
- "version": "26.806.0",
3
+ "version": "26.921.0",
4
4
  "description": "FE command line tool",
5
5
  "main": "bin/an-cli.js",
6
6
  "exports": {