diff --git a/LICENSE b/LICENSE index f6142d8..ab60297 100644 --- a/LICENSE +++ b/LICENSE @@ -1,191 +1,21 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ +MIT License -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Copyright (c) 2018 -1. Definitions. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "{}" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright 2016 宇天 - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Readme.md b/Readme.md old mode 100755 new mode 100644 index dccc917..09a0df8 --- a/Readme.md +++ b/Readme.md @@ -18,12 +18,12 @@ git clone https://github.com/yutent/crypto.js.git ## 属性 -> 其实就一个属性,即 `crypto`,即为原生的`crypto`对象,方便在封装的方法中无法满足需求时,可以自行调用原生的`crypto`实现。 +> 其实就一个属性,即 `origin`,即为原生的`crypto`对象,方便在封装的方法中无法满足需求时,可以自行调用原生的`crypto`实现。 ## 常用API方法 > 对使用频率非常高的几种加密/编码进行更加简便的封装。 -### 1. rand(len[, forceNum]) +### rand(len[, forceNum]) - len `` - forceNum `` 可选 @@ -37,8 +37,18 @@ crypto.rand(6, true) // 439875 crypto.rand(10, true) // 3458765234 ``` +### uuid() +> 返回一个如下格式的 xxxxxxxx-xxxx-xxxx-xxxxxxxx 的唯一ID -### 2. md5(str[, encode]) +```javascript +let crypto = require('crypto.js') +crypto.uuid() // + +``` + + + +### md5(str[, encode]) - str `` | `` - encode `` 可选 @@ -52,7 +62,7 @@ crypto.md5('hello world', 'base64') // XrY7u+Ae7tCTyyK7j1rNww== ``` -### 3. md5Sign(file) +### md5Sign(file) - file `` > 该方法用于计算文件的md5签名,`file`即为文件的绝对路径。 @@ -62,7 +72,7 @@ crypto.md5Sign('xx.jpg') ``` -### 4. sha1(str[, encode]) +### sha1(str[, encode]) - str `` | `` - encode `` 可选 @@ -76,7 +86,7 @@ crypto.sha1('hello world', 'base64') // Kq5sNclPz7QV2+lfQIuc6R7oRu0= ``` -### 5. sha1Sign(file) +### sha1Sign(file) - file `` > 该方法用于计算文件的sha1签名,`file`即为文件的绝对路径。 @@ -86,7 +96,7 @@ crypto.sha1Sign('xx.jpg') ``` -### 6. sha256(str[, encode]) +### sha256(str[, encode]) - str `` | `` - encode `` 可选 @@ -94,7 +104,7 @@ crypto.sha1Sign('xx.jpg') -### 7. base64encode(str[, urlFriendly]) +### base64encode(str[, urlFriendly]) - str `` | `` | `` - urlFriendly `` 可选 @@ -107,7 +117,7 @@ crypto.base64encode('hello world') //aGVsbG8gd29ybGQ= -### 8. base64decode(str[, urlFriendly]) +### base64decode(str[, urlFriendly]) - str `` - urlFriendly `` 可选 diff --git a/index.js b/index.js new file mode 100644 index 0000000..6b0c9c4 --- /dev/null +++ b/index.js @@ -0,0 +1,215 @@ +/** + * 加密类 md5/sha1/base64 + * @authors yutent (yutent@doui.cc) + * @date 2015-09-10 13:56:18 + */ + +'use strict' + +const CRYPTO = require('crypto') +const FS = require('fs') + +module.exports = { + origin: CRYPTO, + + hash(mode, data, outEncode) { + let sum = CRYPTO.createHash(mode) + let isBuffer = Buffer.isBuffer(data) + + sum.update(data, isBuffer ? 'binary' : 'utf8') + return sum.digest(outEncode || 'hex') + }, + + hmac(mode, data, key, outEncode) { + key = key || '' + let sum = CRYPTO.createHmac(mode, key) + let isBuffer = Buffer.isBuffer(data) + + sum.update(data, isBuffer ? 'binary' : 'utf8') + return sum.digest(outEncode || 'hex') + }, + + cipher(mode, data, key, inEncode, outEncode) { + key = key || '' + let isBuffer = Buffer.isBuffer(data) + inEncode = isBuffer ? 'binary' : inEncode || 'utf8' + outEncode = outEncode || 'base64' + + let cp = CRYPTO.createCipher(mode, key) + let res = cp.update(data, inEncode, outEncode) + return res + cp.final(outEncode) + }, + + decipher(mode, data, key, inEncode, outEncode) { + key = key || '' + let isBuffer = Buffer.isBuffer(data) + inEncode = isBuffer ? 'binary' : inEncode || 'base64' + outEncode = outEncode || 'utf8' + + let dcp = CRYPTO.createDecipher(mode, key) + let res = dcp.update(data, inEncode, outEncode) + return res + dcp.final(outEncode) + }, + + cipheriv(mode, data, key, iv, inEncode, outEncode) { + key = key || '0000000000000000' + iv = iv || '' + let isBuffer = Buffer.isBuffer(data) + inEncode = isBuffer ? 'binary' : inEncode || 'utf8' + outEncode = outEncode || 'base64' + + let cp = CRYPTO.createCipheriv(mode, key, iv) + let res = cp.update(data, inEncode, outEncode) + return res + cp.final(outEncode) + }, + + decipheriv(mode, data, key, iv, inEncode, outEncode) { + key = key || '0000000000000000' + iv = iv || '' + let isBuffer = Buffer.isBuffer(data) + inEncode = isBuffer ? 'binary' : inEncode || 'base64' + outEncode = outEncode || 'utf8' + + let dcp = CRYPTO.createDecipheriv(mode, key, iv) + let res = dcp.update(data, inEncode, outEncode) + return res + dcp.final(outEncode) + }, + + /** + * [rand 生成指定长度的随机字符串] + * @param {[type]} len [要得到的字符串长度] + * @param {[type]} forceNum [是否强制返回纯数字] + */ + rand(len, forceNum) { + let str = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789' + if (forceNum) { + str = '0123456789' + } + let max = str.length + let tmp = '' + for (let i = 0; i < len; i++) { + let r = Math.floor(Math.random() * max) + tmp += str[r] + } + return tmp + }, + + // 返回一个如下格式的 xxxxxxxx-xxxx-xxxx-xxxxxxxx 的唯一ID + uuid() { + let rand = CRYPTO.randomBytes(8).toString('hex') + let stamp = ((Date.now() / 1000) >>> 0).toString(16) + + rand = rand.replace(/^([0-9a-z]{8})([0-9a-z]{4})([0-9a-z]{4})$/, '$1-$2-$3') + return rand + '-' + stamp + }, + + /** + * [md5 md5加密] + * @param {Str/Num} str [要加密的字符串] + * @param {Str} encode [hex/base64] + */ + md5(str, encode) { + if (typeof str !== 'string' && typeof str !== 'number') { + return str + } + + return this.hash('md5', str + '', encode) + }, + + /** + * [md5Sign 获取文件的md5签名] + * @param {Str} file [文件路径] + */ + md5Sign(file) { + if (!fs.existsSync(file)) { + return null + } + + let fileStream = fs.readFileSync(file) + return this.hash('md5', fileStream) + }, + + /** + * [sha1 sha1加密] + * @param {Str/Num} str [要加密的字符串] + * @param {Str} encode [hex/base64] + */ + sha1(str, encode) { + if (typeof str !== 'string' && typeof str !== 'number') { + return str + } + + return this.hash('sha1', str + '', encode) + }, + + /** + * [sha1Sign 获取文件的sha1签名] + * @param {Str} file [文件路径] + */ + sha1Sign(file) { + if (!fs.existsSync(file)) { + return null + } + + let fileStream = fs.readFileSync(file) + return this.hash('sha1', fileStream) + }, + + /** + * [sha256 sha256加密] + * @param {Str/Num} str [要加密的字符串] + * @param {Str} encoding [hex/base64] + */ + sha256(str, encoding) { + if (typeof str !== 'string' && typeof str !== 'number') { + return str + } + + return this.hash('sha256', str + '', encoding) + }, + + /** + * [base64encode base64加密] + * @param {Str/Num/Buffer} str [要加密的字符串] + * @param {bool} urlFriendly [是否对URL友好,默认否,是则会把+转成-,/转成_] + */ + base64encode(str, urlFriendly) { + if (!Buffer.isBuffer(str)) { + str = Buffer.from(str + '') + } + let encode = str.toString('base64') + if (urlFriendly) { + encode = encode + .replace(/[+\/]/g, m => { + return m === '+' ? '-' : '_' + }) + .replace(/=/g, '') + } + + return encode + }, + + /** + * [base64decode base64解密] + * @param {Str} str [要解密的字符串] + * @param {bool} urlFriendly [之前是否对结果采用了URL友好处理] + * @param {Str/Buffer} encoding [编码,默认utf-8] + */ + base64decode(str, urlFriendly, encoding) { + if (urlFriendly) { + str = str + .replace(/[-_]/g, m => { + return m === '-' ? '+' : '/' + }) + .replace(/[^A-Za-z0-9\+\/]/g, '') + } + + let buff = Buffer.from(str, 'base64') + + if (encoding === 'buffer') { + return buff + } + + return buff.toString(encoding || 'ascii') + } +} diff --git a/lib/main.js b/lib/main.js deleted file mode 100755 index e1f21a6..0000000 --- a/lib/main.js +++ /dev/null @@ -1,206 +0,0 @@ -/** - * 加密类 md5/sha1/base64 - * @authors yutent (yutent@doui.cc) - * @date 2015-09-10 13:56:18 - */ - -"use strict"; - -let crypto = require('crypto') -let fs = require('fs') - - -class C { - - constructor(){ - this.crypto = crypto - - this.hash = (mode, data, outEncode) => { - let sum = crypto.createHash(mode) - let isBuffer = Buffer.isBuffer(data) - - sum.update(data, isBuffer ? 'binary' : 'utf8') - return sum.digest(outEncode || 'hex') - } - - this.hmac = (mode, data, key, outEncode) => { - key = key || '' - let sum = crypto.createHmac(mode, key) - let isBuffer = Buffer.isBuffer(data) - - sum.update(data, isBuffer ? 'binary' : 'utf8') - return sum.digest(outEncode || 'hex') - } - - this.cipher = (mode, data, key, inEncode, outEncode) => { - key = key || '' - let isBuffer = Buffer.isBuffer(data) - inEncode = isBuffer ? 'binary' : (inEncode || 'utf8') - outEncode = outEncode || 'base64' - - let cp = crypto.createCipher(mode, key) - let res = cp.update(data, inEncode, outEncode) - return res + cp.final(outEncode) - } - - this.decipher = (mode, data, key, inEncode, outEncode) => { - key = key || '' - let isBuffer = Buffer.isBuffer(data) - inEncode = isBuffer ? 'binary' : (inEncode || 'base64') - outEncode = outEncode || 'utf8' - - let dcp = crypto.createDecipher(mode, key) - let res = dcp.update(data, inEncode, outEncode) - return res + dcp.final(outEncode) - } - - this.cipheriv = (mode, data, key, iv, inEncode, outEncode) => { - key = key || '0000000000000000' - iv = iv || '' - let isBuffer = Buffer.isBuffer(data) - inEncode = isBuffer ? 'binary' : (inEncode || 'utf8') - outEncode = outEncode || 'base64' - - let cp = crypto.createCipheriv(mode, key, iv) - let res = cp.update(data, inEncode, outEncode) - return res + cp.final(outEncode) - } - - this.decipheriv = (mode, data, key, iv, inEncode, outEncode) => { - key = key || '0000000000000000' - iv = iv || '' - let isBuffer = Buffer.isBuffer(data) - inEncode = isBuffer ? 'binary' : (inEncode || 'base64') - outEncode = outEncode || 'utf8' - - let dcp = crypto.createDecipheriv(mode, key, iv) - let res = dcp.update(data, inEncode, outEncode) - return res + dcp.final(outEncode) - } - - - } - - /** - * [rand 生成指定长度的随机字符串] - * @param {[type]} len [要得到的字符串长度] - * @param {[type]} forceNum [是否强制返回纯数字] - */ - rand(len, forceNum){ - let str = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789' - if(forceNum) - str = '0123456789' - let max = str.length - let tmp = '' - for(let i = 0; i < len; i++){ - let r = Math.floor(Math.random() * max) - tmp += str[r] - } - return tmp - } - - - /** - * [md5 md5加密] - * @param {Str/Num} str [要加密的字符串] - * @param {Str} encode [hex/base64] - */ - md5(str, encode){ - if(typeof str !== 'string' && typeof str !== 'number') - return str - - return this.hash('md5', str + '', encode) - } - - /** - * [md5Sign 获取文件的md5签名] - * @param {Str} file [文件路径] - */ - md5Sign(file){ - if(!fs.existsSync(file)) - return - - let fileStream = fs.readFileSync(file) - return this.hash('md5', fileStream) - } - - /** - * [sha1 sha1加密] - * @param {Str/Num} str [要加密的字符串] - * @param {Str} encode [hex/base64] - */ - sha1(str, encode){ - if(typeof str !== 'string' && typeof str !== 'number') - return str - - return this.hash('sha1', str + '', encode) - } - - - /** - * [sha1Sign 获取文件的sha1签名] - * @param {Str} file [文件路径] - */ - sha1Sign(file){ - if(!fs.existsSync(file)) - return - - let fileStream = fs.readFileSync(file) - return this.hash('sha1', fileStream) - } - - - - /** - * [sha256 sha256加密] - * @param {Str/Num} str [要加密的字符串] - * @param {Str} encoding [hex/base64] - */ - sha256(str, encoding){ - if(typeof str !== 'string' && typeof str !== 'number') - return str - - return this.hash('sha256', str + '', encoding) - } - - - - /** - * [base64encode base64加密] - * @param {Str/Num/Buffer} str [要加密的字符串] - * @param {bool} urlFriendly [是否对URL友好,默认否,是则会把+转成-,/转成_] - */ - base64encode(str, urlFriendly){ - if(!Buffer.isBuffer(str)) - str = Buffer.from(str + '') - let encode = str.toString('base64') - if(urlFriendly) - encode = encode.replace(/[+\/]/g, m => {return m === '+' ? '-' : '_'}).replace(/=/g, '') - - return encode - } - - /** - * [base64decode base64解密] - * @param {Str} str [要解密的字符串] - * @param {bool} urlFriendly [之前是否对结果采用了URL友好处理] - * @param {Str/Buffer} encoding [编码,默认utf-8] - */ - base64decode(str, urlFriendly, encoding){ - if(urlFriendly) - str = str.replace(/[-_]/g, m => {return m === '-' ? '+' : '/'}).replace(/[^A-Za-z0-9\+\/]/g, '') - - let buff = Buffer.from(str, 'base64') - - if (encoding === 'buffer') - return buff - - return buff.toString(encoding || 'ascii') - } - - -} - - - -module.exports = new C \ No newline at end of file diff --git a/package.json b/package.json old mode 100755 new mode 100644 index f0a7e09..6f29274 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { "name": "crypto.js", - "version": "1.0.1", + "version": "1.1.0", "description": "原生crypto加密模块的二次封装,简化常用加密函数的使用", "keywords": [ "md5", "sha1", "base64", - "dojs", + "fivejs", "crypto" ], "author": "yutent ", "repository": { "type": "git", "url": "https://github.com/yutent/crypto.js.git" }, "dependencies": {}, "devDependencies": {}, - "main": "lib/main.js" + "main": "index.js" }