vite-plugin-blocklet 0.8.6 → 0.8.8

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 CHANGED
@@ -3,12 +3,12 @@
3
3
  var vitePluginNodePolyfills = require('vite-plugin-node-polyfills');
4
4
  var vite = require('vite');
5
5
  var semver = require('semver');
6
+ var node_fs = require('node:fs');
7
+ var YAML = require('yaml');
6
8
  var Mcrypto = require('@ocap/mcrypto');
7
9
  var util = require('@ocap/util');
8
10
  var did = require('@arcblock/did');
9
- var fs = require('node:fs');
10
- var path = require('node:path');
11
- var YAML = require('yaml');
11
+ var ufo = require('ufo');
12
12
  var isMobile = require('ismobilejs');
13
13
  var getPort = require('get-port');
14
14
  var mri = require('mri');
@@ -25,6 +25,13 @@ function toBlockletDid(name) {
25
25
  return did.fromPublicKey(pk, { role: types.RoleType.ROLE_ANY });
26
26
  }
27
27
 
28
+ async function getBlockletYAML() {
29
+ const blockletYamlPath = './blocklet.yml';
30
+ const content = await node_fs.promises.readFile(blockletYamlPath, 'utf8');
31
+ const blockletYaml = YAML.parse(content);
32
+ return blockletYaml || {};
33
+ }
34
+
28
35
  const isInBlocklet = !!process.env.BLOCKLET_PORT;
29
36
  const blockletPort = process.env.BLOCKLET_PORT;
30
37
  const blockletPrefix = process.env.BLOCKLET_DEV_MOUNT_POINT || '/';
@@ -34,7 +41,7 @@ const blockletPrefix = process.env.BLOCKLET_DEV_MOUNT_POINT || '/';
34
41
  *
35
42
  * @param {Object} options - The options for the HMR plugin.
36
43
  * @param {string} options.version - The version of the vite version.
37
- * @param {'middleware'|'client'|'server'} options.hmrMode - The version of the vite version.
44
+ * @param {'middleware'|'client'|'server'|'wsUpgrade'} [options.hmrMode = 'client'] - The version of the vite version.
38
45
  * @return {Object} The HMR plugin object.
39
46
  */
40
47
  function createHmrPlugin(options = {}) {
@@ -55,17 +62,24 @@ function createHmrPlugin(options = {}) {
55
62
  return replacedCode;
56
63
  }
57
64
 
58
- if (['client', 'middleware'].includes(hmrMode)) {
59
- replacedCode = replacedCode.replace(/__HMR_BASE__/g, `"${blockletPrefix}"+__HMR_BASE__`);
65
+ // 兼容不带服务端的情况、vite 以中间件形式挂载到服务端代码的情况
66
+ if (['client', 'middleware', 'wsUpgrade'].includes(hmrMode)) {
67
+ replacedCode = replacedCode.replace(
68
+ /__HMR_BASE__/g,
69
+ `"${blockletPrefix === '/' ? '' : blockletPrefix}"+__HMR_BASE__`,
70
+ );
60
71
  }
61
72
 
62
- if (hmrMode === 'middleware') {
73
+ // 兼容 vite 以中间件形式挂载到服务端代码的情况,无论 ws 是中间件挂载还是 ws 直接监听 upgrade 事件都支持
74
+ if (['middleware', 'wsUpgrade'].includes(hmrMode)) {
63
75
  // 根据页面的协议自动判断端口
64
76
  replacedCode = replacedCode.replace(
65
77
  /__HMR_PORT__/g,
66
78
  'location.port || (location.protocol === "https:" ? 443 : 80);',
67
79
  );
68
-
80
+ }
81
+ // 当 ws 是以中间件的形式挂载到服务端代码时,需要手动在页面触发一次 upgrade 事件
82
+ if (hmrMode === 'middleware') {
69
83
  // 在页面加载时,触发一次 upgrade
70
84
  replacedCode = replacedCode.replace(
71
85
  'function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {',
@@ -73,7 +87,9 @@ function createHmrPlugin(options = {}) {
73
87
  );
74
88
  replacedCode = replacedCode.replace('fallback = () => {', 'fallback = async () => {');
75
89
  replacedCode = replacedCode.replace(/socket = setupWebSocket\(/g, 'socket = await setupWebSocket(');
76
-
90
+ }
91
+ // 当 ws 是通过服务端的 proxy 来实现时,需要更改页面自动刷新的判断逻辑
92
+ if (['middleware', 'wsUpgrade'].includes(hmrMode)) {
77
93
  if ([4, 5].includes(pureVersion)) {
78
94
  // 改变刷新页面的判断
79
95
  replacedCode = replacedCode.replace(
@@ -98,16 +114,16 @@ function createConfigPlugin$1() {
98
114
  server.middlewares.use((req, res, next) => {
99
115
  // blocklet server 会把设置的 base 从请求 url 中移除,所以需要再加回 base
100
116
  if (!req.url.startsWith(blockletPrefix)) {
101
- req.url = path.join(blockletPrefix || '/', req.url);
117
+ req.url = ufo.joinURL(blockletPrefix || '/', req.url);
102
118
  }
103
119
  return next();
104
120
  });
105
121
  }
106
122
  },
107
- config(config, { command }) {
123
+ async config(config, { command }) {
108
124
  if (command === 'serve') {
109
125
  const targetConfig = {};
110
- targetConfig.base = path.join('/', config.base || blockletPrefix, '/');
126
+ targetConfig.base = ufo.withTrailingSlash(ufo.joinURL('/', config.base || blockletPrefix));
111
127
  if (!(config.server && config.server.port)) {
112
128
  const port = blockletPort || 3000;
113
129
  targetConfig.server = {
@@ -120,9 +136,7 @@ function createConfigPlugin$1() {
120
136
  if (command === 'build') {
121
137
  if (!config.base) {
122
138
  try {
123
- const blockletYamlPath = './blocklet.yml';
124
- const blockletYaml = YAML.parse(fs.readFileSync(blockletYamlPath, 'utf8'));
125
- let { name, did } = blockletYaml;
139
+ let { name, did } = await getBlockletYAML();
126
140
  if (!did && name) {
127
141
  did = toBlockletDid(name);
128
142
  }
@@ -147,7 +161,7 @@ function createConfigPlugin$1() {
147
161
  function createMetaPlugin() {
148
162
  return {
149
163
  name: 'blocklet:meta',
150
- transformIndexHtml(html, ctx) {
164
+ async transformIndexHtml(html, ctx) {
151
165
  const tags = [];
152
166
  if (ctx?.chunk?.isEntry) {
153
167
  let script;
@@ -160,12 +174,10 @@ function createMetaPlugin() {
160
174
  html = html.replace('</body>', `${script}</body>`);
161
175
  }
162
176
  }
163
- // 如果 index.html 中没有设置 title,则自动注入 blocklet.yml 中的 title
164
- if (!/<title>(.*?)<\/title>/.test(html)) {
165
- const blockletYamlPath = './blocklet.yml';
166
- const blockletYaml = YAML.parse(fs.readFileSync(blockletYamlPath, 'utf8'));
167
- const { title } = blockletYaml;
177
+ const { title } = await getBlockletYAML();
168
178
 
179
+ // 如果 index.html 中没有设置 title,则自动注入 blocklet.yml 中的 title
180
+ if (title && !/<title>(.*?)<\/title>/.test(html)) {
169
181
  tags.push({
170
182
  tag: 'title',
171
183
  children: title,
@@ -251,6 +263,10 @@ function generateHtml({ color, image }) {
251
263
  -webkit-animation-delay: -0.16s;
252
264
  animation-delay: -0.16s;
253
265
  }
266
+ #loadingImage {
267
+ margin-bottom: 16px;
268
+ object-fit: contain;
269
+ }
254
270
 
255
271
  @-webkit-keyframes fadeIn {
256
272
  0% { opacity: 0; }
@@ -286,13 +302,23 @@ function generateHtml({ color, image }) {
286
302
  }
287
303
  </style>
288
304
  <div class="spinner-wrapper">
289
- <img src="${image}" width="70" height="70" style="margin-bottom: 16px" />
305
+ <img id="loadingImage" width="70" height="70" />
290
306
  <div class="spinner">
291
307
  <div class="bounce1"></div>
292
308
  <div class="bounce2"></div>
293
309
  <div class="bounce3"></div>
294
310
  </div>
295
- </div>`;
311
+ </div>
312
+ <script>
313
+ (() => {
314
+ const loadingImage = document.getElementById('loadingImage');
315
+ if (window?.blocklet?.appLogo) {
316
+ loadingImage.src = window.blocklet.appLogo;
317
+ } else {
318
+ loadingImage.src = "${image}";
319
+ }
320
+ })();
321
+ </script>`;
296
322
  }
297
323
 
298
324
  /**
@@ -304,11 +330,14 @@ function generateHtml({ color, image }) {
304
330
  * @param {string} [options.loadingImage='/.well-known/service/blocklet/logo?imageFilter=convert&f=png&w=80'] - The URL of the loading image.
305
331
  * @return {Object} - The Vite plugin object.
306
332
  */
307
- function createLoadingPlugin({
308
- loadingElementId = 'app',
309
- loadingColor = '#8abaf0',
310
- loadingImage = '/.well-known/service/blocklet/logo?imageFilter=convert&f=png&w=80',
311
- } = {}) {
333
+ function createLoadingPlugin({ loadingElementId = 'app', loadingColor = '#8abaf0', loadingImage } = {}) {
334
+ if (!loadingImage) {
335
+ loadingImage = ufo.withQuery('/.well-known/service/blocklet/logo', {
336
+ imageFilter: 'convert',
337
+ f: 'png',
338
+ w: 80,
339
+ });
340
+ }
312
341
  const injectHtml = generateHtml({
313
342
  color: loadingColor,
314
343
  image: loadingImage,
@@ -478,13 +507,27 @@ async function setupClient(app, options = {}) {
478
507
  const clientPort = options?.clientPort || port;
479
508
  const enableWsMiddleware = !host;
480
509
  if (enableWsMiddleware) {
481
- process.env.VITE_HMR_MODE = 'middleware';
482
510
  // 创建 hmr proxy
511
+ const hmrWsPath = ufo.joinURL(blockletPrefix, '/__vite_hmr__');
483
512
  const wsProxy = httpProxyMiddleware.createProxyMiddleware({
484
513
  target: `ws://127.0.0.1:${port}`,
485
514
  ws: true,
486
515
  });
487
- app.use(path.join(blockletPrefix, '/__vite_hmr__'), wsProxy);
516
+ try {
517
+ if (options?.server) {
518
+ options.server.on('upgrade', (req, socket, head) => {
519
+ if ((req.originalUrl || req.url).includes(hmrWsPath)) {
520
+ wsProxy.upgrade(req, socket, head);
521
+ }
522
+ });
523
+ process.env.VITE_HMR_MODE = 'wsUpgrade';
524
+ } else {
525
+ throw new Error('Missing options.server, fallback to use middleware mode.');
526
+ }
527
+ } catch {
528
+ process.env.VITE_HMR_MODE = 'middleware';
529
+ app.use(hmrWsPath, wsProxy);
530
+ }
488
531
  } else {
489
532
  process.env.VITE_HMR_MODE = 'server';
490
533
  }
@@ -549,7 +592,7 @@ async function setupClient(app, options = {}) {
549
592
  * @property {string} [loadingImage]
550
593
  * @property {'all'|'mobile'|'desktop'} [debugPlatform='mobile']
551
594
  * @property {string} [debugScript]
552
- * @property {'middleware'|'client'} [hmrMode='middleware']
595
+ * @property {'middleware'|'client'|'server'|'wsUpgrade'} [hmrMode='middleware'] - 当未传入任何 option 参数时,会自动变为 middleware 模式
553
596
  */
554
597
 
555
598
  /**
package/index.js CHANGED
@@ -24,7 +24,7 @@ import setupClient from './libs/client.js';
24
24
  * @property {string} [loadingImage]
25
25
  * @property {'all'|'mobile'|'desktop'} [debugPlatform='mobile']
26
26
  * @property {string} [debugScript]
27
- * @property {'middleware'|'client'} [hmrMode='middleware']
27
+ * @property {'middleware'|'client'|'server'|'wsUpgrade'} [hmrMode='middleware'] - 当未传入任何 option 参数时,会自动变为 middleware 模式
28
28
  */
29
29
 
30
30
  /**
package/libs/client.js CHANGED
@@ -1,4 +1,4 @@
1
- import path from 'node:path';
1
+ import { joinURL } from 'ufo';
2
2
  import getPort from 'get-port';
3
3
  import { createServer } from 'vite';
4
4
  import mri from 'mri';
@@ -35,13 +35,27 @@ export default async function setupClient(app, options = {}) {
35
35
  const clientPort = options?.clientPort || port;
36
36
  const enableWsMiddleware = !host;
37
37
  if (enableWsMiddleware) {
38
- process.env.VITE_HMR_MODE = 'middleware';
39
38
  // 创建 hmr proxy
39
+ const hmrWsPath = joinURL(blockletPrefix, '/__vite_hmr__');
40
40
  const wsProxy = createProxyMiddleware({
41
41
  target: `ws://127.0.0.1:${port}`,
42
42
  ws: true,
43
43
  });
44
- app.use(path.join(blockletPrefix, '/__vite_hmr__'), wsProxy);
44
+ try {
45
+ if (options?.server) {
46
+ options.server.on('upgrade', (req, socket, head) => {
47
+ if ((req.originalUrl || req.url).includes(hmrWsPath)) {
48
+ wsProxy.upgrade(req, socket, head);
49
+ }
50
+ });
51
+ process.env.VITE_HMR_MODE = 'wsUpgrade';
52
+ } else {
53
+ throw new Error('Missing options.server, fallback to use middleware mode.');
54
+ }
55
+ } catch {
56
+ process.env.VITE_HMR_MODE = 'middleware';
57
+ app.use(hmrWsPath, wsProxy);
58
+ }
45
59
  } else {
46
60
  process.env.VITE_HMR_MODE = 'server';
47
61
  }
package/libs/config.js CHANGED
@@ -1,7 +1,5 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import YAML from 'yaml';
4
- import { toBlockletDid, isInBlocklet, blockletPort, blockletPrefix } from './utils.js';
1
+ import { joinURL, withTrailingSlash } from 'ufo';
2
+ import { toBlockletDid, isInBlocklet, blockletPort, blockletPrefix, getBlockletYAML } from './utils.js';
5
3
 
6
4
  export default function createConfigPlugin() {
7
5
  return {
@@ -11,16 +9,16 @@ export default function createConfigPlugin() {
11
9
  server.middlewares.use((req, res, next) => {
12
10
  // blocklet server 会把设置的 base 从请求 url 中移除,所以需要再加回 base
13
11
  if (!req.url.startsWith(blockletPrefix)) {
14
- req.url = path.join(blockletPrefix || '/', req.url);
12
+ req.url = joinURL(blockletPrefix || '/', req.url);
15
13
  }
16
14
  return next();
17
15
  });
18
16
  }
19
17
  },
20
- config(config, { command }) {
18
+ async config(config, { command }) {
21
19
  if (command === 'serve') {
22
20
  const targetConfig = {};
23
- targetConfig.base = path.join('/', config.base || blockletPrefix, '/');
21
+ targetConfig.base = withTrailingSlash(joinURL('/', config.base || blockletPrefix));
24
22
  if (!(config.server && config.server.port)) {
25
23
  const port = blockletPort || 3000;
26
24
  targetConfig.server = {
@@ -33,9 +31,7 @@ export default function createConfigPlugin() {
33
31
  if (command === 'build') {
34
32
  if (!config.base) {
35
33
  try {
36
- const blockletYamlPath = './blocklet.yml';
37
- const blockletYaml = YAML.parse(fs.readFileSync(blockletYamlPath, 'utf8'));
38
- let { name, did } = blockletYaml;
34
+ let { name, did } = await getBlockletYAML();
39
35
  if (!did && name) {
40
36
  did = toBlockletDid(name);
41
37
  }
package/libs/hmr.js CHANGED
@@ -7,7 +7,7 @@ import { blockletPrefix, isInBlocklet } from './utils.js';
7
7
  *
8
8
  * @param {Object} options - The options for the HMR plugin.
9
9
  * @param {string} options.version - The version of the vite version.
10
- * @param {'middleware'|'client'|'server'} options.hmrMode - The version of the vite version.
10
+ * @param {'middleware'|'client'|'server'|'wsUpgrade'} [options.hmrMode = 'client'] - The version of the vite version.
11
11
  * @return {Object} The HMR plugin object.
12
12
  */
13
13
  export default function createHmrPlugin(options = {}) {
@@ -28,17 +28,24 @@ export default function createHmrPlugin(options = {}) {
28
28
  return replacedCode;
29
29
  }
30
30
 
31
- if (['client', 'middleware'].includes(hmrMode)) {
32
- replacedCode = replacedCode.replace(/__HMR_BASE__/g, `"${blockletPrefix}"+__HMR_BASE__`);
31
+ // 兼容不带服务端的情况、vite 以中间件形式挂载到服务端代码的情况
32
+ if (['client', 'middleware', 'wsUpgrade'].includes(hmrMode)) {
33
+ replacedCode = replacedCode.replace(
34
+ /__HMR_BASE__/g,
35
+ `"${blockletPrefix === '/' ? '' : blockletPrefix}"+__HMR_BASE__`,
36
+ );
33
37
  }
34
38
 
35
- if (hmrMode === 'middleware') {
39
+ // 兼容 vite 以中间件形式挂载到服务端代码的情况,无论 ws 是中间件挂载还是 ws 直接监听 upgrade 事件都支持
40
+ if (['middleware', 'wsUpgrade'].includes(hmrMode)) {
36
41
  // 根据页面的协议自动判断端口
37
42
  replacedCode = replacedCode.replace(
38
43
  /__HMR_PORT__/g,
39
44
  'location.port || (location.protocol === "https:" ? 443 : 80);',
40
45
  );
41
-
46
+ }
47
+ // 当 ws 是以中间件的形式挂载到服务端代码时,需要手动在页面触发一次 upgrade 事件
48
+ if (hmrMode === 'middleware') {
42
49
  // 在页面加载时,触发一次 upgrade
43
50
  replacedCode = replacedCode.replace(
44
51
  'function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {',
@@ -46,7 +53,9 @@ export default function createHmrPlugin(options = {}) {
46
53
  );
47
54
  replacedCode = replacedCode.replace('fallback = () => {', 'fallback = async () => {');
48
55
  replacedCode = replacedCode.replace(/socket = setupWebSocket\(/g, 'socket = await setupWebSocket(');
49
-
56
+ }
57
+ // 当 ws 是通过服务端的 proxy 来实现时,需要更改页面自动刷新的判断逻辑
58
+ if (['middleware', 'wsUpgrade'].includes(hmrMode)) {
50
59
  if ([4, 5].includes(pureVersion)) {
51
60
  // 改变刷新页面的判断
52
61
  replacedCode = replacedCode.replace(
package/libs/loading.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { withQuery } from 'ufo';
2
+
1
3
  /**
2
4
  * Generates an HTML string containing a spinner with optional color and image.
3
5
  *
@@ -61,6 +63,10 @@ function generateHtml({ color, image }) {
61
63
  -webkit-animation-delay: -0.16s;
62
64
  animation-delay: -0.16s;
63
65
  }
66
+ #loadingImage {
67
+ margin-bottom: 16px;
68
+ object-fit: contain;
69
+ }
64
70
 
65
71
  @-webkit-keyframes fadeIn {
66
72
  0% { opacity: 0; }
@@ -96,13 +102,23 @@ function generateHtml({ color, image }) {
96
102
  }
97
103
  </style>
98
104
  <div class="spinner-wrapper">
99
- <img src="${image}" width="70" height="70" style="margin-bottom: 16px" />
105
+ <img id="loadingImage" width="70" height="70" />
100
106
  <div class="spinner">
101
107
  <div class="bounce1"></div>
102
108
  <div class="bounce2"></div>
103
109
  <div class="bounce3"></div>
104
110
  </div>
105
- </div>`;
111
+ </div>
112
+ <script>
113
+ (() => {
114
+ const loadingImage = document.getElementById('loadingImage');
115
+ if (window?.blocklet?.appLogo) {
116
+ loadingImage.src = window.blocklet.appLogo;
117
+ } else {
118
+ loadingImage.src = "${image}";
119
+ }
120
+ })();
121
+ </script>`;
106
122
  }
107
123
 
108
124
  /**
@@ -114,11 +130,14 @@ function generateHtml({ color, image }) {
114
130
  * @param {string} [options.loadingImage='/.well-known/service/blocklet/logo?imageFilter=convert&f=png&w=80'] - The URL of the loading image.
115
131
  * @return {Object} - The Vite plugin object.
116
132
  */
117
- export default function createLoadingPlugin({
118
- loadingElementId = 'app',
119
- loadingColor = '#8abaf0',
120
- loadingImage = '/.well-known/service/blocklet/logo?imageFilter=convert&f=png&w=80',
121
- } = {}) {
133
+ export default function createLoadingPlugin({ loadingElementId = 'app', loadingColor = '#8abaf0', loadingImage } = {}) {
134
+ if (!loadingImage) {
135
+ loadingImage = withQuery('/.well-known/service/blocklet/logo', {
136
+ imageFilter: 'convert',
137
+ f: 'png',
138
+ w: 80,
139
+ });
140
+ }
122
141
  const injectHtml = generateHtml({
123
142
  color: loadingColor,
124
143
  image: loadingImage,
package/libs/meta.js CHANGED
@@ -1,10 +1,9 @@
1
- import fs from 'node:fs';
2
- import YAML from 'yaml';
1
+ import { getBlockletYAML } from './utils.js';
3
2
 
4
3
  export default function createMetaPlugin() {
5
4
  return {
6
5
  name: 'blocklet:meta',
7
- transformIndexHtml(html, ctx) {
6
+ async transformIndexHtml(html, ctx) {
8
7
  const tags = [];
9
8
  if (ctx?.chunk?.isEntry) {
10
9
  let script;
@@ -17,12 +16,10 @@ export default function createMetaPlugin() {
17
16
  html = html.replace('</body>', `${script}</body>`);
18
17
  }
19
18
  }
20
- // 如果 index.html 中没有设置 title,则自动注入 blocklet.yml 中的 title
21
- if (!/<title>(.*?)<\/title>/.test(html)) {
22
- const blockletYamlPath = './blocklet.yml';
23
- const blockletYaml = YAML.parse(fs.readFileSync(blockletYamlPath, 'utf8'));
24
- const { title } = blockletYaml;
19
+ const { title } = await getBlockletYAML();
25
20
 
21
+ // 如果 index.html 中没有设置 title,则自动注入 blocklet.yml 中的 title
22
+ if (title && !/<title>(.*?)<\/title>/.test(html)) {
26
23
  tags.push({
27
24
  tag: 'title',
28
25
  children: title,
package/libs/utils.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import YAML from 'yaml';
1
3
  import Mcrypto from '@ocap/mcrypto';
2
4
  import { toHex } from '@ocap/util';
3
5
  import { fromPublicKey, isValid } from '@arcblock/did';
@@ -13,6 +15,13 @@ export function toBlockletDid(name) {
13
15
  return fromPublicKey(pk, { role: types.RoleType.ROLE_ANY });
14
16
  }
15
17
 
18
+ export async function getBlockletYAML() {
19
+ const blockletYamlPath = './blocklet.yml';
20
+ const content = await fs.readFile(blockletYamlPath, 'utf8');
21
+ const blockletYaml = YAML.parse(content);
22
+ return blockletYaml || {};
23
+ }
24
+
16
25
  export const isInBlocklet = !!process.env.BLOCKLET_PORT;
17
26
  export const blockletPort = process.env.BLOCKLET_PORT;
18
27
  export const blockletPrefix = process.env.BLOCKLET_DEV_MOUNT_POINT || '/';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vite-plugin-blocklet",
3
3
  "type": "module",
4
- "version": "0.8.6",
4
+ "version": "0.8.8",
5
5
  "description": "",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -35,6 +35,7 @@
35
35
  "ismobilejs": "^1.1.1",
36
36
  "mri": "^1.2.0",
37
37
  "semver": "^7.6.2",
38
+ "ufo": "^1.5.3",
38
39
  "vite-plugin-node-polyfills": "^0.22.0",
39
40
  "yaml": "^2.4.5"
40
41
  },