diff --git a/dist/otpauth.cjs.js b/dist/otpauth.cjs.js index 74bb3aa2..37e0afa4 100644 --- a/dist/otpauth.cjs.js +++ b/dist/otpauth.cjs.js @@ -1,4 +1,4 @@ -/*! otpauth v7.0.3 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ +/*! otpauth v7.0.4 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ /*! jssha v3.2.0 | (c) Brian Turek | BSD-3-Clause | https://github.com/Caligatio/jsSHA */ 'use strict'; @@ -2302,7 +2302,7 @@ var URI = /*#__PURE__*/function () { * Library version. * @type {string} */ -var version = '7.0.3'; +var version = '7.0.4'; exports.HOTP = HOTP; exports.Secret = Secret; diff --git a/dist/otpauth.cjs.min.js b/dist/otpauth.cjs.min.js index 5949482a..355f2afd 100644 --- a/dist/otpauth.cjs.min.js +++ b/dist/otpauth.cjs.min.js @@ -1,4 +1,4 @@ -/*! otpauth v7.0.3 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ +/*! otpauth v7.0.4 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ /*! jssha v3.2.0 | (c) Brian Turek | BSD-3-Clause | https://github.com/Caligatio/jsSHA */ "use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{},n=t.issuer,r=voi ;return e.validate({token:n,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:r,window:i})}},{key:"toString",value:function(){var e=encodeURIComponent;return"otpauth://totp/"+"".concat(this.issuer.length>0?"".concat(e(this.issuer),":").concat(e(this.label),"?issuer=").concat(e(this.issuer),"&"):"".concat(e(this.label),"?"))+"secret=".concat(e(this.secret.base32),"&")+"algorithm=".concat(e(this.algorithm),"&")+"digits=".concat(e(this.digits),"&")+"period=".concat(e(this.period))}}],[{key:"defaults",get:function(){return{issuer:"",label:"OTPAuth",algorithm:"SHA1",digits:6,period:30,window:1}}},{key:"generate",value:function(t){var n=t.secret,r=t.algorithm,i=t.digits,o=t.period,u=void 0===o?e.defaults.period:o,a=t.timestamp,s=void 0===a?Date.now():a;return HOTP.generate({secret:n,algorithm:r,digits:i,counter:Math.floor(s/1e3/u)})}},{key:"validate",value:function(t){ var n=t.token,r=t.secret,i=t.algorithm,o=t.digits,u=t.period,a=void 0===u?e.defaults.period:u,s=t.timestamp,f=void 0===s?Date.now():s,c=t.window;return HOTP.validate({token:n,secret:r,algorithm:i,digits:o,counter:Math.floor(f/1e3/a),window:c})}}]),e}(),OTPURI_REGEX=/^otpauth:\/\/([ht]otp)\/(.+)\?((?:&?[A-Z0-9.~_-]+=[^&]*)+)$/i,SECRET_REGEX=/^[2-7A-Z]+=*$/i,ALGORITHM_REGEX=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,INTEGER_REGEX=/^[+-]?\d+$/,POSITIVE_INTEGER_REGEX=/^\+?[1-9]\d*$/,URI=function(){function e(){_classCallCheck(this,e)}return _createClass(e,null,[{key:"parse",value:function(e){var t;try{t=e.match(OTPURI_REGEX)}catch(e){}if(!Array.isArray(t))throw new URIError("Invalid URI format");var n,r=t[1].toLowerCase(),i=t[2].split(/:(.+)/,2).map(decodeURIComponent),o=t[3].split("&").reduce((function(e,t){var n=t.split(/=(.*)/,2).map(decodeURIComponent),r=n[0].toLowerCase(),i=n[1],o=e;return o[r]=i,o}),{}),u={};if("hotp"===r){if(n=HOTP, void 0===o.counter||!INTEGER_REGEX.test(o.counter))throw new TypeError("Missing or invalid 'counter' parameter");u.counter=parseInt(o.counter,10)}else{if("totp"!==r)throw new TypeError("Unknown OTP type");if(n=TOTP,void 0!==o.period){if(!POSITIVE_INTEGER_REGEX.test(o.period))throw new TypeError("Invalid 'period' parameter");u.period=parseInt(o.period,10)}}if(2===i.length)if(u.label=i[1],void 0===o.issuer)u.issuer=i[0];else{if(o.issuer!==i[0])throw new TypeError("Invalid 'issuer' parameter");u.issuer=o.issuer}else u.label=i[0],void 0!==o.issuer&&(u.issuer=o.issuer);if(void 0===o.secret||!SECRET_REGEX.test(o.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(u.secret=o.secret,void 0!==o.algorithm){if(!ALGORITHM_REGEX.test(o.algorithm))throw new TypeError("Invalid 'algorithm' parameter");u.algorithm=o.algorithm}if(void 0!==o.digits){if(!POSITIVE_INTEGER_REGEX.test(o.digits))throw new TypeError("Invalid 'digits' parameter");u.digits=parseInt(o.digits,10)}return new n(u)}},{key:"stringify", -value:function(e){if(e instanceof HOTP||e instanceof TOTP)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}]),e}(),version="7.0.3";exports.HOTP=HOTP,exports.Secret=Secret,exports.TOTP=TOTP,exports.URI=URI,exports.version=version; +value:function(e){if(e instanceof HOTP||e instanceof TOTP)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}]),e}(),version="7.0.4";exports.HOTP=HOTP,exports.Secret=Secret,exports.TOTP=TOTP,exports.URI=URI,exports.version=version; //# sourceMappingURL=otpauth.cjs.min.js.map diff --git a/dist/otpauth.esm.js b/dist/otpauth.esm.js index bbbc5e8f..12f01bbf 100644 --- a/dist/otpauth.esm.js +++ b/dist/otpauth.esm.js @@ -1,4 +1,4 @@ -/*! otpauth v7.0.3 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ +/*! otpauth v7.0.4 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ /*! jssha v3.2.0 | (c) Brian Turek | BSD-3-Clause | https://github.com/Caligatio/jsSHA */ function _typeof(obj) { @@ -2298,6 +2298,6 @@ var URI = /*#__PURE__*/function () { * Library version. * @type {string} */ -var version = '7.0.3'; +var version = '7.0.4'; export { HOTP, Secret, TOTP, URI, version }; diff --git a/dist/otpauth.esm.min.js b/dist/otpauth.esm.min.js index 4294b890..c05398a0 100644 --- a/dist/otpauth.esm.min.js +++ b/dist/otpauth.esm.min.js @@ -1,4 +1,4 @@ -/*! otpauth v7.0.3 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ +/*! otpauth v7.0.4 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ /*! jssha v3.2.0 | (c) Brian Turek | BSD-3-Clause | https://github.com/Caligatio/jsSHA */ function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{},n=t.issuer,r=voi ;return e.validate({token:n,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:r,window:i})}},{key:"toString",value:function(){var e=encodeURIComponent;return"otpauth://totp/"+"".concat(this.issuer.length>0?"".concat(e(this.issuer),":").concat(e(this.label),"?issuer=").concat(e(this.issuer),"&"):"".concat(e(this.label),"?"))+"secret=".concat(e(this.secret.base32),"&")+"algorithm=".concat(e(this.algorithm),"&")+"digits=".concat(e(this.digits),"&")+"period=".concat(e(this.period))}}],[{key:"defaults",get:function(){return{issuer:"",label:"OTPAuth",algorithm:"SHA1",digits:6,period:30,window:1}}},{key:"generate",value:function(t){var n=t.secret,r=t.algorithm,i=t.digits,o=t.period,u=void 0===o?e.defaults.period:o,a=t.timestamp,s=void 0===a?Date.now():a;return HOTP.generate({secret:n,algorithm:r,digits:i,counter:Math.floor(s/1e3/u)})}},{key:"validate",value:function(t){ var n=t.token,r=t.secret,i=t.algorithm,o=t.digits,u=t.period,a=void 0===u?e.defaults.period:u,s=t.timestamp,f=void 0===s?Date.now():s,c=t.window;return HOTP.validate({token:n,secret:r,algorithm:i,digits:o,counter:Math.floor(f/1e3/a),window:c})}}]),e}(),OTPURI_REGEX=/^otpauth:\/\/([ht]otp)\/(.+)\?((?:&?[A-Z0-9.~_-]+=[^&]*)+)$/i,SECRET_REGEX=/^[2-7A-Z]+=*$/i,ALGORITHM_REGEX=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,INTEGER_REGEX=/^[+-]?\d+$/,POSITIVE_INTEGER_REGEX=/^\+?[1-9]\d*$/,URI=function(){function e(){_classCallCheck(this,e)}return _createClass(e,null,[{key:"parse",value:function(e){var t;try{t=e.match(OTPURI_REGEX)}catch(e){}if(!Array.isArray(t))throw new URIError("Invalid URI format");var n,r=t[1].toLowerCase(),i=t[2].split(/:(.+)/,2).map(decodeURIComponent),o=t[3].split("&").reduce((function(e,t){var n=t.split(/=(.*)/,2).map(decodeURIComponent),r=n[0].toLowerCase(),i=n[1],o=e;return o[r]=i,o}),{}),u={};if("hotp"===r){if(n=HOTP, void 0===o.counter||!INTEGER_REGEX.test(o.counter))throw new TypeError("Missing or invalid 'counter' parameter");u.counter=parseInt(o.counter,10)}else{if("totp"!==r)throw new TypeError("Unknown OTP type");if(n=TOTP,void 0!==o.period){if(!POSITIVE_INTEGER_REGEX.test(o.period))throw new TypeError("Invalid 'period' parameter");u.period=parseInt(o.period,10)}}if(2===i.length)if(u.label=i[1],void 0===o.issuer)u.issuer=i[0];else{if(o.issuer!==i[0])throw new TypeError("Invalid 'issuer' parameter");u.issuer=o.issuer}else u.label=i[0],void 0!==o.issuer&&(u.issuer=o.issuer);if(void 0===o.secret||!SECRET_REGEX.test(o.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(u.secret=o.secret,void 0!==o.algorithm){if(!ALGORITHM_REGEX.test(o.algorithm))throw new TypeError("Invalid 'algorithm' parameter");u.algorithm=o.algorithm}if(void 0!==o.digits){if(!POSITIVE_INTEGER_REGEX.test(o.digits))throw new TypeError("Invalid 'digits' parameter");u.digits=parseInt(o.digits,10)}return new n(u)}},{key:"stringify", -value:function(e){if(e instanceof HOTP||e instanceof TOTP)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}]),e}(),version="7.0.3";export{HOTP,Secret,TOTP,URI,version}; +value:function(e){if(e instanceof HOTP||e instanceof TOTP)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}]),e}(),version="7.0.4";export{HOTP,Secret,TOTP,URI,version}; //# sourceMappingURL=otpauth.esm.min.js.map diff --git a/dist/otpauth.umd.js b/dist/otpauth.umd.js index 1bad258d..fba853e5 100644 --- a/dist/otpauth.umd.js +++ b/dist/otpauth.umd.js @@ -1,4 +1,4 @@ -/*! otpauth v7.0.3 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ +/*! otpauth v7.0.4 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ /*! jssha v3.2.0 | (c) Brian Turek | BSD-3-Clause | https://github.com/Caligatio/jsSHA */ (function (global, factory) { @@ -2304,7 +2304,7 @@ * Library version. * @type {string} */ - var version = '7.0.3'; + var version = '7.0.4'; exports.HOTP = HOTP; exports.Secret = Secret; diff --git a/dist/otpauth.umd.min.js b/dist/otpauth.umd.min.js index 99dd6fec..32493807 100644 --- a/dist/otpauth.umd.min.js +++ b/dist/otpauth.umd.min.js @@ -1,4 +1,4 @@ -/*! otpauth v7.0.3 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ +/*! otpauth v7.0.4 | (c) Héctor Molinero Fernández | MIT | https://github.com/hectorm/otpauth */ /*! jssha v3.2.0 | (c) Brian Turek | BSD-3-Clause | https://github.com/Caligatio/jsSHA */ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).OTPAuth={})}(this,(function(exports){"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{},n=t.issuer,r=voi ;return e.validate({token:n,secret:this.secret,algorithm:this.algorithm,digits:this.digits,period:this.period,timestamp:r,window:i})}},{key:"toString",value:function(){var e=encodeURIComponent;return"otpauth://totp/"+"".concat(this.issuer.length>0?"".concat(e(this.issuer),":").concat(e(this.label),"?issuer=").concat(e(this.issuer),"&"):"".concat(e(this.label),"?"))+"secret=".concat(e(this.secret.base32),"&")+"algorithm=".concat(e(this.algorithm),"&")+"digits=".concat(e(this.digits),"&")+"period=".concat(e(this.period))}}],[{key:"defaults",get:function(){return{issuer:"",label:"OTPAuth",algorithm:"SHA1",digits:6,period:30,window:1}}},{key:"generate",value:function(t){var n=t.secret,r=t.algorithm,i=t.digits,o=t.period,u=void 0===o?e.defaults.period:o,a=t.timestamp,s=void 0===a?Date.now():a;return HOTP.generate({secret:n,algorithm:r,digits:i,counter:Math.floor(s/1e3/u)})}},{key:"validate",value:function(t){ var n=t.token,r=t.secret,i=t.algorithm,o=t.digits,u=t.period,a=void 0===u?e.defaults.period:u,s=t.timestamp,f=void 0===s?Date.now():s,c=t.window;return HOTP.validate({token:n,secret:r,algorithm:i,digits:o,counter:Math.floor(f/1e3/a),window:c})}}]),e}(),OTPURI_REGEX=/^otpauth:\/\/([ht]otp)\/(.+)\?((?:&?[A-Z0-9.~_-]+=[^&]*)+)$/i,SECRET_REGEX=/^[2-7A-Z]+=*$/i,ALGORITHM_REGEX=/^SHA(?:1|224|256|384|512|3-224|3-256|3-384|3-512)$/i,INTEGER_REGEX=/^[+-]?\d+$/,POSITIVE_INTEGER_REGEX=/^\+?[1-9]\d*$/,URI=function(){function e(){_classCallCheck(this,e)}return _createClass(e,null,[{key:"parse",value:function(e){var t;try{t=e.match(OTPURI_REGEX)}catch(e){}if(!Array.isArray(t))throw new URIError("Invalid URI format");var n,r=t[1].toLowerCase(),i=t[2].split(/:(.+)/,2).map(decodeURIComponent),o=t[3].split("&").reduce((function(e,t){var n=t.split(/=(.*)/,2).map(decodeURIComponent),r=n[0].toLowerCase(),i=n[1],o=e;return o[r]=i,o}),{}),u={};if("hotp"===r){if(n=HOTP, void 0===o.counter||!INTEGER_REGEX.test(o.counter))throw new TypeError("Missing or invalid 'counter' parameter");u.counter=parseInt(o.counter,10)}else{if("totp"!==r)throw new TypeError("Unknown OTP type");if(n=TOTP,void 0!==o.period){if(!POSITIVE_INTEGER_REGEX.test(o.period))throw new TypeError("Invalid 'period' parameter");u.period=parseInt(o.period,10)}}if(2===i.length)if(u.label=i[1],void 0===o.issuer)u.issuer=i[0];else{if(o.issuer!==i[0])throw new TypeError("Invalid 'issuer' parameter");u.issuer=o.issuer}else u.label=i[0],void 0!==o.issuer&&(u.issuer=o.issuer);if(void 0===o.secret||!SECRET_REGEX.test(o.secret))throw new TypeError("Missing or invalid 'secret' parameter");if(u.secret=o.secret,void 0!==o.algorithm){if(!ALGORITHM_REGEX.test(o.algorithm))throw new TypeError("Invalid 'algorithm' parameter");u.algorithm=o.algorithm}if(void 0!==o.digits){if(!POSITIVE_INTEGER_REGEX.test(o.digits))throw new TypeError("Invalid 'digits' parameter");u.digits=parseInt(o.digits,10)}return new n(u)}},{key:"stringify", -value:function(e){if(e instanceof HOTP||e instanceof TOTP)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}]),e}(),version="7.0.3";exports.HOTP=HOTP,exports.Secret=Secret,exports.TOTP=TOTP,exports.URI=URI,exports.version=version,Object.defineProperty(exports,"__esModule",{value:!0})})); +value:function(e){if(e instanceof HOTP||e instanceof TOTP)return e.toString();throw new TypeError("Invalid 'HOTP/TOTP' object")}}]),e}(),version="7.0.4";exports.HOTP=HOTP,exports.Secret=Secret,exports.TOTP=TOTP,exports.URI=URI,exports.version=version,Object.defineProperty(exports,"__esModule",{value:!0})})); //# sourceMappingURL=otpauth.umd.min.js.map diff --git a/docs/classes/hotp.html b/docs/classes/hotp.html index 47238dc7..919daea8 100644 --- a/docs/classes/hotp.html +++ b/docs/classes/hotp.html @@ -2832,7 +2832,7 @@

constructor

  • @@ -2858,42 +2858,90 @@

    Properties

    algorithm

    algorithm: string
    +
    +
    +

    HMAC hashing algorithm.

    +
    +

    counter

    counter: number
    +
    +
    +

    Initial counter value.

    +
    +

    digits

    digits: number
    +
    +
    +

    Token length.

    +
    +

    issuer

    issuer: string
    +
    +
    +

    Account provider.

    +
    +

    label

    label: string
    +
    +
    +

    Account label.

    +
    +

    secret

    secret: Secret
    +
    +
    +

    Secret key.

    +
    +
    @@ -2908,7 +2956,7 @@

    Static defaults

  • @@ -2953,7 +3001,7 @@

    generate

  • @@ -2982,7 +3030,7 @@

    toString

  • @@ -3005,7 +3053,7 @@

    validate

  • @@ -3045,7 +3093,7 @@

    Static generate

  • @@ -3088,7 +3136,7 @@

    Static validate

  • diff --git a/docs/classes/secret.html b/docs/classes/secret.html index 425a1e54..25144357 100644 --- a/docs/classes/secret.html +++ b/docs/classes/secret.html @@ -2819,7 +2819,7 @@

    constructor

  • @@ -2845,7 +2845,15 @@

    Properties

    buffer

    buffer: ArrayBuffer
    +
    +
    +

    Secret key.

    +
    +
  • @@ -2860,7 +2868,7 @@

    base32

  • @@ -2882,7 +2890,7 @@

    hex

  • @@ -2904,7 +2912,7 @@

    latin1

  • @@ -2926,7 +2934,7 @@

    utf8

  • @@ -2951,7 +2959,7 @@

    Static fromBase32

  • @@ -2983,7 +2991,7 @@

    Static fromHex

  • @@ -3015,7 +3023,7 @@

    Static fromLatin1

  • @@ -3047,7 +3055,7 @@

    Static fromUTF8

  • diff --git a/docs/classes/totp.html b/docs/classes/totp.html index 6dd4ce0a..0aa38a8d 100644 --- a/docs/classes/totp.html +++ b/docs/classes/totp.html @@ -2832,7 +2832,7 @@

    constructor

  • @@ -2858,42 +2858,90 @@

    Properties

    algorithm

    algorithm: string
    +
    +
    +

    HMAC hashing algorithm.

    +
    +
  • digits

    digits: number
    +
    +
    +

    Token length.

    +
    +

    issuer

    issuer: string
    +
    +
    +

    Account provider.

    +
    +

    label

    label: string
    +
    +
    +

    Account label.

    +
    +

    period

    period: number
    +
    +
    +

    Token time-step duration.

    +
    +

    secret

    secret: Secret
    +
    +
    +

    Secret key.

    +
    +
    @@ -2908,7 +2956,7 @@

    Static defaults

  • @@ -2953,7 +3001,7 @@

    generate

  • @@ -2982,7 +3030,7 @@

    toString

  • @@ -3005,7 +3053,7 @@

    validate

  • @@ -3045,7 +3093,7 @@

    Static generate

  • @@ -3091,7 +3139,7 @@

    Static validate

  • diff --git a/docs/classes/uri.html b/docs/classes/uri.html index a81a022b..fc20f0a5 100644 --- a/docs/classes/uri.html +++ b/docs/classes/uri.html @@ -2799,7 +2799,7 @@

    Static parse

  • @@ -2831,7 +2831,7 @@

    Static stringify

  • diff --git a/docs/index.html b/docs/index.html index 5ad77946..62031e1b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2836,7 +2836,7 @@

    Const version

    version: string = '__OTPAUTH_VERSION__'
    diff --git a/docs/modules.html b/docs/modules.html index efc8670a..76c2e71c 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -2835,7 +2835,7 @@

    Const version

    version: string = '__OTPAUTH_VERSION__'
    diff --git a/package-lock.json b/package-lock.json index 79e3dacd..ada9f6cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "otpauth", - "version": "7.0.3", + "version": "7.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "7.0.3", + "version": "7.0.4", "license": "MIT", "dependencies": { "jssha": "~3.2.0" diff --git a/package.json b/package.json index b6542341..fce6249f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "otpauth", - "version": "7.0.3", + "version": "7.0.4", "description": "One Time Password (HOTP/TOTP) library for Node.js, Deno and browsers", "keywords": [ "otp",