diff --git a/core/package-lock.json b/core/package-lock.json index 9386e7f84..c8d7e4ccc 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -8,9 +8,6 @@ "name": "neo4j-driver-core", "version": "4.3.0", "license": "Apache-2.0", - "dependencies": { - "uri-js": "^4.4.1" - }, "devDependencies": { "@types/jest": "^26.0.20", "esdoc": "^1.1.0", @@ -6188,6 +6185,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "engines": { "node": ">=6" } @@ -7568,6 +7566,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -13067,7 +13066,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "qs": { "version": "6.5.2", @@ -14191,6 +14191,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "requires": { "punycode": "^2.1.0" } diff --git a/core/package.json b/core/package.json index 62eee7764..c1222eaf1 100644 --- a/core/package.json +++ b/core/package.json @@ -26,9 +26,6 @@ "url": "https://github.com/neo4j/neo4j-javascript-driver/issues" }, "homepage": "https://github.com/neo4j/neo4j-javascript-driver#readme", - "dependencies": { - "uri-js": "^4.4.1" - }, "devDependencies": { "@types/jest": "^26.0.20", "esdoc": "^1.1.0", diff --git a/core/src/internal/url-util.ts b/core/src/internal/url-util.ts index b855d3c79..ee3aea216 100644 --- a/core/src/internal/url-util.ts +++ b/core/src/internal/url-util.ts @@ -17,7 +17,6 @@ * limitations under the License. */ -import { parse as uriJsParse } from 'uri-js' import { assertString } from './util' const DEFAULT_BOLT_PORT = 7687 @@ -76,6 +75,17 @@ class Url { } } +interface ParsedUri { + scheme?: string + host?: string + port?: number | string + query?: string + fragment?: string + userInfo?: string + authority?: string + path?: string +} + function parseDatabaseUrl(url: string) { assertString(url, 'URL') @@ -230,9 +240,104 @@ function defaultPortForScheme(scheme: string | null): number { } } +function uriJsParse(value: string) { + // JS version of Python partition function + function partition(s: string, delimiter: string): [string, string, string] { + const i = s.indexOf(delimiter) + if (i >= 0) return [s.substring(0, i), s[i], s.substring(i + 1)] + else return [s, '', ''] + } + + // JS version of Python rpartition function + function rpartition(s: string, delimiter: string): [string, string, string] { + const i = s.lastIndexOf(delimiter) + if (i >= 0) return [s.substring(0, i), s[i], s.substring(i + 1)] + else return ['', '', s] + } + + function between( + s: string, + ldelimiter: string, + rdelimiter: string + ): [string, string] { + const lpartition = partition(s, ldelimiter) + const rpartition = partition(lpartition[2], rdelimiter) + return [rpartition[0], rpartition[2]] + } + + // Parse an authority string into an object + // with the following keys: + // - userInfo (optional, might contain both user name and password) + // - host + // - port (optional, included only as a string) + function parseAuthority(value: string): ParsedUri { + let parsed: ParsedUri = {}, + parts: [string, string, string] + + // Parse user info + parts = rpartition(value, '@') + if (parts[1] === '@') { + parsed.userInfo = decodeURIComponent(parts[0]) + value = parts[2] + } + + // Parse host and port + const [ipv6Host, rest] = between(value, `[`, `]`) + if (ipv6Host !== '') { + parsed.host = ipv6Host + parts = partition(rest, ':') + } else { + parts = partition(value, ':') + parsed.host = parts[0] + } + + if (parts[1] === ':') { + parsed.port = parts[2] + } + + return parsed + } + + let parsed: ParsedUri = {}, + parts: string[] + + // Parse scheme + parts = partition(value, ':') + if (parts[1] === ':') { + parsed.scheme = decodeURIComponent(parts[0]) + value = parts[2] + } + + // Parse fragment + parts = partition(value, '#') + if (parts[1] === '#') { + parsed.fragment = decodeURIComponent(parts[2]) + value = parts[0] + } + + // Parse query + parts = partition(value, '?') + if (parts[1] === '?') { + parsed.query = parts[2] + value = parts[0] + } + + // Parse authority and path + if (value.startsWith('//')) { + parts = partition(value.substr(2), '/') + parsed = { ...parsed, ...parseAuthority(parts[0]) } + parsed.path = parts[1] + parts[2] + } else { + parsed.path = value + } + + return parsed +} + export { parseDatabaseUrl, defaultPortForScheme, formatIPv4Address, - formatIPv6Address + formatIPv6Address, + Url } diff --git a/test/internal/url-util.test.js b/core/test/internal/url-util.test.ts similarity index 97% rename from test/internal/url-util.test.js rename to core/test/internal/url-util.test.ts index e47c0e7be..008ccfc29 100644 --- a/test/internal/url-util.test.js +++ b/core/test/internal/url-util.test.ts @@ -17,9 +17,16 @@ * limitations under the License. */ -import { internal } from 'neo4j-driver-core' +import * as urlUtil from '../../src/internal/url-util' -const { urlUtil } = internal +interface PartialUrl { + readonly scheme?: string | null + readonly host?: string + readonly port?: number + readonly hostAndPort?: string + readonly query?: Object + readonly ipv6?: boolean +} describe('#unit url-util', () => { it('should parse URL with just host name', () => { @@ -785,7 +792,7 @@ describe('#unit url-util', () => { }) }) - function verifyUrl (urlString, expectedUrl) { + function verifyUrl(urlString: string, expectedUrl: PartialUrl) { const url = parse(urlString) if (expectedUrl.scheme) { expect(url.scheme).toEqual(expectedUrl.scheme) @@ -800,7 +807,9 @@ describe('#unit url-util', () => { if (expectedUrl.port) { expect(url.port).toEqual(expectedUrl.port) } else { - expect(url.port).toEqual(urlUtil.defaultPortForScheme(expectedUrl.scheme)) + expect(url.port).toEqual( + urlUtil.defaultPortForScheme(expectedUrl.scheme!!) + ) } verifyHostAndPort(url, expectedUrl) @@ -811,11 +820,11 @@ describe('#unit url-util', () => { } } - function verifyHostAndPort (url, expectedUrl) { + function verifyHostAndPort(url: urlUtil.Url, expectedUrl: PartialUrl) { const port = expectedUrl.port === 0 || expectedUrl.port ? expectedUrl.port - : urlUtil.defaultPortForScheme(expectedUrl.scheme) + : urlUtil.defaultPortForScheme(expectedUrl.scheme!!) if (expectedUrl.ipv6) { expect(url.hostAndPort).toEqual(`[${expectedUrl.host}]:${port}`) @@ -824,7 +833,7 @@ describe('#unit url-util', () => { } } - function parse (url) { + function parse(url: any): urlUtil.Url { return urlUtil.parseDatabaseUrl(url) } }) diff --git a/neo4j-driver-lite/package-lock.json b/neo4j-driver-lite/package-lock.json index 687a8300e..af3eadd12 100644 --- a/neo4j-driver-lite/package-lock.json +++ b/neo4j-driver-lite/package-lock.json @@ -49,9 +49,6 @@ "name": "neo4j-driver-core", "version": "4.3.0", "license": "Apache-2.0", - "dependencies": { - "uri-js": "^4.4.1" - }, "devDependencies": { "@types/jest": "^26.0.20", "esdoc": "^1.1.0", @@ -12255,8 +12252,7 @@ "jest": "^26.6.3", "ts-jest": "^26.5.1", "ts-node": "^9.1.1", - "typescript": "^4.1.3", - "uri-js": "^4.4.1" + "typescript": "^4.1.3" } }, "nice-try": { diff --git a/package-lock.json b/package-lock.json index 24ec1af09..f4154c551 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,7 @@ "neo4j-driver-bolt-connection": "file:bolt-connection", "neo4j-driver-core": "file:core", "rxjs": "^6.6.3", - "text-encoding-utf-8": "^1.0.2", - "uri-js": "^4.4.0" + "text-encoding-utf-8": "^1.0.2" }, "devDependencies": { "@babel/core": "^7.5.5", @@ -119,9 +118,6 @@ "name": "neo4j-driver-core", "version": "4.3.0", "license": "Apache-2.0", - "dependencies": { - "uri-js": "^4.4.1" - }, "devDependencies": { "@types/jest": "^26.0.20", "esdoc": "^1.1.0", @@ -9090,6 +9086,7 @@ "yallist" ], "dev": true, + "hasInstallScript": true, "optional": true, "os": [ "darwin" @@ -20279,6 +20276,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true, "engines": { "node": ">=6" } @@ -23469,6 +23467,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -39392,8 +39391,7 @@ "jest": "^26.6.3", "ts-jest": "^26.5.1", "ts-node": "^9.1.1", - "typescript": "^4.1.3", - "uri-js": "^4.4.1" + "typescript": "^4.1.3" }, "dependencies": { "typescript": { @@ -41262,7 +41260,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true }, "qjobs": { "version": "1.2.0", @@ -43905,6 +43904,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "requires": { "punycode": "^2.1.0" } diff --git a/package.json b/package.json index f77c1679f..19fad1f42 100644 --- a/package.json +++ b/package.json @@ -111,8 +111,7 @@ "neo4j-driver-bolt-connection": "file:bolt-connection", "neo4j-driver-core": "file:core", "rxjs": "^6.6.3", - "text-encoding-utf-8": "^1.0.2", - "uri-js": "^4.4.0" + "text-encoding-utf-8": "^1.0.2" }, "bundledDependencies": [ "neo4j-driver-core",