diff --git a/js-user-library/default.nix b/js-user-library/default.nix new file mode 100644 index 0000000000..4450c8102f --- /dev/null +++ b/js-user-library/default.nix @@ -0,0 +1,2 @@ +{ system ? builtins.currentSystem }: +(import ../. { inherit system; }).pkgs.dfinity-sdk.packages.js-user-library diff --git a/js-user-library/jest.config.js b/js-user-library/jest.config.js index 73e7003be7..3806c6ce1f 100644 --- a/js-user-library/jest.config.js +++ b/js-user-library/jest.config.js @@ -1,7 +1,14 @@ module.exports = { bail: false, + setupFiles: [ + "./test-setup", + ], setupFilesAfterEnv: [ "jest-expect-message", ], - testEnvironment: "jsdom" + testEnvironment: "jsdom", + testPathIgnorePatterns: [ + "/node_modules/", + "/out/", + ] }; diff --git a/js-user-library/package-lock.json b/js-user-library/package-lock.json index 098fe0a1f5..e5c3f43b14 100644 --- a/js-user-library/package-lock.json +++ b/js-user-library/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@babel/cli": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.3.tgz", - "integrity": "sha512-kWKOEeuylpa781yCeA5//eEx1u3WtLZqbi2VWXLKmb3QDPb5T2f7Yk311MK7bvvjR70dluAeiu4VXXsG1WwJsw==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.4.tgz", + "integrity": "sha512-tqrDyvPryBM6xjIyKKUwr3s8CzmmYidwgdswd7Uc/Cv0ogZcuS1TYQTLx/eWKP3UbJ6JxZAiYlBZabXm/rtRsQ==", "dev": true, "requires": { "chokidar": "^2.1.8", @@ -19,7 +19,7 @@ "mkdirp": "^0.5.1", "output-file-sync": "^2.0.0", "slash": "^2.0.0", - "source-map": "^0.6.1" + "source-map": "^0.5.0" } }, "@babel/code-frame": { @@ -32,15 +32,15 @@ } }, "@babel/core": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.3.tgz", - "integrity": "sha512-QfQ5jTBgXLzJuo7Mo8bZK/ePywmgNRgk/UQykiKwEtZPiFIn8ZqE6jB+AnD1hbB1S2xQyL4//it5vuAUOVAMTw==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.4.tgz", + "integrity": "sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.3", + "@babel/generator": "^7.6.4", "@babel/helpers": "^7.6.2", - "@babel/parser": "^7.6.3", + "@babel/parser": "^7.6.4", "@babel/template": "^7.6.0", "@babel/traverse": "^7.6.3", "@babel/types": "^7.6.3", @@ -50,7 +50,7 @@ "lodash": "^4.17.13", "resolve": "^1.3.2", "semver": "^5.4.1", - "source-map": "^0.6.1" + "source-map": "^0.5.0" }, "dependencies": { "debug": { @@ -71,15 +71,15 @@ } }, "@babel/generator": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.3.tgz", - "integrity": "sha512-hLhYbAb3pHwxjlijC4AQ7mqZdcoujiNaW7izCT04CIowHK8psN0IN8QjDv0iyFtycF5FowUOTwDloIheI25aMw==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", + "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", "dev": true, "requires": { "@babel/types": "^7.6.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", - "source-map": "^0.6.1" + "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { @@ -311,9 +311,9 @@ } }, "@babel/parser": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.3.tgz", - "integrity": "sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", + "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { @@ -883,6 +883,14 @@ "requires": { "exec-sh": "^0.3.2", "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } } }, "@jest/console": { @@ -982,6 +990,14 @@ "slash": "^2.0.0", "source-map": "^0.6.0", "string-length": "^2.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "@jest/source-map": { @@ -993,6 +1009,14 @@ "callsites": "^3.0.0", "graceful-fs": "^4.1.15", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "@jest/test-result": { @@ -1040,6 +1064,14 @@ "slash": "^2.0.0", "source-map": "^0.6.1", "write-file-atomic": "2.4.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "@jest/types": { @@ -1053,6 +1085,38 @@ "@types/yargs": "^13.0.0" } }, + "@trust/keyto": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@trust/keyto/-/keyto-0.3.7.tgz", + "integrity": "sha512-t5kWWCTkPgg24JWVuCTPMx7l13F7YHdxBeJkT1vmoHjROgiOIEAN8eeY+iRmP1Hwsx+S7U55HyuqSsECr08a8A==", + "dev": true, + "requires": { + "asn1.js": "^5.0.1", + "base64url": "^3.0.1", + "elliptic": "^6.4.1" + } + }, + "@trust/webcrypto": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@trust/webcrypto/-/webcrypto-0.9.2.tgz", + "integrity": "sha512-5iMAVcGYKhqLJGjefB1nzuQSqUJTru0nG4CytpBT/GGp1Piz/MVnj2jORdYf4JBYzggCIa8WZUr2rchP2Ngn/w==", + "dev": true, + "requires": { + "@trust/keyto": "^0.3.4", + "base64url": "^3.0.0", + "elliptic": "^6.4.0", + "node-rsa": "^0.4.0", + "text-encoding": "^0.6.1" + }, + "dependencies": { + "text-encoding": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", + "dev": true + } + } + }, "@types/babel__core": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", @@ -1232,6 +1296,17 @@ "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } } }, "argparse": { @@ -1274,12 +1349,28 @@ "dev": true }, "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "asn1.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.2.0.tgz", + "integrity": "sha512-Q7hnYGGNYbcmGrCPulXfkEw7oW7qjWeM4ZTALmgpuIcZLxyqqKYWxCZg2UBm8bklrnB4m2mGyJPWfoktdORD8A==", "dev": true, "requires": { - "safer-buffer": "~2.1.0" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } } }, "assert-plus": { @@ -1458,6 +1549,12 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "dev": true + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -1537,6 +1634,12 @@ } } }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", @@ -1689,15 +1792,6 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", "upath": "^1.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "optional": true - } } }, "ci-info": { @@ -1781,9 +1875,9 @@ } }, "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "component-emitter": { @@ -2043,6 +2137,29 @@ "integrity": "sha512-qYWNMjKLEfQAWZF2Sarvo+ahigu0EArnpCFSoUuZJS3W5wIeVfeEvsgmT2mgIrieQkeQ0+xFmykK3nx2ezekPQ==", "dev": true }, + "elliptic": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } + } + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -2113,6 +2230,15 @@ "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } } }, "esprima": { @@ -3040,6 +3166,14 @@ "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "har-schema": { @@ -3111,6 +3245,27 @@ } } }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "hosted-git-info": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", @@ -3513,6 +3668,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -3905,6 +4066,14 @@ "mkdirp": "^0.5.1", "slash": "^2.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "jest-validate": { @@ -4073,6 +4242,14 @@ "dev": true, "requires": { "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } } }, "jsprim": { @@ -4267,6 +4444,18 @@ "mime-db": "1.40.0" } }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4277,9 +4466,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mixin-deep": { @@ -4310,14 +4499,6 @@ "dev": true, "requires": { "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } } }, "ms": { @@ -4412,6 +4593,15 @@ } } }, + "node-rsa": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-0.4.2.tgz", + "integrity": "sha1-1jkXKewWqDDtWjgEKzFX0tXXJTA=", + "dev": true, + "requires": { + "asn1": "0.2.3" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -4425,13 +4615,11 @@ } }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } + "optional": true }, "npm-run-path": { "version": "2.0.2", @@ -4554,14 +4742,6 @@ "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - } } }, "optionator": { @@ -5146,6 +5326,14 @@ "micromatch": "^3.1.4", "minimist": "^1.1.1", "walker": "~1.0.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } } }, "sax": { @@ -5261,12 +5449,6 @@ "requires": { "is-extendable": "^0.1.0" } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true } } }, @@ -5342,9 +5524,9 @@ } }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, "source-map-resolve": { @@ -5368,6 +5550,14 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "source-map-url": { @@ -5757,6 +5947,22 @@ "requires": { "commander": "2.20.0", "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } } }, "unicode-canonical-property-names-ecmascript": { diff --git a/js-user-library/package.json b/js-user-library/package.json index 3f482bc9a2..45d6ba65c3 100644 --- a/js-user-library/package.json +++ b/js-user-library/package.json @@ -25,6 +25,7 @@ "@babel/core": "^7.6.0", "@babel/preset-env": "^7.6.0", "@babel/preset-typescript": "^7.6.0", + "@trust/webcrypto": "^0.9.2", "@types/jest": "^24.0.18", "babel-jest": "^24.9.0", "borc": "^2.1.1", diff --git a/js-user-library/src/blob.ts b/js-user-library/src/blob.ts index 5c5f600125..51654513aa 100644 --- a/js-user-library/src/blob.ts +++ b/js-user-library/src/blob.ts @@ -1,10 +1,14 @@ import { Buffer } from "buffer/"; import { Hex } from "./hex"; -export const fromHex = (hex: Hex): Uint8Array => { - return new Uint8Array(Buffer.from(hex, "hex").buffer); +// Named `BinaryBlob` as opposed to `Blob` so not to conflict with +// https://developer.mozilla.org/en-US/docs/Web/API/Blob +export type BinaryBlob = Uint8Array & { __blob__: void }; + +export const fromHex = (hex: Hex): BinaryBlob => { + return new Uint8Array(Buffer.from(hex, "hex").buffer) as BinaryBlob; }; -export const toHex = (blob: Uint8Array): string => { +export const toHex = (blob: BinaryBlob): string => { return Buffer.from(blob).toString("hex"); }; diff --git a/js-user-library/src/canisterId.ts b/js-user-library/src/canisterId.ts new file mode 100644 index 0000000000..10e503e459 --- /dev/null +++ b/js-user-library/src/canisterId.ts @@ -0,0 +1,12 @@ +import { Hex } from "./hex"; +import { Int } from "./int"; +import * as int from "./int"; + +// FIXME +// The current implementation of the client expects canister IDs to be +// represented as u64. This `Int` type will not be sufficient for u64 but may be +// good enough for now. + +export type CanisterId = Int & { __canisterId__: void }; + +export const fromHex = (hex: Hex): CanisterId => int.fromHex(hex) as CanisterId; diff --git a/js-user-library/src/cbor.test.ts b/js-user-library/src/cbor.test.ts index 33296affaa..dea3919753 100644 --- a/js-user-library/src/cbor.test.ts +++ b/js-user-library/src/cbor.test.ts @@ -1,4 +1,5 @@ -import { toHex } from "./blob"; +import { BinaryBlob } from "./blob"; +import * as blob from "./blob"; import { CborValue, decode, encode } from "./cbor"; import { Int } from "./int"; @@ -6,14 +7,14 @@ test("round trip", () => { interface Data extends Record { a: Int; b: string; - c: Uint8Array; + c: BinaryBlob; d: { four: string }; } const input: Data = { a: 1 as Int, b: "two", - c: Uint8Array.from([3]), + c: Uint8Array.from([3]) as BinaryBlob, d: { four: "four" }, }; @@ -24,5 +25,5 @@ test("round trip", () => { const { c: inputC, ...inputRest } = input; const { c: outputC, ...outputRest } = output; expect(outputRest).toEqual(inputRest); - expect(toHex(outputC)).toBe(toHex(inputC)); + expect(blob.toHex(outputC)).toBe(blob.toHex(inputC)); }); diff --git a/js-user-library/src/cbor.ts b/js-user-library/src/cbor.ts index 4dcdf11a2b..421c4ef90f 100644 --- a/js-user-library/src/cbor.ts +++ b/js-user-library/src/cbor.ts @@ -1,6 +1,7 @@ // tslint:disable-next-line: max-line-length // https://github.com/dfinity-lab/dfinity/blob/9bca65f8edd65701ea6bdb00e0752f9186bbc893/docs/spec/public/index.adoc#cbor-encoding-of-requests-and-responses import borc from "borc"; +import { BinaryBlob } from "./blob"; import { Int } from "./int"; const SEMANTIC_TAG = 55799; @@ -12,7 +13,7 @@ export type CborValue = string // Blobs: Major type 2 (“Byte string”) - | Uint8Array + | BinaryBlob // Integer numbers: Major type 0 or 1 (“Unsigned/signed integer”) if small // enough to fit that type, else the Bignum format is used. @@ -21,11 +22,11 @@ export type CborValue // Nested records: Major type 5 followed by string keys. | CborRecord; -export const encode = (value: CborValue): Uint8Array => { +export const encode = (value: CborValue): BinaryBlob => { const buffer = borc.encode( new borc.Tagged(SEMANTIC_TAG, value), ); - return new Uint8Array(buffer); + return new Uint8Array(buffer) as BinaryBlob; }; export const decode = (input: Uint8Array): CborValue => { diff --git a/js-user-library/src/int.ts b/js-user-library/src/int.ts index 4ab8da6ce1..e96ecd1b9e 100644 --- a/js-user-library/src/int.ts +++ b/js-user-library/src/int.ts @@ -1 +1,7 @@ +import { Hex } from "./hex"; + export type Int = number & { __int__: void }; + +export const fromHex = (hex: Hex) => parseInt(hex, 16); + +export const toHex = (int: Int): Hex => int.toString(16) as Hex; diff --git a/js-user-library/src/readRequest.ts b/js-user-library/src/readRequest.ts new file mode 100644 index 0000000000..37e45a1331 --- /dev/null +++ b/js-user-library/src/readRequest.ts @@ -0,0 +1,5 @@ +// The types of values allowed in the `request_type` field for read requests. +export enum ReadRequestType { + Query = "query", + RequestStatus = "request-status", +} diff --git a/js-user-library/src/request.ts b/js-user-library/src/request.ts new file mode 100644 index 0000000000..6c225871cd --- /dev/null +++ b/js-user-library/src/request.ts @@ -0,0 +1,14 @@ +import { BinaryBlob } from "./blob"; +import { RequestType } from "./requestType"; + +// Common request fields. +// TODO: add missing common fields from the spec; `expiry` and `sender` +export interface Request extends Record { + request_type: RequestType; + // NOTE: `nonce`, but we provide it so that requests are unique and we avoid a + // bug in the client when the same request is submitted more than once: + // https://dfinity.atlassian.net/browse/DFN-895 + nonce?: BinaryBlob; + sender_pubkey: BinaryBlob; + sender_sig: BinaryBlob; +} diff --git a/js-user-library/src/requestId.test.ts b/js-user-library/src/requestId.test.ts new file mode 100644 index 0000000000..1656a4be79 --- /dev/null +++ b/js-user-library/src/requestId.test.ts @@ -0,0 +1,92 @@ +// tslint:disable-next-line: max-line-length +// https://github.com/dfinity-lab/dfinity/blob/5fef1450c9ab16ccf18381379149e504b11c8218/docs/spec/public/index.adoc#request-ids + +import { BinaryBlob } from "./blob"; +import * as blob from "./blob"; +import { Request } from "./request"; +import { hash, requestIdOf } from "./requestId"; +import { RequestType } from "./requestType"; + +const testHashOfBlob = async (input: BinaryBlob, expected: string) => { + const hashed = await hash(input); + const hex = blob.toHex(hashed); + expect(hex).toBe(expected); +}; + +const testHashOfString = async (input: string, expected: string) => { + const encoded = (new TextEncoder()).encode(input); + return testHashOfBlob(encoded as BinaryBlob, expected); +}; + +// This is based on the intermediate hashes of the request components from +// example in the spec. +test("hash", async () => { + await testHashOfString( + "request_type", + "769e6f87bdda39c859642b74ce9763cdd37cb1cd672733e8c54efaa33ab78af9", + ); + await testHashOfString( + "call", + "7edb360f06acaef2cc80dba16cf563f199d347db4443da04da0c8173e3f9e4ed", + ); + await testHashOfString( + "callee", // The "canister_id" field was previously named "callee" + "92ca4c0ced628df1e7b9f336416ead190bd0348615b6f71a64b21d1b68d4e7e2", + ); + await testHashOfString( + "canister_id", + "0a3eb2ba16702a387e6321066dd952db7a31f9b5cc92981e0a92dd56802d3df9", + ); + await testHashOfBlob( + Uint8Array.from([0, 0, 0, 0, 0, 0, 4, 210]) as BinaryBlob, + "4d8c47c3c1c837964011441882d745f7e92d10a40cef0520447c63029eafe396", + ); + await testHashOfString( + "method_name", + "293536232cf9231c86002f4ee293176a0179c002daa9fc24be9bb51acdd642b6", + ); + await testHashOfString( + "hello", + "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", + ); + await testHashOfString( + "arg", + "b25f03dedd69be07f356a06fe35c1b0ddc0de77dcd9066c4be0c6bbde14b23ff", + ); + await testHashOfBlob( + Uint8Array.from([68, 73, 68, 76, 0, 253, 42]) as BinaryBlob, + "6c0b2ae49718f6995c02ac5700c9c789d7b7862a0d53e6d40a73f1fcd2f70189", + ); +}); + +// This is based on the example in the spec. +test("requestIdOf", async () => { + const request: Request = { + request_type: "call" as RequestType, + method_name: "hello", + + // 0x00000000000004D2 + // \x00\x00\x00\x00\x00\x00\x04\xD2 + // 0 0 0 0 0 0 4 210 + canister_id: Uint8Array.from([0, 0, 0, 0, 0, 0, 4, 210]) as BinaryBlob, + + // DIDL\x00\xFD* + // D I D L \x00 \253 * + // 68 73 68 76 0 253 42 + arg: Uint8Array.from([68, 73, 68, 76, 0, 253, 42]) as BinaryBlob, + + // These fields are not included in the example provided in the spec but we + // provide them here to verify that they do not affect the request ID: + // "Remove the fields that are only used for authentication" + sender_pubkey: new Uint8Array(32) as BinaryBlob, + sender_sig: new Uint8Array(64) as BinaryBlob, + }; + + const requestId = await requestIdOf(request); + + expect( + blob.toHex(requestId), + ).toEqual( + "8781291c347db32a9d8c10eb62b710fce5a93be676474c42babc74c51858f94b", + ); +}); diff --git a/js-user-library/src/requestId.ts b/js-user-library/src/requestId.ts new file mode 100644 index 0000000000..9aef5719fd --- /dev/null +++ b/js-user-library/src/requestId.ts @@ -0,0 +1,86 @@ +import { Buffer } from "buffer/"; +import { BinaryBlob } from "./blob"; +import * as blob from "./blob"; +import { CborValue } from "./cbor"; +import { Hex } from "./hex"; +import * as int from "./int"; +import { Int } from "./int"; +import { Request } from "./request"; + +export type RequestId = BinaryBlob & { __requestId__: void }; + +// The spec describes encoding for these types. +// The exception here is integers, which are used in the current implementation +// of the HTTP handler. +type HashableValue = string | Uint8Array | Int; + +export const hash = async (data: Uint8Array): Promise => { + const hashed = await crypto.subtle.digest({ name: "SHA-256" }, data); + return new Uint8Array(hashed) as BinaryBlob; +}; + +const hashValue = (value: HashableValue): Promise => { + if (isString(value)) { + return hashString(value as string); + } else if (isInt(value)) { + // HACK: HTTP handler expects canister_id to be an u64 & hashed in this way. + const hex = int.toHex(value); + const padded = `${"0000000000000000".slice(hex.length)}${hex}` as Hex; + return hash(blob.fromHex(padded)); + } else if (isBlob) { + return hash(value as BinaryBlob); + } else { + throw new Error(`Attempt to hash a value if unsupported type: ${value}`); + } +}; + +const hashString = (value: string): Promise => { + const encoder = new TextEncoder(); + const encoded = encoder.encode(value); + return hash(encoded); +}; + +const isBlob = (value: HashableValue): value is BinaryBlob => { + return value instanceof Uint8Array; +}; + +const isInt = (value: HashableValue): value is Int => { + return typeof value === "number"; +}; + +const isString = (value: HashableValue): value is string => { + return typeof value === "string"; +}; + +const concat = (bs: Array): BinaryBlob => { + return bs.reduce((state: Uint8Array, b: BinaryBlob): Uint8Array => { + return new Uint8Array([ ...state, ...b ]); + }, new Uint8Array()) as BinaryBlob; +}; + +export const requestIdOf = async (request: Request): Promise => { + const { sender_pubkey, sender_sig, ...fields } = request; + + const hashed: Array> = Object + .entries(fields) + .map(async ([key, value]: [string, CborValue]) => { + const hashedKey = await hashString(key); + const hashedValue = await hashValue(value as HashableValue); + + return [ + hashedKey, + hashedValue, + ] as [BinaryBlob, BinaryBlob]; + }); + + const traversed: Array<[BinaryBlob, BinaryBlob]> = await Promise.all(hashed); + + const sorted: Array<[BinaryBlob, BinaryBlob]> = traversed + .sort(([k1, v1], [k2, v2]) => { + return Buffer.compare(Buffer.from(k1), Buffer.from(k2)); + }); + + const concatenated: BinaryBlob = concat(sorted.map(concat)); + const requestId = await hash(concatenated) as RequestId; + return requestId; +}; diff --git a/js-user-library/src/requestType.ts b/js-user-library/src/requestType.ts new file mode 100644 index 0000000000..aa5a631b0a --- /dev/null +++ b/js-user-library/src/requestType.ts @@ -0,0 +1,4 @@ +import { ReadRequestType } from "./readRequest"; +import { SubmitRequestType } from "./submitRequest"; + +export type RequestType = ReadRequestType | SubmitRequestType; diff --git a/js-user-library/src/submitRequest.ts b/js-user-library/src/submitRequest.ts new file mode 100644 index 0000000000..f1953173f5 --- /dev/null +++ b/js-user-library/src/submitRequest.ts @@ -0,0 +1,19 @@ +import { BinaryBlob } from "./blob"; +import { CanisterId } from "./canisterId"; + +// An ADT that represents requests to the "submit" endpoint. +export type SubmitRequest + = CallRequest; + +// The types of values allowed in the `request_type` field for submit requests. +export enum SubmitRequestType { + Call = "call", +} + +// The fields in a "call" submit request. +interface CallRequest extends Request { + request_type: SubmitRequestType.Call; + canister_id: CanisterId; + method_name: string; + arg: BinaryBlob; +} diff --git a/js-user-library/test-setup.js b/js-user-library/test-setup.js new file mode 100644 index 0000000000..b1465b0aae --- /dev/null +++ b/js-user-library/test-setup.js @@ -0,0 +1,11 @@ +// This file may be used to polyfill features that aren't available in the test +// environment, i.e. JSDom. +// +// We sometimes need to do this because our target browsers are expected to have +// a feature that JSDom doesn't. +// +// Note that we can use webpack configuration to make some features available to +// Node.js in a similar way. + +window.crypto = require("@trust/webcrypto"); +window.TextEncoder = require("text-encoding").TextEncoder;