@wot-ui/router 0.0.4 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +84 -50
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +84 -50
- package/package.json +17 -3
package/dist/index.cjs
CHANGED
|
@@ -68,21 +68,16 @@ function createRouter(options) {
|
|
|
68
68
|
if (typeof to === "string") return resolvePath(to);
|
|
69
69
|
if (to.name) {
|
|
70
70
|
const route = routes.find((r) => r.name === to.name);
|
|
71
|
-
if (!route) {
|
|
72
|
-
console.warn(`[router] Route with name '${to.name}' not found`);
|
|
73
|
-
return {
|
|
74
|
-
...START_LOCATION_NORMALIZED,
|
|
75
|
-
path: "/"
|
|
76
|
-
};
|
|
77
|
-
}
|
|
71
|
+
if (!route) throw new Error(`您正在尝试访问的路由 '${to.name}' 未在路由表中定义。请检查您的路由配置。`);
|
|
78
72
|
const path = fillParams(route.path, to.params);
|
|
73
|
+
const finalQuery = to.params || {};
|
|
79
74
|
return {
|
|
80
75
|
path,
|
|
81
76
|
name: to.name,
|
|
82
77
|
params: to.params || {},
|
|
83
|
-
query:
|
|
78
|
+
query: finalQuery,
|
|
84
79
|
hash: to.hash || "",
|
|
85
|
-
fullPath: stringifyQuery(path,
|
|
80
|
+
fullPath: stringifyQuery(path, finalQuery),
|
|
86
81
|
meta: route.meta || {}
|
|
87
82
|
};
|
|
88
83
|
}
|
|
@@ -92,6 +87,7 @@ function createRouter(options) {
|
|
|
92
87
|
function resolvePath(path, query) {
|
|
93
88
|
const normalizedPath = normalizeUrl(path.split("?")[0]);
|
|
94
89
|
const route = routes.find((r) => r.path === normalizedPath || r.aliasPath === normalizedPath);
|
|
90
|
+
if (!route) throw new Error(`您正在尝试访问的路由 '${normalizedPath}' 未在路由表中定义。请检查您的路由配置。`);
|
|
95
91
|
const finalQuery = {
|
|
96
92
|
...getUrlParams(path),
|
|
97
93
|
...query || {}
|
|
@@ -128,57 +124,82 @@ function createRouter(options) {
|
|
|
128
124
|
if (back$1 === void 0 || typeof back$1 === "number") uni.navigateBack({ delta: back$1 ?? 1 });
|
|
129
125
|
else uni.navigateBack(back$1);
|
|
130
126
|
}
|
|
131
|
-
async function navigate(to, type) {
|
|
127
|
+
async function navigate(to, type, skipGuards = false) {
|
|
132
128
|
const targetLocation = resolve(to);
|
|
133
129
|
const fromLocation = (0, vue.unref)(currentRoute);
|
|
134
|
-
try {
|
|
130
|
+
if (!skipGuards) try {
|
|
135
131
|
await runGuardQueue(beforeGuards, targetLocation, fromLocation);
|
|
136
132
|
} catch (error) {
|
|
137
133
|
if (error && (error.message === "NavigationCancelled" || error.message === "NavigationRedirect")) {
|
|
138
|
-
if (error.message === "NavigationRedirect" && error.to) return navigate(error.to, type);
|
|
134
|
+
if (error.message === "NavigationRedirect" && error.to) return navigate(error.to, type, true);
|
|
139
135
|
return Promise.reject(error);
|
|
140
136
|
}
|
|
141
137
|
return Promise.reject(error);
|
|
142
138
|
}
|
|
143
139
|
const url = targetLocation.fullPath;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
140
|
+
const finalType = typeof to === "object" && to.navType || type || "push";
|
|
141
|
+
try {
|
|
142
|
+
await performUniNavigate(finalType, url);
|
|
143
|
+
currentRoute.value = targetLocation;
|
|
144
|
+
afterGuards.forEach((guard) => guard(targetLocation, fromLocation));
|
|
145
|
+
return Promise.resolve();
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return Promise.reject(error);
|
|
148
|
+
}
|
|
148
149
|
}
|
|
149
150
|
function performUniNavigate(type, url) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
151
|
+
return new Promise((resolve$1, reject) => {
|
|
152
|
+
const success = () => resolve$1();
|
|
153
|
+
const fail = (error) => {
|
|
154
|
+
reject(/* @__PURE__ */ new Error(`Navigation failed: ${error.message || error.errMsg || "Unknown error"}`));
|
|
155
|
+
};
|
|
156
|
+
switch (type) {
|
|
157
|
+
case "push":
|
|
158
|
+
uni.navigateTo({
|
|
159
|
+
url,
|
|
160
|
+
success,
|
|
161
|
+
fail
|
|
162
|
+
});
|
|
163
|
+
break;
|
|
164
|
+
case "replace":
|
|
165
|
+
uni.redirectTo({
|
|
166
|
+
url,
|
|
167
|
+
success,
|
|
168
|
+
fail
|
|
169
|
+
});
|
|
170
|
+
break;
|
|
171
|
+
case "pushTab":
|
|
172
|
+
uni.switchTab({
|
|
173
|
+
url,
|
|
174
|
+
success,
|
|
175
|
+
fail
|
|
176
|
+
});
|
|
177
|
+
break;
|
|
178
|
+
case "replaceAll":
|
|
179
|
+
uni.reLaunch({
|
|
180
|
+
url,
|
|
181
|
+
success,
|
|
182
|
+
fail
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
case "back":
|
|
186
|
+
uni.navigateBack({
|
|
187
|
+
success,
|
|
188
|
+
fail
|
|
189
|
+
});
|
|
190
|
+
break;
|
|
191
|
+
default: uni.navigateTo({
|
|
192
|
+
url,
|
|
193
|
+
success,
|
|
194
|
+
fail
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
});
|
|
168
198
|
}
|
|
169
199
|
async function runGuardQueue(guards, to, from) {
|
|
170
200
|
for (const guard of guards) await new Promise((resolve$1, reject) => {
|
|
171
201
|
const next = (val) => {
|
|
172
|
-
|
|
173
|
-
else if (val === void 0) resolve$1();
|
|
174
|
-
else {
|
|
175
|
-
const error = /* @__PURE__ */ new Error("NavigationRedirect");
|
|
176
|
-
error.to = val;
|
|
177
|
-
reject(error);
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
const res = guard(to, from, next);
|
|
181
|
-
if (res instanceof Promise) res.then((val) => {
|
|
202
|
+
next._called = true;
|
|
182
203
|
if (val === false) reject(/* @__PURE__ */ new Error("NavigationCancelled"));
|
|
183
204
|
else if (val === void 0 || val === true) resolve$1();
|
|
184
205
|
else {
|
|
@@ -186,14 +207,26 @@ function createRouter(options) {
|
|
|
186
207
|
error.to = val;
|
|
187
208
|
reject(error);
|
|
188
209
|
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
210
|
+
};
|
|
211
|
+
const guardReturn = guard(to, from, next);
|
|
212
|
+
let guardCall = Promise.resolve(guardReturn);
|
|
213
|
+
if (guard.length < 3) guardCall = guardCall.then(next);
|
|
192
214
|
else {
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
215
|
+
const message = `The "next" callback was never called inside of ${guard.name ? `"${guard.name}"` : ""}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`;
|
|
216
|
+
if (guardReturn !== null && typeof guardReturn === "object" && "then" in guardReturn) guardCall = guardCall.then((resolvedValue) => {
|
|
217
|
+
if (!next._called) {
|
|
218
|
+
console.warn(message);
|
|
219
|
+
return Promise.reject(/* @__PURE__ */ new Error("Invalid navigation guard"));
|
|
220
|
+
}
|
|
221
|
+
return resolvedValue;
|
|
222
|
+
});
|
|
223
|
+
else if (!next._called) {
|
|
224
|
+
console.warn(message);
|
|
225
|
+
reject(/* @__PURE__ */ new Error("Invalid navigation guard"));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
196
228
|
}
|
|
229
|
+
guardCall.catch((err) => reject(err));
|
|
197
230
|
});
|
|
198
231
|
}
|
|
199
232
|
function beforeEach(guard) {
|
|
@@ -237,6 +270,7 @@ function createRouter(options) {
|
|
|
237
270
|
if (router$1.currentRoute.value.path === newPath) return;
|
|
238
271
|
const from = (0, vue.unref)(router$1.currentRoute);
|
|
239
272
|
const matched = router$1.routes.find((r) => r.path === newPath || r.aliasPath === newPath);
|
|
273
|
+
if (!matched) console.warn(`[router] 路由 '${newPath}' 未在路由表中定义,但页面已加载。请检查您的路由配置。`);
|
|
240
274
|
const to = {
|
|
241
275
|
path: newPath,
|
|
242
276
|
name: matched?.name,
|
package/dist/index.d.cts
CHANGED
|
@@ -43,7 +43,7 @@ type RouteLocationRaw = string | {
|
|
|
43
43
|
interface RouterOptions {
|
|
44
44
|
routes: RouteRecordRaw[];
|
|
45
45
|
}
|
|
46
|
-
type NavigationGuardNext = (to?: RouteLocationRaw | false | void) => void;
|
|
46
|
+
type NavigationGuardNext = (to?: RouteLocationRaw | false | true | void) => void;
|
|
47
47
|
type NavigationGuard = (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => Promise<void | RouteLocationRaw | false | boolean> | void | RouteLocationRaw | false | boolean;
|
|
48
48
|
type NavigationHookAfter = (to: RouteLocationNormalized, from: RouteLocationNormalized) => void;
|
|
49
49
|
type NavType = 'push' | 'replace' | 'replaceAll' | 'pushTab' | 'back';
|
package/dist/index.d.mts
CHANGED
|
@@ -43,7 +43,7 @@ type RouteLocationRaw = string | {
|
|
|
43
43
|
interface RouterOptions {
|
|
44
44
|
routes: RouteRecordRaw[];
|
|
45
45
|
}
|
|
46
|
-
type NavigationGuardNext = (to?: RouteLocationRaw | false | void) => void;
|
|
46
|
+
type NavigationGuardNext = (to?: RouteLocationRaw | false | true | void) => void;
|
|
47
47
|
type NavigationGuard = (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => Promise<void | RouteLocationRaw | false | boolean> | void | RouteLocationRaw | false | boolean;
|
|
48
48
|
type NavigationHookAfter = (to: RouteLocationNormalized, from: RouteLocationNormalized) => void;
|
|
49
49
|
type NavType = 'push' | 'replace' | 'replaceAll' | 'pushTab' | 'back';
|
package/dist/index.mjs
CHANGED
|
@@ -68,21 +68,16 @@ function createRouter(options) {
|
|
|
68
68
|
if (typeof to === "string") return resolvePath(to);
|
|
69
69
|
if (to.name) {
|
|
70
70
|
const route = routes.find((r) => r.name === to.name);
|
|
71
|
-
if (!route) {
|
|
72
|
-
console.warn(`[router] Route with name '${to.name}' not found`);
|
|
73
|
-
return {
|
|
74
|
-
...START_LOCATION_NORMALIZED,
|
|
75
|
-
path: "/"
|
|
76
|
-
};
|
|
77
|
-
}
|
|
71
|
+
if (!route) throw new Error(`您正在尝试访问的路由 '${to.name}' 未在路由表中定义。请检查您的路由配置。`);
|
|
78
72
|
const path = fillParams(route.path, to.params);
|
|
73
|
+
const finalQuery = to.params || {};
|
|
79
74
|
return {
|
|
80
75
|
path,
|
|
81
76
|
name: to.name,
|
|
82
77
|
params: to.params || {},
|
|
83
|
-
query:
|
|
78
|
+
query: finalQuery,
|
|
84
79
|
hash: to.hash || "",
|
|
85
|
-
fullPath: stringifyQuery(path,
|
|
80
|
+
fullPath: stringifyQuery(path, finalQuery),
|
|
86
81
|
meta: route.meta || {}
|
|
87
82
|
};
|
|
88
83
|
}
|
|
@@ -92,6 +87,7 @@ function createRouter(options) {
|
|
|
92
87
|
function resolvePath(path, query) {
|
|
93
88
|
const normalizedPath = normalizeUrl(path.split("?")[0]);
|
|
94
89
|
const route = routes.find((r) => r.path === normalizedPath || r.aliasPath === normalizedPath);
|
|
90
|
+
if (!route) throw new Error(`您正在尝试访问的路由 '${normalizedPath}' 未在路由表中定义。请检查您的路由配置。`);
|
|
95
91
|
const finalQuery = {
|
|
96
92
|
...getUrlParams(path),
|
|
97
93
|
...query || {}
|
|
@@ -128,57 +124,82 @@ function createRouter(options) {
|
|
|
128
124
|
if (back$1 === void 0 || typeof back$1 === "number") uni.navigateBack({ delta: back$1 ?? 1 });
|
|
129
125
|
else uni.navigateBack(back$1);
|
|
130
126
|
}
|
|
131
|
-
async function navigate(to, type) {
|
|
127
|
+
async function navigate(to, type, skipGuards = false) {
|
|
132
128
|
const targetLocation = resolve(to);
|
|
133
129
|
const fromLocation = unref(currentRoute);
|
|
134
|
-
try {
|
|
130
|
+
if (!skipGuards) try {
|
|
135
131
|
await runGuardQueue(beforeGuards, targetLocation, fromLocation);
|
|
136
132
|
} catch (error) {
|
|
137
133
|
if (error && (error.message === "NavigationCancelled" || error.message === "NavigationRedirect")) {
|
|
138
|
-
if (error.message === "NavigationRedirect" && error.to) return navigate(error.to, type);
|
|
134
|
+
if (error.message === "NavigationRedirect" && error.to) return navigate(error.to, type, true);
|
|
139
135
|
return Promise.reject(error);
|
|
140
136
|
}
|
|
141
137
|
return Promise.reject(error);
|
|
142
138
|
}
|
|
143
139
|
const url = targetLocation.fullPath;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
140
|
+
const finalType = typeof to === "object" && to.navType || type || "push";
|
|
141
|
+
try {
|
|
142
|
+
await performUniNavigate(finalType, url);
|
|
143
|
+
currentRoute.value = targetLocation;
|
|
144
|
+
afterGuards.forEach((guard) => guard(targetLocation, fromLocation));
|
|
145
|
+
return Promise.resolve();
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return Promise.reject(error);
|
|
148
|
+
}
|
|
148
149
|
}
|
|
149
150
|
function performUniNavigate(type, url) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
151
|
+
return new Promise((resolve$1, reject) => {
|
|
152
|
+
const success = () => resolve$1();
|
|
153
|
+
const fail = (error) => {
|
|
154
|
+
reject(/* @__PURE__ */ new Error(`Navigation failed: ${error.message || error.errMsg || "Unknown error"}`));
|
|
155
|
+
};
|
|
156
|
+
switch (type) {
|
|
157
|
+
case "push":
|
|
158
|
+
uni.navigateTo({
|
|
159
|
+
url,
|
|
160
|
+
success,
|
|
161
|
+
fail
|
|
162
|
+
});
|
|
163
|
+
break;
|
|
164
|
+
case "replace":
|
|
165
|
+
uni.redirectTo({
|
|
166
|
+
url,
|
|
167
|
+
success,
|
|
168
|
+
fail
|
|
169
|
+
});
|
|
170
|
+
break;
|
|
171
|
+
case "pushTab":
|
|
172
|
+
uni.switchTab({
|
|
173
|
+
url,
|
|
174
|
+
success,
|
|
175
|
+
fail
|
|
176
|
+
});
|
|
177
|
+
break;
|
|
178
|
+
case "replaceAll":
|
|
179
|
+
uni.reLaunch({
|
|
180
|
+
url,
|
|
181
|
+
success,
|
|
182
|
+
fail
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
case "back":
|
|
186
|
+
uni.navigateBack({
|
|
187
|
+
success,
|
|
188
|
+
fail
|
|
189
|
+
});
|
|
190
|
+
break;
|
|
191
|
+
default: uni.navigateTo({
|
|
192
|
+
url,
|
|
193
|
+
success,
|
|
194
|
+
fail
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
});
|
|
168
198
|
}
|
|
169
199
|
async function runGuardQueue(guards, to, from) {
|
|
170
200
|
for (const guard of guards) await new Promise((resolve$1, reject) => {
|
|
171
201
|
const next = (val) => {
|
|
172
|
-
|
|
173
|
-
else if (val === void 0) resolve$1();
|
|
174
|
-
else {
|
|
175
|
-
const error = /* @__PURE__ */ new Error("NavigationRedirect");
|
|
176
|
-
error.to = val;
|
|
177
|
-
reject(error);
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
const res = guard(to, from, next);
|
|
181
|
-
if (res instanceof Promise) res.then((val) => {
|
|
202
|
+
next._called = true;
|
|
182
203
|
if (val === false) reject(/* @__PURE__ */ new Error("NavigationCancelled"));
|
|
183
204
|
else if (val === void 0 || val === true) resolve$1();
|
|
184
205
|
else {
|
|
@@ -186,14 +207,26 @@ function createRouter(options) {
|
|
|
186
207
|
error.to = val;
|
|
187
208
|
reject(error);
|
|
188
209
|
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
210
|
+
};
|
|
211
|
+
const guardReturn = guard(to, from, next);
|
|
212
|
+
let guardCall = Promise.resolve(guardReturn);
|
|
213
|
+
if (guard.length < 3) guardCall = guardCall.then(next);
|
|
192
214
|
else {
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
215
|
+
const message = `The "next" callback was never called inside of ${guard.name ? `"${guard.name}"` : ""}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`;
|
|
216
|
+
if (guardReturn !== null && typeof guardReturn === "object" && "then" in guardReturn) guardCall = guardCall.then((resolvedValue) => {
|
|
217
|
+
if (!next._called) {
|
|
218
|
+
console.warn(message);
|
|
219
|
+
return Promise.reject(/* @__PURE__ */ new Error("Invalid navigation guard"));
|
|
220
|
+
}
|
|
221
|
+
return resolvedValue;
|
|
222
|
+
});
|
|
223
|
+
else if (!next._called) {
|
|
224
|
+
console.warn(message);
|
|
225
|
+
reject(/* @__PURE__ */ new Error("Invalid navigation guard"));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
196
228
|
}
|
|
229
|
+
guardCall.catch((err) => reject(err));
|
|
197
230
|
});
|
|
198
231
|
}
|
|
199
232
|
function beforeEach(guard) {
|
|
@@ -237,6 +270,7 @@ function createRouter(options) {
|
|
|
237
270
|
if (router$1.currentRoute.value.path === newPath) return;
|
|
238
271
|
const from = unref(router$1.currentRoute);
|
|
239
272
|
const matched = router$1.routes.find((r) => r.path === newPath || r.aliasPath === newPath);
|
|
273
|
+
if (!matched) console.warn(`[router] 路由 '${newPath}' 未在路由表中定义,但页面已加载。请检查您的路由配置。`);
|
|
240
274
|
const to = {
|
|
241
275
|
path: newPath,
|
|
242
276
|
name: matched?.name,
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wot-ui/router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "轻量级 uni-app vue3 路由库",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/wot-ui/my-uni.git",
|
|
8
|
+
"directory": "packages/router"
|
|
9
|
+
},
|
|
5
10
|
"type": "module",
|
|
6
11
|
"main": "dist/index.js",
|
|
7
12
|
"module": "dist/index.mjs",
|
|
@@ -23,7 +28,10 @@
|
|
|
23
28
|
"clean": "rimraf dist",
|
|
24
29
|
"build": "tsdown",
|
|
25
30
|
"dev": "tsdown --watch",
|
|
26
|
-
"lint": "eslint --fix --ext .json,.js,.ts src"
|
|
31
|
+
"lint": "eslint --fix --ext .json,.js,.ts src",
|
|
32
|
+
"test": "vitest",
|
|
33
|
+
"test:ui": "vitest --ui",
|
|
34
|
+
"test:coverage": "vitest --coverage"
|
|
27
35
|
},
|
|
28
36
|
"license": "MIT",
|
|
29
37
|
"keywords": [
|
|
@@ -42,19 +50,25 @@
|
|
|
42
50
|
"@types/node": "^20.16.2",
|
|
43
51
|
"@typescript-eslint/eslint-plugin": "^5.54.1",
|
|
44
52
|
"@typescript-eslint/parser": "^5.54.1",
|
|
53
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
54
|
+
"@vitest/ui": "^4.0.16",
|
|
55
|
+
"@vue/test-utils": "^2.4.6",
|
|
45
56
|
"eslint": "^8.57.0",
|
|
46
57
|
"eslint-config-prettier": "^8.7.0",
|
|
47
58
|
"eslint-plugin-import": "^2.25.2",
|
|
48
59
|
"eslint-plugin-n": "^15.0.0",
|
|
49
60
|
"eslint-plugin-prettier": "^4.2.1",
|
|
50
61
|
"git-cz": "^4.9.0",
|
|
62
|
+
"happy-dom": "^20.0.11",
|
|
51
63
|
"husky": "^8.0.3",
|
|
64
|
+
"jsdom": "^27.3.0",
|
|
52
65
|
"lint-staged": "^13.1.2",
|
|
53
66
|
"prettier": "^2.8.4",
|
|
54
67
|
"rimraf": "^4.4.0",
|
|
55
68
|
"standard-version": "^9.5.0",
|
|
56
69
|
"ts-jest": "^29.0.5",
|
|
57
70
|
"tsdown": "^0.18.2",
|
|
58
|
-
"typescript": "~5.5.4"
|
|
71
|
+
"typescript": "~5.5.4",
|
|
72
|
+
"vitest": "^4.0.16"
|
|
59
73
|
}
|
|
60
74
|
}
|