diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 00000000..1a2fb332
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index f335ebfb..01fd0ae9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,8 @@
"npmlog": "^6.0.2",
"pkginfo": "^0.4.1",
"promptly": "^3.2.0",
+ "semver": "^7.5.4",
+ "ts-brand": "^0.0.2",
"update-notifier": "^5.1.0",
"yaml": "^2.0.1"
},
@@ -42,10 +44,11 @@
"@types/lodash": "^4.14.199",
"@types/mocha": "^10.0.3",
"@types/node": "^14.18.63",
+ "@types/node-fetch": "^2.6.9",
"@types/npmlog": "^4.1.4",
"@types/pkginfo": "^0.4.1",
"@types/promptly": "^3.0.3",
- "@types/request": "^2.48.11",
+ "@types/request": "^2.48.12",
"@types/test-console": "^2.0.1",
"@types/update-notifier": "^6.0.5",
"@typescript-eslint/eslint-plugin": "^6.8.0",
@@ -212,6 +215,15 @@
"url": "https://opencollective.com/babel"
}
},
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/@babel/eslint-parser": {
"version": "7.18.2",
"dev": true,
@@ -229,6 +241,15 @@
"eslint": "^7.5.0 || ^8.0.0"
}
},
+ "node_modules/@babel/eslint-parser/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/@babel/generator": {
"version": "7.23.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz",
@@ -283,6 +304,15 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -884,29 +914,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
- "node_modules/@npmcli/fs/node_modules/lru-cache": {
- "version": "6.0.0",
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@npmcli/fs/node_modules/semver": {
- "version": "7.3.7",
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@npmcli/move-file": {
"version": "2.0.0",
"license": "MIT",
@@ -1212,31 +1219,6 @@
"semantic-release": ">=19.0.0"
}
},
- "node_modules/@semantic-release/npm/node_modules/lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@semantic-release/npm/node_modules/semver": {
- "version": "7.3.7",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@semantic-release/release-notes-generator": {
"version": "10.0.3",
"dev": true,
@@ -1285,9 +1267,10 @@
}
},
"node_modules/@types/caseless": {
- "version": "0.12.4",
- "dev": true,
- "license": "MIT"
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
+ "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
+ "dev": true
},
"node_modules/@types/cli-table": {
"version": "0.3.2",
@@ -1357,9 +1340,10 @@
"license": "MIT"
},
"node_modules/@types/node-fetch": {
- "version": "2.6.6",
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz",
+ "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@types/node": "*",
"form-data": "^4.0.0"
@@ -1427,9 +1411,10 @@
}
},
"node_modules/@types/request": {
- "version": "2.48.11",
+ "version": "2.48.12",
+ "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz",
+ "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@types/caseless": "*",
"@types/node": "*",
@@ -1439,8 +1424,9 @@
},
"node_modules/@types/request/node_modules/form-data": {
"version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+ "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
@@ -1474,9 +1460,10 @@
"license": "MIT"
},
"node_modules/@types/tough-cookie": {
- "version": "4.0.4",
- "dev": true,
- "license": "MIT"
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
+ "dev": true
},
"node_modules/@types/update-notifier": {
"version": "6.0.5",
@@ -1673,31 +1660,6 @@
}
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
- "version": "7.5.4",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@typescript-eslint/parser": {
"version": "6.8.0",
"dev": true,
@@ -1805,31 +1767,6 @@
}
}
},
- "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.5.4",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@typescript-eslint/utils": {
"version": "6.8.0",
"dev": true,
@@ -1854,31 +1791,6 @@
"eslint": "^7.0.0 || ^8.0.0"
}
},
- "node_modules/@typescript-eslint/utils/node_modules/lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@typescript-eslint/utils/node_modules/semver": {
- "version": "7.5.4",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@typescript-eslint/visitor-keys": {
"version": "6.8.0",
"dev": true,
@@ -2040,16 +1952,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/another-npm-registry-client/node_modules/lru-cache": {
- "version": "6.0.0",
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/another-npm-registry-client/node_modules/npmlog": {
"version": "4.1.2",
"license": "ISC",
@@ -2061,19 +1963,6 @@
"set-blocking": "~2.0.0"
}
},
- "node_modules/another-npm-registry-client/node_modules/semver": {
- "version": "7.3.7",
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/another-npm-registry-client/node_modules/string-width": {
"version": "1.0.2",
"license": "MIT",
@@ -2893,6 +2782,15 @@
"node": ">=10"
}
},
+ "node_modules/conventional-changelog-writer/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/conventional-commits-filter": {
"version": "2.0.7",
"dev": true,
@@ -3395,31 +3293,6 @@
"eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
}
},
- "node_modules/eslint-plugin-vue/node_modules/lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-plugin-vue/node_modules/semver": {
- "version": "7.3.7",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/eslint-scope": {
"version": "5.1.1",
"dev": true,
@@ -4916,6 +4789,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/make-error": {
"version": "1.3.6",
"dev": true,
@@ -5068,20 +4949,6 @@
"node": ">=10"
}
},
- "node_modules/meow/node_modules/semver": {
- "version": "7.3.7",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/meow/node_modules/type-fest": {
"version": "0.18.1",
"dev": true,
@@ -5726,29 +5593,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
- "node_modules/npm-registry-fetch/node_modules/semver": {
- "version": "7.3.7",
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/npm-registry-fetch/node_modules/semver/node_modules/lru-cache": {
- "version": "6.0.0",
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/npm-registry-fetch/node_modules/validate-npm-package-name": {
"version": "4.0.0",
"license": "ISC",
@@ -8283,6 +8127,14 @@
"node": ">=8"
}
},
+ "node_modules/package-json/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"dev": true,
@@ -9132,10 +8984,10 @@
"node": ">=8"
}
},
- "node_modules/semantic-release/node_modules/semver": {
- "version": "7.3.7",
- "dev": true,
- "license": "ISC",
+ "node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -9146,14 +8998,6 @@
"node": ">=10"
}
},
- "node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/semver-diff": {
"version": "3.1.1",
"license": "MIT",
@@ -9164,6 +9008,14 @@
"node": ">=8"
}
},
+ "node_modules/semver-diff/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/semver-regex": {
"version": "3.1.4",
"dev": true,
@@ -9175,6 +9027,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/semver/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/serialize-javascript": {
"version": "6.0.0",
"dev": true,
@@ -9817,6 +9680,11 @@
"typescript": ">=4.2.0"
}
},
+ "node_modules/ts-brand": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/ts-brand/-/ts-brand-0.0.2.tgz",
+ "integrity": "sha512-UhSzWY4On9ZHIj6DKkRYVN/8OaprbLAZ3b/Y2AJwdl6oozSABsQ0PvwDh4vOVdkvOtWQOkIrjctZ1kj8YfF3jA=="
+ },
"node_modules/ts-mocha": {
"version": "10.0.0",
"dev": true,
@@ -10072,29 +9940,6 @@
"url": "https://github.com/yeoman/update-notifier?sponsor=1"
}
},
- "node_modules/update-notifier/node_modules/lru-cache": {
- "version": "6.0.0",
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/update-notifier/node_modules/semver": {
- "version": "7.3.7",
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/uri-js": {
"version": "4.4.1",
"license": "BSD-2-Clause",
@@ -10210,31 +10055,6 @@
"node": ">=4.0"
}
},
- "node_modules/vue-eslint-parser/node_modules/lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/vue-eslint-parser/node_modules/semver": {
- "version": "7.3.7",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"dev": true,
@@ -10560,6 +10380,14 @@
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.3",
"semver": "^6.3.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ }
}
},
"@babel/eslint-parser": {
@@ -10569,6 +10397,14 @@
"eslint-scope": "^5.1.1",
"eslint-visitor-keys": "^2.1.0",
"semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ }
}
},
"@babel/generator": {
@@ -10618,6 +10454,12 @@
"yallist": "^3.0.2"
}
},
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ },
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -11036,20 +10878,6 @@
"requires": {
"@gar/promisify": "^1.1.3",
"semver": "^7.3.5"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "semver": {
- "version": "7.3.7",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
}
},
"@npmcli/move-file": {
@@ -11277,22 +11105,6 @@
"registry-auth-token": "^4.0.0",
"semver": "^7.1.2",
"tempy": "^1.0.0"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "semver": {
- "version": "7.3.7",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
}
},
"@semantic-release/release-notes-generator": {
@@ -11324,7 +11136,9 @@
"version": "2.0.0"
},
"@types/caseless": {
- "version": "0.12.4",
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
+ "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
"dev": true
},
"@types/cli-table": {
@@ -11384,7 +11198,9 @@
"dev": true
},
"@types/node-fetch": {
- "version": "2.6.6",
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz",
+ "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==",
"dev": true,
"requires": {
"@types/node": "*",
@@ -11444,7 +11260,9 @@
}
},
"@types/request": {
- "version": "2.48.11",
+ "version": "2.48.12",
+ "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz",
+ "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==",
"dev": true,
"requires": {
"@types/caseless": "*",
@@ -11455,6 +11273,8 @@
"dependencies": {
"form-data": {
"version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+ "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
@@ -11484,7 +11304,9 @@
"dev": true
},
"@types/tough-cookie": {
- "version": "4.0.4",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"dev": true
},
"@types/update-notifier": {
@@ -11586,22 +11408,6 @@
"natural-compare": "^1.4.0",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "semver": {
- "version": "7.5.4",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
}
},
"@typescript-eslint/parser": {
@@ -11648,22 +11454,6 @@
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "semver": {
- "version": "7.5.4",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
}
},
"@typescript-eslint/utils": {
@@ -11677,22 +11467,6 @@
"@typescript-eslint/types": "6.8.0",
"@typescript-eslint/typescript-estree": "6.8.0",
"semver": "^7.5.4"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "semver": {
- "version": "7.5.4",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
}
},
"@typescript-eslint/visitor-keys": {
@@ -11802,12 +11576,6 @@
"number-is-nan": "^1.0.0"
}
},
- "lru-cache": {
- "version": "6.0.0",
- "requires": {
- "yallist": "^4.0.0"
- }
- },
"npmlog": {
"version": "4.1.2",
"optional": true,
@@ -11818,12 +11586,6 @@
"set-blocking": "~2.0.0"
}
},
- "semver": {
- "version": "7.3.7",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- },
"string-width": {
"version": "1.0.2",
"optional": true,
@@ -12334,6 +12096,14 @@
"semver": "^6.0.0",
"split": "^1.0.0",
"through2": "^4.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ }
}
},
"conventional-commits-filter": {
@@ -12684,22 +12454,6 @@
"postcss-selector-parser": "^6.0.9",
"semver": "^7.3.5",
"vue-eslint-parser": "^8.0.1"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "semver": {
- "version": "7.3.7",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
}
},
"eslint-scope": {
@@ -13580,6 +13334,13 @@
"version": "3.1.0",
"requires": {
"semver": "^6.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
+ }
}
},
"make-error": {
@@ -13682,13 +13443,6 @@
"validate-npm-package-license": "^3.0.1"
}
},
- "semver": {
- "version": "7.3.7",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- },
"type-fest": {
"version": "0.18.1",
"dev": true
@@ -15693,20 +15447,6 @@
"validate-npm-package-name": "^4.0.0"
}
},
- "semver": {
- "version": "7.3.7",
- "requires": {
- "lru-cache": "^6.0.0"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "requires": {
- "yallist": "^4.0.0"
- }
- }
- }
- },
"validate-npm-package-name": {
"version": "4.0.0",
"requires": {
@@ -15854,6 +15594,13 @@
"registry-auth-token": "^4.0.0",
"registry-url": "^5.0.0",
"semver": "^6.2.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
+ }
}
},
"parent-module": {
@@ -16377,25 +16124,38 @@
"resolve-from": {
"version": "5.0.0",
"dev": true
- },
- "semver": {
- "version": "7.3.7",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
}
}
},
"semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "requires": {
+ "lru-cache": "^6.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
},
"semver-diff": {
"version": "3.1.1",
"requires": {
"semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
+ }
}
},
"semver-regex": {
@@ -16834,6 +16594,11 @@
"dev": true,
"requires": {}
},
+ "ts-brand": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/ts-brand/-/ts-brand-0.0.2.tgz",
+ "integrity": "sha512-UhSzWY4On9ZHIj6DKkRYVN/8OaprbLAZ3b/Y2AJwdl6oozSABsQ0PvwDh4vOVdkvOtWQOkIrjctZ1kj8YfF3jA=="
+ },
"ts-mocha": {
"version": "10.0.0",
"dev": true,
@@ -16984,20 +16749,6 @@
"semver": "^7.3.4",
"semver-diff": "^3.1.1",
"xdg-basedir": "^4.0.0"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "semver": {
- "version": "7.3.7",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
}
},
"uri-js": {
@@ -17076,20 +16827,6 @@
"estraverse": {
"version": "5.3.0",
"dev": true
- },
- "lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "semver": {
- "version": "7.3.7",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
}
}
},
diff --git a/package.json b/package.json
index e9108219..36cbf1c4 100644
--- a/package.json
+++ b/package.json
@@ -43,10 +43,11 @@
"@types/lodash": "^4.14.199",
"@types/mocha": "^10.0.3",
"@types/node": "^14.18.63",
+ "@types/node-fetch": "^2.6.9",
"@types/npmlog": "^4.1.4",
"@types/pkginfo": "^0.4.1",
"@types/promptly": "^3.0.3",
- "@types/request": "^2.48.11",
+ "@types/request": "^2.48.12",
"@types/test-console": "^2.0.1",
"@types/update-notifier": "^6.0.5",
"@typescript-eslint/eslint-plugin": "^6.8.0",
@@ -81,6 +82,8 @@
"npmlog": "^6.0.2",
"pkginfo": "^0.4.1",
"promptly": "^3.2.0",
+ "semver": "^7.5.4",
+ "ts-brand": "^0.0.2",
"update-notifier": "^5.1.0",
"yaml": "^2.0.1"
}
diff --git a/src/cmd-add.ts b/src/cmd-add.ts
index 0490b7ec..1380f34f 100644
--- a/src/cmd-add.ts
+++ b/src/cmd-add.ts
@@ -1,17 +1,22 @@
import log from "./logger";
import url from "url";
-import { isUrlVersion } from "./utils/pkg-version";
-import { atVersion, splitPkgName } from "./utils/pkg-name";
-import { GlobalOptions, PkgName, ScopedRegistry } from "./types/global";
+import { isPackageUrl } from "./types/package-url";
+import { GlobalOptions, ScopedRegistry } from "./types/global";
import { tryGetLatestVersion } from "./utils/pkg-info";
import { loadManifest, saveManifest } from "./utils/manifest";
import { env, parseEnv } from "./utils/env";
-
import {
compareEditorVersion,
tryParseEditorVersion,
} from "./utils/editor-version";
import { fetchPackageDependencies, fetchPackageInfo } from "./registry-client";
+import { DomainName, isDomainName } from "./types/domain-name";
+import { SemanticVersion } from "./types/semantic-version";
+import {
+ packageReference,
+ PackageReference,
+ splitPackageReference,
+} from "./types/package-reference";
export type AddOptions = {
test?: boolean;
@@ -27,7 +32,7 @@ type AddResult = {
};
export const add = async function (
- pkgs: PkgName | PkgName[],
+ pkgs: PackageReference | PackageReference[],
options: AddOptions
): Promise {
if (!Array.isArray(pkgs)) pkgs = [pkgs];
@@ -55,7 +60,7 @@ const _add = async function ({
testables,
force,
}: {
- pkg: PkgName;
+ pkg: PackageReference;
testables?: boolean;
force?: boolean;
}): Promise {
@@ -64,9 +69,9 @@ const _add = async function ({
// is upstream package flag
let isUpstreamPackage = false;
// parse name
- const split = splitPkgName(pkg);
- const name = split.name;
- let version = split.version;
+ const split = splitPackageReference(pkg);
+ const name = split[0];
+ let version = split[1];
// load manifest
const manifest = loadManifest();
@@ -76,8 +81,8 @@ const _add = async function ({
manifest.dependencies = {};
}
// packages that added to scope registry
- const pkgsInScope: PkgName[] = [];
- if (version === undefined || !isUrlVersion(version)) {
+ const pkgsInScope: DomainName[] = [];
+ if (version === undefined || !isPackageUrl(version)) {
// verify name
let pkgInfo = await fetchPackageInfo(name);
if (!pkgInfo && env.upstream) {
@@ -89,10 +94,11 @@ const _add = async function ({
return { code: 1, dirty };
}
// verify version
- const versions = Object.keys(pkgInfo.versions);
+ const versions = Object.keys(pkgInfo.versions) as SemanticVersion[];
// eslint-disable-next-line require-atomic-updates
- if (!version || version == "latest") version = tryGetLatestVersion(pkgInfo);
- if (versions.filter((x) => x == version).length <= 0) {
+ if (!version || version === "latest")
+ version = tryGetLatestVersion(pkgInfo);
+ if (versions.filter((x) => x === version).length <= 0) {
log.warn(
"404",
`version ${version} is not a valid choice of: ${versions
@@ -170,7 +176,7 @@ const _add = async function ({
if (!depObj.resolved)
log.notice(
"suggest",
- `to install ${atVersion(
+ `to install ${packageReference(
depObj.name,
depObj.version
)} or a replaceable version manually`
@@ -193,7 +199,7 @@ const _add = async function ({
manifest.dependencies[name] = version;
if (!oldVersion) {
// Log the added package
- log.notice("manifest", `added ${atVersion(name, version)}`);
+ log.notice("manifest", `added ${packageReference(name, version)}`);
dirty = true;
} else if (oldVersion != version) {
// Log the modified package version
@@ -201,7 +207,7 @@ const _add = async function ({
dirty = true;
} else {
// Log the existed package
- log.notice("manifest", `existed ${atVersion(name, version)}`);
+ log.notice("manifest", `existed ${packageReference(name, version)}`);
}
if (!isUpstreamPackage) {
// add to scopedRegistries
@@ -227,7 +233,7 @@ const _add = async function ({
const entry = manifest.scopedRegistries.filter(filterEntry)[0];
// apply pkgsInScope
const scopesSet = new Set(entry.scopes || []);
- pkgsInScope.push(env.namespace);
+ if (isDomainName(env.namespace)) pkgsInScope.push(env.namespace);
pkgsInScope.forEach((name) => {
if (!scopesSet.has(name)) {
scopesSet.add(name);
diff --git a/src/cmd-deps.ts b/src/cmd-deps.ts
index ee63424b..0ab8fd37 100644
--- a/src/cmd-deps.ts
+++ b/src/cmd-deps.ts
@@ -1,20 +1,30 @@
import log from "./logger";
-import { atVersion, splitPkgName } from "./utils/pkg-name";
-import { GlobalOptions, PkgName, PkgVersion } from "./types/global";
+import { GlobalOptions } from "./types/global";
import { parseEnv } from "./utils/env";
import { fetchPackageDependencies } from "./registry-client";
+import { DomainName } from "./types/domain-name";
+import { isPackageUrl } from "./types/package-url";
+import {
+ packageReference,
+ PackageReference,
+ splitPackageReference,
+ VersionReference,
+} from "./types/package-reference";
export type DepsOptions = {
deep?: boolean;
_global: GlobalOptions;
};
-export const deps = async function (pkg: PkgName, options: DepsOptions) {
+export const deps = async function (
+ pkg: PackageReference,
+ options: DepsOptions
+) {
// parse env
const envOk = await parseEnv(options, { checkPath: false });
if (!envOk) return 1;
// parse name
- const { name, version } = splitPkgName(pkg);
+ const [name, version] = splitPackageReference(pkg);
// deps
await _deps({ name, version, deep: options.deep });
return 0;
@@ -25,11 +35,13 @@ const _deps = async function ({
version,
deep,
}: {
- name: PkgName;
- version: PkgVersion | undefined;
+ name: DomainName;
+ version: VersionReference | undefined;
deep?: boolean;
}) {
- // eslint-disable-next-line no-unused-vars
+ if (version !== undefined && isPackageUrl(version))
+ throw new Error("Cannot get dependencies for url-version");
+
const [depsValid, depsInvalid] = await fetchPackageDependencies({
name,
version,
@@ -38,7 +50,7 @@ const _deps = async function ({
depsValid
.filter((x) => !x.self)
.forEach((x) =>
- log.notice("dependency", `${atVersion(x.name, x.version)}`)
+ log.notice("dependency", `${packageReference(x.name, x.version)}`)
);
depsInvalid
.filter((x) => !x.self)
@@ -46,6 +58,6 @@ const _deps = async function ({
let reason = "unknown";
if (x.reason == "package404") reason = "missing dependency";
else if (x.reason == "version404") reason = "missing dependency version";
- log.warn(reason, atVersion(x.name, x.version));
+ log.warn(reason, packageReference(x.name, x.version));
});
};
diff --git a/src/cmd-login.ts b/src/cmd-login.ts
index e5198e47..fbb010d3 100644
--- a/src/cmd-login.ts
+++ b/src/cmd-login.ts
@@ -6,13 +6,18 @@ import { assertIsNpmClientError, getNpmClient } from "./registry-client";
import log from "./logger";
-import { GlobalOptions, Registry } from "./types/global";
+import { GlobalOptions } from "./types/global";
import {
getUpmConfigDir,
loadUpmConfig,
saveUpmConfig,
} from "./utils/upm-config";
import { parseEnv } from "./utils/env";
+import {
+ RegistryUrl,
+ registryUrl,
+ removeTrailingSlash,
+} from "./types/registry-url";
export type LoginOptions = {
username?: string;
@@ -33,9 +38,9 @@ export const login = async function (options: LoginOptions) {
options.password = await promptly.password("Password: ");
if (!options.email) options.email = await promptly.prompt("Email: ");
if (!options._global.registry)
- options._global.registry = await promptly.prompt("Registry: ", {
- validator: [validateRegistry],
- });
+ options._global.registry = (await promptly.prompt("Registry: ", {
+ validator: [registryUrl],
+ })) as RegistryUrl;
let token: string | null = null;
let _auth: string | null = null;
if (options.basicAuth) {
@@ -48,7 +53,7 @@ export const login = async function (options: LoginOptions) {
username: options.username,
password: options.password,
email: options.email,
- registry: options._global.registry,
+ registry: options._global.registry as RegistryUrl,
});
if (result.code == 1) return result.code;
if (!result.token) {
@@ -58,7 +63,7 @@ export const login = async function (options: LoginOptions) {
token = result.token;
// write npm token
await writeNpmToken({
- registry: options._global.registry,
+ registry: options._global.registry as RegistryUrl,
token: result.token,
});
}
@@ -69,7 +74,7 @@ export const login = async function (options: LoginOptions) {
alwaysAuth: options.alwaysAuth || false,
basicAuth: options.basicAuth || false,
email: options.email,
- registry: options._global.registry,
+ registry: options._global.registry as RegistryUrl,
token,
});
};
@@ -86,7 +91,7 @@ const npmLogin = async function ({
username: string;
password: string;
email: string;
- registry: Registry;
+ registry: RegistryUrl;
}) {
const client = getNpmClient();
try {
@@ -125,7 +130,7 @@ const writeNpmToken = async function ({
registry,
token,
}: {
- registry: Registry;
+ registry: RegistryUrl;
token: string;
}) {
const configPath = getNpmrcPath();
@@ -160,7 +165,7 @@ export const getNpmrcPath = function () {
*/
export const generateNpmrcLines = function (
content: string,
- registry: Registry,
+ registry: RegistryUrl,
token: string
) {
let lines = content ? content.split("\n") : [];
@@ -189,16 +194,6 @@ export const generateNpmrcLines = function (
return lines;
};
-/**
- * http protocal validator
- * @param {*} value
- */
-export const validateRegistry = function (value: Registry): Registry {
- if (!/http(s?):\/\//.test(value))
- throw new Error("The registry address should starts with http(s)://");
- return value;
-};
-
/**
* Write npm token to Unity
*/
@@ -214,7 +209,7 @@ const writeUnityToken = async function ({
alwaysAuth: boolean;
basicAuth: boolean;
email: string;
- registry: Registry;
+ registry: RegistryUrl;
token: string | null;
}) {
// Create config dir if necessary
@@ -223,7 +218,7 @@ const writeUnityToken = async function ({
const config = (await loadUpmConfig(configDir)) || {};
if (!config.npmAuth) config.npmAuth = {};
// Remove ending slash of registry
- if (registry.endsWith("/")) registry = registry.replace(/\/$/, "");
+ registry = removeTrailingSlash(registry);
if (basicAuth) {
if (_auth === null) throw new Error("Auth is null");
diff --git a/src/cmd-remove.ts b/src/cmd-remove.ts
index b051367d..b6c93c93 100644
--- a/src/cmd-remove.ts
+++ b/src/cmd-remove.ts
@@ -1,15 +1,20 @@
import log from "./logger";
-import { atVersion, splitPkgName } from "./utils/pkg-name";
-import { GlobalOptions, PkgName, ScopedRegistry } from "./types/global";
+import { GlobalOptions, ScopedRegistry } from "./types/global";
import { loadManifest, saveManifest } from "./utils/manifest";
import { env, parseEnv } from "./utils/env";
+import { isDomainName } from "./types/domain-name";
+import {
+ packageReference,
+ PackageReference,
+ splitPackageReference,
+} from "./types/package-reference";
export type RemoveOptions = {
_global: GlobalOptions;
};
export const remove = async function (
- pkgs: PkgName[] | PkgName,
+ pkgs: PackageReference[] | PackageReference,
options: RemoveOptions
) {
if (!Array.isArray(pkgs)) pkgs = [pkgs];
@@ -29,15 +34,18 @@ export const remove = async function (
return result.code;
};
-const _remove = async function (pkg: PkgName) {
+const _remove = async function (pkg: PackageReference) {
// dirty flag
let dirty = false;
// parse name
- const split = splitPkgName(pkg);
- const name = split.name;
- let version = split.version;
+ const split = splitPackageReference(pkg);
+ const name = split[0];
+ let version = split[1];
if (version) {
- log.warn("", `please replace '${atVersion(name, version)}' with '${name}'`);
+ log.warn(
+ "",
+ `please replace '${packageReference(name, version)}' with '${name}'`
+ );
return { code: 1, dirty };
}
// load manifest
@@ -49,7 +57,7 @@ const _remove = async function (pkg: PkgName) {
if (manifest.dependencies) {
version = manifest.dependencies[name];
if (version) {
- log.notice("manifest", `removed ${atVersion(name, version)}`);
+ log.notice("manifest", `removed ${packageReference(name, version)}`);
delete manifest.dependencies[name];
dirty = true;
} else pkgsNotFound.push(pkg);
@@ -68,7 +76,7 @@ const _remove = async function (pkg: PkgName) {
if (index > -1) {
entry.scopes.splice(index, 1);
const scopesSet = new Set(entry.scopes);
- scopesSet.add(env.namespace);
+ if (isDomainName(env.namespace)) scopesSet.add(env.namespace);
entry.scopes = Array.from(scopesSet).sort();
dirty = true;
}
diff --git a/src/cmd-search.ts b/src/cmd-search.ts
index 2dcb471b..e1d4a4ec 100644
--- a/src/cmd-search.ts
+++ b/src/cmd-search.ts
@@ -5,32 +5,28 @@ import log from "./logger";
import { is404Error, isHttpError } from "./utils/error-type-guards";
import * as os from "os";
import assert from "assert";
-import {
- GlobalOptions,
- PkgInfo,
- PkgName,
- PkgVersion,
- Registry,
- ReverseDomainName,
-} from "./types/global";
+import { GlobalOptions, PkgInfo } from "./types/global";
import { tryGetLatestVersion } from "./utils/pkg-info";
import { env, parseEnv } from "./utils/env";
+import { DomainName } from "./types/domain-name";
+import { SemanticVersion } from "./types/semantic-version";
+import { RegistryUrl } from "./types/registry-url";
type DateString = string;
-type TableRow = [PkgName, PkgVersion, DateString, ""];
+type TableRow = [DomainName, SemanticVersion, DateString, ""];
export type SearchOptions = {
_global: GlobalOptions;
};
export type SearchedPkgInfo = Omit & {
- versions: Record;
+ versions: Record;
};
export type OldSearchResult =
| SearchedPkgInfo[]
- | Record;
+ | Record;
// Get npm fetch options
const getNpmFetchOptions = function (): Options {
@@ -45,7 +41,7 @@ const getNpmFetchOptions = function (): Options {
const searchEndpoint = async function (
keyword: string,
- registry?: Registry
+ registry?: RegistryUrl
): Promise {
if (!registry) registry = env.registry;
try {
diff --git a/src/cmd-view.ts b/src/cmd-view.ts
index b9f76248..0208cf66 100644
--- a/src/cmd-view.ts
+++ b/src/cmd-view.ts
@@ -1,24 +1,35 @@
import chalk from "chalk";
import log from "./logger";
import assert from "assert";
-import { atVersion, splitPkgName } from "./utils/pkg-name";
-import { GlobalOptions, PkgInfo, PkgName } from "./types/global";
+import { GlobalOptions, PkgInfo } from "./types/global";
import { tryGetLatestVersion } from "./utils/pkg-info";
import { env, parseEnv } from "./utils/env";
import { fetchPackageInfo } from "./registry-client";
+import { DomainName } from "./types/domain-name";
+import {
+ packageReference,
+ PackageReference,
+ splitPackageReference,
+} from "./types/package-reference";
export type ViewOptions = {
_global: GlobalOptions;
};
-export const view = async function (pkg: PkgName, options: ViewOptions) {
+export const view = async function (
+ pkg: PackageReference,
+ options: ViewOptions
+) {
// parse env
const envOk = await parseEnv(options, { checkPath: false });
if (!envOk) return 1;
// parse name
- const { name, version } = splitPkgName(pkg);
+ const [name, version] = splitPackageReference(pkg);
if (version) {
- log.warn("", `please replace '${atVersion(name, version)}' with '${name}'`);
+ log.warn(
+ "",
+ `please replace '${packageReference(name, version)}' with '${name}'`
+ );
return 1;
}
// verify name
@@ -79,7 +90,7 @@ const printInfo = function (pkg: PkgInfo) {
if (dependencies && Object.keys(dependencies).length > 0) {
console.log();
console.log("dependencies");
- Object.keys(dependencies)
+ (Object.keys(dependencies) as DomainName[])
.sort()
.forEach((n) => console.log(chalk.yellow(n) + ` ${dependencies[n]}`));
}
diff --git a/src/registry-client.ts b/src/registry-client.ts
index 2e0152be..99d20e32 100644
--- a/src/registry-client.ts
+++ b/src/registry-client.ts
@@ -8,18 +8,14 @@ import RegClient, {
import log from "./logger";
import request from "request";
import assert, { AssertionError } from "assert";
-import {
- Dependency,
- NameVersionPair,
- PkgInfo,
- PkgName,
- PkgVersion,
- Registry,
-} from "./types/global";
+import { Dependency, NameVersionPair, PkgInfo } from "./types/global";
import { env } from "./utils/env";
-import { atVersion, isInternalPackage } from "./utils/pkg-name";
import _ from "lodash";
import { tryGetLatestVersion } from "./utils/pkg-info";
+import { DomainName, isInternalPackage } from "./types/domain-name";
+import { SemanticVersion } from "./types/semantic-version";
+import { packageReference } from "./types/package-reference";
+import { RegistryUrl } from "./types/registry-url";
export type NpmClient = {
rawClient: RegClient;
@@ -96,8 +92,8 @@ export const getNpmClient = (): NpmClient => {
};
// Fetch package info json from registry
export const fetchPackageInfo = async function (
- name: PkgName,
- registry?: Registry
+ name: DomainName,
+ registry?: RegistryUrl
): Promise {
if (!registry) registry = env.registry;
const pkgPath = `${registry}/${name}`;
@@ -124,15 +120,13 @@ export const fetchPackageDependencies = async function ({
version,
deep,
}: {
- name: PkgName;
- version: PkgVersion | undefined;
+ name: DomainName;
+ version: SemanticVersion | "latest" | undefined;
deep?: boolean;
}): Promise<[Dependency[], Dependency[]]> {
log.verbose(
"dependency",
- `fetch: ${
- version !== undefined ? atVersion(name, version) : name
- } deep=${deep}`
+ `fetch: ${packageReference(name, version)} deep=${deep}`
);
// a list of pending dependency {name, version}
const pendingList: NameVersionPair[] = [{ name, version }];
@@ -144,7 +138,7 @@ export const fetchPackageDependencies = async function ({
const depsInvalid = [];
// cached dict: {pkg-name: pkgInfo}
const cachedPacakgeInfoDict: Record<
- PkgVersion,
+ DomainName,
{ pkgInfo: PkgInfo; upstream: boolean }
> = {};
while (pendingList.length > 0) {
@@ -155,11 +149,16 @@ export const fetchPackageDependencies = async function ({
processedList.push(entry);
// create valid depedenency structure
const depObj: Dependency = {
- ...entry,
+ name: entry.name,
+ /*
+ NOTE: entry.version could also be "latest" or undefiend.
+ Later code guarantees that in that case depObj.version will be replaced
+ with a valid-semantic version. So we can assert the value here safely
+ */
+ version: entry.version as SemanticVersion,
internal: isInternalPackage(entry.name),
upstream: false,
self: entry.name == name,
- version: "",
reason: null,
};
if (!depObj.internal) {
@@ -209,9 +208,10 @@ export const fetchPackageDependencies = async function ({
if (!versions.find((x) => x == entry.version)) {
log.warn(
"404",
- `package ${
- version !== undefined ? atVersion(name, version) : name
- } is not a valid choice of ${versions.reverse().join(", ")}`
+ `package ${packageReference(
+ name,
+ version
+ )} is not a valid choice of ${versions.reverse().join(", ")}`
);
depObj.reason = "version404";
// eslint-disable-next-line require-atomic-updates
@@ -222,10 +222,16 @@ export const fetchPackageDependencies = async function ({
}
// add dependencies to pending list
if (depObj.self || deep) {
- const deps: NameVersionPair[] = _.toPairs(
- pkgInfo.versions[entry.version]["dependencies"]
- ).map((x: [PkgName, PkgVersion]): NameVersionPair => {
- return { name: x[0], version: x[1] };
+ const deps: NameVersionPair[] = (
+ _.toPairs(pkgInfo.versions[entry.version]["dependencies"]) as [
+ DomainName,
+ SemanticVersion
+ ][]
+ ).map((x): NameVersionPair => {
+ return {
+ name: x[0],
+ version: x[1],
+ };
});
deps.forEach((x) => pendingList.push(x));
}
@@ -233,13 +239,9 @@ export const fetchPackageDependencies = async function ({
depsValid.push(depObj);
log.verbose(
"dependency",
- `${
- entry.version !== undefined
- ? atVersion(entry.name, entry.version)
- : entry.name
- } ${depObj.internal ? "[internal] " : ""}${
- depObj.upstream ? "[upstream]" : ""
- }`
+ `${packageReference(entry.name, entry.version)} ${
+ depObj.internal ? "[internal] " : ""
+ }${depObj.upstream ? "[upstream]" : ""}`
);
}
}
diff --git a/src/types/another-npm-registry-client.d.ts b/src/types/another-npm-registry-client.d.ts
index 1cdce144..b74b52ad 100644
--- a/src/types/another-npm-registry-client.d.ts
+++ b/src/types/another-npm-registry-client.d.ts
@@ -1,5 +1,7 @@
+import { Response } from "request";
+import { PkgInfo } from "./global";
+
declare module "another-npm-registry-client" {
- import request from "request";
export type NpmAuth =
| {
username: string;
@@ -24,7 +26,7 @@ declare module "another-npm-registry-client" {
error: Error | null,
data: TData,
raw: string,
- res: request.Response
+ res: Response
) => void;
export default class RegClient {
diff --git a/src/types/domain-name.ts b/src/types/domain-name.ts
new file mode 100644
index 00000000..ecb94487
--- /dev/null
+++ b/src/types/domain-name.ts
@@ -0,0 +1,83 @@
+import { Brand } from "ts-brand";
+import assert from "assert";
+
+/**
+ * A string matching the format of a domain name.
+ * @example com.unity
+ * @example com.my-company
+ */
+export type DomainName = Brand;
+
+const segmentRegex = /^(?!.*--|^-.*|.*-$)[a-zA-Z0-9-]+$/;
+
+export const openUpmReverseDomainName = domainName("com.openupm");
+
+function domainSegmentsIn(hostName: string): string[] {
+ return hostName.split(".");
+}
+
+/**
+ * Creates a namespace by reversing the TDL of a host-name.
+ * @param hostname The host-name to reverse.
+ * @example unity.com becomes com.unity.
+ * @example registry.npmjs.org becomes org.npmjs.
+ * @example my-school.ac.at becomes at.ac.my-school
+ */
+export function namespaceFor(hostname: string): DomainName {
+ const segments = domainSegmentsIn(hostname);
+ const namespaceSegments = (function () {
+ const count = segments.length;
+
+ /*
+ NOTE: This function does not handle domains with longer extensions
+ such as registry.example.team.com. In this case it would incorrectly only
+ return "com.team" even though "example" is also part of the domain.
+ Let's just hope this does not happen for now 🤞
+ */
+
+ // 2-part domains, like unity.com
+ if (count < 3) return segments;
+ // Domains with two short extensions like my-school.ac.at
+ if (segments[count - 1].length <= 3 && segments[count - 2].length <= 3)
+ return segments.slice(count - 3);
+ // Domains with one extension such as registry.npmjs.org
+ return segments.slice(count - 2);
+ })();
+ return namespaceSegments.reverse().join(".") as DomainName;
+}
+
+/**
+ * Checks if a string is a domain name. Only does basic syntax validation.
+ * Does not check for correct segment-count etc.
+ * @param s The string
+ */
+export function isDomainName(s: string): s is DomainName {
+ const segments = domainSegmentsIn(s);
+ if (segments === null || segments.length === 0) return false;
+ return segments.every((segment) => segmentRegex.test(segment));
+}
+
+/**
+ * Detect if the given package name is an internal package
+ * @param name The name of the package
+ */
+export const isInternalPackage = (name: DomainName): boolean => {
+ const internals = [
+ "com.unity.ugui",
+ "com.unity.2d.sprite",
+ "com.unity.2d.tilemap",
+ "com.unity.package-manager-ui",
+ "com.unity.ugui",
+ ];
+ return /com.unity.modules/i.test(name) || internals.includes(name);
+};
+
+/**
+ * Constructs a domain-name from a string.
+ * @param s The string.
+ * @throws assert.AssertionError if string is not in valid format
+ */
+export function domainName(s: string): DomainName {
+ assert(isDomainName(s), `"${s}" is a domain name`);
+ return s;
+}
diff --git a/src/types/global.ts b/src/types/global.ts
index 306f93a9..85d2f82c 100644
--- a/src/types/global.ts
+++ b/src/types/global.ts
@@ -1,15 +1,13 @@
import { NpmAuth } from "another-npm-registry-client";
-
-export type PkgVersion = string;
-
-export type ReverseDomainName = string;
-
-export type PkgName = ReverseDomainName | `${ReverseDomainName}@${PkgVersion}`;
+import { IpAddress } from "./ip-address";
+import { DomainName } from "./domain-name";
+import { PackageUrl } from "./package-url";
+import { SemanticVersion } from "./semantic-version";
+import { PackageId } from "./package-id";
+import { RegistryUrl } from "./registry-url";
export type Region = "us" | "cn";
-export type Registry = string;
-
export type EditorVersion = {
major: number;
minor: number;
@@ -27,12 +25,12 @@ export type Env = {
color: boolean;
systemUser: boolean;
wsl: boolean;
- npmAuth?: Record;
- auth: Record;
+ npmAuth?: Record;
+ auth: Record;
upstream: boolean;
- upstreamRegistry: string;
- registry: string;
- namespace: string;
+ upstreamRegistry: RegistryUrl;
+ registry: RegistryUrl;
+ namespace: DomainName | IpAddress;
editorVersion: string | null;
region: Region;
manifestPath: string;
@@ -51,15 +49,15 @@ export type Contact = {
};
export type PkgVersionInfo = {
- _id?: PkgName;
+ _id?: PackageId;
_nodeVersion?: string;
_npmVersion?: string;
_rev?: string;
name: string;
- version: string;
+ version: SemanticVersion;
unity?: string;
unityRelease?: string;
- dependencies?: Record;
+ dependencies?: Record;
license?: string;
displayName?: string;
description?: string;
@@ -74,29 +72,33 @@ export type PkgVersionInfo = {
};
export type PkgInfo = {
- name: ReverseDomainName;
- _id?: PkgName;
+ name: DomainName;
+ _id?: DomainName;
_rev?: string;
_attachments?: Record;
readme?: string;
- versions: Record;
- "dist-tags"?: { latest?: PkgVersion };
- version?: PkgVersion;
+ versions: Record;
+ "dist-tags"?: { latest?: SemanticVersion };
+ version?: SemanticVersion;
description?: string;
keywords?: string[];
- time: Record<"created" | "modified" | PkgVersion, string>;
+ time: {
+ [key: SemanticVersion]: string;
+ created?: string;
+ modified?: string;
+ };
date?: Date;
users?: Record;
};
export type NameVersionPair = {
- name: PkgName;
- version: PkgVersion | undefined;
+ name: DomainName;
+ version: SemanticVersion | "latest" | undefined;
};
export type Dependency = {
- name: PkgName;
- version: PkgVersion;
+ name: DomainName;
+ version?: SemanticVersion;
upstream: boolean;
self: boolean;
internal: boolean;
@@ -106,18 +108,18 @@ export type Dependency = {
export type ScopedRegistry = {
name: string;
- url: string;
- scopes: PkgName[];
+ url: RegistryUrl;
+ scopes: DomainName[];
};
export type PkgManifest = {
- dependencies: Record;
+ dependencies: Record;
scopedRegistries?: ScopedRegistry[];
testables?: string[];
};
export type GlobalOptions = {
- registry?: Registry;
+ registry?: string;
verbose?: boolean;
color?: boolean;
upstream?: boolean;
@@ -133,5 +135,5 @@ export type UpmAuth = {
} & ({ token: string } | { _auth: string });
export type UPMConfig = {
- npmAuth?: Record;
+ npmAuth?: Record;
};
diff --git a/src/types/ip-address.ts b/src/types/ip-address.ts
new file mode 100644
index 00000000..5a9de148
--- /dev/null
+++ b/src/types/ip-address.ts
@@ -0,0 +1,15 @@
+import { Brand } from "ts-brand";
+import net from "node:net";
+
+/**
+ * A string that is either a valid v4 or v6 ip-address
+ */
+export type IpAddress = Brand;
+
+/**
+ * Checks if a string is valid {@link IpAddress}
+ * @param s The string
+ */
+export function isIpAddress(s: string): s is IpAddress {
+ return net.isIPv4(s) || net.isIPv6(s);
+}
diff --git a/src/types/npm-registry-fetch.d.ts b/src/types/npm-registry-fetch.d.ts
index 3dedc984..ea3f1149 100644
--- a/src/types/npm-registry-fetch.d.ts
+++ b/src/types/npm-registry-fetch.d.ts
@@ -1,4 +1,4 @@
-import { Response } from "request";
+import { Response } from "node-fetch";
declare module "npm-registry-fetch" {
class HttpErrorBase extends Error {
diff --git a/src/types/package-id.ts b/src/types/package-id.ts
new file mode 100644
index 00000000..5290502b
--- /dev/null
+++ b/src/types/package-id.ts
@@ -0,0 +1,33 @@
+import { DomainName, isDomainName } from "./domain-name";
+import { isSemanticVersion, SemanticVersion } from "./semantic-version";
+import { trySplitAtFirstOccurrenceOf } from "../utils/string-utils";
+
+/**
+ * Represents a package at a specific version. The version is here is a
+ * concrete semantic version.
+ * @example com.abc.my-package@1.2.3
+ */
+export type PackageId = `${DomainName}@${SemanticVersion}`;
+
+/**
+ * Checks if a string is a package-id
+ * @param s The string
+ */
+export function isPackageId(s: string): s is PackageId {
+ const [name, version] = trySplitAtFirstOccurrenceOf(s, "@");
+ return (
+ isDomainName(name) && version !== undefined && isSemanticVersion(version)
+ );
+}
+
+/**
+ * Constructs a package-id
+ * @param name The package name
+ * @param version The version
+ */
+export function packageId(
+ name: DomainName,
+ version: SemanticVersion
+): PackageId {
+ return `${name}@${version}`;
+}
diff --git a/src/types/package-reference.ts b/src/types/package-reference.ts
new file mode 100644
index 00000000..9fc30d13
--- /dev/null
+++ b/src/types/package-reference.ts
@@ -0,0 +1,73 @@
+import { DomainName, isDomainName } from "./domain-name";
+import { isSemanticVersion, SemanticVersion } from "./semantic-version";
+import { isPackageUrl, PackageUrl } from "./package-url";
+import { trySplitAtFirstOccurrenceOf } from "../utils/string-utils";
+import assert from "assert";
+
+/**
+ * A string with the format of one of the supported version tags.
+ * NOTE: Currently we only support "latest"
+ */
+type PackageTag = "latest";
+
+/**
+ * Reference to a version, either directly by a semantic version or via an
+ * url or tag.
+ */
+export type VersionReference = SemanticVersion | PackageUrl | PackageTag;
+
+/**
+ * References a package. Could be just the name or a reference to a specific
+ * version. Not as specific as a {@link PackageId} as other version-formats
+ * besides semantic versions, such as "latest" are also allowed
+ */
+export type PackageReference = DomainName | `${DomainName}@${VersionReference}`;
+
+/**
+ * Checks if a string is a version-reference
+ * @param s The string
+ */
+function isVersionReference(s: string): s is VersionReference {
+ return s === "latest" || isSemanticVersion(s) || isPackageUrl(s);
+}
+
+/**
+ * Checks if a string is a package-reference
+ * @param s The string
+ */
+export function isPackageReference(s: string): s is PackageReference {
+ const [name, version] = trySplitAtFirstOccurrenceOf(s, "@");
+ return (
+ isDomainName(name) && (version === undefined || isVersionReference(version))
+ );
+}
+
+/**
+ * Split a package-reference into the name and version if present.
+ * @param reference The reference
+ */
+export function splitPackageReference(
+ reference: PackageReference
+): [DomainName, VersionReference | undefined] {
+ return trySplitAtFirstOccurrenceOf(reference, "@") as [
+ DomainName,
+ VersionReference | undefined
+ ];
+}
+
+/**
+ * Constructs a package-reference
+ * @param name The package-name. Will be validated to be a {@link DomainName}
+ * @param version Optional version-reference. Will be validated to be a {@link VersionReference}
+ */
+export function packageReference(
+ name: string,
+ version?: string
+): PackageReference {
+ assert(isDomainName(name), `${name} is valid package-name`);
+ assert(
+ version === undefined || isVersionReference(version),
+ `"${version}" is valid version-reference`
+ );
+ return version !== undefined ? `${name}@${version}` : name;
+}
diff --git a/src/types/package-url.ts b/src/types/package-url.ts
new file mode 100644
index 00000000..59e9b25a
--- /dev/null
+++ b/src/types/package-url.ts
@@ -0,0 +1,19 @@
+import { Brand } from "ts-brand";
+
+/**
+ * A string of an url pointing to a local or remote package
+ */
+export type PackageUrl = Brand;
+
+const isGit = (version: string): boolean => version.startsWith("git");
+
+const isHttp = (version: string): boolean => version.startsWith("http");
+
+const isLocal = (version: string): boolean => version.startsWith("file");
+
+/**
+ * Checks if a version is a package-url
+ * @param version The version
+ */
+export const isPackageUrl = (version: string): version is PackageUrl =>
+ isGit(version) || isHttp(version) || isLocal(version);
diff --git a/src/types/registry-url.ts b/src/types/registry-url.ts
new file mode 100644
index 00000000..b3f20478
--- /dev/null
+++ b/src/types/registry-url.ts
@@ -0,0 +1,45 @@
+import { Brand } from "ts-brand";
+import assert from "assert";
+
+/**
+ * A string of a http-based registry-url
+ */
+export type RegistryUrl = Brand;
+
+/**
+ * Checks that a string is a valid registry
+ * @param s The string
+ */
+export function isRegistryUrl(s: string): s is RegistryUrl {
+ return /http(s?):\/\//.test(s);
+}
+
+/**
+ * Constructs a registry-url
+ * @param s The string
+ * @throws assert.AssertionError if string does not have valid format
+ */
+export function registryUrl(s: string): RegistryUrl {
+ assert(isRegistryUrl(s), `"${s}" is url`);
+ return s;
+}
+
+/**
+ * Removes trailing slash from a registry-url
+ * @param registry The url
+ */
+export function removeTrailingSlash(registry: RegistryUrl): RegistryUrl {
+ if (registry.endsWith("/")) return registry.slice(0, -1) as RegistryUrl;
+ return registry;
+}
+
+/**
+ * Attempts to coerce a string into a registry-url, by
+ * - Prepending http if it is missing
+ * - Removing trailing slashes
+ * @param s The string
+ */
+export function coerceRegistryUrl(s: string): RegistryUrl {
+ if (!s.toLowerCase().startsWith("http")) s = "http://" + s;
+ return removeTrailingSlash(registryUrl(s));
+}
diff --git a/src/types/semantic-version.ts b/src/types/semantic-version.ts
new file mode 100644
index 00000000..d7e16b45
--- /dev/null
+++ b/src/types/semantic-version.ts
@@ -0,0 +1,26 @@
+import { Brand } from "ts-brand";
+import semver from "semver/preload";
+import assert from "assert";
+
+/**
+ * A string with a semantic-version format
+ * @see https://semver.org/
+ */
+export type SemanticVersion = Brand;
+
+/**
+ * Checks if a string is a semantic version
+ * @param s The string
+ */
+export function isSemanticVersion(s: string): s is SemanticVersion {
+ return semver.parse(s) !== null;
+}
+
+/**
+ * Constructs a semantic version from a string.
+ * @param s The string. Will be validated.
+ */
+export function semanticVersion(s: string): SemanticVersion {
+ assert(isSemanticVersion(s), `"${s}" is a semantic version`);
+ return s;
+}
diff --git a/src/utils/env.ts b/src/utils/env.ts
index c49b6f1e..bca13744 100644
--- a/src/utils/env.ts
+++ b/src/utils/env.ts
@@ -1,40 +1,34 @@
import { Env, GlobalOptions } from "../types/global";
import log from "../logger";
import chalk from "chalk";
-import url from "url";
-import net from "node:net";
import { loadUpmConfig } from "./upm-config";
import path from "path";
import fs from "fs";
import yaml from "yaml";
+import { isIpAddress } from "../types/ip-address";
+import { namespaceFor, openUpmReverseDomainName } from "../types/domain-name";
+import {
+ coerceRegistryUrl,
+ RegistryUrl,
+ registryUrl,
+} from "../types/registry-url";
+import url from "url";
+
+export const env: Env = {};
-export const env: Env = {
- auth: {},
- color: false,
- cwd: "",
- editorVersion: null,
- manifestPath: "",
- namespace: "",
- region: "us",
- registry: "",
- systemUser: false,
- upstream: false,
- upstreamRegistry: "",
- wsl: false,
-};
// Parse env
export const parseEnv = async function (
options: { _global: GlobalOptions } & Record,
{ checkPath }: { checkPath: unknown }
) {
// set defaults
- env.registry = "https://package.openupm.com";
- env.namespace = "com.openupm";
+ env.registry = registryUrl("https://package.openupm.com");
env.cwd = "";
env.manifestPath = "";
+ env.namespace = openUpmReverseDomainName;
env.upstream = true;
env.color = true;
- env.upstreamRegistry = "https://packages.unity.com";
+ env.upstreamRegistry = registryUrl("https://packages.unity.com");
env.systemUser = false;
env.wsl = false;
env.editorVersion = null;
@@ -56,22 +50,18 @@ export const parseEnv = async function (
if (options._global.upstream === false) env.upstream = false;
// region cn
if (options._global.cn === true) {
- env.registry = "https://package.openupm.cn";
- env.upstreamRegistry = "https://packages.unity.cn";
+ env.registry = registryUrl("https://package.openupm.cn");
+ env.upstreamRegistry = registryUrl("https://packages.unity.cn");
env.region = "cn";
log.notice("region", "cn");
}
// registry
if (options._global.registry) {
- let registry = options._global.registry;
- if (!registry.toLowerCase().startsWith("http"))
- registry = "http://" + registry;
- if (registry.endsWith("/")) registry = registry.slice(0, -1);
- env.registry = registry;
+ env.registry = coerceRegistryUrl(options._global.registry);
// TODO: Check hostname for null
- const hostname = url.parse(registry).hostname as string;
- if (net.isIP(hostname)) env.namespace = hostname;
- else env.namespace = hostname.split(".").reverse().slice(0, 2).join(".");
+ const hostname = url.parse(env.registry).hostname as string;
+ if (isIpAddress(hostname)) env.namespace = hostname;
+ else env.namespace = namespaceFor(hostname);
}
// auth
if (options._global.systemUser) env.systemUser = true;
@@ -79,9 +69,9 @@ export const parseEnv = async function (
const upmConfig = await loadUpmConfig();
if (upmConfig) {
env.npmAuth = upmConfig.npmAuth;
- if (env.npmAuth) {
- for (const reg in env.npmAuth) {
- const regAuth = env.npmAuth[reg];
+ if (env.npmAuth !== undefined) {
+ (Object.keys(env.npmAuth) as RegistryUrl[]).forEach((reg) => {
+ const regAuth = env.npmAuth![reg];
if ("token" in regAuth) {
env.auth[reg] = {
token: regAuth.token,
@@ -104,7 +94,7 @@ export const parseEnv = async function (
);
log.warn("env.auth", regAuth);
}
- }
+ });
}
}
// log.verbose("env.npmAuth", env.npmAuth);
diff --git a/src/utils/pkg-info.ts b/src/utils/pkg-info.ts
index 1b225b13..62616842 100644
--- a/src/utils/pkg-info.ts
+++ b/src/utils/pkg-info.ts
@@ -1,8 +1,11 @@
-import { PkgInfo, PkgVersion } from "../types/global";
+import { PkgInfo } from "../types/global";
+import { SemanticVersion } from "../types/semantic-version";
const hasLatestDistTag = (
pkgInfo: Partial
-): pkgInfo is Partial & { "dist-tags": { latest: PkgVersion } } => {
+): pkgInfo is Partial & {
+ "dist-tags": { latest: SemanticVersion };
+} => {
return pkgInfo["dist-tags"]?.["latest"] !== undefined;
};
@@ -11,9 +14,9 @@ const hasLatestDistTag = (
* @param pkgInfo The package. All properties are assumed to be potentially missing
*/
export const tryGetLatestVersion = function (pkgInfo: {
- "dist-tags"?: { latest?: PkgVersion };
- version?: PkgVersion;
-}): PkgVersion | undefined {
+ "dist-tags"?: { latest?: SemanticVersion };
+ version?: SemanticVersion;
+}): SemanticVersion | undefined {
if (hasLatestDistTag(pkgInfo)) return pkgInfo["dist-tags"].latest;
else if (pkgInfo.version) return pkgInfo.version;
};
diff --git a/src/utils/pkg-name.ts b/src/utils/pkg-name.ts
deleted file mode 100644
index e663f4b1..00000000
--- a/src/utils/pkg-name.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { PkgName, PkgVersion, ReverseDomainName } from "../types/global";
-
-/**
- * Split package-name, which may include a version into the actual name of the
- * package and the version if it exists
- */
-export const splitPkgName = function (pkgName: PkgName): {
- name: ReverseDomainName;
- version: PkgVersion | undefined;
-} {
- const segments = pkgName.split("@");
- const name = segments[0];
- const version =
- segments.length > 1
- ? segments.slice(1, segments.length).join("@")
- : undefined;
- return { name, version };
-};
-
-/**
- * Merges a package name and version to create a package name for that specific version
- * @param name The name of the package
- * @param version The version of the package
- */
-export const atVersion = (
- name: ReverseDomainName,
- version: PkgVersion
-): PkgName => `${name}@${version}`;
-
-/**
- * Detect if the given package name is an internal package
- * @param name The name of the package
- */
-export const isInternalPackage = (name: ReverseDomainName): boolean => {
- const internals = [
- "com.unity.ugui",
- "com.unity.2d.sprite",
- "com.unity.2d.tilemap",
- "com.unity.package-manager-ui",
- "com.unity.ugui",
- ];
- return /com.unity.modules/i.test(name) || internals.includes(name);
-};
diff --git a/src/utils/pkg-version.ts b/src/utils/pkg-version.ts
deleted file mode 100644
index 171f37cd..00000000
--- a/src/utils/pkg-version.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { PkgVersion } from "../types/global";
-
-const isGit = (version: PkgVersion): boolean => version.startsWith("git");
-
-const isHttp = (version: PkgVersion): boolean => version.startsWith("http");
-
-const isLocal = (version: PkgVersion): boolean => version.startsWith("file");
-
-export const isUrlVersion = (version: PkgVersion): boolean =>
- isGit(version) || isHttp(version) || isLocal(version);
diff --git a/src/utils/string-utils.ts b/src/utils/string-utils.ts
new file mode 100644
index 00000000..1e57281b
--- /dev/null
+++ b/src/utils/string-utils.ts
@@ -0,0 +1,8 @@
+export function trySplitAtFirstOccurrenceOf(
+ s: string,
+ split: string
+): [string, string | undefined] {
+ const elements = s.split(split);
+ if (elements.length === 1) return [s, undefined];
+ return [elements[0], elements.slice(1).join(split)];
+}
diff --git a/test/data-pkg-info.ts b/test/data-pkg-info.ts
new file mode 100644
index 00000000..278089be
--- /dev/null
+++ b/test/data-pkg-info.ts
@@ -0,0 +1,115 @@
+import { PkgInfo, PkgVersionInfo } from "../src/types/global";
+import assert from "assert";
+import { DomainName, isDomainName } from "../src/types/domain-name";
+import {
+ isSemanticVersion,
+ SemanticVersion,
+} from "../src/types/semantic-version";
+import { packageId } from "../src/types/package-id";
+
+/**
+ * Builder class for {@link PkgVersionInfo}
+ */
+class VersionInfoBuilder {
+ readonly version: PkgVersionInfo;
+
+ constructor(name: DomainName, version: SemanticVersion) {
+ this.version = {
+ name,
+ _id: packageId(name, version),
+ version,
+ dependencies: {},
+ contributors: [],
+ };
+ }
+
+ /**
+ * Add a dependency to this version
+ * @param name The name of the dependency
+ * @param version The version
+ */
+ addDependency(name: string, version: string): VersionInfoBuilder {
+ assert(isDomainName(name), `${name} is domain name`);
+ assert(isSemanticVersion(version), `${version} is semantic version`);
+ this.version.dependencies![name] = version;
+ return this;
+ }
+
+ /**
+ * Set an arbitrary value on the version
+ * @param key The key
+ * @param value The value
+ */
+ set<
+ TKey extends keyof Omit<
+ PkgVersionInfo,
+ "version" | "name" | "dependencies" | "_id"
+ >
+ >(key: TKey, value: PkgVersionInfo[TKey]): VersionInfoBuilder {
+ this.version[key] = value;
+ return this;
+ }
+}
+
+/**
+ * Builder class for {@link PkgInfo}
+ */
+class PackageInfoBuilder {
+ readonly package: PkgInfo;
+
+ constructor(name: DomainName) {
+ this.package = {
+ name,
+ _id: name,
+ versions: {},
+ time: {},
+ users: {},
+ _attachments: {},
+ };
+ }
+
+ /**
+ * Adds a version to this package
+ * @param version The name of the version
+ * @param build A builder function
+ */
+ addVersion(
+ version: string,
+ build?: (builder: VersionInfoBuilder) => unknown
+ ): PackageInfoBuilder {
+ assert(isSemanticVersion(version), `${version} is semantic version`);
+ const builder = new VersionInfoBuilder(this.package.name, version);
+ if (build !== undefined) build(builder);
+ this.package.versions[version] = builder.version;
+ this.package["dist-tags"] = {
+ latest: version,
+ };
+ return this;
+ }
+
+ set<
+ TKey extends keyof Omit<
+ PkgInfo,
+ "name" | "version" | "versions" | "dist-tags" | "_id"
+ >
+ >(key: TKey, value: PkgInfo[TKey]): PackageInfoBuilder {
+ this.package[key] = value;
+ return this;
+ }
+}
+
+/**
+ * Helper for building a {@link PkgInfo} object. Does validation and also
+ * sets repeated properties for you
+ * @param name The name of the package
+ * @param build A builder function
+ */
+export function buildPackageInfo(
+ name: string,
+ build?: (builder: PackageInfoBuilder) => unknown
+): PkgInfo {
+ assert(isDomainName(name), `${name} is domain name`);
+ const builder = new PackageInfoBuilder(name);
+ if (build !== undefined) build(builder);
+ return builder.package;
+}
diff --git a/test/data-pkg-manifest.ts b/test/data-pkg-manifest.ts
new file mode 100644
index 00000000..ea6404fe
--- /dev/null
+++ b/test/data-pkg-manifest.ts
@@ -0,0 +1,87 @@
+import { PkgManifest } from "../src/types/global";
+import assert from "assert";
+import { domainName, isDomainName } from "../src/types/domain-name";
+import { exampleRegistryUrl } from "./mock-registry";
+import { isSemanticVersion } from "../src/types/semantic-version";
+
+/**
+ * Builder class for {@link PkgManifest}
+ */
+class PkgManifestBuilder {
+ readonly manifest: PkgManifest;
+
+ constructor() {
+ this.manifest = {
+ dependencies: {},
+ };
+ }
+
+ /**
+ * Add a scope to the manifests scoped registry
+ * @param name The name of the scope
+ */
+ addScope(name: string): PkgManifestBuilder {
+ assert(isDomainName(name), `${name} is domain name`);
+
+ if (this.manifest.scopedRegistries === undefined)
+ this.manifest.scopedRegistries = [
+ {
+ name: "example.com",
+ scopes: [domainName("com.example")],
+ url: exampleRegistryUrl,
+ },
+ ];
+
+ const registry = this.manifest.scopedRegistries![0];
+ registry.scopes = [name, ...registry.scopes];
+ registry.scopes.sort();
+
+ return this;
+ }
+
+ /**
+ * Add a testable to the manifest
+ * @param name The packages name
+ */
+ addTestable(name: string): PkgManifestBuilder {
+ assert(isDomainName(name), `${name} is domain name`);
+ if (this.manifest.testables === undefined) this.manifest.testables = [];
+ this.manifest.testables.push(name);
+ this.manifest.testables.sort();
+ return this;
+ }
+
+ /**
+ * Add a dependency to the manifests scoped registry
+ * @param name The packages name
+ * @param version The packages version
+ * @param withScope Whether to also add the package to the scope
+ * @param testable Whether to also add the package to the testables
+ */
+ addDependency(
+ name: string,
+ version: string,
+ withScope: boolean,
+ testable: boolean
+ ): PkgManifestBuilder {
+ assert(isDomainName(name), `${name} is domain name`);
+ assert(isSemanticVersion(version), `${version} is semantic version`);
+ if (withScope) this.addScope(name);
+ if (testable) this.addTestable(name);
+ this.manifest.dependencies[name] = version;
+ return this;
+ }
+}
+
+/**
+ * Builder function for {@link PkgManifest}. All dependencies will be put
+ * into a default scoped-registry referencing an example registry
+ * @param build A builder function.
+ */
+export function buildPackageManifest(
+ build?: (builder: PkgManifestBuilder) => unknown
+) {
+ const builder = new PkgManifestBuilder();
+ if (build !== undefined) build(builder);
+ return builder.manifest;
+}
diff --git a/test/manifest-assertions.ts b/test/manifest-assertions.ts
index c1ba79c8..9cbbd2c6 100644
--- a/test/manifest-assertions.ts
+++ b/test/manifest-assertions.ts
@@ -1,6 +1,9 @@
-import { PkgManifest, PkgName, PkgVersion } from "../src/types/global";
+import { PkgManifest } from "../src/types/global";
import { loadManifest } from "../src/utils/manifest";
import should from "should";
+import { DomainName } from "../src/types/domain-name";
+import { SemanticVersion } from "../src/types/semantic-version";
+import { PackageUrl } from "../src/types/package-url";
export function shouldHaveManifest(): PkgManifest {
const manifest = loadManifest();
@@ -15,8 +18,8 @@ export function shouldHaveNoManifest() {
export function shouldHaveDependency(
manifest: PkgManifest,
- name: PkgName,
- version: PkgVersion
+ name: DomainName,
+ version: SemanticVersion | PackageUrl
) {
should(manifest.dependencies[name]).equal(version);
}
@@ -24,13 +27,17 @@ export function shouldHaveDependency(
export function shouldNotHaveAnyDependencies(manifest: PkgManifest) {
should(manifest.dependencies).be.empty();
}
-export function shouldNotHaveDependency(manifest: PkgManifest, name: PkgName) {
+
+export function shouldNotHaveDependency(
+ manifest: PkgManifest,
+ name: DomainName
+) {
should(manifest.dependencies[name]).be.undefined();
}
export function shouldHaveRegistryWithScopes(
manifest: PkgManifest,
- scopes: PkgName[]
+ scopes: DomainName[]
) {
should(manifest.scopedRegistries).not.be.undefined();
manifest
diff --git a/test/mock-registry.ts b/test/mock-registry.ts
index 113fd0d8..bcddc0e9 100644
--- a/test/mock-registry.ts
+++ b/test/mock-registry.ts
@@ -1,9 +1,13 @@
-import { PkgInfo, PkgName } from "../src/types/global";
+import { PkgInfo } from "../src/types/global";
import nock from "nock";
import { SearchEndpointResult } from "./types";
+import { domainName, isDomainName } from "../src/types/domain-name";
+import assert from "assert";
+import { registryUrl } from "../src/types/registry-url";
-export const unityRegistryUrl = "https://packages.unity.com";
-export const exampleRegistryUrl = "http://example.com";
+export const unityRegistryUrl = registryUrl("https://packages.unity.com");
+export const exampleRegistryUrl = registryUrl("http://example.com");
+export const exampleRegistryReverseDomain = domainName("com.example");
export function startMockRegistry() {
if (!nock.isActive()) nock.activate();
@@ -23,7 +27,8 @@ export function registerRemoteUpstreamPkg(pkg: PkgInfo) {
nock(exampleRegistryUrl).persist().get(`/${pkg.name}`).reply(404);
}
-export function registerMissingPackage(name: PkgName) {
+export function registerMissingPackage(name: string) {
+ assert(isDomainName(name));
nock(exampleRegistryUrl).persist().get(`/${name}`).reply(404);
nock(unityRegistryUrl).persist().get(`/${name}`).reply(404);
}
diff --git a/test/test-cmd-add.ts b/test/test-cmd-add.ts
index b24d2a0d..ab5910da 100644
--- a/test/test-cmd-add.ts
+++ b/test/test-cmd-add.ts
@@ -1,6 +1,5 @@
import "should";
import { add, AddOptions } from "../src/cmd-add";
-import { PkgInfo, PkgManifest } from "../src/types/global";
import {
exampleRegistryUrl,
registerMissingPackage,
@@ -15,8 +14,30 @@ import {
shouldHaveDependency,
shouldHaveManifest,
} from "./manifest-assertions";
+import { buildPackageInfo } from "./data-pkg-info";
+import { buildPackageManifest } from "./data-pkg-manifest";
+import { domainName } from "../src/types/domain-name";
+import { PackageUrl } from "../src/types/package-url";
+import { semanticVersion } from "../src/types/semantic-version";
+import { packageReference } from "../src/types/package-reference";
describe("cmd-add.ts", function () {
+ const packageMissing = domainName("pkg-not-exist");
+ const packageA = domainName("com.base.package-a");
+ const packageB = domainName("com.base.package-b");
+ const packageC = domainName("com.base.package-c");
+ const packageD = domainName("com.base.package-d");
+ const packageUp = domainName("com.upstream.package-up");
+ const packageLowerEditor = domainName(
+ "com.base.package-with-lower-editor-version"
+ );
+ const packageHigherEditor = domainName(
+ "com.base.package-with-higher-editor-version"
+ );
+ const packageWrongEditor = domainName(
+ "com.base.package-with-wrong-editor-version"
+ );
+
const options: AddOptions = {
_global: {
registry: exampleRegistryUrl,
@@ -49,187 +70,65 @@ describe("cmd-add.ts", function () {
};
describe("add", function () {
let mockConsole: MockConsole = null!;
- const remotePkgInfoA: PkgInfo = {
- name: "com.base.package-a",
- versions: {
- "0.1.0": {
- name: "com.base.package-a",
- version: "0.1.0",
- dependencies: {},
- },
- "1.0.0": {
- name: "com.base.package-a",
- version: "1.0.0",
- dependencies: {},
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoB: PkgInfo = {
- name: "com.base.package-b",
- versions: {
- "1.0.0": {
- name: "com.base.package-b",
- version: "1.0.0",
- dependencies: {},
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoC: PkgInfo = {
- name: "com.base.package-c",
- versions: {
- "1.0.0": {
- name: "com.base.package-c",
- version: "1.0.0",
- dependencies: {
- "com.base.package-d": "1.0.0",
- "com.unity.modules.x": "1.0.0",
- },
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoD: PkgInfo = {
- name: "com.base.package-d",
- versions: {
- "1.0.0": {
- name: "com.base.package-d",
- version: "1.0.0",
- dependencies: {
- "com.upstream.package-up": "1.0.0",
- },
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoWithLowerEditorVersion: PkgInfo = {
- name: "com.base.package-with-lower-editor-version",
- versions: {
- "1.0.0": {
- name: "com.base.package-with-lower-editor-version",
- version: "1.0.0",
- unity: "2019.1",
- unityRelease: "0b1",
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoWithHigherEditorVersion: PkgInfo = {
- name: "com.base.package-with-higher-editor-version",
- versions: {
- "1.0.0": {
- name: "com.base.package-with-higher-editor-version",
- version: "1.0.0",
- unity: "2020.2",
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoWithWrongEditorVersion: PkgInfo = {
- name: "com.base.package-with-wrong-editor-version",
- versions: {
- "1.0.0": {
- name: "com.base.package-with-wrong-editor-version",
- version: "1.0.0",
- unity: "2020",
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoUp: PkgInfo = {
- name: "com.upstream.package-up",
- versions: {
- "1.0.0": {
- name: "com.upstream.package-up",
- version: "1.0.0",
- dependencies: {},
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const defaultManifest: PkgManifest = {
- dependencies: {},
- };
- const expectedManifestA: PkgManifest = {
- dependencies: {
- "com.base.package-a": "1.0.0",
- },
- scopedRegistries: [
- {
- name: "example.com",
- scopes: ["com.base.package-a", "com.example"],
- url: exampleRegistryUrl,
- },
- ],
- };
- const expectedManifestAB: PkgManifest = {
- dependencies: {
- "com.base.package-a": "1.0.0",
- "com.base.package-b": "1.0.0",
- },
- scopedRegistries: [
- {
- name: "example.com",
- scopes: ["com.base.package-a", "com.base.package-b", "com.example"],
- url: exampleRegistryUrl,
- },
- ],
- };
- const expectedManifestC: PkgManifest = {
- dependencies: {
- "com.base.package-c": "1.0.0",
- },
- scopedRegistries: [
- {
- name: "example.com",
- scopes: ["com.base.package-c", "com.base.package-d", "com.example"],
- url: exampleRegistryUrl,
- },
- ],
- };
- const expectedManifestUpstream: PkgManifest = {
- dependencies: {
- "com.upstream.package-up": "1.0.0",
- },
- };
- const expectedManifestTestable: PkgManifest = {
- dependencies: {
- "com.base.package-a": "1.0.0",
- },
- scopedRegistries: [
- {
- name: "example.com",
- scopes: ["com.base.package-a", "com.example"],
- url: exampleRegistryUrl,
- },
- ],
- testables: ["com.base.package-a"],
- };
+
+ const remotePkgInfoA = buildPackageInfo(packageA, (pkg) =>
+ pkg.addVersion("0.1.0").addVersion("1.0.0")
+ );
+ const remotePkgInfoB = buildPackageInfo(packageB, (pkg) =>
+ pkg.addVersion("1.0.0")
+ );
+ const remotePkgInfoC = buildPackageInfo(packageC, (pkg) =>
+ pkg.addVersion("1.0.0", (version) =>
+ version
+ .addDependency(packageD, "1.0.0")
+ .addDependency("com.unity.modules.x", "1.0.0")
+ )
+ );
+ const remotePkgInfoD = buildPackageInfo(packageD, (pkg) =>
+ pkg.addVersion("1.0.0", (version) => {
+ return version.addDependency(packageUp, "1.0.0");
+ })
+ );
+ const remotePkgInfoWithLowerEditorVersion = buildPackageInfo(
+ packageLowerEditor,
+ (pkg) =>
+ pkg.addVersion("1.0.0", (version) =>
+ version.set("unity", "2019.1").set("unityRelease", "0b1")
+ )
+ );
+ const remotePkgInfoWithHigherEditorVersion = buildPackageInfo(
+ packageHigherEditor,
+ (pkg) =>
+ pkg.addVersion("1.0.0", (version) => version.set("unity", "2020.2"))
+ );
+ const remotePkgInfoWithWrongEditorVersion = buildPackageInfo(
+ packageWrongEditor,
+ (pkg) =>
+ pkg.addVersion("1.0.0", (version) => version.set("unity", "2020"))
+ );
+ const remotePkgInfoUp = buildPackageInfo(packageUp, (pkg) =>
+ pkg.addVersion("1.0.0")
+ );
+
+ const defaultManifest = buildPackageManifest();
+ const expectedManifestA = buildPackageManifest((manifest) =>
+ manifest.addDependency(packageA, "1.0.0", true, false)
+ );
+ const expectedManifestAB = buildPackageManifest((manifest) =>
+ manifest
+ .addDependency(packageA, "1.0.0", true, false)
+ .addDependency(packageB, "1.0.0", true, false)
+ );
+ const expectedManifestC = buildPackageManifest((manifest) =>
+ manifest.addDependency(packageC, "1.0.0", true, false).addScope(packageD)
+ );
+ const expectedManifestUpstream = buildPackageManifest((manifest) =>
+ manifest.addDependency(packageUp, "1.0.0", false, false)
+ );
+ const expectedManifestTestable = buildPackageManifest((manifest) =>
+ manifest.addDependency(packageA, "1.0.0", true, true)
+ );
+
beforeEach(function () {
removeWorkDir("test-openupm-cli");
createWorkDir("test-openupm-cli", {
@@ -246,7 +145,7 @@ describe("cmd-add.ts", function () {
registerRemotePkg(remotePkgInfoWithHigherEditorVersion);
registerRemotePkg(remotePkgInfoWithWrongEditorVersion);
registerRemoteUpstreamPkg(remotePkgInfoUp);
- registerMissingPackage("pkg-not-exist");
+ registerMissingPackage(packageMissing);
mockConsole = attachMockConsole();
});
@@ -257,7 +156,7 @@ describe("cmd-add.ts", function () {
});
it("add pkg", async function () {
- const retCode = await add("com.base.package-a", options);
+ const retCode = await add(packageA, options);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestA);
@@ -265,7 +164,10 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg@1.0.0", async function () {
- const retCode = await add("com.base.package-a@1.0.0", options);
+ const retCode = await add(
+ packageReference(packageA, semanticVersion("1.0.0")),
+ options
+ );
retCode.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestA);
@@ -273,7 +175,7 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg@latest", async function () {
- const retCode = await add("com.base.package-a@latest", options);
+ const retCode = await add(packageReference(packageA, "latest"), options);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestA);
@@ -281,9 +183,15 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg@0.1.0 then pkg@1.0.0", async function () {
- const retCode1 = await add("com.base.package-a@0.1.0", options);
+ const retCode1 = await add(
+ packageReference(packageA, semanticVersion("0.1.0")),
+ options
+ );
retCode1.should.equal(0);
- const retCode2 = await add("com.base.package-a@1.0.0", options);
+ const retCode2 = await add(
+ packageReference(packageA, semanticVersion("1.0.0")),
+ options
+ );
retCode2.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestA);
@@ -291,9 +199,15 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add exited pkg version", async function () {
- const retCode1 = await add("com.base.package-a@1.0.0", options);
+ const retCode1 = await add(
+ packageReference(packageA, semanticVersion("1.0.0")),
+ options
+ );
retCode1.should.equal(0);
- const retCode2 = await add("com.base.package-a@1.0.0", options);
+ const retCode2 = await add(
+ packageReference(packageA, semanticVersion("1.0.0")),
+ options
+ );
retCode2.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestA);
@@ -301,7 +215,10 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg@not-exist-version", async function () {
- const retCode = await add("com.base.package-a@2.0.0", options);
+ const retCode = await add(
+ packageReference(packageA, semanticVersion("2.0.0")),
+ options
+ );
retCode.should.equal(1);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(defaultManifest);
@@ -311,44 +228,41 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "1.0.0").should.be.ok();
});
it("add pkg@http", async function () {
- const gitUrl = "https://github.com/yo/com.base.package-a";
- const retCode = await add("com.base.package-a@" + gitUrl, options);
+ const gitUrl = "https://github.com/yo/com.base.package-a" as PackageUrl;
+ const retCode = await add(packageReference(packageA, gitUrl), options);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
- shouldHaveDependency(manifest, "com.base.package-a", gitUrl);
+ shouldHaveDependency(manifest, packageA, gitUrl);
mockConsole.hasLineIncluding("out", "added").should.be.ok();
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg@git", async function () {
- const gitUrl = "git@github.com:yo/com.base.package-a";
- const retCode = await add("com.base.package-a@" + gitUrl, options);
+ const gitUrl = "git@github.com:yo/com.base.package-a" as PackageUrl;
+ const retCode = await add(packageReference(packageA, gitUrl), options);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
- shouldHaveDependency(manifest, "com.base.package-a", gitUrl);
+ shouldHaveDependency(manifest, packageA, gitUrl);
mockConsole.hasLineIncluding("out", "added").should.be.ok();
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg@file", async function () {
- const fileUrl = "file../yo/com.base.package-a";
- const retCode = await add("com.base.package-a@" + fileUrl, options);
+ const fileUrl = "file../yo/com.base.package-a" as PackageUrl;
+ const retCode = await add(packageReference(packageA, fileUrl), options);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
- shouldHaveDependency(manifest, "com.base.package-a", fileUrl);
+ shouldHaveDependency(manifest, packageA, fileUrl);
mockConsole.hasLineIncluding("out", "added").should.be.ok();
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg-not-exist", async function () {
- const retCode = await add("pkg-not-exist", options);
+ const retCode = await add(packageMissing, options);
retCode.should.equal(1);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(defaultManifest);
mockConsole.hasLineIncluding("out", "package not found").should.be.ok();
});
it("add more than one pkgs", async function () {
- const retCode = await add(
- ["com.base.package-a", "com.base.package-b"],
- options
- );
+ const retCode = await add([packageA, packageB], options);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestAB);
@@ -361,7 +275,7 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg from upstream", async function () {
- const retCode = await add("com.upstream.package-up", upstreamOptions);
+ const retCode = await add(packageUp, upstreamOptions);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestUpstream);
@@ -371,14 +285,17 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg-not-exist from upstream", async function () {
- const retCode = await add("pkg-not-exist", upstreamOptions);
+ const retCode = await add(packageMissing, upstreamOptions);
retCode.should.equal(1);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(defaultManifest);
mockConsole.hasLineIncluding("out", "package not found").should.be.ok();
});
it("add pkg with nested dependencies", async function () {
- const retCode = await add("com.base.package-c@latest", upstreamOptions);
+ const retCode = await add(
+ packageReference(packageC, "latest"),
+ upstreamOptions
+ );
retCode.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestC);
@@ -386,7 +303,7 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg with tests", async function () {
- const retCode = await add("com.base.package-a", testableOptions);
+ const retCode = await add(packageA, testableOptions);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(expectedManifestTestable);
@@ -394,47 +311,32 @@ describe("cmd-add.ts", function () {
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg with lower editor version", async function () {
- const retCode = await add(
- "com.base.package-with-lower-editor-version",
- testableOptions
- );
+ const retCode = await add(packageLowerEditor, testableOptions);
retCode.should.equal(0);
mockConsole.hasLineIncluding("out", "added").should.be.ok();
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
});
it("add pkg with higher editor version", async function () {
- const retCode = await add(
- "com.base.package-with-higher-editor-version",
- testableOptions
- );
+ const retCode = await add(packageHigherEditor, testableOptions);
retCode.should.equal(1);
mockConsole
.hasLineIncluding("out", "requires 2020.2 but found 2019.2.13f1")
.should.be.ok();
});
it("force add pkg with higher editor version", async function () {
- const retCode = await add(
- "com.base.package-with-higher-editor-version",
- forceOptions
- );
+ const retCode = await add(packageHigherEditor, forceOptions);
retCode.should.equal(0);
mockConsole
.hasLineIncluding("out", "requires 2020.2 but found 2019.2.13f1")
.should.be.ok();
});
it("add pkg with wrong editor version", async function () {
- const retCode = await add(
- "com.base.package-with-wrong-editor-version",
- testableOptions
- );
+ const retCode = await add(packageWrongEditor, testableOptions);
retCode.should.equal(1);
mockConsole.hasLineIncluding("out", "2020 is not valid").should.be.ok();
});
it("force add pkg with wrong editor version", async function () {
- const retCode = await add(
- "com.base.package-with-wrong-editor-version",
- forceOptions
- );
+ const retCode = await add(packageWrongEditor, forceOptions);
retCode.should.equal(0);
mockConsole.hasLineIncluding("out", "2020 is not valid").should.be.ok();
});
diff --git a/test/test-cmd-deps.ts b/test/test-cmd-deps.ts
index 082e01a9..fd91acb5 100644
--- a/test/test-cmd-deps.ts
+++ b/test/test-cmd-deps.ts
@@ -8,9 +8,11 @@ import {
startMockRegistry,
stopMockRegistry,
} from "./mock-registry";
-import { PkgInfo } from "../src/types/global";
import { createWorkDir, getWorkDir, removeWorkDir } from "./mock-work-dir";
import { attachMockConsole, MockConsole } from "./mock-console";
+import { buildPackageInfo } from "./data-pkg-info";
+import { domainName } from "../src/types/domain-name";
+import { packageReference } from "../src/types/package-reference";
describe("cmd-deps.ts", function () {
const options: DepsOptions = {
@@ -22,52 +24,20 @@ describe("cmd-deps.ts", function () {
describe("deps", function () {
let mockConsole: MockConsole = null!;
- const remotePkgInfoA: PkgInfo = {
- name: "com.example.package-a",
- versions: {
- "1.0.0": {
- name: "com.example.package-a",
- version: "1.0.0",
- dependencies: {
- "com.example.package-b": "1.0.0",
- },
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoB: PkgInfo = {
- name: "com.example.package-b",
- versions: {
- "1.0.0": {
- name: "com.example.package-b",
- version: "1.0.0",
- dependencies: {
- "com.example.package-up": "1.0.0",
- },
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
- const remotePkgInfoUp: PkgInfo = {
- name: "com.example.package-up",
- versions: {
- "1.0.0": {
- name: "com.example.package-up",
- version: "1.0.0",
- dependencies: {},
- },
- },
- "dist-tags": {
- latest: "1.0.0",
- },
- time: {},
- };
+ const remotePkgInfoA = buildPackageInfo("com.example.package-a", (pkg) =>
+ pkg.addVersion("1.0.0", (version) =>
+ version.addDependency("com.example.package-b", "1.0.0")
+ )
+ );
+ const remotePkgInfoB = buildPackageInfo("com.example.package-b", (pkg) =>
+ pkg.addVersion("1.0.0", (version) =>
+ version.addDependency("com.example.package-up", "1.0.0")
+ )
+ );
+ const remotePkgInfoUp = buildPackageInfo("com.example.package-up", (pkg) =>
+ pkg.addVersion("1.0.0")
+ );
+
beforeEach(function () {
removeWorkDir("test-openupm-cli");
createWorkDir("test-openupm-cli", { manifest: true });
@@ -85,53 +55,52 @@ describe("cmd-deps.ts", function () {
mockConsole.detach();
});
it("deps pkg", async function () {
- const retCode = await deps("com.example.package-a", options);
+ const retCode = await deps(remotePkgInfoA.name, options);
retCode.should.equal(0);
- mockConsole
- .hasLineIncluding("out", "com.example.package-b")
- .should.be.ok();
+ mockConsole.hasLineIncluding("out", remotePkgInfoB.name).should.be.ok();
});
it("deps pkg --deep", async function () {
- const retCode = await deps("com.example.package-a", {
+ const retCode = await deps(remotePkgInfoA.name, {
...options,
deep: true,
});
retCode.should.equal(0);
- mockConsole
- .hasLineIncluding("out", "com.example.package-b")
- .should.be.ok();
- mockConsole
- .hasLineIncluding("out", "com.example.package-up")
- .should.be.ok();
+ mockConsole.hasLineIncluding("out", remotePkgInfoB.name).should.be.ok();
+ mockConsole.hasLineIncluding("out", remotePkgInfoUp.name).should.be.ok();
});
it("deps pkg@latest", async function () {
- const retCode = await deps("com.example.package-a@latest", options);
+ const retCode = await deps(
+ packageReference(remotePkgInfoA.name, "latest"),
+ options
+ );
retCode.should.equal(0);
- mockConsole
- .hasLineIncluding("out", "com.example.package-b")
- .should.be.ok();
+ mockConsole.hasLineIncluding("out", remotePkgInfoB.name).should.be.ok();
});
it("deps pkg@1.0.0", async function () {
- const retCode = await deps("com.example.package-a@1.0.0", options);
+ const retCode = await deps(
+ packageReference(remotePkgInfoA.name, "1.0.0"),
+ options
+ );
retCode.should.equal(0);
- mockConsole
- .hasLineIncluding("out", "com.example.package-b")
- .should.be.ok();
+ mockConsole.hasLineIncluding("out", remotePkgInfoB.name).should.be.ok();
});
it("deps pkg@not-exist-version", async function () {
- const retCode = await deps("com.example.package-a@2.0.0", options);
+ const retCode = await deps(
+ packageReference(remotePkgInfoA.name, "2.0.0"),
+ options
+ );
retCode.should.equal(0);
mockConsole
.hasLineIncluding("out", "is not a valid choice")
.should.be.ok();
});
it("deps pkg-not-exist", async function () {
- const retCode = await deps("pkg-not-exist", options);
+ const retCode = await deps(domainName("pkg-not-exist"), options);
retCode.should.equal(0);
mockConsole.hasLineIncluding("out", "not found").should.be.ok();
});
it("deps pkg upstream", async function () {
- const retCode = await deps("com.example.package-up", options);
+ const retCode = await deps(remotePkgInfoUp.name, options);
retCode.should.equal(0);
});
});
diff --git a/test/test-cmd-login.ts b/test/test-cmd-login.ts
index 2c42d653..62dc4f85 100644
--- a/test/test-cmd-login.ts
+++ b/test/test-cmd-login.ts
@@ -1,38 +1,20 @@
import "nock";
-import should from "should";
-import {
- generateNpmrcLines,
- getNpmrcPath,
- validateRegistry,
-} from "../src/cmd-login";
+import { generateNpmrcLines, getNpmrcPath } from "../src/cmd-login";
+import { registryUrl } from "../src/types/registry-url";
describe("cmd-login.ts", function () {
- describe("validateRegistry", function () {
- it("should validate http", async function () {
- validateRegistry("http://registry.npmjs.org/").should.be.ok();
- });
- it("should validate https", async function () {
- validateRegistry("https://registry.npmjs.org/").should.be.ok();
- });
- it("should reject without http protocal", async function () {
- should(function () {
- validateRegistry("registry.npmjs.org/");
- }).throw("The registry address should starts with http(s)://");
- });
- });
-
describe("generateNpmrcLines", function () {
it("should append token to empty content", async function () {
generateNpmrcLines(
"",
- "http://registry.npmjs.org/",
+ registryUrl("http://registry.npmjs.org/"),
"123-456-789"
).should.deepEqual(["//registry.npmjs.org/:_authToken=123-456-789"]);
});
it("should append token to exist contents", async function () {
generateNpmrcLines(
"registry=https://registry.npmjs.org/",
- "http://registry.npmjs.org/",
+ registryUrl(" registry(http://registry.npmjs.org/"),
"123-456-789"
).should.deepEqual([
"registry=https://registry.npmjs.org/",
@@ -42,7 +24,7 @@ describe("cmd-login.ts", function () {
it("should replace token to exist contents", async function () {
generateNpmrcLines(
"registry=https://registry.npmjs.org/\n//127.0.0.1:4873/:_authToken=blar-blar-blar\n//registry.npmjs.org/:_authToken=blar-blar-blar",
- "http://registry.npmjs.org/",
+ registryUrl("http://registry.npmjs.org/"),
"123-456-789"
).should.deepEqual([
"registry=https://registry.npmjs.org/",
@@ -53,19 +35,19 @@ describe("cmd-login.ts", function () {
it("should handle registry without trailing slash", async function () {
generateNpmrcLines(
"",
- "http://registry.npmjs.org",
+ registryUrl("http://registry.npmjs.org"),
"123-456-789"
).should.deepEqual(["//registry.npmjs.org/:_authToken=123-456-789"]);
});
it("should quote token if necessary", async function () {
generateNpmrcLines(
"",
- "http://registry.npmjs.org/",
+ registryUrl("http://registry.npmjs.org/"),
"=123-456-789="
).should.deepEqual(['//registry.npmjs.org/:_authToken="=123-456-789="']);
generateNpmrcLines(
"",
- "http://registry.npmjs.org/",
+ registryUrl("http://registry.npmjs.org/"),
"?123-456-789?"
).should.deepEqual(['//registry.npmjs.org/:_authToken="?123-456-789?"']);
});
diff --git a/test/test-cmd-remove.ts b/test/test-cmd-remove.ts
index a288a5bc..6ad76d58 100644
--- a/test/test-cmd-remove.ts
+++ b/test/test-cmd-remove.ts
@@ -1,7 +1,9 @@
import "should";
import { remove } from "../src/cmd-remove";
-import { PkgManifest } from "../src/types/global";
-import { exampleRegistryUrl } from "./mock-registry";
+import {
+ exampleRegistryReverseDomain,
+ exampleRegistryUrl,
+} from "./mock-registry";
import { createWorkDir, getWorkDir, removeWorkDir } from "./mock-work-dir";
import { attachMockConsole, MockConsole } from "./mock-console";
import {
@@ -9,27 +11,25 @@ import {
shouldHaveRegistryWithScopes,
shouldNotHaveDependency,
} from "./manifest-assertions";
+import { buildPackageManifest } from "./data-pkg-manifest";
+import { domainName } from "../src/types/domain-name";
+import { semanticVersion } from "../src/types/semantic-version";
+import { packageReference } from "../src/types/package-reference";
+
+const packageA = domainName("com.example.package-a");
+const packageB = domainName("com.example.package-b");
+const missingPackage = domainName("pkg-not-exist");
describe("cmd-remove.ts", function () {
describe("remove", function () {
let mockConsole: MockConsole = null!;
- const defaultManifest: PkgManifest = {
- dependencies: {
- "com.example.package-a": "1.0.0",
- "com.example.package-b": "1.0.0",
- },
- scopedRegistries: [
- {
- name: "example.com",
- scopes: [
- "com.example",
- "com.example.package-a",
- "com.example.package-b",
- ],
- url: exampleRegistryUrl,
- },
- ],
- };
+
+ const defaultManifest = buildPackageManifest((manifest) =>
+ manifest
+ .addDependency(packageA, "1.0.0", true, false)
+ .addDependency(packageB, "1.0.0", true, false)
+ );
+
beforeEach(function () {
removeWorkDir("test-openupm-cli");
createWorkDir("test-openupm-cli", {
@@ -48,13 +48,13 @@ describe("cmd-remove.ts", function () {
chdir: getWorkDir("test-openupm-cli"),
},
};
- const retCode = await remove("com.example.package-a", options);
+ const retCode = await remove(packageA, options);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
- shouldNotHaveDependency(manifest, "com.example.package-a");
+ shouldNotHaveDependency(manifest, packageA);
shouldHaveRegistryWithScopes(manifest, [
- "com.example",
- "com.example.package-b",
+ exampleRegistryReverseDomain,
+ packageB,
]);
mockConsole.hasLineIncluding("out", "removed ").should.be.ok();
mockConsole.hasLineIncluding("out", "open Unity").should.be.ok();
@@ -66,7 +66,10 @@ describe("cmd-remove.ts", function () {
chdir: getWorkDir("test-openupm-cli"),
},
};
- const retCode = await remove("com.example.package-a@1.0.0", options);
+ const retCode = await remove(
+ packageReference(packageA, semanticVersion("1.0.0")),
+ options
+ );
retCode.should.equal(1);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(defaultManifest);
@@ -79,7 +82,7 @@ describe("cmd-remove.ts", function () {
chdir: getWorkDir("test-openupm-cli"),
},
};
- const retCode = await remove("pkg-not-exist", options);
+ const retCode = await remove(missingPackage, options);
retCode.should.equal(1);
const manifest = shouldHaveManifest();
manifest.should.deepEqual(defaultManifest);
@@ -92,15 +95,12 @@ describe("cmd-remove.ts", function () {
chdir: getWorkDir("test-openupm-cli"),
},
};
- const retCode = await remove(
- ["com.example.package-a", "com.example.package-b"],
- options
- );
+ const retCode = await remove([packageA, packageB], options);
retCode.should.equal(0);
const manifest = shouldHaveManifest();
- shouldNotHaveDependency(manifest, "com.example.package-a");
- shouldNotHaveDependency(manifest, "com.example.package-b");
- shouldHaveRegistryWithScopes(manifest, ["com.example"]);
+ shouldNotHaveDependency(manifest, packageA);
+ shouldNotHaveDependency(manifest, packageB);
+ shouldHaveRegistryWithScopes(manifest, [exampleRegistryReverseDomain]);
mockConsole
.hasLineIncluding("out", "removed com.example.package-a")
.should.be.ok();
diff --git a/test/test-cmd-search.ts b/test/test-cmd-search.ts
index 4e0d97eb..2c26f1e2 100644
--- a/test/test-cmd-search.ts
+++ b/test/test-cmd-search.ts
@@ -1,7 +1,6 @@
import nock from "nock";
import "should";
import { search, SearchOptions } from "../src/cmd-search";
-
import {
exampleRegistryUrl,
registerSearchResult,
@@ -11,6 +10,8 @@ import {
import { SearchEndpointResult } from "./types";
import { createWorkDir, getWorkDir, removeWorkDir } from "./mock-work-dir";
import { attachMockConsole, MockConsole } from "./mock-console";
+import { domainName } from "../src/types/domain-name";
+import { semanticVersion } from "../src/types/semantic-version";
describe("cmd-search.ts", function () {
let mockConsole: MockConsole = null!;
@@ -37,9 +38,9 @@ describe("cmd-search.ts", function () {
objects: [
{
package: {
- name: "com.example.package-a",
+ name: domainName("com.example.package-a"),
scope: "unscoped",
- version: "1.0.0",
+ version: semanticVersion("1.0.0"),
description: "A demo package",
date: "2019-10-02T04:02:38.335Z",
links: {},
diff --git a/test/test-cmd-view.ts b/test/test-cmd-view.ts
index 377a0930..e8a1ae9c 100644
--- a/test/test-cmd-view.ts
+++ b/test/test-cmd-view.ts
@@ -1,6 +1,5 @@
import "should";
import { view, ViewOptions } from "../src/cmd-view";
-import { PkgInfo } from "../src/types/global";
import {
exampleRegistryUrl,
registerMissingPackage,
@@ -11,6 +10,14 @@ import {
} from "./mock-registry";
import { createWorkDir, getWorkDir, removeWorkDir } from "./mock-work-dir";
import { attachMockConsole, MockConsole } from "./mock-console";
+import { buildPackageInfo } from "./data-pkg-info";
+import { domainName } from "../src/types/domain-name";
+import { semanticVersion } from "../src/types/semantic-version";
+import { packageReference } from "../src/types/package-reference";
+
+const packageA = domainName("com.example.package-a");
+const packageUp = domainName("com.example.package-up");
+const packageMissing = domainName("pkg-not-exist");
describe("cmd-view.ts", function () {
const options: ViewOptions = {
@@ -29,104 +36,78 @@ describe("cmd-view.ts", function () {
describe("view", function () {
let mockConsole: MockConsole = null!;
- const remotePkgInfoA: PkgInfo = {
- name: "com.example.package-a",
- versions: {
- "1.0.0": {
- name: "com.example.package-a",
- displayName: "Package A",
- author: {
- name: "batman",
- },
- version: "1.0.0",
- unity: "2018.4",
- description: "A demo package",
- keywords: [""],
- category: "Unity",
- dependencies: {
- "com.example.package-a": "^1.0.0",
- },
- gitHead: "5c141ecfac59c389090a07540f44c8ac5d07a729",
- readmeFilename: "README.md",
- _id: "com.example.package-a@1.0.0",
- _nodeVersion: "12.13.1",
- _npmVersion: "6.12.1",
- dist: {
- integrity:
- "sha512-MAh44bur7HGyfbCXH9WKfaUNS67aRMfO0VAbLkr+jwseb1hJue/I1pKsC7PKksuBYh4oqoo9Jov1cBcvjVgjmA==",
- shasum: "516957cac4249f95cafab0290335def7d9703db7",
- tarball:
- "https://cdn.example.com/com.example.package-a/com.example.package-a-1.0.0.tgz",
- },
- contributors: [],
- },
- },
- time: {
- modified: "2019-11-28T18:51:58.123Z",
- created: "2019-11-28T18:51:58.123Z",
- "1.0.0": "2019-11-28T18:51:58.123Z",
- },
- users: {},
- "dist-tags": {
- latest: "1.0.0",
- },
- _rev: "3-418f950115c32bd0",
- _id: "com.example.package-a",
- readme: "A demo package",
- _attachments: {},
- };
- const remotePkgInfoUp: PkgInfo = {
- name: "com.example.package-up",
- versions: {
- "1.0.0": {
- name: "com.example.package-up",
- displayName: "Package A",
- author: {
- name: "batman",
- },
- version: "1.0.0",
- unity: "2018.4",
- description: "A demo package",
- keywords: [""],
- category: "Unity",
- dependencies: {
- "com.example.package-up": "^1.0.0",
- },
- gitHead: "5c141ecfac59c389090a07540f44c8ac5d07a729",
- readmeFilename: "README.md",
- _id: "com.example.package-up@1.0.0",
- _nodeVersion: "12.13.1",
- _npmVersion: "6.12.1",
- dist: {
- integrity:
- "sha512-MAh44bur7HGyfbCXH9WKfaUNS67aRMfO0VAbLkr+jwseb1hJue/I1pKsC7PKksuBYh4oqoo9Jov1cBcvjVgjmA==",
- shasum: "516957cac4249f95cafab0290335def7d9703db7",
- tarball:
- "https://cdn.example.com/com.example.package-up/com.example.package-up-1.0.0.tgz",
- },
- contributors: [],
- },
- },
- time: {
- modified: "2019-11-28T18:51:58.123Z",
- created: "2019-11-28T18:51:58.123Z",
- "1.0.0": "2019-11-28T18:51:58.123Z",
- },
- users: {},
- "dist-tags": {
- latest: "1.0.0",
- },
- _rev: "3-418f950115c32bd0",
- _id: "com.example.package-up",
- readme: "A demo package",
- _attachments: {},
- };
+ const remotePkgInfoA = buildPackageInfo(packageA, (pkg) =>
+ pkg
+ .set("time", {
+ modified: "2019-11-28T18:51:58.123Z",
+ created: "2019-11-28T18:51:58.123Z",
+ [semanticVersion("1.0.0")]: "2019-11-28T18:51:58.123Z",
+ })
+ .set("_rev", "3-418f950115c32bd0")
+ .set("readme", "A demo package")
+ .addVersion("1.0.0", (version) =>
+ version
+ .set("displayName", "Package A")
+ .set("author", { name: "batman" })
+ .set("unity", "2018.4")
+ .set("description", "A demo package")
+ .set("keywords", [""])
+ .set("category", "Unity")
+ .set("gitHead", "5c141ecfac59c389090a07540f44c8ac5d07a729")
+ .set("readmeFilename", "README.md")
+ .set("_nodeVersion", "12.13.1")
+ .set("_npmVersion", "6.12.1")
+ .set("dist", {
+ integrity:
+ "sha512-MAh44bur7HGyfbCXH9WKfaUNS67aRMfO0VAbLkr+jwseb1hJue/I1pKsC7PKksuBYh4oqoo9Jov1cBcvjVgjmA==",
+ shasum: "516957cac4249f95cafab0290335def7d9703db7",
+ tarball:
+ "https://cdn.example.com/com.example.package-a/com.example.package-a-1.0.0.tgz",
+ })
+ .addDependency(packageA, "1.0.0")
+ )
+ );
+
+ const remotePkgInfoUp = buildPackageInfo(packageUp, (pkg) =>
+ pkg
+ .set("time", {
+ modified: "2019-11-28T18:51:58.123Z",
+ created: "2019-11-28T18:51:58.123Z",
+ [semanticVersion("1.0.0")]: "2019-11-28T18:51:58.123Z",
+ })
+ .set("_rev", "3-418f950115c32bd0")
+ .set("readme", "A demo package")
+ .addVersion("1.0.0", (version) =>
+ version
+ .set("displayName", "Package A")
+ .set("author", {
+ name: "batman",
+ })
+ .set("unity", "2018.4")
+ .set("description", "A demo package")
+ .set("keywords", [""])
+ .set("category", "Unity")
+ .addDependency(packageUp, "1.0.0")
+ .set("gitHead", "5c141ecfac59c389090a07540f44c8ac5d07a729")
+ .set("readmeFilename", "README.md")
+ .set("_nodeVersion", "12.13.1")
+ .set("_npmVersion", "6.12.1")
+ .set("dist", {
+ integrity:
+ "sha512-MAh44bur7HGyfbCXH9WKfaUNS67aRMfO0VAbLkr+jwseb1hJue/I1pKsC7PKksuBYh4oqoo9Jov1cBcvjVgjmA==",
+ shasum: "516957cac4249f95cafab0290335def7d9703db7",
+ tarball:
+ "https://cdn.example.com/com.example.package-up/com.example.package-up-1.0.0.tgz",
+ })
+ )
+ );
+
beforeEach(function () {
removeWorkDir("test-openupm-cli");
createWorkDir("test-openupm-cli", { manifest: true });
startMockRegistry();
registerRemotePkg(remotePkgInfoA);
- registerMissingPackage("pkg-not-exist");
+ registerMissingPackage(packageMissing);
registerRemoteUpstreamPkg(remotePkgInfoUp);
mockConsole = attachMockConsole();
});
@@ -136,31 +117,34 @@ describe("cmd-view.ts", function () {
mockConsole.detach();
});
it("view pkg", async function () {
- const retCode = await view("com.example.package-a", options);
+ const retCode = await view(packageA, options);
retCode.should.equal(0);
mockConsole
.hasLineIncluding("out", "com.example.package-a@1.0.0")
.should.be.ok();
});
it("view pkg@1.0.0", async function () {
- const retCode = await view("com.example.package-a@1.0.0", options);
+ const retCode = await view(
+ packageReference(packageA, semanticVersion("1.0.0")),
+ options
+ );
retCode.should.equal(1);
mockConsole.hasLineIncluding("out", "please replace").should.be.ok();
});
it("view pkg-not-exist", async function () {
- const retCode = await view("pkg-not-exist", options);
+ const retCode = await view(packageMissing, options);
retCode.should.equal(1);
mockConsole.hasLineIncluding("out", "package not found").should.be.ok();
});
it("view pkg from upstream", async function () {
- const retCode = await view("com.example.package-up", upstreamOptions);
+ const retCode = await view(packageUp, upstreamOptions);
retCode.should.equal(0);
mockConsole
.hasLineIncluding("out", "com.example.package-up@1.0.0")
.should.be.ok();
});
it("view pkg-not-exist from upstream", async function () {
- const retCode = await view("pkg-not-exist", upstreamOptions);
+ const retCode = await view(packageMissing, upstreamOptions);
retCode.should.equal(1);
mockConsole.hasLineIncluding("out", "package not found").should.be.ok();
});
diff --git a/test/test-domain-name.ts b/test/test-domain-name.ts
new file mode 100644
index 00000000..c9f9c779
--- /dev/null
+++ b/test/test-domain-name.ts
@@ -0,0 +1,62 @@
+import { describe } from "mocha";
+import {
+ domainName,
+ isDomainName,
+ isInternalPackage,
+ namespaceFor,
+} from "../src/types/domain-name";
+import should from "should";
+
+describe("domain-name", function () {
+ describe("namespace", function () {
+ [
+ ["unity.com", "com.unity"],
+ ["my-school.ac.at", "at.ac.my-school"],
+ ["openupm.com", "com.openupm"],
+ ["registry.npmjs.org", "org.npmjs"],
+ ].forEach(([hostName, expected]) =>
+ it(`"${hostName}" should become "${expected}"`, function () {
+ const actual = namespaceFor(hostName);
+ should(actual).be.equal(expected);
+ })
+ );
+ });
+ describe("validation", function () {
+ [
+ "com",
+ "com.unity",
+ "com.openupm",
+ "at.ac.my-school",
+ "dev.comradevanti123",
+ ].forEach((s) =>
+ it(`"${s}" should be domain-name`, () =>
+ should(isDomainName(s)).be.true())
+ );
+ [
+ "",
+ " ",
+ // Invalid characters
+ "com.x💀x",
+ // No double hyphens
+ "com.my--school",
+ // No leading hyphens
+ "com.-unity",
+ // No trailing hyphens
+ "com.unity-",
+ ].forEach((s) =>
+ it(`"${s}" should not be domain-name`, () =>
+ should(isDomainName(s)).be.false())
+ );
+ });
+ describe("internal package", function () {
+ it("test com.otherorg.software", function () {
+ isInternalPackage(domainName("com.otherorg.software")).should.not.be.ok();
+ });
+ it("test com.unity.ugui", function () {
+ isInternalPackage(domainName("com.unity.ugui")).should.be.ok();
+ });
+ it("test com.unity.modules.tilemap", function () {
+ isInternalPackage(domainName("com.unity.modules.tilemap")).should.be.ok();
+ });
+ });
+});
diff --git a/test/test-ip-address.ts b/test/test-ip-address.ts
new file mode 100644
index 00000000..49e28d4b
--- /dev/null
+++ b/test/test-ip-address.ts
@@ -0,0 +1,28 @@
+import { describe } from "mocha";
+import { isIpAddress } from "../src/types/ip-address";
+import should from "should";
+
+describe("ip-address", function () {
+ describe("validate", function () {
+ [
+ "10.20.30.40",
+ "64.233.160.0",
+ "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ ].forEach((s) =>
+ it(`"${s}" should be ip-address`, () => should(isIpAddress(s)).be.true())
+ );
+
+ [
+ "",
+ " ",
+ "hello",
+ // Missing 4th segment
+ "64.233.160",
+ // Deleted some colons
+ "2001:0db8:85a30000:0000:8a2e0370:7334",
+ ].forEach((s) =>
+ it(`"${s}" should not be ip-address`, () =>
+ should(isIpAddress(s)).be.false())
+ );
+ });
+});
diff --git a/test/test-manifest.ts b/test/test-manifest.ts
index 075527b0..8ab18eb4 100644
--- a/test/test-manifest.ts
+++ b/test/test-manifest.ts
@@ -11,6 +11,8 @@ import {
shouldHaveNoManifest,
shouldNotHaveAnyDependencies,
} from "./manifest-assertions";
+import { domainName } from "../src/types/domain-name";
+import { semanticVersion } from "../src/types/semantic-version";
describe("manifest", function () {
let mockConsole: MockConsole = null!;
@@ -73,7 +75,7 @@ describe("manifest", function () {
).should.be.ok();
const manifest = shouldHaveManifest();
shouldNotHaveAnyDependencies(manifest);
- manifest.dependencies["some-pack"] = "1.0.0";
+ manifest.dependencies[domainName("some-pack")] = semanticVersion("1.0.0");
saveManifest(manifest).should.be.ok();
const manifest2 = shouldHaveManifest();
manifest2.should.be.deepEqual(manifest);
diff --git a/test/test-package-id.ts b/test/test-package-id.ts
new file mode 100644
index 00000000..a0c3aa3a
--- /dev/null
+++ b/test/test-package-id.ts
@@ -0,0 +1,24 @@
+import { describe } from "mocha";
+import should from "should";
+import { isPackageId } from "../src/types/package-id";
+
+describe("package-id", function () {
+ describe("validate", function () {
+ ["com.my-package@1.2.3"].forEach((s) =>
+ it(`"${s}" should be package-id`, () => should(isPackageId(s)).be.true())
+ );
+
+ [
+ "",
+ " ",
+ // Missing version
+ "com.my-package",
+ // Incomplete version
+ "com.my-package@1",
+ "com.my-package@1.2",
+ ].forEach((s) =>
+ it(`"${s}" should not be package-id`, () =>
+ should(isPackageId(s)).be.false())
+ );
+ });
+});
diff --git a/test/test-package-reference.ts b/test/test-package-reference.ts
new file mode 100644
index 00000000..dcf30671
--- /dev/null
+++ b/test/test-package-reference.ts
@@ -0,0 +1,49 @@
+import { describe } from "mocha";
+import should from "should";
+import {
+ isPackageReference,
+ packageReference,
+ splitPackageReference,
+} from "../src/types/package-reference";
+
+describe("package-reference", function () {
+ describe("validation", function () {
+ [
+ "com.abc.my-package",
+ "com.abc.my-package@1.2.3",
+ "com.abc.my-package@file://./my-package",
+ "com.abc.my-package@latest",
+ ].forEach((input) =>
+ it(`"${input}" should be package-reference`, function () {
+ should(isPackageReference(input)).be.true();
+ })
+ );
+ [
+ // Not valid domain name
+ "-hello",
+ ].forEach((input) =>
+ it(`"${input}" should not be package-reference`, function () {
+ should(isPackageReference(input)).not.be.true();
+ })
+ );
+ });
+
+ describe("split", function () {
+ function shouldSplitCorrectly(name: string, version?: string) {
+ const [actualName, actualVersion] = splitPackageReference(
+ packageReference(name, version)
+ );
+ should(actualName).be.equal(name);
+ should(actualVersion).be.equal(version);
+ }
+
+ it("should split package without version", () =>
+ shouldSplitCorrectly("com.abc.my-package"));
+ it("should split package with semantic version", () =>
+ shouldSplitCorrectly("com.abc.my-package", "1.0.0"));
+ it("should split package with file-url", () =>
+ shouldSplitCorrectly("com.abc.my-package", "file://./my-package"));
+ it("should split package with latest-tag", () =>
+ shouldSplitCorrectly("com.abc.my-package", "latest"));
+ });
+});
diff --git a/test/test-package-url.ts b/test/test-package-url.ts
new file mode 100644
index 00000000..70f0ed38
--- /dev/null
+++ b/test/test-package-url.ts
@@ -0,0 +1,23 @@
+import { describe } from "mocha";
+import should from "should";
+import { isPackageUrl } from "../src/types/package-url";
+
+describe("package-url", function () {
+ describe("validation", function () {
+ [
+ "https://github.com/yo/com.base.package-a",
+ "git@github.com:yo/com.base.package-a",
+ "file../yo/com.base.package-a",
+ ].forEach((url) =>
+ it(`"${url}" is a package-url`, function () {
+ should(isPackageUrl(url)).be.true();
+ })
+ );
+
+ ["", "com.base.package.a"].forEach((url) =>
+ it(`"${url}" is not a package-url`, function () {
+ should(isPackageUrl(url)).not.be.true();
+ })
+ );
+ });
+});
diff --git a/test/test-pgk-info.ts b/test/test-pgk-info.ts
index b78d3331..1e4c5ac0 100644
--- a/test/test-pgk-info.ts
+++ b/test/test-pgk-info.ts
@@ -2,11 +2,14 @@ import { tryGetLatestVersion } from "../src/utils/pkg-info";
import "should";
import { describe } from "mocha";
import should from "should";
+import { semanticVersion } from "../src/types/semantic-version";
describe("pkg-info", function () {
describe("tryGetLatestVersion", function () {
it("from dist-tags", async function () {
- const version = tryGetLatestVersion({ "dist-tags": { latest: "1.0.0" } });
+ const version = tryGetLatestVersion({
+ "dist-tags": { latest: semanticVersion("1.0.0") },
+ });
should(version).equal("1.0.0");
});
});
diff --git a/test/test-pkg-name.ts b/test/test-pkg-name.ts
deleted file mode 100644
index 592efd26..00000000
--- a/test/test-pkg-name.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import "assert";
-import "should";
-import { isInternalPackage, splitPkgName } from "../src/utils/pkg-name";
-
-describe("pkg-name.ts", function () {
- describe("splitPkgName", function () {
- it("pkg@version", function () {
- splitPkgName("pkg@1.0.0").should.deepEqual({
- name: "pkg",
- version: "1.0.0",
- });
- });
- it("pkg@latest", function () {
- splitPkgName("pkg@latest").should.deepEqual({
- name: "pkg",
- version: "latest",
- });
- });
- it("pkg", function () {
- splitPkgName("pkg").should.deepEqual({
- name: "pkg",
- version: undefined,
- });
- });
- it("pkg@file", function () {
- splitPkgName("pkg@file:../pkg").should.deepEqual({
- name: "pkg",
- version: "file:../pkg",
- });
- });
- it("pkg@http", function () {
- splitPkgName("pkg@https://github.com/owner/pkg").should.deepEqual({
- name: "pkg",
- version: "https://github.com/owner/pkg",
- });
- });
- it("pkg@git", function () {
- splitPkgName("pkg@git@github.com:owner/pkg.git").should.deepEqual({
- name: "pkg",
- version: "git@github.com:owner/pkg.git",
- });
- });
- });
- describe("isInternalPackage", function () {
- it("test com.otherorg.software", function () {
- isInternalPackage("com.otherorg.software").should.not.be.ok();
- });
- it("test com.unity.ugui", function () {
- isInternalPackage("com.unity.ugui").should.be.ok();
- });
- it("test com.unity.modules.tilemap", function () {
- isInternalPackage("com.unity.modules.tilemap").should.be.ok();
- });
- });
-});
diff --git a/test/test-registry-client.ts b/test/test-registry-client.ts
index 668253d4..da03aaaa 100644
--- a/test/test-registry-client.ts
+++ b/test/test-registry-client.ts
@@ -2,7 +2,6 @@ import "assert";
import "should";
import { parseEnv } from "../src/utils/env";
import { fetchPackageInfo } from "../src/registry-client";
-import { PkgInfo } from "../src/types/global";
import {
exampleRegistryUrl,
registerMissingPackage,
@@ -11,6 +10,10 @@ import {
stopMockRegistry,
} from "./mock-registry";
import should from "should";
+import { buildPackageInfo } from "./data-pkg-info";
+import { domainName } from "../src/types/domain-name";
+
+const packageA = domainName("package-a");
describe("registry-client", function () {
describe("fetchPackageInfo", function () {
@@ -27,13 +30,9 @@ describe("registry-client", function () {
{ checkPath: false }
)
).should.be.ok();
- const pkgInfoRemote: PkgInfo = {
- name: "package-a",
- versions: {},
- time: {},
- };
+ const pkgInfoRemote = buildPackageInfo(packageA);
registerRemotePkg(pkgInfoRemote);
- const info = await fetchPackageInfo("package-a");
+ const info = await fetchPackageInfo(packageA);
should(info).deepEqual(pkgInfoRemote);
});
it("404", async function () {
@@ -44,8 +43,8 @@ describe("registry-client", function () {
)
).should.be.ok();
- registerMissingPackage("package-a");
- const info = await fetchPackageInfo("package-a");
+ registerMissingPackage(packageA);
+ const info = await fetchPackageInfo(packageA);
(info === undefined).should.be.ok();
});
});
diff --git a/test/test-registry-url.ts b/test/test-registry-url.ts
new file mode 100644
index 00000000..498d5e88
--- /dev/null
+++ b/test/test-registry-url.ts
@@ -0,0 +1,45 @@
+import { describe } from "mocha";
+import {
+ coerceRegistryUrl,
+ isRegistryUrl,
+ registryUrl,
+ removeTrailingSlash,
+} from "../src/types/registry-url";
+import should from "should";
+
+describe("registry-url", function () {
+ describe("validation", function () {
+ ["http://registry.npmjs.org/", "https://registry.npmjs.org/"].forEach(
+ (input) =>
+ it(`"${input}" should be registry-url`, function () {
+ should(isRegistryUrl(input)).be.ok();
+ })
+ );
+ [
+ // Missing protocol
+ "registry.npmjs.org/",
+ ].forEach((input) =>
+ it(`"${input}" should not be registry-url`, function () {
+ should(isRegistryUrl(input)).not.be.ok();
+ })
+ );
+ });
+ describe("remove trailing slash", function () {
+ it("should remove trailing slash if it is exists", () =>
+ should(removeTrailingSlash(registryUrl("http://test.com/"))).be.equal(
+ "http://test.com"
+ ));
+ it("should do nothing if there is no trailing slash", () =>
+ should(removeTrailingSlash(registryUrl("http://test.com"))).be.equal(
+ "http://test.com"
+ ));
+ });
+ describe("coerce", function () {
+ it("should coerce urls without protocol", () =>
+ should(coerceRegistryUrl("test.com")).be.equal("http://test.com"));
+ it("should remove trailing slash", () =>
+ should(coerceRegistryUrl("http://test.com/")).be.equal(
+ "http://test.com"
+ ));
+ });
+});
diff --git a/test/test-semantic-version.ts b/test/test-semantic-version.ts
new file mode 100644
index 00000000..7088b111
--- /dev/null
+++ b/test/test-semantic-version.ts
@@ -0,0 +1,18 @@
+import { describe } from "mocha";
+import should from "should";
+import { isSemanticVersion } from "../src/types/semantic-version";
+
+describe("semantic-version", function () {
+ describe("validate", function () {
+ ["1.2.3", "1.2.3-alpha"].forEach((input) =>
+ it(`"${input}" is a semantic version`, function () {
+ should(isSemanticVersion(input)).be.true();
+ })
+ );
+ ["", " ", "wow", "1", "1.2"].forEach((input) =>
+ it(`"${input}" is not a semantic version`, function () {
+ should(isSemanticVersion(input)).not.be.true();
+ })
+ );
+ });
+});
diff --git a/test/tsconfig.json b/test/tsconfig.json
new file mode 100644
index 00000000..6392488d
--- /dev/null
+++ b/test/tsconfig.json
@@ -0,0 +1,111 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ // "target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": ["ES2020"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ // "module": "commonjs", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "resolveJsonModule": false, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": false, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./lib", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ "noEmit": true /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ // "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ //"strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ // "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ "include": ["./**/*"]
+}
diff --git a/test/types.ts b/test/types.ts
index ee0ec0d5..e2863c7c 100644
--- a/test/types.ts
+++ b/test/types.ts
@@ -1,15 +1,17 @@
-import { Contact, PkgVersion, ReverseDomainName } from "../src/types/global";
+import { Contact } from "../src/types/global";
+import { DomainName } from "../src/types/domain-name";
+import { SemanticVersion } from "../src/types/semantic-version";
type Maintainer = { username: string; email: string };
export type SearchEndpointResult = {
objects: Array<{
package: {
- name: ReverseDomainName;
+ name: DomainName;
description?: string;
date: string;
scope: "unscoped";
- version: PkgVersion;
+ version: SemanticVersion;
links: Record;
author: Contact;
publisher: Maintainer;
diff --git a/tsconfig.json b/tsconfig.json
index 9293beee..d21ff3b4 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -25,15 +25,14 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
- "module": "commonjs",
- /* Specify what module code is generated. */
+ "module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
"typeRoots": [ /* Specify multiple folders that act like './node_modules/@types'. */
- "lib/types", "node_modules/@types"
+ "./src/types", "./node_modules/@types"
],
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
@@ -80,15 +79,12 @@
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
- "esModuleInterop": true,
- /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
- "forceConsistentCasingInFileNames": true,
- /* Ensure that casing is correct in imports. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
- "strict": true,
- /* Enable all strict type-checking options. */
+ "strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@@ -110,8 +106,7 @@
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
- "skipLibCheck": true
- /* Skip type checking all .d.ts files. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": [
"./src/**/*"