pinme 1.0.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/dist/index.js +2 -0
  4. package/package.json +102 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 PINME
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # Pinme CLI
2
+
3
+ A simple and easy-to-use command-line tool for uploading files and directories to the IPFS network.
4
+
5
+ ![Pinme CLI](https://via.placeholder.com/800x200?text=Pinme+CLI)
6
+
7
+ ## Features
8
+
9
+ - šŸš€ Quickly upload files and directories to IPFS
10
+ - šŸ“‚ Support for various file types and sizes
11
+ - šŸ“Š View and manage upload history
12
+ - šŸ”— Automatically generate accessible IPFS links
13
+ - 🌐 Preview uploaded content
14
+
15
+ ## Installation
16
+
17
+ ### Using npm
18
+
19
+ ```bash
20
+ npm install -g @glitterprotocol/pinme
21
+ ```
22
+
23
+ ### Using yarn
24
+
25
+ ```bash
26
+ yarn global add @glitterprotocol/pinme
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Upload files or directories
32
+
33
+ ```bash
34
+ # Interactive upload
35
+ pinme upload
36
+
37
+ # Specify path directly
38
+ pinme upload /path/to/file-or-directory
39
+ ```
40
+
41
+ ### View upload history
42
+
43
+ ```bash
44
+ # Show the last 10 upload records
45
+ pinme list
46
+
47
+ # Or use the shorthand command
48
+ pinme ls
49
+
50
+ # Limit the number of records shown
51
+ pinme list -l 5
52
+
53
+ # Clear all upload history
54
+ pinme list -c
55
+ ```
56
+
57
+ ### Get help
58
+
59
+ ```bash
60
+ # Display help information
61
+ pinme help
62
+
63
+ # Display help for a specific command
64
+ pinme help upload
65
+ ```
66
+
67
+ ## Command Details
68
+
69
+ ### `upload`
70
+
71
+ Upload a file or directory to the IPFS network.
72
+
73
+ ```bash
74
+ pinme upload [path]
75
+ ```
76
+
77
+ **Options:**
78
+ - `path`: Path to the file or directory to upload (optional, if not provided, interactive mode will be entered)
79
+
80
+ **Examples:**
81
+ ```bash
82
+ # Interactive upload
83
+ pinme upload
84
+
85
+ # Upload a specific file
86
+ pinme upload ./example.jpg
87
+
88
+ # Upload an entire directory
89
+ pinme upload ./my-website
90
+ ```
91
+
92
+ ### `list` / `ls`
93
+
94
+ Display upload history.
95
+
96
+ ```bash
97
+ pinme list [options]
98
+ pinme ls [options]
99
+ ```
100
+
101
+ **Options:**
102
+ - `-l, --limit <number>`: Limit the number of records displayed
103
+ - `-c, --clear`: Clear all upload history
104
+
105
+ **Examples:**
106
+ ```bash
107
+ # Show the last 10 records
108
+ pinme list
109
+
110
+ # Show the last 5 records
111
+ pinme ls -l 5
112
+
113
+ # Clear all history records
114
+ pinme list -c
115
+ ```
116
+
117
+ ### `help`
118
+
119
+ Display help information.
120
+
121
+ ```bash
122
+ pinme help [command]
123
+ ```
124
+
125
+ **Options:**
126
+ - `command`: The specific command to view help for (optional)
127
+
128
+ **Examples:**
129
+ ```bash
130
+ # Display general help
131
+ pinme help
132
+
133
+ # Display help for the upload command
134
+ pinme help upload
135
+ ```
136
+
137
+ ## Upload Limits
138
+
139
+ - Single file size limit: 100MB
140
+ - Total directory size limit: 500MB
141
+
142
+ ## File Storage
143
+
144
+ Uploaded files are stored on the IPFS network and accessible through the Glitter Protocol's IPFS gateway. After a successful upload, you will receive:
145
+
146
+ 1. IPFS hash value
147
+ 2. Accessible URL link
148
+
149
+ ## Data Privacy
150
+
151
+ - Upload history is stored locally (`~/.pinme/upload-history.json`)
152
+ - Device ID is stored locally (`~/.pinme/device-id.json`)
153
+ - All content uploaded to IPFS is public, do not upload sensitive information
154
+
155
+ ## Troubleshooting
156
+
157
+ ### Common Issues
158
+
159
+ 1. **Upload Failure**
160
+ - Check network connection
161
+ - Confirm the file/directory exists and has read permissions
162
+ - Confirm the file size does not exceed limits
163
+
164
+ 2. **Command Not Found**
165
+ - Confirm global installation was successful
166
+ - Check PATH environment variable
167
+ - Try reinstalling
168
+
169
+ 3. **History Not Displaying**
170
+ - Check if the `~/.pinme` directory exists
171
+ - Confirm read/write permissions
172
+
173
+ ### Log Locations
174
+
175
+ Logs and configuration files are stored in:
176
+ - Linux/macOS: `~/.pinme/`
177
+ - Windows: `%USERPROFILE%\.pinme\`
178
+
179
+ ## Contribution Guidelines
180
+
181
+ Contributions of code, issue reports, or improvement suggestions are welcome! Please follow these steps:
182
+
183
+ 1. Fork the repository
184
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
185
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
186
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
187
+ 5. Create a Pull Request
188
+
189
+ ## License
190
+
191
+ MIT License - See the [LICENSE](LICENSE) file for details
192
+
193
+ ## Contact Us
194
+
195
+ If you have questions or suggestions, please contact us through:
196
+
197
+ - GitHub Issues: [https://github.com/glitterprotocol/pinme-cli/issues](https://github.com/glitterprotocol/pinme-cli/issues)
198
+ - Email: [support@example.com](mailto:support@example.com)
199
+
200
+ ---
201
+
202
+ Developed and maintained by the [Glitter Protocol](https://github.com/glitterprotocol) team
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ "use strict";var e=require("commander"),o=require("chalk"),t=require("figlet"),r=require("path"),i=require("inquirer");require("ethers"),require("bip39");var s=require("axios"),n=require("fs-extra"),a=require("form-data"),l=require("ora"),c=require("os"),d=require("uuid"),u=require("fs"),p=require("dayjs"),f=require("crypto-js");function h(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var y=h(e),m=h(o),g=h(t),S=h(r),$=h(i),v=h(s),z=h(n),w=h(a),H=h(l),x=h(c),F=h(d),I=h(u),L=h(p),D=h(f);const E=z.default,U=S.default,b=x.default,{v4:q}=F.default,j=U.join(b.homedir(),".pinme"),k=U.join(j,"device-id.json");var T={getDeviceId:function(){try{if(E.existsSync(j)||E.mkdirSync(j,{recursive:!0}),E.existsSync(k)){const e=E.readJsonSync(k);if(e&&e.deviceId)return e.deviceId}const e=q();return E.writeJsonSync(k,{deviceId:e,createdAt:(new Date).toISOString()},{spaces:2}),e}catch(e){return console.error(`Error getting device ID: ${e.message}`),q()}}};const C=I.default,J=S.default,M=10485760,P=524288e3,R=e=>{let o=0;const t=C.readdirSync(e);for(const r of t){const t=J.join(e,r),i=C.statSync(t);i.isFile()?o+=i.size:i.isDirectory()&&(o+=R(t))}return o};var A={checkFileSizeLimit:e=>{const o=C.statSync(e).size;return{size:o,exceeds:o>M,limit:M}},checkDirectorySizeLimit:e=>{let o=0,t={path:"",size:0},r=!1;const i=e=>{const s=C.readdirSync(e);for(const n of s){const s=J.join(e,n),a=C.statSync(s);a.isFile()?(o+=a.size,a.size>t.size&&(t={path:s,size:a.size}),a.size>M&&(r=!0)):a.isDirectory()&&i(s)}};return i(e),{totalSize:o,hasExceeded:r,largestFile:t,exceedsTotalLimit:o>P,limit:M,totalLimit:P}},calculateDirectorySize:R,formatSize:e=>e<1024?e+" B":e<1048576?(e/1024).toFixed(2)+" KB":e<1073741824?(e/1048576).toFixed(2)+" MB":(e/1073741824).toFixed(2)+" GB",SINGLE_FILE_LIMIT:M,TOTAL_SIZE_LIMIT:P};const B=z.default,_=S.default,N=x.default,W=L.default,Y=m.default,{formatSize:O}=A,Q=_.join(N.homedir(),".pinme"),Z=_.join(Q,"upload-history.json"),G=()=>{B.existsSync(Q)||B.mkdirSync(Q,{recursive:!0}),B.existsSync(Z)||B.writeJsonSync(Z,{uploads:[]})},K=(e=10)=>{try{G();return B.readJsonSync(Z).uploads.slice(0,e)}catch(e){return console.error(Y.red(`Error reading upload history: ${e.message}`)),[]}};var V={saveUploadHistory:e=>{try{G();const o=B.readJsonSync(Z),t={timestamp:Date.now(),date:W().format("YYYY-MM-DD HH:mm:ss"),path:e.path,filename:e.filename||_.basename(e.path),contentHash:e.contentHash,previewHash:e.previewHash,size:e.size,fileCount:e.fileCount||1,type:e.isDirectory?"directory":"file"};return o.uploads.unshift(t),B.writeJsonSync(Z,o,{spaces:2}),!0}catch(e){return console.error(Y.red(`Error saving upload history: ${e.message}`)),!1}},getUploadHistory:K,displayUploadHistory:(e=10)=>{const o=K(e);if(0===o.length)return void console.log(Y.yellow("No upload history found."));console.log(Y.bold("\nšŸ“œ Upload History:")),console.log(Y.dim("─".repeat(80))),o.forEach(((e,o)=>{console.log(Y.bold(`#${o+1} - ${e.date}`)),console.log(Y.cyan(`Name: ${e.filename}`)),console.log(Y.cyan(`Path: ${e.path}`)),console.log(Y.cyan(`Type: ${e.type}`)),console.log(Y.cyan(`Size: ${O(e.size)}`)),"directory"===e.type&&console.log(Y.cyan(`Files: ${e.fileCount}`)),console.log(Y.cyan(`Content Hash: ${e.contentHash}`)),e.previewHash?(console.log(Y.cyan(`Preview Hash: ${e.previewHash}`)),console.log(Y.cyan(`URL: https://ipfs.glitterprotocol.dev/ipfs/${e.previewHash}/#/?from=local`))):console.log(Y.cyan(`URL: https://ipfs.glitterprotocol.dev/ipfs/${e.contentHash}`)),console.log(Y.dim("─".repeat(80)))}));const t=o.reduce(((e,o)=>e+o.size),0),r=o.reduce(((e,o)=>e+o.fileCount),0);console.log(Y.bold(`Total Uploads: ${o.length}`)),console.log(Y.bold(`Total Files: ${r}`)),console.log(Y.bold(`Total Size: ${O(t)}`))},clearUploadHistory:()=>{try{return G(),B.writeJsonSync(Z,{uploads:[]}),console.log(Y.green("Upload history cleared successfully.")),!0}catch(e){return console.error(Y.red(`Error clearing upload history: ${e.message}`)),!1}}};const X=v.default,ee=z.default,oe=S.default,te=w.default,re=H.default,ie=m.default,se=x.default,{getDeviceId:ne}=T,{checkFileSizeLimit:ae,checkDirectorySizeLimit:le,calculateDirectorySize:ce,formatSize:de,TOTAL_SIZE_LIMIT:ue}=A,{saveUploadHistory:pe}=V;oe.join(se.homedir(),".pinme","upload-history.json");const fe="https://ipfs.glitterprotocol.dev/api/v2";let he=null;function ye(e,o){const t=[],r=oe.sep;if(he??=e.replace(o,""),ee.statSync(e).isDirectory()){ee.readdirSync(e).forEach((i=>{const s=oe.join(e,i);if(ee.statSync(s).isFile()){const e=ae(s);if(e.exceeds)throw new Error(`File ${i} exceeds size limit of ${e.limit/1048576}MB (size: ${e.size/1048576}MB)`);const o=s.replace(he,"").replaceAll(r,"%2F");t.push({name:o,path:s})}else if(ee.statSync(s).isDirectory()){const e=ye(s,o);t.push(...e)}}))}else console.error("Error: path must be a directory");return t}const me=e=>{let o=0;const t=e=>{const r=ee.readdirSync(e);for(const i of r){const r=oe.join(e,i),s=ee.statSync(r);s.isFile()?o++:s.isDirectory()&&t(r)}};return t(e),o};async function ge(e,o){const t=le(e);if(t.hasExceeded)throw new Error(`File ${t.largestFile.path} exceeds size limit of ${de(t.limit)} (size: ${de(t.largestFile.size)})`);if(t.exceedsTotalLimit)throw new Error(`Total directory size ${de(t.totalSize)} exceeds the limit of ${de(t.totalLimit)}`);const r=new te;e.endsWith(oe.sep)&&(e=e.slice(0,-1));const i=e.split(oe.sep).pop();ye(e,i).forEach((e=>{r.append("file",ee.createReadStream(e.path),{filename:e.name})}));const s=re(`Uploading ${e} to glitter ipfs...`).start(),n=(await X.post(`${fe}/add`,r,{headers:{...r.getHeaders(),uid:o}})).data.data;if(Array.isArray(n)&&n.length>0){s.succeed();const o=n.find((e=>e.Name===i));if(o){ee.statSync(e);const r=me(e),i={path:e,filename:oe.basename(e),contentHash:o.Hash,previewHash:null,size:t.totalSize,fileCount:r,isDirectory:!0};return pe(i),o.Hash}s.fail(),console.log(ie.red("Directory hash not found in response"))}else s.fail(),console.log(ie.red("Invalid response format from IPFS"));return null}var Se=async function(e){try{const o=ne();let t;return t=ee.statSync(e).isDirectory()?await ge(e,o):await async function(e,o){const t=ae(e);if(t.exceeds)throw new Error(`File exceeds size limit of ${de(t.limit)} (size: ${de(t.size)})`);const r=new te;r.append("file",ee.createReadStream(e));const i=re(`Uploading ${e} to glitter ipfs...`).start(),s=(await X.post(`${fe}/add`,r,{headers:{...r.getHeaders(),uid:o}})).data.data;if(s){i.succeed();const o=ee.statSync(e),t=1,r={path:e,filename:oe.basename(e),contentHash:s[0].Hash,previewHash:null,size:o.size,fileCount:t,isDirectory:!1};return pe(r),s[0].Hash}return i.fail(),console.log(ie.red("Invalid response format from IPFS")),null}(e,o),{contentHash:t}}catch(e){return console.log(ie.red(`${e}`)),null}};const $e=S.default,ve=m.default,ze=$.default,we=g.default,He=Se,xe=I.default,Fe=D.default,Ie="https://ipfs.glitterprotocol.dev/ipfs/QmRumtELULDtHJeJVUF1nthQnWLQ81DWCFpXikZS3WeEtU/#/preview/",Le="pinme-secret-key";function De(e,o){try{const t=Fe.RC4.encrypt(e,o).toString();return t.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}catch(o){return console.error(`Encryption error: ${o.message}`),e}}function Ee(e){try{const o=$e.resolve(e);return xe.existsSync(o)?o:null}catch(e){return console.error(ve.red(`error checking path: ${e.message}`)),null}}var Ue="1.0.0";const be=y.default,qe=m.default,je=g.default,ke=async e=>{try{console.log(we.textSync("PINME",{font:"Shadow",horizontalLayout:"default",verticalLayout:"default",width:180,whitespaceBreak:!0}));const e=process.argv[3];if(e&&!e.startsWith("-")){const o=Ee(e);if(!o)return void console.log(ve.red(`path ${e} does not exist`));console.log(ve.blue(`uploading ${o} to ipfs...`));try{const e=await He(o);if(e){const o=De(e.contentHash,Le);console.log(ve.cyan(we.textSync("Successful",{horizontalLayout:"full"}))),console.log(ve.cyan(`URL: ${Ie}${o}`))}else console.log(ve.red("upload failed"))}catch(e){console.error(ve.red(`error uploading: ${e.message}`)),console.error(e.stack)}return}const o=await ze.prompt([{type:"input",name:"path",message:"path to upload: "}]);if(o.path){const e=Ee(o.path);if(!e)return void console.log(ve.red(`path ${o.path} does not exist`));console.log(ve.blue(`uploading ${e} to ipfs...`));try{const o=await He(e);if(o){const e=De(o.contentHash,Le);console.log(ve.cyan(we.textSync("Successful",{horizontalLayout:"full"}))),console.log(ve.cyan(`IPFS Hash: ${o.contentHash}`)),console.log(ve.cyan(`URL: ${Ie}${e}`))}else console.log(ve.red("upload failed"))}catch(e){console.error(ve.red(`error uploading: ${e.message}`)),console.error(e.stack)}}}catch(e){console.error(ve.red(`error executing: ${e.message}`)),console.error(e.stack)}},{displayUploadHistory:Te,clearUploadHistory:Ce}=V;function Je(){console.log(qe.cyan(je.textSync("Pinme",{horizontalLayout:"full"}))),console.log(qe.cyan("A command-line tool for uploading files to IPFS\n"))}be.name("pinme").version(Ue).option("-v, --version","output the current version"),be.command("upload").description("upload a file or directory to IPFS").action((()=>ke())),be.command("list").description("show upload history").option("-l, --limit <number>","limit the number of records to show",parseInt).option("-c, --clear","clear all upload history").action((e=>{e.clear?Ce():Te(e.limit||10)})),be.command("ls").description("alias for 'list' command").option("-l, --limit <number>","limit the number of records to show",parseInt).option("-c, --clear","clear all upload history").action((e=>{e.clear?Ce():Te(e.limit||10)})),be.command("help").description("display help information").action((()=>{Je(),be.help()})),be.on("--help",(()=>{console.log(""),console.log("Examples:"),console.log(" $ pinme upload"),console.log(" $ pinme list -l 5"),console.log(" $ pinme ls"),console.log(" $ pinme help"),console.log(""),console.log("For more information, visit: https://github.com/glitterprotocol/pinme-cli")})),be.parse(process.argv),2===process.argv.length&&(Je(),be.help()),module.exports={};
package/package.json ADDED
@@ -0,0 +1,102 @@
1
+ {
2
+ "name": "pinme",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Pinme",
8
+ "main": "dist/index.js",
9
+ "scripts": {
10
+ "prebuild": "mkdir -p dist/utils",
11
+ "build": "rollup -c",
12
+ "dev": "rollup -c -w",
13
+ "prepublishOnly": "npm run build",
14
+ "test": "echo \"Error: no test specified\" && exit 1",
15
+ "build:debug": "rollup -c --debug"
16
+ },
17
+ "bin": {
18
+ "pinme": "./dist/index.js"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "keywords": [],
24
+ "author": "Ted",
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ "@pinata/sdk": "^2.1.0",
28
+ "axios": "^1.3.2",
29
+ "base-x": "^5.0.1",
30
+ "bip39": "^3.1.0",
31
+ "chalk": "^2.4.2",
32
+ "commander": "^11.1.0",
33
+ "crypto-js": "^4.2.0",
34
+ "dayjs": "^1.11.7",
35
+ "dotenv": "^16.3.1",
36
+ "download-git-repo": "^3.0.2",
37
+ "ethers": "5.7.2",
38
+ "figlet": "^1.7.0",
39
+ "form-data": "^4.0.0",
40
+ "fs-extra": "^11.2.0",
41
+ "i18next": "^22.4.9",
42
+ "i18next-browser-languagedetector": "^7.0.1",
43
+ "inquirer": "^8.2.5",
44
+ "js-file-downloader": "^1.1.24",
45
+ "nanoid": "^4.0.1",
46
+ "ora": "^3.2.0",
47
+ "rollup-plugin-visualizer": "^5.9.2",
48
+ "uuid": "^9.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "@babel/core": "^7.20.12",
52
+ "@babel/plugin-syntax-flow": "^7.14.5",
53
+ "@babel/plugin-transform-react-jsx": "^7.14.9",
54
+ "@esbuild-plugins/esm-externals": "^0.1.2",
55
+ "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
56
+ "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
57
+ "@rollup/plugin-babel": "^5.3.1",
58
+ "@rollup/plugin-commonjs": "^22.0.2",
59
+ "@rollup/plugin-json": "^4.1.0",
60
+ "@rollup/plugin-node-resolve": "^14.1.0",
61
+ "@types/lodash": "^4.14.191",
62
+ "@types/node": "^16.11.7",
63
+ "@types/react": "^18.0.27",
64
+ "@types/react-dom": "^18.0.10",
65
+ "@types/react-router-dom": "^5.3.3",
66
+ "@vitejs/plugin-basic-ssl": "^1.0.1",
67
+ "@vitejs/plugin-react": "^3.1.0",
68
+ "autoprefixer": "^10.4.13",
69
+ "buffer": "^6.0.3",
70
+ "cssnano": "^5.1.14",
71
+ "cssnano-preset-advanced": "^5.3.9",
72
+ "eslint": "^8.33.0",
73
+ "eslint-config-airbnb-base": "^15.0.0",
74
+ "eslint-config-prettier": "^8.6.0",
75
+ "eslint-config-react-app": "^7.0.1",
76
+ "eslint-plugin-import": "^2.27.5",
77
+ "eslint-plugin-prettier": "^4.2.1",
78
+ "eslint-plugin-react": "^7.32.2",
79
+ "less": "^4.1.3",
80
+ "postcss": "^8.4.21",
81
+ "postcss-import": "^15.1.0",
82
+ "postcss-px-to-viewport": "^1.1.1",
83
+ "postcss-url": "^10.1.3",
84
+ "prettier": "^2.8.3",
85
+ "rollup": "^2.79.1",
86
+ "rollup-plugin-commonjs": "^10.1.0",
87
+ "rollup-plugin-copy": "^3.5.0",
88
+ "rollup-plugin-dotenv": "^0.5.1",
89
+ "rollup-plugin-json": "^4.0.0",
90
+ "rollup-plugin-node-polyfills": "^0.2.1",
91
+ "rollup-plugin-node-resolve": "^5.2.0",
92
+ "rollup-plugin-terser": "^7.0.2",
93
+ "typescript": "^4.9.5",
94
+ "vite": "^4.1.1",
95
+ "vite-plugin-compression": "^0.5.1",
96
+ "vite-plugin-rewrite-all": "^1.0.1",
97
+ "vite-plugin-svg-icons": "^2.0.1"
98
+ },
99
+ "engines": {
100
+ "node": ">= 14.18.0"
101
+ }
102
+ }