From 231a84182eb339413a3e358142517ae260ddead5 Mon Sep 17 00:00:00 2001 From: Deep Koluguri Date: Mon, 25 May 2026 21:43:30 -0400 Subject: [PATCH] feat: full multi-tenant auth - JWT login, clinic registration, super admin CRUD --- backend/authMiddleware.js | 58 + backend/configManager.js | 90 +- backend/db.js | 154 +++ backend/queueManager.js | 16 +- backend/server.js | 593 ++++++-- k8s/app.yaml | 2 +- login.css | 24 + login.html | 117 +- node_modules/.bin/bcrypt | 16 + node_modules/.bin/bcrypt.cmd | 17 + node_modules/.bin/bcrypt.ps1 | 28 + node_modules/.bin/semver | 16 + node_modules/.bin/semver.cmd | 17 + node_modules/.bin/semver.ps1 | 28 + node_modules/.package-lock.json | 274 ++++ node_modules/bcryptjs/LICENSE | 27 + node_modules/bcryptjs/README.md | 201 +++ node_modules/bcryptjs/bin/bcrypt | 23 + node_modules/bcryptjs/index.d.ts | 3 + node_modules/bcryptjs/index.js | 1159 ++++++++++++++++ node_modules/bcryptjs/package.json | 76 + node_modules/bcryptjs/types.d.ts | 157 +++ node_modules/bcryptjs/umd/index.d.ts | 3 + node_modules/bcryptjs/umd/index.js | 1220 +++++++++++++++++ node_modules/bcryptjs/umd/package.json | 3 + node_modules/bcryptjs/umd/types.d.ts | 157 +++ .../buffer-equal-constant-time/.npmignore | 2 + .../buffer-equal-constant-time/.travis.yml | 4 + .../buffer-equal-constant-time/LICENSE.txt | 12 + .../buffer-equal-constant-time/README.md | 50 + .../buffer-equal-constant-time/index.js | 41 + .../buffer-equal-constant-time/package.json | 21 + .../buffer-equal-constant-time/test.js | 42 + node_modules/ecdsa-sig-formatter/CODEOWNERS | 1 + node_modules/ecdsa-sig-formatter/LICENSE | 201 +++ node_modules/ecdsa-sig-formatter/README.md | 65 + node_modules/ecdsa-sig-formatter/package.json | 46 + .../src/ecdsa-sig-formatter.d.ts | 17 + .../src/ecdsa-sig-formatter.js | 187 +++ .../src/param-bytes-for-alg.js | 23 + node_modules/jsonwebtoken/LICENSE | 21 + node_modules/jsonwebtoken/README.md | 396 ++++++ node_modules/jsonwebtoken/decode.js | 30 + node_modules/jsonwebtoken/index.js | 8 + .../jsonwebtoken/lib/JsonWebTokenError.js | 14 + .../jsonwebtoken/lib/NotBeforeError.js | 13 + .../jsonwebtoken/lib/TokenExpiredError.js | 13 + .../lib/asymmetricKeyDetailsSupported.js | 3 + node_modules/jsonwebtoken/lib/psSupported.js | 3 + .../lib/rsaPssKeyDetailsSupported.js | 3 + node_modules/jsonwebtoken/lib/timespan.js | 18 + .../jsonwebtoken/lib/validateAsymmetricKey.js | 66 + .../jsonwebtoken/node_modules/ms/index.js | 162 +++ .../jsonwebtoken/node_modules/ms/license.md | 21 + .../jsonwebtoken/node_modules/ms/package.json | 38 + .../jsonwebtoken/node_modules/ms/readme.md | 59 + node_modules/jsonwebtoken/package.json | 70 + node_modules/jsonwebtoken/sign.js | 253 ++++ node_modules/jsonwebtoken/verify.js | 263 ++++ node_modules/jwa/LICENSE | 17 + node_modules/jwa/README.md | 150 ++ node_modules/jwa/index.js | 266 ++++ node_modules/jwa/opslevel.yml | 6 + node_modules/jwa/package.json | 37 + node_modules/jws/CHANGELOG.md | 56 + node_modules/jws/LICENSE | 17 + node_modules/jws/index.js | 22 + node_modules/jws/lib/data-stream.js | 55 + node_modules/jws/lib/sign-stream.js | 83 ++ node_modules/jws/lib/tostring.js | 10 + node_modules/jws/lib/verify-stream.js | 125 ++ node_modules/jws/opslevel.yml | 6 + node_modules/jws/package.json | 34 + node_modules/jws/readme.md | 255 ++++ node_modules/lodash.includes/LICENSE | 47 + node_modules/lodash.includes/README.md | 18 + node_modules/lodash.includes/index.js | 745 ++++++++++ node_modules/lodash.includes/package.json | 17 + node_modules/lodash.isboolean/LICENSE | 22 + node_modules/lodash.isboolean/README.md | 18 + node_modules/lodash.isboolean/index.js | 70 + node_modules/lodash.isboolean/package.json | 17 + node_modules/lodash.isinteger/LICENSE | 47 + node_modules/lodash.isinteger/README.md | 18 + node_modules/lodash.isinteger/index.js | 265 ++++ node_modules/lodash.isinteger/package.json | 17 + node_modules/lodash.isnumber/LICENSE | 22 + node_modules/lodash.isnumber/README.md | 18 + node_modules/lodash.isnumber/index.js | 79 ++ node_modules/lodash.isnumber/package.json | 17 + node_modules/lodash.isplainobject/LICENSE | 47 + node_modules/lodash.isplainobject/README.md | 18 + node_modules/lodash.isplainobject/index.js | 139 ++ .../lodash.isplainobject/package.json | 17 + node_modules/lodash.isstring/LICENSE | 22 + node_modules/lodash.isstring/README.md | 18 + node_modules/lodash.isstring/index.js | 95 ++ node_modules/lodash.isstring/package.json | 17 + node_modules/lodash.once/LICENSE | 47 + node_modules/lodash.once/README.md | 18 + node_modules/lodash.once/index.js | 294 ++++ node_modules/lodash.once/package.json | 17 + node_modules/pg-cloudflare/LICENSE | 21 + node_modules/pg-cloudflare/README.md | 112 ++ node_modules/pg-cloudflare/dist/empty.d.ts | 2 + node_modules/pg-cloudflare/dist/empty.js | 6 + node_modules/pg-cloudflare/dist/empty.js.map | 1 + node_modules/pg-cloudflare/dist/index.d.ts | 31 + node_modules/pg-cloudflare/dist/index.js | 154 +++ node_modules/pg-cloudflare/dist/index.js.map | 1 + node_modules/pg-cloudflare/esm/index.mjs | 3 + node_modules/pg-cloudflare/package.json | 39 + node_modules/pg-cloudflare/src/empty.ts | 3 + node_modules/pg-cloudflare/src/index.ts | 169 +++ node_modules/pg-cloudflare/src/types.d.ts | 25 + node_modules/pg-connection-string/LICENSE | 21 + node_modules/pg-connection-string/README.md | 105 ++ .../pg-connection-string/esm/index.mjs | 8 + node_modules/pg-connection-string/index.d.ts | 36 + node_modules/pg-connection-string/index.js | 231 ++++ .../pg-connection-string/package.json | 52 + node_modules/pg-int8/LICENSE | 13 + node_modules/pg-int8/README.md | 16 + node_modules/pg-int8/index.js | 100 ++ node_modules/pg-int8/package.json | 24 + node_modules/pg-pool/LICENSE | 21 + node_modules/pg-pool/README.md | 357 +++++ node_modules/pg-pool/esm/index.mjs | 5 + node_modules/pg-pool/index.js | 517 +++++++ node_modules/pg-pool/package.json | 51 + node_modules/pg-protocol/LICENSE | 21 + node_modules/pg-protocol/README.md | 3 + node_modules/pg-protocol/dist/b.d.ts | 1 + node_modules/pg-protocol/dist/b.js | 23 + node_modules/pg-protocol/dist/b.js.map | 1 + .../pg-protocol/dist/buffer-reader.d.ts | 15 + .../pg-protocol/dist/buffer-reader.js | 55 + .../pg-protocol/dist/buffer-reader.js.map | 1 + .../pg-protocol/dist/buffer-writer.d.ts | 16 + .../pg-protocol/dist/buffer-writer.js | 81 ++ .../pg-protocol/dist/buffer-writer.js.map | 1 + .../pg-protocol/dist/inbound-parser.test.d.ts | 1 + .../pg-protocol/dist/inbound-parser.test.js | 530 +++++++ .../dist/inbound-parser.test.js.map | 1 + node_modules/pg-protocol/dist/index.d.ts | 6 + node_modules/pg-protocol/dist/index.js | 15 + node_modules/pg-protocol/dist/index.js.map | 1 + node_modules/pg-protocol/dist/messages.d.ts | 162 +++ node_modules/pg-protocol/dist/messages.js | 160 +++ node_modules/pg-protocol/dist/messages.js.map | 1 + .../dist/outbound-serializer.test.d.ts | 1 + .../dist/outbound-serializer.test.js | 252 ++++ .../dist/outbound-serializer.test.js.map | 1 + node_modules/pg-protocol/dist/parser.d.ts | 24 + node_modules/pg-protocol/dist/parser.js | 324 +++++ node_modules/pg-protocol/dist/parser.js.map | 1 + node_modules/pg-protocol/dist/serializer.d.ts | 43 + node_modules/pg-protocol/dist/serializer.js | 189 +++ .../pg-protocol/dist/serializer.js.map | 1 + node_modules/pg-protocol/esm/index.js | 11 + node_modules/pg-protocol/package.json | 45 + node_modules/pg-protocol/src/b.ts | 25 + node_modules/pg-protocol/src/buffer-reader.ts | 58 + node_modules/pg-protocol/src/buffer-writer.ts | 85 ++ .../pg-protocol/src/inbound-parser.test.ts | 575 ++++++++ node_modules/pg-protocol/src/index.ts | 11 + node_modules/pg-protocol/src/messages.ts | 262 ++++ .../src/outbound-serializer.test.ts | 276 ++++ node_modules/pg-protocol/src/parser.ts | 413 ++++++ node_modules/pg-protocol/src/serializer.ts | 274 ++++ .../pg-protocol/src/testing/buffer-list.ts | 67 + .../pg-protocol/src/testing/test-buffers.ts | 166 +++ .../pg-protocol/src/types/chunky.d.ts | 1 + node_modules/pg-types/.travis.yml | 7 + node_modules/pg-types/Makefile | 14 + node_modules/pg-types/README.md | 75 + node_modules/pg-types/index.d.ts | 137 ++ node_modules/pg-types/index.js | 47 + node_modules/pg-types/index.test-d.ts | 21 + node_modules/pg-types/lib/arrayParser.js | 11 + node_modules/pg-types/lib/binaryParsers.js | 257 ++++ node_modules/pg-types/lib/builtins.js | 73 + node_modules/pg-types/lib/textParsers.js | 215 +++ node_modules/pg-types/package.json | 42 + node_modules/pg-types/test/index.js | 24 + node_modules/pg-types/test/types.js | 597 ++++++++ node_modules/pg/LICENSE | 21 + node_modules/pg/README.md | 95 ++ node_modules/pg/esm/index.mjs | 20 + node_modules/pg/lib/client.js | 766 +++++++++++ node_modules/pg/lib/connection-parameters.js | 171 +++ node_modules/pg/lib/connection.js | 221 +++ node_modules/pg/lib/crypto/cert-signatures.js | 122 ++ node_modules/pg/lib/crypto/sasl.js | 262 ++++ node_modules/pg/lib/crypto/utils.js | 89 ++ node_modules/pg/lib/defaults.js | 91 ++ node_modules/pg/lib/index.js | 73 + node_modules/pg/lib/native/client.js | 329 +++++ node_modules/pg/lib/native/index.js | 2 + node_modules/pg/lib/native/query.js | 165 +++ node_modules/pg/lib/query.js | 252 ++++ node_modules/pg/lib/result.js | 109 ++ node_modules/pg/lib/stream.js | 83 ++ node_modules/pg/lib/type-overrides.js | 35 + node_modules/pg/lib/utils.js | 207 +++ node_modules/pg/package.json | 76 + node_modules/pgpass/README.md | 74 + node_modules/pgpass/lib/helper.js | 233 ++++ node_modules/pgpass/lib/index.js | 23 + node_modules/pgpass/package.json | 41 + node_modules/postgres-array/index.d.ts | 4 + node_modules/postgres-array/index.js | 97 ++ node_modules/postgres-array/license | 21 + node_modules/postgres-array/package.json | 35 + node_modules/postgres-array/readme.md | 43 + node_modules/postgres-bytea/index.js | 33 + node_modules/postgres-bytea/license | 21 + node_modules/postgres-bytea/package.json | 34 + node_modules/postgres-bytea/readme.md | 34 + node_modules/postgres-date/index.js | 116 ++ node_modules/postgres-date/license | 21 + node_modules/postgres-date/package.json | 33 + node_modules/postgres-date/readme.md | 49 + node_modules/postgres-interval/index.d.ts | 20 + node_modules/postgres-interval/index.js | 125 ++ node_modules/postgres-interval/license | 21 + node_modules/postgres-interval/package.json | 36 + node_modules/postgres-interval/readme.md | 48 + node_modules/semver/LICENSE | 15 + node_modules/semver/README.md | 680 +++++++++ node_modules/semver/bin/semver.js | 195 +++ node_modules/semver/classes/comparator.js | 143 ++ node_modules/semver/classes/index.js | 7 + node_modules/semver/classes/range.js | 564 ++++++++ node_modules/semver/classes/semver.js | 333 +++++ node_modules/semver/functions/clean.js | 8 + node_modules/semver/functions/cmp.js | 54 + node_modules/semver/functions/coerce.js | 62 + .../semver/functions/compare-build.js | 9 + .../semver/functions/compare-loose.js | 5 + node_modules/semver/functions/compare.js | 7 + node_modules/semver/functions/diff.js | 60 + node_modules/semver/functions/eq.js | 5 + node_modules/semver/functions/gt.js | 5 + node_modules/semver/functions/gte.js | 5 + node_modules/semver/functions/inc.js | 21 + node_modules/semver/functions/lt.js | 5 + node_modules/semver/functions/lte.js | 5 + node_modules/semver/functions/major.js | 5 + node_modules/semver/functions/minor.js | 5 + node_modules/semver/functions/neq.js | 5 + node_modules/semver/functions/parse.js | 18 + node_modules/semver/functions/patch.js | 5 + node_modules/semver/functions/prerelease.js | 8 + node_modules/semver/functions/rcompare.js | 5 + node_modules/semver/functions/rsort.js | 5 + node_modules/semver/functions/satisfies.js | 12 + node_modules/semver/functions/sort.js | 5 + node_modules/semver/functions/truncate.js | 48 + node_modules/semver/functions/valid.js | 8 + node_modules/semver/index.js | 93 ++ node_modules/semver/internal/constants.js | 37 + node_modules/semver/internal/debug.js | 11 + node_modules/semver/internal/identifiers.js | 29 + node_modules/semver/internal/lrucache.js | 42 + node_modules/semver/internal/parse-options.js | 17 + node_modules/semver/internal/re.js | 223 +++ node_modules/semver/package.json | 78 ++ node_modules/semver/preload.js | 4 + node_modules/semver/range.bnf | 17 + node_modules/semver/ranges/gtr.js | 6 + node_modules/semver/ranges/intersects.js | 9 + node_modules/semver/ranges/ltr.js | 6 + node_modules/semver/ranges/max-satisfying.js | 27 + node_modules/semver/ranges/min-satisfying.js | 26 + node_modules/semver/ranges/min-version.js | 63 + node_modules/semver/ranges/outside.js | 82 ++ node_modules/semver/ranges/simplify.js | 49 + node_modules/semver/ranges/subset.js | 249 ++++ node_modules/semver/ranges/to-comparators.js | 10 + node_modules/semver/ranges/valid.js | 13 + node_modules/split2/LICENSE | 13 + node_modules/split2/README.md | 85 ++ node_modules/split2/bench.js | 27 + node_modules/split2/index.js | 141 ++ node_modules/split2/package.json | 39 + node_modules/split2/test.js | 409 ++++++ node_modules/xtend/.jshintrc | 30 + node_modules/xtend/LICENSE | 20 + node_modules/xtend/README.md | 32 + node_modules/xtend/immutable.js | 19 + node_modules/xtend/mutable.js | 17 + node_modules/xtend/package.json | 55 + node_modules/xtend/test.js | 103 ++ package-lock.json | 277 ++++ package.json | 3 + register.html | 289 +++- super-admin.html | 866 ++++++++++-- tenantLoader.js | 90 +- 299 files changed, 27738 insertions(+), 350 deletions(-) create mode 100644 backend/authMiddleware.js create mode 100644 backend/db.js create mode 100644 node_modules/.bin/bcrypt create mode 100644 node_modules/.bin/bcrypt.cmd create mode 100644 node_modules/.bin/bcrypt.ps1 create mode 100644 node_modules/.bin/semver create mode 100644 node_modules/.bin/semver.cmd create mode 100644 node_modules/.bin/semver.ps1 create mode 100644 node_modules/bcryptjs/LICENSE create mode 100644 node_modules/bcryptjs/README.md create mode 100644 node_modules/bcryptjs/bin/bcrypt create mode 100644 node_modules/bcryptjs/index.d.ts create mode 100644 node_modules/bcryptjs/index.js create mode 100644 node_modules/bcryptjs/package.json create mode 100644 node_modules/bcryptjs/types.d.ts create mode 100644 node_modules/bcryptjs/umd/index.d.ts create mode 100644 node_modules/bcryptjs/umd/index.js create mode 100644 node_modules/bcryptjs/umd/package.json create mode 100644 node_modules/bcryptjs/umd/types.d.ts create mode 100644 node_modules/buffer-equal-constant-time/.npmignore create mode 100644 node_modules/buffer-equal-constant-time/.travis.yml create mode 100644 node_modules/buffer-equal-constant-time/LICENSE.txt create mode 100644 node_modules/buffer-equal-constant-time/README.md create mode 100644 node_modules/buffer-equal-constant-time/index.js create mode 100644 node_modules/buffer-equal-constant-time/package.json create mode 100644 node_modules/buffer-equal-constant-time/test.js create mode 100644 node_modules/ecdsa-sig-formatter/CODEOWNERS create mode 100644 node_modules/ecdsa-sig-formatter/LICENSE create mode 100644 node_modules/ecdsa-sig-formatter/README.md create mode 100644 node_modules/ecdsa-sig-formatter/package.json create mode 100644 node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.d.ts create mode 100644 node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.js create mode 100644 node_modules/ecdsa-sig-formatter/src/param-bytes-for-alg.js create mode 100644 node_modules/jsonwebtoken/LICENSE create mode 100644 node_modules/jsonwebtoken/README.md create mode 100644 node_modules/jsonwebtoken/decode.js create mode 100644 node_modules/jsonwebtoken/index.js create mode 100644 node_modules/jsonwebtoken/lib/JsonWebTokenError.js create mode 100644 node_modules/jsonwebtoken/lib/NotBeforeError.js create mode 100644 node_modules/jsonwebtoken/lib/TokenExpiredError.js create mode 100644 node_modules/jsonwebtoken/lib/asymmetricKeyDetailsSupported.js create mode 100644 node_modules/jsonwebtoken/lib/psSupported.js create mode 100644 node_modules/jsonwebtoken/lib/rsaPssKeyDetailsSupported.js create mode 100644 node_modules/jsonwebtoken/lib/timespan.js create mode 100644 node_modules/jsonwebtoken/lib/validateAsymmetricKey.js create mode 100644 node_modules/jsonwebtoken/node_modules/ms/index.js create mode 100644 node_modules/jsonwebtoken/node_modules/ms/license.md create mode 100644 node_modules/jsonwebtoken/node_modules/ms/package.json create mode 100644 node_modules/jsonwebtoken/node_modules/ms/readme.md create mode 100644 node_modules/jsonwebtoken/package.json create mode 100644 node_modules/jsonwebtoken/sign.js create mode 100644 node_modules/jsonwebtoken/verify.js create mode 100644 node_modules/jwa/LICENSE create mode 100644 node_modules/jwa/README.md create mode 100644 node_modules/jwa/index.js create mode 100644 node_modules/jwa/opslevel.yml create mode 100644 node_modules/jwa/package.json create mode 100644 node_modules/jws/CHANGELOG.md create mode 100644 node_modules/jws/LICENSE create mode 100644 node_modules/jws/index.js create mode 100644 node_modules/jws/lib/data-stream.js create mode 100644 node_modules/jws/lib/sign-stream.js create mode 100644 node_modules/jws/lib/tostring.js create mode 100644 node_modules/jws/lib/verify-stream.js create mode 100644 node_modules/jws/opslevel.yml create mode 100644 node_modules/jws/package.json create mode 100644 node_modules/jws/readme.md create mode 100644 node_modules/lodash.includes/LICENSE create mode 100644 node_modules/lodash.includes/README.md create mode 100644 node_modules/lodash.includes/index.js create mode 100644 node_modules/lodash.includes/package.json create mode 100644 node_modules/lodash.isboolean/LICENSE create mode 100644 node_modules/lodash.isboolean/README.md create mode 100644 node_modules/lodash.isboolean/index.js create mode 100644 node_modules/lodash.isboolean/package.json create mode 100644 node_modules/lodash.isinteger/LICENSE create mode 100644 node_modules/lodash.isinteger/README.md create mode 100644 node_modules/lodash.isinteger/index.js create mode 100644 node_modules/lodash.isinteger/package.json create mode 100644 node_modules/lodash.isnumber/LICENSE create mode 100644 node_modules/lodash.isnumber/README.md create mode 100644 node_modules/lodash.isnumber/index.js create mode 100644 node_modules/lodash.isnumber/package.json create mode 100644 node_modules/lodash.isplainobject/LICENSE create mode 100644 node_modules/lodash.isplainobject/README.md create mode 100644 node_modules/lodash.isplainobject/index.js create mode 100644 node_modules/lodash.isplainobject/package.json create mode 100644 node_modules/lodash.isstring/LICENSE create mode 100644 node_modules/lodash.isstring/README.md create mode 100644 node_modules/lodash.isstring/index.js create mode 100644 node_modules/lodash.isstring/package.json create mode 100644 node_modules/lodash.once/LICENSE create mode 100644 node_modules/lodash.once/README.md create mode 100644 node_modules/lodash.once/index.js create mode 100644 node_modules/lodash.once/package.json create mode 100644 node_modules/pg-cloudflare/LICENSE create mode 100644 node_modules/pg-cloudflare/README.md create mode 100644 node_modules/pg-cloudflare/dist/empty.d.ts create mode 100644 node_modules/pg-cloudflare/dist/empty.js create mode 100644 node_modules/pg-cloudflare/dist/empty.js.map create mode 100644 node_modules/pg-cloudflare/dist/index.d.ts create mode 100644 node_modules/pg-cloudflare/dist/index.js create mode 100644 node_modules/pg-cloudflare/dist/index.js.map create mode 100644 node_modules/pg-cloudflare/esm/index.mjs create mode 100644 node_modules/pg-cloudflare/package.json create mode 100644 node_modules/pg-cloudflare/src/empty.ts create mode 100644 node_modules/pg-cloudflare/src/index.ts create mode 100644 node_modules/pg-cloudflare/src/types.d.ts create mode 100644 node_modules/pg-connection-string/LICENSE create mode 100644 node_modules/pg-connection-string/README.md create mode 100644 node_modules/pg-connection-string/esm/index.mjs create mode 100644 node_modules/pg-connection-string/index.d.ts create mode 100644 node_modules/pg-connection-string/index.js create mode 100644 node_modules/pg-connection-string/package.json create mode 100644 node_modules/pg-int8/LICENSE create mode 100644 node_modules/pg-int8/README.md create mode 100644 node_modules/pg-int8/index.js create mode 100644 node_modules/pg-int8/package.json create mode 100644 node_modules/pg-pool/LICENSE create mode 100644 node_modules/pg-pool/README.md create mode 100644 node_modules/pg-pool/esm/index.mjs create mode 100644 node_modules/pg-pool/index.js create mode 100644 node_modules/pg-pool/package.json create mode 100644 node_modules/pg-protocol/LICENSE create mode 100644 node_modules/pg-protocol/README.md create mode 100644 node_modules/pg-protocol/dist/b.d.ts create mode 100644 node_modules/pg-protocol/dist/b.js create mode 100644 node_modules/pg-protocol/dist/b.js.map create mode 100644 node_modules/pg-protocol/dist/buffer-reader.d.ts create mode 100644 node_modules/pg-protocol/dist/buffer-reader.js create mode 100644 node_modules/pg-protocol/dist/buffer-reader.js.map create mode 100644 node_modules/pg-protocol/dist/buffer-writer.d.ts create mode 100644 node_modules/pg-protocol/dist/buffer-writer.js create mode 100644 node_modules/pg-protocol/dist/buffer-writer.js.map create mode 100644 node_modules/pg-protocol/dist/inbound-parser.test.d.ts create mode 100644 node_modules/pg-protocol/dist/inbound-parser.test.js create mode 100644 node_modules/pg-protocol/dist/inbound-parser.test.js.map create mode 100644 node_modules/pg-protocol/dist/index.d.ts create mode 100644 node_modules/pg-protocol/dist/index.js create mode 100644 node_modules/pg-protocol/dist/index.js.map create mode 100644 node_modules/pg-protocol/dist/messages.d.ts create mode 100644 node_modules/pg-protocol/dist/messages.js create mode 100644 node_modules/pg-protocol/dist/messages.js.map create mode 100644 node_modules/pg-protocol/dist/outbound-serializer.test.d.ts create mode 100644 node_modules/pg-protocol/dist/outbound-serializer.test.js create mode 100644 node_modules/pg-protocol/dist/outbound-serializer.test.js.map create mode 100644 node_modules/pg-protocol/dist/parser.d.ts create mode 100644 node_modules/pg-protocol/dist/parser.js create mode 100644 node_modules/pg-protocol/dist/parser.js.map create mode 100644 node_modules/pg-protocol/dist/serializer.d.ts create mode 100644 node_modules/pg-protocol/dist/serializer.js create mode 100644 node_modules/pg-protocol/dist/serializer.js.map create mode 100644 node_modules/pg-protocol/esm/index.js create mode 100644 node_modules/pg-protocol/package.json create mode 100644 node_modules/pg-protocol/src/b.ts create mode 100644 node_modules/pg-protocol/src/buffer-reader.ts create mode 100644 node_modules/pg-protocol/src/buffer-writer.ts create mode 100644 node_modules/pg-protocol/src/inbound-parser.test.ts create mode 100644 node_modules/pg-protocol/src/index.ts create mode 100644 node_modules/pg-protocol/src/messages.ts create mode 100644 node_modules/pg-protocol/src/outbound-serializer.test.ts create mode 100644 node_modules/pg-protocol/src/parser.ts create mode 100644 node_modules/pg-protocol/src/serializer.ts create mode 100644 node_modules/pg-protocol/src/testing/buffer-list.ts create mode 100644 node_modules/pg-protocol/src/testing/test-buffers.ts create mode 100644 node_modules/pg-protocol/src/types/chunky.d.ts create mode 100644 node_modules/pg-types/.travis.yml create mode 100644 node_modules/pg-types/Makefile create mode 100644 node_modules/pg-types/README.md create mode 100644 node_modules/pg-types/index.d.ts create mode 100644 node_modules/pg-types/index.js create mode 100644 node_modules/pg-types/index.test-d.ts create mode 100644 node_modules/pg-types/lib/arrayParser.js create mode 100644 node_modules/pg-types/lib/binaryParsers.js create mode 100644 node_modules/pg-types/lib/builtins.js create mode 100644 node_modules/pg-types/lib/textParsers.js create mode 100644 node_modules/pg-types/package.json create mode 100644 node_modules/pg-types/test/index.js create mode 100644 node_modules/pg-types/test/types.js create mode 100644 node_modules/pg/LICENSE create mode 100644 node_modules/pg/README.md create mode 100644 node_modules/pg/esm/index.mjs create mode 100644 node_modules/pg/lib/client.js create mode 100644 node_modules/pg/lib/connection-parameters.js create mode 100644 node_modules/pg/lib/connection.js create mode 100644 node_modules/pg/lib/crypto/cert-signatures.js create mode 100644 node_modules/pg/lib/crypto/sasl.js create mode 100644 node_modules/pg/lib/crypto/utils.js create mode 100644 node_modules/pg/lib/defaults.js create mode 100644 node_modules/pg/lib/index.js create mode 100644 node_modules/pg/lib/native/client.js create mode 100644 node_modules/pg/lib/native/index.js create mode 100644 node_modules/pg/lib/native/query.js create mode 100644 node_modules/pg/lib/query.js create mode 100644 node_modules/pg/lib/result.js create mode 100644 node_modules/pg/lib/stream.js create mode 100644 node_modules/pg/lib/type-overrides.js create mode 100644 node_modules/pg/lib/utils.js create mode 100644 node_modules/pg/package.json create mode 100644 node_modules/pgpass/README.md create mode 100644 node_modules/pgpass/lib/helper.js create mode 100644 node_modules/pgpass/lib/index.js create mode 100644 node_modules/pgpass/package.json create mode 100644 node_modules/postgres-array/index.d.ts create mode 100644 node_modules/postgres-array/index.js create mode 100644 node_modules/postgres-array/license create mode 100644 node_modules/postgres-array/package.json create mode 100644 node_modules/postgres-array/readme.md create mode 100644 node_modules/postgres-bytea/index.js create mode 100644 node_modules/postgres-bytea/license create mode 100644 node_modules/postgres-bytea/package.json create mode 100644 node_modules/postgres-bytea/readme.md create mode 100644 node_modules/postgres-date/index.js create mode 100644 node_modules/postgres-date/license create mode 100644 node_modules/postgres-date/package.json create mode 100644 node_modules/postgres-date/readme.md create mode 100644 node_modules/postgres-interval/index.d.ts create mode 100644 node_modules/postgres-interval/index.js create mode 100644 node_modules/postgres-interval/license create mode 100644 node_modules/postgres-interval/package.json create mode 100644 node_modules/postgres-interval/readme.md create mode 100644 node_modules/semver/LICENSE create mode 100644 node_modules/semver/README.md create mode 100644 node_modules/semver/bin/semver.js create mode 100644 node_modules/semver/classes/comparator.js create mode 100644 node_modules/semver/classes/index.js create mode 100644 node_modules/semver/classes/range.js create mode 100644 node_modules/semver/classes/semver.js create mode 100644 node_modules/semver/functions/clean.js create mode 100644 node_modules/semver/functions/cmp.js create mode 100644 node_modules/semver/functions/coerce.js create mode 100644 node_modules/semver/functions/compare-build.js create mode 100644 node_modules/semver/functions/compare-loose.js create mode 100644 node_modules/semver/functions/compare.js create mode 100644 node_modules/semver/functions/diff.js create mode 100644 node_modules/semver/functions/eq.js create mode 100644 node_modules/semver/functions/gt.js create mode 100644 node_modules/semver/functions/gte.js create mode 100644 node_modules/semver/functions/inc.js create mode 100644 node_modules/semver/functions/lt.js create mode 100644 node_modules/semver/functions/lte.js create mode 100644 node_modules/semver/functions/major.js create mode 100644 node_modules/semver/functions/minor.js create mode 100644 node_modules/semver/functions/neq.js create mode 100644 node_modules/semver/functions/parse.js create mode 100644 node_modules/semver/functions/patch.js create mode 100644 node_modules/semver/functions/prerelease.js create mode 100644 node_modules/semver/functions/rcompare.js create mode 100644 node_modules/semver/functions/rsort.js create mode 100644 node_modules/semver/functions/satisfies.js create mode 100644 node_modules/semver/functions/sort.js create mode 100644 node_modules/semver/functions/truncate.js create mode 100644 node_modules/semver/functions/valid.js create mode 100644 node_modules/semver/index.js create mode 100644 node_modules/semver/internal/constants.js create mode 100644 node_modules/semver/internal/debug.js create mode 100644 node_modules/semver/internal/identifiers.js create mode 100644 node_modules/semver/internal/lrucache.js create mode 100644 node_modules/semver/internal/parse-options.js create mode 100644 node_modules/semver/internal/re.js create mode 100644 node_modules/semver/package.json create mode 100644 node_modules/semver/preload.js create mode 100644 node_modules/semver/range.bnf create mode 100644 node_modules/semver/ranges/gtr.js create mode 100644 node_modules/semver/ranges/intersects.js create mode 100644 node_modules/semver/ranges/ltr.js create mode 100644 node_modules/semver/ranges/max-satisfying.js create mode 100644 node_modules/semver/ranges/min-satisfying.js create mode 100644 node_modules/semver/ranges/min-version.js create mode 100644 node_modules/semver/ranges/outside.js create mode 100644 node_modules/semver/ranges/simplify.js create mode 100644 node_modules/semver/ranges/subset.js create mode 100644 node_modules/semver/ranges/to-comparators.js create mode 100644 node_modules/semver/ranges/valid.js create mode 100644 node_modules/split2/LICENSE create mode 100644 node_modules/split2/README.md create mode 100644 node_modules/split2/bench.js create mode 100644 node_modules/split2/index.js create mode 100644 node_modules/split2/package.json create mode 100644 node_modules/split2/test.js create mode 100644 node_modules/xtend/.jshintrc create mode 100644 node_modules/xtend/LICENSE create mode 100644 node_modules/xtend/README.md create mode 100644 node_modules/xtend/immutable.js create mode 100644 node_modules/xtend/mutable.js create mode 100644 node_modules/xtend/package.json create mode 100644 node_modules/xtend/test.js diff --git a/backend/authMiddleware.js b/backend/authMiddleware.js new file mode 100644 index 0000000..555af29 --- /dev/null +++ b/backend/authMiddleware.js @@ -0,0 +1,58 @@ +/** + * authMiddleware.js — JWT sign/verify helpers + Express middleware for Curio HMS. + */ + +const jwt = require('jsonwebtoken'); + +const JWT_SECRET = process.env.JWT_SECRET || 'curio_dev_secret_change_in_production'; +const JWT_EXPIRY = '7d'; + +/** + * Sign a JWT with the given payload. + * @param {{ userId, email, role, tenantId }} payload + * @returns {string} signed JWT + */ +function signToken(payload) { + return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRY }); +} + +/** + * Express middleware: verifies JWT and enforces role access. + * @param {...string} roles - allowed roles; empty = any authenticated user + */ +function requireAuth(...roles) { + return (req, res, next) => { + const header = req.headers['authorization'] || req.headers['x-auth-token']; + if (!header) return res.status(401).json({ error: 'Authentication required' }); + + const token = header.startsWith('Bearer ') ? header.slice(7) : header; + try { + const decoded = jwt.verify(token, JWT_SECRET); + req.user = decoded; + + if (roles.length > 0 && !roles.includes(decoded.role)) { + return res.status(403).json({ error: 'Insufficient permissions' }); + } + next(); + } catch (err) { + return res.status(401).json({ error: 'Invalid or expired token' }); + } + }; +} + +/** + * Middleware: verifies JWT but does NOT reject unauthenticated requests. + * Attaches req.user if valid token present. + */ +function optionalAuth(req, res, next) { + const header = req.headers['authorization'] || req.headers['x-auth-token']; + if (header) { + const token = header.startsWith('Bearer ') ? header.slice(7) : header; + try { + req.user = jwt.verify(token, JWT_SECRET); + } catch (_) { /* ignore */ } + } + next(); +} + +module.exports = { signToken, requireAuth, optionalAuth }; diff --git a/backend/configManager.js b/backend/configManager.js index 6282c60..909c494 100644 --- a/backend/configManager.js +++ b/backend/configManager.js @@ -1,64 +1,40 @@ /** - * ConfigManager: Central configuration for multi-tenant hospital settings and white-labeling. + * configManager.js — In-memory fallback config for multi-tenant hospital settings. + * The server.js DB layer takes priority; this is the fallback for legacy/dev use. */ -class ConfigManager { - constructor() { - this.tenants = { - 'default': { - name: "Curio Clinic", - logo: "C", - primaryColor: "#0F172A", - secondaryColor: "#38BDF8", - consultationFee: 500, - currency: "₹", - workingHours: [ - { start: "09:00", end: "13:00" }, - { start: "17:00", end: "22:00" } - ], - daysOff: [0], // 0 = Sunday - holidays: ["2024-12-25", "2025-01-01"] - }, - 'sharma-clinic': { - name: "Dr. Sharma's Cardiology", - logo: "S", - primaryColor: "#1E3A8A", - secondaryColor: "#60A5FA", - consultationFee: 800, - currency: "₹", - workingHours: [ - { start: "10:00", end: "14:00" }, - { start: "18:00", end: "21:00" } - ], - daysOff: [0, 6], // Sat, Sun - holidays: [] - }, - 'apollo-hospitals': { - name: "Apollo Multispecialty", - logo: "A", - primaryColor: "#065F46", - secondaryColor: "#34D399", - consultationFee: 1200, - currency: "₹", - workingHours: [ - { start: "09:00", end: "22:00" } - ], - daysOff: [], - holidays: ["2024-10-02"] - } - }; +const TENANTS = { + 'default': { + name: "Curio Clinic", logo: "C", + primaryColor: "#0F172A", secondaryColor: "#38BDF8", + consultationFee: 500, currency: "₹", + workingHours: [{ start: "09:00", end: "13:00" }, { start: "17:00", end: "22:00" }], + daysOff: [0], holidays: ["2024-12-25", "2025-01-01"] + }, + 'sharma-clinic': { + name: "Dr. Sharma's Cardiology", logo: "S", + primaryColor: "#1E3A8A", secondaryColor: "#60A5FA", + consultationFee: 800, currency: "₹", + workingHours: [{ start: "10:00", end: "14:00" }, { start: "18:00", end: "21:00" }], + daysOff: [0, 6], holidays: [] + }, + 'apollo-hospitals': { + name: "Apollo Multispecialty", logo: "A", + primaryColor: "#065F46", secondaryColor: "#34D399", + consultationFee: 1200, currency: "₹", + workingHours: [{ start: "09:00", end: "22:00" }], + daysOff: [], holidays: ["2024-10-02"] } +}; - getTenantConfig(tenantId) { - return this.tenants[tenantId] || this.tenants['default']; - } - - updateTenantConfig(tenantId, newConfig) { - if (!this.tenants[tenantId]) { - this.tenants[tenantId] = { ...this.tenants['default'] }; - } - this.tenants[tenantId] = { ...this.tenants[tenantId], ...newConfig }; - } +function getTenantConfig(tenantId) { + return TENANTS[tenantId] || TENANTS['default']; } -module.exports = new ConfigManager(); +function updateTenantConfig(tenantId, newConfig) { + if (!TENANTS[tenantId]) TENANTS[tenantId] = { ...TENANTS['default'] }; + TENANTS[tenantId] = { ...TENANTS[tenantId], ...newConfig }; +} + +module.exports = { getTenantConfig, updateTenantConfig }; + diff --git a/backend/db.js b/backend/db.js new file mode 100644 index 0000000..6a60efb --- /dev/null +++ b/backend/db.js @@ -0,0 +1,154 @@ +/** + * db.js — PostgreSQL connection pool + schema initialisation for Curio HMS. + * Tables: tenants, users, pending_registrations + */ + +const { Pool } = require('pg'); +const bcrypt = require('bcryptjs'); + +const pool = new Pool({ + connectionString: process.env.DATABASE_URL || 'postgres://postgres:curio_secret@curio-db:5432/curio', + ssl: false, +}); + +// ── Schema ────────────────────────────────────────────────────────────────── + +const SCHEMA_SQL = ` +CREATE TABLE IF NOT EXISTS tenants ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + logo TEXT DEFAULT 'C', + primary_color TEXT DEFAULT '#0F172A', + secondary_color TEXT DEFAULT '#38BDF8', + consultation_fee INTEGER DEFAULT 500, + currency TEXT DEFAULT '₹', + plan TEXT DEFAULT 'basic', + status TEXT DEFAULT 'active', + whatsapp_number TEXT, + specialty TEXT, + city TEXT, + working_hours JSONB DEFAULT '[{"start":"09:00","end":"13:00"},{"start":"17:00","end":"22:00"}]', + days_off JSONB DEFAULT '[0]', + holidays JSONB DEFAULT '[]', + created_at TIMESTAMPTZ DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + tenant_id TEXT REFERENCES tenants(id) ON DELETE CASCADE, + email TEXT UNIQUE NOT NULL, + password_hash TEXT NOT NULL, + name TEXT NOT NULL, + role TEXT NOT NULL CHECK (role IN ('SUPER_ADMIN','OWNER','DOCTOR','RECEPTIONIST','PHARMACIST','LAB_TECH')), + status TEXT DEFAULT 'active', + created_at TIMESTAMPTZ DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS pending_registrations ( + id SERIAL PRIMARY KEY, + owner_name TEXT NOT NULL, + clinic_name TEXT NOT NULL, + specialty TEXT, + city TEXT, + whatsapp_number TEXT NOT NULL, + email TEXT NOT NULL, + password_hash TEXT NOT NULL, + plan TEXT DEFAULT 'basic', + status TEXT DEFAULT 'pending', + created_at TIMESTAMPTZ DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_users_tenant ON users(tenant_id); +CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); +CREATE INDEX IF NOT EXISTS idx_pending_status ON pending_registrations(status); +`; + +// ── Seed super admin ───────────────────────────────────────────────────────── + +async function seedSuperAdmin() { + const email = process.env.SUPER_ADMIN_EMAIL || 'admin@curio.app'; + const password = process.env.SUPER_ADMIN_PASSWORD || 'superadmin'; + + const existing = await pool.query('SELECT id FROM users WHERE email = $1', [email]); + if (existing.rows.length > 0) return; + + const hash = await bcrypt.hash(password, 10); + await pool.query( + `INSERT INTO users (tenant_id, email, password_hash, name, role) + VALUES (NULL, $1, $2, 'Super Admin', 'SUPER_ADMIN')`, + [email, hash] + ); + console.log(`[DB] Super admin seeded: ${email}`); +} + +// ── Seed legacy in-memory tenants into DB ───────────────────────────────────── + +async function seedLegacyTenants() { + const legacy = [ + { + id: 'default', + name: 'Curio Clinic', + logo: 'C', + primary_color: '#0F172A', + secondary_color: '#38BDF8', + consultation_fee: 500, + plan: 'basic', + working_hours: JSON.stringify([{ start: '09:00', end: '13:00' }, { start: '17:00', end: '22:00' }]), + days_off: JSON.stringify([0]), + }, + { + id: 'sharma-clinic', + name: "Dr. Sharma's Cardiology", + logo: 'S', + primary_color: '#1E3A8A', + secondary_color: '#60A5FA', + consultation_fee: 800, + plan: 'pro', + working_hours: JSON.stringify([{ start: '10:00', end: '14:00' }, { start: '18:00', end: '21:00' }]), + days_off: JSON.stringify([0, 6]), + }, + { + id: 'apollo-hospitals', + name: 'Apollo Multispecialty', + logo: 'A', + primary_color: '#065F46', + secondary_color: '#34D399', + consultation_fee: 1200, + plan: 'enterprise', + working_hours: JSON.stringify([{ start: '09:00', end: '22:00' }]), + days_off: JSON.stringify([]), + }, + ]; + + for (const t of legacy) { + await pool.query( + `INSERT INTO tenants (id, name, logo, primary_color, secondary_color, consultation_fee, plan, working_hours, days_off) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9::jsonb) + ON CONFLICT (id) DO NOTHING`, + [t.id, t.name, t.logo, t.primary_color, t.secondary_color, t.consultation_fee, t.plan, t.working_hours, t.days_off] + ); + } + console.log('[DB] Legacy tenants seeded.'); +} + +// ── Init ────────────────────────────────────────────────────────────────────── + +async function initSchema() { + try { + await pool.query(SCHEMA_SQL); + console.log('[DB] Schema ready.'); + await seedSuperAdmin(); + await seedLegacyTenants(); + } catch (err) { + console.error('[DB] Schema init failed:', err.message); + // Don't crash — let server start even if DB is temporarily unavailable + } +} + +// ── Exports ─────────────────────────────────────────────────────────────────── + +function query(sql, params) { + return pool.query(sql, params); +} + +module.exports = { query, pool, initSchema }; diff --git a/backend/queueManager.js b/backend/queueManager.js index db3a3f4..e9a875d 100644 --- a/backend/queueManager.js +++ b/backend/queueManager.js @@ -5,7 +5,7 @@ * - 5 Walk-in slots */ -const configManager = require('./configManager'); +const { getTenantConfig } = require('./configManager'); const notificationManager = require('./notificationManager'); class QueueManager { @@ -14,7 +14,7 @@ class QueueManager { } getSlotKey(tenantId, date, time) { - const config = configManager.getTenantConfig(tenantId); + const config = getTenantConfig(tenantId); const duration = config.slotDuration || 30; const [hours, minutes] = time.split(':'); const roundedMins = parseInt(minutes) < duration ? '00' : duration; @@ -22,7 +22,7 @@ class QueueManager { } isAvailable(tenantId, date, time) { - const config = configManager.getTenantConfig(tenantId); + const config = getTenantConfig(tenantId); const d = new Date(date + "T00:00:00"); // Check days off @@ -53,7 +53,7 @@ class QueueManager { getSlotStatus(tenantId, date, time) { const key = this.getSlotKey(tenantId, date, time); if (!this.slots[key]) { - const config = configManager.getTenantConfig(tenantId); + const config = getTenantConfig(tenantId); this.slots[key] = { online: 0, walkin: 0, @@ -69,7 +69,7 @@ class QueueManager { return { success: false, message: "The clinic is closed at this time / on this day." }; } const status = this.getSlotStatus(tenantId, date, time); - const config = configManager.getTenantConfig(tenantId); + const config = getTenantConfig(tenantId); if (status.online < status.maxOnline) { status.online++; return { @@ -88,7 +88,7 @@ class QueueManager { return { success: false, message: "The clinic is closed at this time / on this day." }; } const status = this.getSlotStatus(tenantId, date, time); - const config = configManager.getTenantConfig(tenantId); + const config = getTenantConfig(tenantId); if (status.walkin < status.maxWalkin) { status.walkin++; return { @@ -102,7 +102,7 @@ class QueueManager { } getDailyUpdates(patientId, tenantId = 'default') { - const config = configManager.getTenantConfig(tenantId); + const config = getTenantConfig(tenantId); return [ `Good morning! Your appointment with ${config.name} is scheduled for today.`, "Queue Update: The clinic is running 10 mins behind schedule. Please plan accordingly.", @@ -121,7 +121,7 @@ class QueueManager { } notifyAvailableSlot(tenantId, date, time) { - const config = configManager.getTenantConfig(tenantId); + const config = getTenantConfig(tenantId); console.log(`[Queue] Triggering notifications for open slot: ${time}`); // Mock finding a patient with a later slot diff --git a/backend/server.js b/backend/server.js index 8361b64..c61eae1 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,9 +1,14 @@ const express = require('express'); const path = require('path'); -const botLogic = require('./botLogic'); -const configManager = require('./configManager'); -const { getTemporalClient } = require('./temporalClient'); const crypto = require('crypto'); +const bcrypt = require('bcryptjs'); + +const botLogic = require('./botLogic'); +const { getTenantConfig, updateTenantConfig } = require('./configManager'); +const { getTemporalClient } = require('./temporalClient'); +const { query, initSchema } = require('./db'); +const { signToken, requireAuth } = require('./authMiddleware'); + const app = express(); const port = 3000; @@ -11,17 +16,13 @@ app.use(express.json()); const staticRoot = path.join(__dirname, '../'); -/** UTF-8 Content-Type for text assets (fixes emoji/₹ mojibake when proxy omits charset). */ +// ── Helpers ─────────────────────────────────────────────────────────────────── + function setUtf8ContentType(res, filePath) { - if (filePath.endsWith('.html')) { - res.setHeader('Content-Type', 'text/html; charset=utf-8'); - } else if (filePath.endsWith('.css')) { - res.setHeader('Content-Type', 'text/css; charset=utf-8'); - } else if (filePath.endsWith('.js')) { - res.setHeader('Content-Type', 'application/javascript; charset=utf-8'); - } else if (filePath.endsWith('.json')) { - res.setHeader('Content-Type', 'application/json; charset=utf-8'); - } + if (filePath.endsWith('.html')) res.setHeader('Content-Type', 'text/html; charset=utf-8'); + else if (filePath.endsWith('.css')) res.setHeader('Content-Type', 'text/css; charset=utf-8'); + else if (filePath.endsWith('.js')) res.setHeader('Content-Type', 'application/javascript; charset=utf-8'); + else if (filePath.endsWith('.json')) res.setHeader('Content-Type', 'application/json; charset=utf-8'); } function setNoCacheHeaders(res) { @@ -36,98 +37,498 @@ function sendHtmlPage(res, filename) { res.sendFile(path.join(staticRoot, filename)); } -// Serve static files +// Slug-safe clinic ID generator from clinic name +function slugify(name) { + return name.toLowerCase() + .replace(/[^\w\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .slice(0, 40); +} + +async function uniqueSlug(base) { + let slug = slugify(base); + let attempt = slug; + let counter = 2; + while (true) { + const existing = await query('SELECT id FROM tenants WHERE id = $1', [attempt]); + if (existing.rows.length === 0) return attempt; + attempt = `${slug}-${counter++}`; + } +} + +// ── Static files ────────────────────────────────────────────────────────────── + app.use(express.static(staticRoot, { setHeaders: (res, filePath) => { setUtf8ContentType(res, filePath); - if (filePath.endsWith('.html')) { - setNoCacheHeaders(res); - } + if (filePath.endsWith('.html')) setNoCacheHeaders(res); } })); -// Primary Routes -app.get('/', (req, res) => sendHtmlPage(res, 'login.html')); -app.get('/admin', (req, res) => sendHtmlPage(res, 'admin.html')); -app.get('/dashboard', (req, res) => sendHtmlPage(res, 'dashboard.html')); +// ── Page routes ─────────────────────────────────────────────────────────────── -// Multi-tenant Config API -app.get('/api/config/:tenantId', (req, res) => { - const config = configManager.getTenantConfig(req.params.tenantId); - res.json(config); +app.get('/', (req, res) => sendHtmlPage(res, 'login.html')); +app.get('/admin', (req, res) => sendHtmlPage(res, 'admin.html')); +app.get('/dashboard', (req, res) => sendHtmlPage(res, 'dashboard.html')); +app.get('/register', (req, res) => sendHtmlPage(res, 'register.html')); +app.get('/super-admin', (req, res) => sendHtmlPage(res, 'super-admin.html')); + +// ── Multi-tenant Config API (DB-first, fallback to in-memory) ───────────────── + +app.get('/api/config/:tenantId', async (req, res) => { + try { + const row = await query('SELECT * FROM tenants WHERE id = $1 AND status = $2', [req.params.tenantId, 'active']); + if (row.rows.length > 0) { + const t = row.rows[0]; + return res.json({ + name: t.name, + logo: t.logo, + primaryColor: t.primary_color, + secondaryColor: t.secondary_color, + consultationFee: t.consultation_fee, + currency: t.currency || '₹', + plan: t.plan, + specialty: t.specialty, + city: t.city, + workingHours: t.working_hours, + daysOff: t.days_off, + holidays: t.holidays, + }); + } + } catch (err) { + console.error('[Config API] DB error, falling back to in-memory:', err.message); + } + // Fallback to in-memory config + res.json(getTenantConfig(req.params.tenantId)); }); -// Official Twilio Webhook parsing middleware +// ── AUTH ROUTES ─────────────────────────────────────────────────────────────── + app.use(express.urlencoded({ extended: true })); -// WhatsApp Webhook (Twilio) -app.post('/whatsapp/webhook', async (req, res) => { - // Twilio sends urlencoded fields: From, Body - // Or our mock sends json: from, body - const from = req.body.From || req.body.from; - const body = req.body.Body || req.body.body; - - console.log(`[WhatsApp Webhook] Received message from ${from}: ${body}`); - - if (!from || !body) { - return res.status(400).send("Missing from or body"); +/** + * POST /api/auth/register + * Public — submit a clinic registration for super admin approval + */ +app.post('/api/auth/register', async (req, res) => { + const { ownerName, clinicName, specialty, city, whatsappNumber, email, password, plan } = req.body; + + if (!ownerName || !clinicName || !whatsappNumber || !email || !password) { + return res.status(400).json({ error: 'Missing required fields' }); + } + if (password.length < 8) { + return res.status(400).json({ error: 'Password must be at least 8 characters' }); } + try { + // Check for duplicate email in users or pending + const dupUser = await query('SELECT id FROM users WHERE email = $1', [email]); + const dupPending = await query('SELECT id FROM pending_registrations WHERE email = $1 AND status = $2', [email, 'pending']); + if (dupUser.rows.length > 0 || dupPending.rows.length > 0) { + return res.status(409).json({ error: 'An account with this email already exists or is pending approval' }); + } + + const hash = await bcrypt.hash(password, 10); + const result = await query( + `INSERT INTO pending_registrations + (owner_name, clinic_name, specialty, city, whatsapp_number, email, password_hash, plan) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING id`, + [ownerName, clinicName, specialty || null, city || null, whatsappNumber, email, hash, plan || 'basic'] + ); + res.json({ success: true, registrationId: result.rows[0].id }); + } catch (err) { + console.error('[Register]', err); + res.status(500).json({ error: 'Registration failed' }); + } +}); + +/** + * POST /api/auth/login + * Public — authenticate user, return JWT + */ +app.post('/api/auth/login', async (req, res) => { + const { email, password } = req.body; + if (!email || !password) return res.status(400).json({ error: 'Email and password required' }); + + try { + const result = await query('SELECT * FROM users WHERE email = $1', [email]); + if (result.rows.length === 0) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + const user = result.rows[0]; + + // Check tenant status (super admin has null tenant_id — always allowed) + if (user.tenant_id) { + const tenant = await query('SELECT status FROM tenants WHERE id = $1', [user.tenant_id]); + if (tenant.rows.length === 0 || tenant.rows[0].status !== 'active') { + return res.status(403).json({ error: 'Your clinic account is suspended. Contact Curio support.' }); + } + } + + if (user.status !== 'active') { + return res.status(403).json({ error: 'Your account is inactive. Contact your clinic admin.' }); + } + + const match = await bcrypt.compare(password, user.password_hash); + if (!match) return res.status(401).json({ error: 'Invalid credentials' }); + + const token = signToken({ + userId: user.id, + email: user.email, + name: user.name, + role: user.role, + tenantId: user.tenant_id, + }); + + res.json({ success: true, token, role: user.role, tenantId: user.tenant_id, name: user.name }); + } catch (err) { + console.error('[Login]', err); + res.status(500).json({ error: 'Login failed' }); + } +}); + +/** + * GET /api/auth/me + * Returns current user info (for page load verification) + */ +app.get('/api/auth/me', requireAuth(), (req, res) => { + res.json({ user: req.user }); +}); + +// ── SUPER ADMIN ROUTES ──────────────────────────────────────────────────────── + +/** + * GET /api/super/stats + */ +app.get('/api/super/stats', requireAuth('SUPER_ADMIN'), async (req, res) => { + try { + const [clinics, pending, users] = await Promise.all([ + query("SELECT COUNT(*) FROM tenants WHERE status = 'active'"), + query("SELECT COUNT(*) FROM pending_registrations WHERE status = 'pending'"), + query("SELECT COUNT(*) FROM users WHERE role != 'SUPER_ADMIN'"), + ]); + res.json({ + activeClinics: parseInt(clinics.rows[0].count), + pendingCount: parseInt(pending.rows[0].count), + totalUsers: parseInt(users.rows[0].count), + }); + } catch (err) { + console.error('[Super Stats]', err); + res.status(500).json({ error: 'Failed to fetch stats' }); + } +}); + +/** + * GET /api/super/clinics + */ +app.get('/api/super/clinics', requireAuth('SUPER_ADMIN'), async (req, res) => { + try { + const result = await query( + `SELECT t.*, COUNT(u.id) AS user_count + FROM tenants t + LEFT JOIN users u ON u.tenant_id = t.id + GROUP BY t.id ORDER BY t.created_at DESC` + ); + res.json(result.rows); + } catch (err) { + console.error('[Super Clinics]', err); + res.status(500).json({ error: 'Failed to fetch clinics' }); + } +}); + +/** + * POST /api/super/clinics + * Direct clinic creation by super admin (bypasses pending flow) + */ +app.post('/api/super/clinics', requireAuth('SUPER_ADMIN'), async (req, res) => { + const { ownerName, ownerEmail, ownerPassword, clinicName, specialty, city, whatsappNumber, plan, + primaryColor, secondaryColor, consultationFee } = req.body; + + if (!ownerName || !ownerEmail || !ownerPassword || !clinicName) { + return res.status(400).json({ error: 'Missing required fields' }); + } + + try { + const tenantId = await uniqueSlug(clinicName); + const hash = await bcrypt.hash(ownerPassword, 10); + + await query('BEGIN'); + try { + await query( + `INSERT INTO tenants (id, name, logo, primary_color, secondary_color, consultation_fee, plan, specialty, city, whatsapp_number) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`, + [tenantId, clinicName, clinicName[0].toUpperCase(), + primaryColor || '#0F172A', secondaryColor || '#38BDF8', + consultationFee || 500, plan || 'basic', + specialty || null, city || null, whatsappNumber || null] + ); + const userResult = await query( + `INSERT INTO users (tenant_id, email, password_hash, name, role) + VALUES ($1,$2,$3,$4,'OWNER') RETURNING id`, + [tenantId, ownerEmail, hash, ownerName] + ); + await query('COMMIT'); + res.json({ success: true, tenantId, userId: userResult.rows[0].id }); + } catch (inner) { + await query('ROLLBACK'); + throw inner; + } + } catch (err) { + console.error('[Super Create Clinic]', err); + if (err.code === '23505') return res.status(409).json({ error: 'Email already in use' }); + res.status(500).json({ error: 'Failed to create clinic' }); + } +}); + +/** + * PATCH /api/super/clinics/:id/status + * Toggle active / suspended + */ +app.patch('/api/super/clinics/:id/status', requireAuth('SUPER_ADMIN'), async (req, res) => { + const { status } = req.body; + if (!['active', 'suspended'].includes(status)) { + return res.status(400).json({ error: 'Status must be active or suspended' }); + } + try { + await query('UPDATE tenants SET status = $1 WHERE id = $2', [status, req.params.id]); + res.json({ success: true }); + } catch (err) { + console.error('[Super Clinic Status]', err); + res.status(500).json({ error: 'Failed to update status' }); + } +}); + +/** + * DELETE /api/super/clinics/:id + */ +app.delete('/api/super/clinics/:id', requireAuth('SUPER_ADMIN'), async (req, res) => { + try { + await query('DELETE FROM tenants WHERE id = $1', [req.params.id]); + res.json({ success: true }); + } catch (err) { + console.error('[Super Delete Clinic]', err); + res.status(500).json({ error: 'Failed to delete clinic' }); + } +}); + +/** + * GET /api/super/clinics/:id/users + */ +app.get('/api/super/clinics/:id/users', requireAuth('SUPER_ADMIN'), async (req, res) => { + try { + const result = await query( + `SELECT id, name, email, role, status, created_at FROM users WHERE tenant_id = $1 ORDER BY created_at`, + [req.params.id] + ); + res.json(result.rows); + } catch (err) { + console.error('[Super Clinic Users]', err); + res.status(500).json({ error: 'Failed to fetch users' }); + } +}); + +/** + * POST /api/super/clinics/:id/users + * Add a user to a clinic + */ +app.post('/api/super/clinics/:id/users', requireAuth('SUPER_ADMIN'), async (req, res) => { + const { name, email, password, role } = req.body; + const validRoles = ['OWNER', 'DOCTOR', 'RECEPTIONIST', 'PHARMACIST', 'LAB_TECH']; + if (!name || !email || !password || !role) return res.status(400).json({ error: 'Missing required fields' }); + if (!validRoles.includes(role)) return res.status(400).json({ error: 'Invalid role' }); + + try { + const hash = await bcrypt.hash(password, 10); + const result = await query( + `INSERT INTO users (tenant_id, email, password_hash, name, role) VALUES ($1,$2,$3,$4,$5) RETURNING id`, + [req.params.id, email, hash, name, role] + ); + res.json({ success: true, userId: result.rows[0].id }); + } catch (err) { + if (err.code === '23505') return res.status(409).json({ error: 'Email already in use' }); + console.error('[Super Add User]', err); + res.status(500).json({ error: 'Failed to create user' }); + } +}); + +/** + * PATCH /api/super/users/:userId/status + */ +app.patch('/api/super/users/:userId/status', requireAuth('SUPER_ADMIN'), async (req, res) => { + const { status } = req.body; + if (!['active', 'inactive'].includes(status)) return res.status(400).json({ error: 'Invalid status' }); + try { + await query('UPDATE users SET status = $1 WHERE id = $2 AND role != $3', [status, req.params.userId, 'SUPER_ADMIN']); + res.json({ success: true }); + } catch (err) { + console.error('[Super User Status]', err); + res.status(500).json({ error: 'Failed to update user status' }); + } +}); + +/** + * DELETE /api/super/users/:userId + */ +app.delete('/api/super/users/:userId', requireAuth('SUPER_ADMIN'), async (req, res) => { + try { + await query('DELETE FROM users WHERE id = $1 AND role != $2', [req.params.userId, 'SUPER_ADMIN']); + res.json({ success: true }); + } catch (err) { + console.error('[Super Delete User]', err); + res.status(500).json({ error: 'Failed to delete user' }); + } +}); + +/** + * GET /api/super/pending + */ +app.get('/api/super/pending', requireAuth('SUPER_ADMIN'), async (req, res) => { + try { + const result = await query( + `SELECT id, owner_name, clinic_name, specialty, city, whatsapp_number, email, plan, status, created_at + FROM pending_registrations ORDER BY created_at DESC` + ); + res.json(result.rows); + } catch (err) { + console.error('[Super Pending]', err); + res.status(500).json({ error: 'Failed to fetch pending registrations' }); + } +}); + +/** + * POST /api/super/pending/:id/approve + * Creates tenant + owner user, marks pending as approved + */ +app.post('/api/super/pending/:id/approve', requireAuth('SUPER_ADMIN'), async (req, res) => { + try { + const result = await query('SELECT * FROM pending_registrations WHERE id = $1', [req.params.id]); + if (result.rows.length === 0) return res.status(404).json({ error: 'Registration not found' }); + + const p = result.rows[0]; + if (p.status !== 'pending') return res.status(400).json({ error: 'Already processed' }); + + const tenantId = await uniqueSlug(p.clinic_name); + + await query('BEGIN'); + try { + await query( + `INSERT INTO tenants (id, name, logo, specialty, city, whatsapp_number, plan) + VALUES ($1,$2,$3,$4,$5,$6,$7)`, + [tenantId, p.clinic_name, p.clinic_name[0].toUpperCase(), + p.specialty, p.city, p.whatsapp_number, p.plan] + ); + await query( + `INSERT INTO users (tenant_id, email, password_hash, name, role) + VALUES ($1,$2,$3,$4,'OWNER')`, + [tenantId, p.email, p.password_hash, p.owner_name] + ); + await query( + `UPDATE pending_registrations SET status = 'approved' WHERE id = $1`, + [p.id] + ); + await query('COMMIT'); + res.json({ success: true, tenantId }); + } catch (inner) { + await query('ROLLBACK'); + throw inner; + } + } catch (err) { + console.error('[Super Approve]', err); + if (err.code === '23505') return res.status(409).json({ error: 'Email already registered' }); + res.status(500).json({ error: 'Failed to approve registration' }); + } +}); + +/** + * POST /api/super/pending/:id/reject + */ +app.post('/api/super/pending/:id/reject', requireAuth('SUPER_ADMIN'), async (req, res) => { + try { + await query("UPDATE pending_registrations SET status = 'rejected' WHERE id = $1", [req.params.id]); + res.json({ success: true }); + } catch (err) { + console.error('[Super Reject]', err); + res.status(500).json({ error: 'Failed to reject registration' }); + } +}); + +// ── CLINIC OWNER ROUTES ─────────────────────────────────────────────────────── + +/** + * PATCH /api/clinic/settings — clinic owner updates own settings + */ +app.patch('/api/clinic/settings', requireAuth('OWNER'), async (req, res) => { + const { consultationFee, workingHours, daysOff, holidays, primaryColor, secondaryColor } = req.body; + const tenantId = req.user.tenantId; + + try { + const fields = []; + const values = []; + let i = 1; + + if (consultationFee !== undefined) { fields.push(`consultation_fee=$${i++}`); values.push(consultationFee); } + if (primaryColor !== undefined) { fields.push(`primary_color=$${i++}`); values.push(primaryColor); } + if (secondaryColor !== undefined) { fields.push(`secondary_color=$${i++}`); values.push(secondaryColor); } + if (workingHours !== undefined) { fields.push(`working_hours=$${i++}::jsonb`); values.push(JSON.stringify(workingHours)); } + if (daysOff !== undefined) { fields.push(`days_off=$${i++}::jsonb`); values.push(JSON.stringify(daysOff)); } + if (holidays !== undefined) { fields.push(`holidays=$${i++}::jsonb`); values.push(JSON.stringify(holidays)); } + + if (fields.length === 0) return res.status(400).json({ error: 'No fields to update' }); + + values.push(tenantId); + await query(`UPDATE tenants SET ${fields.join(',')} WHERE id=$${i}`, values); + res.json({ success: true }); + } catch (err) { + console.error('[Clinic Settings]', err); + res.status(500).json({ error: 'Failed to update settings' }); + } +}); + +// ── WHATSAPP WEBHOOK ────────────────────────────────────────────────────────── + +app.post('/whatsapp/webhook', async (req, res) => { + const from = req.body.From || req.body.from; + const body = req.body.Body || req.body.body; + + console.log(`[WhatsApp Webhook] Received message from ${from}: ${body}`); + if (!from || !body) return res.status(400).send('Missing from or body'); + try { const temporalClient = await getTemporalClient(); - // Use the phone number as the unique Workflow ID const workflowId = `booking-${from.replace(/[^a-zA-Z0-9]/g, '')}`; - + let handle; try { handle = temporalClient.workflow.getHandle(workflowId); const desc = await handle.describe(); if (desc.status.name === 'RUNNING') { - // Ongoing conversation! Send a signal with the new message - console.log(`[WhatsApp Webhook] Signaling existing workflow ${workflowId}`); await handle.signal('receive_message', body); - - // Twilio expects a valid TwiML response. Empty response means no immediate automated reply. - // Our Temporal worker will asynchronously send a reply back via Twilio API. - res.type('text/xml').send(''); - return; + return res.type('text/xml').send(''); } - } catch (err) { - // Workflow does not exist. We will start a new one below. - } + } catch (err) { /* start new */ } - console.log(`[WhatsApp Webhook] Starting new AppointmentBookingWorkflow for ${from}`); await temporalClient.workflow.start('AppointmentBookingWorkflow', { - args: [body, from], - taskQueue: 'curaflow-tasks', - workflowId: workflowId, + args: [body, from], taskQueue: 'curaflow-tasks', workflowId, }); - res.type('text/xml').send(''); } catch (err) { - console.error('[WhatsApp Webhook] Error processing message:', err); + console.error('[WhatsApp Webhook] Error:', err); res.status(500).send('Internal Server Error'); } }); -// AI Scribe Endpoints +// ── AI SCRIBE ───────────────────────────────────────────────────────────────── + app.post('/api/ai/scribe/start', async (req, res) => { try { const { dictation } = req.body; if (!dictation) return res.status(400).json({ error: 'Dictation is required' }); - const temporalClient = await getTemporalClient(); const jobId = `scribe-${crypto.randomUUID()}`; - - console.log(`[AI Scribe] Starting job ${jobId} for dictation of length ${dictation.length}`); - - // Start the workflow asynchronously await temporalClient.workflow.start('ClinicalIntakeWorkflow', { - args: [dictation], - taskQueue: 'curaflow-tasks', - workflowId: jobId, + args: [dictation], taskQueue: 'curaflow-tasks', workflowId: jobId, }); - res.json({ success: true, jobId }); } catch (err) { console.error('[AI Scribe] Start Error:', err); @@ -137,54 +538,38 @@ app.post('/api/ai/scribe/start', async (req, res) => { app.get('/api/ai/scribe/status/:jobId', async (req, res) => { try { - const { jobId } = req.params; const temporalClient = await getTemporalClient(); - - const handle = temporalClient.workflow.getHandle(jobId); + const handle = temporalClient.workflow.getHandle(req.params.jobId); const description = await handle.describe(); - - // Check if completed if (description.status.name === 'COMPLETED') { - const result = await handle.result(); - return res.json({ status: 'COMPLETED', result }); + return res.json({ status: 'COMPLETED', result: await handle.result() }); } else if (description.status.name === 'FAILED') { return res.json({ status: 'FAILED' }); } - - return res.json({ status: description.status.name }); + res.json({ status: description.status.name }); } catch (err) { - // If not found or error - console.error(`[AI Scribe] Status Error for ${req.params.jobId}:`, err); + console.error('[AI Scribe] Status Error:', err); res.status(500).json({ error: 'Failed to get job status' }); } }); -// Lab Watcher Endpoints +// ── LAB WATCHER ─────────────────────────────────────────────────────────────── + app.post('/api/ai/lab/watch/start', async (req, res) => { try { const { patientId, baselineData } = req.body; if (!patientId) return res.status(400).json({ error: 'Patient ID is required' }); - const temporalClient = await getTemporalClient(); const workflowId = `lab-watcher-${patientId}`; - - // Ensure we don't start it twice if it's already running try { const handle = temporalClient.workflow.getHandle(workflowId); const desc = await handle.describe(); - if (desc.status.name === 'RUNNING') { - return res.json({ success: true, message: 'Watcher already running' }); - } - } catch (e) { - // Not found, so we proceed to start it - } - + if (desc.status.name === 'RUNNING') return res.json({ success: true, message: 'Watcher already running' }); + } catch (e) { /* start new */ } await temporalClient.workflow.start('LabResultWatcherWorkflow', { - args: [baselineData || { baseline: "Patient is healthy", medications: [] }], - taskQueue: 'curaflow-tasks', - workflowId: workflowId, + args: [baselineData || { baseline: 'Patient is healthy', medications: [] }], + taskQueue: 'curaflow-tasks', workflowId, }); - res.json({ success: true }); } catch (err) { console.error('[AI Lab Watcher] Start Error:', err); @@ -194,42 +579,38 @@ app.post('/api/ai/lab/watch/start', async (req, res) => { app.post('/api/ai/lab/result/:patientId', async (req, res) => { try { - const { patientId } = req.params; const { result } = req.body; if (!result) return res.status(400).json({ error: 'Lab result string is required' }); - const temporalClient = await getTemporalClient(); - const workflowId = `lab-watcher-${patientId}`; - const handle = temporalClient.workflow.getHandle(workflowId); - + const handle = temporalClient.workflow.getHandle(`lab-watcher-${req.params.patientId}`); await handle.signal('add_lab_result', result); res.json({ success: true }); } catch (err) { - console.error(`[AI Lab Watcher] Signal Error for ${req.params.patientId}:`, err); + console.error('[AI Lab Watcher] Signal Error:', err); res.status(500).json({ error: 'Failed to send lab result to AI Watcher' }); } }); app.get('/api/ai/lab/alerts/:patientId', async (req, res) => { try { - const { patientId } = req.params; const temporalClient = await getTemporalClient(); - const workflowId = `lab-watcher-${patientId}`; - try { - const handle = temporalClient.workflow.getHandle(workflowId); + const handle = temporalClient.workflow.getHandle(`lab-watcher-${req.params.patientId}`); const alerts = await handle.query('get_alerts'); - res.json({ success: true, alerts }); + return res.json({ success: true, alerts }); } catch (e) { - // Workflow might not be running res.json({ success: true, alerts: [] }); } } catch (err) { - console.error(`[AI Lab Watcher] Query Error for ${req.params.patientId}:`, err); + console.error('[AI Lab Watcher] Query Error:', err); res.status(500).json({ error: 'Failed to fetch alerts' }); } }); -app.listen(port, '0.0.0.0', () => { - console.log(`Curio WhatsApp Mock Server running at http://0.0.0.0:${port}`); +// ── START ───────────────────────────────────────────────────────────────────── + +initSchema().then(() => { + app.listen(port, '0.0.0.0', () => { + console.log(`Curio HMS running at http://0.0.0.0:${port}`); + }); }); diff --git a/k8s/app.yaml b/k8s/app.yaml index ce98ef4..bb10bc0 100644 --- a/k8s/app.yaml +++ b/k8s/app.yaml @@ -13,7 +13,7 @@ spec: labels: app: curio-app annotations: - redeploy-timestamp: "2026-05-24T18:55:00" + redeploy-timestamp: "2026-05-25T21:43:00" spec: containers: - name: curio diff --git a/login.css b/login.css index 4a129b1..09c6879 100644 --- a/login.css +++ b/login.css @@ -107,3 +107,27 @@ .auth-footer a:hover { text-decoration: underline; } + +/* ── Utility ── */ +.hidden { display: none !important; } + +.auth-error { + background: #FEF2F2; + border: 1px solid #FCA5A5; + color: #DC2626; + padding: 0.75rem 1rem; + border-radius: 8px; + margin-bottom: 1.25rem; + font-size: 0.875rem; + text-align: left; +} + +.btn-spinner { font-size: 0.9rem; } + +code { + background: var(--bg); + padding: 0.1rem 0.4rem; + border-radius: 4px; + font-size: 0.82rem; + color: var(--primary); +} diff --git a/login.html b/login.html index 35629ee..a7d9de9 100644 --- a/login.html +++ b/login.html @@ -4,8 +4,8 @@ Login | Curio HMS + - @@ -21,55 +21,100 @@

Welcome Back

Login to manage your clinic OPD and accounts.

- + + +
- - + +
- - + +
- +
- + + diff --git a/node_modules/.bin/bcrypt b/node_modules/.bin/bcrypt new file mode 100644 index 0000000..88dd8ed --- /dev/null +++ b/node_modules/.bin/bcrypt @@ -0,0 +1,16 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) + if command -v cygpath > /dev/null 2>&1; then + basedir=`cygpath -w "$basedir"` + fi + ;; +esac + +if [ -x "$basedir/node" ]; then + exec "$basedir/node" "$basedir/../bcryptjs/bin/bcrypt" "$@" +else + exec node "$basedir/../bcryptjs/bin/bcrypt" "$@" +fi diff --git a/node_modules/.bin/bcrypt.cmd b/node_modules/.bin/bcrypt.cmd new file mode 100644 index 0000000..762efef --- /dev/null +++ b/node_modules/.bin/bcrypt.cmd @@ -0,0 +1,17 @@ +@ECHO off +GOTO start +:find_dp0 +SET dp0=%~dp0 +EXIT /b +:start +SETLOCAL +CALL :find_dp0 + +IF EXIST "%dp0%\node.exe" ( + SET "_prog=%dp0%\node.exe" +) ELSE ( + SET "_prog=node" + SET PATHEXT=%PATHEXT:;.JS;=;% +) + +endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\bcryptjs\bin\bcrypt" %* diff --git a/node_modules/.bin/bcrypt.ps1 b/node_modules/.bin/bcrypt.ps1 new file mode 100644 index 0000000..fbe0a31 --- /dev/null +++ b/node_modules/.bin/bcrypt.ps1 @@ -0,0 +1,28 @@ +#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +} +$ret=0 +if (Test-Path "$basedir/node$exe") { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "$basedir/node$exe" "$basedir/../bcryptjs/bin/bcrypt" $args + } else { + & "$basedir/node$exe" "$basedir/../bcryptjs/bin/bcrypt" $args + } + $ret=$LASTEXITCODE +} else { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "node$exe" "$basedir/../bcryptjs/bin/bcrypt" $args + } else { + & "node$exe" "$basedir/../bcryptjs/bin/bcrypt" $args + } + $ret=$LASTEXITCODE +} +exit $ret diff --git a/node_modules/.bin/semver b/node_modules/.bin/semver new file mode 100644 index 0000000..97c5327 --- /dev/null +++ b/node_modules/.bin/semver @@ -0,0 +1,16 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) + if command -v cygpath > /dev/null 2>&1; then + basedir=`cygpath -w "$basedir"` + fi + ;; +esac + +if [ -x "$basedir/node" ]; then + exec "$basedir/node" "$basedir/../semver/bin/semver.js" "$@" +else + exec node "$basedir/../semver/bin/semver.js" "$@" +fi diff --git a/node_modules/.bin/semver.cmd b/node_modules/.bin/semver.cmd new file mode 100644 index 0000000..9913fa9 --- /dev/null +++ b/node_modules/.bin/semver.cmd @@ -0,0 +1,17 @@ +@ECHO off +GOTO start +:find_dp0 +SET dp0=%~dp0 +EXIT /b +:start +SETLOCAL +CALL :find_dp0 + +IF EXIST "%dp0%\node.exe" ( + SET "_prog=%dp0%\node.exe" +) ELSE ( + SET "_prog=node" + SET PATHEXT=%PATHEXT:;.JS;=;% +) + +endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\semver\bin\semver.js" %* diff --git a/node_modules/.bin/semver.ps1 b/node_modules/.bin/semver.ps1 new file mode 100644 index 0000000..314717a --- /dev/null +++ b/node_modules/.bin/semver.ps1 @@ -0,0 +1,28 @@ +#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +} +$ret=0 +if (Test-Path "$basedir/node$exe") { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "$basedir/node$exe" "$basedir/../semver/bin/semver.js" $args + } else { + & "$basedir/node$exe" "$basedir/../semver/bin/semver.js" $args + } + $ret=$LASTEXITCODE +} else { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & "node$exe" "$basedir/../semver/bin/semver.js" $args + } else { + & "node$exe" "$basedir/../semver/bin/semver.js" $args + } + $ret=$LASTEXITCODE +} +exit $ret diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index ab23f9e..1c903bc 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -231,6 +231,15 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, "node_modules/body-parser": { "version": "1.20.5", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", @@ -255,6 +264,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -409,6 +424,15 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -722,12 +746,103 @@ "node": ">=8" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", @@ -869,6 +984,135 @@ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, + "node_modules/pg": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", + "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", + "license": "MIT", + "peer": true, + "dependencies": { + "pg-connection-string": "^2.13.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.14.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", + "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", + "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/proto3-json-serializer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", @@ -992,6 +1236,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", @@ -1115,6 +1371,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -1235,6 +1500,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/node_modules/bcryptjs/LICENSE b/node_modules/bcryptjs/LICENSE new file mode 100644 index 0000000..6ffc6d9 --- /dev/null +++ b/node_modules/bcryptjs/LICENSE @@ -0,0 +1,27 @@ +bcrypt.js +--------- +Copyright (c) 2012 Nevins Bartolomeo +Copyright (c) 2012 Shane Girish +Copyright (c) 2025 Daniel Wirtz + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/node_modules/bcryptjs/README.md b/node_modules/bcryptjs/README.md new file mode 100644 index 0000000..15546c4 --- /dev/null +++ b/node_modules/bcryptjs/README.md @@ -0,0 +1,201 @@ +# bcrypt.js + +Optimized bcrypt in JavaScript with zero dependencies, with TypeScript support. Compatible to the C++ +[bcrypt](https://npmjs.org/package/bcrypt) binding on Node.js and also working in the browser. + +[![Build Status](https://img.shields.io/github/actions/workflow/status/dcodeIO/bcrypt.js/test.yml?branch=main&label=test&logo=github)](https://github.com/dcodeIO/bcrypt.js/actions/workflows/test.yml) [![Publish Status](https://img.shields.io/github/actions/workflow/status/dcodeIO/bcrypt.js/publish.yml?branch=main&label=publish&logo=github)](https://github.com/dcodeIO/bcrypt.js/actions/workflows/publish.yml) [![npm](https://img.shields.io/npm/v/bcryptjs.svg?label=npm&color=007acc&logo=npm)](https://www.npmjs.com/package/bcryptjs) + +## Security considerations + +Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the +iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with +increasing computation power. ([see](http://en.wikipedia.org/wiki/Bcrypt)) + +While bcrypt.js is compatible to the C++ bcrypt binding, it is written in pure JavaScript and thus slower ([about 30%](https://github.com/dcodeIO/bcrypt.js/wiki/Benchmark)), effectively reducing the number of iterations that can be +processed in an equal time span. + +The maximum input length is 72 bytes (note that UTF-8 encoded characters use up to 4 bytes) and the length of generated +hashes is 60 characters. Note that maximum input length is not implicitly checked by the library for compatibility with +the C++ binding on Node.js, but should be checked with `bcrypt.truncates(password)` where necessary. + +## Usage + +The package exports an ECMAScript module with an UMD fallback. + +``` +$> npm install bcryptjs +``` + +```ts +import bcrypt from "bcryptjs"; +``` + +### Usage with a CDN + +- From GitHub via [jsDelivr](https://www.jsdelivr.com):
+ `https://cdn.jsdelivr.net/gh/dcodeIO/bcrypt.js@TAG/index.js` (ESM) +- From npm via [jsDelivr](https://www.jsdelivr.com):
+ `https://cdn.jsdelivr.net/npm/bcryptjs@VERSION/index.js` (ESM)
+ `https://cdn.jsdelivr.net/npm/bcryptjs@VERSION/umd/index.js` (UMD) +- From npm via [unpkg](https://unpkg.com):
+ `https://unpkg.com/bcryptjs@VERSION/index.js` (ESM)
+ `https://unpkg.com/bcryptjs@VERSION/umd/index.js` (UMD) + +Replace `TAG` respectively `VERSION` with a [specific version](https://github.com/dcodeIO/bcrypt.js/releases) or omit it (not recommended in production) to use latest. + +When using the ESM variant in a browser, the `crypto` import needs to be stubbed out, for example using an [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap). Bundlers should omit it automatically. + +### Usage - Sync + +To hash a password: + +```ts +const salt = bcrypt.genSaltSync(10); +const hash = bcrypt.hashSync("B4c0/\/", salt); +// Store hash in your password DB +``` + +To check a password: + +```ts +// Load hash from your password DB +bcrypt.compareSync("B4c0/\/", hash); // true +bcrypt.compareSync("not_bacon", hash); // false +``` + +Auto-gen a salt and hash: + +```ts +const hash = bcrypt.hashSync("bacon", 10); +``` + +### Usage - Async + +To hash a password: + +```ts +const salt = await bcrypt.genSalt(10); +const hash = await bcrypt.hash("B4c0/\/", salt); +// Store hash in your password DB +``` + +```ts +bcrypt.genSalt(10, (err, salt) => { + bcrypt.hash("B4c0/\/", salt, function (err, hash) { + // Store hash in your password DB + }); +}); +``` + +To check a password: + +```ts +// Load hash from your password DB +await bcrypt.compare("B4c0/\/", hash); // true +await bcrypt.compare("not_bacon", hash); // false +``` + +```ts +// Load hash from your password DB +bcrypt.compare("B4c0/\/", hash, (err, res) => { + // res === true +}); +bcrypt.compare("not_bacon", hash, (err, res) => { + // res === false +}); +``` + +Auto-gen a salt and hash: + +```ts +await bcrypt.hash("B4c0/\/", 10); +// Store hash in your password DB +``` + +```ts +bcrypt.hash("B4c0/\/", 10, (err, hash) => { + // Store hash in your password DB +}); +``` + +**Note:** Under the hood, asynchronous APIs split an operation into small chunks. After the completion of a chunk, the execution of the next chunk is placed on the back of the [JS event queue](https://developer.mozilla.org/en/docs/Web/JavaScript/EventLoop), efficiently yielding for other computation to execute. + +### Usage - Command Line + +``` +Usage: bcrypt [rounds|salt] +``` + +## API + +### Callback types + +- **Callback<`T`>**: `(err: Error | null, result?: T) => void`
+ Called with an error on failure or a value of type `T` upon success. + +- **ProgressCallback**: `(percentage: number) => void`
+ Called with the percentage of rounds completed (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms. + +- **RandomFallback**: `(length: number) => number[]`
+ Called to obtain random bytes when both [Web Crypto API](http://www.w3.org/TR/WebCryptoAPI/) and Node.js + [crypto](http://nodejs.org/api/crypto.html) are not available. + +### Functions + +- bcrypt.**genSaltSync**(rounds?: `number`): `string`
+ Synchronously generates a salt. Number of rounds defaults to 10 when omitted. + +- bcrypt.**genSalt**(rounds?: `number`): `Promise`
+ Asynchronously generates a salt. Number of rounds defaults to 10 when omitted. + +- bcrypt.**genSalt**([rounds: `number`, ]callback: `Callback`): `void`
+ Asynchronously generates a salt. Number of rounds defaults to 10 when omitted. + +- bcrypt.**truncates**(password: `string`): `boolean`
+ Tests if a password will be truncated when hashed, that is its length is greater than 72 bytes when converted to UTF-8. + +- bcrypt.**hashSync**(password: `string`, salt?: `number | string`): `string` + Synchronously generates a hash for the given password. Number of rounds defaults to 10 when omitted. + +- bcrypt.**hash**(password: `string`, salt: `number | string`): `Promise`
+ Asynchronously generates a hash for the given password. + +- bcrypt.**hash**(password: `string`, salt: `number | string`, callback: `Callback`, progressCallback?: `ProgressCallback`): `void`
+ Asynchronously generates a hash for the given password. + +- bcrypt.**compareSync**(password: `string`, hash: `string`): `boolean`
+ Synchronously tests a password against a hash. + +- bcrypt.**compare**(password: `string`, hash: `string`): `Promise`
+ Asynchronously compares a password against a hash. + +- bcrypt.**compare**(password: `string`, hash: `string`, callback: `Callback`, progressCallback?: `ProgressCallback`)
+ Asynchronously compares a password against a hash. + +- bcrypt.**getRounds**(hash: `string`): `number`
+ Gets the number of rounds used to encrypt the specified hash. + +- bcrypt.**getSalt**(hash: `string`): `string`
+ Gets the salt portion from a hash. Does not validate the hash. + +- bcrypt.**setRandomFallback**(random: `RandomFallback`): `void`
+ Sets the pseudo random number generator to use as a fallback if neither [Web Crypto API](http://www.w3.org/TR/WebCryptoAPI/) nor Node.js [crypto](http://nodejs.org/api/crypto.html) are available. Please note: It is highly important that the PRNG used is cryptographically secure and that it is seeded properly! + +## Building + +Building the UMD fallback: + +``` +$> npm run build +``` + +Running the [tests](./tests): + +``` +$> npm test +``` + +## Credits + +Based on work started by Shane Girish at [bcrypt-nodejs](https://github.com/shaneGirish/bcrypt-nodejs), which is itself +based on [javascript-bcrypt](http://code.google.com/p/javascript-bcrypt/) (New BSD-licensed). diff --git a/node_modules/bcryptjs/bin/bcrypt b/node_modules/bcryptjs/bin/bcrypt new file mode 100644 index 0000000..5c72e0f --- /dev/null +++ b/node_modules/bcryptjs/bin/bcrypt @@ -0,0 +1,23 @@ +#!/usr/bin/env node + +import path from "node:path"; +import bcrypt from "../index.js"; + +if (process.argv.length < 3) { + console.log( + "Usage: " + path.basename(process.argv[1]) + " [rounds|salt]", + ); + process.exit(1); +} else { + var salt; + if (process.argv.length > 3) { + salt = process.argv[3]; + var rounds = parseInt(salt, 10); + if (rounds == salt) { + salt = bcrypt.genSaltSync(rounds); + } + } else { + salt = bcrypt.genSaltSync(); + } + console.log(bcrypt.hashSync(process.argv[2], salt)); +} diff --git a/node_modules/bcryptjs/index.d.ts b/node_modules/bcryptjs/index.d.ts new file mode 100644 index 0000000..3ae838f --- /dev/null +++ b/node_modules/bcryptjs/index.d.ts @@ -0,0 +1,3 @@ +import * as bcrypt from "./types.js"; +export * from "./types.js"; +export default bcrypt; diff --git a/node_modules/bcryptjs/index.js b/node_modules/bcryptjs/index.js new file mode 100644 index 0000000..4a19b3d --- /dev/null +++ b/node_modules/bcryptjs/index.js @@ -0,0 +1,1159 @@ +/* + Copyright (c) 2012 Nevins Bartolomeo + Copyright (c) 2012 Shane Girish + Copyright (c) 2025 Daniel Wirtz + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// The Node.js crypto module is used as a fallback for the Web Crypto API. When +// building for the browser, inclusion of the crypto module should be disabled, +// which the package hints at in its package.json for bundlers that support it. +import nodeCrypto from "crypto"; + +/** + * The random implementation to use as a fallback. + * @type {?function(number):!Array.} + * @inner + */ +var randomFallback = null; + +/** + * Generates cryptographically secure random bytes. + * @function + * @param {number} len Bytes length + * @returns {!Array.} Random bytes + * @throws {Error} If no random implementation is available + * @inner + */ +function randomBytes(len) { + // Web Crypto API. Globally available in the browser and in Node.js >=23. + try { + return crypto.getRandomValues(new Uint8Array(len)); + } catch {} + // Node.js crypto module for non-browser environments. + try { + return nodeCrypto.randomBytes(len); + } catch {} + // Custom fallback specified with `setRandomFallback`. + if (!randomFallback) { + throw Error( + "Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative", + ); + } + return randomFallback(len); +} + +/** + * Sets the pseudo random number generator to use as a fallback if neither node's `crypto` module nor the Web Crypto + * API is available. Please note: It is highly important that the PRNG used is cryptographically secure and that it + * is seeded properly! + * @param {?function(number):!Array.} random Function taking the number of bytes to generate as its + * sole argument, returning the corresponding array of cryptographically secure random byte values. + * @see http://nodejs.org/api/crypto.html + * @see http://www.w3.org/TR/WebCryptoAPI/ + */ +export function setRandomFallback(random) { + randomFallback = random; +} + +/** + * Synchronously generates a salt. + * @param {number=} rounds Number of rounds to use, defaults to 10 if omitted + * @param {number=} seed_length Not supported. + * @returns {string} Resulting salt + * @throws {Error} If a random fallback is required but not set + */ +export function genSaltSync(rounds, seed_length) { + rounds = rounds || GENSALT_DEFAULT_LOG2_ROUNDS; + if (typeof rounds !== "number") + throw Error( + "Illegal arguments: " + typeof rounds + ", " + typeof seed_length, + ); + if (rounds < 4) rounds = 4; + else if (rounds > 31) rounds = 31; + var salt = []; + salt.push("$2b$"); + if (rounds < 10) salt.push("0"); + salt.push(rounds.toString()); + salt.push("$"); + salt.push(base64_encode(randomBytes(BCRYPT_SALT_LEN), BCRYPT_SALT_LEN)); // May throw + return salt.join(""); +} + +/** + * Asynchronously generates a salt. + * @param {(number|function(Error, string=))=} rounds Number of rounds to use, defaults to 10 if omitted + * @param {(number|function(Error, string=))=} seed_length Not supported. + * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting salt + * @returns {!Promise} If `callback` has been omitted + * @throws {Error} If `callback` is present but not a function + */ +export function genSalt(rounds, seed_length, callback) { + if (typeof seed_length === "function") + (callback = seed_length), (seed_length = undefined); // Not supported. + if (typeof rounds === "function") (callback = rounds), (rounds = undefined); + if (typeof rounds === "undefined") rounds = GENSALT_DEFAULT_LOG2_ROUNDS; + else if (typeof rounds !== "number") + throw Error("illegal arguments: " + typeof rounds); + + function _async(callback) { + nextTick(function () { + // Pretty thin, but salting is fast enough + try { + callback(null, genSaltSync(rounds)); + } catch (err) { + callback(err); + } + }); + } + + if (callback) { + if (typeof callback !== "function") + throw Error("Illegal callback: " + typeof callback); + _async(callback); + } else + return new Promise(function (resolve, reject) { + _async(function (err, res) { + if (err) { + reject(err); + return; + } + resolve(res); + }); + }); +} + +/** + * Synchronously generates a hash for the given password. + * @param {string} password Password to hash + * @param {(number|string)=} salt Salt length to generate or salt to use, default to 10 + * @returns {string} Resulting hash + */ +export function hashSync(password, salt) { + if (typeof salt === "undefined") salt = GENSALT_DEFAULT_LOG2_ROUNDS; + if (typeof salt === "number") salt = genSaltSync(salt); + if (typeof password !== "string" || typeof salt !== "string") + throw Error("Illegal arguments: " + typeof password + ", " + typeof salt); + return _hash(password, salt); +} + +/** + * Asynchronously generates a hash for the given password. + * @param {string} password Password to hash + * @param {number|string} salt Salt length to generate or salt to use + * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash + * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed + * (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms. + * @returns {!Promise} If `callback` has been omitted + * @throws {Error} If `callback` is present but not a function + */ +export function hash(password, salt, callback, progressCallback) { + function _async(callback) { + if (typeof password === "string" && typeof salt === "number") + genSalt(salt, function (err, salt) { + _hash(password, salt, callback, progressCallback); + }); + else if (typeof password === "string" && typeof salt === "string") + _hash(password, salt, callback, progressCallback); + else + nextTick( + callback.bind( + this, + Error("Illegal arguments: " + typeof password + ", " + typeof salt), + ), + ); + } + + if (callback) { + if (typeof callback !== "function") + throw Error("Illegal callback: " + typeof callback); + _async(callback); + } else + return new Promise(function (resolve, reject) { + _async(function (err, res) { + if (err) { + reject(err); + return; + } + resolve(res); + }); + }); +} + +/** + * Compares two strings of the same length in constant time. + * @param {string} known Must be of the correct length + * @param {string} unknown Must be the same length as `known` + * @returns {boolean} + * @inner + */ +function safeStringCompare(known, unknown) { + var diff = known.length ^ unknown.length; + for (var i = 0; i < known.length; ++i) { + diff |= known.charCodeAt(i) ^ unknown.charCodeAt(i); + } + return diff === 0; +} + +/** + * Synchronously tests a password against a hash. + * @param {string} password Password to compare + * @param {string} hash Hash to test against + * @returns {boolean} true if matching, otherwise false + * @throws {Error} If an argument is illegal + */ +export function compareSync(password, hash) { + if (typeof password !== "string" || typeof hash !== "string") + throw Error("Illegal arguments: " + typeof password + ", " + typeof hash); + if (hash.length !== 60) return false; + return safeStringCompare( + hashSync(password, hash.substring(0, hash.length - 31)), + hash, + ); +} + +/** + * Asynchronously tests a password against a hash. + * @param {string} password Password to compare + * @param {string} hashValue Hash to test against + * @param {function(Error, boolean)=} callback Callback receiving the error, if any, otherwise the result + * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed + * (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms. + * @returns {!Promise} If `callback` has been omitted + * @throws {Error} If `callback` is present but not a function + */ +export function compare(password, hashValue, callback, progressCallback) { + function _async(callback) { + if (typeof password !== "string" || typeof hashValue !== "string") { + nextTick( + callback.bind( + this, + Error( + "Illegal arguments: " + typeof password + ", " + typeof hashValue, + ), + ), + ); + return; + } + if (hashValue.length !== 60) { + nextTick(callback.bind(this, null, false)); + return; + } + hash( + password, + hashValue.substring(0, 29), + function (err, comp) { + if (err) callback(err); + else callback(null, safeStringCompare(comp, hashValue)); + }, + progressCallback, + ); + } + + if (callback) { + if (typeof callback !== "function") + throw Error("Illegal callback: " + typeof callback); + _async(callback); + } else + return new Promise(function (resolve, reject) { + _async(function (err, res) { + if (err) { + reject(err); + return; + } + resolve(res); + }); + }); +} + +/** + * Gets the number of rounds used to encrypt the specified hash. + * @param {string} hash Hash to extract the used number of rounds from + * @returns {number} Number of rounds used + * @throws {Error} If `hash` is not a string + */ +export function getRounds(hash) { + if (typeof hash !== "string") + throw Error("Illegal arguments: " + typeof hash); + return parseInt(hash.split("$")[2], 10); +} + +/** + * Gets the salt portion from a hash. Does not validate the hash. + * @param {string} hash Hash to extract the salt from + * @returns {string} Extracted salt part + * @throws {Error} If `hash` is not a string or otherwise invalid + */ +export function getSalt(hash) { + if (typeof hash !== "string") + throw Error("Illegal arguments: " + typeof hash); + if (hash.length !== 60) + throw Error("Illegal hash length: " + hash.length + " != 60"); + return hash.substring(0, 29); +} + +/** + * Tests if a password will be truncated when hashed, that is its length is + * greater than 72 bytes when converted to UTF-8. + * @param {string} password The password to test + * @returns {boolean} `true` if truncated, otherwise `false` + */ +export function truncates(password) { + if (typeof password !== "string") + throw Error("Illegal arguments: " + typeof password); + return utf8Length(password) > 72; +} + +/** + * Continues with the callback after yielding to the event loop. + * @function + * @param {function(...[*])} callback Callback to execute + * @inner + */ +var nextTick = + typeof setImmediate === "function" + ? setImmediate + : typeof scheduler === "object" && typeof scheduler.postTask === "function" + ? scheduler.postTask.bind(scheduler) + : setTimeout; + +/** Calculates the byte length of a string encoded as UTF8. */ +function utf8Length(string) { + var len = 0, + c = 0; + for (var i = 0; i < string.length; ++i) { + c = string.charCodeAt(i); + if (c < 128) len += 1; + else if (c < 2048) len += 2; + else if ( + (c & 0xfc00) === 0xd800 && + (string.charCodeAt(i + 1) & 0xfc00) === 0xdc00 + ) { + ++i; + len += 4; + } else len += 3; + } + return len; +} + +/** Converts a string to an array of UTF8 bytes. */ +function utf8Array(string) { + var offset = 0, + c1, + c2; + var buffer = new Array(utf8Length(string)); + for (var i = 0, k = string.length; i < k; ++i) { + c1 = string.charCodeAt(i); + if (c1 < 128) { + buffer[offset++] = c1; + } else if (c1 < 2048) { + buffer[offset++] = (c1 >> 6) | 192; + buffer[offset++] = (c1 & 63) | 128; + } else if ( + (c1 & 0xfc00) === 0xd800 && + ((c2 = string.charCodeAt(i + 1)) & 0xfc00) === 0xdc00 + ) { + c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff); + ++i; + buffer[offset++] = (c1 >> 18) | 240; + buffer[offset++] = ((c1 >> 12) & 63) | 128; + buffer[offset++] = ((c1 >> 6) & 63) | 128; + buffer[offset++] = (c1 & 63) | 128; + } else { + buffer[offset++] = (c1 >> 12) | 224; + buffer[offset++] = ((c1 >> 6) & 63) | 128; + buffer[offset++] = (c1 & 63) | 128; + } + } + return buffer; +} + +// A base64 implementation for the bcrypt algorithm. This is partly non-standard. + +/** + * bcrypt's own non-standard base64 dictionary. + * @type {!Array.} + * @const + * @inner + **/ +var BASE64_CODE = + "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split(""); + +/** + * @type {!Array.} + * @const + * @inner + **/ +var BASE64_INDEX = [ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1, +]; + +/** + * Encodes a byte array to base64 with up to len bytes of input. + * @param {!Array.} b Byte array + * @param {number} len Maximum input length + * @returns {string} + * @inner + */ +function base64_encode(b, len) { + var off = 0, + rs = [], + c1, + c2; + if (len <= 0 || len > b.length) throw Error("Illegal len: " + len); + while (off < len) { + c1 = b[off++] & 0xff; + rs.push(BASE64_CODE[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= len) { + rs.push(BASE64_CODE[c1 & 0x3f]); + break; + } + c2 = b[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.push(BASE64_CODE[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= len) { + rs.push(BASE64_CODE[c1 & 0x3f]); + break; + } + c2 = b[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.push(BASE64_CODE[c1 & 0x3f]); + rs.push(BASE64_CODE[c2 & 0x3f]); + } + return rs.join(""); +} + +/** + * Decodes a base64 encoded string to up to len bytes of output. + * @param {string} s String to decode + * @param {number} len Maximum output length + * @returns {!Array.} + * @inner + */ +function base64_decode(s, len) { + var off = 0, + slen = s.length, + olen = 0, + rs = [], + c1, + c2, + c3, + c4, + o, + code; + if (len <= 0) throw Error("Illegal len: " + len); + while (off < slen - 1 && olen < len) { + code = s.charCodeAt(off++); + c1 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1; + code = s.charCodeAt(off++); + c2 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1; + if (c1 == -1 || c2 == -1) break; + o = (c1 << 2) >>> 0; + o |= (c2 & 0x30) >> 4; + rs.push(String.fromCharCode(o)); + if (++olen >= len || off >= slen) break; + code = s.charCodeAt(off++); + c3 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1; + if (c3 == -1) break; + o = ((c2 & 0x0f) << 4) >>> 0; + o |= (c3 & 0x3c) >> 2; + rs.push(String.fromCharCode(o)); + if (++olen >= len || off >= slen) break; + code = s.charCodeAt(off++); + c4 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1; + o = ((c3 & 0x03) << 6) >>> 0; + o |= c4; + rs.push(String.fromCharCode(o)); + ++olen; + } + var res = []; + for (off = 0; off < olen; off++) res.push(rs[off].charCodeAt(0)); + return res; +} + +/** + * @type {number} + * @const + * @inner + */ +var BCRYPT_SALT_LEN = 16; + +/** + * @type {number} + * @const + * @inner + */ +var GENSALT_DEFAULT_LOG2_ROUNDS = 10; + +/** + * @type {number} + * @const + * @inner + */ +var BLOWFISH_NUM_ROUNDS = 16; + +/** + * @type {number} + * @const + * @inner + */ +var MAX_EXECUTION_TIME = 100; + +/** + * @type {Array.} + * @const + * @inner + */ +var P_ORIG = [ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, +]; + +/** + * @type {Array.} + * @const + * @inner + */ +var S_ORIG = [ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, + 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, + 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, + 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, + 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, + 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, + 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, + 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, + 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, + 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, + 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, + 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, + 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, + 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, + 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, + 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, + 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, + 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, + 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, + 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, + 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, + 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944, + 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, + 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, + 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, + 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, + 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, + 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, + 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, + 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, + 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, + 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, + 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, + 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, + 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, + 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, + 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, + 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, + 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, + 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, + 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, + 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, + 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, + 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, + 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, + 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, + 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, + 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, + 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, + 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, + 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, + 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, + 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, + 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, + 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, + 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, + 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, + 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, + 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, + 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, + 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, + 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, + 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, + 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, + 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, + 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, + 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, + 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, + 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, + 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, + 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, + 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, + 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, + 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, + 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, + 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, + 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, + 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, + 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, + 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, + 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, + 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, + 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, + 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, + 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, + 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +]; + +/** + * @type {Array.} + * @const + * @inner + */ +var C_ORIG = [ + 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274, +]; + +/** + * @param {Array.} lr + * @param {number} off + * @param {Array.} P + * @param {Array.} S + * @returns {Array.} + * @inner + */ +function _encipher(lr, off, P, S) { + // This is our bottleneck: 1714/1905 ticks / 90% - see profile.txt + var n, + l = lr[off], + r = lr[off + 1]; + + l ^= P[0]; + + /* + for (var i=0, k=BLOWFISH_NUM_ROUNDS-2; i<=k;) + // Feistel substitution on left word + n = S[l >>> 24], + n += S[0x100 | ((l >> 16) & 0xff)], + n ^= S[0x200 | ((l >> 8) & 0xff)], + n += S[0x300 | (l & 0xff)], + r ^= n ^ P[++i], + // Feistel substitution on right word + n = S[r >>> 24], + n += S[0x100 | ((r >> 16) & 0xff)], + n ^= S[0x200 | ((r >> 8) & 0xff)], + n += S[0x300 | (r & 0xff)], + l ^= n ^ P[++i]; + */ + + //The following is an unrolled version of the above loop. + //Iteration 0 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[1]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[2]; + //Iteration 1 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[3]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[4]; + //Iteration 2 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[5]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[6]; + //Iteration 3 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[7]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[8]; + //Iteration 4 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[9]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[10]; + //Iteration 5 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[11]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[12]; + //Iteration 6 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[13]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[14]; + //Iteration 7 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[15]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[16]; + + lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; + lr[off + 1] = l; + return lr; +} + +/** + * @param {Array.} data + * @param {number} offp + * @returns {{key: number, offp: number}} + * @inner + */ +function _streamtoword(data, offp) { + for (var i = 0, word = 0; i < 4; ++i) + (word = (word << 8) | (data[offp] & 0xff)), + (offp = (offp + 1) % data.length); + return { key: word, offp: offp }; +} + +/** + * @param {Array.} key + * @param {Array.} P + * @param {Array.} S + * @inner + */ +function _key(key, P, S) { + var offset = 0, + lr = [0, 0], + plen = P.length, + slen = S.length, + sw; + for (var i = 0; i < plen; i++) + (sw = _streamtoword(key, offset)), + (offset = sw.offp), + (P[i] = P[i] ^ sw.key); + for (i = 0; i < plen; i += 2) + (lr = _encipher(lr, 0, P, S)), (P[i] = lr[0]), (P[i + 1] = lr[1]); + for (i = 0; i < slen; i += 2) + (lr = _encipher(lr, 0, P, S)), (S[i] = lr[0]), (S[i + 1] = lr[1]); +} + +/** + * Expensive key schedule Blowfish. + * @param {Array.} data + * @param {Array.} key + * @param {Array.} P + * @param {Array.} S + * @inner + */ +function _ekskey(data, key, P, S) { + var offp = 0, + lr = [0, 0], + plen = P.length, + slen = S.length, + sw; + for (var i = 0; i < plen; i++) + (sw = _streamtoword(key, offp)), (offp = sw.offp), (P[i] = P[i] ^ sw.key); + offp = 0; + for (i = 0; i < plen; i += 2) + (sw = _streamtoword(data, offp)), + (offp = sw.offp), + (lr[0] ^= sw.key), + (sw = _streamtoword(data, offp)), + (offp = sw.offp), + (lr[1] ^= sw.key), + (lr = _encipher(lr, 0, P, S)), + (P[i] = lr[0]), + (P[i + 1] = lr[1]); + for (i = 0; i < slen; i += 2) + (sw = _streamtoword(data, offp)), + (offp = sw.offp), + (lr[0] ^= sw.key), + (sw = _streamtoword(data, offp)), + (offp = sw.offp), + (lr[1] ^= sw.key), + (lr = _encipher(lr, 0, P, S)), + (S[i] = lr[0]), + (S[i + 1] = lr[1]); +} + +/** + * Internaly crypts a string. + * @param {Array.} b Bytes to crypt + * @param {Array.} salt Salt bytes to use + * @param {number} rounds Number of rounds + * @param {function(Error, Array.=)=} callback Callback receiving the error, if any, and the resulting bytes. If + * omitted, the operation will be performed synchronously. + * @param {function(number)=} progressCallback Callback called with the current progress + * @returns {!Array.|undefined} Resulting bytes if callback has been omitted, otherwise `undefined` + * @inner + */ +function _crypt(b, salt, rounds, callback, progressCallback) { + var cdata = C_ORIG.slice(), + clen = cdata.length, + err; + + // Validate + if (rounds < 4 || rounds > 31) { + err = Error("Illegal number of rounds (4-31): " + rounds); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + if (salt.length !== BCRYPT_SALT_LEN) { + err = Error( + "Illegal salt length: " + salt.length + " != " + BCRYPT_SALT_LEN, + ); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + rounds = (1 << rounds) >>> 0; + + var P, + S, + i = 0, + j; + + //Use typed arrays when available - huge speedup! + if (typeof Int32Array === "function") { + P = new Int32Array(P_ORIG); + S = new Int32Array(S_ORIG); + } else { + P = P_ORIG.slice(); + S = S_ORIG.slice(); + } + + _ekskey(salt, b, P, S); + + /** + * Calcualtes the next round. + * @returns {Array.|undefined} Resulting array if callback has been omitted, otherwise `undefined` + * @inner + */ + function next() { + if (progressCallback) progressCallback(i / rounds); + if (i < rounds) { + var start = Date.now(); + for (; i < rounds; ) { + i = i + 1; + _key(b, P, S); + _key(salt, P, S); + if (Date.now() - start > MAX_EXECUTION_TIME) break; + } + } else { + for (i = 0; i < 64; i++) + for (j = 0; j < clen >> 1; j++) _encipher(cdata, j << 1, P, S); + var ret = []; + for (i = 0; i < clen; i++) + ret.push(((cdata[i] >> 24) & 0xff) >>> 0), + ret.push(((cdata[i] >> 16) & 0xff) >>> 0), + ret.push(((cdata[i] >> 8) & 0xff) >>> 0), + ret.push((cdata[i] & 0xff) >>> 0); + if (callback) { + callback(null, ret); + return; + } else return ret; + } + if (callback) nextTick(next); + } + + // Async + if (typeof callback !== "undefined") { + next(); + + // Sync + } else { + var res; + while (true) if (typeof (res = next()) !== "undefined") return res || []; + } +} + +/** + * Internally hashes a password. + * @param {string} password Password to hash + * @param {?string} salt Salt to use, actually never null + * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash. If omitted, + * hashing is performed synchronously. + * @param {function(number)=} progressCallback Callback called with the current progress + * @returns {string|undefined} Resulting hash if callback has been omitted, otherwise `undefined` + * @inner + */ +function _hash(password, salt, callback, progressCallback) { + var err; + if (typeof password !== "string" || typeof salt !== "string") { + err = Error("Invalid string / salt: Not a string"); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + + // Validate the salt + var minor, offset; + if (salt.charAt(0) !== "$" || salt.charAt(1) !== "2") { + err = Error("Invalid salt version: " + salt.substring(0, 2)); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + if (salt.charAt(2) === "$") (minor = String.fromCharCode(0)), (offset = 3); + else { + minor = salt.charAt(2); + if ( + (minor !== "a" && minor !== "b" && minor !== "y") || + salt.charAt(3) !== "$" + ) { + err = Error("Invalid salt revision: " + salt.substring(2, 4)); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + offset = 4; + } + + // Extract number of rounds + if (salt.charAt(offset + 2) > "$") { + err = Error("Missing salt rounds"); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + var r1 = parseInt(salt.substring(offset, offset + 1), 10) * 10, + r2 = parseInt(salt.substring(offset + 1, offset + 2), 10), + rounds = r1 + r2, + real_salt = salt.substring(offset + 3, offset + 25); + password += minor >= "a" ? "\x00" : ""; + + var passwordb = utf8Array(password), + saltb = base64_decode(real_salt, BCRYPT_SALT_LEN); + + /** + * Finishes hashing. + * @param {Array.} bytes Byte array + * @returns {string} + * @inner + */ + function finish(bytes) { + var res = []; + res.push("$2"); + if (minor >= "a") res.push(minor); + res.push("$"); + if (rounds < 10) res.push("0"); + res.push(rounds.toString()); + res.push("$"); + res.push(base64_encode(saltb, saltb.length)); + res.push(base64_encode(bytes, C_ORIG.length * 4 - 1)); + return res.join(""); + } + + // Sync + if (typeof callback == "undefined") + return finish(_crypt(passwordb, saltb, rounds)); + // Async + else { + _crypt( + passwordb, + saltb, + rounds, + function (err, bytes) { + if (err) callback(err, null); + else callback(null, finish(bytes)); + }, + progressCallback, + ); + } +} + +/** + * Encodes a byte array to base64 with up to len bytes of input, using the custom bcrypt alphabet. + * @function + * @param {!Array.} bytes Byte array + * @param {number} length Maximum input length + * @returns {string} + */ +export function encodeBase64(bytes, length) { + return base64_encode(bytes, length); +} + +/** + * Decodes a base64 encoded string to up to len bytes of output, using the custom bcrypt alphabet. + * @function + * @param {string} string String to decode + * @param {number} length Maximum output length + * @returns {!Array.} + */ +export function decodeBase64(string, length) { + return base64_decode(string, length); +} + +export default { + setRandomFallback, + genSaltSync, + genSalt, + hashSync, + hash, + compareSync, + compare, + getRounds, + getSalt, + truncates, + encodeBase64, + decodeBase64, +}; diff --git a/node_modules/bcryptjs/package.json b/node_modules/bcryptjs/package.json new file mode 100644 index 0000000..8b3ddb4 --- /dev/null +++ b/node_modules/bcryptjs/package.json @@ -0,0 +1,76 @@ +{ + "name": "bcryptjs", + "description": "Optimized bcrypt in plain JavaScript with zero dependencies, with TypeScript support. Compatible to 'bcrypt'.", + "version": "3.0.3", + "author": "Daniel Wirtz ", + "contributors": [ + "Shane Girish (https://github.com/shaneGirish)", + "Alex Murray <> (https://github.com/alexmurray)", + "Nicolas Pelletier <> (https://github.com/NicolasPelletier)", + "Josh Rogers <> (https://github.com/geekymole)", + "Noah Isaacson (https://github.com/nisaacson)" + ], + "repository": { + "type": "url", + "url": "https://github.com/dcodeIO/bcrypt.js.git" + }, + "bugs": { + "url": "https://github.com/dcodeIO/bcrypt.js/issues" + }, + "keywords": [ + "bcrypt", + "password", + "auth", + "authentication", + "encryption", + "crypt", + "crypto" + ], + "type": "module", + "main": "umd/index.js", + "types": "umd/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./index.d.ts", + "default": "./index.js" + }, + "require": { + "types": "./umd/index.d.ts", + "default": "./umd/index.js" + } + } + }, + "bin": { + "bcrypt": "bin/bcrypt" + }, + "license": "BSD-3-Clause", + "scripts": { + "build": "node scripts/build.js", + "lint": "prettier --check .", + "format": "prettier --write .", + "test": "npm run test:unit && npm run test:typescript", + "test:unit": "node tests", + "test:typescript": "tsc --project tests/typescript/tsconfig.esnext.json && tsc --project tests/typescript/tsconfig.nodenext.json && tsc --project tests/typescript/tsconfig.commonjs.json && tsc --project tests/typescript/tsconfig.global.json" + }, + "files": [ + "index.js", + "index.d.ts", + "types.d.ts", + "umd/index.js", + "umd/index.d.ts", + "umd/types.d.ts", + "umd/package.json", + "LICENSE", + "README.md" + ], + "browser": { + "crypto": false + }, + "devDependencies": { + "bcrypt": "^5.1.1", + "esm2umd": "^0.3.1", + "prettier": "^3.5.0", + "typescript": "^5.7.3" + } +} diff --git a/node_modules/bcryptjs/types.d.ts b/node_modules/bcryptjs/types.d.ts new file mode 100644 index 0000000..3cbe5b1 --- /dev/null +++ b/node_modules/bcryptjs/types.d.ts @@ -0,0 +1,157 @@ +// Originally imported from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8b36dbdf95b624b8a7cd7f8416f06c15d274f9e6/types/bcryptjs/index.d.ts +// MIT license. + +/** Called with an error on failure or a value of type `T` upon success. */ +type Callback = (err: Error | null, result?: T) => void; +/** Called with the percentage of rounds completed (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms. */ +type ProgressCallback = (percentage: number) => void; +/** Called to obtain random bytes when both Web Crypto API and Node.js crypto are not available. */ +type RandomFallback = (length: number) => number[]; + +/** + * Sets the pseudo random number generator to use as a fallback if neither node's crypto module nor the Web Crypto API is available. + * Please note: It is highly important that the PRNG used is cryptographically secure and that it is seeded properly! + * @param random Function taking the number of bytes to generate as its sole argument, returning the corresponding array of cryptographically secure random byte values. + */ +export declare function setRandomFallback(random: RandomFallback): void; + +/** + * Synchronously generates a salt. + * @param rounds Number of rounds to use, defaults to 10 if omitted + * @return Resulting salt + * @throws If a random fallback is required but not set + */ +export declare function genSaltSync(rounds?: number): string; + +/** + * Asynchronously generates a salt. + * @param rounds Number of rounds to use, defaults to 10 if omitted + * @return Promise with resulting salt, if callback has been omitted + */ +export declare function genSalt(rounds?: number): Promise; + +/** + * Asynchronously generates a salt. + * @param callback Callback receiving the error, if any, and the resulting salt + */ +export declare function genSalt(callback: Callback): void; + +/** + * Asynchronously generates a salt. + * @param rounds Number of rounds to use, defaults to 10 if omitted + * @param callback Callback receiving the error, if any, and the resulting salt + */ +export declare function genSalt( + rounds: number, + callback: Callback, +): void; + +/** + * Synchronously generates a hash for the given password. + * @param password Password to hash + * @param salt Salt length to generate or salt to use, default to 10 + * @return Resulting hash + */ +export declare function hashSync( + password: string, + salt?: number | string, +): string; + +/** + * Asynchronously generates a hash for the given password. + * @param password Password to hash + * @param salt Salt length to generate or salt to use + * @return Promise with resulting hash, if callback has been omitted + */ +export declare function hash( + password: string, + salt: number | string, +): Promise; + +/** + * Asynchronously generates a hash for the given password. + * @param password Password to hash + * @param salt Salt length to generate or salt to use + * @param callback Callback receiving the error, if any, and the resulting hash + * @param progressCallback Callback successively called with the percentage of rounds completed (0.0 - 1.0), maximally once per MAX_EXECUTION_TIME = 100 ms. + */ +export declare function hash( + password: string, + salt: number | string, + callback?: Callback, + progressCallback?: ProgressCallback, +): void; + +/** + * Synchronously tests a password against a hash. + * @param password Password to test + * @param hash Hash to test against + * @return true if matching, otherwise false + */ +export declare function compareSync(password: string, hash: string): boolean; + +/** + * Asynchronously tests a password against a hash. + * @param password Password to test + * @param hash Hash to test against + * @return Promise, if callback has been omitted + */ +export declare function compare( + password: string, + hash: string, +): Promise; + +/** + * Asynchronously tests a password against a hash. + * @param password Password to test + * @param hash Hash to test against + * @param callback Callback receiving the error, if any, otherwise the result + * @param progressCallback Callback successively called with the percentage of rounds completed (0.0 - 1.0), maximally once per MAX_EXECUTION_TIME = 100 ms. + */ +export declare function compare( + password: string, + hash: string, + callback?: Callback, + progressCallback?: ProgressCallback, +): void; + +/** + * Gets the number of rounds used to encrypt the specified hash. + * @param hash Hash to extract the used number of rounds from + * @return Number of rounds used + */ +export declare function getRounds(hash: string): number; + +/** + * Gets the salt portion from a hash. Does not validate the hash. + * @param hash Hash to extract the salt from + * @return Extracted salt part + */ +export declare function getSalt(hash: string): string; + +/** + * Tests if a password will be truncated when hashed, that is its length is + * greater than 72 bytes when converted to UTF-8. + * @param password The password to test + * @returns `true` if truncated, otherwise `false` + */ +export declare function truncates(password: string): boolean; + +/** + * Encodes a byte array to base64 with up to len bytes of input, using the custom bcrypt alphabet. + * @function + * @param b Byte array + * @param len Maximum input length + */ +export declare function encodeBase64( + b: Readonly>, + len: number, +): string; + +/** + * Decodes a base64 encoded string to up to len bytes of output, using the custom bcrypt alphabet. + * @function + * @param s String to decode + * @param len Maximum output length + */ +export declare function decodeBase64(s: string, len: number): number[]; diff --git a/node_modules/bcryptjs/umd/index.d.ts b/node_modules/bcryptjs/umd/index.d.ts new file mode 100644 index 0000000..8c2eb07 --- /dev/null +++ b/node_modules/bcryptjs/umd/index.d.ts @@ -0,0 +1,3 @@ +import * as bcrypt from "./types.js"; +export = bcrypt; +export as namespace bcrypt; diff --git a/node_modules/bcryptjs/umd/index.js b/node_modules/bcryptjs/umd/index.js new file mode 100644 index 0000000..517ea20 --- /dev/null +++ b/node_modules/bcryptjs/umd/index.js @@ -0,0 +1,1220 @@ +// GENERATED FILE. DO NOT EDIT. +(function (global, factory) { + function preferDefault(exports) { + return exports.default || exports; + } + if (typeof define === "function" && define.amd) { + define(["crypto"], function (_crypto) { + var exports = {}; + factory(exports, _crypto); + return preferDefault(exports); + }); + } else if (typeof exports === "object") { + factory(exports, require("crypto")); + if (typeof module === "object") module.exports = preferDefault(exports); + } else { + (function () { + var exports = {}; + factory(exports, global.crypto); + global.bcrypt = preferDefault(exports); + })(); + } +})( + typeof globalThis !== "undefined" + ? globalThis + : typeof self !== "undefined" + ? self + : this, + function (_exports, _crypto) { + "use strict"; + + Object.defineProperty(_exports, "__esModule", { + value: true, + }); + _exports.compare = compare; + _exports.compareSync = compareSync; + _exports.decodeBase64 = decodeBase64; + _exports.default = void 0; + _exports.encodeBase64 = encodeBase64; + _exports.genSalt = genSalt; + _exports.genSaltSync = genSaltSync; + _exports.getRounds = getRounds; + _exports.getSalt = getSalt; + _exports.hash = hash; + _exports.hashSync = hashSync; + _exports.setRandomFallback = setRandomFallback; + _exports.truncates = truncates; + _crypto = _interopRequireDefault(_crypto); + function _interopRequireDefault(e) { + return e && e.__esModule ? e : { default: e }; + } + /* + Copyright (c) 2012 Nevins Bartolomeo + Copyright (c) 2012 Shane Girish + Copyright (c) 2025 Daniel Wirtz + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + // The Node.js crypto module is used as a fallback for the Web Crypto API. When + // building for the browser, inclusion of the crypto module should be disabled, + // which the package hints at in its package.json for bundlers that support it. + + /** + * The random implementation to use as a fallback. + * @type {?function(number):!Array.} + * @inner + */ + var randomFallback = null; + + /** + * Generates cryptographically secure random bytes. + * @function + * @param {number} len Bytes length + * @returns {!Array.} Random bytes + * @throws {Error} If no random implementation is available + * @inner + */ + function randomBytes(len) { + // Web Crypto API. Globally available in the browser and in Node.js >=23. + try { + return crypto.getRandomValues(new Uint8Array(len)); + } catch {} + // Node.js crypto module for non-browser environments. + try { + return _crypto.default.randomBytes(len); + } catch {} + // Custom fallback specified with `setRandomFallback`. + if (!randomFallback) { + throw Error( + "Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative", + ); + } + return randomFallback(len); + } + + /** + * Sets the pseudo random number generator to use as a fallback if neither node's `crypto` module nor the Web Crypto + * API is available. Please note: It is highly important that the PRNG used is cryptographically secure and that it + * is seeded properly! + * @param {?function(number):!Array.} random Function taking the number of bytes to generate as its + * sole argument, returning the corresponding array of cryptographically secure random byte values. + * @see http://nodejs.org/api/crypto.html + * @see http://www.w3.org/TR/WebCryptoAPI/ + */ + function setRandomFallback(random) { + randomFallback = random; + } + + /** + * Synchronously generates a salt. + * @param {number=} rounds Number of rounds to use, defaults to 10 if omitted + * @param {number=} seed_length Not supported. + * @returns {string} Resulting salt + * @throws {Error} If a random fallback is required but not set + */ + function genSaltSync(rounds, seed_length) { + rounds = rounds || GENSALT_DEFAULT_LOG2_ROUNDS; + if (typeof rounds !== "number") + throw Error( + "Illegal arguments: " + typeof rounds + ", " + typeof seed_length, + ); + if (rounds < 4) rounds = 4; + else if (rounds > 31) rounds = 31; + var salt = []; + salt.push("$2b$"); + if (rounds < 10) salt.push("0"); + salt.push(rounds.toString()); + salt.push("$"); + salt.push(base64_encode(randomBytes(BCRYPT_SALT_LEN), BCRYPT_SALT_LEN)); // May throw + return salt.join(""); + } + + /** + * Asynchronously generates a salt. + * @param {(number|function(Error, string=))=} rounds Number of rounds to use, defaults to 10 if omitted + * @param {(number|function(Error, string=))=} seed_length Not supported. + * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting salt + * @returns {!Promise} If `callback` has been omitted + * @throws {Error} If `callback` is present but not a function + */ + function genSalt(rounds, seed_length, callback) { + if (typeof seed_length === "function") + (callback = seed_length), (seed_length = undefined); // Not supported. + if (typeof rounds === "function") + (callback = rounds), (rounds = undefined); + if (typeof rounds === "undefined") rounds = GENSALT_DEFAULT_LOG2_ROUNDS; + else if (typeof rounds !== "number") + throw Error("illegal arguments: " + typeof rounds); + function _async(callback) { + nextTick(function () { + // Pretty thin, but salting is fast enough + try { + callback(null, genSaltSync(rounds)); + } catch (err) { + callback(err); + } + }); + } + if (callback) { + if (typeof callback !== "function") + throw Error("Illegal callback: " + typeof callback); + _async(callback); + } else + return new Promise(function (resolve, reject) { + _async(function (err, res) { + if (err) { + reject(err); + return; + } + resolve(res); + }); + }); + } + + /** + * Synchronously generates a hash for the given password. + * @param {string} password Password to hash + * @param {(number|string)=} salt Salt length to generate or salt to use, default to 10 + * @returns {string} Resulting hash + */ + function hashSync(password, salt) { + if (typeof salt === "undefined") salt = GENSALT_DEFAULT_LOG2_ROUNDS; + if (typeof salt === "number") salt = genSaltSync(salt); + if (typeof password !== "string" || typeof salt !== "string") + throw Error( + "Illegal arguments: " + typeof password + ", " + typeof salt, + ); + return _hash(password, salt); + } + + /** + * Asynchronously generates a hash for the given password. + * @param {string} password Password to hash + * @param {number|string} salt Salt length to generate or salt to use + * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash + * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed + * (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms. + * @returns {!Promise} If `callback` has been omitted + * @throws {Error} If `callback` is present but not a function + */ + function hash(password, salt, callback, progressCallback) { + function _async(callback) { + if (typeof password === "string" && typeof salt === "number") + genSalt(salt, function (err, salt) { + _hash(password, salt, callback, progressCallback); + }); + else if (typeof password === "string" && typeof salt === "string") + _hash(password, salt, callback, progressCallback); + else + nextTick( + callback.bind( + this, + Error( + "Illegal arguments: " + typeof password + ", " + typeof salt, + ), + ), + ); + } + if (callback) { + if (typeof callback !== "function") + throw Error("Illegal callback: " + typeof callback); + _async(callback); + } else + return new Promise(function (resolve, reject) { + _async(function (err, res) { + if (err) { + reject(err); + return; + } + resolve(res); + }); + }); + } + + /** + * Compares two strings of the same length in constant time. + * @param {string} known Must be of the correct length + * @param {string} unknown Must be the same length as `known` + * @returns {boolean} + * @inner + */ + function safeStringCompare(known, unknown) { + var diff = known.length ^ unknown.length; + for (var i = 0; i < known.length; ++i) { + diff |= known.charCodeAt(i) ^ unknown.charCodeAt(i); + } + return diff === 0; + } + + /** + * Synchronously tests a password against a hash. + * @param {string} password Password to compare + * @param {string} hash Hash to test against + * @returns {boolean} true if matching, otherwise false + * @throws {Error} If an argument is illegal + */ + function compareSync(password, hash) { + if (typeof password !== "string" || typeof hash !== "string") + throw Error( + "Illegal arguments: " + typeof password + ", " + typeof hash, + ); + if (hash.length !== 60) return false; + return safeStringCompare( + hashSync(password, hash.substring(0, hash.length - 31)), + hash, + ); + } + + /** + * Asynchronously tests a password against a hash. + * @param {string} password Password to compare + * @param {string} hashValue Hash to test against + * @param {function(Error, boolean)=} callback Callback receiving the error, if any, otherwise the result + * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed + * (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms. + * @returns {!Promise} If `callback` has been omitted + * @throws {Error} If `callback` is present but not a function + */ + function compare(password, hashValue, callback, progressCallback) { + function _async(callback) { + if (typeof password !== "string" || typeof hashValue !== "string") { + nextTick( + callback.bind( + this, + Error( + "Illegal arguments: " + + typeof password + + ", " + + typeof hashValue, + ), + ), + ); + return; + } + if (hashValue.length !== 60) { + nextTick(callback.bind(this, null, false)); + return; + } + hash( + password, + hashValue.substring(0, 29), + function (err, comp) { + if (err) callback(err); + else callback(null, safeStringCompare(comp, hashValue)); + }, + progressCallback, + ); + } + if (callback) { + if (typeof callback !== "function") + throw Error("Illegal callback: " + typeof callback); + _async(callback); + } else + return new Promise(function (resolve, reject) { + _async(function (err, res) { + if (err) { + reject(err); + return; + } + resolve(res); + }); + }); + } + + /** + * Gets the number of rounds used to encrypt the specified hash. + * @param {string} hash Hash to extract the used number of rounds from + * @returns {number} Number of rounds used + * @throws {Error} If `hash` is not a string + */ + function getRounds(hash) { + if (typeof hash !== "string") + throw Error("Illegal arguments: " + typeof hash); + return parseInt(hash.split("$")[2], 10); + } + + /** + * Gets the salt portion from a hash. Does not validate the hash. + * @param {string} hash Hash to extract the salt from + * @returns {string} Extracted salt part + * @throws {Error} If `hash` is not a string or otherwise invalid + */ + function getSalt(hash) { + if (typeof hash !== "string") + throw Error("Illegal arguments: " + typeof hash); + if (hash.length !== 60) + throw Error("Illegal hash length: " + hash.length + " != 60"); + return hash.substring(0, 29); + } + + /** + * Tests if a password will be truncated when hashed, that is its length is + * greater than 72 bytes when converted to UTF-8. + * @param {string} password The password to test + * @returns {boolean} `true` if truncated, otherwise `false` + */ + function truncates(password) { + if (typeof password !== "string") + throw Error("Illegal arguments: " + typeof password); + return utf8Length(password) > 72; + } + + /** + * Continues with the callback after yielding to the event loop. + * @function + * @param {function(...[*])} callback Callback to execute + * @inner + */ + var nextTick = + typeof setImmediate === "function" + ? setImmediate + : typeof scheduler === "object" && + typeof scheduler.postTask === "function" + ? scheduler.postTask.bind(scheduler) + : setTimeout; + + /** Calculates the byte length of a string encoded as UTF8. */ + function utf8Length(string) { + var len = 0, + c = 0; + for (var i = 0; i < string.length; ++i) { + c = string.charCodeAt(i); + if (c < 128) len += 1; + else if (c < 2048) len += 2; + else if ( + (c & 0xfc00) === 0xd800 && + (string.charCodeAt(i + 1) & 0xfc00) === 0xdc00 + ) { + ++i; + len += 4; + } else len += 3; + } + return len; + } + + /** Converts a string to an array of UTF8 bytes. */ + function utf8Array(string) { + var offset = 0, + c1, + c2; + var buffer = new Array(utf8Length(string)); + for (var i = 0, k = string.length; i < k; ++i) { + c1 = string.charCodeAt(i); + if (c1 < 128) { + buffer[offset++] = c1; + } else if (c1 < 2048) { + buffer[offset++] = (c1 >> 6) | 192; + buffer[offset++] = (c1 & 63) | 128; + } else if ( + (c1 & 0xfc00) === 0xd800 && + ((c2 = string.charCodeAt(i + 1)) & 0xfc00) === 0xdc00 + ) { + c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff); + ++i; + buffer[offset++] = (c1 >> 18) | 240; + buffer[offset++] = ((c1 >> 12) & 63) | 128; + buffer[offset++] = ((c1 >> 6) & 63) | 128; + buffer[offset++] = (c1 & 63) | 128; + } else { + buffer[offset++] = (c1 >> 12) | 224; + buffer[offset++] = ((c1 >> 6) & 63) | 128; + buffer[offset++] = (c1 & 63) | 128; + } + } + return buffer; + } + + // A base64 implementation for the bcrypt algorithm. This is partly non-standard. + + /** + * bcrypt's own non-standard base64 dictionary. + * @type {!Array.} + * @const + * @inner + **/ + var BASE64_CODE = + "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split( + "", + ); + + /** + * @type {!Array.} + * @const + * @inner + **/ + var BASE64_INDEX = [ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, + -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1, + ]; + + /** + * Encodes a byte array to base64 with up to len bytes of input. + * @param {!Array.} b Byte array + * @param {number} len Maximum input length + * @returns {string} + * @inner + */ + function base64_encode(b, len) { + var off = 0, + rs = [], + c1, + c2; + if (len <= 0 || len > b.length) throw Error("Illegal len: " + len); + while (off < len) { + c1 = b[off++] & 0xff; + rs.push(BASE64_CODE[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= len) { + rs.push(BASE64_CODE[c1 & 0x3f]); + break; + } + c2 = b[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.push(BASE64_CODE[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= len) { + rs.push(BASE64_CODE[c1 & 0x3f]); + break; + } + c2 = b[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.push(BASE64_CODE[c1 & 0x3f]); + rs.push(BASE64_CODE[c2 & 0x3f]); + } + return rs.join(""); + } + + /** + * Decodes a base64 encoded string to up to len bytes of output. + * @param {string} s String to decode + * @param {number} len Maximum output length + * @returns {!Array.} + * @inner + */ + function base64_decode(s, len) { + var off = 0, + slen = s.length, + olen = 0, + rs = [], + c1, + c2, + c3, + c4, + o, + code; + if (len <= 0) throw Error("Illegal len: " + len); + while (off < slen - 1 && olen < len) { + code = s.charCodeAt(off++); + c1 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1; + code = s.charCodeAt(off++); + c2 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1; + if (c1 == -1 || c2 == -1) break; + o = (c1 << 2) >>> 0; + o |= (c2 & 0x30) >> 4; + rs.push(String.fromCharCode(o)); + if (++olen >= len || off >= slen) break; + code = s.charCodeAt(off++); + c3 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1; + if (c3 == -1) break; + o = ((c2 & 0x0f) << 4) >>> 0; + o |= (c3 & 0x3c) >> 2; + rs.push(String.fromCharCode(o)); + if (++olen >= len || off >= slen) break; + code = s.charCodeAt(off++); + c4 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1; + o = ((c3 & 0x03) << 6) >>> 0; + o |= c4; + rs.push(String.fromCharCode(o)); + ++olen; + } + var res = []; + for (off = 0; off < olen; off++) res.push(rs[off].charCodeAt(0)); + return res; + } + + /** + * @type {number} + * @const + * @inner + */ + var BCRYPT_SALT_LEN = 16; + + /** + * @type {number} + * @const + * @inner + */ + var GENSALT_DEFAULT_LOG2_ROUNDS = 10; + + /** + * @type {number} + * @const + * @inner + */ + var BLOWFISH_NUM_ROUNDS = 16; + + /** + * @type {number} + * @const + * @inner + */ + var MAX_EXECUTION_TIME = 100; + + /** + * @type {Array.} + * @const + * @inner + */ + var P_ORIG = [ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, + ]; + + /** + * @type {Array.} + * @const + * @inner + */ + var S_ORIG = [ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, + 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, + 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, + 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, + 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, + 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, + 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, + 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, + 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, + 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, + 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, + 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, + 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, + 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, + 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, + 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, + 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, + 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, + 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, + 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, + 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, + 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944, + 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, + 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, + 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, + 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, + 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, + 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, + 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, + 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, + 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, + 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, + 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, + 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, + 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, + 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, + 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, + 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, + 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, + 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, + 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, + 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, + 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, + 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, + 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, + 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, + 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, + 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, + 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, + 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, + 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, + 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, + 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, + 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, + 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, + 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, + 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, + 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, + 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, + 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, + 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, + 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, + 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, + 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, + 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, + 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, + 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, + 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, + 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, + 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, + 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, + 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, + 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, + 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, + 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, + 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, + 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, + 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, + 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, + 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, + 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, + 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, + 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, + 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, + 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, + 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, + ]; + + /** + * @type {Array.} + * @const + * @inner + */ + var C_ORIG = [ + 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274, + ]; + + /** + * @param {Array.} lr + * @param {number} off + * @param {Array.} P + * @param {Array.} S + * @returns {Array.} + * @inner + */ + function _encipher(lr, off, P, S) { + // This is our bottleneck: 1714/1905 ticks / 90% - see profile.txt + var n, + l = lr[off], + r = lr[off + 1]; + l ^= P[0]; + + /* + for (var i=0, k=BLOWFISH_NUM_ROUNDS-2; i<=k;) + // Feistel substitution on left word + n = S[l >>> 24], + n += S[0x100 | ((l >> 16) & 0xff)], + n ^= S[0x200 | ((l >> 8) & 0xff)], + n += S[0x300 | (l & 0xff)], + r ^= n ^ P[++i], + // Feistel substitution on right word + n = S[r >>> 24], + n += S[0x100 | ((r >> 16) & 0xff)], + n ^= S[0x200 | ((r >> 8) & 0xff)], + n += S[0x300 | (r & 0xff)], + l ^= n ^ P[++i]; + */ + + //The following is an unrolled version of the above loop. + //Iteration 0 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[1]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[2]; + //Iteration 1 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[3]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[4]; + //Iteration 2 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[5]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[6]; + //Iteration 3 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[7]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[8]; + //Iteration 4 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[9]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[10]; + //Iteration 5 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[11]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[12]; + //Iteration 6 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[13]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[14]; + //Iteration 7 + n = S[l >>> 24]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[15]; + n = S[r >>> 24]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[16]; + lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; + lr[off + 1] = l; + return lr; + } + + /** + * @param {Array.} data + * @param {number} offp + * @returns {{key: number, offp: number}} + * @inner + */ + function _streamtoword(data, offp) { + for (var i = 0, word = 0; i < 4; ++i) + (word = (word << 8) | (data[offp] & 0xff)), + (offp = (offp + 1) % data.length); + return { + key: word, + offp: offp, + }; + } + + /** + * @param {Array.} key + * @param {Array.} P + * @param {Array.} S + * @inner + */ + function _key(key, P, S) { + var offset = 0, + lr = [0, 0], + plen = P.length, + slen = S.length, + sw; + for (var i = 0; i < plen; i++) + (sw = _streamtoword(key, offset)), + (offset = sw.offp), + (P[i] = P[i] ^ sw.key); + for (i = 0; i < plen; i += 2) + (lr = _encipher(lr, 0, P, S)), (P[i] = lr[0]), (P[i + 1] = lr[1]); + for (i = 0; i < slen; i += 2) + (lr = _encipher(lr, 0, P, S)), (S[i] = lr[0]), (S[i + 1] = lr[1]); + } + + /** + * Expensive key schedule Blowfish. + * @param {Array.} data + * @param {Array.} key + * @param {Array.} P + * @param {Array.} S + * @inner + */ + function _ekskey(data, key, P, S) { + var offp = 0, + lr = [0, 0], + plen = P.length, + slen = S.length, + sw; + for (var i = 0; i < plen; i++) + (sw = _streamtoword(key, offp)), + (offp = sw.offp), + (P[i] = P[i] ^ sw.key); + offp = 0; + for (i = 0; i < plen; i += 2) + (sw = _streamtoword(data, offp)), + (offp = sw.offp), + (lr[0] ^= sw.key), + (sw = _streamtoword(data, offp)), + (offp = sw.offp), + (lr[1] ^= sw.key), + (lr = _encipher(lr, 0, P, S)), + (P[i] = lr[0]), + (P[i + 1] = lr[1]); + for (i = 0; i < slen; i += 2) + (sw = _streamtoword(data, offp)), + (offp = sw.offp), + (lr[0] ^= sw.key), + (sw = _streamtoword(data, offp)), + (offp = sw.offp), + (lr[1] ^= sw.key), + (lr = _encipher(lr, 0, P, S)), + (S[i] = lr[0]), + (S[i + 1] = lr[1]); + } + + /** + * Internaly crypts a string. + * @param {Array.} b Bytes to crypt + * @param {Array.} salt Salt bytes to use + * @param {number} rounds Number of rounds + * @param {function(Error, Array.=)=} callback Callback receiving the error, if any, and the resulting bytes. If + * omitted, the operation will be performed synchronously. + * @param {function(number)=} progressCallback Callback called with the current progress + * @returns {!Array.|undefined} Resulting bytes if callback has been omitted, otherwise `undefined` + * @inner + */ + function _crypt(b, salt, rounds, callback, progressCallback) { + var cdata = C_ORIG.slice(), + clen = cdata.length, + err; + + // Validate + if (rounds < 4 || rounds > 31) { + err = Error("Illegal number of rounds (4-31): " + rounds); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + if (salt.length !== BCRYPT_SALT_LEN) { + err = Error( + "Illegal salt length: " + salt.length + " != " + BCRYPT_SALT_LEN, + ); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + rounds = (1 << rounds) >>> 0; + var P, + S, + i = 0, + j; + + //Use typed arrays when available - huge speedup! + if (typeof Int32Array === "function") { + P = new Int32Array(P_ORIG); + S = new Int32Array(S_ORIG); + } else { + P = P_ORIG.slice(); + S = S_ORIG.slice(); + } + _ekskey(salt, b, P, S); + + /** + * Calcualtes the next round. + * @returns {Array.|undefined} Resulting array if callback has been omitted, otherwise `undefined` + * @inner + */ + function next() { + if (progressCallback) progressCallback(i / rounds); + if (i < rounds) { + var start = Date.now(); + for (; i < rounds; ) { + i = i + 1; + _key(b, P, S); + _key(salt, P, S); + if (Date.now() - start > MAX_EXECUTION_TIME) break; + } + } else { + for (i = 0; i < 64; i++) + for (j = 0; j < clen >> 1; j++) _encipher(cdata, j << 1, P, S); + var ret = []; + for (i = 0; i < clen; i++) + ret.push(((cdata[i] >> 24) & 0xff) >>> 0), + ret.push(((cdata[i] >> 16) & 0xff) >>> 0), + ret.push(((cdata[i] >> 8) & 0xff) >>> 0), + ret.push((cdata[i] & 0xff) >>> 0); + if (callback) { + callback(null, ret); + return; + } else return ret; + } + if (callback) nextTick(next); + } + + // Async + if (typeof callback !== "undefined") { + next(); + + // Sync + } else { + var res; + while (true) + if (typeof (res = next()) !== "undefined") return res || []; + } + } + + /** + * Internally hashes a password. + * @param {string} password Password to hash + * @param {?string} salt Salt to use, actually never null + * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash. If omitted, + * hashing is performed synchronously. + * @param {function(number)=} progressCallback Callback called with the current progress + * @returns {string|undefined} Resulting hash if callback has been omitted, otherwise `undefined` + * @inner + */ + function _hash(password, salt, callback, progressCallback) { + var err; + if (typeof password !== "string" || typeof salt !== "string") { + err = Error("Invalid string / salt: Not a string"); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + + // Validate the salt + var minor, offset; + if (salt.charAt(0) !== "$" || salt.charAt(1) !== "2") { + err = Error("Invalid salt version: " + salt.substring(0, 2)); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + if (salt.charAt(2) === "$") + (minor = String.fromCharCode(0)), (offset = 3); + else { + minor = salt.charAt(2); + if ( + (minor !== "a" && minor !== "b" && minor !== "y") || + salt.charAt(3) !== "$" + ) { + err = Error("Invalid salt revision: " + salt.substring(2, 4)); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + offset = 4; + } + + // Extract number of rounds + if (salt.charAt(offset + 2) > "$") { + err = Error("Missing salt rounds"); + if (callback) { + nextTick(callback.bind(this, err)); + return; + } else throw err; + } + var r1 = parseInt(salt.substring(offset, offset + 1), 10) * 10, + r2 = parseInt(salt.substring(offset + 1, offset + 2), 10), + rounds = r1 + r2, + real_salt = salt.substring(offset + 3, offset + 25); + password += minor >= "a" ? "\x00" : ""; + var passwordb = utf8Array(password), + saltb = base64_decode(real_salt, BCRYPT_SALT_LEN); + + /** + * Finishes hashing. + * @param {Array.} bytes Byte array + * @returns {string} + * @inner + */ + function finish(bytes) { + var res = []; + res.push("$2"); + if (minor >= "a") res.push(minor); + res.push("$"); + if (rounds < 10) res.push("0"); + res.push(rounds.toString()); + res.push("$"); + res.push(base64_encode(saltb, saltb.length)); + res.push(base64_encode(bytes, C_ORIG.length * 4 - 1)); + return res.join(""); + } + + // Sync + if (typeof callback == "undefined") + return finish(_crypt(passwordb, saltb, rounds)); + // Async + else { + _crypt( + passwordb, + saltb, + rounds, + function (err, bytes) { + if (err) callback(err, null); + else callback(null, finish(bytes)); + }, + progressCallback, + ); + } + } + + /** + * Encodes a byte array to base64 with up to len bytes of input, using the custom bcrypt alphabet. + * @function + * @param {!Array.} bytes Byte array + * @param {number} length Maximum input length + * @returns {string} + */ + function encodeBase64(bytes, length) { + return base64_encode(bytes, length); + } + + /** + * Decodes a base64 encoded string to up to len bytes of output, using the custom bcrypt alphabet. + * @function + * @param {string} string String to decode + * @param {number} length Maximum output length + * @returns {!Array.} + */ + function decodeBase64(string, length) { + return base64_decode(string, length); + } + var _default = (_exports.default = { + setRandomFallback, + genSaltSync, + genSalt, + hashSync, + hash, + compareSync, + compare, + getRounds, + getSalt, + truncates, + encodeBase64, + decodeBase64, + }); + }, +); diff --git a/node_modules/bcryptjs/umd/package.json b/node_modules/bcryptjs/umd/package.json new file mode 100644 index 0000000..5bbefff --- /dev/null +++ b/node_modules/bcryptjs/umd/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/node_modules/bcryptjs/umd/types.d.ts b/node_modules/bcryptjs/umd/types.d.ts new file mode 100644 index 0000000..3cbe5b1 --- /dev/null +++ b/node_modules/bcryptjs/umd/types.d.ts @@ -0,0 +1,157 @@ +// Originally imported from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8b36dbdf95b624b8a7cd7f8416f06c15d274f9e6/types/bcryptjs/index.d.ts +// MIT license. + +/** Called with an error on failure or a value of type `T` upon success. */ +type Callback = (err: Error | null, result?: T) => void; +/** Called with the percentage of rounds completed (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms. */ +type ProgressCallback = (percentage: number) => void; +/** Called to obtain random bytes when both Web Crypto API and Node.js crypto are not available. */ +type RandomFallback = (length: number) => number[]; + +/** + * Sets the pseudo random number generator to use as a fallback if neither node's crypto module nor the Web Crypto API is available. + * Please note: It is highly important that the PRNG used is cryptographically secure and that it is seeded properly! + * @param random Function taking the number of bytes to generate as its sole argument, returning the corresponding array of cryptographically secure random byte values. + */ +export declare function setRandomFallback(random: RandomFallback): void; + +/** + * Synchronously generates a salt. + * @param rounds Number of rounds to use, defaults to 10 if omitted + * @return Resulting salt + * @throws If a random fallback is required but not set + */ +export declare function genSaltSync(rounds?: number): string; + +/** + * Asynchronously generates a salt. + * @param rounds Number of rounds to use, defaults to 10 if omitted + * @return Promise with resulting salt, if callback has been omitted + */ +export declare function genSalt(rounds?: number): Promise; + +/** + * Asynchronously generates a salt. + * @param callback Callback receiving the error, if any, and the resulting salt + */ +export declare function genSalt(callback: Callback): void; + +/** + * Asynchronously generates a salt. + * @param rounds Number of rounds to use, defaults to 10 if omitted + * @param callback Callback receiving the error, if any, and the resulting salt + */ +export declare function genSalt( + rounds: number, + callback: Callback, +): void; + +/** + * Synchronously generates a hash for the given password. + * @param password Password to hash + * @param salt Salt length to generate or salt to use, default to 10 + * @return Resulting hash + */ +export declare function hashSync( + password: string, + salt?: number | string, +): string; + +/** + * Asynchronously generates a hash for the given password. + * @param password Password to hash + * @param salt Salt length to generate or salt to use + * @return Promise with resulting hash, if callback has been omitted + */ +export declare function hash( + password: string, + salt: number | string, +): Promise; + +/** + * Asynchronously generates a hash for the given password. + * @param password Password to hash + * @param salt Salt length to generate or salt to use + * @param callback Callback receiving the error, if any, and the resulting hash + * @param progressCallback Callback successively called with the percentage of rounds completed (0.0 - 1.0), maximally once per MAX_EXECUTION_TIME = 100 ms. + */ +export declare function hash( + password: string, + salt: number | string, + callback?: Callback, + progressCallback?: ProgressCallback, +): void; + +/** + * Synchronously tests a password against a hash. + * @param password Password to test + * @param hash Hash to test against + * @return true if matching, otherwise false + */ +export declare function compareSync(password: string, hash: string): boolean; + +/** + * Asynchronously tests a password against a hash. + * @param password Password to test + * @param hash Hash to test against + * @return Promise, if callback has been omitted + */ +export declare function compare( + password: string, + hash: string, +): Promise; + +/** + * Asynchronously tests a password against a hash. + * @param password Password to test + * @param hash Hash to test against + * @param callback Callback receiving the error, if any, otherwise the result + * @param progressCallback Callback successively called with the percentage of rounds completed (0.0 - 1.0), maximally once per MAX_EXECUTION_TIME = 100 ms. + */ +export declare function compare( + password: string, + hash: string, + callback?: Callback, + progressCallback?: ProgressCallback, +): void; + +/** + * Gets the number of rounds used to encrypt the specified hash. + * @param hash Hash to extract the used number of rounds from + * @return Number of rounds used + */ +export declare function getRounds(hash: string): number; + +/** + * Gets the salt portion from a hash. Does not validate the hash. + * @param hash Hash to extract the salt from + * @return Extracted salt part + */ +export declare function getSalt(hash: string): string; + +/** + * Tests if a password will be truncated when hashed, that is its length is + * greater than 72 bytes when converted to UTF-8. + * @param password The password to test + * @returns `true` if truncated, otherwise `false` + */ +export declare function truncates(password: string): boolean; + +/** + * Encodes a byte array to base64 with up to len bytes of input, using the custom bcrypt alphabet. + * @function + * @param b Byte array + * @param len Maximum input length + */ +export declare function encodeBase64( + b: Readonly>, + len: number, +): string; + +/** + * Decodes a base64 encoded string to up to len bytes of output, using the custom bcrypt alphabet. + * @function + * @param s String to decode + * @param len Maximum output length + */ +export declare function decodeBase64(s: string, len: number): number[]; diff --git a/node_modules/buffer-equal-constant-time/.npmignore b/node_modules/buffer-equal-constant-time/.npmignore new file mode 100644 index 0000000..34e4f5c --- /dev/null +++ b/node_modules/buffer-equal-constant-time/.npmignore @@ -0,0 +1,2 @@ +.*.sw[mnop] +node_modules/ diff --git a/node_modules/buffer-equal-constant-time/.travis.yml b/node_modules/buffer-equal-constant-time/.travis.yml new file mode 100644 index 0000000..78e1c01 --- /dev/null +++ b/node_modules/buffer-equal-constant-time/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: +- "0.11" +- "0.10" diff --git a/node_modules/buffer-equal-constant-time/LICENSE.txt b/node_modules/buffer-equal-constant-time/LICENSE.txt new file mode 100644 index 0000000..9a064f3 --- /dev/null +++ b/node_modules/buffer-equal-constant-time/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) 2013, GoInstant Inc., a salesforce.com company +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of salesforce.com, nor GoInstant, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/node_modules/buffer-equal-constant-time/README.md b/node_modules/buffer-equal-constant-time/README.md new file mode 100644 index 0000000..4f227f5 --- /dev/null +++ b/node_modules/buffer-equal-constant-time/README.md @@ -0,0 +1,50 @@ +# buffer-equal-constant-time + +Constant-time `Buffer` comparison for node.js. Should work with browserify too. + +[![Build Status](https://travis-ci.org/goinstant/buffer-equal-constant-time.png?branch=master)](https://travis-ci.org/goinstant/buffer-equal-constant-time) + +```sh + npm install buffer-equal-constant-time +``` + +# Usage + +```js + var bufferEq = require('buffer-equal-constant-time'); + + var a = new Buffer('asdf'); + var b = new Buffer('asdf'); + if (bufferEq(a,b)) { + // the same! + } else { + // different in at least one byte! + } +``` + +If you'd like to install an `.equal()` method onto the node.js `Buffer` and +`SlowBuffer` prototypes: + +```js + require('buffer-equal-constant-time').install(); + + var a = new Buffer('asdf'); + var b = new Buffer('asdf'); + if (a.equal(b)) { + // the same! + } else { + // different in at least one byte! + } +``` + +To get rid of the installed `.equal()` method, call `.restore()`: + +```js + require('buffer-equal-constant-time').restore(); +``` + +# Legal + +© 2013 GoInstant Inc., a salesforce.com company + +Licensed under the BSD 3-clause license. diff --git a/node_modules/buffer-equal-constant-time/index.js b/node_modules/buffer-equal-constant-time/index.js new file mode 100644 index 0000000..5462c1f --- /dev/null +++ b/node_modules/buffer-equal-constant-time/index.js @@ -0,0 +1,41 @@ +/*jshint node:true */ +'use strict'; +var Buffer = require('buffer').Buffer; // browserify +var SlowBuffer = require('buffer').SlowBuffer; + +module.exports = bufferEq; + +function bufferEq(a, b) { + + // shortcutting on type is necessary for correctness + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + return false; + } + + // buffer sizes should be well-known information, so despite this + // shortcutting, it doesn't leak any information about the *contents* of the + // buffers. + if (a.length !== b.length) { + return false; + } + + var c = 0; + for (var i = 0; i < a.length; i++) { + /*jshint bitwise:false */ + c |= a[i] ^ b[i]; // XOR + } + return c === 0; +} + +bufferEq.install = function() { + Buffer.prototype.equal = SlowBuffer.prototype.equal = function equal(that) { + return bufferEq(this, that); + }; +}; + +var origBufEqual = Buffer.prototype.equal; +var origSlowBufEqual = SlowBuffer.prototype.equal; +bufferEq.restore = function() { + Buffer.prototype.equal = origBufEqual; + SlowBuffer.prototype.equal = origSlowBufEqual; +}; diff --git a/node_modules/buffer-equal-constant-time/package.json b/node_modules/buffer-equal-constant-time/package.json new file mode 100644 index 0000000..17c7de2 --- /dev/null +++ b/node_modules/buffer-equal-constant-time/package.json @@ -0,0 +1,21 @@ +{ + "name": "buffer-equal-constant-time", + "version": "1.0.1", + "description": "Constant-time comparison of Buffers", + "main": "index.js", + "scripts": { + "test": "mocha test.js" + }, + "repository": "git@github.com:goinstant/buffer-equal-constant-time.git", + "keywords": [ + "buffer", + "equal", + "constant-time", + "crypto" + ], + "author": "GoInstant Inc., a salesforce.com company", + "license": "BSD-3-Clause", + "devDependencies": { + "mocha": "~1.15.1" + } +} diff --git a/node_modules/buffer-equal-constant-time/test.js b/node_modules/buffer-equal-constant-time/test.js new file mode 100644 index 0000000..0bc972d --- /dev/null +++ b/node_modules/buffer-equal-constant-time/test.js @@ -0,0 +1,42 @@ +/*jshint node:true */ +'use strict'; + +var bufferEq = require('./index'); +var assert = require('assert'); + +describe('buffer-equal-constant-time', function() { + var a = new Buffer('asdfasdf123456'); + var b = new Buffer('asdfasdf123456'); + var c = new Buffer('asdfasdf'); + + describe('bufferEq', function() { + it('says a == b', function() { + assert.strictEqual(bufferEq(a, b), true); + }); + + it('says a != c', function() { + assert.strictEqual(bufferEq(a, c), false); + }); + }); + + describe('install/restore', function() { + before(function() { + bufferEq.install(); + }); + after(function() { + bufferEq.restore(); + }); + + it('installed an .equal method', function() { + var SlowBuffer = require('buffer').SlowBuffer; + assert.ok(Buffer.prototype.equal); + assert.ok(SlowBuffer.prototype.equal); + }); + + it('infected existing Buffers', function() { + assert.strictEqual(a.equal(b), true); + assert.strictEqual(a.equal(c), false); + }); + }); + +}); diff --git a/node_modules/ecdsa-sig-formatter/CODEOWNERS b/node_modules/ecdsa-sig-formatter/CODEOWNERS new file mode 100644 index 0000000..4451d3d --- /dev/null +++ b/node_modules/ecdsa-sig-formatter/CODEOWNERS @@ -0,0 +1 @@ +* @omsmith diff --git a/node_modules/ecdsa-sig-formatter/LICENSE b/node_modules/ecdsa-sig-formatter/LICENSE new file mode 100644 index 0000000..8754ed6 --- /dev/null +++ b/node_modules/ecdsa-sig-formatter/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015 D2L Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/node_modules/ecdsa-sig-formatter/README.md b/node_modules/ecdsa-sig-formatter/README.md new file mode 100644 index 0000000..daa95d6 --- /dev/null +++ b/node_modules/ecdsa-sig-formatter/README.md @@ -0,0 +1,65 @@ +# ecdsa-sig-formatter + +[![Build Status](https://travis-ci.org/Brightspace/node-ecdsa-sig-formatter.svg?branch=master)](https://travis-ci.org/Brightspace/node-ecdsa-sig-formatter) [![Coverage Status](https://coveralls.io/repos/Brightspace/node-ecdsa-sig-formatter/badge.svg)](https://coveralls.io/r/Brightspace/node-ecdsa-sig-formatter) + +Translate between JOSE and ASN.1/DER encodings for ECDSA signatures + +## Install +```sh +npm install ecdsa-sig-formatter --save +``` + +## Usage +```js +var format = require('ecdsa-sig-formatter'); + +var derSignature = '..'; // asn.1/DER encoded ecdsa signature + +var joseSignature = format.derToJose(derSignature); + +``` + +### API + +--- + +#### `.derToJose(Buffer|String signature, String alg)` -> `String` + +Convert the ASN.1/DER encoded signature to a JOSE-style concatenated signature. +Returns a _base64 url_ encoded `String`. + +* If _signature_ is a `String`, it should be _base64_ encoded +* _alg_ must be one of _ES256_, _ES384_ or _ES512_ + +--- + +#### `.joseToDer(Buffer|String signature, String alg)` -> `Buffer` + +Convert the JOSE-style concatenated signature to an ASN.1/DER encoded +signature. Returns a `Buffer` + +* If _signature_ is a `String`, it should be _base64 url_ encoded +* _alg_ must be one of _ES256_, _ES384_ or _ES512_ + +## Contributing + +1. **Fork** the repository. Committing directly against this repository is + highly discouraged. + +2. Make your modifications in a branch, updating and writing new unit tests + as necessary in the `spec` directory. + +3. Ensure that all tests pass with `npm test` + +4. `rebase` your changes against master. *Do not merge*. + +5. Submit a pull request to this repository. Wait for tests to run and someone + to chime in. + +### Code Style + +This repository is configured with [EditorConfig][EditorConfig] and +[ESLint][ESLint] rules. + +[EditorConfig]: http://editorconfig.org/ +[ESLint]: http://eslint.org diff --git a/node_modules/ecdsa-sig-formatter/package.json b/node_modules/ecdsa-sig-formatter/package.json new file mode 100644 index 0000000..6fb5ebf --- /dev/null +++ b/node_modules/ecdsa-sig-formatter/package.json @@ -0,0 +1,46 @@ +{ + "name": "ecdsa-sig-formatter", + "version": "1.0.11", + "description": "Translate ECDSA signatures between ASN.1/DER and JOSE-style concatenation", + "main": "src/ecdsa-sig-formatter.js", + "scripts": { + "check-style": "eslint .", + "pretest": "npm run check-style", + "test": "istanbul cover --root src _mocha -- spec", + "report-cov": "cat ./coverage/lcov.info | coveralls" + }, + "typings": "./src/ecdsa-sig-formatter.d.ts", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/Brightspace/node-ecdsa-sig-formatter.git" + }, + "keywords": [ + "ecdsa", + "der", + "asn.1", + "jwt", + "jwa", + "jsonwebtoken", + "jose" + ], + "author": "D2L Corporation", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/Brightspace/node-ecdsa-sig-formatter/issues" + }, + "homepage": "https://github.com/Brightspace/node-ecdsa-sig-formatter#readme", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "devDependencies": { + "bench": "^0.3.6", + "chai": "^3.5.0", + "coveralls": "^2.11.9", + "eslint": "^2.12.0", + "eslint-config-brightspace": "^0.2.1", + "istanbul": "^0.4.3", + "jwk-to-pem": "^1.2.5", + "mocha": "^2.5.3", + "native-crypto": "^1.7.0" + } +} diff --git a/node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.d.ts b/node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.d.ts new file mode 100644 index 0000000..9693aa0 --- /dev/null +++ b/node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.d.ts @@ -0,0 +1,17 @@ +/// + +declare module "ecdsa-sig-formatter" { + /** + * Convert the ASN.1/DER encoded signature to a JOSE-style concatenated signature. Returns a base64 url encoded String. + * If signature is a String, it should be base64 encoded + * alg must be one of ES256, ES384 or ES512 + */ + export function derToJose(signature: Buffer | string, alg: string): string; + + /** + * Convert the JOSE-style concatenated signature to an ASN.1/DER encoded signature. Returns a Buffer + * If signature is a String, it should be base64 url encoded + * alg must be one of ES256, ES384 or ES512 + */ + export function joseToDer(signature: Buffer | string, alg: string): Buffer +} diff --git a/node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.js b/node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.js new file mode 100644 index 0000000..38eeb9b --- /dev/null +++ b/node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.js @@ -0,0 +1,187 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer; + +var getParamBytesForAlg = require('./param-bytes-for-alg'); + +var MAX_OCTET = 0x80, + CLASS_UNIVERSAL = 0, + PRIMITIVE_BIT = 0x20, + TAG_SEQ = 0x10, + TAG_INT = 0x02, + ENCODED_TAG_SEQ = (TAG_SEQ | PRIMITIVE_BIT) | (CLASS_UNIVERSAL << 6), + ENCODED_TAG_INT = TAG_INT | (CLASS_UNIVERSAL << 6); + +function base64Url(base64) { + return base64 + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); +} + +function signatureAsBuffer(signature) { + if (Buffer.isBuffer(signature)) { + return signature; + } else if ('string' === typeof signature) { + return Buffer.from(signature, 'base64'); + } + + throw new TypeError('ECDSA signature must be a Base64 string or a Buffer'); +} + +function derToJose(signature, alg) { + signature = signatureAsBuffer(signature); + var paramBytes = getParamBytesForAlg(alg); + + // the DER encoded param should at most be the param size, plus a padding + // zero, since due to being a signed integer + var maxEncodedParamLength = paramBytes + 1; + + var inputLength = signature.length; + + var offset = 0; + if (signature[offset++] !== ENCODED_TAG_SEQ) { + throw new Error('Could not find expected "seq"'); + } + + var seqLength = signature[offset++]; + if (seqLength === (MAX_OCTET | 1)) { + seqLength = signature[offset++]; + } + + if (inputLength - offset < seqLength) { + throw new Error('"seq" specified length of "' + seqLength + '", only "' + (inputLength - offset) + '" remaining'); + } + + if (signature[offset++] !== ENCODED_TAG_INT) { + throw new Error('Could not find expected "int" for "r"'); + } + + var rLength = signature[offset++]; + + if (inputLength - offset - 2 < rLength) { + throw new Error('"r" specified length of "' + rLength + '", only "' + (inputLength - offset - 2) + '" available'); + } + + if (maxEncodedParamLength < rLength) { + throw new Error('"r" specified length of "' + rLength + '", max of "' + maxEncodedParamLength + '" is acceptable'); + } + + var rOffset = offset; + offset += rLength; + + if (signature[offset++] !== ENCODED_TAG_INT) { + throw new Error('Could not find expected "int" for "s"'); + } + + var sLength = signature[offset++]; + + if (inputLength - offset !== sLength) { + throw new Error('"s" specified length of "' + sLength + '", expected "' + (inputLength - offset) + '"'); + } + + if (maxEncodedParamLength < sLength) { + throw new Error('"s" specified length of "' + sLength + '", max of "' + maxEncodedParamLength + '" is acceptable'); + } + + var sOffset = offset; + offset += sLength; + + if (offset !== inputLength) { + throw new Error('Expected to consume entire buffer, but "' + (inputLength - offset) + '" bytes remain'); + } + + var rPadding = paramBytes - rLength, + sPadding = paramBytes - sLength; + + var dst = Buffer.allocUnsafe(rPadding + rLength + sPadding + sLength); + + for (offset = 0; offset < rPadding; ++offset) { + dst[offset] = 0; + } + signature.copy(dst, offset, rOffset + Math.max(-rPadding, 0), rOffset + rLength); + + offset = paramBytes; + + for (var o = offset; offset < o + sPadding; ++offset) { + dst[offset] = 0; + } + signature.copy(dst, offset, sOffset + Math.max(-sPadding, 0), sOffset + sLength); + + dst = dst.toString('base64'); + dst = base64Url(dst); + + return dst; +} + +function countPadding(buf, start, stop) { + var padding = 0; + while (start + padding < stop && buf[start + padding] === 0) { + ++padding; + } + + var needsSign = buf[start + padding] >= MAX_OCTET; + if (needsSign) { + --padding; + } + + return padding; +} + +function joseToDer(signature, alg) { + signature = signatureAsBuffer(signature); + var paramBytes = getParamBytesForAlg(alg); + + var signatureBytes = signature.length; + if (signatureBytes !== paramBytes * 2) { + throw new TypeError('"' + alg + '" signatures must be "' + paramBytes * 2 + '" bytes, saw "' + signatureBytes + '"'); + } + + var rPadding = countPadding(signature, 0, paramBytes); + var sPadding = countPadding(signature, paramBytes, signature.length); + var rLength = paramBytes - rPadding; + var sLength = paramBytes - sPadding; + + var rsBytes = 1 + 1 + rLength + 1 + 1 + sLength; + + var shortLength = rsBytes < MAX_OCTET; + + var dst = Buffer.allocUnsafe((shortLength ? 2 : 3) + rsBytes); + + var offset = 0; + dst[offset++] = ENCODED_TAG_SEQ; + if (shortLength) { + // Bit 8 has value "0" + // bits 7-1 give the length. + dst[offset++] = rsBytes; + } else { + // Bit 8 of first octet has value "1" + // bits 7-1 give the number of additional length octets. + dst[offset++] = MAX_OCTET | 1; + // length, base 256 + dst[offset++] = rsBytes & 0xff; + } + dst[offset++] = ENCODED_TAG_INT; + dst[offset++] = rLength; + if (rPadding < 0) { + dst[offset++] = 0; + offset += signature.copy(dst, offset, 0, paramBytes); + } else { + offset += signature.copy(dst, offset, rPadding, paramBytes); + } + dst[offset++] = ENCODED_TAG_INT; + dst[offset++] = sLength; + if (sPadding < 0) { + dst[offset++] = 0; + signature.copy(dst, offset, paramBytes); + } else { + signature.copy(dst, offset, paramBytes + sPadding); + } + + return dst; +} + +module.exports = { + derToJose: derToJose, + joseToDer: joseToDer +}; diff --git a/node_modules/ecdsa-sig-formatter/src/param-bytes-for-alg.js b/node_modules/ecdsa-sig-formatter/src/param-bytes-for-alg.js new file mode 100644 index 0000000..9fe67ac --- /dev/null +++ b/node_modules/ecdsa-sig-formatter/src/param-bytes-for-alg.js @@ -0,0 +1,23 @@ +'use strict'; + +function getParamSize(keySize) { + var result = ((keySize / 8) | 0) + (keySize % 8 === 0 ? 0 : 1); + return result; +} + +var paramBytesForAlg = { + ES256: getParamSize(256), + ES384: getParamSize(384), + ES512: getParamSize(521) +}; + +function getParamBytesForAlg(alg) { + var paramBytes = paramBytesForAlg[alg]; + if (paramBytes) { + return paramBytes; + } + + throw new Error('Unknown algorithm "' + alg + '"'); +} + +module.exports = getParamBytesForAlg; diff --git a/node_modules/jsonwebtoken/LICENSE b/node_modules/jsonwebtoken/LICENSE new file mode 100644 index 0000000..bcd1854 --- /dev/null +++ b/node_modules/jsonwebtoken/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Auth0, Inc. (http://auth0.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/jsonwebtoken/README.md b/node_modules/jsonwebtoken/README.md new file mode 100644 index 0000000..4e20dd9 --- /dev/null +++ b/node_modules/jsonwebtoken/README.md @@ -0,0 +1,396 @@ +# jsonwebtoken + +| **Build** | **Dependency** | +|-----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| +| [![Build Status](https://secure.travis-ci.org/auth0/node-jsonwebtoken.svg?branch=master)](http://travis-ci.org/auth0/node-jsonwebtoken) | [![Dependency Status](https://david-dm.org/auth0/node-jsonwebtoken.svg)](https://david-dm.org/auth0/node-jsonwebtoken) | + + +An implementation of [JSON Web Tokens](https://tools.ietf.org/html/rfc7519). + +This was developed against `draft-ietf-oauth-json-web-token-08`. It makes use of [node-jws](https://github.com/brianloveswords/node-jws) + +# Install + +```bash +$ npm install jsonwebtoken +``` + +# Migration notes + +* [From v8 to v9](https://github.com/auth0/node-jsonwebtoken/wiki/Migration-Notes:-v8-to-v9) +* [From v7 to v8](https://github.com/auth0/node-jsonwebtoken/wiki/Migration-Notes:-v7-to-v8) + +# Usage + +### jwt.sign(payload, secretOrPrivateKey, [options, callback]) + +(Asynchronous) If a callback is supplied, the callback is called with the `err` or the JWT. + +(Synchronous) Returns the JsonWebToken as string + +`payload` could be an object literal, buffer or string representing valid JSON. +> **Please _note_ that** `exp` or any other claim is only set if the payload is an object literal. Buffer or string payloads are not checked for JSON validity. + +> If `payload` is not a buffer or a string, it will be coerced into a string using `JSON.stringify`. + +`secretOrPrivateKey` is a string (utf-8 encoded), buffer, object, or KeyObject containing either the secret for HMAC algorithms or the PEM +encoded private key for RSA and ECDSA. In case of a private key with passphrase an object `{ key, passphrase }` can be used (based on [crypto documentation](https://nodejs.org/api/crypto.html#crypto_sign_sign_private_key_output_format)), in this case be sure you pass the `algorithm` option. +When signing with RSA algorithms the minimum modulus length is 2048 except when the allowInsecureKeySizes option is set to true. Private keys below this size will be rejected with an error. + +`options`: + +* `algorithm` (default: `HS256`) +* `expiresIn`: expressed in seconds or a string describing a time span [vercel/ms](https://github.com/vercel/ms). + > Eg: `60`, `"2 days"`, `"10h"`, `"7d"`. A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default (`"120"` is equal to `"120ms"`). +* `notBefore`: expressed in seconds or a string describing a time span [vercel/ms](https://github.com/vercel/ms). + > Eg: `60`, `"2 days"`, `"10h"`, `"7d"`. A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default (`"120"` is equal to `"120ms"`). +* `audience` +* `issuer` +* `jwtid` +* `subject` +* `noTimestamp` +* `header` +* `keyid` +* `mutatePayload`: if true, the sign function will modify the payload object directly. This is useful if you need a raw reference to the payload after claims have been applied to it but before it has been encoded into a token. +* `allowInsecureKeySizes`: if true allows private keys with a modulus below 2048 to be used for RSA +* `allowInvalidAsymmetricKeyTypes`: if true, allows asymmetric keys which do not match the specified algorithm. This option is intended only for backwards compatability and should be avoided. + + + +> There are no default values for `expiresIn`, `notBefore`, `audience`, `subject`, `issuer`. These claims can also be provided in the payload directly with `exp`, `nbf`, `aud`, `sub` and `iss` respectively, but you **_can't_** include in both places. + +Remember that `exp`, `nbf` and `iat` are **NumericDate**, see related [Token Expiration (exp claim)](#token-expiration-exp-claim) + + +The header can be customized via the `options.header` object. + +Generated jwts will include an `iat` (issued at) claim by default unless `noTimestamp` is specified. If `iat` is inserted in the payload, it will be used instead of the real timestamp for calculating other things like `exp` given a timespan in `options.expiresIn`. + +Synchronous Sign with default (HMAC SHA256) + +```js +var jwt = require('jsonwebtoken'); +var token = jwt.sign({ foo: 'bar' }, 'shhhhh'); +``` + +Synchronous Sign with RSA SHA256 +```js +// sign with RSA SHA256 +var privateKey = fs.readFileSync('private.key'); +var token = jwt.sign({ foo: 'bar' }, privateKey, { algorithm: 'RS256' }); +``` + +Sign asynchronously +```js +jwt.sign({ foo: 'bar' }, privateKey, { algorithm: 'RS256' }, function(err, token) { + console.log(token); +}); +``` + +Backdate a jwt 30 seconds +```js +var older_token = jwt.sign({ foo: 'bar', iat: Math.floor(Date.now() / 1000) - 30 }, 'shhhhh'); +``` + +#### Token Expiration (exp claim) + +The standard for JWT defines an `exp` claim for expiration. The expiration is represented as a **NumericDate**: + +> A JSON numeric value representing the number of seconds from 1970-01-01T00:00:00Z UTC until the specified UTC date/time, ignoring leap seconds. This is equivalent to the IEEE Std 1003.1, 2013 Edition [POSIX.1] definition "Seconds Since the Epoch", in which each day is accounted for by exactly 86400 seconds, other than that non-integer values can be represented. See RFC 3339 [RFC3339] for details regarding date/times in general and UTC in particular. + +This means that the `exp` field should contain the number of seconds since the epoch. + +Signing a token with 1 hour of expiration: + +```javascript +jwt.sign({ + exp: Math.floor(Date.now() / 1000) + (60 * 60), + data: 'foobar' +}, 'secret'); +``` + +Another way to generate a token like this with this library is: + +```javascript +jwt.sign({ + data: 'foobar' +}, 'secret', { expiresIn: 60 * 60 }); + +//or even better: + +jwt.sign({ + data: 'foobar' +}, 'secret', { expiresIn: '1h' }); +``` + +### jwt.verify(token, secretOrPublicKey, [options, callback]) + +(Asynchronous) If a callback is supplied, function acts asynchronously. The callback is called with the decoded payload if the signature is valid and optional expiration, audience, or issuer are valid. If not, it will be called with the error. + +(Synchronous) If a callback is not supplied, function acts synchronously. Returns the payload decoded if the signature is valid and optional expiration, audience, or issuer are valid. If not, it will throw the error. + +> __Warning:__ When the token comes from an untrusted source (e.g. user input or external requests), the returned decoded payload should be treated like any other user input; please make sure to sanitize and only work with properties that are expected + +`token` is the JsonWebToken string + +`secretOrPublicKey` is a string (utf-8 encoded), buffer, or KeyObject containing either the secret for HMAC algorithms, or the PEM +encoded public key for RSA and ECDSA. +If `jwt.verify` is called asynchronous, `secretOrPublicKey` can be a function that should fetch the secret or public key. See below for a detailed example + +As mentioned in [this comment](https://github.com/auth0/node-jsonwebtoken/issues/208#issuecomment-231861138), there are other libraries that expect base64 encoded secrets (random bytes encoded using base64), if that is your case you can pass `Buffer.from(secret, 'base64')`, by doing this the secret will be decoded using base64 and the token verification will use the original random bytes. + +`options` + +* `algorithms`: List of strings with the names of the allowed algorithms. For instance, `["HS256", "HS384"]`. + > If not specified a defaults will be used based on the type of key provided + > * secret - ['HS256', 'HS384', 'HS512'] + > * rsa - ['RS256', 'RS384', 'RS512'] + > * ec - ['ES256', 'ES384', 'ES512'] + > * default - ['RS256', 'RS384', 'RS512'] +* `audience`: if you want to check audience (`aud`), provide a value here. The audience can be checked against a string, a regular expression or a list of strings and/or regular expressions. + > Eg: `"urn:foo"`, `/urn:f[o]{2}/`, `[/urn:f[o]{2}/, "urn:bar"]` +* `complete`: return an object with the decoded `{ payload, header, signature }` instead of only the usual content of the payload. +* `issuer` (optional): string or array of strings of valid values for the `iss` field. +* `jwtid` (optional): if you want to check JWT ID (`jti`), provide a string value here. +* `ignoreExpiration`: if `true` do not validate the expiration of the token. +* `ignoreNotBefore`... +* `subject`: if you want to check subject (`sub`), provide a value here +* `clockTolerance`: number of seconds to tolerate when checking the `nbf` and `exp` claims, to deal with small clock differences among different servers +* `maxAge`: the maximum allowed age for tokens to still be valid. It is expressed in seconds or a string describing a time span [vercel/ms](https://github.com/vercel/ms). + > Eg: `1000`, `"2 days"`, `"10h"`, `"7d"`. A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default (`"120"` is equal to `"120ms"`). +* `clockTimestamp`: the time in seconds that should be used as the current time for all necessary comparisons. +* `nonce`: if you want to check `nonce` claim, provide a string value here. It is used on Open ID for the ID Tokens. ([Open ID implementation notes](https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes)) +* `allowInvalidAsymmetricKeyTypes`: if true, allows asymmetric keys which do not match the specified algorithm. This option is intended only for backwards compatability and should be avoided. + +```js +// verify a token symmetric - synchronous +var decoded = jwt.verify(token, 'shhhhh'); +console.log(decoded.foo) // bar + +// verify a token symmetric +jwt.verify(token, 'shhhhh', function(err, decoded) { + console.log(decoded.foo) // bar +}); + +// invalid token - synchronous +try { + var decoded = jwt.verify(token, 'wrong-secret'); +} catch(err) { + // err +} + +// invalid token +jwt.verify(token, 'wrong-secret', function(err, decoded) { + // err + // decoded undefined +}); + +// verify a token asymmetric +var cert = fs.readFileSync('public.pem'); // get public key +jwt.verify(token, cert, function(err, decoded) { + console.log(decoded.foo) // bar +}); + +// verify audience +var cert = fs.readFileSync('public.pem'); // get public key +jwt.verify(token, cert, { audience: 'urn:foo' }, function(err, decoded) { + // if audience mismatch, err == invalid audience +}); + +// verify issuer +var cert = fs.readFileSync('public.pem'); // get public key +jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer' }, function(err, decoded) { + // if issuer mismatch, err == invalid issuer +}); + +// verify jwt id +var cert = fs.readFileSync('public.pem'); // get public key +jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer', jwtid: 'jwtid' }, function(err, decoded) { + // if jwt id mismatch, err == invalid jwt id +}); + +// verify subject +var cert = fs.readFileSync('public.pem'); // get public key +jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer', jwtid: 'jwtid', subject: 'subject' }, function(err, decoded) { + // if subject mismatch, err == invalid subject +}); + +// alg mismatch +var cert = fs.readFileSync('public.pem'); // get public key +jwt.verify(token, cert, { algorithms: ['RS256'] }, function (err, payload) { + // if token alg != RS256, err == invalid signature +}); + +// Verify using getKey callback +// Example uses https://github.com/auth0/node-jwks-rsa as a way to fetch the keys. +var jwksClient = require('jwks-rsa'); +var client = jwksClient({ + jwksUri: 'https://sandrino.auth0.com/.well-known/jwks.json' +}); +function getKey(header, callback){ + client.getSigningKey(header.kid, function(err, key) { + var signingKey = key.publicKey || key.rsaPublicKey; + callback(null, signingKey); + }); +} + +jwt.verify(token, getKey, options, function(err, decoded) { + console.log(decoded.foo) // bar +}); + +``` + +
+Need to peek into a JWT without verifying it? (Click to expand) + +### jwt.decode(token [, options]) + +(Synchronous) Returns the decoded payload without verifying if the signature is valid. + +> __Warning:__ This will __not__ verify whether the signature is valid. You should __not__ use this for untrusted messages. You most likely want to use `jwt.verify` instead. + +> __Warning:__ When the token comes from an untrusted source (e.g. user input or external request), the returned decoded payload should be treated like any other user input; please make sure to sanitize and only work with properties that are expected + + +`token` is the JsonWebToken string + +`options`: + +* `json`: force JSON.parse on the payload even if the header doesn't contain `"typ":"JWT"`. +* `complete`: return an object with the decoded payload and header. + +Example + +```js +// get the decoded payload ignoring signature, no secretOrPrivateKey needed +var decoded = jwt.decode(token); + +// get the decoded payload and header +var decoded = jwt.decode(token, {complete: true}); +console.log(decoded.header); +console.log(decoded.payload) +``` + +
+ +## Errors & Codes +Possible thrown errors during verification. +Error is the first argument of the verification callback. + +### TokenExpiredError + +Thrown error if the token is expired. + +Error object: + +* name: 'TokenExpiredError' +* message: 'jwt expired' +* expiredAt: [ExpDate] + +```js +jwt.verify(token, 'shhhhh', function(err, decoded) { + if (err) { + /* + err = { + name: 'TokenExpiredError', + message: 'jwt expired', + expiredAt: 1408621000 + } + */ + } +}); +``` + +### JsonWebTokenError +Error object: + +* name: 'JsonWebTokenError' +* message: + * 'invalid token' - the header or payload could not be parsed + * 'jwt malformed' - the token does not have three components (delimited by a `.`) + * 'jwt signature is required' + * 'invalid signature' + * 'jwt audience invalid. expected: [OPTIONS AUDIENCE]' + * 'jwt issuer invalid. expected: [OPTIONS ISSUER]' + * 'jwt id invalid. expected: [OPTIONS JWT ID]' + * 'jwt subject invalid. expected: [OPTIONS SUBJECT]' + +```js +jwt.verify(token, 'shhhhh', function(err, decoded) { + if (err) { + /* + err = { + name: 'JsonWebTokenError', + message: 'jwt malformed' + } + */ + } +}); +``` + +### NotBeforeError +Thrown if current time is before the nbf claim. + +Error object: + +* name: 'NotBeforeError' +* message: 'jwt not active' +* date: 2018-10-04T16:10:44.000Z + +```js +jwt.verify(token, 'shhhhh', function(err, decoded) { + if (err) { + /* + err = { + name: 'NotBeforeError', + message: 'jwt not active', + date: 2018-10-04T16:10:44.000Z + } + */ + } +}); +``` + + +## Algorithms supported + +Array of supported algorithms. The following algorithms are currently supported. + +| alg Parameter Value | Digital Signature or MAC Algorithm | +|---------------------|------------------------------------------------------------------------| +| HS256 | HMAC using SHA-256 hash algorithm | +| HS384 | HMAC using SHA-384 hash algorithm | +| HS512 | HMAC using SHA-512 hash algorithm | +| RS256 | RSASSA-PKCS1-v1_5 using SHA-256 hash algorithm | +| RS384 | RSASSA-PKCS1-v1_5 using SHA-384 hash algorithm | +| RS512 | RSASSA-PKCS1-v1_5 using SHA-512 hash algorithm | +| PS256 | RSASSA-PSS using SHA-256 hash algorithm (only node ^6.12.0 OR >=8.0.0) | +| PS384 | RSASSA-PSS using SHA-384 hash algorithm (only node ^6.12.0 OR >=8.0.0) | +| PS512 | RSASSA-PSS using SHA-512 hash algorithm (only node ^6.12.0 OR >=8.0.0) | +| ES256 | ECDSA using P-256 curve and SHA-256 hash algorithm | +| ES384 | ECDSA using P-384 curve and SHA-384 hash algorithm | +| ES512 | ECDSA using P-521 curve and SHA-512 hash algorithm | +| none | No digital signature or MAC value included | + +## Refreshing JWTs + +First of all, we recommend you to think carefully if auto-refreshing a JWT will not introduce any vulnerability in your system. + +We are not comfortable including this as part of the library, however, you can take a look at [this example](https://gist.github.com/ziluvatar/a3feb505c4c0ec37059054537b38fc48) to show how this could be accomplished. +Apart from that example there are [an issue](https://github.com/auth0/node-jsonwebtoken/issues/122) and [a pull request](https://github.com/auth0/node-jsonwebtoken/pull/172) to get more knowledge about this topic. + +# TODO + +* X.509 certificate chain is not checked + +## Issue Reporting + +If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. + +## Author + +[Auth0](https://auth0.com) + +## License + +This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. diff --git a/node_modules/jsonwebtoken/decode.js b/node_modules/jsonwebtoken/decode.js new file mode 100644 index 0000000..8fe1adc --- /dev/null +++ b/node_modules/jsonwebtoken/decode.js @@ -0,0 +1,30 @@ +var jws = require('jws'); + +module.exports = function (jwt, options) { + options = options || {}; + var decoded = jws.decode(jwt, options); + if (!decoded) { return null; } + var payload = decoded.payload; + + //try parse the payload + if(typeof payload === 'string') { + try { + var obj = JSON.parse(payload); + if(obj !== null && typeof obj === 'object') { + payload = obj; + } + } catch (e) { } + } + + //return header if `complete` option is enabled. header includes claims + //such as `kid` and `alg` used to select the key within a JWKS needed to + //verify the signature + if (options.complete === true) { + return { + header: decoded.header, + payload: payload, + signature: decoded.signature + }; + } + return payload; +}; diff --git a/node_modules/jsonwebtoken/index.js b/node_modules/jsonwebtoken/index.js new file mode 100644 index 0000000..161eb2d --- /dev/null +++ b/node_modules/jsonwebtoken/index.js @@ -0,0 +1,8 @@ +module.exports = { + decode: require('./decode'), + verify: require('./verify'), + sign: require('./sign'), + JsonWebTokenError: require('./lib/JsonWebTokenError'), + NotBeforeError: require('./lib/NotBeforeError'), + TokenExpiredError: require('./lib/TokenExpiredError'), +}; diff --git a/node_modules/jsonwebtoken/lib/JsonWebTokenError.js b/node_modules/jsonwebtoken/lib/JsonWebTokenError.js new file mode 100644 index 0000000..e068222 --- /dev/null +++ b/node_modules/jsonwebtoken/lib/JsonWebTokenError.js @@ -0,0 +1,14 @@ +var JsonWebTokenError = function (message, error) { + Error.call(this, message); + if(Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + this.name = 'JsonWebTokenError'; + this.message = message; + if (error) this.inner = error; +}; + +JsonWebTokenError.prototype = Object.create(Error.prototype); +JsonWebTokenError.prototype.constructor = JsonWebTokenError; + +module.exports = JsonWebTokenError; diff --git a/node_modules/jsonwebtoken/lib/NotBeforeError.js b/node_modules/jsonwebtoken/lib/NotBeforeError.js new file mode 100644 index 0000000..7b30084 --- /dev/null +++ b/node_modules/jsonwebtoken/lib/NotBeforeError.js @@ -0,0 +1,13 @@ +var JsonWebTokenError = require('./JsonWebTokenError'); + +var NotBeforeError = function (message, date) { + JsonWebTokenError.call(this, message); + this.name = 'NotBeforeError'; + this.date = date; +}; + +NotBeforeError.prototype = Object.create(JsonWebTokenError.prototype); + +NotBeforeError.prototype.constructor = NotBeforeError; + +module.exports = NotBeforeError; \ No newline at end of file diff --git a/node_modules/jsonwebtoken/lib/TokenExpiredError.js b/node_modules/jsonwebtoken/lib/TokenExpiredError.js new file mode 100644 index 0000000..abb704f --- /dev/null +++ b/node_modules/jsonwebtoken/lib/TokenExpiredError.js @@ -0,0 +1,13 @@ +var JsonWebTokenError = require('./JsonWebTokenError'); + +var TokenExpiredError = function (message, expiredAt) { + JsonWebTokenError.call(this, message); + this.name = 'TokenExpiredError'; + this.expiredAt = expiredAt; +}; + +TokenExpiredError.prototype = Object.create(JsonWebTokenError.prototype); + +TokenExpiredError.prototype.constructor = TokenExpiredError; + +module.exports = TokenExpiredError; \ No newline at end of file diff --git a/node_modules/jsonwebtoken/lib/asymmetricKeyDetailsSupported.js b/node_modules/jsonwebtoken/lib/asymmetricKeyDetailsSupported.js new file mode 100644 index 0000000..a6ede56 --- /dev/null +++ b/node_modules/jsonwebtoken/lib/asymmetricKeyDetailsSupported.js @@ -0,0 +1,3 @@ +const semver = require('semver'); + +module.exports = semver.satisfies(process.version, '>=15.7.0'); diff --git a/node_modules/jsonwebtoken/lib/psSupported.js b/node_modules/jsonwebtoken/lib/psSupported.js new file mode 100644 index 0000000..8c04144 --- /dev/null +++ b/node_modules/jsonwebtoken/lib/psSupported.js @@ -0,0 +1,3 @@ +var semver = require('semver'); + +module.exports = semver.satisfies(process.version, '^6.12.0 || >=8.0.0'); diff --git a/node_modules/jsonwebtoken/lib/rsaPssKeyDetailsSupported.js b/node_modules/jsonwebtoken/lib/rsaPssKeyDetailsSupported.js new file mode 100644 index 0000000..7fcf368 --- /dev/null +++ b/node_modules/jsonwebtoken/lib/rsaPssKeyDetailsSupported.js @@ -0,0 +1,3 @@ +const semver = require('semver'); + +module.exports = semver.satisfies(process.version, '>=16.9.0'); diff --git a/node_modules/jsonwebtoken/lib/timespan.js b/node_modules/jsonwebtoken/lib/timespan.js new file mode 100644 index 0000000..e509869 --- /dev/null +++ b/node_modules/jsonwebtoken/lib/timespan.js @@ -0,0 +1,18 @@ +var ms = require('ms'); + +module.exports = function (time, iat) { + var timestamp = iat || Math.floor(Date.now() / 1000); + + if (typeof time === 'string') { + var milliseconds = ms(time); + if (typeof milliseconds === 'undefined') { + return; + } + return Math.floor(timestamp + milliseconds / 1000); + } else if (typeof time === 'number') { + return timestamp + time; + } else { + return; + } + +}; \ No newline at end of file diff --git a/node_modules/jsonwebtoken/lib/validateAsymmetricKey.js b/node_modules/jsonwebtoken/lib/validateAsymmetricKey.js new file mode 100644 index 0000000..c10340b --- /dev/null +++ b/node_modules/jsonwebtoken/lib/validateAsymmetricKey.js @@ -0,0 +1,66 @@ +const ASYMMETRIC_KEY_DETAILS_SUPPORTED = require('./asymmetricKeyDetailsSupported'); +const RSA_PSS_KEY_DETAILS_SUPPORTED = require('./rsaPssKeyDetailsSupported'); + +const allowedAlgorithmsForKeys = { + 'ec': ['ES256', 'ES384', 'ES512'], + 'rsa': ['RS256', 'PS256', 'RS384', 'PS384', 'RS512', 'PS512'], + 'rsa-pss': ['PS256', 'PS384', 'PS512'] +}; + +const allowedCurves = { + ES256: 'prime256v1', + ES384: 'secp384r1', + ES512: 'secp521r1', +}; + +module.exports = function(algorithm, key) { + if (!algorithm || !key) return; + + const keyType = key.asymmetricKeyType; + if (!keyType) return; + + const allowedAlgorithms = allowedAlgorithmsForKeys[keyType]; + + if (!allowedAlgorithms) { + throw new Error(`Unknown key type "${keyType}".`); + } + + if (!allowedAlgorithms.includes(algorithm)) { + throw new Error(`"alg" parameter for "${keyType}" key type must be one of: ${allowedAlgorithms.join(', ')}.`) + } + + /* + * Ignore the next block from test coverage because it gets executed + * conditionally depending on the Node version. Not ignoring it would + * prevent us from reaching the target % of coverage for versions of + * Node under 15.7.0. + */ + /* istanbul ignore next */ + if (ASYMMETRIC_KEY_DETAILS_SUPPORTED) { + switch (keyType) { + case 'ec': + const keyCurve = key.asymmetricKeyDetails.namedCurve; + const allowedCurve = allowedCurves[algorithm]; + + if (keyCurve !== allowedCurve) { + throw new Error(`"alg" parameter "${algorithm}" requires curve "${allowedCurve}".`); + } + break; + + case 'rsa-pss': + if (RSA_PSS_KEY_DETAILS_SUPPORTED) { + const length = parseInt(algorithm.slice(-3), 10); + const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = key.asymmetricKeyDetails; + + if (hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm) { + throw new Error(`Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" ${algorithm}.`); + } + + if (saltLength !== undefined && saltLength > length >> 3) { + throw new Error(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${algorithm}.`) + } + } + break; + } + } +} diff --git a/node_modules/jsonwebtoken/node_modules/ms/index.js b/node_modules/jsonwebtoken/node_modules/ms/index.js new file mode 100644 index 0000000..ea734fb --- /dev/null +++ b/node_modules/jsonwebtoken/node_modules/ms/index.js @@ -0,0 +1,162 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var w = d * 7; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + +module.exports = function (val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isFinite(val)) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'weeks': + case 'week': + case 'w': + return n * w; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtShort(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return Math.round(ms / d) + 'd'; + } + if (msAbs >= h) { + return Math.round(ms / h) + 'h'; + } + if (msAbs >= m) { + return Math.round(ms / m) + 'm'; + } + if (msAbs >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtLong(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return plural(ms, msAbs, d, 'day'); + } + if (msAbs >= h) { + return plural(ms, msAbs, h, 'hour'); + } + if (msAbs >= m) { + return plural(ms, msAbs, m, 'minute'); + } + if (msAbs >= s) { + return plural(ms, msAbs, s, 'second'); + } + return ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5; + return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); +} diff --git a/node_modules/jsonwebtoken/node_modules/ms/license.md b/node_modules/jsonwebtoken/node_modules/ms/license.md new file mode 100644 index 0000000..fa5d39b --- /dev/null +++ b/node_modules/jsonwebtoken/node_modules/ms/license.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Vercel, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/jsonwebtoken/node_modules/ms/package.json b/node_modules/jsonwebtoken/node_modules/ms/package.json new file mode 100644 index 0000000..4997189 --- /dev/null +++ b/node_modules/jsonwebtoken/node_modules/ms/package.json @@ -0,0 +1,38 @@ +{ + "name": "ms", + "version": "2.1.3", + "description": "Tiny millisecond conversion utility", + "repository": "vercel/ms", + "main": "./index", + "files": [ + "index.js" + ], + "scripts": { + "precommit": "lint-staged", + "lint": "eslint lib/* bin/*", + "test": "mocha tests.js" + }, + "eslintConfig": { + "extends": "eslint:recommended", + "env": { + "node": true, + "es6": true + } + }, + "lint-staged": { + "*.js": [ + "npm run lint", + "prettier --single-quote --write", + "git add" + ] + }, + "license": "MIT", + "devDependencies": { + "eslint": "4.18.2", + "expect.js": "0.3.1", + "husky": "0.14.3", + "lint-staged": "5.0.0", + "mocha": "4.0.1", + "prettier": "2.0.5" + } +} diff --git a/node_modules/jsonwebtoken/node_modules/ms/readme.md b/node_modules/jsonwebtoken/node_modules/ms/readme.md new file mode 100644 index 0000000..0fc1abb --- /dev/null +++ b/node_modules/jsonwebtoken/node_modules/ms/readme.md @@ -0,0 +1,59 @@ +# ms + +![CI](https://github.com/vercel/ms/workflows/CI/badge.svg) + +Use this package to easily convert various time formats to milliseconds. + +## Examples + +```js +ms('2 days') // 172800000 +ms('1d') // 86400000 +ms('10h') // 36000000 +ms('2.5 hrs') // 9000000 +ms('2h') // 7200000 +ms('1m') // 60000 +ms('5s') // 5000 +ms('1y') // 31557600000 +ms('100') // 100 +ms('-3 days') // -259200000 +ms('-1h') // -3600000 +ms('-200') // -200 +``` + +### Convert from Milliseconds + +```js +ms(60000) // "1m" +ms(2 * 60000) // "2m" +ms(-3 * 60000) // "-3m" +ms(ms('10 hours')) // "10h" +``` + +### Time Format Written-Out + +```js +ms(60000, { long: true }) // "1 minute" +ms(2 * 60000, { long: true }) // "2 minutes" +ms(-3 * 60000, { long: true }) // "-3 minutes" +ms(ms('10 hours'), { long: true }) // "10 hours" +``` + +## Features + +- Works both in [Node.js](https://nodejs.org) and in the browser +- If a number is supplied to `ms`, a string with a unit is returned +- If a string that contains the number is supplied, it returns it as a number (e.g.: it returns `100` for `'100'`) +- If you pass a string with a number and a valid unit, the number of equivalent milliseconds is returned + +## Related Packages + +- [ms.macro](https://github.com/knpwrs/ms.macro) - Run `ms` as a macro at build-time. + +## Caught a Bug? + +1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device +2. Link the package to the global module directory: `npm link` +3. Within the module you want to test your local development instance of ms, just link it to the dependencies: `npm link ms`. Instead of the default one from npm, Node.js will now use your clone of ms! + +As always, you can run the tests using: `npm test` diff --git a/node_modules/jsonwebtoken/package.json b/node_modules/jsonwebtoken/package.json new file mode 100644 index 0000000..eab30c0 --- /dev/null +++ b/node_modules/jsonwebtoken/package.json @@ -0,0 +1,70 @@ +{ + "name": "jsonwebtoken", + "version": "9.0.3", + "description": "JSON Web Token implementation (symmetric and asymmetric)", + "main": "index.js", + "nyc": { + "check-coverage": true, + "lines": 95, + "statements": 95, + "functions": 100, + "branches": 95, + "exclude": [ + "./test/**" + ], + "reporter": [ + "json", + "lcov", + "text-summary" + ] + }, + "scripts": { + "lint": "eslint .", + "coverage": "nyc mocha --use_strict", + "test": "mocha" + }, + "repository": { + "type": "git", + "url": "https://github.com/auth0/node-jsonwebtoken" + }, + "keywords": [ + "jwt" + ], + "author": "auth0", + "license": "MIT", + "bugs": { + "url": "https://github.com/auth0/node-jsonwebtoken/issues" + }, + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "devDependencies": { + "atob": "^2.1.2", + "chai": "^4.1.2", + "conventional-changelog": "~1.1.0", + "eslint": "^4.19.1", + "mocha": "^5.2.0", + "nsp": "^2.6.2", + "nyc": "^11.9.0", + "sinon": "^6.0.0" + }, + "engines": { + "npm": ">=6", + "node": ">=12" + }, + "files": [ + "lib", + "decode.js", + "sign.js", + "verify.js" + ] +} diff --git a/node_modules/jsonwebtoken/sign.js b/node_modules/jsonwebtoken/sign.js new file mode 100644 index 0000000..82bf526 --- /dev/null +++ b/node_modules/jsonwebtoken/sign.js @@ -0,0 +1,253 @@ +const timespan = require('./lib/timespan'); +const PS_SUPPORTED = require('./lib/psSupported'); +const validateAsymmetricKey = require('./lib/validateAsymmetricKey'); +const jws = require('jws'); +const includes = require('lodash.includes'); +const isBoolean = require('lodash.isboolean'); +const isInteger = require('lodash.isinteger'); +const isNumber = require('lodash.isnumber'); +const isPlainObject = require('lodash.isplainobject'); +const isString = require('lodash.isstring'); +const once = require('lodash.once'); +const { KeyObject, createSecretKey, createPrivateKey } = require('crypto') + +const SUPPORTED_ALGS = ['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512', 'none']; +if (PS_SUPPORTED) { + SUPPORTED_ALGS.splice(3, 0, 'PS256', 'PS384', 'PS512'); +} + +const sign_options_schema = { + expiresIn: { isValid: function(value) { return isInteger(value) || (isString(value) && value); }, message: '"expiresIn" should be a number of seconds or string representing a timespan' }, + notBefore: { isValid: function(value) { return isInteger(value) || (isString(value) && value); }, message: '"notBefore" should be a number of seconds or string representing a timespan' }, + audience: { isValid: function(value) { return isString(value) || Array.isArray(value); }, message: '"audience" must be a string or array' }, + algorithm: { isValid: includes.bind(null, SUPPORTED_ALGS), message: '"algorithm" must be a valid string enum value' }, + header: { isValid: isPlainObject, message: '"header" must be an object' }, + encoding: { isValid: isString, message: '"encoding" must be a string' }, + issuer: { isValid: isString, message: '"issuer" must be a string' }, + subject: { isValid: isString, message: '"subject" must be a string' }, + jwtid: { isValid: isString, message: '"jwtid" must be a string' }, + noTimestamp: { isValid: isBoolean, message: '"noTimestamp" must be a boolean' }, + keyid: { isValid: isString, message: '"keyid" must be a string' }, + mutatePayload: { isValid: isBoolean, message: '"mutatePayload" must be a boolean' }, + allowInsecureKeySizes: { isValid: isBoolean, message: '"allowInsecureKeySizes" must be a boolean'}, + allowInvalidAsymmetricKeyTypes: { isValid: isBoolean, message: '"allowInvalidAsymmetricKeyTypes" must be a boolean'} +}; + +const registered_claims_schema = { + iat: { isValid: isNumber, message: '"iat" should be a number of seconds' }, + exp: { isValid: isNumber, message: '"exp" should be a number of seconds' }, + nbf: { isValid: isNumber, message: '"nbf" should be a number of seconds' } +}; + +function validate(schema, allowUnknown, object, parameterName) { + if (!isPlainObject(object)) { + throw new Error('Expected "' + parameterName + '" to be a plain object.'); + } + Object.keys(object) + .forEach(function(key) { + const validator = schema[key]; + if (!validator) { + if (!allowUnknown) { + throw new Error('"' + key + '" is not allowed in "' + parameterName + '"'); + } + return; + } + if (!validator.isValid(object[key])) { + throw new Error(validator.message); + } + }); +} + +function validateOptions(options) { + return validate(sign_options_schema, false, options, 'options'); +} + +function validatePayload(payload) { + return validate(registered_claims_schema, true, payload, 'payload'); +} + +const options_to_payload = { + 'audience': 'aud', + 'issuer': 'iss', + 'subject': 'sub', + 'jwtid': 'jti' +}; + +const options_for_objects = [ + 'expiresIn', + 'notBefore', + 'noTimestamp', + 'audience', + 'issuer', + 'subject', + 'jwtid', +]; + +module.exports = function (payload, secretOrPrivateKey, options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } else { + options = options || {}; + } + + const isObjectPayload = typeof payload === 'object' && + !Buffer.isBuffer(payload); + + const header = Object.assign({ + alg: options.algorithm || 'HS256', + typ: isObjectPayload ? 'JWT' : undefined, + kid: options.keyid + }, options.header); + + function failure(err) { + if (callback) { + return callback(err); + } + throw err; + } + + if (!secretOrPrivateKey && options.algorithm !== 'none') { + return failure(new Error('secretOrPrivateKey must have a value')); + } + + if (secretOrPrivateKey != null && !(secretOrPrivateKey instanceof KeyObject)) { + try { + secretOrPrivateKey = createPrivateKey(secretOrPrivateKey) + } catch (_) { + try { + secretOrPrivateKey = createSecretKey(typeof secretOrPrivateKey === 'string' ? Buffer.from(secretOrPrivateKey) : secretOrPrivateKey) + } catch (_) { + return failure(new Error('secretOrPrivateKey is not valid key material')); + } + } + } + + if (header.alg.startsWith('HS') && secretOrPrivateKey.type !== 'secret') { + return failure(new Error((`secretOrPrivateKey must be a symmetric key when using ${header.alg}`))) + } else if (/^(?:RS|PS|ES)/.test(header.alg)) { + if (secretOrPrivateKey.type !== 'private') { + return failure(new Error((`secretOrPrivateKey must be an asymmetric key when using ${header.alg}`))) + } + if (!options.allowInsecureKeySizes && + !header.alg.startsWith('ES') && + secretOrPrivateKey.asymmetricKeyDetails !== undefined && //KeyObject.asymmetricKeyDetails is supported in Node 15+ + secretOrPrivateKey.asymmetricKeyDetails.modulusLength < 2048) { + return failure(new Error(`secretOrPrivateKey has a minimum key size of 2048 bits for ${header.alg}`)); + } + } + + if (typeof payload === 'undefined') { + return failure(new Error('payload is required')); + } else if (isObjectPayload) { + try { + validatePayload(payload); + } + catch (error) { + return failure(error); + } + if (!options.mutatePayload) { + payload = Object.assign({},payload); + } + } else { + const invalid_options = options_for_objects.filter(function (opt) { + return typeof options[opt] !== 'undefined'; + }); + + if (invalid_options.length > 0) { + return failure(new Error('invalid ' + invalid_options.join(',') + ' option for ' + (typeof payload ) + ' payload')); + } + } + + if (typeof payload.exp !== 'undefined' && typeof options.expiresIn !== 'undefined') { + return failure(new Error('Bad "options.expiresIn" option the payload already has an "exp" property.')); + } + + if (typeof payload.nbf !== 'undefined' && typeof options.notBefore !== 'undefined') { + return failure(new Error('Bad "options.notBefore" option the payload already has an "nbf" property.')); + } + + try { + validateOptions(options); + } + catch (error) { + return failure(error); + } + + if (!options.allowInvalidAsymmetricKeyTypes) { + try { + validateAsymmetricKey(header.alg, secretOrPrivateKey); + } catch (error) { + return failure(error); + } + } + + const timestamp = payload.iat || Math.floor(Date.now() / 1000); + + if (options.noTimestamp) { + delete payload.iat; + } else if (isObjectPayload) { + payload.iat = timestamp; + } + + if (typeof options.notBefore !== 'undefined') { + try { + payload.nbf = timespan(options.notBefore, timestamp); + } + catch (err) { + return failure(err); + } + if (typeof payload.nbf === 'undefined') { + return failure(new Error('"notBefore" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60')); + } + } + + if (typeof options.expiresIn !== 'undefined' && typeof payload === 'object') { + try { + payload.exp = timespan(options.expiresIn, timestamp); + } + catch (err) { + return failure(err); + } + if (typeof payload.exp === 'undefined') { + return failure(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60')); + } + } + + Object.keys(options_to_payload).forEach(function (key) { + const claim = options_to_payload[key]; + if (typeof options[key] !== 'undefined') { + if (typeof payload[claim] !== 'undefined') { + return failure(new Error('Bad "options.' + key + '" option. The payload already has an "' + claim + '" property.')); + } + payload[claim] = options[key]; + } + }); + + const encoding = options.encoding || 'utf8'; + + if (typeof callback === 'function') { + callback = callback && once(callback); + + jws.createSign({ + header: header, + privateKey: secretOrPrivateKey, + payload: payload, + encoding: encoding + }).once('error', callback) + .once('done', function (signature) { + // TODO: Remove in favor of the modulus length check before signing once node 15+ is the minimum supported version + if(!options.allowInsecureKeySizes && /^(?:RS|PS)/.test(header.alg) && signature.length < 256) { + return callback(new Error(`secretOrPrivateKey has a minimum key size of 2048 bits for ${header.alg}`)) + } + callback(null, signature); + }); + } else { + let signature = jws.sign({header: header, payload: payload, secret: secretOrPrivateKey, encoding: encoding}); + // TODO: Remove in favor of the modulus length check before signing once node 15+ is the minimum supported version + if(!options.allowInsecureKeySizes && /^(?:RS|PS)/.test(header.alg) && signature.length < 256) { + throw new Error(`secretOrPrivateKey has a minimum key size of 2048 bits for ${header.alg}`) + } + return signature + } +}; diff --git a/node_modules/jsonwebtoken/verify.js b/node_modules/jsonwebtoken/verify.js new file mode 100644 index 0000000..cdbfdc4 --- /dev/null +++ b/node_modules/jsonwebtoken/verify.js @@ -0,0 +1,263 @@ +const JsonWebTokenError = require('./lib/JsonWebTokenError'); +const NotBeforeError = require('./lib/NotBeforeError'); +const TokenExpiredError = require('./lib/TokenExpiredError'); +const decode = require('./decode'); +const timespan = require('./lib/timespan'); +const validateAsymmetricKey = require('./lib/validateAsymmetricKey'); +const PS_SUPPORTED = require('./lib/psSupported'); +const jws = require('jws'); +const {KeyObject, createSecretKey, createPublicKey} = require("crypto"); + +const PUB_KEY_ALGS = ['RS256', 'RS384', 'RS512']; +const EC_KEY_ALGS = ['ES256', 'ES384', 'ES512']; +const RSA_KEY_ALGS = ['RS256', 'RS384', 'RS512']; +const HS_ALGS = ['HS256', 'HS384', 'HS512']; + +if (PS_SUPPORTED) { + PUB_KEY_ALGS.splice(PUB_KEY_ALGS.length, 0, 'PS256', 'PS384', 'PS512'); + RSA_KEY_ALGS.splice(RSA_KEY_ALGS.length, 0, 'PS256', 'PS384', 'PS512'); +} + +module.exports = function (jwtString, secretOrPublicKey, options, callback) { + if ((typeof options === 'function') && !callback) { + callback = options; + options = {}; + } + + if (!options) { + options = {}; + } + + //clone this object since we are going to mutate it. + options = Object.assign({}, options); + + let done; + + if (callback) { + done = callback; + } else { + done = function(err, data) { + if (err) throw err; + return data; + }; + } + + if (options.clockTimestamp && typeof options.clockTimestamp !== 'number') { + return done(new JsonWebTokenError('clockTimestamp must be a number')); + } + + if (options.nonce !== undefined && (typeof options.nonce !== 'string' || options.nonce.trim() === '')) { + return done(new JsonWebTokenError('nonce must be a non-empty string')); + } + + if (options.allowInvalidAsymmetricKeyTypes !== undefined && typeof options.allowInvalidAsymmetricKeyTypes !== 'boolean') { + return done(new JsonWebTokenError('allowInvalidAsymmetricKeyTypes must be a boolean')); + } + + const clockTimestamp = options.clockTimestamp || Math.floor(Date.now() / 1000); + + if (!jwtString){ + return done(new JsonWebTokenError('jwt must be provided')); + } + + if (typeof jwtString !== 'string') { + return done(new JsonWebTokenError('jwt must be a string')); + } + + const parts = jwtString.split('.'); + + if (parts.length !== 3){ + return done(new JsonWebTokenError('jwt malformed')); + } + + let decodedToken; + + try { + decodedToken = decode(jwtString, { complete: true }); + } catch(err) { + return done(err); + } + + if (!decodedToken) { + return done(new JsonWebTokenError('invalid token')); + } + + const header = decodedToken.header; + let getSecret; + + if(typeof secretOrPublicKey === 'function') { + if(!callback) { + return done(new JsonWebTokenError('verify must be called asynchronous if secret or public key is provided as a callback')); + } + + getSecret = secretOrPublicKey; + } + else { + getSecret = function(header, secretCallback) { + return secretCallback(null, secretOrPublicKey); + }; + } + + return getSecret(header, function(err, secretOrPublicKey) { + if(err) { + return done(new JsonWebTokenError('error in secret or public key callback: ' + err.message)); + } + + const hasSignature = parts[2].trim() !== ''; + + if (!hasSignature && secretOrPublicKey){ + return done(new JsonWebTokenError('jwt signature is required')); + } + + if (hasSignature && !secretOrPublicKey) { + return done(new JsonWebTokenError('secret or public key must be provided')); + } + + if (!hasSignature && !options.algorithms) { + return done(new JsonWebTokenError('please specify "none" in "algorithms" to verify unsigned tokens')); + } + + if (secretOrPublicKey != null && !(secretOrPublicKey instanceof KeyObject)) { + try { + secretOrPublicKey = createPublicKey(secretOrPublicKey); + } catch (_) { + try { + secretOrPublicKey = createSecretKey(typeof secretOrPublicKey === 'string' ? Buffer.from(secretOrPublicKey) : secretOrPublicKey); + } catch (_) { + return done(new JsonWebTokenError('secretOrPublicKey is not valid key material')) + } + } + } + + if (!options.algorithms) { + if (secretOrPublicKey.type === 'secret') { + options.algorithms = HS_ALGS; + } else if (['rsa', 'rsa-pss'].includes(secretOrPublicKey.asymmetricKeyType)) { + options.algorithms = RSA_KEY_ALGS + } else if (secretOrPublicKey.asymmetricKeyType === 'ec') { + options.algorithms = EC_KEY_ALGS + } else { + options.algorithms = PUB_KEY_ALGS + } + } + + if (options.algorithms.indexOf(decodedToken.header.alg) === -1) { + return done(new JsonWebTokenError('invalid algorithm')); + } + + if (header.alg.startsWith('HS') && secretOrPublicKey.type !== 'secret') { + return done(new JsonWebTokenError((`secretOrPublicKey must be a symmetric key when using ${header.alg}`))) + } else if (/^(?:RS|PS|ES)/.test(header.alg) && secretOrPublicKey.type !== 'public') { + return done(new JsonWebTokenError((`secretOrPublicKey must be an asymmetric key when using ${header.alg}`))) + } + + if (!options.allowInvalidAsymmetricKeyTypes) { + try { + validateAsymmetricKey(header.alg, secretOrPublicKey); + } catch (e) { + return done(e); + } + } + + let valid; + + try { + valid = jws.verify(jwtString, decodedToken.header.alg, secretOrPublicKey); + } catch (e) { + return done(e); + } + + if (!valid) { + return done(new JsonWebTokenError('invalid signature')); + } + + const payload = decodedToken.payload; + + if (typeof payload.nbf !== 'undefined' && !options.ignoreNotBefore) { + if (typeof payload.nbf !== 'number') { + return done(new JsonWebTokenError('invalid nbf value')); + } + if (payload.nbf > clockTimestamp + (options.clockTolerance || 0)) { + return done(new NotBeforeError('jwt not active', new Date(payload.nbf * 1000))); + } + } + + if (typeof payload.exp !== 'undefined' && !options.ignoreExpiration) { + if (typeof payload.exp !== 'number') { + return done(new JsonWebTokenError('invalid exp value')); + } + if (clockTimestamp >= payload.exp + (options.clockTolerance || 0)) { + return done(new TokenExpiredError('jwt expired', new Date(payload.exp * 1000))); + } + } + + if (options.audience) { + const audiences = Array.isArray(options.audience) ? options.audience : [options.audience]; + const target = Array.isArray(payload.aud) ? payload.aud : [payload.aud]; + + const match = target.some(function (targetAudience) { + return audiences.some(function (audience) { + return audience instanceof RegExp ? audience.test(targetAudience) : audience === targetAudience; + }); + }); + + if (!match) { + return done(new JsonWebTokenError('jwt audience invalid. expected: ' + audiences.join(' or '))); + } + } + + if (options.issuer) { + const invalid_issuer = + (typeof options.issuer === 'string' && payload.iss !== options.issuer) || + (Array.isArray(options.issuer) && options.issuer.indexOf(payload.iss) === -1); + + if (invalid_issuer) { + return done(new JsonWebTokenError('jwt issuer invalid. expected: ' + options.issuer)); + } + } + + if (options.subject) { + if (payload.sub !== options.subject) { + return done(new JsonWebTokenError('jwt subject invalid. expected: ' + options.subject)); + } + } + + if (options.jwtid) { + if (payload.jti !== options.jwtid) { + return done(new JsonWebTokenError('jwt jwtid invalid. expected: ' + options.jwtid)); + } + } + + if (options.nonce) { + if (payload.nonce !== options.nonce) { + return done(new JsonWebTokenError('jwt nonce invalid. expected: ' + options.nonce)); + } + } + + if (options.maxAge) { + if (typeof payload.iat !== 'number') { + return done(new JsonWebTokenError('iat required when maxAge is specified')); + } + + const maxAgeTimestamp = timespan(options.maxAge, payload.iat); + if (typeof maxAgeTimestamp === 'undefined') { + return done(new JsonWebTokenError('"maxAge" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60')); + } + if (clockTimestamp >= maxAgeTimestamp + (options.clockTolerance || 0)) { + return done(new TokenExpiredError('maxAge exceeded', new Date(maxAgeTimestamp * 1000))); + } + } + + if (options.complete === true) { + const signature = decodedToken.signature; + + return done(null, { + header: header, + payload: payload, + signature: signature + }); + } + + return done(null, payload); + }); +}; diff --git a/node_modules/jwa/LICENSE b/node_modules/jwa/LICENSE new file mode 100644 index 0000000..caeb849 --- /dev/null +++ b/node_modules/jwa/LICENSE @@ -0,0 +1,17 @@ +Copyright (c) 2013 Brian J. Brennan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/jwa/README.md b/node_modules/jwa/README.md new file mode 100644 index 0000000..09e9648 --- /dev/null +++ b/node_modules/jwa/README.md @@ -0,0 +1,150 @@ +# node-jwa [![Build Status](https://travis-ci.org/brianloveswords/node-jwa.svg?branch=master)](https://travis-ci.org/brianloveswords/node-jwa) + +A +[JSON Web Algorithms](http://tools.ietf.org/id/draft-ietf-jose-json-web-algorithms-08.html) +implementation focusing (exclusively, at this point) on the algorithms necessary for +[JSON Web Signatures](http://self-issued.info/docs/draft-ietf-jose-json-web-signature.html). + +This library supports all of the required, recommended and optional cryptographic algorithms for JWS: + +alg Parameter Value | Digital Signature or MAC Algorithm +----------------|---------------------------- +HS256 | HMAC using SHA-256 hash algorithm +HS384 | HMAC using SHA-384 hash algorithm +HS512 | HMAC using SHA-512 hash algorithm +RS256 | RSASSA using SHA-256 hash algorithm +RS384 | RSASSA using SHA-384 hash algorithm +RS512 | RSASSA using SHA-512 hash algorithm +PS256 | RSASSA-PSS using SHA-256 hash algorithm +PS384 | RSASSA-PSS using SHA-384 hash algorithm +PS512 | RSASSA-PSS using SHA-512 hash algorithm +ES256 | ECDSA using P-256 curve and SHA-256 hash algorithm +ES384 | ECDSA using P-384 curve and SHA-384 hash algorithm +ES512 | ECDSA using P-521 curve and SHA-512 hash algorithm +none | No digital signature or MAC value included + +Please note that PS* only works on Node 6.12+ (excluding 7.x). + +# Requirements + +In order to run the tests, a recent version of OpenSSL is +required. **The version that comes with OS X (OpenSSL 0.9.8r 8 Feb +2011) is not recent enough**, as it does not fully support ECDSA +keys. You'll need to use a version > 1.0.0; I tested with OpenSSL 1.0.1c 10 May 2012. + +# Testing + +To run the tests, do + +```bash +$ npm test +``` + +This will generate a bunch of keypairs to use in testing. If you want to +generate new keypairs, do `make clean` before running `npm test` again. + +## Methodology + +I spawn `openssl dgst -sign` to test OpenSSL sign → JS verify and +`openssl dgst -verify` to test JS sign → OpenSSL verify for each of the +RSA and ECDSA algorithms. + +# Usage + +## jwa(algorithm) + +Creates a new `jwa` object with `sign` and `verify` methods for the +algorithm. Valid values for algorithm can be found in the table above +(`'HS256'`, `'HS384'`, etc) and are case-sensitive. Passing an invalid +algorithm value will throw a `TypeError`. + + +## jwa#sign(input, secretOrPrivateKey) + +Sign some input with either a secret for HMAC algorithms, or a private +key for RSA and ECDSA algorithms. + +If input is not already a string or buffer, `JSON.stringify` will be +called on it to attempt to coerce it. + +For the HMAC algorithm, `secretOrPrivateKey` should be a string or a +buffer. For ECDSA and RSA, the value should be a string representing a +PEM encoded **private** key. + +Output [base64url](http://en.wikipedia.org/wiki/Base64#URL_applications) +formatted. This is for convenience as JWS expects the signature in this +format. If your application needs the output in a different format, +[please open an issue](https://github.com/brianloveswords/node-jwa/issues). In +the meantime, you can use +[brianloveswords/base64url](https://github.com/brianloveswords/base64url) +to decode the signature. + +As of nodejs *v0.11.8*, SPKAC support was introduce. If your nodeJs +version satisfies, then you can pass an object `{ key: '..', passphrase: '...' }` + + +## jwa#verify(input, signature, secretOrPublicKey) + +Verify a signature. Returns `true` or `false`. + +`signature` should be a base64url encoded string. + +For the HMAC algorithm, `secretOrPublicKey` should be a string or a +buffer. For ECDSA and RSA, the value should be a string represented a +PEM encoded **public** key. + + +# Example + +HMAC +```js +const jwa = require('jwa'); + +const hmac = jwa('HS256'); +const input = 'super important stuff'; +const secret = 'shhhhhh'; + +const signature = hmac.sign(input, secret); +hmac.verify(input, signature, secret) // === true +hmac.verify(input, signature, 'trickery!') // === false +``` + +With keys +```js +const fs = require('fs'); +const jwa = require('jwa'); +const privateKey = fs.readFileSync(__dirname + '/ecdsa-p521-private.pem'); +const publicKey = fs.readFileSync(__dirname + '/ecdsa-p521-public.pem'); + +const ecdsa = jwa('ES512'); +const input = 'very important stuff'; + +const signature = ecdsa.sign(input, privateKey); +ecdsa.verify(input, signature, publicKey) // === true +``` +## License + +MIT + +``` +Copyright (c) 2013 Brian J. Brennan + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` diff --git a/node_modules/jwa/index.js b/node_modules/jwa/index.js new file mode 100644 index 0000000..5072c34 --- /dev/null +++ b/node_modules/jwa/index.js @@ -0,0 +1,266 @@ +var Buffer = require('safe-buffer').Buffer; +var crypto = require('crypto'); +var formatEcdsa = require('ecdsa-sig-formatter'); +var util = require('util'); + +var MSG_INVALID_ALGORITHM = '"%s" is not a valid algorithm.\n Supported algorithms are:\n "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".' +var MSG_INVALID_SECRET = 'secret must be a string or buffer'; +var MSG_INVALID_VERIFIER_KEY = 'key must be a string or a buffer'; +var MSG_INVALID_SIGNER_KEY = 'key must be a string, a buffer or an object'; + +var supportsKeyObjects = typeof crypto.createPublicKey === 'function'; +if (supportsKeyObjects) { + MSG_INVALID_VERIFIER_KEY += ' or a KeyObject'; + MSG_INVALID_SECRET += 'or a KeyObject'; +} + +function checkIsPublicKey(key) { + if (Buffer.isBuffer(key)) { + return; + } + + if (typeof key === 'string') { + return; + } + + if (!supportsKeyObjects) { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + + if (typeof key !== 'object') { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + + if (typeof key.type !== 'string') { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + + if (typeof key.asymmetricKeyType !== 'string') { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + + if (typeof key.export !== 'function') { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } +}; + +function checkIsPrivateKey(key) { + if (Buffer.isBuffer(key)) { + return; + } + + if (typeof key === 'string') { + return; + } + + if (typeof key === 'object') { + return; + } + + throw typeError(MSG_INVALID_SIGNER_KEY); +}; + +function checkIsSecretKey(key) { + if (Buffer.isBuffer(key)) { + return; + } + + if (typeof key === 'string') { + return key; + } + + if (!supportsKeyObjects) { + throw typeError(MSG_INVALID_SECRET); + } + + if (typeof key !== 'object') { + throw typeError(MSG_INVALID_SECRET); + } + + if (key.type !== 'secret') { + throw typeError(MSG_INVALID_SECRET); + } + + if (typeof key.export !== 'function') { + throw typeError(MSG_INVALID_SECRET); + } +} + +function fromBase64(base64) { + return base64 + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); +} + +function toBase64(base64url) { + base64url = base64url.toString(); + + var padding = 4 - base64url.length % 4; + if (padding !== 4) { + for (var i = 0; i < padding; ++i) { + base64url += '='; + } + } + + return base64url + .replace(/\-/g, '+') + .replace(/_/g, '/'); +} + +function typeError(template) { + var args = [].slice.call(arguments, 1); + var errMsg = util.format.bind(util, template).apply(null, args); + return new TypeError(errMsg); +} + +function bufferOrString(obj) { + return Buffer.isBuffer(obj) || typeof obj === 'string'; +} + +function normalizeInput(thing) { + if (!bufferOrString(thing)) + thing = JSON.stringify(thing); + return thing; +} + +function createHmacSigner(bits) { + return function sign(thing, secret) { + checkIsSecretKey(secret); + thing = normalizeInput(thing); + var hmac = crypto.createHmac('sha' + bits, secret); + var sig = (hmac.update(thing), hmac.digest('base64')) + return fromBase64(sig); + } +} + +var bufferEqual; +var timingSafeEqual = 'timingSafeEqual' in crypto ? function timingSafeEqual(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + return crypto.timingSafeEqual(a, b) +} : function timingSafeEqual(a, b) { + if (!bufferEqual) { + bufferEqual = require('buffer-equal-constant-time'); + } + + return bufferEqual(a, b) +} + +function createHmacVerifier(bits) { + return function verify(thing, signature, secret) { + var computedSig = createHmacSigner(bits)(thing, secret); + return timingSafeEqual(Buffer.from(signature), Buffer.from(computedSig)); + } +} + +function createKeySigner(bits) { + return function sign(thing, privateKey) { + checkIsPrivateKey(privateKey); + thing = normalizeInput(thing); + // Even though we are specifying "RSA" here, this works with ECDSA + // keys as well. + var signer = crypto.createSign('RSA-SHA' + bits); + var sig = (signer.update(thing), signer.sign(privateKey, 'base64')); + return fromBase64(sig); + } +} + +function createKeyVerifier(bits) { + return function verify(thing, signature, publicKey) { + checkIsPublicKey(publicKey); + thing = normalizeInput(thing); + signature = toBase64(signature); + var verifier = crypto.createVerify('RSA-SHA' + bits); + verifier.update(thing); + return verifier.verify(publicKey, signature, 'base64'); + } +} + +function createPSSKeySigner(bits) { + return function sign(thing, privateKey) { + checkIsPrivateKey(privateKey); + thing = normalizeInput(thing); + var signer = crypto.createSign('RSA-SHA' + bits); + var sig = (signer.update(thing), signer.sign({ + key: privateKey, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST + }, 'base64')); + return fromBase64(sig); + } +} + +function createPSSKeyVerifier(bits) { + return function verify(thing, signature, publicKey) { + checkIsPublicKey(publicKey); + thing = normalizeInput(thing); + signature = toBase64(signature); + var verifier = crypto.createVerify('RSA-SHA' + bits); + verifier.update(thing); + return verifier.verify({ + key: publicKey, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST + }, signature, 'base64'); + } +} + +function createECDSASigner(bits) { + var inner = createKeySigner(bits); + return function sign() { + var signature = inner.apply(null, arguments); + signature = formatEcdsa.derToJose(signature, 'ES' + bits); + return signature; + }; +} + +function createECDSAVerifer(bits) { + var inner = createKeyVerifier(bits); + return function verify(thing, signature, publicKey) { + signature = formatEcdsa.joseToDer(signature, 'ES' + bits).toString('base64'); + var result = inner(thing, signature, publicKey); + return result; + }; +} + +function createNoneSigner() { + return function sign() { + return ''; + } +} + +function createNoneVerifier() { + return function verify(thing, signature) { + return signature === ''; + } +} + +module.exports = function jwa(algorithm) { + var signerFactories = { + hs: createHmacSigner, + rs: createKeySigner, + ps: createPSSKeySigner, + es: createECDSASigner, + none: createNoneSigner, + } + var verifierFactories = { + hs: createHmacVerifier, + rs: createKeyVerifier, + ps: createPSSKeyVerifier, + es: createECDSAVerifer, + none: createNoneVerifier, + } + var match = algorithm.match(/^(RS|PS|ES|HS)(256|384|512)$|^(none)$/); + if (!match) + throw typeError(MSG_INVALID_ALGORITHM, algorithm); + var algo = (match[1] || match[3]).toLowerCase(); + var bits = match[2]; + + return { + sign: signerFactories[algo](bits), + verify: verifierFactories[algo](bits), + } +}; diff --git a/node_modules/jwa/opslevel.yml b/node_modules/jwa/opslevel.yml new file mode 100644 index 0000000..aeeeea7 --- /dev/null +++ b/node_modules/jwa/opslevel.yml @@ -0,0 +1,6 @@ +--- +version: 1 +repository: + owner: iam_protocols + tier: + tags: diff --git a/node_modules/jwa/package.json b/node_modules/jwa/package.json new file mode 100644 index 0000000..fd5824a --- /dev/null +++ b/node_modules/jwa/package.json @@ -0,0 +1,37 @@ +{ + "name": "jwa", + "version": "2.0.1", + "description": "JWA implementation (supports all JWS algorithms)", + "main": "index.js", + "directories": { + "test": "test" + }, + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + }, + "devDependencies": { + "base64url": "^2.0.0", + "jwk-to-pem": "^2.0.1", + "semver": "4.3.6", + "tap": "6.2.0" + }, + "scripts": { + "test": "make test" + }, + "repository": { + "type": "git", + "url": "git://github.com/brianloveswords/node-jwa.git" + }, + "keywords": [ + "jwa", + "jws", + "jwt", + "rsa", + "ecdsa", + "hmac" + ], + "author": "Brian J. Brennan ", + "license": "MIT" +} diff --git a/node_modules/jws/CHANGELOG.md b/node_modules/jws/CHANGELOG.md new file mode 100644 index 0000000..18078df --- /dev/null +++ b/node_modules/jws/CHANGELOG.md @@ -0,0 +1,56 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +## [4.0.1] + +### Changed + +- Fix advisory GHSA-869p-cjfg-cm3x: createSign and createVerify now require + that a non empty secret is provided (via opts.secret, opts.privateKey or opts.key) + when using HMAC algorithms. +- Upgrading JWA version to 2.0.1, adressing a compatibility issue for Node >= 25. + +## [3.2.3] + +### Changed + +- Fix advisory GHSA-869p-cjfg-cm3x: createSign and createVerify now require + that a non empty secret is provided (via opts.secret, opts.privateKey or opts.key) + when using HMAC algorithms. +- Upgrading JWA version to 1.4.2, adressing a compatibility issue for Node >= 25. + +## [3.0.0] + +### Changed + +- **BREAKING**: `jwt.verify` now requires an `algorithm` parameter, and + `jws.createVerify` requires an `algorithm` option. The `"alg"` field + signature headers is ignored. This mitigates a critical security flaw + in the library which would allow an attacker to generate signatures with + arbitrary contents that would be accepted by `jwt.verify`. See + https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/ + for details. + +## [2.0.0] - 2015-01-30 + +### Changed + +- **BREAKING**: Default payload encoding changed from `binary` to + `utf8`. `utf8` is a is a more sensible default than `binary` because + many payloads, as far as I can tell, will contain user-facing + strings that could be in any language. ([6b6de48]) + +- Code reorganization, thanks [@fearphage]! ([7880050]) + +### Added + +- Option in all relevant methods for `encoding`. For those few users + that might be depending on a `binary` encoding of the messages, this + is for them. ([6b6de48]) + +[unreleased]: https://github.com/brianloveswords/node-jws/compare/v2.0.0...HEAD +[2.0.0]: https://github.com/brianloveswords/node-jws/compare/v1.0.1...v2.0.0 +[7880050]: https://github.com/brianloveswords/node-jws/commit/7880050 +[6b6de48]: https://github.com/brianloveswords/node-jws/commit/6b6de48 +[@fearphage]: https://github.com/fearphage diff --git a/node_modules/jws/LICENSE b/node_modules/jws/LICENSE new file mode 100644 index 0000000..caeb849 --- /dev/null +++ b/node_modules/jws/LICENSE @@ -0,0 +1,17 @@ +Copyright (c) 2013 Brian J. Brennan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/jws/index.js b/node_modules/jws/index.js new file mode 100644 index 0000000..8c8da93 --- /dev/null +++ b/node_modules/jws/index.js @@ -0,0 +1,22 @@ +/*global exports*/ +var SignStream = require('./lib/sign-stream'); +var VerifyStream = require('./lib/verify-stream'); + +var ALGORITHMS = [ + 'HS256', 'HS384', 'HS512', + 'RS256', 'RS384', 'RS512', + 'PS256', 'PS384', 'PS512', + 'ES256', 'ES384', 'ES512' +]; + +exports.ALGORITHMS = ALGORITHMS; +exports.sign = SignStream.sign; +exports.verify = VerifyStream.verify; +exports.decode = VerifyStream.decode; +exports.isValid = VerifyStream.isValid; +exports.createSign = function createSign(opts) { + return new SignStream(opts); +}; +exports.createVerify = function createVerify(opts) { + return new VerifyStream(opts); +}; diff --git a/node_modules/jws/lib/data-stream.js b/node_modules/jws/lib/data-stream.js new file mode 100644 index 0000000..3535d31 --- /dev/null +++ b/node_modules/jws/lib/data-stream.js @@ -0,0 +1,55 @@ +/*global module, process*/ +var Buffer = require('safe-buffer').Buffer; +var Stream = require('stream'); +var util = require('util'); + +function DataStream(data) { + this.buffer = null; + this.writable = true; + this.readable = true; + + // No input + if (!data) { + this.buffer = Buffer.alloc(0); + return this; + } + + // Stream + if (typeof data.pipe === 'function') { + this.buffer = Buffer.alloc(0); + data.pipe(this); + return this; + } + + // Buffer or String + // or Object (assumedly a passworded key) + if (data.length || typeof data === 'object') { + this.buffer = data; + this.writable = false; + process.nextTick(function () { + this.emit('end', data); + this.readable = false; + this.emit('close'); + }.bind(this)); + return this; + } + + throw new TypeError('Unexpected data type ('+ typeof data + ')'); +} +util.inherits(DataStream, Stream); + +DataStream.prototype.write = function write(data) { + this.buffer = Buffer.concat([this.buffer, Buffer.from(data)]); + this.emit('data', data); +}; + +DataStream.prototype.end = function end(data) { + if (data) + this.write(data); + this.emit('end', data); + this.emit('close'); + this.writable = false; + this.readable = false; +}; + +module.exports = DataStream; diff --git a/node_modules/jws/lib/sign-stream.js b/node_modules/jws/lib/sign-stream.js new file mode 100644 index 0000000..4a7b288 --- /dev/null +++ b/node_modules/jws/lib/sign-stream.js @@ -0,0 +1,83 @@ +/*global module*/ +var Buffer = require('safe-buffer').Buffer; +var DataStream = require('./data-stream'); +var jwa = require('jwa'); +var Stream = require('stream'); +var toString = require('./tostring'); +var util = require('util'); + +function base64url(string, encoding) { + return Buffer + .from(string, encoding) + .toString('base64') + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); +} + +function jwsSecuredInput(header, payload, encoding) { + encoding = encoding || 'utf8'; + var encodedHeader = base64url(toString(header), 'binary'); + var encodedPayload = base64url(toString(payload), encoding); + return util.format('%s.%s', encodedHeader, encodedPayload); +} + +function jwsSign(opts) { + var header = opts.header; + var payload = opts.payload; + var secretOrKey = opts.secret || opts.privateKey; + var encoding = opts.encoding; + var algo = jwa(header.alg); + var securedInput = jwsSecuredInput(header, payload, encoding); + var signature = algo.sign(securedInput, secretOrKey); + return util.format('%s.%s', securedInput, signature); +} + +function SignStream(opts) { + var secret = opts.secret; + secret = secret == null ? opts.privateKey : secret; + secret = secret == null ? opts.key : secret; + if (/^hs/i.test(opts.header.alg) === true && secret == null) { + throw new TypeError('secret must be a string or buffer or a KeyObject') + } + var secretStream = new DataStream(secret); + this.readable = true; + this.header = opts.header; + this.encoding = opts.encoding; + this.secret = this.privateKey = this.key = secretStream; + this.payload = new DataStream(opts.payload); + this.secret.once('close', function () { + if (!this.payload.writable && this.readable) + this.sign(); + }.bind(this)); + + this.payload.once('close', function () { + if (!this.secret.writable && this.readable) + this.sign(); + }.bind(this)); +} +util.inherits(SignStream, Stream); + +SignStream.prototype.sign = function sign() { + try { + var signature = jwsSign({ + header: this.header, + payload: this.payload.buffer, + secret: this.secret.buffer, + encoding: this.encoding + }); + this.emit('done', signature); + this.emit('data', signature); + this.emit('end'); + this.readable = false; + return signature; + } catch (e) { + this.readable = false; + this.emit('error', e); + this.emit('close'); + } +}; + +SignStream.sign = jwsSign; + +module.exports = SignStream; diff --git a/node_modules/jws/lib/tostring.js b/node_modules/jws/lib/tostring.js new file mode 100644 index 0000000..f5a49a3 --- /dev/null +++ b/node_modules/jws/lib/tostring.js @@ -0,0 +1,10 @@ +/*global module*/ +var Buffer = require('buffer').Buffer; + +module.exports = function toString(obj) { + if (typeof obj === 'string') + return obj; + if (typeof obj === 'number' || Buffer.isBuffer(obj)) + return obj.toString(); + return JSON.stringify(obj); +}; diff --git a/node_modules/jws/lib/verify-stream.js b/node_modules/jws/lib/verify-stream.js new file mode 100644 index 0000000..bb1cb00 --- /dev/null +++ b/node_modules/jws/lib/verify-stream.js @@ -0,0 +1,125 @@ +/*global module*/ +var Buffer = require('safe-buffer').Buffer; +var DataStream = require('./data-stream'); +var jwa = require('jwa'); +var Stream = require('stream'); +var toString = require('./tostring'); +var util = require('util'); +var JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/; + +function isObject(thing) { + return Object.prototype.toString.call(thing) === '[object Object]'; +} + +function safeJsonParse(thing) { + if (isObject(thing)) + return thing; + try { return JSON.parse(thing); } + catch (e) { return undefined; } +} + +function headerFromJWS(jwsSig) { + var encodedHeader = jwsSig.split('.', 1)[0]; + return safeJsonParse(Buffer.from(encodedHeader, 'base64').toString('binary')); +} + +function securedInputFromJWS(jwsSig) { + return jwsSig.split('.', 2).join('.'); +} + +function signatureFromJWS(jwsSig) { + return jwsSig.split('.')[2]; +} + +function payloadFromJWS(jwsSig, encoding) { + encoding = encoding || 'utf8'; + var payload = jwsSig.split('.')[1]; + return Buffer.from(payload, 'base64').toString(encoding); +} + +function isValidJws(string) { + return JWS_REGEX.test(string) && !!headerFromJWS(string); +} + +function jwsVerify(jwsSig, algorithm, secretOrKey) { + if (!algorithm) { + var err = new Error("Missing algorithm parameter for jws.verify"); + err.code = "MISSING_ALGORITHM"; + throw err; + } + jwsSig = toString(jwsSig); + var signature = signatureFromJWS(jwsSig); + var securedInput = securedInputFromJWS(jwsSig); + var algo = jwa(algorithm); + return algo.verify(securedInput, signature, secretOrKey); +} + +function jwsDecode(jwsSig, opts) { + opts = opts || {}; + jwsSig = toString(jwsSig); + + if (!isValidJws(jwsSig)) + return null; + + var header = headerFromJWS(jwsSig); + + if (!header) + return null; + + var payload = payloadFromJWS(jwsSig); + if (header.typ === 'JWT' || opts.json) + payload = JSON.parse(payload, opts.encoding); + + return { + header: header, + payload: payload, + signature: signatureFromJWS(jwsSig) + }; +} + +function VerifyStream(opts) { + opts = opts || {}; + var secretOrKey = opts.secret; + secretOrKey = secretOrKey == null ? opts.publicKey : secretOrKey; + secretOrKey = secretOrKey == null ? opts.key : secretOrKey; + if (/^hs/i.test(opts.algorithm) === true && secretOrKey == null) { + throw new TypeError('secret must be a string or buffer or a KeyObject') + } + var secretStream = new DataStream(secretOrKey); + this.readable = true; + this.algorithm = opts.algorithm; + this.encoding = opts.encoding; + this.secret = this.publicKey = this.key = secretStream; + this.signature = new DataStream(opts.signature); + this.secret.once('close', function () { + if (!this.signature.writable && this.readable) + this.verify(); + }.bind(this)); + + this.signature.once('close', function () { + if (!this.secret.writable && this.readable) + this.verify(); + }.bind(this)); +} +util.inherits(VerifyStream, Stream); +VerifyStream.prototype.verify = function verify() { + try { + var valid = jwsVerify(this.signature.buffer, this.algorithm, this.key.buffer); + var obj = jwsDecode(this.signature.buffer, this.encoding); + this.emit('done', valid, obj); + this.emit('data', valid); + this.emit('end'); + this.readable = false; + return valid; + } catch (e) { + this.readable = false; + this.emit('error', e); + this.emit('close'); + } +}; + +VerifyStream.decode = jwsDecode; +VerifyStream.isValid = isValidJws; +VerifyStream.verify = jwsVerify; + +module.exports = VerifyStream; diff --git a/node_modules/jws/opslevel.yml b/node_modules/jws/opslevel.yml new file mode 100644 index 0000000..aeeeea7 --- /dev/null +++ b/node_modules/jws/opslevel.yml @@ -0,0 +1,6 @@ +--- +version: 1 +repository: + owner: iam_protocols + tier: + tags: diff --git a/node_modules/jws/package.json b/node_modules/jws/package.json new file mode 100644 index 0000000..464d72b --- /dev/null +++ b/node_modules/jws/package.json @@ -0,0 +1,34 @@ +{ + "name": "jws", + "version": "4.0.1", + "description": "Implementation of JSON Web Signatures", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "make test" + }, + "repository": { + "type": "git", + "url": "git://github.com/brianloveswords/node-jws.git" + }, + "keywords": [ + "jws", + "json", + "web", + "signatures" + ], + "author": "Brian J Brennan", + "license": "MIT", + "readmeFilename": "readme.md", + "gitHead": "c0f6b27bcea5a2ad2e304d91c2e842e4076a6b03", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "devDependencies": { + "semver": "^5.1.0", + "tape": "~2.14.0" + } +} diff --git a/node_modules/jws/readme.md b/node_modules/jws/readme.md new file mode 100644 index 0000000..2f32dca --- /dev/null +++ b/node_modules/jws/readme.md @@ -0,0 +1,255 @@ +# node-jws [![Build Status](https://secure.travis-ci.org/brianloveswords/node-jws.svg)](http://travis-ci.org/brianloveswords/node-jws) + +An implementation of [JSON Web Signatures](http://self-issued.info/docs/draft-ietf-jose-json-web-signature.html). + +This was developed against `draft-ietf-jose-json-web-signature-08` and +implements the entire spec **except** X.509 Certificate Chain +signing/verifying (patches welcome). + +There are both synchronous (`jws.sign`, `jws.verify`) and streaming +(`jws.createSign`, `jws.createVerify`) APIs. + +# Install + +```bash +$ npm install jws +``` + +# Usage + +## jws.ALGORITHMS + +Array of supported algorithms. The following algorithms are currently supported. + +alg Parameter Value | Digital Signature or MAC Algorithm +----------------|---------------------------- +HS256 | HMAC using SHA-256 hash algorithm +HS384 | HMAC using SHA-384 hash algorithm +HS512 | HMAC using SHA-512 hash algorithm +RS256 | RSASSA using SHA-256 hash algorithm +RS384 | RSASSA using SHA-384 hash algorithm +RS512 | RSASSA using SHA-512 hash algorithm +PS256 | RSASSA-PSS using SHA-256 hash algorithm +PS384 | RSASSA-PSS using SHA-384 hash algorithm +PS512 | RSASSA-PSS using SHA-512 hash algorithm +ES256 | ECDSA using P-256 curve and SHA-256 hash algorithm +ES384 | ECDSA using P-384 curve and SHA-384 hash algorithm +ES512 | ECDSA using P-521 curve and SHA-512 hash algorithm +none | No digital signature or MAC value included + +## jws.sign(options) + +(Synchronous) Return a JSON Web Signature for a header and a payload. + +Options: + +* `header` +* `payload` +* `secret` or `privateKey` +* `encoding` (Optional, defaults to 'utf8') + +`header` must be an object with an `alg` property. `header.alg` must be +one a value found in `jws.ALGORITHMS`. See above for a table of +supported algorithms. + +If `payload` is not a buffer or a string, it will be coerced into a string +using `JSON.stringify`. + +Example + +```js +const signature = jws.sign({ + header: { alg: 'HS256' }, + payload: 'h. jon benjamin', + secret: 'has a van', +}); +``` + +## jws.verify(signature, algorithm, secretOrKey) + +(Synchronous) Returns `true` or `false` for whether a signature matches a +secret or key. + +`signature` is a JWS Signature. `header.alg` must be a value found in `jws.ALGORITHMS`. +See above for a table of supported algorithms. `secretOrKey` is a string or +buffer containing either the secret for HMAC algorithms, or the PEM +encoded public key for RSA and ECDSA. + +Note that the `"alg"` value from the signature header is ignored. + + +## jws.decode(signature) + +(Synchronous) Returns the decoded header, decoded payload, and signature +parts of the JWS Signature. + +Returns an object with three properties, e.g. +```js +{ header: { alg: 'HS256' }, + payload: 'h. jon benjamin', + signature: 'YOWPewyGHKu4Y_0M_vtlEnNlqmFOclqp4Hy6hVHfFT4' +} +``` + +## jws.createSign(options) + +Returns a new SignStream object. + +Options: + +* `header` (required) +* `payload` +* `key` || `privateKey` || `secret` +* `encoding` (Optional, defaults to 'utf8') + +Other than `header`, all options expect a string or a buffer when the +value is known ahead of time, or a stream for convenience. +`key`/`privateKey`/`secret` may also be an object when using an encrypted +private key, see the [crypto documentation][encrypted-key-docs]. + +Example: + +```js + +// This... +jws.createSign({ + header: { alg: 'RS256' }, + privateKey: privateKeyStream, + payload: payloadStream, +}).on('done', function(signature) { + // ... +}); + +// is equivalent to this: +const signer = jws.createSign({ + header: { alg: 'RS256' }, +}); +privateKeyStream.pipe(signer.privateKey); +payloadStream.pipe(signer.payload); +signer.on('done', function(signature) { + // ... +}); +``` + +## jws.createVerify(options) + +Returns a new VerifyStream object. + +Options: + +* `signature` +* `algorithm` +* `key` || `publicKey` || `secret` +* `encoding` (Optional, defaults to 'utf8') + +All options expect a string or a buffer when the value is known ahead of +time, or a stream for convenience. + +Example: + +```js + +// This... +jws.createVerify({ + publicKey: pubKeyStream, + signature: sigStream, +}).on('done', function(verified, obj) { + // ... +}); + +// is equivilant to this: +const verifier = jws.createVerify(); +pubKeyStream.pipe(verifier.publicKey); +sigStream.pipe(verifier.signature); +verifier.on('done', function(verified, obj) { + // ... +}); +``` + +## Class: SignStream + +A `Readable Stream` that emits a single data event (the calculated +signature) when done. + +### Event: 'done' +`function (signature) { }` + +### signer.payload + +A `Writable Stream` that expects the JWS payload. Do *not* use if you +passed a `payload` option to the constructor. + +Example: + +```js +payloadStream.pipe(signer.payload); +``` + +### signer.secret
signer.key
signer.privateKey + +A `Writable Stream`. Expects the JWS secret for HMAC, or the privateKey +for ECDSA and RSA. Do *not* use if you passed a `secret` or `key` option +to the constructor. + +Example: + +```js +privateKeyStream.pipe(signer.privateKey); +``` + +## Class: VerifyStream + +This is a `Readable Stream` that emits a single data event, the result +of whether or not that signature was valid. + +### Event: 'done' +`function (valid, obj) { }` + +`valid` is a boolean for whether or not the signature is valid. + +### verifier.signature + +A `Writable Stream` that expects a JWS Signature. Do *not* use if you +passed a `signature` option to the constructor. + +### verifier.secret
verifier.key
verifier.publicKey + +A `Writable Stream` that expects a public key or secret. Do *not* use if you +passed a `key` or `secret` option to the constructor. + +# TODO + +* It feels like there should be some convenience options/APIs for + defining the algorithm rather than having to define a header object + with `{ alg: 'ES512' }` or whatever every time. + +* X.509 support, ugh + +# License + +MIT + +``` +Copyright (c) 2013-2015 Brian J. Brennan + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +[encrypted-key-docs]: https://nodejs.org/api/crypto.html#crypto_sign_sign_private_key_output_format diff --git a/node_modules/lodash.includes/LICENSE b/node_modules/lodash.includes/LICENSE new file mode 100644 index 0000000..e0c69d5 --- /dev/null +++ b/node_modules/lodash.includes/LICENSE @@ -0,0 +1,47 @@ +Copyright jQuery Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. diff --git a/node_modules/lodash.includes/README.md b/node_modules/lodash.includes/README.md new file mode 100644 index 0000000..26e9377 --- /dev/null +++ b/node_modules/lodash.includes/README.md @@ -0,0 +1,18 @@ +# lodash.includes v4.3.0 + +The [lodash](https://lodash.com/) method `_.includes` exported as a [Node.js](https://nodejs.org/) module. + +## Installation + +Using npm: +```bash +$ {sudo -H} npm i -g npm +$ npm i --save lodash.includes +``` + +In Node.js: +```js +var includes = require('lodash.includes'); +``` + +See the [documentation](https://lodash.com/docs#includes) or [package source](https://github.com/lodash/lodash/blob/4.3.0-npm-packages/lodash.includes) for more details. diff --git a/node_modules/lodash.includes/index.js b/node_modules/lodash.includes/index.js new file mode 100644 index 0000000..e88d533 --- /dev/null +++ b/node_modules/lodash.includes/index.js @@ -0,0 +1,745 @@ +/** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + +/** Used as references for various `Number` constants. */ +var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + +/** `Object#toString` result references. */ +var argsTag = '[object Arguments]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + stringTag = '[object String]', + symbolTag = '[object Symbol]'; + +/** Used to match leading and trailing whitespace. */ +var reTrim = /^\s+|\s+$/g; + +/** Used to detect bad signed hexadecimal string values. */ +var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + +/** Used to detect binary string values. */ +var reIsBinary = /^0b[01]+$/i; + +/** Used to detect octal string values. */ +var reIsOctal = /^0o[0-7]+$/i; + +/** Used to detect unsigned integer values. */ +var reIsUint = /^(?:0|[1-9]\d*)$/; + +/** Built-in method references without a dependency on `root`. */ +var freeParseInt = parseInt; + +/** + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ +function arrayMap(array, iteratee) { + var index = -1, + length = array ? array.length : 0, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; +} + +/** + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ +function baseFindIndex(array, predicate, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 1 : -1); + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; +} + +/** + * The base implementation of `_.indexOf` without `fromIndex` bounds checks. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ +function baseIndexOf(array, value, fromIndex) { + if (value !== value) { + return baseFindIndex(array, baseIsNaN, fromIndex); + } + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; +} + +/** + * The base implementation of `_.isNaN` without support for number objects. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + */ +function baseIsNaN(value) { + return value !== value; +} + +/** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ +function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; +} + +/** + * The base implementation of `_.values` and `_.valuesIn` which creates an + * array of `object` property values corresponding to the property names + * of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the array of property values. + */ +function baseValues(object, props) { + return arrayMap(props, function(key) { + return object[key]; + }); +} + +/** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ +function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; +} + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** Built-in value references. */ +var propertyIsEnumerable = objectProto.propertyIsEnumerable; + +/* Built-in method references for those with the same name as other `lodash` methods. */ +var nativeKeys = overArg(Object.keys, Object), + nativeMax = Math.max; + +/** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ +function arrayLikeKeys(value, inherited) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + // Safari 9 makes `arguments.length` enumerable in strict mode. + var result = (isArray(value) || isArguments(value)) + ? baseTimes(value.length, String) + : []; + + var length = result.length, + skipIndexes = !!length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && (key == 'length' || isIndex(key, length)))) { + result.push(key); + } + } + return result; +} + +/** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ +function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; +} + +/** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ +function isIndex(value, length) { + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && + (typeof value == 'number' || reIsUint.test(value)) && + (value > -1 && value % 1 == 0 && value < length); +} + +/** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ +function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; +} + +/** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * is used for equality comparisons. If `fromIndex` is negative, it's used as + * the offset from the end of `collection`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ +function includes(collection, value, fromIndex, guard) { + collection = isArrayLike(collection) ? collection : values(collection); + fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0; + + var length = collection.length; + if (fromIndex < 0) { + fromIndex = nativeMax(length + fromIndex, 0); + } + return isString(collection) + ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1) + : (!!length && baseIndexOf(collection, value, fromIndex) > -1); +} + +/** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ +function isArguments(value) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && + (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); +} + +/** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ +var isArray = Array.isArray; + +/** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ +function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); +} + +/** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ +function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); +} + +/** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ +function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 8-9 which returns 'object' for typed array and other constructors. + var tag = isObject(value) ? objectToString.call(value) : ''; + return tag == funcTag || tag == genTag; +} + +/** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ +function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; +} + +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ +function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a string, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ +function isString(value) { + return typeof value == 'string' || + (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag); +} + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ +function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); +} + +/** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ +function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = (value < 0 ? -1 : 1); + return sign * MAX_INTEGER; + } + return value === value ? value : 0; +} + +/** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ +function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + + return result === result ? (remainder ? result - remainder : result) : 0; +} + +/** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ +function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); +} + +/** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ +function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); +} + +/** + * Creates an array of the own enumerable string keyed property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */ +function values(object) { + return object ? baseValues(object, keys(object)) : []; +} + +module.exports = includes; diff --git a/node_modules/lodash.includes/package.json b/node_modules/lodash.includes/package.json new file mode 100644 index 0000000..a02e645 --- /dev/null +++ b/node_modules/lodash.includes/package.json @@ -0,0 +1,17 @@ +{ + "name": "lodash.includes", + "version": "4.3.0", + "description": "The lodash method `_.includes` exported as a module.", + "homepage": "https://lodash.com/", + "icon": "https://lodash.com/icon.svg", + "license": "MIT", + "keywords": "lodash-modularized, includes", + "author": "John-David Dalton (http://allyoucanleet.com/)", + "contributors": [ + "John-David Dalton (http://allyoucanleet.com/)", + "Blaine Bublitz (https://github.com/phated)", + "Mathias Bynens (https://mathiasbynens.be/)" + ], + "repository": "lodash/lodash", + "scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" } +} diff --git a/node_modules/lodash.isboolean/LICENSE b/node_modules/lodash.isboolean/LICENSE new file mode 100644 index 0000000..b054ca5 --- /dev/null +++ b/node_modules/lodash.isboolean/LICENSE @@ -0,0 +1,22 @@ +Copyright 2012-2016 The Dojo Foundation +Based on Underscore.js, copyright 2009-2016 Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/lodash.isboolean/README.md b/node_modules/lodash.isboolean/README.md new file mode 100644 index 0000000..b3c476b --- /dev/null +++ b/node_modules/lodash.isboolean/README.md @@ -0,0 +1,18 @@ +# lodash.isboolean v3.0.3 + +The [lodash](https://lodash.com/) method `_.isBoolean` exported as a [Node.js](https://nodejs.org/) module. + +## Installation + +Using npm: +```bash +$ {sudo -H} npm i -g npm +$ npm i --save lodash.isboolean +``` + +In Node.js: +```js +var isBoolean = require('lodash.isboolean'); +``` + +See the [documentation](https://lodash.com/docs#isBoolean) or [package source](https://github.com/lodash/lodash/blob/3.0.3-npm-packages/lodash.isboolean) for more details. diff --git a/node_modules/lodash.isboolean/index.js b/node_modules/lodash.isboolean/index.js new file mode 100644 index 0000000..23bbabd --- /dev/null +++ b/node_modules/lodash.isboolean/index.js @@ -0,0 +1,70 @@ +/** + * lodash 3.0.3 (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright 2012-2016 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** `Object#toString` result references. */ +var boolTag = '[object Boolean]'; + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */ +function isBoolean(value) { + return value === true || value === false || + (isObjectLike(value) && objectToString.call(value) == boolTag); +} + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +module.exports = isBoolean; diff --git a/node_modules/lodash.isboolean/package.json b/node_modules/lodash.isboolean/package.json new file mode 100644 index 0000000..01d6e8b --- /dev/null +++ b/node_modules/lodash.isboolean/package.json @@ -0,0 +1,17 @@ +{ + "name": "lodash.isboolean", + "version": "3.0.3", + "description": "The lodash method `_.isBoolean` exported as a module.", + "homepage": "https://lodash.com/", + "icon": "https://lodash.com/icon.svg", + "license": "MIT", + "keywords": "lodash-modularized, isboolean", + "author": "John-David Dalton (http://allyoucanleet.com/)", + "contributors": [ + "John-David Dalton (http://allyoucanleet.com/)", + "Blaine Bublitz (https://github.com/phated)", + "Mathias Bynens (https://mathiasbynens.be/)" + ], + "repository": "lodash/lodash", + "scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" } +} diff --git a/node_modules/lodash.isinteger/LICENSE b/node_modules/lodash.isinteger/LICENSE new file mode 100644 index 0000000..e0c69d5 --- /dev/null +++ b/node_modules/lodash.isinteger/LICENSE @@ -0,0 +1,47 @@ +Copyright jQuery Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. diff --git a/node_modules/lodash.isinteger/README.md b/node_modules/lodash.isinteger/README.md new file mode 100644 index 0000000..3a78567 --- /dev/null +++ b/node_modules/lodash.isinteger/README.md @@ -0,0 +1,18 @@ +# lodash.isinteger v4.0.4 + +The [lodash](https://lodash.com/) method `_.isInteger` exported as a [Node.js](https://nodejs.org/) module. + +## Installation + +Using npm: +```bash +$ {sudo -H} npm i -g npm +$ npm i --save lodash.isinteger +``` + +In Node.js: +```js +var isInteger = require('lodash.isinteger'); +``` + +See the [documentation](https://lodash.com/docs#isInteger) or [package source](https://github.com/lodash/lodash/blob/4.0.4-npm-packages/lodash.isinteger) for more details. diff --git a/node_modules/lodash.isinteger/index.js b/node_modules/lodash.isinteger/index.js new file mode 100644 index 0000000..3bf06f0 --- /dev/null +++ b/node_modules/lodash.isinteger/index.js @@ -0,0 +1,265 @@ +/** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + +/** Used as references for various `Number` constants. */ +var INFINITY = 1 / 0, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + +/** `Object#toString` result references. */ +var symbolTag = '[object Symbol]'; + +/** Used to match leading and trailing whitespace. */ +var reTrim = /^\s+|\s+$/g; + +/** Used to detect bad signed hexadecimal string values. */ +var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + +/** Used to detect binary string values. */ +var reIsBinary = /^0b[01]+$/i; + +/** Used to detect octal string values. */ +var reIsOctal = /^0o[0-7]+$/i; + +/** Built-in method references without a dependency on `root`. */ +var freeParseInt = parseInt; + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** + * Checks if `value` is an integer. + * + * **Note:** This method is based on + * [`Number.isInteger`](https://mdn.io/Number/isInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an integer, else `false`. + * @example + * + * _.isInteger(3); + * // => true + * + * _.isInteger(Number.MIN_VALUE); + * // => false + * + * _.isInteger(Infinity); + * // => false + * + * _.isInteger('3'); + * // => false + */ +function isInteger(value) { + return typeof value == 'number' && value == toInteger(value); +} + +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ +function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ +function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); +} + +/** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ +function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = (value < 0 ? -1 : 1); + return sign * MAX_INTEGER; + } + return value === value ? value : 0; +} + +/** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ +function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + + return result === result ? (remainder ? result - remainder : result) : 0; +} + +/** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ +function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); +} + +module.exports = isInteger; diff --git a/node_modules/lodash.isinteger/package.json b/node_modules/lodash.isinteger/package.json new file mode 100644 index 0000000..92db256 --- /dev/null +++ b/node_modules/lodash.isinteger/package.json @@ -0,0 +1,17 @@ +{ + "name": "lodash.isinteger", + "version": "4.0.4", + "description": "The lodash method `_.isInteger` exported as a module.", + "homepage": "https://lodash.com/", + "icon": "https://lodash.com/icon.svg", + "license": "MIT", + "keywords": "lodash-modularized, isinteger", + "author": "John-David Dalton (http://allyoucanleet.com/)", + "contributors": [ + "John-David Dalton (http://allyoucanleet.com/)", + "Blaine Bublitz (https://github.com/phated)", + "Mathias Bynens (https://mathiasbynens.be/)" + ], + "repository": "lodash/lodash", + "scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" } +} diff --git a/node_modules/lodash.isnumber/LICENSE b/node_modules/lodash.isnumber/LICENSE new file mode 100644 index 0000000..b054ca5 --- /dev/null +++ b/node_modules/lodash.isnumber/LICENSE @@ -0,0 +1,22 @@ +Copyright 2012-2016 The Dojo Foundation +Based on Underscore.js, copyright 2009-2016 Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/lodash.isnumber/README.md b/node_modules/lodash.isnumber/README.md new file mode 100644 index 0000000..a1d434d --- /dev/null +++ b/node_modules/lodash.isnumber/README.md @@ -0,0 +1,18 @@ +# lodash.isnumber v3.0.3 + +The [lodash](https://lodash.com/) method `_.isNumber` exported as a [Node.js](https://nodejs.org/) module. + +## Installation + +Using npm: +```bash +$ {sudo -H} npm i -g npm +$ npm i --save lodash.isnumber +``` + +In Node.js: +```js +var isNumber = require('lodash.isnumber'); +``` + +See the [documentation](https://lodash.com/docs#isNumber) or [package source](https://github.com/lodash/lodash/blob/3.0.3-npm-packages/lodash.isnumber) for more details. diff --git a/node_modules/lodash.isnumber/index.js b/node_modules/lodash.isnumber/index.js new file mode 100644 index 0000000..35a8573 --- /dev/null +++ b/node_modules/lodash.isnumber/index.js @@ -0,0 +1,79 @@ +/** + * lodash 3.0.3 (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright 2012-2016 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** `Object#toString` result references. */ +var numberTag = '[object Number]'; + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified + * as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isNumber(3); + * // => true + * + * _.isNumber(Number.MIN_VALUE); + * // => true + * + * _.isNumber(Infinity); + * // => true + * + * _.isNumber('3'); + * // => false + */ +function isNumber(value) { + return typeof value == 'number' || + (isObjectLike(value) && objectToString.call(value) == numberTag); +} + +module.exports = isNumber; diff --git a/node_modules/lodash.isnumber/package.json b/node_modules/lodash.isnumber/package.json new file mode 100644 index 0000000..4c33c2a --- /dev/null +++ b/node_modules/lodash.isnumber/package.json @@ -0,0 +1,17 @@ +{ + "name": "lodash.isnumber", + "version": "3.0.3", + "description": "The lodash method `_.isNumber` exported as a module.", + "homepage": "https://lodash.com/", + "icon": "https://lodash.com/icon.svg", + "license": "MIT", + "keywords": "lodash-modularized, isnumber", + "author": "John-David Dalton (http://allyoucanleet.com/)", + "contributors": [ + "John-David Dalton (http://allyoucanleet.com/)", + "Blaine Bublitz (https://github.com/phated)", + "Mathias Bynens (https://mathiasbynens.be/)" + ], + "repository": "lodash/lodash", + "scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" } +} diff --git a/node_modules/lodash.isplainobject/LICENSE b/node_modules/lodash.isplainobject/LICENSE new file mode 100644 index 0000000..e0c69d5 --- /dev/null +++ b/node_modules/lodash.isplainobject/LICENSE @@ -0,0 +1,47 @@ +Copyright jQuery Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. diff --git a/node_modules/lodash.isplainobject/README.md b/node_modules/lodash.isplainobject/README.md new file mode 100644 index 0000000..aeefd74 --- /dev/null +++ b/node_modules/lodash.isplainobject/README.md @@ -0,0 +1,18 @@ +# lodash.isplainobject v4.0.6 + +The [lodash](https://lodash.com/) method `_.isPlainObject` exported as a [Node.js](https://nodejs.org/) module. + +## Installation + +Using npm: +```bash +$ {sudo -H} npm i -g npm +$ npm i --save lodash.isplainobject +``` + +In Node.js: +```js +var isPlainObject = require('lodash.isplainobject'); +``` + +See the [documentation](https://lodash.com/docs#isPlainObject) or [package source](https://github.com/lodash/lodash/blob/4.0.6-npm-packages/lodash.isplainobject) for more details. diff --git a/node_modules/lodash.isplainobject/index.js b/node_modules/lodash.isplainobject/index.js new file mode 100644 index 0000000..0f820ee --- /dev/null +++ b/node_modules/lodash.isplainobject/index.js @@ -0,0 +1,139 @@ +/** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + +/** `Object#toString` result references. */ +var objectTag = '[object Object]'; + +/** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ +function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; +} + +/** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ +function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; +} + +/** Used for built-in method references. */ +var funcProto = Function.prototype, + objectProto = Object.prototype; + +/** Used to resolve the decompiled source of functions. */ +var funcToString = funcProto.toString; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** Used to infer the `Object` constructor. */ +var objectCtorString = funcToString.call(Object); + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** Built-in value references. */ +var getPrototype = overArg(Object.getPrototypeOf, Object); + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ +function isPlainObject(value) { + if (!isObjectLike(value) || + objectToString.call(value) != objectTag || isHostObject(value)) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return (typeof Ctor == 'function' && + Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString); +} + +module.exports = isPlainObject; diff --git a/node_modules/lodash.isplainobject/package.json b/node_modules/lodash.isplainobject/package.json new file mode 100644 index 0000000..86f6a07 --- /dev/null +++ b/node_modules/lodash.isplainobject/package.json @@ -0,0 +1,17 @@ +{ + "name": "lodash.isplainobject", + "version": "4.0.6", + "description": "The lodash method `_.isPlainObject` exported as a module.", + "homepage": "https://lodash.com/", + "icon": "https://lodash.com/icon.svg", + "license": "MIT", + "keywords": "lodash-modularized, isplainobject", + "author": "John-David Dalton (http://allyoucanleet.com/)", + "contributors": [ + "John-David Dalton (http://allyoucanleet.com/)", + "Blaine Bublitz (https://github.com/phated)", + "Mathias Bynens (https://mathiasbynens.be/)" + ], + "repository": "lodash/lodash", + "scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" } +} diff --git a/node_modules/lodash.isstring/LICENSE b/node_modules/lodash.isstring/LICENSE new file mode 100644 index 0000000..b054ca5 --- /dev/null +++ b/node_modules/lodash.isstring/LICENSE @@ -0,0 +1,22 @@ +Copyright 2012-2016 The Dojo Foundation +Based on Underscore.js, copyright 2009-2016 Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/lodash.isstring/README.md b/node_modules/lodash.isstring/README.md new file mode 100644 index 0000000..f184029 --- /dev/null +++ b/node_modules/lodash.isstring/README.md @@ -0,0 +1,18 @@ +# lodash.isstring v4.0.1 + +The [lodash](https://lodash.com/) method `_.isString` exported as a [Node.js](https://nodejs.org/) module. + +## Installation + +Using npm: +```bash +$ {sudo -H} npm i -g npm +$ npm i --save lodash.isstring +``` + +In Node.js: +```js +var isString = require('lodash.isstring'); +``` + +See the [documentation](https://lodash.com/docs#isString) or [package source](https://github.com/lodash/lodash/blob/4.0.1-npm-packages/lodash.isstring) for more details. diff --git a/node_modules/lodash.isstring/index.js b/node_modules/lodash.isstring/index.js new file mode 100644 index 0000000..408225c --- /dev/null +++ b/node_modules/lodash.isstring/index.js @@ -0,0 +1,95 @@ +/** + * lodash 4.0.1 (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright 2012-2016 The Dojo Foundation + * Based on Underscore.js 1.8.3 + * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ + +/** `Object#toString` result references. */ +var stringTag = '[object String]'; + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @type Function + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ +var isArray = Array.isArray; + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ +function isString(value) { + return typeof value == 'string' || + (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag); +} + +module.exports = isString; diff --git a/node_modules/lodash.isstring/package.json b/node_modules/lodash.isstring/package.json new file mode 100644 index 0000000..1331535 --- /dev/null +++ b/node_modules/lodash.isstring/package.json @@ -0,0 +1,17 @@ +{ + "name": "lodash.isstring", + "version": "4.0.1", + "description": "The lodash method `_.isString` exported as a module.", + "homepage": "https://lodash.com/", + "icon": "https://lodash.com/icon.svg", + "license": "MIT", + "keywords": "lodash-modularized, isstring", + "author": "John-David Dalton (http://allyoucanleet.com/)", + "contributors": [ + "John-David Dalton (http://allyoucanleet.com/)", + "Blaine Bublitz (https://github.com/phated)", + "Mathias Bynens (https://mathiasbynens.be/)" + ], + "repository": "lodash/lodash", + "scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" } +} diff --git a/node_modules/lodash.once/LICENSE b/node_modules/lodash.once/LICENSE new file mode 100644 index 0000000..e0c69d5 --- /dev/null +++ b/node_modules/lodash.once/LICENSE @@ -0,0 +1,47 @@ +Copyright jQuery Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. diff --git a/node_modules/lodash.once/README.md b/node_modules/lodash.once/README.md new file mode 100644 index 0000000..c4a2f16 --- /dev/null +++ b/node_modules/lodash.once/README.md @@ -0,0 +1,18 @@ +# lodash.once v4.1.1 + +The [lodash](https://lodash.com/) method `_.once` exported as a [Node.js](https://nodejs.org/) module. + +## Installation + +Using npm: +```bash +$ {sudo -H} npm i -g npm +$ npm i --save lodash.once +``` + +In Node.js: +```js +var once = require('lodash.once'); +``` + +See the [documentation](https://lodash.com/docs#once) or [package source](https://github.com/lodash/lodash/blob/4.1.1-npm-packages/lodash.once) for more details. diff --git a/node_modules/lodash.once/index.js b/node_modules/lodash.once/index.js new file mode 100644 index 0000000..414ceb3 --- /dev/null +++ b/node_modules/lodash.once/index.js @@ -0,0 +1,294 @@ +/** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** Used as references for various `Number` constants. */ +var INFINITY = 1 / 0, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + +/** `Object#toString` result references. */ +var symbolTag = '[object Symbol]'; + +/** Used to match leading and trailing whitespace. */ +var reTrim = /^\s+|\s+$/g; + +/** Used to detect bad signed hexadecimal string values. */ +var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + +/** Used to detect binary string values. */ +var reIsBinary = /^0b[01]+$/i; + +/** Used to detect octal string values. */ +var reIsOctal = /^0o[0-7]+$/i; + +/** Built-in method references without a dependency on `root`. */ +var freeParseInt = parseInt; + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it's called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery(element).on('click', _.before(5, addContactToList)); + * // => Allows adding up to 4 contacts to the list. + */ +function before(n, func) { + var result; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; +} + +/** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first invocation. The `func` is + * invoked with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // => `createApplication` is invoked once + */ +function once(func) { + return before(2, func); +} + +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ +function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ +function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); +} + +/** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ +function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = (value < 0 ? -1 : 1); + return sign * MAX_INTEGER; + } + return value === value ? value : 0; +} + +/** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ +function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + + return result === result ? (remainder ? result - remainder : result) : 0; +} + +/** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ +function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); +} + +module.exports = once; diff --git a/node_modules/lodash.once/package.json b/node_modules/lodash.once/package.json new file mode 100644 index 0000000..fae782c --- /dev/null +++ b/node_modules/lodash.once/package.json @@ -0,0 +1,17 @@ +{ + "name": "lodash.once", + "version": "4.1.1", + "description": "The lodash method `_.once` exported as a module.", + "homepage": "https://lodash.com/", + "icon": "https://lodash.com/icon.svg", + "license": "MIT", + "keywords": "lodash-modularized, once", + "author": "John-David Dalton (http://allyoucanleet.com/)", + "contributors": [ + "John-David Dalton (http://allyoucanleet.com/)", + "Blaine Bublitz (https://github.com/phated)", + "Mathias Bynens (https://mathiasbynens.be/)" + ], + "repository": "lodash/lodash", + "scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" } +} diff --git a/node_modules/pg-cloudflare/LICENSE b/node_modules/pg-cloudflare/LICENSE new file mode 100644 index 0000000..5c14056 --- /dev/null +++ b/node_modules/pg-cloudflare/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010 - 2021 Brian Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/pg-cloudflare/README.md b/node_modules/pg-cloudflare/README.md new file mode 100644 index 0000000..68663c4 --- /dev/null +++ b/node_modules/pg-cloudflare/README.md @@ -0,0 +1,112 @@ +# pg-cloudflare + +`pg-cloudflare` makes it easier to take an existing package that relies on `tls` and `net`, and make it work in environments where only `connect()` is supported, such as Cloudflare Workers. + +`pg-cloudflare` wraps `connect()`, the [TCP Socket API](https://github.com/wintercg/proposal-sockets-api) proposed within WinterCG, and implemented in [Cloudflare Workers](https://developers.cloudflare.com/workers/runtime-apis/tcp-sockets/), and exposes an interface with methods similar to what the `net` and `tls` modules in Node.js expose. (ex: `net.connect(path[, options][, callback])`). This minimizes the number of changes needed in order to make an existing package work across JavaScript runtimes. + +## Installation + +``` +npm i --save-dev pg-cloudflare +``` + +The package uses conditional exports to support bundlers that don't know about +`cloudflare:sockets`, so the consumer code by default imports an empty file. To +enable the package, resolve to the `cloudflare` condition in your bundler's +config. For example: + +- `webpack.config.js` + ```js + export default { + ..., + resolve: { conditionNames: [..., "workerd"] }, + plugins: [ + // ignore cloudflare:sockets imports + new webpack.IgnorePlugin({ + resourceRegExp: /^cloudflare:sockets$/, + }), + ], + } + ``` +- `vite.config.js` + + > [!NOTE] + > If you are using the [Cloudflare Vite plugin](https://www.npmjs.com/package/@cloudflare/vite-plugin) then the following configuration is not necessary. + + ```js + export default defineConfig({ + ..., + resolve: { + conditions: [..., "workerd"], + }, + build: { + ..., + // don't try to bundle cloudflare:sockets + rollupOptions: { + external: [..., 'cloudflare:sockets'], + }, + }, + }) + ``` + +- `rollup.config.js` + ```js + export default defineConfig({ + ..., + plugins: [..., nodeResolve({ exportConditions: [..., 'workerd'] })], + // don't try to bundle cloudflare:sockets + external: [..., 'cloudflare:sockets'], + }) + ``` +- `esbuild.config.js` + ```js + await esbuild.build({ + ..., + conditions: [..., 'workerd'], + }) + ``` + +The concrete examples can be found in `packages/pg-bundler-test`. + +## How to use conditionally, in non-Node.js environments + +As implemented in `pg` [here](https://github.com/brianc/node-postgres/commit/07553428e9c0eacf761a5d4541a3300ff7859578#diff-34588ad868ebcb232660aba7ee6a99d1e02f4bc93f73497d2688c3f074e60533R5-R13), a typical use case might look as follows, where in a Node.js environment the `net` module is used, while in a non-Node.js environment, where `net` is unavailable, `pg-cloudflare` is used instead, providing an equivalent interface: + +```js +module.exports.getStream = function getStream(ssl = false) { + const net = require('net') + if (typeof net.Socket === 'function') { + return net.Socket() + } + const { CloudflareSocket } = require('pg-cloudflare') + return new CloudflareSocket(ssl) +} +``` + +## Node.js implementation of the Socket API proposal + +If you're looking for a way to rely on `connect()` as the interface you use to interact with raw sockets, but need this interface to be available in a Node.js environment, [`@arrowood.dev/socket`](https://github.com/Ethan-Arrowood/socket) provides a Node.js implementation of the Socket API. + +### license + +The MIT License (MIT) + +Copyright (c) 2023 Brian M. Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/pg-cloudflare/dist/empty.d.ts b/node_modules/pg-cloudflare/dist/empty.d.ts new file mode 100644 index 0000000..d87c485 --- /dev/null +++ b/node_modules/pg-cloudflare/dist/empty.d.ts @@ -0,0 +1,2 @@ +declare const _default: {}; +export default _default; diff --git a/node_modules/pg-cloudflare/dist/empty.js b/node_modules/pg-cloudflare/dist/empty.js new file mode 100644 index 0000000..bd78fc9 --- /dev/null +++ b/node_modules/pg-cloudflare/dist/empty.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// This is an empty module that is served up when outside of a workerd environment +// See the `exports` field in package.json +exports.default = {}; +//# sourceMappingURL=empty.js.map \ No newline at end of file diff --git a/node_modules/pg-cloudflare/dist/empty.js.map b/node_modules/pg-cloudflare/dist/empty.js.map new file mode 100644 index 0000000..3eaa3b1 --- /dev/null +++ b/node_modules/pg-cloudflare/dist/empty.js.map @@ -0,0 +1 @@ +{"version":3,"file":"empty.js","sourceRoot":"","sources":["../src/empty.ts"],"names":[],"mappings":";;AAAA,kFAAkF;AAClF,0CAA0C;AAC1C,kBAAe,EAAE,CAAA"} \ No newline at end of file diff --git a/node_modules/pg-cloudflare/dist/index.d.ts b/node_modules/pg-cloudflare/dist/index.d.ts new file mode 100644 index 0000000..a779d14 --- /dev/null +++ b/node_modules/pg-cloudflare/dist/index.d.ts @@ -0,0 +1,31 @@ +/// +/// +/// +import { TlsOptions } from 'cloudflare:sockets'; +import { EventEmitter } from 'events'; +/** + * Wrapper around the Cloudflare built-in socket that can be used by the `Connection`. + */ +export declare class CloudflareSocket extends EventEmitter { + readonly ssl: boolean; + writable: boolean; + destroyed: boolean; + private _upgrading; + private _upgraded; + private _cfSocket; + private _cfWriter; + private _cfReader; + constructor(ssl: boolean); + setNoDelay(): this; + setKeepAlive(): this; + ref(): this; + unref(): this; + connect(port: number, host: string, connectListener?: (...args: unknown[]) => void): Promise; + _listen(): Promise; + _listenOnce(): Promise; + write(data: Uint8Array | string, encoding?: BufferEncoding, callback?: (...args: unknown[]) => void): true | void; + end(data?: Buffer, encoding?: BufferEncoding, callback?: (...args: unknown[]) => void): this; + destroy(reason: string): this; + startTls(options: TlsOptions): void; + _addClosedHandler(): void; +} diff --git a/node_modules/pg-cloudflare/dist/index.js b/node_modules/pg-cloudflare/dist/index.js new file mode 100644 index 0000000..7f0933e --- /dev/null +++ b/node_modules/pg-cloudflare/dist/index.js @@ -0,0 +1,154 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CloudflareSocket = void 0; +const events_1 = require("events"); +/** + * Wrapper around the Cloudflare built-in socket that can be used by the `Connection`. + */ +class CloudflareSocket extends events_1.EventEmitter { + constructor(ssl) { + super(); + this.ssl = ssl; + this.writable = false; + this.destroyed = false; + this._upgrading = false; + this._upgraded = false; + this._cfSocket = null; + this._cfWriter = null; + this._cfReader = null; + } + setNoDelay() { + return this; + } + setKeepAlive() { + return this; + } + ref() { + return this; + } + unref() { + return this; + } + async connect(port, host, connectListener) { + try { + log('connecting'); + if (connectListener) + this.once('connect', connectListener); + const options = this.ssl ? { secureTransport: 'starttls' } : {}; + const mod = await import('cloudflare:sockets'); + const connect = mod.connect; + this._cfSocket = connect(`${host}:${port}`, options); + this._cfWriter = this._cfSocket.writable.getWriter(); + this._addClosedHandler(); + this._cfReader = this._cfSocket.readable.getReader(); + if (this.ssl) { + this._listenOnce().catch((e) => this.emit('error', e)); + } + else { + this._listen().catch((e) => this.emit('error', e)); + } + await this._cfWriter.ready; + log('socket ready'); + this.writable = true; + this.emit('connect'); + return this; + } + catch (e) { + this.emit('error', e); + } + } + async _listen() { + // eslint-disable-next-line no-constant-condition + while (true) { + log('awaiting receive from CF socket'); + const { done, value } = await this._cfReader.read(); + log('CF socket received:', done, value); + if (done) { + log('done'); + break; + } + this.emit('data', Buffer.from(value)); + } + } + async _listenOnce() { + log('awaiting first receive from CF socket'); + const { done, value } = await this._cfReader.read(); + log('First CF socket received:', done, value); + this.emit('data', Buffer.from(value)); + } + write(data, encoding = 'utf8', callback = () => { }) { + if (data.length === 0) + return callback(); + if (typeof data === 'string') + data = Buffer.from(data, encoding); + log('sending data direct:', data); + this._cfWriter.write(data).then(() => { + log('data sent'); + callback(); + }, (err) => { + log('send error', err); + callback(err); + }); + return true; + } + end(data = Buffer.alloc(0), encoding = 'utf8', callback = () => { }) { + log('ending CF socket'); + this.write(data, encoding, (err) => { + this._cfSocket.close(); + if (callback) + callback(err); + }); + return this; + } + destroy(reason) { + log('destroying CF socket', reason); + this.destroyed = true; + return this.end(); + } + startTls(options) { + if (this._upgraded) { + // Don't try to upgrade again. + this.emit('error', 'Cannot call `startTls()` more than once on a socket'); + return; + } + this._cfWriter.releaseLock(); + this._cfReader.releaseLock(); + this._upgrading = true; + this._cfSocket = this._cfSocket.startTls(options); + this._cfWriter = this._cfSocket.writable.getWriter(); + this._cfReader = this._cfSocket.readable.getReader(); + this._addClosedHandler(); + this._listen().catch((e) => this.emit('error', e)); + } + _addClosedHandler() { + this._cfSocket.closed.then(() => { + if (!this._upgrading) { + log('CF socket closed'); + this._cfSocket = null; + this.emit('close'); + } + else { + this._upgrading = false; + this._upgraded = true; + } + }).catch((e) => this.emit('error', e)); + } +} +exports.CloudflareSocket = CloudflareSocket; +const debug = false; +function dump(data) { + if (data instanceof Uint8Array || data instanceof ArrayBuffer) { + // workaround https://github.com/microsoft/TypeScript/issues/63447 + const buf = data instanceof Uint8Array ? Buffer.from(data) : Buffer.from(data); + const hex = buf.toString('hex'); + const str = new TextDecoder().decode(data); + return `\n>>> STR: "${str.replace(/\n/g, '\\n')}"\n>>> HEX: ${hex}\n`; + } + else { + return data; + } +} +function log(...args) { + debug && console.log(...args.map(dump)); +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/node_modules/pg-cloudflare/dist/index.js.map b/node_modules/pg-cloudflare/dist/index.js.map new file mode 100644 index 0000000..0791273 --- /dev/null +++ b/node_modules/pg-cloudflare/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/pg-cloudflare/esm/index.mjs b/node_modules/pg-cloudflare/esm/index.mjs new file mode 100644 index 0000000..6384216 --- /dev/null +++ b/node_modules/pg-cloudflare/esm/index.mjs @@ -0,0 +1,3 @@ +import cf from '../dist/index.js' + +export const CloudflareSocket = cf.CloudflareSocket diff --git a/node_modules/pg-cloudflare/package.json b/node_modules/pg-cloudflare/package.json new file mode 100644 index 0000000..8081b54 --- /dev/null +++ b/node_modules/pg-cloudflare/package.json @@ -0,0 +1,39 @@ +{ + "name": "pg-cloudflare", + "version": "1.4.0", + "description": "A socket implementation that can run on Cloudflare Workers using native TCP connections.", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "license": "MIT", + "devDependencies": { + "ts-node": "^8.5.4", + "typescript": "^6.0.3" + }, + "exports": { + ".": { + "workerd": { + "import": "./esm/index.mjs", + "require": "./dist/index.js" + }, + "default": "./dist/empty.js" + }, + "./package.json": "./package.json" + }, + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "prepublish": "yarn build", + "test": "echo e2e test in pg package" + }, + "repository": { + "type": "git", + "url": "git://github.com/brianc/node-postgres.git", + "directory": "packages/pg-cloudflare" + }, + "files": [ + "/dist/*{js,ts,map}", + "/src", + "/esm" + ], + "gitHead": "544b1ce8152bc280e398dc1e8a66920abe6a640e" +} diff --git a/node_modules/pg-cloudflare/src/empty.ts b/node_modules/pg-cloudflare/src/empty.ts new file mode 100644 index 0000000..f1e6740 --- /dev/null +++ b/node_modules/pg-cloudflare/src/empty.ts @@ -0,0 +1,3 @@ +// This is an empty module that is served up when outside of a workerd environment +// See the `exports` field in package.json +export default {} diff --git a/node_modules/pg-cloudflare/src/index.ts b/node_modules/pg-cloudflare/src/index.ts new file mode 100644 index 0000000..9b1e517 --- /dev/null +++ b/node_modules/pg-cloudflare/src/index.ts @@ -0,0 +1,169 @@ +import { SocketOptions, Socket, TlsOptions } from 'cloudflare:sockets' +import { EventEmitter } from 'events' + +/** + * Wrapper around the Cloudflare built-in socket that can be used by the `Connection`. + */ +export class CloudflareSocket extends EventEmitter { + writable = false + destroyed = false + + private _upgrading = false + private _upgraded = false + private _cfSocket: Socket | null = null + private _cfWriter: WritableStreamDefaultWriter | null = null + private _cfReader: ReadableStreamDefaultReader | null = null + + constructor(readonly ssl: boolean) { + super() + } + + setNoDelay() { + return this + } + setKeepAlive() { + return this + } + ref() { + return this + } + unref() { + return this + } + + async connect(port: number, host: string, connectListener?: (...args: unknown[]) => void) { + try { + log('connecting') + if (connectListener) this.once('connect', connectListener) + + const options: SocketOptions = this.ssl ? { secureTransport: 'starttls' } : {} + const mod = await import('cloudflare:sockets') + const connect = mod.connect + this._cfSocket = connect(`${host}:${port}`, options) + this._cfWriter = this._cfSocket.writable.getWriter() + this._addClosedHandler() + + this._cfReader = this._cfSocket.readable.getReader() + if (this.ssl) { + this._listenOnce().catch((e) => this.emit('error', e)) + } else { + this._listen().catch((e) => this.emit('error', e)) + } + + await this._cfWriter!.ready + log('socket ready') + this.writable = true + this.emit('connect') + + return this + } catch (e) { + this.emit('error', e) + } + } + + async _listen() { + // eslint-disable-next-line no-constant-condition + while (true) { + log('awaiting receive from CF socket') + const { done, value } = await this._cfReader!.read() + log('CF socket received:', done, value) + if (done) { + log('done') + break + } + this.emit('data', Buffer.from(value)) + } + } + + async _listenOnce() { + log('awaiting first receive from CF socket') + const { done, value } = await this._cfReader!.read() + log('First CF socket received:', done, value) + this.emit('data', Buffer.from(value)) + } + + write( + data: Uint8Array | string, + encoding: BufferEncoding = 'utf8', + callback: (...args: unknown[]) => void = () => {} + ) { + if (data.length === 0) return callback() + if (typeof data === 'string') data = Buffer.from(data, encoding) + + log('sending data direct:', data) + this._cfWriter!.write(data).then( + () => { + log('data sent') + callback() + }, + (err) => { + log('send error', err) + callback(err) + } + ) + return true + } + + end(data = Buffer.alloc(0), encoding: BufferEncoding = 'utf8', callback: (...args: unknown[]) => void = () => {}) { + log('ending CF socket') + this.write(data, encoding, (err) => { + this._cfSocket!.close() + if (callback) callback(err) + }) + return this + } + + destroy(reason: string) { + log('destroying CF socket', reason) + this.destroyed = true + return this.end() + } + + startTls(options: TlsOptions) { + if (this._upgraded) { + // Don't try to upgrade again. + this.emit('error', 'Cannot call `startTls()` more than once on a socket') + return + } + this._cfWriter!.releaseLock() + this._cfReader!.releaseLock() + this._upgrading = true + this._cfSocket = this._cfSocket!.startTls(options) + this._cfWriter = this._cfSocket.writable.getWriter() + this._cfReader = this._cfSocket.readable.getReader() + this._addClosedHandler() + this._listen().catch((e) => this.emit('error', e)) + } + + _addClosedHandler() { + this._cfSocket!.closed.then(() => { + if (!this._upgrading) { + log('CF socket closed') + this._cfSocket = null + this.emit('close') + } else { + this._upgrading = false + this._upgraded = true + } + }).catch((e) => this.emit('error', e)) + } +} + +const debug = false + +function dump(data: unknown) { + if (data instanceof Uint8Array || data instanceof ArrayBuffer) { + // workaround https://github.com/microsoft/TypeScript/issues/63447 + const buf = data instanceof Uint8Array ? Buffer.from(data) : Buffer.from(data) + + const hex = buf.toString('hex') + const str = new TextDecoder().decode(data) + return `\n>>> STR: "${str.replace(/\n/g, '\\n')}"\n>>> HEX: ${hex}\n` + } else { + return data + } +} + +function log(...args: unknown[]) { + debug && console.log(...args.map(dump)) +} diff --git a/node_modules/pg-cloudflare/src/types.d.ts b/node_modules/pg-cloudflare/src/types.d.ts new file mode 100644 index 0000000..f6f1c3f --- /dev/null +++ b/node_modules/pg-cloudflare/src/types.d.ts @@ -0,0 +1,25 @@ +declare module 'cloudflare:sockets' { + export class Socket { + public readonly readable: any + public readonly writable: any + public readonly closed: Promise + public close(): Promise + public startTls(options: TlsOptions): Socket + } + + export type TlsOptions = { + expectedServerHostname?: string + } + + export type SocketAddress = { + hostname: string + port: number + } + + export type SocketOptions = { + secureTransport?: 'off' | 'on' | 'starttls' + allowHalfOpen?: boolean + } + + export function connect(address: string | SocketAddress, options?: SocketOptions): Socket +} diff --git a/node_modules/pg-connection-string/LICENSE b/node_modules/pg-connection-string/LICENSE new file mode 100644 index 0000000..b068a6c --- /dev/null +++ b/node_modules/pg-connection-string/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Iced Development + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/node_modules/pg-connection-string/README.md b/node_modules/pg-connection-string/README.md new file mode 100644 index 0000000..e47adc8 --- /dev/null +++ b/node_modules/pg-connection-string/README.md @@ -0,0 +1,105 @@ +pg-connection-string +==================== + +[![NPM](https://nodei.co/npm/pg-connection-string.png?compact=true)](https://nodei.co/npm/pg-connection-string/) + +Functions for dealing with a PostgresSQL connection string + +`parse` method taken from [node-postgres](https://github.com/brianc/node-postgres.git) +Copyright (c) 2010-2014 Brian Carlson (brian.m.carlson@gmail.com) +MIT License + +## Usage + +```js +const parse = require('pg-connection-string').parse; + +const config = parse('postgres://someuser:somepassword@somehost:381/somedatabase') +``` + +The resulting config contains a subset of the following properties: + +* `user` - User with which to authenticate to the server +* `password` - Corresponding password +* `host` - Postgres server hostname or, for UNIX domain sockets, the socket filename +* `port` - port on which to connect +* `database` - Database name within the server +* `client_encoding` - string encoding the client will use +* `ssl`, either a boolean or an object with properties + * `rejectUnauthorized` + * `cert` + * `key` + * `ca` +* any other query parameters (for example, `application_name`) are preserved intact. + +### ClientConfig Compatibility for TypeScript + +The pg-connection-string `ConnectionOptions` interface is not compatible with the `ClientConfig` interface that [pg.Client](https://node-postgres.com/apis/client) expects. To remedy this, use the `parseIntoClientConfig` function instead of `parse`: + +```ts +import { ClientConfig } from 'pg'; +import { parseIntoClientConfig } from 'pg-connection-string'; + +const config: ClientConfig = parseIntoClientConfig('postgres://someuser:somepassword@somehost:381/somedatabase') +``` + +You can also use `toClientConfig` to convert an existing `ConnectionOptions` interface into a `ClientConfig` interface: + +```ts +import { ClientConfig } from 'pg'; +import { parse, toClientConfig } from 'pg-connection-string'; + +const config = parse('postgres://someuser:somepassword@somehost:381/somedatabase') +const clientConfig: ClientConfig = toClientConfig(config) +``` + +## Connection Strings + +The short summary of acceptable URLs is: + + * `socket:?` - UNIX domain socket + * `postgres://:@:/?` - TCP connection + +But see below for more details. + +### UNIX Domain Sockets + +When user and password are not given, the socket path follows `socket:`, as in `socket:/var/run/pgsql`. +This form can be shortened to just a path: `/var/run/pgsql`. + +When user and password are given, they are included in the typical URL positions, with an empty `host`, as in `socket://user:pass@/var/run/pgsql`. + +Query parameters follow a `?` character, including the following special query parameters: + + * `db=` - sets the database name (urlencoded) + * `encoding=` - sets the `client_encoding` property + +### TCP Connections + +TCP connections to the Postgres server are indicated with `pg:` or `postgres:` schemes (in fact, any scheme but `socket:` is accepted). +If username and password are included, they should be urlencoded. +The database name, however, should *not* be urlencoded. + +Query parameters follow a `?` character, including the following special query parameters: + * `host=` - sets `host` property, overriding the URL's host + * `encoding=` - sets the `client_encoding` property + * `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly + * `uselibpqcompat=true` - use libpq semantics + * `sslmode=` when `uselibpqcompat=true` is not set + * `sslmode=disable` - sets `ssl` to false + * `sslmode=no-verify` - sets `ssl` to `{ rejectUnauthorized: false }` + * `sslmode=prefer`, `sslmode=require`, `sslmode=verify-ca`, `sslmode=verify-full` - sets `ssl` to true + * `sslmode=` when `uselibpqcompat=true` + * `sslmode=disable` - sets `ssl` to false + * `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }` + * `sslmode=require` - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca` + * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity). This verifies the presented certificate against the effective CA specified in sslrootcert. + * `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity) + * `sslcert=` - reads data from the given file and includes the result as `ssl.cert` + * `sslkey=` - reads data from the given file and includes the result as `ssl.key` + * `sslrootcert=` - reads data from the given file and includes the result as `ssl.ca` + +A bare relative URL, such as `salesdata`, will indicate a database name while leaving other properties empty. + +> [!CAUTION] +> Choosing an sslmode other than verify-full has serious security implications. Please read https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS to understand the trade-offs. diff --git a/node_modules/pg-connection-string/esm/index.mjs b/node_modules/pg-connection-string/esm/index.mjs new file mode 100644 index 0000000..7b390c5 --- /dev/null +++ b/node_modules/pg-connection-string/esm/index.mjs @@ -0,0 +1,8 @@ +// ESM wrapper for pg-connection-string +import connectionString from '../index.js' + +// Re-export the parse function +export default connectionString.parse +export const parse = connectionString.parse +export const toClientConfig = connectionString.toClientConfig +export const parseIntoClientConfig = connectionString.parseIntoClientConfig diff --git a/node_modules/pg-connection-string/index.d.ts b/node_modules/pg-connection-string/index.d.ts new file mode 100644 index 0000000..2ebe675 --- /dev/null +++ b/node_modules/pg-connection-string/index.d.ts @@ -0,0 +1,36 @@ +import { ClientConfig } from 'pg' + +export function parse(connectionString: string, options?: Options): ConnectionOptions + +export interface Options { + // Use libpq semantics when interpreting the connection string + useLibpqCompat?: boolean +} + +interface SSLConfig { + ca?: string + cert?: string | null + key?: string + rejectUnauthorized?: boolean +} + +export interface ConnectionOptions { + host: string | null + password?: string + user?: string + port?: string | null + database: string | null | undefined + client_encoding?: string + ssl?: boolean | string | SSLConfig + + application_name?: string + fallback_application_name?: string + options?: string + keepalives?: number + + // We allow any other options to be passed through + [key: string]: unknown +} + +export function toClientConfig(config: ConnectionOptions): ClientConfig +export function parseIntoClientConfig(connectionString: string): ClientConfig diff --git a/node_modules/pg-connection-string/index.js b/node_modules/pg-connection-string/index.js new file mode 100644 index 0000000..4b8d7af --- /dev/null +++ b/node_modules/pg-connection-string/index.js @@ -0,0 +1,231 @@ +'use strict' + +//Parse method copied from https://github.com/brianc/node-postgres +//Copyright (c) 2010-2014 Brian Carlson (brian.m.carlson@gmail.com) +//MIT License + +//parses a connection string +function parse(str, options = {}) { + //unix socket + if (str.charAt(0) === '/') { + const config = str.split(' ') + return { host: config[0], database: config[1] } + } + + // Check for empty host in URL + + const config = Object.create(null) + let result + let dummyHost = false + if (/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str)) { + // Ensure spaces are encoded as %20 + str = encodeURI(str).replace(/%25(\d\d)/g, '%$1') + } + + try { + try { + result = new URL(str, 'postgres://base') + } catch (e) { + // The URL is invalid so try again with a dummy host + result = new URL(str.replace('@/', '@___DUMMY___/'), 'postgres://base') + dummyHost = true + } + } catch (err) { + // Remove the input from the error message to avoid leaking sensitive information + err.input && (err.input = '*****REDACTED*****') + throw err + } + + // We'd like to use Object.fromEntries() here but Node.js 10 does not support it + for (const entry of result.searchParams.entries()) { + config[entry[0]] = entry[1] + } + + config.user = config.user || decodeURIComponent(result.username) + config.password = config.password || decodeURIComponent(result.password) + + if (result.protocol == 'socket:') { + config.host = decodeURI(result.pathname) + config.database = result.searchParams.get('db') + config.client_encoding = result.searchParams.get('encoding') + return config + } + const hostname = dummyHost ? '' : result.hostname + if (!config.host) { + // Only set the host if there is no equivalent query param. + config.host = decodeURIComponent(hostname) + } else if (hostname && /^%2f/i.test(hostname)) { + // Only prepend the hostname to the pathname if it is not a URL encoded Unix socket host. + result.pathname = hostname + result.pathname + } + if (!config.port) { + // Only set the port if there is no equivalent query param. + config.port = result.port + } + + const pathname = result.pathname.slice(1) || null + config.database = pathname ? decodeURI(pathname) : null + + if (config.ssl === 'true' || config.ssl === '1') { + config.ssl = true + } + + if (config.ssl === '0') { + config.ssl = false + } + + if (config.sslcert || config.sslkey || config.sslrootcert || config.sslmode) { + config.ssl = {} + } + + // Only try to load fs if we expect to read from the disk + const fs = config.sslcert || config.sslkey || config.sslrootcert ? require('fs') : null + + if (config.sslcert) { + config.ssl.cert = fs.readFileSync(config.sslcert).toString() + } + + if (config.sslkey) { + config.ssl.key = fs.readFileSync(config.sslkey).toString() + } + + if (config.sslrootcert) { + config.ssl.ca = fs.readFileSync(config.sslrootcert).toString() + } + + if (options.useLibpqCompat && config.uselibpqcompat) { + throw new Error('Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.') + } + + if (config.uselibpqcompat === 'true' || options.useLibpqCompat) { + switch (config.sslmode) { + case 'disable': { + config.ssl = false + break + } + case 'prefer': { + config.ssl.rejectUnauthorized = false + break + } + case 'require': { + if (config.sslrootcert) { + // If a root CA is specified, behavior of `sslmode=require` will be the same as that of `verify-ca` + config.ssl.checkServerIdentity = function () {} + } else { + config.ssl.rejectUnauthorized = false + } + break + } + case 'verify-ca': { + if (!config.ssl.ca) { + throw new Error( + 'SECURITY WARNING: Using sslmode=verify-ca requires specifying a CA with sslrootcert. If a public CA is used, verify-ca allows connections to a server that somebody else may have registered with the CA, making you vulnerable to Man-in-the-Middle attacks. Either specify a custom CA certificate with sslrootcert parameter or use sslmode=verify-full for proper security.' + ) + } + config.ssl.checkServerIdentity = function () {} + break + } + case 'verify-full': { + break + } + } + } else { + switch (config.sslmode) { + case 'disable': { + config.ssl = false + break + } + case 'prefer': + case 'require': + case 'verify-ca': + case 'verify-full': { + if (config.sslmode !== 'verify-full') { + deprecatedSslModeWarning(config.sslmode) + } + break + } + case 'no-verify': { + config.ssl.rejectUnauthorized = false + break + } + } + } + + return config +} + +// convert pg-connection-string ssl config to a ClientConfig.ConnectionOptions +function toConnectionOptions(sslConfig) { + const connectionOptions = Object.entries(sslConfig).reduce((c, [key, value]) => { + // we explicitly check for undefined and null instead of `if (value)` because some + // options accept falsy values. Example: `ssl.rejectUnauthorized = false` + if (value !== undefined && value !== null) { + c[key] = value + } + + return c + }, Object.create(null)) + + return connectionOptions +} + +// convert pg-connection-string config to a ClientConfig +function toClientConfig(config) { + const poolConfig = Object.entries(config).reduce((c, [key, value]) => { + if (key === 'ssl') { + const sslConfig = value + + if (typeof sslConfig === 'boolean') { + c[key] = sslConfig + } + + if (typeof sslConfig === 'object') { + c[key] = toConnectionOptions(sslConfig) + } + } else if (value !== undefined && value !== null) { + if (key === 'port') { + // when port is not specified, it is converted into an empty string + // we want to avoid NaN or empty string as a values in ClientConfig + if (value !== '') { + const v = parseInt(value, 10) + if (isNaN(v)) { + throw new Error(`Invalid ${key}: ${value}`) + } + + c[key] = v + } + } else { + c[key] = value + } + } + + return c + }, Object.create(null)) + + return poolConfig +} + +// parses a connection string into ClientConfig +function parseIntoClientConfig(str) { + return toClientConfig(parse(str)) +} + +function deprecatedSslModeWarning(sslmode) { + if (!deprecatedSslModeWarning.warned && typeof process !== 'undefined' && process.emitWarning) { + deprecatedSslModeWarning.warned = true + process.emitWarning(`SECURITY WARNING: The SSL modes 'prefer', 'require', and 'verify-ca' are treated as aliases for 'verify-full'. +In the next major version (pg-connection-string v3.0.0 and pg v9.0.0), these modes will adopt standard libpq semantics, which have weaker security guarantees. + +To prepare for this change: +- If you want the current behavior, explicitly use 'sslmode=verify-full' +- If you want libpq compatibility now, use 'uselibpqcompat=true&sslmode=${sslmode}' + +See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode definitions.`) + } +} + +module.exports = parse + +parse.parse = parse +parse.toClientConfig = toClientConfig +parse.parseIntoClientConfig = parseIntoClientConfig diff --git a/node_modules/pg-connection-string/package.json b/node_modules/pg-connection-string/package.json new file mode 100644 index 0000000..a0f93f6 --- /dev/null +++ b/node_modules/pg-connection-string/package.json @@ -0,0 +1,52 @@ +{ + "name": "pg-connection-string", + "version": "2.13.0", + "description": "Functions for dealing with a PostgresSQL connection string", + "main": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./esm/index.mjs", + "require": "./index.js", + "default": "./index.js" + } + }, + "scripts": { + "test": "nyc --reporter=lcov mocha && npm run check-coverage", + "check-coverage": "nyc check-coverage --statements 100 --branches 100 --lines 100 --functions 100" + }, + "repository": { + "type": "git", + "url": "git://github.com/brianc/node-postgres.git", + "directory": "packages/pg-connection-string" + }, + "keywords": [ + "pg", + "connection", + "string", + "parse" + ], + "author": "Blaine Bublitz (http://iceddev.com/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/brianc/node-postgres/issues" + }, + "homepage": "https://github.com/brianc/node-postgres/tree/master/packages/pg-connection-string", + "devDependencies": { + "@types/pg": "^8.12.0", + "chai": "^4.1.1", + "coveralls": "^3.0.4", + "istanbul": "^0.4.5", + "mocha": "^11.7.5", + "nyc": "^15", + "tsx": "^4.19.4", + "typescript": "^6.0.3" + }, + "files": [ + "index.js", + "index.d.ts", + "esm" + ], + "gitHead": "544b1ce8152bc280e398dc1e8a66920abe6a640e" +} diff --git a/node_modules/pg-int8/LICENSE b/node_modules/pg-int8/LICENSE new file mode 100644 index 0000000..c56c973 --- /dev/null +++ b/node_modules/pg-int8/LICENSE @@ -0,0 +1,13 @@ +Copyright © 2017, Charmander <~@charmander.me> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/pg-int8/README.md b/node_modules/pg-int8/README.md new file mode 100644 index 0000000..ef2e608 --- /dev/null +++ b/node_modules/pg-int8/README.md @@ -0,0 +1,16 @@ +[![Build status][ci image]][ci] + +64-bit big-endian signed integer-to-string conversion designed for [pg][]. + +```js +const readInt8 = require('pg-int8'); + +readInt8(Buffer.from([0, 1, 2, 3, 4, 5, 6, 7])) +// '283686952306183' +``` + + + [pg]: https://github.com/brianc/node-postgres + + [ci]: https://travis-ci.org/charmander/pg-int8 + [ci image]: https://api.travis-ci.org/charmander/pg-int8.svg diff --git a/node_modules/pg-int8/index.js b/node_modules/pg-int8/index.js new file mode 100644 index 0000000..db77975 --- /dev/null +++ b/node_modules/pg-int8/index.js @@ -0,0 +1,100 @@ +'use strict'; + +// selected so (BASE - 1) * 0x100000000 + 0xffffffff is a safe integer +var BASE = 1000000; + +function readInt8(buffer) { + var high = buffer.readInt32BE(0); + var low = buffer.readUInt32BE(4); + var sign = ''; + + if (high < 0) { + high = ~high + (low === 0); + low = (~low + 1) >>> 0; + sign = '-'; + } + + var result = ''; + var carry; + var t; + var digits; + var pad; + var l; + var i; + + { + carry = high % BASE; + high = high / BASE >>> 0; + + t = 0x100000000 * carry + low; + low = t / BASE >>> 0; + digits = '' + (t - BASE * low); + + if (low === 0 && high === 0) { + return sign + digits + result; + } + + pad = ''; + l = 6 - digits.length; + + for (i = 0; i < l; i++) { + pad += '0'; + } + + result = pad + digits + result; + } + + { + carry = high % BASE; + high = high / BASE >>> 0; + + t = 0x100000000 * carry + low; + low = t / BASE >>> 0; + digits = '' + (t - BASE * low); + + if (low === 0 && high === 0) { + return sign + digits + result; + } + + pad = ''; + l = 6 - digits.length; + + for (i = 0; i < l; i++) { + pad += '0'; + } + + result = pad + digits + result; + } + + { + carry = high % BASE; + high = high / BASE >>> 0; + + t = 0x100000000 * carry + low; + low = t / BASE >>> 0; + digits = '' + (t - BASE * low); + + if (low === 0 && high === 0) { + return sign + digits + result; + } + + pad = ''; + l = 6 - digits.length; + + for (i = 0; i < l; i++) { + pad += '0'; + } + + result = pad + digits + result; + } + + { + carry = high % BASE; + t = 0x100000000 * carry + low; + digits = '' + t % BASE; + + return sign + digits + result; + } +} + +module.exports = readInt8; diff --git a/node_modules/pg-int8/package.json b/node_modules/pg-int8/package.json new file mode 100644 index 0000000..4b937e1 --- /dev/null +++ b/node_modules/pg-int8/package.json @@ -0,0 +1,24 @@ +{ + "name": "pg-int8", + "version": "1.0.1", + "description": "64-bit big-endian signed integer-to-string conversion", + "bugs": "https://github.com/charmander/pg-int8/issues", + "license": "ISC", + "files": [ + "index.js" + ], + "repository": { + "type": "git", + "url": "https://github.com/charmander/pg-int8" + }, + "scripts": { + "test": "tap test" + }, + "devDependencies": { + "@charmander/eslint-config-base": "1.0.2", + "tap": "10.7.3" + }, + "engines": { + "node": ">=4.0.0" + } +} diff --git a/node_modules/pg-pool/LICENSE b/node_modules/pg-pool/LICENSE new file mode 100644 index 0000000..4e90581 --- /dev/null +++ b/node_modules/pg-pool/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Brian M. Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/pg-pool/README.md b/node_modules/pg-pool/README.md new file mode 100644 index 0000000..80c6447 --- /dev/null +++ b/node_modules/pg-pool/README.md @@ -0,0 +1,357 @@ +# pg-pool + +[![Build Status](https://travis-ci.org/brianc/node-pg-pool.svg?branch=master)](https://travis-ci.org/brianc/node-pg-pool) + +A connection pool for node-postgres + +## install + +```sh +npm i pg-pool pg +``` + +## use + +### create + +to use pg-pool you must first create an instance of a pool + +```js +const Pool = require('pg-pool') + +// by default the pool uses the same +// configuration as whatever `pg` version you have installed +const pool = new Pool() + +// you can pass properties to the pool +// these properties are passed unchanged to both the node-postgres Client constructor +// and the pool constructor, allowing you to fully configure the behavior of both +const pool2 = new Pool({ + database: 'postgres', + user: 'brianc', + password: 'secret!', + port: 5432, + ssl: true, + max: 20, // set pool max size to 20 + idleTimeoutMillis: 1000, // close idle clients after 1 second + connectionTimeoutMillis: 1000, // return an error after 1 second if connection could not be established + maxUses: 7500, // close (and replace) a connection after it has been used 7500 times (see below for discussion) +}) + +// you can supply a custom client constructor +// if you want to use the native postgres client +const NativeClient = require('pg').native.Client +const nativePool = new Pool({ Client: NativeClient }) + +// you can even pool pg-native clients directly +const PgNativeClient = require('pg-native') +const pgNativePool = new Pool({ Client: PgNativeClient }) +``` + +##### Note: + +The Pool constructor does not support passing a Database URL as the parameter. To use pg-pool on heroku, for example, you need to parse the URL into a config object. Here is an example of how to parse a Database URL. + +```js +const Pool = require('pg-pool') +const url = require('url') + +const params = url.parse(process.env.DATABASE_URL) +const auth = params.auth.split(':') + +const config = { + user: auth[0], + password: auth[1], + host: params.hostname, + port: params.port, + database: params.pathname.split('/')[1], + ssl: true, +} + +const pool = new Pool(config) + +/* + Transforms, 'postgres://DBuser:secret@DBHost:#####/myDB', into + config = { + user: 'DBuser', + password: 'secret', + host: 'DBHost', + port: '#####', + database: 'myDB', + ssl: true + } +*/ +``` + +### acquire clients with a promise + +pg-pool supports a fully promise-based api for acquiring clients + +```js +const pool = new Pool() +pool.connect().then((client) => { + client + .query('select $1::text as name', ['pg-pool']) + .then((res) => { + client.release() + console.log('hello from', res.rows[0].name) + }) + .catch((e) => { + client.release() + console.error('query error', e.message, e.stack) + }) +}) +``` + +### plays nice with async/await + +this ends up looking much nicer if you're using [co](https://github.com/tj/co) or async/await: + +```js +// with async/await +;(async () => { + const pool = new Pool() + const client = await pool.connect() + try { + const result = await client.query('select $1::text as name', ['brianc']) + console.log('hello from', result.rows[0]) + } finally { + client.release() + } +})().catch((e) => console.error(e.message, e.stack)) + +// with co +co(function* () { + const client = yield pool.connect() + try { + const result = yield client.query('select $1::text as name', ['brianc']) + console.log('hello from', result.rows[0]) + } finally { + client.release() + } +}).catch((e) => console.error(e.message, e.stack)) +``` + +### your new favorite helper method + +because its so common to just run a query and return the client to the pool afterward pg-pool has this built-in: + +```js +const pool = new Pool() +const time = await pool.query('SELECT NOW()') +const name = await pool.query('select $1::text as name', ['brianc']) +console.log(name.rows[0].name, 'says hello at', time.rows[0].now) +``` + +you can also use a callback here if you'd like: + +```js +const pool = new Pool() +pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { + console.log(res.rows[0].name) // brianc +}) +``` + +**pro tip:** unless you need to run a transaction (which requires a single client for multiple queries) or you +have some other edge case like [streaming rows](https://github.com/brianc/node-pg-query-stream) or using a [cursor](https://github.com/brianc/node-pg-cursor) +you should almost always just use `pool.query`. Its easy, it does the right thing :tm:, and wont ever forget to return +clients back to the pool after the query is done. + +### drop-in backwards compatible + +pg-pool still and will always support the traditional callback api for acquiring a client. This is the exact API node-postgres has shipped with for years: + +```js +const pool = new Pool() +pool.connect((err, client, done) => { + if (err) return done(err) + + client.query('SELECT $1::text as name', ['pg-pool'], (err, res) => { + done() + if (err) { + return console.error('query error', err.message, err.stack) + } + console.log('hello from', res.rows[0].name) + }) +}) +``` + +### shut it down + +When you are finished with the pool if all the clients are idle the pool will close them after `config.idleTimeoutMillis` and your app +will shutdown gracefully. If you don't want to wait for the timeout you can end the pool as follows: + +```js +const pool = new Pool() +const client = await pool.connect() +console.log(await client.query('select now()')) +client.release() +await pool.end() +``` + +### a note on instances + +The pool should be a **long-lived object** in your application. Generally you'll want to instantiate one pool when your app starts up and use the same instance of the pool throughout the lifetime of your application. If you are frequently creating a new pool within your code you likely don't have your pool initialization code in the correct place. Example: + +```js +// assume this is a file in your program at ./your-app/lib/db.js + +// correct usage: create the pool and let it live +// 'globally' here, controlling access to it through exported methods +const pool = new pg.Pool() + +// this is the right way to export the query method +module.exports.query = (text, values) => { + console.log('query:', text, values) + return pool.query(text, values) +} + +// this would be the WRONG way to export the connect method +module.exports.connect = () => { + // notice how we would be creating a pool instance here + // every time we called 'connect' to get a new client? + // that's a bad thing & results in creating an unbounded + // number of pools & therefore connections + const aPool = new pg.Pool() + return aPool.connect() +} +``` + +### events + +Every instance of a `Pool` is an event emitter. These instances emit the following events: + +#### error + +Emitted whenever an idle client in the pool encounters an error. This is common when your PostgreSQL server shuts down, reboots, or a network partition otherwise causes it to become unavailable while your pool has connected clients. + +Example: + +```js +const Pool = require('pg-pool') +const pool = new Pool() + +// attach an error handler to the pool for when a connected, idle client +// receives an error by being disconnected, etc +pool.on('error', function (error, client) { + // handle this in the same way you would treat process.on('uncaughtException') + // it is supplied the error as well as the idle client which received the error +}) +``` + +#### connect + +Fired whenever the pool creates a **new** `pg.Client` instance and successfully connects it to the backend. + +Example: + +```js +const Pool = require('pg-pool') +const pool = new Pool() + +const count = 0 + +pool.on('connect', (client) => { + client.count = count++ +}) + +pool + .connect() + .then((client) => { + return client + .query('SELECT $1::int AS "clientCount"', [client.count]) + .then((res) => console.log(res.rows[0].clientCount)) // outputs 0 + .then(() => client) + }) + .then((client) => client.release()) +``` + +#### acquire + +Fired whenever a client is acquired from the pool + +Example: + +This allows you to count the number of clients which have ever been acquired from the pool. + +```js +const Pool = require('pg-pool') +const pool = new Pool() + +const acquireCount = 0 +pool.on('acquire', function (client) { + acquireCount++ +}) + +const connectCount = 0 +pool.on('connect', function () { + connectCount++ +}) + +for (let i = 0; i < 200; i++) { + pool.query('SELECT NOW()') +} + +setTimeout(function () { + console.log('connect count:', connectCount) // output: connect count: 10 + console.log('acquire count:', acquireCount) // output: acquire count: 200 +}, 100) +``` + +### environment variables + +pg-pool & node-postgres support some of the same environment variables as `psql` supports. The most common are: + +``` +PGDATABASE=my_db +PGUSER=username +PGPASSWORD="my awesome password" +PGPORT=5432 +PGSSLMODE=require +``` + +Usually I will export these into my local environment via a `.env` file with environment settings or export them in `~/.bash_profile` or something similar. This way I get configurability which works with both the postgres suite of tools (`psql`, `pg_dump`, `pg_restore`) and node, I can vary the environment variables locally and in production, and it supports the concept of a [12-factor app](http://12factor.net/) out of the box. + +## maxUses and read-replica autoscaling (e.g. AWS Aurora) + +The maxUses config option can help an application instance rebalance load against a replica set that has been auto-scaled after the connection pool is already full of healthy connections. + +The mechanism here is that a connection is considered "expended" after it has been acquired and released `maxUses` number of times. Depending on the load on your system, this means there will be an approximate time in which any given connection will live, thus creating a window for rebalancing. + +Imagine a scenario where you have 10 app instances providing an API running against a replica cluster of 3 that are accessed via a round-robin DNS entry. Each instance runs a connection pool size of 20. With an ambient load of 50 requests per second, the connection pool will likely fill up in a few minutes with healthy connections. + +If you have weekly bursts of traffic which peak at 1,000 requests per second, you might want to grow your replicas to 10 during this period. Without setting `maxUses`, the new replicas will not be adopted by the app servers without an intervention -- namely, restarting each in turn in order to build up new connection pools that are balanced against all the replicas. Adding additional app server instances will help to some extent because they will adopt all the replicas in an even way, but the initial app servers will continue to focus additional load on the original replicas. + +This is where the `maxUses` configuration option comes into play. Setting `maxUses` to 7500 will ensure that over a period of 30 minutes or so the new replicas will be adopted as the pre-existing connections are closed and replaced with new ones, thus creating a window for eventual balance. + +You'll want to test based on your own scenarios, but one way to make a first guess at `maxUses` is to identify an acceptable window for rebalancing and then solve for the value: + +``` +maxUses = rebalanceWindowSeconds * totalRequestsPerSecond / numAppInstances / poolSize +``` + +In the example above, assuming we acquire and release 1 connection per request and we are aiming for a 30 minute rebalancing window: + +``` +maxUses = rebalanceWindowSeconds * totalRequestsPerSecond / numAppInstances / poolSize + 7200 = 1800 * 1000 / 10 / 25 +``` + +## tests + +To run tests clone the repo, `npm i` in the working dir, and then run `npm test` + +## contributions + +I love contributions. Please make sure they have tests, and submit a PR. If you're not sure if the issue is worth it or will be accepted it never hurts to open an issue to begin the conversation. If you're interested in keeping up with node-postgres releated stuff, you can follow me on twitter at [@briancarlson](https://twitter.com/briancarlson) - I generally announce any noteworthy updates there. + +## license + +The MIT License (MIT) +Copyright (c) 2016 Brian M. Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/pg-pool/esm/index.mjs b/node_modules/pg-pool/esm/index.mjs new file mode 100644 index 0000000..a97fb62 --- /dev/null +++ b/node_modules/pg-pool/esm/index.mjs @@ -0,0 +1,5 @@ +// ESM wrapper for pg-pool +import Pool from '../index.js' + +// Export as default only to match CJS module +export default Pool diff --git a/node_modules/pg-pool/index.js b/node_modules/pg-pool/index.js new file mode 100644 index 0000000..ab514fa --- /dev/null +++ b/node_modules/pg-pool/index.js @@ -0,0 +1,517 @@ +'use strict' +const EventEmitter = require('events').EventEmitter + +const NOOP = function () {} + +const removeWhere = (list, predicate) => { + const i = list.findIndex(predicate) + + return i === -1 ? undefined : list.splice(i, 1)[0] +} + +class IdleItem { + constructor(client, idleListener, timeoutId) { + this.client = client + this.idleListener = idleListener + this.timeoutId = timeoutId + } +} + +class PendingItem { + constructor(callback) { + this.callback = callback + } +} + +function throwOnDoubleRelease() { + throw new Error('Release called on client which has already been released to the pool.') +} + +function promisify(Promise, callback) { + if (callback) { + return { callback: callback, result: undefined } + } + let rej + let res + const cb = function (err, client) { + err ? rej(err) : res(client) + } + const result = new Promise(function (resolve, reject) { + res = resolve + rej = reject + }).catch((err) => { + // replace the stack trace that leads to `TCP.onStreamRead` with one that leads back to the + // application that created the query + Error.captureStackTrace(err) + throw err + }) + return { callback: cb, result: result } +} + +function makeIdleListener(pool, client) { + return function idleListener(err) { + err.client = client + + client.removeListener('error', idleListener) + client.on('error', () => { + pool.log('additional client error after disconnection due to error', err) + }) + pool._remove(client) + // TODO - document that once the pool emits an error + // the client has already been closed & purged and is unusable + pool.emit('error', err, client) + } +} + +class Pool extends EventEmitter { + constructor(options, Client) { + super() + this.options = Object.assign({}, options) + + if (options != null && 'password' in options) { + // "hiding" the password so it doesn't show up in stack traces + // or if the client is console.logged + Object.defineProperty(this.options, 'password', { + configurable: true, + enumerable: false, + writable: true, + value: options.password, + }) + } + if (options != null && options.ssl && options.ssl.key) { + // "hiding" the ssl->key so it doesn't show up in stack traces + // or if the client is console.logged + Object.defineProperty(this.options.ssl, 'key', { + enumerable: false, + }) + } + + this.options.max = this.options.max || this.options.poolSize || 10 + this.options.min = this.options.min || 0 + this.options.maxUses = this.options.maxUses || Infinity + this.options.allowExitOnIdle = this.options.allowExitOnIdle || false + this.options.maxLifetimeSeconds = this.options.maxLifetimeSeconds || 0 + this.log = this.options.log || function () {} + this.Client = this.options.Client || Client || require('pg').Client + this.Promise = this.options.Promise || global.Promise + + if (typeof this.options.idleTimeoutMillis === 'undefined') { + this.options.idleTimeoutMillis = 10000 + } + + this._clients = [] + this._idle = [] + this._expired = new WeakSet() + this._pendingQueue = [] + this._endCallback = undefined + this.ending = false + this.ended = false + } + + _promiseTry(f) { + const Promise = this.Promise + if (typeof Promise.try === 'function') { + return Promise.try(f) + } + return new Promise((resolve) => resolve(f())) + } + + _isFull() { + return this._clients.length >= this.options.max + } + + _isAboveMin() { + return this._clients.length > this.options.min + } + + _pulseQueue() { + this.log('pulse queue') + if (this.ended) { + this.log('pulse queue ended') + return + } + if (this.ending) { + this.log('pulse queue on ending') + if (this._idle.length) { + this._idle.slice().map((item) => { + this._remove(item.client) + }) + } + if (!this._clients.length) { + this.ended = true + this._endCallback() + } + return + } + + // if we don't have any waiting, do nothing + if (!this._pendingQueue.length) { + this.log('no queued requests') + return + } + // if we don't have any idle clients and we have no more room do nothing + if (!this._idle.length && this._isFull()) { + return + } + const pendingItem = this._pendingQueue.shift() + if (this._idle.length) { + const idleItem = this._idle.pop() + clearTimeout(idleItem.timeoutId) + const client = idleItem.client + client.ref && client.ref() + const idleListener = idleItem.idleListener + + return this._acquireClient(client, pendingItem, idleListener, false) + } + if (!this._isFull()) { + return this.newClient(pendingItem) + } + throw new Error('unexpected condition') + } + + _remove(client, callback) { + const removed = removeWhere(this._idle, (item) => item.client === client) + + if (removed !== undefined) { + clearTimeout(removed.timeoutId) + } + + this._clients = this._clients.filter((c) => c !== client) + const context = this + client.end(() => { + context.emit('remove', client) + + if (typeof callback === 'function') { + callback() + } + }) + } + + connect(cb) { + if (this.ending) { + const err = new Error('Cannot use a pool after calling end on the pool') + return cb ? cb(err) : this.Promise.reject(err) + } + + const response = promisify(this.Promise, cb) + const result = response.result + + // if we don't have to connect a new client, don't do so + if (this._isFull() || this._idle.length) { + // if we have idle clients schedule a pulse immediately + if (this._idle.length) { + process.nextTick(() => this._pulseQueue()) + } + + if (!this.options.connectionTimeoutMillis) { + this._pendingQueue.push(new PendingItem(response.callback)) + return result + } + + const queueCallback = (err, res, done) => { + clearTimeout(tid) + response.callback(err, res, done) + } + + const pendingItem = new PendingItem(queueCallback) + + // set connection timeout on checking out an existing client + const tid = setTimeout(() => { + // remove the callback from pending waiters because + // we're going to call it with a timeout error + removeWhere(this._pendingQueue, (i) => i.callback === queueCallback) + pendingItem.timedOut = true + response.callback(new Error('timeout exceeded when trying to connect')) + }, this.options.connectionTimeoutMillis) + + if (tid.unref) { + tid.unref() + } + + this._pendingQueue.push(pendingItem) + return result + } + + this.newClient(new PendingItem(response.callback)) + + return result + } + + newClient(pendingItem) { + const client = new this.Client(this.options) + this._clients.push(client) + const idleListener = makeIdleListener(this, client) + + this.log('checking client timeout') + + // connection timeout logic + let tid + let timeoutHit = false + if (this.options.connectionTimeoutMillis) { + tid = setTimeout(() => { + if (client.connection) { + this.log('ending client due to timeout') + timeoutHit = true + client.connection.stream.destroy() + } else if (!client.isConnected()) { + this.log('ending client due to timeout') + timeoutHit = true + // force kill the node driver, and let libpq do its teardown + client.end() + } + }, this.options.connectionTimeoutMillis) + } + + this.log('connecting new client') + client.connect((err) => { + if (tid) { + clearTimeout(tid) + } + client.on('error', idleListener) + if (err) { + this.log('client failed to connect', err) + // remove the dead client from our list of clients + this._clients = this._clients.filter((c) => c !== client) + if (timeoutHit) { + err = new Error('Connection terminated due to connection timeout', { cause: err }) + } + + // this client won’t be released, so move on immediately + this._pulseQueue() + + if (!pendingItem.timedOut) { + pendingItem.callback(err, undefined, NOOP) + } + } else { + this.log('new client connected') + + if (this.options.onConnect) { + this._promiseTry(() => this.options.onConnect(client)).then( + () => { + this._afterConnect(client, pendingItem, idleListener) + }, + (hookErr) => { + this._clients = this._clients.filter((c) => c !== client) + client.end(() => { + this._pulseQueue() + if (!pendingItem.timedOut) { + pendingItem.callback(hookErr, undefined, NOOP) + } + }) + } + ) + return + } + + return this._afterConnect(client, pendingItem, idleListener) + } + }) + } + + _afterConnect(client, pendingItem, idleListener) { + if (this.options.maxLifetimeSeconds !== 0) { + const maxLifetimeTimeout = setTimeout(() => { + this.log('ending client due to expired lifetime') + this._expired.add(client) + const idleIndex = this._idle.findIndex((idleItem) => idleItem.client === client) + if (idleIndex !== -1) { + this._acquireClient( + client, + new PendingItem((err, client, clientRelease) => clientRelease()), + idleListener, + false + ) + } + }, this.options.maxLifetimeSeconds * 1000) + + maxLifetimeTimeout.unref() + client.once('end', () => clearTimeout(maxLifetimeTimeout)) + } + + return this._acquireClient(client, pendingItem, idleListener, true) + } + + // acquire a client for a pending work item + _acquireClient(client, pendingItem, idleListener, isNew) { + if (isNew) { + this.emit('connect', client) + } + + this.emit('acquire', client) + + client.release = this._releaseOnce(client, idleListener) + + client.removeListener('error', idleListener) + + if (!pendingItem.timedOut) { + if (isNew && this.options.verify) { + this.options.verify(client, (err) => { + if (err) { + client.release(err) + return pendingItem.callback(err, undefined, NOOP) + } + + pendingItem.callback(undefined, client, client.release) + }) + } else { + pendingItem.callback(undefined, client, client.release) + } + } else { + if (isNew && this.options.verify) { + this.options.verify(client, client.release) + } else { + client.release() + } + } + } + + // returns a function that wraps _release and throws if called more than once + _releaseOnce(client, idleListener) { + let released = false + + return (err) => { + if (released) { + throwOnDoubleRelease() + } + + released = true + this._release(client, idleListener, err) + } + } + + // release a client back to the poll, include an error + // to remove it from the pool + _release(client, idleListener, err) { + client.on('error', idleListener) + + client._poolUseCount = (client._poolUseCount || 0) + 1 + + this.emit('release', err, client) + + // TODO(bmc): expose a proper, public interface _queryable and _ending + if (err || this.ending || !client._queryable || client._ending || client._poolUseCount >= this.options.maxUses) { + if (client._poolUseCount >= this.options.maxUses) { + this.log('remove expended client') + } + + return this._remove(client, this._pulseQueue.bind(this)) + } + + const isExpired = this._expired.has(client) + if (isExpired) { + this.log('remove expired client') + this._expired.delete(client) + return this._remove(client, this._pulseQueue.bind(this)) + } + + // idle timeout + let tid + if (this.options.idleTimeoutMillis && this._isAboveMin()) { + tid = setTimeout(() => { + if (this._isAboveMin()) { + this.log('remove idle client') + this._remove(client, this._pulseQueue.bind(this)) + } + }, this.options.idleTimeoutMillis) + + if (this.options.allowExitOnIdle) { + // allow Node to exit if this is all that's left + tid.unref() + } + } + + if (this.options.allowExitOnIdle) { + client.unref() + } + + this._idle.push(new IdleItem(client, idleListener, tid)) + this._pulseQueue() + } + + query(text, values, cb) { + // guard clause against passing a function as the first parameter + if (typeof text === 'function') { + const response = promisify(this.Promise, text) + setImmediate(function () { + return response.callback(new Error('Passing a function as the first parameter to pool.query is not supported')) + }) + return response.result + } + + // allow plain text query without values, but callback + if (typeof values === 'function') { + cb = values + values = undefined + } + const response = promisify(this.Promise, cb) + cb = response.callback + + this.connect((err, client) => { + if (err) { + return cb(err) + } + + let clientReleased = false + const onError = (err) => { + if (clientReleased) { + return + } + clientReleased = true + client.release(err) + cb(err) + } + + client.once('error', onError) + this.log('dispatching query') + try { + client.query(text, values, (err, res) => { + this.log('query dispatched') + client.removeListener('error', onError) + if (clientReleased) { + return + } + clientReleased = true + client.release(err) + if (err) { + return cb(err) + } + return cb(undefined, res) + }) + } catch (err) { + client.release(err) + return cb(err) + } + }) + return response.result + } + + end(cb) { + this.log('ending') + if (this.ending) { + const err = new Error('Called end on pool more than once') + return cb ? cb(err) : this.Promise.reject(err) + } + this.ending = true + const promised = promisify(this.Promise, cb) + this._endCallback = promised.callback + this._pulseQueue() + return promised.result + } + + get waitingCount() { + return this._pendingQueue.length + } + + get idleCount() { + return this._idle.length + } + + get expiredCount() { + return this._clients.reduce((acc, client) => acc + (this._expired.has(client) ? 1 : 0), 0) + } + + get totalCount() { + return this._clients.length + } +} +module.exports = Pool diff --git a/node_modules/pg-pool/package.json b/node_modules/pg-pool/package.json new file mode 100644 index 0000000..16cf295 --- /dev/null +++ b/node_modules/pg-pool/package.json @@ -0,0 +1,51 @@ +{ + "name": "pg-pool", + "version": "3.14.0", + "description": "Connection pool for node-postgres", + "main": "index.js", + "exports": { + ".": { + "import": "./esm/index.mjs", + "require": "./index.js", + "default": "./index.js" + } + }, + "directories": { + "test": "test" + }, + "scripts": { + "test": " node_modules/.bin/mocha" + }, + "repository": { + "type": "git", + "url": "git://github.com/brianc/node-postgres.git", + "directory": "packages/pg-pool" + }, + "keywords": [ + "pg", + "postgres", + "pool", + "database" + ], + "author": "Brian M. Carlson", + "license": "MIT", + "bugs": { + "url": "https://github.com/brianc/node-postgres/issues" + }, + "homepage": "https://github.com/brianc/node-postgres/tree/master/packages/pg-pool#readme", + "devDependencies": { + "bluebird": "3.7.2", + "co": "4.6.0", + "expect.js": "0.3.1", + "lodash": "^4.17.11", + "mocha": "^11.7.5" + }, + "peerDependencies": { + "pg": ">=8.0" + }, + "files": [ + "index.js", + "esm" + ], + "gitHead": "544b1ce8152bc280e398dc1e8a66920abe6a640e" +} diff --git a/node_modules/pg-protocol/LICENSE b/node_modules/pg-protocol/LICENSE new file mode 100644 index 0000000..5c14056 --- /dev/null +++ b/node_modules/pg-protocol/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010 - 2021 Brian Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/pg-protocol/README.md b/node_modules/pg-protocol/README.md new file mode 100644 index 0000000..8c52e40 --- /dev/null +++ b/node_modules/pg-protocol/README.md @@ -0,0 +1,3 @@ +# pg-protocol + +Low level postgres wire protocol parser and serializer written in Typescript. Used by node-postgres. Needs more documentation. :smile: diff --git a/node_modules/pg-protocol/dist/b.d.ts b/node_modules/pg-protocol/dist/b.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/pg-protocol/dist/b.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/pg-protocol/dist/b.js b/node_modules/pg-protocol/dist/b.js new file mode 100644 index 0000000..6c47c10 --- /dev/null +++ b/node_modules/pg-protocol/dist/b.js @@ -0,0 +1,23 @@ +"use strict"; +// file for microbenchmarking +Object.defineProperty(exports, "__esModule", { value: true }); +const buffer_reader_1 = require("./buffer-reader"); +const LOOPS = 1000; +let count = 0; +const start = performance.now(); +const reader = new buffer_reader_1.BufferReader(); +const buffer = Buffer.from([33, 33, 33, 33, 33, 33, 33, 0]); +const run = () => { + if (count > LOOPS) { + console.log(performance.now() - start); + return; + } + count++; + for (let i = 0; i < LOOPS; i++) { + reader.setBuffer(0, buffer); + reader.cstring(); + } + setImmediate(run); +}; +run(); +//# sourceMappingURL=b.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/b.js.map b/node_modules/pg-protocol/dist/b.js.map new file mode 100644 index 0000000..ccc8d6f --- /dev/null +++ b/node_modules/pg-protocol/dist/b.js.map @@ -0,0 +1 @@ +{"version":3,"file":"b.js","sourceRoot":"","sources":["../src/b.ts"],"names":[],"mappings":";AAAA,6BAA6B;;AAE7B,mDAA8C;AAE9C,MAAM,KAAK,GAAG,IAAI,CAAA;AAClB,IAAI,KAAK,GAAG,CAAC,CAAA;AACb,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;AAE/B,MAAM,MAAM,GAAG,IAAI,4BAAY,EAAE,CAAA;AACjC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;AAE3D,MAAM,GAAG,GAAG,GAAG,EAAE;IACf,IAAI,KAAK,GAAG,KAAK,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAA;QACtC,OAAM;KACP;IACD,KAAK,EAAE,CAAA;IACP,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;QAC3B,MAAM,CAAC,OAAO,EAAE,CAAA;KACjB;IACD,YAAY,CAAC,GAAG,CAAC,CAAA;AACnB,CAAC,CAAA;AAED,GAAG,EAAE,CAAA"} \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/buffer-reader.d.ts b/node_modules/pg-protocol/dist/buffer-reader.d.ts new file mode 100644 index 0000000..41f753e --- /dev/null +++ b/node_modules/pg-protocol/dist/buffer-reader.d.ts @@ -0,0 +1,15 @@ +/// +export declare class BufferReader { + private offset; + private buffer; + private encoding; + constructor(offset?: number); + setBuffer(offset: number, buffer: Buffer): void; + int16(): number; + byte(): number; + int32(): number; + uint32(): number; + string(length: number): string; + cstring(): string; + bytes(length: number): Buffer; +} diff --git a/node_modules/pg-protocol/dist/buffer-reader.js b/node_modules/pg-protocol/dist/buffer-reader.js new file mode 100644 index 0000000..1679fa8 --- /dev/null +++ b/node_modules/pg-protocol/dist/buffer-reader.js @@ -0,0 +1,55 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BufferReader = void 0; +class BufferReader { + constructor(offset = 0) { + this.offset = offset; + this.buffer = Buffer.allocUnsafe(0); + // TODO(bmc): support non-utf8 encoding? + this.encoding = 'utf-8'; + } + setBuffer(offset, buffer) { + this.offset = offset; + this.buffer = buffer; + } + int16() { + const result = this.buffer.readInt16BE(this.offset); + this.offset += 2; + return result; + } + byte() { + const result = this.buffer[this.offset]; + this.offset++; + return result; + } + int32() { + const result = this.buffer.readInt32BE(this.offset); + this.offset += 4; + return result; + } + uint32() { + const result = this.buffer.readUInt32BE(this.offset); + this.offset += 4; + return result; + } + string(length) { + const result = this.buffer.toString(this.encoding, this.offset, this.offset + length); + this.offset += length; + return result; + } + cstring() { + const start = this.offset; + let end = start; + // eslint-disable-next-line no-empty + while (this.buffer[end++] !== 0) { } + this.offset = end; + return this.buffer.toString(this.encoding, start, end - 1); + } + bytes(length) { + const result = this.buffer.slice(this.offset, this.offset + length); + this.offset += length; + return result; + } +} +exports.BufferReader = BufferReader; +//# sourceMappingURL=buffer-reader.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/buffer-reader.js.map b/node_modules/pg-protocol/dist/buffer-reader.js.map new file mode 100644 index 0000000..cbe5038 --- /dev/null +++ b/node_modules/pg-protocol/dist/buffer-reader.js.map @@ -0,0 +1 @@ +{"version":3,"file":"buffer-reader.js","sourceRoot":"","sources":["../src/buffer-reader.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAY;IAMvB,YAAoB,SAAiB,CAAC;QAAlB,WAAM,GAAN,MAAM,CAAY;QAL9B,WAAM,GAAW,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QAE9C,wCAAwC;QAChC,aAAQ,GAAmB,OAAO,CAAA;IAED,CAAC;IAEnC,SAAS,CAAC,MAAc,EAAE,MAAc;QAC7C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAEM,KAAK;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACnD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;QAChB,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,IAAI;QACT,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACvC,IAAI,CAAC,MAAM,EAAE,CAAA;QACb,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,KAAK;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACnD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;QAChB,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,MAAM;QACX,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACpD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;QAChB,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,MAAM,CAAC,MAAc;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;QACrF,IAAI,CAAC,MAAM,IAAI,MAAM,CAAA;QACrB,OAAO,MAAM,CAAA;IACf,CAAC;IAEM,OAAO;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAA;QACzB,IAAI,GAAG,GAAG,KAAK,CAAA;QACf,oCAAoC;QACpC,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,GAAE;QACnC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAA;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;IAC5D,CAAC;IAEM,KAAK,CAAC,MAAc;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;QACnE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAA;QACrB,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAzDD,oCAyDC"} \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/buffer-writer.d.ts b/node_modules/pg-protocol/dist/buffer-writer.d.ts new file mode 100644 index 0000000..4ac41e6 --- /dev/null +++ b/node_modules/pg-protocol/dist/buffer-writer.d.ts @@ -0,0 +1,16 @@ +/// +export declare class Writer { + private size; + private buffer; + private offset; + private headerPosition; + constructor(size?: number); + private ensure; + addInt32(num: number): Writer; + addInt16(num: number): Writer; + addCString(string: string): Writer; + addString(string?: string): Writer; + add(otherBuffer: Buffer): Writer; + private join; + flush(code?: number): Buffer; +} diff --git a/node_modules/pg-protocol/dist/buffer-writer.js b/node_modules/pg-protocol/dist/buffer-writer.js new file mode 100644 index 0000000..76b3f8c --- /dev/null +++ b/node_modules/pg-protocol/dist/buffer-writer.js @@ -0,0 +1,81 @@ +"use strict"; +//binary data writer tuned for encoding binary specific to the postgres binary protocol +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Writer = void 0; +class Writer { + constructor(size = 256) { + this.size = size; + this.offset = 5; + this.headerPosition = 0; + this.buffer = Buffer.allocUnsafe(size); + } + ensure(size) { + const remaining = this.buffer.length - this.offset; + if (remaining < size) { + const oldBuffer = this.buffer; + // exponential growth factor of around ~ 1.5 + // https://stackoverflow.com/questions/2269063/buffer-growth-strategy + const newSize = oldBuffer.length + (oldBuffer.length >> 1) + size; + this.buffer = Buffer.allocUnsafe(newSize); + oldBuffer.copy(this.buffer); + } + } + addInt32(num) { + this.ensure(4); + this.buffer[this.offset++] = (num >>> 24) & 0xff; + this.buffer[this.offset++] = (num >>> 16) & 0xff; + this.buffer[this.offset++] = (num >>> 8) & 0xff; + this.buffer[this.offset++] = (num >>> 0) & 0xff; + return this; + } + addInt16(num) { + this.ensure(2); + this.buffer[this.offset++] = (num >>> 8) & 0xff; + this.buffer[this.offset++] = (num >>> 0) & 0xff; + return this; + } + addCString(string) { + if (!string) { + this.ensure(1); + } + else { + const len = Buffer.byteLength(string); + this.ensure(len + 1); // +1 for null terminator + this.buffer.write(string, this.offset, 'utf-8'); + this.offset += len; + } + this.buffer[this.offset++] = 0; // null terminator + return this; + } + addString(string = '') { + const len = Buffer.byteLength(string); + this.ensure(len); + this.buffer.write(string, this.offset); + this.offset += len; + return this; + } + add(otherBuffer) { + this.ensure(otherBuffer.length); + otherBuffer.copy(this.buffer, this.offset); + this.offset += otherBuffer.length; + return this; + } + join(code) { + if (code) { + this.buffer[this.headerPosition] = code; + //length is everything in this packet minus the code + const length = this.offset - (this.headerPosition + 1); + this.buffer.writeInt32BE(length, this.headerPosition + 1); + } + return this.buffer.slice(code ? 0 : 5, this.offset); + } + flush(code) { + const result = this.join(code); + this.offset = 5; + this.headerPosition = 0; + this.buffer = Buffer.allocUnsafe(this.size); + return result; + } +} +exports.Writer = Writer; +//# sourceMappingURL=buffer-writer.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/buffer-writer.js.map b/node_modules/pg-protocol/dist/buffer-writer.js.map new file mode 100644 index 0000000..5aa1ba0 --- /dev/null +++ b/node_modules/pg-protocol/dist/buffer-writer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"buffer-writer.js","sourceRoot":"","sources":["../src/buffer-writer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/inbound-parser.test.d.ts b/node_modules/pg-protocol/dist/inbound-parser.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/pg-protocol/dist/inbound-parser.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/pg-protocol/dist/inbound-parser.test.js b/node_modules/pg-protocol/dist/inbound-parser.test.js new file mode 100644 index 0000000..f64fa74 --- /dev/null +++ b/node_modules/pg-protocol/dist/inbound-parser.test.js @@ -0,0 +1,530 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const test_buffers_1 = __importDefault(require("./testing/test-buffers")); +const buffer_list_1 = __importDefault(require("./testing/buffer-list")); +const _1 = require("."); +const assert_1 = __importDefault(require("assert")); +const stream_1 = require("stream"); +const parser_1 = require("./parser"); +const authOkBuffer = test_buffers_1.default.authenticationOk(); +const paramStatusBuffer = test_buffers_1.default.parameterStatus('client_encoding', 'UTF8'); +const readyForQueryBuffer = test_buffers_1.default.readyForQuery(); +const backendKeyDataBuffer = test_buffers_1.default.backendKeyData(1, 2); +const commandCompleteBuffer = test_buffers_1.default.commandComplete('SELECT 3'); +const parseCompleteBuffer = test_buffers_1.default.parseComplete(); +const bindCompleteBuffer = test_buffers_1.default.bindComplete(); +const portalSuspendedBuffer = test_buffers_1.default.portalSuspended(); +const row1 = { + name: 'id', + tableID: 1, + attributeNumber: 2, + dataTypeID: 3, + dataTypeSize: 4, + typeModifier: 5, + formatCode: 0, +}; +const oneRowDescBuff = test_buffers_1.default.rowDescription([row1]); +row1.name = 'bang'; +const twoRowBuf = test_buffers_1.default.rowDescription([ + row1, + { + name: 'whoah', + tableID: 10, + attributeNumber: 11, + dataTypeID: 12, + dataTypeSize: 13, + typeModifier: 14, + formatCode: 0, + }, +]); +const rowWithBigOids = { + name: 'bigoid', + tableID: 3000000001, + attributeNumber: 2, + dataTypeID: 3000000003, + dataTypeSize: 4, + typeModifier: 5, + formatCode: 0, +}; +const bigOidDescBuff = test_buffers_1.default.rowDescription([rowWithBigOids]); +const emptyRowFieldBuf = test_buffers_1.default.dataRow([]); +const oneFieldBuf = test_buffers_1.default.dataRow(['test']); +const expectedAuthenticationOkayMessage = { + name: 'authenticationOk', + length: 8, +}; +const expectedParameterStatusMessage = { + name: 'parameterStatus', + parameterName: 'client_encoding', + parameterValue: 'UTF8', + length: 25, +}; +const expectedBackendKeyDataMessage = { + name: 'backendKeyData', + processID: 1, + secretKey: 2, +}; +const expectedReadyForQueryMessage = { + name: 'readyForQuery', + length: 5, + status: 'I', +}; +const expectedCommandCompleteMessage = { + name: 'commandComplete', + length: 13, + text: 'SELECT 3', +}; +const emptyRowDescriptionBuffer = new buffer_list_1.default() + .addInt16(0) // number of fields + .join(true, 'T'); +const expectedEmptyRowDescriptionMessage = { + name: 'rowDescription', + length: 6, + fieldCount: 0, + fields: [], +}; +const expectedOneRowMessage = { + name: 'rowDescription', + length: 27, + fieldCount: 1, + fields: [ + { + name: 'id', + tableID: 1, + columnID: 2, + dataTypeID: 3, + dataTypeSize: 4, + dataTypeModifier: 5, + format: 'text', + }, + ], +}; +const expectedTwoRowMessage = { + name: 'rowDescription', + length: 53, + fieldCount: 2, + fields: [ + { + name: 'bang', + tableID: 1, + columnID: 2, + dataTypeID: 3, + dataTypeSize: 4, + dataTypeModifier: 5, + format: 'text', + }, + { + name: 'whoah', + tableID: 10, + columnID: 11, + dataTypeID: 12, + dataTypeSize: 13, + dataTypeModifier: 14, + format: 'text', + }, + ], +}; +const expectedBigOidMessage = { + name: 'rowDescription', + length: 31, + fieldCount: 1, + fields: [ + { + name: 'bigoid', + tableID: 3000000001, + columnID: 2, + dataTypeID: 3000000003, + dataTypeSize: 4, + dataTypeModifier: 5, + format: 'text', + }, + ], +}; +const emptyParameterDescriptionBuffer = new buffer_list_1.default() + .addInt16(0) // number of parameters + .join(true, 't'); +const oneParameterDescBuf = test_buffers_1.default.parameterDescription([1111]); +const twoParameterDescBuf = test_buffers_1.default.parameterDescription([2222, 3333]); +const expectedEmptyParameterDescriptionMessage = { + name: 'parameterDescription', + length: 6, + parameterCount: 0, + dataTypeIDs: [], +}; +const expectedOneParameterMessage = { + name: 'parameterDescription', + length: 10, + parameterCount: 1, + dataTypeIDs: [1111], +}; +const expectedTwoParameterMessage = { + name: 'parameterDescription', + length: 14, + parameterCount: 2, + dataTypeIDs: [2222, 3333], +}; +const testForMessage = function (buffer, expectedMessage) { + it('receives and parses ' + expectedMessage.name, () => __awaiter(this, void 0, void 0, function* () { + const messages = yield parseBuffers([buffer]); + const [lastMessage] = messages; + for (const key in expectedMessage) { + assert_1.default.deepEqual(lastMessage[key], expectedMessage[key]); + } + })); +}; +const plainPasswordBuffer = test_buffers_1.default.authenticationCleartextPassword(); +const md5PasswordBuffer = test_buffers_1.default.authenticationMD5Password(); +const SASLBuffer = test_buffers_1.default.authenticationSASL(); +const SASLContinueBuffer = test_buffers_1.default.authenticationSASLContinue(); +const SASLFinalBuffer = test_buffers_1.default.authenticationSASLFinal(); +const expectedPlainPasswordMessage = { + name: 'authenticationCleartextPassword', +}; +const expectedMD5PasswordMessage = { + name: 'authenticationMD5Password', + salt: Buffer.from([1, 2, 3, 4]), +}; +const expectedSASLMessage = { + name: 'authenticationSASL', + mechanisms: ['SCRAM-SHA-256'], +}; +const expectedSASLContinueMessage = { + name: 'authenticationSASLContinue', + data: 'data', +}; +const expectedSASLFinalMessage = { + name: 'authenticationSASLFinal', + data: 'data', +}; +const notificationResponseBuffer = test_buffers_1.default.notification(4, 'hi', 'boom'); +const expectedNotificationResponseMessage = { + name: 'notification', + processId: 4, + channel: 'hi', + payload: 'boom', +}; +const parseBuffers = (buffers) => __awaiter(void 0, void 0, void 0, function* () { + const stream = new stream_1.PassThrough(); + for (const buffer of buffers) { + stream.write(buffer); + } + stream.end(); + const msgs = []; + yield (0, _1.parse)(stream, (msg) => msgs.push(msg)); + return msgs; +}); +describe('PgPacketStream', function () { + testForMessage(authOkBuffer, expectedAuthenticationOkayMessage); + testForMessage(plainPasswordBuffer, expectedPlainPasswordMessage); + testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage); + testForMessage(SASLBuffer, expectedSASLMessage); + testForMessage(SASLContinueBuffer, expectedSASLContinueMessage); + // this exercises a found bug in the parser: + // https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084 + // and adds a test which is deterministic, rather than relying on network packet chunking + const extendedSASLContinueBuffer = Buffer.concat([SASLContinueBuffer, Buffer.from([1, 2, 3, 4])]); + testForMessage(extendedSASLContinueBuffer, expectedSASLContinueMessage); + testForMessage(SASLFinalBuffer, expectedSASLFinalMessage); + // this exercises a found bug in the parser: + // https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084 + // and adds a test which is deterministic, rather than relying on network packet chunking + const extendedSASLFinalBuffer = Buffer.concat([SASLFinalBuffer, Buffer.from([1, 2, 4, 5])]); + testForMessage(extendedSASLFinalBuffer, expectedSASLFinalMessage); + testForMessage(paramStatusBuffer, expectedParameterStatusMessage); + testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage); + testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage); + testForMessage(commandCompleteBuffer, expectedCommandCompleteMessage); + testForMessage(notificationResponseBuffer, expectedNotificationResponseMessage); + testForMessage(test_buffers_1.default.emptyQuery(), { + name: 'emptyQuery', + length: 4, + }); + testForMessage(Buffer.from([0x6e, 0, 0, 0, 4]), { + name: 'noData', + }); + describe('rowDescription messages', function () { + testForMessage(emptyRowDescriptionBuffer, expectedEmptyRowDescriptionMessage); + testForMessage(oneRowDescBuff, expectedOneRowMessage); + testForMessage(twoRowBuf, expectedTwoRowMessage); + testForMessage(bigOidDescBuff, expectedBigOidMessage); + }); + describe('parameterDescription messages', function () { + testForMessage(emptyParameterDescriptionBuffer, expectedEmptyParameterDescriptionMessage); + testForMessage(oneParameterDescBuf, expectedOneParameterMessage); + testForMessage(twoParameterDescBuf, expectedTwoParameterMessage); + }); + describe('parsing rows', function () { + describe('parsing empty row', function () { + testForMessage(emptyRowFieldBuf, { + name: 'dataRow', + fieldCount: 0, + }); + }); + describe('parsing data row with fields', function () { + testForMessage(oneFieldBuf, { + name: 'dataRow', + fieldCount: 1, + fields: ['test'], + }); + }); + }); + describe('notice message', function () { + // this uses the same logic as error message + const buff = test_buffers_1.default.notice([{ type: 'C', value: 'code' }]); + testForMessage(buff, { + name: 'notice', + code: 'code', + }); + }); + testForMessage(test_buffers_1.default.error([]), { + name: 'error', + }); + describe('with all the fields', function () { + const buffer = test_buffers_1.default.error([ + { + type: 'S', + value: 'ERROR', + }, + { + type: 'C', + value: 'code', + }, + { + type: 'M', + value: 'message', + }, + { + type: 'D', + value: 'details', + }, + { + type: 'H', + value: 'hint', + }, + { + type: 'P', + value: '100', + }, + { + type: 'p', + value: '101', + }, + { + type: 'q', + value: 'query', + }, + { + type: 'W', + value: 'where', + }, + { + type: 'F', + value: 'file', + }, + { + type: 'L', + value: 'line', + }, + { + type: 'R', + value: 'routine', + }, + { + type: 'Z', + value: 'alsdkf', + }, + ]); + testForMessage(buffer, { + name: 'error', + severity: 'ERROR', + code: 'code', + message: 'message', + detail: 'details', + hint: 'hint', + position: '100', + internalPosition: '101', + internalQuery: 'query', + where: 'where', + file: 'file', + line: 'line', + routine: 'routine', + }); + }); + testForMessage(parseCompleteBuffer, { + name: 'parseComplete', + }); + testForMessage(bindCompleteBuffer, { + name: 'bindComplete', + }); + testForMessage(bindCompleteBuffer, { + name: 'bindComplete', + }); + testForMessage(test_buffers_1.default.closeComplete(), { + name: 'closeComplete', + }); + describe('parses portal suspended message', function () { + testForMessage(portalSuspendedBuffer, { + name: 'portalSuspended', + }); + }); + describe('parses replication start message', function () { + testForMessage(Buffer.from([0x57, 0x00, 0x00, 0x00, 0x04]), { + name: 'replicationStart', + length: 4, + }); + }); + describe('copy', () => { + testForMessage(test_buffers_1.default.copyIn(0), { + name: 'copyInResponse', + length: 7, + binary: false, + columnTypes: [], + }); + testForMessage(test_buffers_1.default.copyIn(2), { + name: 'copyInResponse', + length: 11, + binary: false, + columnTypes: [0, 1], + }); + testForMessage(test_buffers_1.default.copyOut(0), { + name: 'copyOutResponse', + length: 7, + binary: false, + columnTypes: [], + }); + testForMessage(test_buffers_1.default.copyOut(3), { + name: 'copyOutResponse', + length: 13, + binary: false, + columnTypes: [0, 1, 2], + }); + testForMessage(test_buffers_1.default.copyDone(), { + name: 'copyDone', + length: 4, + }); + testForMessage(test_buffers_1.default.copyData(Buffer.from([5, 6, 7])), { + name: 'copyData', + length: 7, + chunk: Buffer.from([5, 6, 7]), + }); + }); + // since the data message on a stream can randomly divide the incomming + // tcp packets anywhere, we need to make sure we can parse every single + // split on a tcp message + describe('split buffer, single message parsing', function () { + const fullBuffer = test_buffers_1.default.dataRow([null, 'bang', 'zug zug', null, '!']); + it('parses when full buffer comes in', function () { + return __awaiter(this, void 0, void 0, function* () { + const messages = yield parseBuffers([fullBuffer]); + const message = messages[0]; + assert_1.default.equal(message.fields.length, 5); + assert_1.default.equal(message.fields[0], null); + assert_1.default.equal(message.fields[1], 'bang'); + assert_1.default.equal(message.fields[2], 'zug zug'); + assert_1.default.equal(message.fields[3], null); + assert_1.default.equal(message.fields[4], '!'); + }); + }); + const testMessageReceivedAfterSplitAt = function (split) { + return __awaiter(this, void 0, void 0, function* () { + const firstBuffer = Buffer.alloc(fullBuffer.length - split); + const secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length); + fullBuffer.copy(firstBuffer, 0, 0); + fullBuffer.copy(secondBuffer, 0, firstBuffer.length); + const messages = yield parseBuffers([firstBuffer, secondBuffer]); + const message = messages[0]; + assert_1.default.equal(message.fields.length, 5); + assert_1.default.equal(message.fields[0], null); + assert_1.default.equal(message.fields[1], 'bang'); + assert_1.default.equal(message.fields[2], 'zug zug'); + assert_1.default.equal(message.fields[3], null); + assert_1.default.equal(message.fields[4], '!'); + }); + }; + it('parses when split in the middle', function () { + return testMessageReceivedAfterSplitAt(6); + }); + it('parses when split at end', function () { + return testMessageReceivedAfterSplitAt(2); + }); + it('parses when split at beginning', function () { + return Promise.all([ + testMessageReceivedAfterSplitAt(fullBuffer.length - 2), + testMessageReceivedAfterSplitAt(fullBuffer.length - 1), + testMessageReceivedAfterSplitAt(fullBuffer.length - 5), + ]); + }); + }); + describe('split buffer, multiple message parsing', function () { + const dataRowBuffer = test_buffers_1.default.dataRow(['!']); + const readyForQueryBuffer = test_buffers_1.default.readyForQuery(); + const fullBuffer = Buffer.alloc(dataRowBuffer.length + readyForQueryBuffer.length); + dataRowBuffer.copy(fullBuffer, 0, 0); + readyForQueryBuffer.copy(fullBuffer, dataRowBuffer.length, 0); + const verifyMessages = function (messages) { + assert_1.default.strictEqual(messages.length, 2); + assert_1.default.deepEqual(messages[0], { + name: 'dataRow', + fieldCount: 1, + length: 11, + fields: ['!'], + }); + assert_1.default.equal(messages[0].fields[0], '!'); + assert_1.default.deepEqual(messages[1], { + name: 'readyForQuery', + length: 5, + status: 'I', + }); + }; + // sanity check + it('receives both messages when packet is not split', function () { + return __awaiter(this, void 0, void 0, function* () { + const messages = yield parseBuffers([fullBuffer]); + verifyMessages(messages); + }); + }); + const splitAndVerifyTwoMessages = function (split) { + return __awaiter(this, void 0, void 0, function* () { + const firstBuffer = Buffer.alloc(fullBuffer.length - split); + const secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length); + fullBuffer.copy(firstBuffer, 0, 0); + fullBuffer.copy(secondBuffer, 0, firstBuffer.length); + const messages = yield parseBuffers([firstBuffer, secondBuffer]); + verifyMessages(messages); + }); + }; + describe('receives both messages when packet is split', function () { + it('in the middle', function () { + return splitAndVerifyTwoMessages(11); + }); + it('at the front', function () { + return Promise.all([ + splitAndVerifyTwoMessages(fullBuffer.length - 1), + splitAndVerifyTwoMessages(fullBuffer.length - 4), + splitAndVerifyTwoMessages(fullBuffer.length - 6), + ]); + }); + it('at the end', function () { + return Promise.all([splitAndVerifyTwoMessages(8), splitAndVerifyTwoMessages(1)]); + }); + }); + }); + it('cleans up the reader after handling a packet', function () { + const parser = new parser_1.Parser(); + parser.parse(oneFieldBuf, () => { }); + assert_1.default.strictEqual(parser.reader.buffer.byteLength, 0); + }); +}); +//# sourceMappingURL=inbound-parser.test.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/inbound-parser.test.js.map b/node_modules/pg-protocol/dist/inbound-parser.test.js.map new file mode 100644 index 0000000..aaa00e6 --- /dev/null +++ b/node_modules/pg-protocol/dist/inbound-parser.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"inbound-parser.test.js","sourceRoot":"","sources":["../src/inbound-parser.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/index.d.ts b/node_modules/pg-protocol/dist/index.d.ts new file mode 100644 index 0000000..3961def --- /dev/null +++ b/node_modules/pg-protocol/dist/index.d.ts @@ -0,0 +1,6 @@ +/// +import { DatabaseError } from './messages'; +import { serialize } from './serializer'; +import { MessageCallback } from './parser'; +export declare function parse(stream: NodeJS.ReadableStream, callback: MessageCallback): Promise; +export { serialize, DatabaseError }; diff --git a/node_modules/pg-protocol/dist/index.js b/node_modules/pg-protocol/dist/index.js new file mode 100644 index 0000000..7eca3bf --- /dev/null +++ b/node_modules/pg-protocol/dist/index.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DatabaseError = exports.serialize = exports.parse = void 0; +const messages_1 = require("./messages"); +Object.defineProperty(exports, "DatabaseError", { enumerable: true, get: function () { return messages_1.DatabaseError; } }); +const serializer_1 = require("./serializer"); +Object.defineProperty(exports, "serialize", { enumerable: true, get: function () { return serializer_1.serialize; } }); +const parser_1 = require("./parser"); +function parse(stream, callback) { + const parser = new parser_1.Parser(); + stream.on('data', (buffer) => parser.parse(buffer, callback)); + return new Promise((resolve) => stream.on('end', () => resolve())); +} +exports.parse = parse; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/index.js.map b/node_modules/pg-protocol/dist/index.js.map new file mode 100644 index 0000000..db9423f --- /dev/null +++ b/node_modules/pg-protocol/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAA0C;AAUtB,8FAVX,wBAAa,OAUW;AATjC,6CAAwC;AAS/B,0FATA,sBAAS,OASA;AARlB,qCAAkD;AAElD,SAAgB,KAAK,CAAC,MAA6B,EAAE,QAAyB;IAC5E,MAAM,MAAM,GAAG,IAAI,eAAM,EAAE,CAAA;IAC3B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IACrE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;AACpE,CAAC;AAJD,sBAIC"} \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/messages.d.ts b/node_modules/pg-protocol/dist/messages.d.ts new file mode 100644 index 0000000..f8f2e63 --- /dev/null +++ b/node_modules/pg-protocol/dist/messages.d.ts @@ -0,0 +1,162 @@ +/// +export declare type Mode = 'text' | 'binary'; +export declare type MessageName = 'parseComplete' | 'bindComplete' | 'closeComplete' | 'noData' | 'portalSuspended' | 'replicationStart' | 'emptyQuery' | 'copyDone' | 'copyData' | 'rowDescription' | 'parameterDescription' | 'parameterStatus' | 'backendKeyData' | 'notification' | 'readyForQuery' | 'commandComplete' | 'dataRow' | 'copyInResponse' | 'copyOutResponse' | 'authenticationOk' | 'authenticationMD5Password' | 'authenticationCleartextPassword' | 'authenticationSASL' | 'authenticationSASLContinue' | 'authenticationSASLFinal' | 'error' | 'notice'; +export interface BackendMessage { + name: MessageName; + length: number; +} +export declare const parseComplete: BackendMessage; +export declare const bindComplete: BackendMessage; +export declare const closeComplete: BackendMessage; +export declare const noData: BackendMessage; +export declare const portalSuspended: BackendMessage; +export declare const replicationStart: BackendMessage; +export declare const emptyQuery: BackendMessage; +export declare const copyDone: BackendMessage; +interface NoticeOrError { + message: string | undefined; + severity: string | undefined; + code: string | undefined; + detail: string | undefined; + hint: string | undefined; + position: string | undefined; + internalPosition: string | undefined; + internalQuery: string | undefined; + where: string | undefined; + schema: string | undefined; + table: string | undefined; + column: string | undefined; + dataType: string | undefined; + constraint: string | undefined; + file: string | undefined; + line: string | undefined; + routine: string | undefined; +} +export declare class DatabaseError extends Error implements NoticeOrError { + readonly length: number; + readonly name: MessageName; + severity: string | undefined; + code: string | undefined; + detail: string | undefined; + hint: string | undefined; + position: string | undefined; + internalPosition: string | undefined; + internalQuery: string | undefined; + where: string | undefined; + schema: string | undefined; + table: string | undefined; + column: string | undefined; + dataType: string | undefined; + constraint: string | undefined; + file: string | undefined; + line: string | undefined; + routine: string | undefined; + constructor(message: string, length: number, name: MessageName); +} +export declare class CopyDataMessage { + readonly length: number; + readonly chunk: Buffer; + readonly name = "copyData"; + constructor(length: number, chunk: Buffer); +} +export declare class CopyResponse { + readonly length: number; + readonly name: MessageName; + readonly binary: boolean; + readonly columnTypes: number[]; + constructor(length: number, name: MessageName, binary: boolean, columnCount: number); +} +export declare class Field { + readonly name: string; + readonly tableID: number; + readonly columnID: number; + readonly dataTypeID: number; + readonly dataTypeSize: number; + readonly dataTypeModifier: number; + readonly format: Mode; + constructor(name: string, tableID: number, columnID: number, dataTypeID: number, dataTypeSize: number, dataTypeModifier: number, format: Mode); +} +export declare class RowDescriptionMessage { + readonly length: number; + readonly fieldCount: number; + readonly name: MessageName; + readonly fields: Field[]; + constructor(length: number, fieldCount: number); +} +export declare class ParameterDescriptionMessage { + readonly length: number; + readonly parameterCount: number; + readonly name: MessageName; + readonly dataTypeIDs: number[]; + constructor(length: number, parameterCount: number); +} +export declare class ParameterStatusMessage { + readonly length: number; + readonly parameterName: string; + readonly parameterValue: string; + readonly name: MessageName; + constructor(length: number, parameterName: string, parameterValue: string); +} +export declare class AuthenticationMD5Password implements BackendMessage { + readonly length: number; + readonly salt: Buffer; + readonly name: MessageName; + constructor(length: number, salt: Buffer); +} +export declare class BackendKeyDataMessage { + readonly length: number; + readonly processID: number; + readonly secretKey: number; + readonly name: MessageName; + constructor(length: number, processID: number, secretKey: number); +} +export declare class NotificationResponseMessage { + readonly length: number; + readonly processId: number; + readonly channel: string; + readonly payload: string; + readonly name: MessageName; + constructor(length: number, processId: number, channel: string, payload: string); +} +export declare class ReadyForQueryMessage { + readonly length: number; + readonly status: string; + readonly name: MessageName; + constructor(length: number, status: string); +} +export declare class CommandCompleteMessage { + readonly length: number; + readonly text: string; + readonly name: MessageName; + constructor(length: number, text: string); +} +export declare class DataRowMessage { + length: number; + fields: any[]; + readonly fieldCount: number; + readonly name: MessageName; + constructor(length: number, fields: any[]); +} +export declare class NoticeMessage implements BackendMessage, NoticeOrError { + readonly length: number; + readonly message: string | undefined; + constructor(length: number, message: string | undefined); + readonly name = "notice"; + severity: string | undefined; + code: string | undefined; + detail: string | undefined; + hint: string | undefined; + position: string | undefined; + internalPosition: string | undefined; + internalQuery: string | undefined; + where: string | undefined; + schema: string | undefined; + table: string | undefined; + column: string | undefined; + dataType: string | undefined; + constraint: string | undefined; + file: string | undefined; + line: string | undefined; + routine: string | undefined; +} +export {}; diff --git a/node_modules/pg-protocol/dist/messages.js b/node_modules/pg-protocol/dist/messages.js new file mode 100644 index 0000000..b9f2c44 --- /dev/null +++ b/node_modules/pg-protocol/dist/messages.js @@ -0,0 +1,160 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NoticeMessage = exports.DataRowMessage = exports.CommandCompleteMessage = exports.ReadyForQueryMessage = exports.NotificationResponseMessage = exports.BackendKeyDataMessage = exports.AuthenticationMD5Password = exports.ParameterStatusMessage = exports.ParameterDescriptionMessage = exports.RowDescriptionMessage = exports.Field = exports.CopyResponse = exports.CopyDataMessage = exports.DatabaseError = exports.copyDone = exports.emptyQuery = exports.replicationStart = exports.portalSuspended = exports.noData = exports.closeComplete = exports.bindComplete = exports.parseComplete = void 0; +exports.parseComplete = { + name: 'parseComplete', + length: 5, +}; +exports.bindComplete = { + name: 'bindComplete', + length: 5, +}; +exports.closeComplete = { + name: 'closeComplete', + length: 5, +}; +exports.noData = { + name: 'noData', + length: 5, +}; +exports.portalSuspended = { + name: 'portalSuspended', + length: 5, +}; +exports.replicationStart = { + name: 'replicationStart', + length: 4, +}; +exports.emptyQuery = { + name: 'emptyQuery', + length: 4, +}; +exports.copyDone = { + name: 'copyDone', + length: 4, +}; +class DatabaseError extends Error { + constructor(message, length, name) { + super(message); + this.length = length; + this.name = name; + } +} +exports.DatabaseError = DatabaseError; +class CopyDataMessage { + constructor(length, chunk) { + this.length = length; + this.chunk = chunk; + this.name = 'copyData'; + } +} +exports.CopyDataMessage = CopyDataMessage; +class CopyResponse { + constructor(length, name, binary, columnCount) { + this.length = length; + this.name = name; + this.binary = binary; + this.columnTypes = new Array(columnCount); + } +} +exports.CopyResponse = CopyResponse; +class Field { + constructor(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, format) { + this.name = name; + this.tableID = tableID; + this.columnID = columnID; + this.dataTypeID = dataTypeID; + this.dataTypeSize = dataTypeSize; + this.dataTypeModifier = dataTypeModifier; + this.format = format; + } +} +exports.Field = Field; +class RowDescriptionMessage { + constructor(length, fieldCount) { + this.length = length; + this.fieldCount = fieldCount; + this.name = 'rowDescription'; + this.fields = new Array(this.fieldCount); + } +} +exports.RowDescriptionMessage = RowDescriptionMessage; +class ParameterDescriptionMessage { + constructor(length, parameterCount) { + this.length = length; + this.parameterCount = parameterCount; + this.name = 'parameterDescription'; + this.dataTypeIDs = new Array(this.parameterCount); + } +} +exports.ParameterDescriptionMessage = ParameterDescriptionMessage; +class ParameterStatusMessage { + constructor(length, parameterName, parameterValue) { + this.length = length; + this.parameterName = parameterName; + this.parameterValue = parameterValue; + this.name = 'parameterStatus'; + } +} +exports.ParameterStatusMessage = ParameterStatusMessage; +class AuthenticationMD5Password { + constructor(length, salt) { + this.length = length; + this.salt = salt; + this.name = 'authenticationMD5Password'; + } +} +exports.AuthenticationMD5Password = AuthenticationMD5Password; +class BackendKeyDataMessage { + constructor(length, processID, secretKey) { + this.length = length; + this.processID = processID; + this.secretKey = secretKey; + this.name = 'backendKeyData'; + } +} +exports.BackendKeyDataMessage = BackendKeyDataMessage; +class NotificationResponseMessage { + constructor(length, processId, channel, payload) { + this.length = length; + this.processId = processId; + this.channel = channel; + this.payload = payload; + this.name = 'notification'; + } +} +exports.NotificationResponseMessage = NotificationResponseMessage; +class ReadyForQueryMessage { + constructor(length, status) { + this.length = length; + this.status = status; + this.name = 'readyForQuery'; + } +} +exports.ReadyForQueryMessage = ReadyForQueryMessage; +class CommandCompleteMessage { + constructor(length, text) { + this.length = length; + this.text = text; + this.name = 'commandComplete'; + } +} +exports.CommandCompleteMessage = CommandCompleteMessage; +class DataRowMessage { + constructor(length, fields) { + this.length = length; + this.fields = fields; + this.name = 'dataRow'; + this.fieldCount = fields.length; + } +} +exports.DataRowMessage = DataRowMessage; +class NoticeMessage { + constructor(length, message) { + this.length = length; + this.message = message; + this.name = 'notice'; + } +} +exports.NoticeMessage = NoticeMessage; +//# sourceMappingURL=messages.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/messages.js.map b/node_modules/pg-protocol/dist/messages.js.map new file mode 100644 index 0000000..9cf2581 --- /dev/null +++ b/node_modules/pg-protocol/dist/messages.js.map @@ -0,0 +1 @@ +{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/outbound-serializer.test.d.ts b/node_modules/pg-protocol/dist/outbound-serializer.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/node_modules/pg-protocol/dist/outbound-serializer.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/node_modules/pg-protocol/dist/outbound-serializer.test.js b/node_modules/pg-protocol/dist/outbound-serializer.test.js new file mode 100644 index 0000000..9644e1e --- /dev/null +++ b/node_modules/pg-protocol/dist/outbound-serializer.test.js @@ -0,0 +1,252 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const assert_1 = __importDefault(require("assert")); +const serializer_1 = require("./serializer"); +const buffer_list_1 = __importDefault(require("./testing/buffer-list")); +describe('serializer', () => { + it('builds startup message', function () { + const actual = serializer_1.serialize.startup({ + user: 'brian', + database: 'bang', + }); + assert_1.default.deepEqual(actual, new buffer_list_1.default() + .addInt16(3) + .addInt16(0) + .addCString('user') + .addCString('brian') + .addCString('database') + .addCString('bang') + .addCString('client_encoding') + .addCString('UTF8') + .addCString('') + .join(true)); + }); + it('builds password message', function () { + const actual = serializer_1.serialize.password('!'); + assert_1.default.deepEqual(actual, new buffer_list_1.default().addCString('!').join(true, 'p')); + }); + it('builds request ssl message', function () { + const actual = serializer_1.serialize.requestSsl(); + const expected = new buffer_list_1.default().addInt32(80877103).join(true); + assert_1.default.deepEqual(actual, expected); + }); + it('builds SASLInitialResponseMessage message', function () { + const actual = serializer_1.serialize.sendSASLInitialResponseMessage('mech', 'data'); + assert_1.default.deepEqual(actual, new buffer_list_1.default().addCString('mech').addInt32(4).addString('data').join(true, 'p')); + }); + it('builds SCRAMClientFinalMessage message', function () { + const actual = serializer_1.serialize.sendSCRAMClientFinalMessage('data'); + assert_1.default.deepEqual(actual, new buffer_list_1.default().addString('data').join(true, 'p')); + }); + it('builds query message', function () { + const txt = 'select * from boom'; + const actual = serializer_1.serialize.query(txt); + assert_1.default.deepEqual(actual, new buffer_list_1.default().addCString(txt).join(true, 'Q')); + }); + describe('parse message', () => { + it('builds parse message', function () { + const actual = serializer_1.serialize.parse({ text: '!' }); + const expected = new buffer_list_1.default().addCString('').addCString('!').addInt16(0).join(true, 'P'); + assert_1.default.deepEqual(actual, expected); + }); + it('builds parse message with named query', function () { + const actual = serializer_1.serialize.parse({ + name: 'boom', + text: 'select * from boom', + types: [], + }); + const expected = new buffer_list_1.default().addCString('boom').addCString('select * from boom').addInt16(0).join(true, 'P'); + assert_1.default.deepEqual(actual, expected); + }); + it('with multiple parameters', function () { + const actual = serializer_1.serialize.parse({ + name: 'force', + text: 'select * from bang where name = $1', + types: [1, 2, 3, 4], + }); + const expected = new buffer_list_1.default() + .addCString('force') + .addCString('select * from bang where name = $1') + .addInt16(4) + .addInt32(1) + .addInt32(2) + .addInt32(3) + .addInt32(4) + .join(true, 'P'); + assert_1.default.deepEqual(actual, expected); + }); + }); + describe('bind messages', function () { + it('with no values', function () { + const actual = serializer_1.serialize.bind(); + const expectedBuffer = new buffer_list_1.default() + .addCString('') + .addCString('') + .addInt16(0) + .addInt16(0) + .addInt16(1) + .addInt16(0) + .join(true, 'B'); + assert_1.default.deepEqual(actual, expectedBuffer); + }); + it('with named statement, portal, and values', function () { + const actual = serializer_1.serialize.bind({ + portal: 'bang', + statement: 'woo', + values: ['1', 'hi', null, 'zing'], + }); + const expectedBuffer = new buffer_list_1.default() + .addCString('bang') // portal name + .addCString('woo') // statement name + .addInt16(4) + .addInt16(0) + .addInt16(0) + .addInt16(0) + .addInt16(0) + .addInt16(4) + .addInt32(1) + .add(Buffer.from('1')) + .addInt32(2) + .add(Buffer.from('hi')) + .addInt32(-1) + .addInt32(4) + .add(Buffer.from('zing')) + .addInt16(1) + .addInt16(0) + .join(true, 'B'); + assert_1.default.deepEqual(actual, expectedBuffer); + }); + }); + it('with custom valueMapper', function () { + const actual = serializer_1.serialize.bind({ + portal: 'bang', + statement: 'woo', + values: ['1', 'hi', null, 'zing'], + valueMapper: () => null, + }); + const expectedBuffer = new buffer_list_1.default() + .addCString('bang') // portal name + .addCString('woo') // statement name + .addInt16(4) + .addInt16(0) + .addInt16(0) + .addInt16(0) + .addInt16(0) + .addInt16(4) + .addInt32(-1) + .addInt32(-1) + .addInt32(-1) + .addInt32(-1) + .addInt16(1) + .addInt16(0) + .join(true, 'B'); + assert_1.default.deepEqual(actual, expectedBuffer); + }); + it('with named statement, portal, and buffer value', function () { + const actual = serializer_1.serialize.bind({ + portal: 'bang', + statement: 'woo', + values: ['1', 'hi', null, Buffer.from('zing', 'utf8')], + }); + const expectedBuffer = new buffer_list_1.default() + .addCString('bang') // portal name + .addCString('woo') // statement name + .addInt16(4) // value count + .addInt16(0) // string + .addInt16(0) // string + .addInt16(0) // string + .addInt16(1) // binary + .addInt16(4) + .addInt32(1) + .add(Buffer.from('1')) + .addInt32(2) + .add(Buffer.from('hi')) + .addInt32(-1) + .addInt32(4) + .add(Buffer.from('zing', 'utf-8')) + .addInt16(1) + .addInt16(0) + .join(true, 'B'); + assert_1.default.deepEqual(actual, expectedBuffer); + }); + describe('builds execute message', function () { + it('for unamed portal with no row limit', function () { + const actual = serializer_1.serialize.execute(); + const expectedBuffer = new buffer_list_1.default().addCString('').addInt32(0).join(true, 'E'); + assert_1.default.deepEqual(actual, expectedBuffer); + }); + it('for named portal with row limit', function () { + const actual = serializer_1.serialize.execute({ + portal: 'my favorite portal', + rows: 100, + }); + const expectedBuffer = new buffer_list_1.default().addCString('my favorite portal').addInt32(100).join(true, 'E'); + assert_1.default.deepEqual(actual, expectedBuffer); + }); + }); + it('builds flush command', function () { + const actual = serializer_1.serialize.flush(); + const expected = new buffer_list_1.default().join(true, 'H'); + assert_1.default.deepEqual(actual, expected); + }); + it('builds sync command', function () { + const actual = serializer_1.serialize.sync(); + const expected = new buffer_list_1.default().join(true, 'S'); + assert_1.default.deepEqual(actual, expected); + }); + it('builds end command', function () { + const actual = serializer_1.serialize.end(); + const expected = Buffer.from([0x58, 0, 0, 0, 4]); + assert_1.default.deepEqual(actual, expected); + }); + describe('builds describe command', function () { + it('describe statement', function () { + const actual = serializer_1.serialize.describe({ type: 'S', name: 'bang' }); + const expected = new buffer_list_1.default().addChar('S').addCString('bang').join(true, 'D'); + assert_1.default.deepEqual(actual, expected); + }); + it('describe unnamed portal', function () { + const actual = serializer_1.serialize.describe({ type: 'P' }); + const expected = new buffer_list_1.default().addChar('P').addCString('').join(true, 'D'); + assert_1.default.deepEqual(actual, expected); + }); + }); + describe('builds close command', function () { + it('describe statement', function () { + const actual = serializer_1.serialize.close({ type: 'S', name: 'bang' }); + const expected = new buffer_list_1.default().addChar('S').addCString('bang').join(true, 'C'); + assert_1.default.deepEqual(actual, expected); + }); + it('describe unnamed portal', function () { + const actual = serializer_1.serialize.close({ type: 'P' }); + const expected = new buffer_list_1.default().addChar('P').addCString('').join(true, 'C'); + assert_1.default.deepEqual(actual, expected); + }); + }); + describe('copy messages', function () { + it('builds copyFromChunk', () => { + const actual = serializer_1.serialize.copyData(Buffer.from([1, 2, 3])); + const expected = new buffer_list_1.default().add(Buffer.from([1, 2, 3])).join(true, 'd'); + assert_1.default.deepEqual(actual, expected); + }); + it('builds copy fail', () => { + const actual = serializer_1.serialize.copyFail('err!'); + const expected = new buffer_list_1.default().addCString('err!').join(true, 'f'); + assert_1.default.deepEqual(actual, expected); + }); + it('builds copy done', () => { + const actual = serializer_1.serialize.copyDone(); + const expected = new buffer_list_1.default().join(true, 'c'); + assert_1.default.deepEqual(actual, expected); + }); + }); + it('builds cancel message', () => { + const actual = serializer_1.serialize.cancel(3, 4); + const expected = new buffer_list_1.default().addInt16(1234).addInt16(5678).addInt32(3).addInt32(4).join(true); + assert_1.default.deepEqual(actual, expected); + }); +}); +//# sourceMappingURL=outbound-serializer.test.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/outbound-serializer.test.js.map b/node_modules/pg-protocol/dist/outbound-serializer.test.js.map new file mode 100644 index 0000000..0a3cdce --- /dev/null +++ b/node_modules/pg-protocol/dist/outbound-serializer.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"outbound-serializer.test.js","sourceRoot":"","sources":["../src/outbound-serializer.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/parser.d.ts b/node_modules/pg-protocol/dist/parser.d.ts new file mode 100644 index 0000000..f1e37f9 --- /dev/null +++ b/node_modules/pg-protocol/dist/parser.d.ts @@ -0,0 +1,24 @@ +/// +/// +import { TransformOptions } from 'stream'; +import { Mode, BackendMessage } from './messages'; +export declare type Packet = { + code: number; + packet: Buffer; +}; +declare type StreamOptions = TransformOptions & { + mode: Mode; +}; +export declare type MessageCallback = (msg: BackendMessage) => void; +export declare class Parser { + private buffer; + private bufferLength; + private bufferOffset; + private reader; + private mode; + constructor(opts?: StreamOptions); + parse(buffer: Buffer, callback: MessageCallback): void; + private mergeBuffer; + private handlePacket; +} +export {}; diff --git a/node_modules/pg-protocol/dist/parser.js b/node_modules/pg-protocol/dist/parser.js new file mode 100644 index 0000000..b05fe75 --- /dev/null +++ b/node_modules/pg-protocol/dist/parser.js @@ -0,0 +1,324 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Parser = void 0; +const messages_1 = require("./messages"); +const buffer_reader_1 = require("./buffer-reader"); +// every message is prefixed with a single bye +const CODE_LENGTH = 1; +// every message has an int32 length which includes itself but does +// NOT include the code in the length +const LEN_LENGTH = 4; +const HEADER_LENGTH = CODE_LENGTH + LEN_LENGTH; +// A placeholder for a `BackendMessage`’s length value that will be set after construction. +const LATEINIT_LENGTH = -1; +const emptyBuffer = Buffer.allocUnsafe(0); +class Parser { + constructor(opts) { + this.buffer = emptyBuffer; + this.bufferLength = 0; + this.bufferOffset = 0; + this.reader = new buffer_reader_1.BufferReader(); + if ((opts === null || opts === void 0 ? void 0 : opts.mode) === 'binary') { + throw new Error('Binary mode not supported yet'); + } + this.mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'text'; + } + parse(buffer, callback) { + this.mergeBuffer(buffer); + const bufferFullLength = this.bufferOffset + this.bufferLength; + let offset = this.bufferOffset; + while (offset + HEADER_LENGTH <= bufferFullLength) { + // code is 1 byte long - it identifies the message type + const code = this.buffer[offset]; + // length is 1 Uint32BE - it is the length of the message EXCLUDING the code + const length = this.buffer.readUInt32BE(offset + CODE_LENGTH); + const fullMessageLength = CODE_LENGTH + length; + if (fullMessageLength + offset <= bufferFullLength) { + const message = this.handlePacket(offset + HEADER_LENGTH, code, length, this.buffer); + callback(message); + offset += fullMessageLength; + } + else { + break; + } + } + if (offset === bufferFullLength) { + // No more use for the buffer + this.buffer = emptyBuffer; + this.bufferLength = 0; + this.bufferOffset = 0; + } + else { + // Adjust the cursors of remainingBuffer + this.bufferLength = bufferFullLength - offset; + this.bufferOffset = offset; + } + } + mergeBuffer(buffer) { + if (this.bufferLength > 0) { + const newLength = this.bufferLength + buffer.byteLength; + const newFullLength = newLength + this.bufferOffset; + if (newFullLength > this.buffer.byteLength) { + // We can't concat the new buffer with the remaining one + let newBuffer; + if (newLength <= this.buffer.byteLength && this.bufferOffset >= this.bufferLength) { + // We can move the relevant part to the beginning of the buffer instead of allocating a new buffer + newBuffer = this.buffer; + } + else { + // Allocate a new larger buffer + let newBufferLength = this.buffer.byteLength * 2; + while (newLength >= newBufferLength) { + newBufferLength *= 2; + } + newBuffer = Buffer.allocUnsafe(newBufferLength); + } + // Move the remaining buffer to the new one + this.buffer.copy(newBuffer, 0, this.bufferOffset, this.bufferOffset + this.bufferLength); + this.buffer = newBuffer; + this.bufferOffset = 0; + } + // Concat the new buffer with the remaining one + buffer.copy(this.buffer, this.bufferOffset + this.bufferLength); + this.bufferLength = newLength; + } + else { + this.buffer = buffer; + this.bufferOffset = 0; + this.bufferLength = buffer.byteLength; + } + } + handlePacket(offset, code, length, bytes) { + const { reader } = this; + // NOTE: This undesirably retains the buffer in `this.reader` if the `parse*Message` calls below throw. However, those should only throw in the case of a protocol error, which normally results in the reader being discarded. + reader.setBuffer(offset, bytes); + let message; + switch (code) { + case 50 /* MessageCodes.BindComplete */: + message = messages_1.bindComplete; + break; + case 49 /* MessageCodes.ParseComplete */: + message = messages_1.parseComplete; + break; + case 51 /* MessageCodes.CloseComplete */: + message = messages_1.closeComplete; + break; + case 110 /* MessageCodes.NoData */: + message = messages_1.noData; + break; + case 115 /* MessageCodes.PortalSuspended */: + message = messages_1.portalSuspended; + break; + case 99 /* MessageCodes.CopyDone */: + message = messages_1.copyDone; + break; + case 87 /* MessageCodes.ReplicationStart */: + message = messages_1.replicationStart; + break; + case 73 /* MessageCodes.EmptyQuery */: + message = messages_1.emptyQuery; + break; + case 68 /* MessageCodes.DataRow */: + message = parseDataRowMessage(reader); + break; + case 67 /* MessageCodes.CommandComplete */: + message = parseCommandCompleteMessage(reader); + break; + case 90 /* MessageCodes.ReadyForQuery */: + message = parseReadyForQueryMessage(reader); + break; + case 65 /* MessageCodes.NotificationResponse */: + message = parseNotificationMessage(reader); + break; + case 82 /* MessageCodes.AuthenticationResponse */: + message = parseAuthenticationResponse(reader, length); + break; + case 83 /* MessageCodes.ParameterStatus */: + message = parseParameterStatusMessage(reader); + break; + case 75 /* MessageCodes.BackendKeyData */: + message = parseBackendKeyData(reader); + break; + case 69 /* MessageCodes.ErrorMessage */: + message = parseErrorMessage(reader, 'error'); + break; + case 78 /* MessageCodes.NoticeMessage */: + message = parseErrorMessage(reader, 'notice'); + break; + case 84 /* MessageCodes.RowDescriptionMessage */: + message = parseRowDescriptionMessage(reader); + break; + case 116 /* MessageCodes.ParameterDescriptionMessage */: + message = parseParameterDescriptionMessage(reader); + break; + case 71 /* MessageCodes.CopyIn */: + message = parseCopyInMessage(reader); + break; + case 72 /* MessageCodes.CopyOut */: + message = parseCopyOutMessage(reader); + break; + case 100 /* MessageCodes.CopyData */: + message = parseCopyData(reader, length); + break; + default: + return new messages_1.DatabaseError('received invalid response: ' + code.toString(16), length, 'error'); + } + reader.setBuffer(0, emptyBuffer); + message.length = length; + return message; + } +} +exports.Parser = Parser; +const parseReadyForQueryMessage = (reader) => { + const status = reader.string(1); + return new messages_1.ReadyForQueryMessage(LATEINIT_LENGTH, status); +}; +const parseCommandCompleteMessage = (reader) => { + const text = reader.cstring(); + return new messages_1.CommandCompleteMessage(LATEINIT_LENGTH, text); +}; +const parseCopyData = (reader, length) => { + const chunk = reader.bytes(length - 4); + return new messages_1.CopyDataMessage(LATEINIT_LENGTH, chunk); +}; +const parseCopyInMessage = (reader) => parseCopyMessage(reader, 'copyInResponse'); +const parseCopyOutMessage = (reader) => parseCopyMessage(reader, 'copyOutResponse'); +const parseCopyMessage = (reader, messageName) => { + const isBinary = reader.byte() !== 0; + const columnCount = reader.int16(); + const message = new messages_1.CopyResponse(LATEINIT_LENGTH, messageName, isBinary, columnCount); + for (let i = 0; i < columnCount; i++) { + message.columnTypes[i] = reader.int16(); + } + return message; +}; +const parseNotificationMessage = (reader) => { + const processId = reader.int32(); + const channel = reader.cstring(); + const payload = reader.cstring(); + return new messages_1.NotificationResponseMessage(LATEINIT_LENGTH, processId, channel, payload); +}; +const parseRowDescriptionMessage = (reader) => { + const fieldCount = reader.int16(); + const message = new messages_1.RowDescriptionMessage(LATEINIT_LENGTH, fieldCount); + for (let i = 0; i < fieldCount; i++) { + message.fields[i] = parseField(reader); + } + return message; +}; +const parseField = (reader) => { + const name = reader.cstring(); + const tableID = reader.uint32(); + const columnID = reader.int16(); + const dataTypeID = reader.uint32(); + const dataTypeSize = reader.int16(); + const dataTypeModifier = reader.int32(); + const mode = reader.int16() === 0 ? 'text' : 'binary'; + return new messages_1.Field(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, mode); +}; +const parseParameterDescriptionMessage = (reader) => { + const parameterCount = reader.int16(); + const message = new messages_1.ParameterDescriptionMessage(LATEINIT_LENGTH, parameterCount); + for (let i = 0; i < parameterCount; i++) { + message.dataTypeIDs[i] = reader.int32(); + } + return message; +}; +const parseDataRowMessage = (reader) => { + const fieldCount = reader.int16(); + const fields = new Array(fieldCount); + for (let i = 0; i < fieldCount; i++) { + const len = reader.int32(); + // a -1 for length means the value of the field is null + fields[i] = len === -1 ? null : reader.string(len); + } + return new messages_1.DataRowMessage(LATEINIT_LENGTH, fields); +}; +const parseParameterStatusMessage = (reader) => { + const name = reader.cstring(); + const value = reader.cstring(); + return new messages_1.ParameterStatusMessage(LATEINIT_LENGTH, name, value); +}; +const parseBackendKeyData = (reader) => { + const processID = reader.int32(); + const secretKey = reader.int32(); + return new messages_1.BackendKeyDataMessage(LATEINIT_LENGTH, processID, secretKey); +}; +const parseAuthenticationResponse = (reader, length) => { + const code = reader.int32(); + // TODO(bmc): maybe better types here + const message = { + name: 'authenticationOk', + length, + }; + switch (code) { + case 0: // AuthenticationOk + break; + case 3: // AuthenticationCleartextPassword + if (message.length === 8) { + message.name = 'authenticationCleartextPassword'; + } + break; + case 5: // AuthenticationMD5Password + if (message.length === 12) { + message.name = 'authenticationMD5Password'; + const salt = reader.bytes(4); + return new messages_1.AuthenticationMD5Password(LATEINIT_LENGTH, salt); + } + break; + case 10: // AuthenticationSASL + { + message.name = 'authenticationSASL'; + message.mechanisms = []; + let mechanism; + do { + mechanism = reader.cstring(); + if (mechanism) { + message.mechanisms.push(mechanism); + } + } while (mechanism); + } + break; + case 11: // AuthenticationSASLContinue + message.name = 'authenticationSASLContinue'; + message.data = reader.string(length - 8); + break; + case 12: // AuthenticationSASLFinal + message.name = 'authenticationSASLFinal'; + message.data = reader.string(length - 8); + break; + default: + throw new Error('Unknown authenticationOk message type ' + code); + } + return message; +}; +const parseErrorMessage = (reader, name) => { + const fields = {}; + let fieldType = reader.string(1); + while (fieldType !== '\0') { + fields[fieldType] = reader.cstring(); + fieldType = reader.string(1); + } + const messageValue = fields.M; + const message = name === 'notice' + ? new messages_1.NoticeMessage(LATEINIT_LENGTH, messageValue) + : new messages_1.DatabaseError(messageValue, LATEINIT_LENGTH, name); + message.severity = fields.S; + message.code = fields.C; + message.detail = fields.D; + message.hint = fields.H; + message.position = fields.P; + message.internalPosition = fields.p; + message.internalQuery = fields.q; + message.where = fields.W; + message.schema = fields.s; + message.table = fields.t; + message.column = fields.c; + message.dataType = fields.d; + message.constraint = fields.n; + message.file = fields.F; + message.line = fields.L; + message.routine = fields.R; + return message; +}; +//# sourceMappingURL=parser.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/parser.js.map b/node_modules/pg-protocol/dist/parser.js.map new file mode 100644 index 0000000..947ab29 --- /dev/null +++ b/node_modules/pg-protocol/dist/parser.js.map @@ -0,0 +1 @@ +{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/serializer.d.ts b/node_modules/pg-protocol/dist/serializer.d.ts new file mode 100644 index 0000000..1895010 --- /dev/null +++ b/node_modules/pg-protocol/dist/serializer.d.ts @@ -0,0 +1,43 @@ +/// +declare type ParseOpts = { + name?: string; + types?: number[]; + text: string; +}; +declare type ValueMapper = (param: any, index: number) => any; +declare type BindOpts = { + portal?: string; + binary?: boolean; + statement?: string; + values?: any[]; + valueMapper?: ValueMapper; +}; +declare type ExecOpts = { + portal?: string; + rows?: number; +}; +declare type PortalOpts = { + type: 'S' | 'P'; + name?: string; +}; +declare const serialize: { + startup: (opts: Record) => Buffer; + password: (password: string) => Buffer; + requestSsl: () => Buffer; + sendSASLInitialResponseMessage: (mechanism: string, initialResponse: string) => Buffer; + sendSCRAMClientFinalMessage: (additionalData: string) => Buffer; + query: (text: string) => Buffer; + parse: (query: ParseOpts) => Buffer; + bind: (config?: BindOpts) => Buffer; + execute: (config?: ExecOpts) => Buffer; + describe: (msg: PortalOpts) => Buffer; + close: (msg: PortalOpts) => Buffer; + flush: () => Buffer; + sync: () => Buffer; + end: () => Buffer; + copyData: (chunk: Buffer) => Buffer; + copyDone: () => Buffer; + copyFail: (message: string) => Buffer; + cancel: (processID: number, secretKey: number) => Buffer; +}; +export { serialize }; diff --git a/node_modules/pg-protocol/dist/serializer.js b/node_modules/pg-protocol/dist/serializer.js new file mode 100644 index 0000000..f3e5d28 --- /dev/null +++ b/node_modules/pg-protocol/dist/serializer.js @@ -0,0 +1,189 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.serialize = void 0; +const buffer_writer_1 = require("./buffer-writer"); +const writer = new buffer_writer_1.Writer(); +const startup = (opts) => { + // protocol version + writer.addInt16(3).addInt16(0); + for (const key of Object.keys(opts)) { + writer.addCString(key).addCString(opts[key]); + } + writer.addCString('client_encoding').addCString('UTF8'); + const bodyBuffer = writer.addCString('').flush(); + // this message is sent without a code + const length = bodyBuffer.length + 4; + return new buffer_writer_1.Writer().addInt32(length).add(bodyBuffer).flush(); +}; +const requestSsl = () => { + const response = Buffer.allocUnsafe(8); + response.writeInt32BE(8, 0); + response.writeInt32BE(80877103, 4); + return response; +}; +const password = (password) => { + return writer.addCString(password).flush(112 /* code.startup */); +}; +const sendSASLInitialResponseMessage = function (mechanism, initialResponse) { + // 0x70 = 'p' + writer.addCString(mechanism).addInt32(Buffer.byteLength(initialResponse)).addString(initialResponse); + return writer.flush(112 /* code.startup */); +}; +const sendSCRAMClientFinalMessage = function (additionalData) { + return writer.addString(additionalData).flush(112 /* code.startup */); +}; +const query = (text) => { + return writer.addCString(text).flush(81 /* code.query */); +}; +const emptyArray = []; +const parse = (query) => { + // expect something like this: + // { name: 'queryName', + // text: 'select * from blah', + // types: ['int8', 'bool'] } + // normalize missing query names to allow for null + const name = query.name || ''; + if (name.length > 63) { + console.error('Warning! Postgres only supports 63 characters for query names.'); + console.error('You supplied %s (%s)', name, name.length); + console.error('This can cause conflicts and silent errors executing queries'); + } + const types = query.types || emptyArray; + const len = types.length; + const buffer = writer + .addCString(name) // name of query + .addCString(query.text) // actual query text + .addInt16(len); + for (let i = 0; i < len; i++) { + buffer.addInt32(types[i]); + } + return writer.flush(80 /* code.parse */); +}; +const paramWriter = new buffer_writer_1.Writer(); +const writeValues = function (values, valueMapper) { + for (let i = 0; i < values.length; i++) { + const mappedVal = valueMapper ? valueMapper(values[i], i) : values[i]; + if (mappedVal == null) { + // add the param type (string) to the writer + writer.addInt16(0 /* ParamType.STRING */); + // write -1 to the param writer to indicate null + paramWriter.addInt32(-1); + } + else if (mappedVal instanceof Buffer) { + // add the param type (binary) to the writer + writer.addInt16(1 /* ParamType.BINARY */); + // add the buffer to the param writer + paramWriter.addInt32(mappedVal.length); + paramWriter.add(mappedVal); + } + else { + // add the param type (string) to the writer + writer.addInt16(0 /* ParamType.STRING */); + paramWriter.addInt32(Buffer.byteLength(mappedVal)); + paramWriter.addString(mappedVal); + } + } +}; +const bind = (config = {}) => { + // normalize config + const portal = config.portal || ''; + const statement = config.statement || ''; + const binary = config.binary || false; + const values = config.values || emptyArray; + const len = values.length; + writer.addCString(portal).addCString(statement); + writer.addInt16(len); + writeValues(values, config.valueMapper); + writer.addInt16(len); + writer.add(paramWriter.flush()); + // all results use the same format code + writer.addInt16(1); + // format code + writer.addInt16(binary ? 1 /* ParamType.BINARY */ : 0 /* ParamType.STRING */); + return writer.flush(66 /* code.bind */); +}; +const emptyExecute = Buffer.from([69 /* code.execute */, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00]); +const execute = (config) => { + // this is the happy path for most queries + if (!config || (!config.portal && !config.rows)) { + return emptyExecute; + } + const portal = config.portal || ''; + const rows = config.rows || 0; + const portalLength = Buffer.byteLength(portal); + const len = 4 + portalLength + 1 + 4; + // one extra bit for code + const buff = Buffer.allocUnsafe(1 + len); + buff[0] = 69 /* code.execute */; + buff.writeInt32BE(len, 1); + buff.write(portal, 5, 'utf-8'); + buff[portalLength + 5] = 0; // null terminate portal cString + buff.writeUInt32BE(rows, buff.length - 4); + return buff; +}; +const cancel = (processID, secretKey) => { + const buffer = Buffer.allocUnsafe(16); + buffer.writeInt32BE(16, 0); + buffer.writeInt16BE(1234, 4); + buffer.writeInt16BE(5678, 6); + buffer.writeInt32BE(processID, 8); + buffer.writeInt32BE(secretKey, 12); + return buffer; +}; +const cstringMessage = (code, string) => { + const stringLen = Buffer.byteLength(string); + const len = 4 + stringLen + 1; + // one extra bit for code + const buffer = Buffer.allocUnsafe(1 + len); + buffer[0] = code; + buffer.writeInt32BE(len, 1); + buffer.write(string, 5, 'utf-8'); + buffer[len] = 0; // null terminate cString + return buffer; +}; +const emptyDescribePortal = writer.addCString('P').flush(68 /* code.describe */); +const emptyDescribeStatement = writer.addCString('S').flush(68 /* code.describe */); +const describe = (msg) => { + return msg.name + ? cstringMessage(68 /* code.describe */, `${msg.type}${msg.name || ''}`) + : msg.type === 'P' + ? emptyDescribePortal + : emptyDescribeStatement; +}; +const close = (msg) => { + const text = `${msg.type}${msg.name || ''}`; + return cstringMessage(67 /* code.close */, text); +}; +const copyData = (chunk) => { + return writer.add(chunk).flush(100 /* code.copyFromChunk */); +}; +const copyFail = (message) => { + return cstringMessage(102 /* code.copyFail */, message); +}; +const codeOnlyBuffer = (code) => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]); +const flushBuffer = codeOnlyBuffer(72 /* code.flush */); +const syncBuffer = codeOnlyBuffer(83 /* code.sync */); +const endBuffer = codeOnlyBuffer(88 /* code.end */); +const copyDoneBuffer = codeOnlyBuffer(99 /* code.copyDone */); +const serialize = { + startup, + password, + requestSsl, + sendSASLInitialResponseMessage, + sendSCRAMClientFinalMessage, + query, + parse, + bind, + execute, + describe, + close, + flush: () => flushBuffer, + sync: () => syncBuffer, + end: () => endBuffer, + copyData, + copyDone: () => copyDoneBuffer, + copyFail, + cancel, +}; +exports.serialize = serialize; +//# sourceMappingURL=serializer.js.map \ No newline at end of file diff --git a/node_modules/pg-protocol/dist/serializer.js.map b/node_modules/pg-protocol/dist/serializer.js.map new file mode 100644 index 0000000..513c2fa --- /dev/null +++ b/node_modules/pg-protocol/dist/serializer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/pg-protocol/esm/index.js b/node_modules/pg-protocol/esm/index.js new file mode 100644 index 0000000..c52807d --- /dev/null +++ b/node_modules/pg-protocol/esm/index.js @@ -0,0 +1,11 @@ +// ESM wrapper for pg-protocol +import * as protocol from '../dist/index.js' + +// Re-export all the properties +export const DatabaseError = protocol.DatabaseError +export const SASL = protocol.SASL +export const serialize = protocol.serialize +export const parse = protocol.parse + +// Re-export the default +export default protocol diff --git a/node_modules/pg-protocol/package.json b/node_modules/pg-protocol/package.json new file mode 100644 index 0000000..88a9a7b --- /dev/null +++ b/node_modules/pg-protocol/package.json @@ -0,0 +1,45 @@ +{ + "name": "pg-protocol", + "version": "1.14.0", + "description": "The postgres client/server binary protocol, implemented in TypeScript", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./esm/index.js", + "require": "./dist/index.js", + "default": "./dist/index.js" + }, + "./dist/*": "./dist/*.js", + "./dist/*.js": "./dist/*.js" + }, + "license": "MIT", + "devDependencies": { + "@types/chai": "^4.2.7", + "@types/mocha": "^10.0.10", + "@types/node": "^16", + "chai": "^4.2.0", + "chunky": "^0.0.0", + "mocha": "^11.7.5", + "ts-node": "^8.5.4", + "typescript": "^6.0.3" + }, + "scripts": { + "test": "mocha dist/**/*.test.js", + "build": "tsc", + "build:watch": "tsc --watch", + "prepublish": "yarn build", + "pretest": "yarn build" + }, + "repository": { + "type": "git", + "url": "git://github.com/brianc/node-postgres.git", + "directory": "packages/pg-protocol" + }, + "files": [ + "/dist/*{js,ts,map}", + "/src", + "/esm" + ], + "gitHead": "544b1ce8152bc280e398dc1e8a66920abe6a640e" +} diff --git a/node_modules/pg-protocol/src/b.ts b/node_modules/pg-protocol/src/b.ts new file mode 100644 index 0000000..c8a2411 --- /dev/null +++ b/node_modules/pg-protocol/src/b.ts @@ -0,0 +1,25 @@ +// file for microbenchmarking + +import { BufferReader } from './buffer-reader' + +const LOOPS = 1000 +let count = 0 +const start = performance.now() + +const reader = new BufferReader() +const buffer = Buffer.from([33, 33, 33, 33, 33, 33, 33, 0]) + +const run = () => { + if (count > LOOPS) { + console.log(performance.now() - start) + return + } + count++ + for (let i = 0; i < LOOPS; i++) { + reader.setBuffer(0, buffer) + reader.cstring() + } + setImmediate(run) +} + +run() diff --git a/node_modules/pg-protocol/src/buffer-reader.ts b/node_modules/pg-protocol/src/buffer-reader.ts new file mode 100644 index 0000000..c9d9c2b --- /dev/null +++ b/node_modules/pg-protocol/src/buffer-reader.ts @@ -0,0 +1,58 @@ +export class BufferReader { + private buffer: Buffer = Buffer.allocUnsafe(0) + + // TODO(bmc): support non-utf8 encoding? + private encoding: BufferEncoding = 'utf-8' + + constructor(private offset: number = 0) {} + + public setBuffer(offset: number, buffer: Buffer): void { + this.offset = offset + this.buffer = buffer + } + + public int16(): number { + const result = this.buffer.readInt16BE(this.offset) + this.offset += 2 + return result + } + + public byte(): number { + const result = this.buffer[this.offset] + this.offset++ + return result + } + + public int32(): number { + const result = this.buffer.readInt32BE(this.offset) + this.offset += 4 + return result + } + + public uint32(): number { + const result = this.buffer.readUInt32BE(this.offset) + this.offset += 4 + return result + } + + public string(length: number): string { + const result = this.buffer.toString(this.encoding, this.offset, this.offset + length) + this.offset += length + return result + } + + public cstring(): string { + const start = this.offset + let end = start + // eslint-disable-next-line no-empty + while (this.buffer[end++] !== 0) {} + this.offset = end + return this.buffer.toString(this.encoding, start, end - 1) + } + + public bytes(length: number): Buffer { + const result = this.buffer.slice(this.offset, this.offset + length) + this.offset += length + return result + } +} diff --git a/node_modules/pg-protocol/src/buffer-writer.ts b/node_modules/pg-protocol/src/buffer-writer.ts new file mode 100644 index 0000000..cebb0d9 --- /dev/null +++ b/node_modules/pg-protocol/src/buffer-writer.ts @@ -0,0 +1,85 @@ +//binary data writer tuned for encoding binary specific to the postgres binary protocol + +export class Writer { + private buffer: Buffer + private offset: number = 5 + private headerPosition: number = 0 + constructor(private size = 256) { + this.buffer = Buffer.allocUnsafe(size) + } + + private ensure(size: number): void { + const remaining = this.buffer.length - this.offset + if (remaining < size) { + const oldBuffer = this.buffer + // exponential growth factor of around ~ 1.5 + // https://stackoverflow.com/questions/2269063/buffer-growth-strategy + const newSize = oldBuffer.length + (oldBuffer.length >> 1) + size + this.buffer = Buffer.allocUnsafe(newSize) + oldBuffer.copy(this.buffer) + } + } + + public addInt32(num: number): Writer { + this.ensure(4) + this.buffer[this.offset++] = (num >>> 24) & 0xff + this.buffer[this.offset++] = (num >>> 16) & 0xff + this.buffer[this.offset++] = (num >>> 8) & 0xff + this.buffer[this.offset++] = (num >>> 0) & 0xff + return this + } + + public addInt16(num: number): Writer { + this.ensure(2) + this.buffer[this.offset++] = (num >>> 8) & 0xff + this.buffer[this.offset++] = (num >>> 0) & 0xff + return this + } + + public addCString(string: string): Writer { + if (!string) { + this.ensure(1) + } else { + const len = Buffer.byteLength(string) + this.ensure(len + 1) // +1 for null terminator + this.buffer.write(string, this.offset, 'utf-8') + this.offset += len + } + + this.buffer[this.offset++] = 0 // null terminator + return this + } + + public addString(string: string = ''): Writer { + const len = Buffer.byteLength(string) + this.ensure(len) + this.buffer.write(string, this.offset) + this.offset += len + return this + } + + public add(otherBuffer: Buffer): Writer { + this.ensure(otherBuffer.length) + otherBuffer.copy(this.buffer, this.offset) + this.offset += otherBuffer.length + return this + } + + private join(code?: number): Buffer { + if (code) { + this.buffer[this.headerPosition] = code + //length is everything in this packet minus the code + const length = this.offset - (this.headerPosition + 1) + this.buffer.writeInt32BE(length, this.headerPosition + 1) + } + return this.buffer.slice(code ? 0 : 5, this.offset) + } + + public flush(code?: number): Buffer { + const result = this.join(code) + this.offset = 5 + this.headerPosition = 0 + this.buffer = Buffer.allocUnsafe(this.size) + return result + } +} diff --git a/node_modules/pg-protocol/src/inbound-parser.test.ts b/node_modules/pg-protocol/src/inbound-parser.test.ts new file mode 100644 index 0000000..285f4bf --- /dev/null +++ b/node_modules/pg-protocol/src/inbound-parser.test.ts @@ -0,0 +1,575 @@ +import buffers from './testing/test-buffers' +import BufferList from './testing/buffer-list' +import { parse } from '.' +import assert from 'assert' +import { PassThrough } from 'stream' +import { BackendMessage } from './messages' +import { Parser } from './parser' + +const authOkBuffer = buffers.authenticationOk() +const paramStatusBuffer = buffers.parameterStatus('client_encoding', 'UTF8') +const readyForQueryBuffer = buffers.readyForQuery() +const backendKeyDataBuffer = buffers.backendKeyData(1, 2) +const commandCompleteBuffer = buffers.commandComplete('SELECT 3') +const parseCompleteBuffer = buffers.parseComplete() +const bindCompleteBuffer = buffers.bindComplete() +const portalSuspendedBuffer = buffers.portalSuspended() + +const row1 = { + name: 'id', + tableID: 1, + attributeNumber: 2, + dataTypeID: 3, + dataTypeSize: 4, + typeModifier: 5, + formatCode: 0, +} +const oneRowDescBuff = buffers.rowDescription([row1]) +row1.name = 'bang' + +const twoRowBuf = buffers.rowDescription([ + row1, + { + name: 'whoah', + tableID: 10, + attributeNumber: 11, + dataTypeID: 12, + dataTypeSize: 13, + typeModifier: 14, + formatCode: 0, + }, +]) + +const rowWithBigOids = { + name: 'bigoid', + tableID: 3000000001, + attributeNumber: 2, + dataTypeID: 3000000003, + dataTypeSize: 4, + typeModifier: 5, + formatCode: 0, +} +const bigOidDescBuff = buffers.rowDescription([rowWithBigOids]) + +const emptyRowFieldBuf = buffers.dataRow([]) + +const oneFieldBuf = buffers.dataRow(['test']) + +const expectedAuthenticationOkayMessage = { + name: 'authenticationOk', + length: 8, +} + +const expectedParameterStatusMessage = { + name: 'parameterStatus', + parameterName: 'client_encoding', + parameterValue: 'UTF8', + length: 25, +} + +const expectedBackendKeyDataMessage = { + name: 'backendKeyData', + processID: 1, + secretKey: 2, +} + +const expectedReadyForQueryMessage = { + name: 'readyForQuery', + length: 5, + status: 'I', +} + +const expectedCommandCompleteMessage = { + name: 'commandComplete', + length: 13, + text: 'SELECT 3', +} +const emptyRowDescriptionBuffer = new BufferList() + .addInt16(0) // number of fields + .join(true, 'T') + +const expectedEmptyRowDescriptionMessage = { + name: 'rowDescription', + length: 6, + fieldCount: 0, + fields: [], +} +const expectedOneRowMessage = { + name: 'rowDescription', + length: 27, + fieldCount: 1, + fields: [ + { + name: 'id', + tableID: 1, + columnID: 2, + dataTypeID: 3, + dataTypeSize: 4, + dataTypeModifier: 5, + format: 'text', + }, + ], +} + +const expectedTwoRowMessage = { + name: 'rowDescription', + length: 53, + fieldCount: 2, + fields: [ + { + name: 'bang', + tableID: 1, + columnID: 2, + dataTypeID: 3, + dataTypeSize: 4, + dataTypeModifier: 5, + format: 'text', + }, + { + name: 'whoah', + tableID: 10, + columnID: 11, + dataTypeID: 12, + dataTypeSize: 13, + dataTypeModifier: 14, + format: 'text', + }, + ], +} +const expectedBigOidMessage = { + name: 'rowDescription', + length: 31, + fieldCount: 1, + fields: [ + { + name: 'bigoid', + tableID: 3000000001, + columnID: 2, + dataTypeID: 3000000003, + dataTypeSize: 4, + dataTypeModifier: 5, + format: 'text', + }, + ], +} + +const emptyParameterDescriptionBuffer = new BufferList() + .addInt16(0) // number of parameters + .join(true, 't') + +const oneParameterDescBuf = buffers.parameterDescription([1111]) + +const twoParameterDescBuf = buffers.parameterDescription([2222, 3333]) + +const expectedEmptyParameterDescriptionMessage = { + name: 'parameterDescription', + length: 6, + parameterCount: 0, + dataTypeIDs: [], +} + +const expectedOneParameterMessage = { + name: 'parameterDescription', + length: 10, + parameterCount: 1, + dataTypeIDs: [1111], +} + +const expectedTwoParameterMessage = { + name: 'parameterDescription', + length: 14, + parameterCount: 2, + dataTypeIDs: [2222, 3333], +} + +const testForMessage = function (buffer: Buffer, expectedMessage: any) { + it('receives and parses ' + expectedMessage.name, async () => { + const messages = await parseBuffers([buffer]) + const [lastMessage] = messages + + for (const key in expectedMessage) { + assert.deepEqual((lastMessage as any)[key], expectedMessage[key]) + } + }) +} + +const plainPasswordBuffer = buffers.authenticationCleartextPassword() +const md5PasswordBuffer = buffers.authenticationMD5Password() +const SASLBuffer = buffers.authenticationSASL() +const SASLContinueBuffer = buffers.authenticationSASLContinue() +const SASLFinalBuffer = buffers.authenticationSASLFinal() + +const expectedPlainPasswordMessage = { + name: 'authenticationCleartextPassword', +} + +const expectedMD5PasswordMessage = { + name: 'authenticationMD5Password', + salt: Buffer.from([1, 2, 3, 4]), +} + +const expectedSASLMessage = { + name: 'authenticationSASL', + mechanisms: ['SCRAM-SHA-256'], +} + +const expectedSASLContinueMessage = { + name: 'authenticationSASLContinue', + data: 'data', +} + +const expectedSASLFinalMessage = { + name: 'authenticationSASLFinal', + data: 'data', +} + +const notificationResponseBuffer = buffers.notification(4, 'hi', 'boom') +const expectedNotificationResponseMessage = { + name: 'notification', + processId: 4, + channel: 'hi', + payload: 'boom', +} + +const parseBuffers = async (buffers: Buffer[]): Promise => { + const stream = new PassThrough() + for (const buffer of buffers) { + stream.write(buffer) + } + stream.end() + const msgs: BackendMessage[] = [] + await parse(stream, (msg) => msgs.push(msg)) + return msgs +} + +describe('PgPacketStream', function () { + testForMessage(authOkBuffer, expectedAuthenticationOkayMessage) + testForMessage(plainPasswordBuffer, expectedPlainPasswordMessage) + testForMessage(md5PasswordBuffer, expectedMD5PasswordMessage) + testForMessage(SASLBuffer, expectedSASLMessage) + testForMessage(SASLContinueBuffer, expectedSASLContinueMessage) + + // this exercises a found bug in the parser: + // https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084 + // and adds a test which is deterministic, rather than relying on network packet chunking + const extendedSASLContinueBuffer = Buffer.concat([SASLContinueBuffer, Buffer.from([1, 2, 3, 4])]) + testForMessage(extendedSASLContinueBuffer, expectedSASLContinueMessage) + + testForMessage(SASLFinalBuffer, expectedSASLFinalMessage) + + // this exercises a found bug in the parser: + // https://github.com/brianc/node-postgres/pull/2210#issuecomment-627626084 + // and adds a test which is deterministic, rather than relying on network packet chunking + const extendedSASLFinalBuffer = Buffer.concat([SASLFinalBuffer, Buffer.from([1, 2, 4, 5])]) + testForMessage(extendedSASLFinalBuffer, expectedSASLFinalMessage) + + testForMessage(paramStatusBuffer, expectedParameterStatusMessage) + testForMessage(backendKeyDataBuffer, expectedBackendKeyDataMessage) + testForMessage(readyForQueryBuffer, expectedReadyForQueryMessage) + testForMessage(commandCompleteBuffer, expectedCommandCompleteMessage) + testForMessage(notificationResponseBuffer, expectedNotificationResponseMessage) + testForMessage(buffers.emptyQuery(), { + name: 'emptyQuery', + length: 4, + }) + + testForMessage(Buffer.from([0x6e, 0, 0, 0, 4]), { + name: 'noData', + }) + + describe('rowDescription messages', function () { + testForMessage(emptyRowDescriptionBuffer, expectedEmptyRowDescriptionMessage) + testForMessage(oneRowDescBuff, expectedOneRowMessage) + testForMessage(twoRowBuf, expectedTwoRowMessage) + testForMessage(bigOidDescBuff, expectedBigOidMessage) + }) + + describe('parameterDescription messages', function () { + testForMessage(emptyParameterDescriptionBuffer, expectedEmptyParameterDescriptionMessage) + testForMessage(oneParameterDescBuf, expectedOneParameterMessage) + testForMessage(twoParameterDescBuf, expectedTwoParameterMessage) + }) + + describe('parsing rows', function () { + describe('parsing empty row', function () { + testForMessage(emptyRowFieldBuf, { + name: 'dataRow', + fieldCount: 0, + }) + }) + + describe('parsing data row with fields', function () { + testForMessage(oneFieldBuf, { + name: 'dataRow', + fieldCount: 1, + fields: ['test'], + }) + }) + }) + + describe('notice message', function () { + // this uses the same logic as error message + const buff = buffers.notice([{ type: 'C', value: 'code' }]) + testForMessage(buff, { + name: 'notice', + code: 'code', + }) + }) + + testForMessage(buffers.error([]), { + name: 'error', + }) + + describe('with all the fields', function () { + const buffer = buffers.error([ + { + type: 'S', + value: 'ERROR', + }, + { + type: 'C', + value: 'code', + }, + { + type: 'M', + value: 'message', + }, + { + type: 'D', + value: 'details', + }, + { + type: 'H', + value: 'hint', + }, + { + type: 'P', + value: '100', + }, + { + type: 'p', + value: '101', + }, + { + type: 'q', + value: 'query', + }, + { + type: 'W', + value: 'where', + }, + { + type: 'F', + value: 'file', + }, + { + type: 'L', + value: 'line', + }, + { + type: 'R', + value: 'routine', + }, + { + type: 'Z', // ignored + value: 'alsdkf', + }, + ]) + + testForMessage(buffer, { + name: 'error', + severity: 'ERROR', + code: 'code', + message: 'message', + detail: 'details', + hint: 'hint', + position: '100', + internalPosition: '101', + internalQuery: 'query', + where: 'where', + file: 'file', + line: 'line', + routine: 'routine', + }) + }) + + testForMessage(parseCompleteBuffer, { + name: 'parseComplete', + }) + + testForMessage(bindCompleteBuffer, { + name: 'bindComplete', + }) + + testForMessage(bindCompleteBuffer, { + name: 'bindComplete', + }) + + testForMessage(buffers.closeComplete(), { + name: 'closeComplete', + }) + + describe('parses portal suspended message', function () { + testForMessage(portalSuspendedBuffer, { + name: 'portalSuspended', + }) + }) + + describe('parses replication start message', function () { + testForMessage(Buffer.from([0x57, 0x00, 0x00, 0x00, 0x04]), { + name: 'replicationStart', + length: 4, + }) + }) + + describe('copy', () => { + testForMessage(buffers.copyIn(0), { + name: 'copyInResponse', + length: 7, + binary: false, + columnTypes: [], + }) + + testForMessage(buffers.copyIn(2), { + name: 'copyInResponse', + length: 11, + binary: false, + columnTypes: [0, 1], + }) + + testForMessage(buffers.copyOut(0), { + name: 'copyOutResponse', + length: 7, + binary: false, + columnTypes: [], + }) + + testForMessage(buffers.copyOut(3), { + name: 'copyOutResponse', + length: 13, + binary: false, + columnTypes: [0, 1, 2], + }) + + testForMessage(buffers.copyDone(), { + name: 'copyDone', + length: 4, + }) + + testForMessage(buffers.copyData(Buffer.from([5, 6, 7])), { + name: 'copyData', + length: 7, + chunk: Buffer.from([5, 6, 7]), + }) + }) + + // since the data message on a stream can randomly divide the incomming + // tcp packets anywhere, we need to make sure we can parse every single + // split on a tcp message + describe('split buffer, single message parsing', function () { + const fullBuffer = buffers.dataRow([null, 'bang', 'zug zug', null, '!']) + + it('parses when full buffer comes in', async function () { + const messages = await parseBuffers([fullBuffer]) + const message = messages[0] as any + assert.equal(message.fields.length, 5) + assert.equal(message.fields[0], null) + assert.equal(message.fields[1], 'bang') + assert.equal(message.fields[2], 'zug zug') + assert.equal(message.fields[3], null) + assert.equal(message.fields[4], '!') + }) + + const testMessageReceivedAfterSplitAt = async function (split: number) { + const firstBuffer = Buffer.alloc(fullBuffer.length - split) + const secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length) + fullBuffer.copy(firstBuffer, 0, 0) + fullBuffer.copy(secondBuffer, 0, firstBuffer.length) + const messages = await parseBuffers([firstBuffer, secondBuffer]) + const message = messages[0] as any + assert.equal(message.fields.length, 5) + assert.equal(message.fields[0], null) + assert.equal(message.fields[1], 'bang') + assert.equal(message.fields[2], 'zug zug') + assert.equal(message.fields[3], null) + assert.equal(message.fields[4], '!') + } + + it('parses when split in the middle', function () { + return testMessageReceivedAfterSplitAt(6) + }) + + it('parses when split at end', function () { + return testMessageReceivedAfterSplitAt(2) + }) + + it('parses when split at beginning', function () { + return Promise.all([ + testMessageReceivedAfterSplitAt(fullBuffer.length - 2), + testMessageReceivedAfterSplitAt(fullBuffer.length - 1), + testMessageReceivedAfterSplitAt(fullBuffer.length - 5), + ]) + }) + }) + + describe('split buffer, multiple message parsing', function () { + const dataRowBuffer = buffers.dataRow(['!']) + const readyForQueryBuffer = buffers.readyForQuery() + const fullBuffer = Buffer.alloc(dataRowBuffer.length + readyForQueryBuffer.length) + dataRowBuffer.copy(fullBuffer, 0, 0) + readyForQueryBuffer.copy(fullBuffer, dataRowBuffer.length, 0) + + const verifyMessages = function (messages: any[]) { + assert.strictEqual(messages.length, 2) + assert.deepEqual(messages[0], { + name: 'dataRow', + fieldCount: 1, + length: 11, + fields: ['!'], + }) + assert.equal(messages[0].fields[0], '!') + assert.deepEqual(messages[1], { + name: 'readyForQuery', + length: 5, + status: 'I', + }) + } + // sanity check + it('receives both messages when packet is not split', async function () { + const messages = await parseBuffers([fullBuffer]) + verifyMessages(messages) + }) + + const splitAndVerifyTwoMessages = async function (split: number) { + const firstBuffer = Buffer.alloc(fullBuffer.length - split) + const secondBuffer = Buffer.alloc(fullBuffer.length - firstBuffer.length) + fullBuffer.copy(firstBuffer, 0, 0) + fullBuffer.copy(secondBuffer, 0, firstBuffer.length) + const messages = await parseBuffers([firstBuffer, secondBuffer]) + verifyMessages(messages) + } + + describe('receives both messages when packet is split', function () { + it('in the middle', function () { + return splitAndVerifyTwoMessages(11) + }) + it('at the front', function () { + return Promise.all([ + splitAndVerifyTwoMessages(fullBuffer.length - 1), + splitAndVerifyTwoMessages(fullBuffer.length - 4), + splitAndVerifyTwoMessages(fullBuffer.length - 6), + ]) + }) + + it('at the end', function () { + return Promise.all([splitAndVerifyTwoMessages(8), splitAndVerifyTwoMessages(1)]) + }) + }) + }) + + it('cleans up the reader after handling a packet', function () { + const parser = new Parser() + parser.parse(oneFieldBuf, () => {}) + assert.strictEqual((parser as any).reader.buffer.byteLength, 0) + }) +}) diff --git a/node_modules/pg-protocol/src/index.ts b/node_modules/pg-protocol/src/index.ts new file mode 100644 index 0000000..703ff2e --- /dev/null +++ b/node_modules/pg-protocol/src/index.ts @@ -0,0 +1,11 @@ +import { DatabaseError } from './messages' +import { serialize } from './serializer' +import { Parser, MessageCallback } from './parser' + +export function parse(stream: NodeJS.ReadableStream, callback: MessageCallback): Promise { + const parser = new Parser() + stream.on('data', (buffer: Buffer) => parser.parse(buffer, callback)) + return new Promise((resolve) => stream.on('end', () => resolve())) +} + +export { serialize, DatabaseError } diff --git a/node_modules/pg-protocol/src/messages.ts b/node_modules/pg-protocol/src/messages.ts new file mode 100644 index 0000000..c3fbbdd --- /dev/null +++ b/node_modules/pg-protocol/src/messages.ts @@ -0,0 +1,262 @@ +export type Mode = 'text' | 'binary' + +export type MessageName = + | 'parseComplete' + | 'bindComplete' + | 'closeComplete' + | 'noData' + | 'portalSuspended' + | 'replicationStart' + | 'emptyQuery' + | 'copyDone' + | 'copyData' + | 'rowDescription' + | 'parameterDescription' + | 'parameterStatus' + | 'backendKeyData' + | 'notification' + | 'readyForQuery' + | 'commandComplete' + | 'dataRow' + | 'copyInResponse' + | 'copyOutResponse' + | 'authenticationOk' + | 'authenticationMD5Password' + | 'authenticationCleartextPassword' + | 'authenticationSASL' + | 'authenticationSASLContinue' + | 'authenticationSASLFinal' + | 'error' + | 'notice' + +export interface BackendMessage { + name: MessageName + length: number +} + +export const parseComplete: BackendMessage = { + name: 'parseComplete', + length: 5, +} + +export const bindComplete: BackendMessage = { + name: 'bindComplete', + length: 5, +} + +export const closeComplete: BackendMessage = { + name: 'closeComplete', + length: 5, +} + +export const noData: BackendMessage = { + name: 'noData', + length: 5, +} + +export const portalSuspended: BackendMessage = { + name: 'portalSuspended', + length: 5, +} + +export const replicationStart: BackendMessage = { + name: 'replicationStart', + length: 4, +} + +export const emptyQuery: BackendMessage = { + name: 'emptyQuery', + length: 4, +} + +export const copyDone: BackendMessage = { + name: 'copyDone', + length: 4, +} + +interface NoticeOrError { + message: string | undefined + severity: string | undefined + code: string | undefined + detail: string | undefined + hint: string | undefined + position: string | undefined + internalPosition: string | undefined + internalQuery: string | undefined + where: string | undefined + schema: string | undefined + table: string | undefined + column: string | undefined + dataType: string | undefined + constraint: string | undefined + file: string | undefined + line: string | undefined + routine: string | undefined +} + +export class DatabaseError extends Error implements NoticeOrError { + public severity: string | undefined + public code: string | undefined + public detail: string | undefined + public hint: string | undefined + public position: string | undefined + public internalPosition: string | undefined + public internalQuery: string | undefined + public where: string | undefined + public schema: string | undefined + public table: string | undefined + public column: string | undefined + public dataType: string | undefined + public constraint: string | undefined + public file: string | undefined + public line: string | undefined + public routine: string | undefined + constructor( + message: string, + public readonly length: number, + public readonly name: MessageName + ) { + super(message) + } +} + +export class CopyDataMessage { + public readonly name = 'copyData' + constructor( + public readonly length: number, + public readonly chunk: Buffer + ) {} +} + +export class CopyResponse { + public readonly columnTypes: number[] + constructor( + public readonly length: number, + public readonly name: MessageName, + public readonly binary: boolean, + columnCount: number + ) { + this.columnTypes = new Array(columnCount) + } +} + +export class Field { + constructor( + public readonly name: string, + public readonly tableID: number, + public readonly columnID: number, + public readonly dataTypeID: number, + public readonly dataTypeSize: number, + public readonly dataTypeModifier: number, + public readonly format: Mode + ) {} +} + +export class RowDescriptionMessage { + public readonly name: MessageName = 'rowDescription' + public readonly fields: Field[] + constructor( + public readonly length: number, + public readonly fieldCount: number + ) { + this.fields = new Array(this.fieldCount) + } +} + +export class ParameterDescriptionMessage { + public readonly name: MessageName = 'parameterDescription' + public readonly dataTypeIDs: number[] + constructor( + public readonly length: number, + public readonly parameterCount: number + ) { + this.dataTypeIDs = new Array(this.parameterCount) + } +} + +export class ParameterStatusMessage { + public readonly name: MessageName = 'parameterStatus' + constructor( + public readonly length: number, + public readonly parameterName: string, + public readonly parameterValue: string + ) {} +} + +export class AuthenticationMD5Password implements BackendMessage { + public readonly name: MessageName = 'authenticationMD5Password' + constructor( + public readonly length: number, + public readonly salt: Buffer + ) {} +} + +export class BackendKeyDataMessage { + public readonly name: MessageName = 'backendKeyData' + constructor( + public readonly length: number, + public readonly processID: number, + public readonly secretKey: number + ) {} +} + +export class NotificationResponseMessage { + public readonly name: MessageName = 'notification' + constructor( + public readonly length: number, + public readonly processId: number, + public readonly channel: string, + public readonly payload: string + ) {} +} + +export class ReadyForQueryMessage { + public readonly name: MessageName = 'readyForQuery' + constructor( + public readonly length: number, + public readonly status: string + ) {} +} + +export class CommandCompleteMessage { + public readonly name: MessageName = 'commandComplete' + constructor( + public readonly length: number, + public readonly text: string + ) {} +} + +export class DataRowMessage { + public readonly fieldCount: number + public readonly name: MessageName = 'dataRow' + constructor( + public length: number, + public fields: any[] + ) { + this.fieldCount = fields.length + } +} + +export class NoticeMessage implements BackendMessage, NoticeOrError { + constructor( + public readonly length: number, + public readonly message: string | undefined + ) {} + public readonly name = 'notice' + public severity: string | undefined + public code: string | undefined + public detail: string | undefined + public hint: string | undefined + public position: string | undefined + public internalPosition: string | undefined + public internalQuery: string | undefined + public where: string | undefined + public schema: string | undefined + public table: string | undefined + public column: string | undefined + public dataType: string | undefined + public constraint: string | undefined + public file: string | undefined + public line: string | undefined + public routine: string | undefined +} diff --git a/node_modules/pg-protocol/src/outbound-serializer.test.ts b/node_modules/pg-protocol/src/outbound-serializer.test.ts new file mode 100644 index 0000000..0d3e387 --- /dev/null +++ b/node_modules/pg-protocol/src/outbound-serializer.test.ts @@ -0,0 +1,276 @@ +import assert from 'assert' +import { serialize } from './serializer' +import BufferList from './testing/buffer-list' + +describe('serializer', () => { + it('builds startup message', function () { + const actual = serialize.startup({ + user: 'brian', + database: 'bang', + }) + assert.deepEqual( + actual, + new BufferList() + .addInt16(3) + .addInt16(0) + .addCString('user') + .addCString('brian') + .addCString('database') + .addCString('bang') + .addCString('client_encoding') + .addCString('UTF8') + .addCString('') + .join(true) + ) + }) + + it('builds password message', function () { + const actual = serialize.password('!') + assert.deepEqual(actual, new BufferList().addCString('!').join(true, 'p')) + }) + + it('builds request ssl message', function () { + const actual = serialize.requestSsl() + const expected = new BufferList().addInt32(80877103).join(true) + assert.deepEqual(actual, expected) + }) + + it('builds SASLInitialResponseMessage message', function () { + const actual = serialize.sendSASLInitialResponseMessage('mech', 'data') + assert.deepEqual(actual, new BufferList().addCString('mech').addInt32(4).addString('data').join(true, 'p')) + }) + + it('builds SCRAMClientFinalMessage message', function () { + const actual = serialize.sendSCRAMClientFinalMessage('data') + assert.deepEqual(actual, new BufferList().addString('data').join(true, 'p')) + }) + + it('builds query message', function () { + const txt = 'select * from boom' + const actual = serialize.query(txt) + assert.deepEqual(actual, new BufferList().addCString(txt).join(true, 'Q')) + }) + + describe('parse message', () => { + it('builds parse message', function () { + const actual = serialize.parse({ text: '!' }) + const expected = new BufferList().addCString('').addCString('!').addInt16(0).join(true, 'P') + assert.deepEqual(actual, expected) + }) + + it('builds parse message with named query', function () { + const actual = serialize.parse({ + name: 'boom', + text: 'select * from boom', + types: [], + }) + const expected = new BufferList().addCString('boom').addCString('select * from boom').addInt16(0).join(true, 'P') + assert.deepEqual(actual, expected) + }) + + it('with multiple parameters', function () { + const actual = serialize.parse({ + name: 'force', + text: 'select * from bang where name = $1', + types: [1, 2, 3, 4], + }) + const expected = new BufferList() + .addCString('force') + .addCString('select * from bang where name = $1') + .addInt16(4) + .addInt32(1) + .addInt32(2) + .addInt32(3) + .addInt32(4) + .join(true, 'P') + assert.deepEqual(actual, expected) + }) + }) + + describe('bind messages', function () { + it('with no values', function () { + const actual = serialize.bind() + + const expectedBuffer = new BufferList() + .addCString('') + .addCString('') + .addInt16(0) + .addInt16(0) + .addInt16(1) + .addInt16(0) + .join(true, 'B') + assert.deepEqual(actual, expectedBuffer) + }) + + it('with named statement, portal, and values', function () { + const actual = serialize.bind({ + portal: 'bang', + statement: 'woo', + values: ['1', 'hi', null, 'zing'], + }) + const expectedBuffer = new BufferList() + .addCString('bang') // portal name + .addCString('woo') // statement name + .addInt16(4) + .addInt16(0) + .addInt16(0) + .addInt16(0) + .addInt16(0) + .addInt16(4) + .addInt32(1) + .add(Buffer.from('1')) + .addInt32(2) + .add(Buffer.from('hi')) + .addInt32(-1) + .addInt32(4) + .add(Buffer.from('zing')) + .addInt16(1) + .addInt16(0) + .join(true, 'B') + assert.deepEqual(actual, expectedBuffer) + }) + }) + + it('with custom valueMapper', function () { + const actual = serialize.bind({ + portal: 'bang', + statement: 'woo', + values: ['1', 'hi', null, 'zing'], + valueMapper: () => null, + }) + const expectedBuffer = new BufferList() + .addCString('bang') // portal name + .addCString('woo') // statement name + .addInt16(4) + .addInt16(0) + .addInt16(0) + .addInt16(0) + .addInt16(0) + .addInt16(4) + .addInt32(-1) + .addInt32(-1) + .addInt32(-1) + .addInt32(-1) + .addInt16(1) + .addInt16(0) + .join(true, 'B') + assert.deepEqual(actual, expectedBuffer) + }) + + it('with named statement, portal, and buffer value', function () { + const actual = serialize.bind({ + portal: 'bang', + statement: 'woo', + values: ['1', 'hi', null, Buffer.from('zing', 'utf8')], + }) + const expectedBuffer = new BufferList() + .addCString('bang') // portal name + .addCString('woo') // statement name + .addInt16(4) // value count + .addInt16(0) // string + .addInt16(0) // string + .addInt16(0) // string + .addInt16(1) // binary + .addInt16(4) + .addInt32(1) + .add(Buffer.from('1')) + .addInt32(2) + .add(Buffer.from('hi')) + .addInt32(-1) + .addInt32(4) + .add(Buffer.from('zing', 'utf-8')) + .addInt16(1) + .addInt16(0) + .join(true, 'B') + assert.deepEqual(actual, expectedBuffer) + }) + + describe('builds execute message', function () { + it('for unamed portal with no row limit', function () { + const actual = serialize.execute() + const expectedBuffer = new BufferList().addCString('').addInt32(0).join(true, 'E') + assert.deepEqual(actual, expectedBuffer) + }) + + it('for named portal with row limit', function () { + const actual = serialize.execute({ + portal: 'my favorite portal', + rows: 100, + }) + const expectedBuffer = new BufferList().addCString('my favorite portal').addInt32(100).join(true, 'E') + assert.deepEqual(actual, expectedBuffer) + }) + }) + + it('builds flush command', function () { + const actual = serialize.flush() + const expected = new BufferList().join(true, 'H') + assert.deepEqual(actual, expected) + }) + + it('builds sync command', function () { + const actual = serialize.sync() + const expected = new BufferList().join(true, 'S') + assert.deepEqual(actual, expected) + }) + + it('builds end command', function () { + const actual = serialize.end() + const expected = Buffer.from([0x58, 0, 0, 0, 4]) + assert.deepEqual(actual, expected) + }) + + describe('builds describe command', function () { + it('describe statement', function () { + const actual = serialize.describe({ type: 'S', name: 'bang' }) + const expected = new BufferList().addChar('S').addCString('bang').join(true, 'D') + assert.deepEqual(actual, expected) + }) + + it('describe unnamed portal', function () { + const actual = serialize.describe({ type: 'P' }) + const expected = new BufferList().addChar('P').addCString('').join(true, 'D') + assert.deepEqual(actual, expected) + }) + }) + + describe('builds close command', function () { + it('describe statement', function () { + const actual = serialize.close({ type: 'S', name: 'bang' }) + const expected = new BufferList().addChar('S').addCString('bang').join(true, 'C') + assert.deepEqual(actual, expected) + }) + + it('describe unnamed portal', function () { + const actual = serialize.close({ type: 'P' }) + const expected = new BufferList().addChar('P').addCString('').join(true, 'C') + assert.deepEqual(actual, expected) + }) + }) + + describe('copy messages', function () { + it('builds copyFromChunk', () => { + const actual = serialize.copyData(Buffer.from([1, 2, 3])) + const expected = new BufferList().add(Buffer.from([1, 2, 3])).join(true, 'd') + assert.deepEqual(actual, expected) + }) + + it('builds copy fail', () => { + const actual = serialize.copyFail('err!') + const expected = new BufferList().addCString('err!').join(true, 'f') + assert.deepEqual(actual, expected) + }) + + it('builds copy done', () => { + const actual = serialize.copyDone() + const expected = new BufferList().join(true, 'c') + assert.deepEqual(actual, expected) + }) + }) + + it('builds cancel message', () => { + const actual = serialize.cancel(3, 4) + const expected = new BufferList().addInt16(1234).addInt16(5678).addInt32(3).addInt32(4).join(true) + assert.deepEqual(actual, expected) + }) +}) diff --git a/node_modules/pg-protocol/src/parser.ts b/node_modules/pg-protocol/src/parser.ts new file mode 100644 index 0000000..998077a --- /dev/null +++ b/node_modules/pg-protocol/src/parser.ts @@ -0,0 +1,413 @@ +import { TransformOptions } from 'stream' +import { + Mode, + bindComplete, + parseComplete, + closeComplete, + noData, + portalSuspended, + copyDone, + replicationStart, + emptyQuery, + ReadyForQueryMessage, + CommandCompleteMessage, + CopyDataMessage, + CopyResponse, + NotificationResponseMessage, + RowDescriptionMessage, + ParameterDescriptionMessage, + Field, + DataRowMessage, + ParameterStatusMessage, + BackendKeyDataMessage, + DatabaseError, + BackendMessage, + MessageName, + AuthenticationMD5Password, + NoticeMessage, +} from './messages' +import { BufferReader } from './buffer-reader' + +// every message is prefixed with a single bye +const CODE_LENGTH = 1 +// every message has an int32 length which includes itself but does +// NOT include the code in the length +const LEN_LENGTH = 4 + +const HEADER_LENGTH = CODE_LENGTH + LEN_LENGTH + +// A placeholder for a `BackendMessage`’s length value that will be set after construction. +const LATEINIT_LENGTH = -1 + +export type Packet = { + code: number + packet: Buffer +} + +const emptyBuffer = Buffer.allocUnsafe(0) + +type StreamOptions = TransformOptions & { + mode: Mode +} + +const enum MessageCodes { + DataRow = 0x44, // D + ParseComplete = 0x31, // 1 + BindComplete = 0x32, // 2 + CloseComplete = 0x33, // 3 + CommandComplete = 0x43, // C + ReadyForQuery = 0x5a, // Z + NoData = 0x6e, // n + NotificationResponse = 0x41, // A + AuthenticationResponse = 0x52, // R + ParameterStatus = 0x53, // S + BackendKeyData = 0x4b, // K + ErrorMessage = 0x45, // E + NoticeMessage = 0x4e, // N + RowDescriptionMessage = 0x54, // T + ParameterDescriptionMessage = 0x74, // t + PortalSuspended = 0x73, // s + ReplicationStart = 0x57, // W + EmptyQuery = 0x49, // I + CopyIn = 0x47, // G + CopyOut = 0x48, // H + CopyDone = 0x63, // c + CopyData = 0x64, // d +} + +export type MessageCallback = (msg: BackendMessage) => void + +export class Parser { + private buffer: Buffer = emptyBuffer + private bufferLength: number = 0 + private bufferOffset: number = 0 + private reader = new BufferReader() + private mode: Mode + + constructor(opts?: StreamOptions) { + if (opts?.mode === 'binary') { + throw new Error('Binary mode not supported yet') + } + this.mode = opts?.mode || 'text' + } + + public parse(buffer: Buffer, callback: MessageCallback) { + this.mergeBuffer(buffer) + const bufferFullLength = this.bufferOffset + this.bufferLength + let offset = this.bufferOffset + while (offset + HEADER_LENGTH <= bufferFullLength) { + // code is 1 byte long - it identifies the message type + const code = this.buffer[offset] + // length is 1 Uint32BE - it is the length of the message EXCLUDING the code + const length = this.buffer.readUInt32BE(offset + CODE_LENGTH) + const fullMessageLength = CODE_LENGTH + length + if (fullMessageLength + offset <= bufferFullLength) { + const message = this.handlePacket(offset + HEADER_LENGTH, code, length, this.buffer) + callback(message) + offset += fullMessageLength + } else { + break + } + } + if (offset === bufferFullLength) { + // No more use for the buffer + this.buffer = emptyBuffer + this.bufferLength = 0 + this.bufferOffset = 0 + } else { + // Adjust the cursors of remainingBuffer + this.bufferLength = bufferFullLength - offset + this.bufferOffset = offset + } + } + + private mergeBuffer(buffer: Buffer): void { + if (this.bufferLength > 0) { + const newLength = this.bufferLength + buffer.byteLength + const newFullLength = newLength + this.bufferOffset + if (newFullLength > this.buffer.byteLength) { + // We can't concat the new buffer with the remaining one + let newBuffer: Buffer + if (newLength <= this.buffer.byteLength && this.bufferOffset >= this.bufferLength) { + // We can move the relevant part to the beginning of the buffer instead of allocating a new buffer + newBuffer = this.buffer + } else { + // Allocate a new larger buffer + let newBufferLength = this.buffer.byteLength * 2 + while (newLength >= newBufferLength) { + newBufferLength *= 2 + } + newBuffer = Buffer.allocUnsafe(newBufferLength) + } + // Move the remaining buffer to the new one + this.buffer.copy(newBuffer, 0, this.bufferOffset, this.bufferOffset + this.bufferLength) + this.buffer = newBuffer + this.bufferOffset = 0 + } + // Concat the new buffer with the remaining one + buffer.copy(this.buffer, this.bufferOffset + this.bufferLength) + this.bufferLength = newLength + } else { + this.buffer = buffer + this.bufferOffset = 0 + this.bufferLength = buffer.byteLength + } + } + + private handlePacket(offset: number, code: number, length: number, bytes: Buffer): BackendMessage { + const { reader } = this + + // NOTE: This undesirably retains the buffer in `this.reader` if the `parse*Message` calls below throw. However, those should only throw in the case of a protocol error, which normally results in the reader being discarded. + reader.setBuffer(offset, bytes) + + let message: BackendMessage + + switch (code) { + case MessageCodes.BindComplete: + message = bindComplete + break + case MessageCodes.ParseComplete: + message = parseComplete + break + case MessageCodes.CloseComplete: + message = closeComplete + break + case MessageCodes.NoData: + message = noData + break + case MessageCodes.PortalSuspended: + message = portalSuspended + break + case MessageCodes.CopyDone: + message = copyDone + break + case MessageCodes.ReplicationStart: + message = replicationStart + break + case MessageCodes.EmptyQuery: + message = emptyQuery + break + case MessageCodes.DataRow: + message = parseDataRowMessage(reader) + break + case MessageCodes.CommandComplete: + message = parseCommandCompleteMessage(reader) + break + case MessageCodes.ReadyForQuery: + message = parseReadyForQueryMessage(reader) + break + case MessageCodes.NotificationResponse: + message = parseNotificationMessage(reader) + break + case MessageCodes.AuthenticationResponse: + message = parseAuthenticationResponse(reader, length) + break + case MessageCodes.ParameterStatus: + message = parseParameterStatusMessage(reader) + break + case MessageCodes.BackendKeyData: + message = parseBackendKeyData(reader) + break + case MessageCodes.ErrorMessage: + message = parseErrorMessage(reader, 'error') + break + case MessageCodes.NoticeMessage: + message = parseErrorMessage(reader, 'notice') + break + case MessageCodes.RowDescriptionMessage: + message = parseRowDescriptionMessage(reader) + break + case MessageCodes.ParameterDescriptionMessage: + message = parseParameterDescriptionMessage(reader) + break + case MessageCodes.CopyIn: + message = parseCopyInMessage(reader) + break + case MessageCodes.CopyOut: + message = parseCopyOutMessage(reader) + break + case MessageCodes.CopyData: + message = parseCopyData(reader, length) + break + default: + return new DatabaseError('received invalid response: ' + code.toString(16), length, 'error') + } + + reader.setBuffer(0, emptyBuffer) + + message.length = length + return message + } +} + +const parseReadyForQueryMessage = (reader: BufferReader) => { + const status = reader.string(1) + return new ReadyForQueryMessage(LATEINIT_LENGTH, status) +} + +const parseCommandCompleteMessage = (reader: BufferReader) => { + const text = reader.cstring() + return new CommandCompleteMessage(LATEINIT_LENGTH, text) +} + +const parseCopyData = (reader: BufferReader, length: number) => { + const chunk = reader.bytes(length - 4) + return new CopyDataMessage(LATEINIT_LENGTH, chunk) +} + +const parseCopyInMessage = (reader: BufferReader) => parseCopyMessage(reader, 'copyInResponse') + +const parseCopyOutMessage = (reader: BufferReader) => parseCopyMessage(reader, 'copyOutResponse') + +const parseCopyMessage = (reader: BufferReader, messageName: MessageName) => { + const isBinary = reader.byte() !== 0 + const columnCount = reader.int16() + const message = new CopyResponse(LATEINIT_LENGTH, messageName, isBinary, columnCount) + for (let i = 0; i < columnCount; i++) { + message.columnTypes[i] = reader.int16() + } + return message +} + +const parseNotificationMessage = (reader: BufferReader) => { + const processId = reader.int32() + const channel = reader.cstring() + const payload = reader.cstring() + return new NotificationResponseMessage(LATEINIT_LENGTH, processId, channel, payload) +} + +const parseRowDescriptionMessage = (reader: BufferReader) => { + const fieldCount = reader.int16() + const message = new RowDescriptionMessage(LATEINIT_LENGTH, fieldCount) + for (let i = 0; i < fieldCount; i++) { + message.fields[i] = parseField(reader) + } + return message +} + +const parseField = (reader: BufferReader) => { + const name = reader.cstring() + const tableID = reader.uint32() + const columnID = reader.int16() + const dataTypeID = reader.uint32() + const dataTypeSize = reader.int16() + const dataTypeModifier = reader.int32() + const mode = reader.int16() === 0 ? 'text' : 'binary' + return new Field(name, tableID, columnID, dataTypeID, dataTypeSize, dataTypeModifier, mode) +} + +const parseParameterDescriptionMessage = (reader: BufferReader) => { + const parameterCount = reader.int16() + const message = new ParameterDescriptionMessage(LATEINIT_LENGTH, parameterCount) + for (let i = 0; i < parameterCount; i++) { + message.dataTypeIDs[i] = reader.int32() + } + return message +} + +const parseDataRowMessage = (reader: BufferReader) => { + const fieldCount = reader.int16() + const fields: any[] = new Array(fieldCount) + for (let i = 0; i < fieldCount; i++) { + const len = reader.int32() + // a -1 for length means the value of the field is null + fields[i] = len === -1 ? null : reader.string(len) + } + return new DataRowMessage(LATEINIT_LENGTH, fields) +} + +const parseParameterStatusMessage = (reader: BufferReader) => { + const name = reader.cstring() + const value = reader.cstring() + return new ParameterStatusMessage(LATEINIT_LENGTH, name, value) +} + +const parseBackendKeyData = (reader: BufferReader) => { + const processID = reader.int32() + const secretKey = reader.int32() + return new BackendKeyDataMessage(LATEINIT_LENGTH, processID, secretKey) +} + +const parseAuthenticationResponse = (reader: BufferReader, length: number) => { + const code = reader.int32() + // TODO(bmc): maybe better types here + const message: BackendMessage & any = { + name: 'authenticationOk', + length, + } + + switch (code) { + case 0: // AuthenticationOk + break + case 3: // AuthenticationCleartextPassword + if (message.length === 8) { + message.name = 'authenticationCleartextPassword' + } + break + case 5: // AuthenticationMD5Password + if (message.length === 12) { + message.name = 'authenticationMD5Password' + const salt = reader.bytes(4) + return new AuthenticationMD5Password(LATEINIT_LENGTH, salt) + } + break + case 10: // AuthenticationSASL + { + message.name = 'authenticationSASL' + message.mechanisms = [] + let mechanism: string + do { + mechanism = reader.cstring() + if (mechanism) { + message.mechanisms.push(mechanism) + } + } while (mechanism) + } + break + case 11: // AuthenticationSASLContinue + message.name = 'authenticationSASLContinue' + message.data = reader.string(length - 8) + break + case 12: // AuthenticationSASLFinal + message.name = 'authenticationSASLFinal' + message.data = reader.string(length - 8) + break + default: + throw new Error('Unknown authenticationOk message type ' + code) + } + return message +} + +const parseErrorMessage = (reader: BufferReader, name: MessageName) => { + const fields: Record = {} + let fieldType = reader.string(1) + while (fieldType !== '\0') { + fields[fieldType] = reader.cstring() + fieldType = reader.string(1) + } + + const messageValue = fields.M + + const message = + name === 'notice' + ? new NoticeMessage(LATEINIT_LENGTH, messageValue) + : new DatabaseError(messageValue, LATEINIT_LENGTH, name) + + message.severity = fields.S + message.code = fields.C + message.detail = fields.D + message.hint = fields.H + message.position = fields.P + message.internalPosition = fields.p + message.internalQuery = fields.q + message.where = fields.W + message.schema = fields.s + message.table = fields.t + message.column = fields.c + message.dataType = fields.d + message.constraint = fields.n + message.file = fields.F + message.line = fields.L + message.routine = fields.R + return message +} diff --git a/node_modules/pg-protocol/src/serializer.ts b/node_modules/pg-protocol/src/serializer.ts new file mode 100644 index 0000000..bb0441f --- /dev/null +++ b/node_modules/pg-protocol/src/serializer.ts @@ -0,0 +1,274 @@ +import { Writer } from './buffer-writer' + +const enum code { + startup = 0x70, + query = 0x51, + parse = 0x50, + bind = 0x42, + execute = 0x45, + flush = 0x48, + sync = 0x53, + end = 0x58, + close = 0x43, + describe = 0x44, + copyFromChunk = 0x64, + copyDone = 0x63, + copyFail = 0x66, +} + +const writer = new Writer() + +const startup = (opts: Record): Buffer => { + // protocol version + writer.addInt16(3).addInt16(0) + for (const key of Object.keys(opts)) { + writer.addCString(key).addCString(opts[key]) + } + + writer.addCString('client_encoding').addCString('UTF8') + + const bodyBuffer = writer.addCString('').flush() + // this message is sent without a code + + const length = bodyBuffer.length + 4 + + return new Writer().addInt32(length).add(bodyBuffer).flush() +} + +const requestSsl = (): Buffer => { + const response = Buffer.allocUnsafe(8) + response.writeInt32BE(8, 0) + response.writeInt32BE(80877103, 4) + return response +} + +const password = (password: string): Buffer => { + return writer.addCString(password).flush(code.startup) +} + +const sendSASLInitialResponseMessage = function (mechanism: string, initialResponse: string): Buffer { + // 0x70 = 'p' + writer.addCString(mechanism).addInt32(Buffer.byteLength(initialResponse)).addString(initialResponse) + + return writer.flush(code.startup) +} + +const sendSCRAMClientFinalMessage = function (additionalData: string): Buffer { + return writer.addString(additionalData).flush(code.startup) +} + +const query = (text: string): Buffer => { + return writer.addCString(text).flush(code.query) +} + +type ParseOpts = { + name?: string + types?: number[] + text: string +} + +const emptyArray: any[] = [] + +const parse = (query: ParseOpts): Buffer => { + // expect something like this: + // { name: 'queryName', + // text: 'select * from blah', + // types: ['int8', 'bool'] } + + // normalize missing query names to allow for null + const name = query.name || '' + if (name.length > 63) { + console.error('Warning! Postgres only supports 63 characters for query names.') + console.error('You supplied %s (%s)', name, name.length) + console.error('This can cause conflicts and silent errors executing queries') + } + + const types = query.types || emptyArray + + const len = types.length + + const buffer = writer + .addCString(name) // name of query + .addCString(query.text) // actual query text + .addInt16(len) + + for (let i = 0; i < len; i++) { + buffer.addInt32(types[i]) + } + + return writer.flush(code.parse) +} + +type ValueMapper = (param: any, index: number) => any + +type BindOpts = { + portal?: string + binary?: boolean + statement?: string + values?: any[] + // optional map from JS value to postgres value per parameter + valueMapper?: ValueMapper +} + +const paramWriter = new Writer() + +// make this a const enum so typescript will inline the value +const enum ParamType { + STRING = 0, + BINARY = 1, +} + +const writeValues = function (values: any[], valueMapper?: ValueMapper): void { + for (let i = 0; i < values.length; i++) { + const mappedVal = valueMapper ? valueMapper(values[i], i) : values[i] + if (mappedVal == null) { + // add the param type (string) to the writer + writer.addInt16(ParamType.STRING) + // write -1 to the param writer to indicate null + paramWriter.addInt32(-1) + } else if (mappedVal instanceof Buffer) { + // add the param type (binary) to the writer + writer.addInt16(ParamType.BINARY) + // add the buffer to the param writer + paramWriter.addInt32(mappedVal.length) + paramWriter.add(mappedVal) + } else { + // add the param type (string) to the writer + writer.addInt16(ParamType.STRING) + paramWriter.addInt32(Buffer.byteLength(mappedVal)) + paramWriter.addString(mappedVal) + } + } +} + +const bind = (config: BindOpts = {}): Buffer => { + // normalize config + const portal = config.portal || '' + const statement = config.statement || '' + const binary = config.binary || false + const values = config.values || emptyArray + const len = values.length + + writer.addCString(portal).addCString(statement) + writer.addInt16(len) + + writeValues(values, config.valueMapper) + + writer.addInt16(len) + writer.add(paramWriter.flush()) + + // all results use the same format code + writer.addInt16(1) + // format code + writer.addInt16(binary ? ParamType.BINARY : ParamType.STRING) + return writer.flush(code.bind) +} + +type ExecOpts = { + portal?: string + rows?: number +} + +const emptyExecute = Buffer.from([code.execute, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00]) + +const execute = (config?: ExecOpts): Buffer => { + // this is the happy path for most queries + if (!config || (!config.portal && !config.rows)) { + return emptyExecute + } + + const portal = config.portal || '' + const rows = config.rows || 0 + + const portalLength = Buffer.byteLength(portal) + const len = 4 + portalLength + 1 + 4 + // one extra bit for code + const buff = Buffer.allocUnsafe(1 + len) + buff[0] = code.execute + buff.writeInt32BE(len, 1) + buff.write(portal, 5, 'utf-8') + buff[portalLength + 5] = 0 // null terminate portal cString + buff.writeUInt32BE(rows, buff.length - 4) + return buff +} + +const cancel = (processID: number, secretKey: number): Buffer => { + const buffer = Buffer.allocUnsafe(16) + buffer.writeInt32BE(16, 0) + buffer.writeInt16BE(1234, 4) + buffer.writeInt16BE(5678, 6) + buffer.writeInt32BE(processID, 8) + buffer.writeInt32BE(secretKey, 12) + return buffer +} + +type PortalOpts = { + type: 'S' | 'P' + name?: string +} + +const cstringMessage = (code: code, string: string): Buffer => { + const stringLen = Buffer.byteLength(string) + const len = 4 + stringLen + 1 + // one extra bit for code + const buffer = Buffer.allocUnsafe(1 + len) + buffer[0] = code + buffer.writeInt32BE(len, 1) + buffer.write(string, 5, 'utf-8') + buffer[len] = 0 // null terminate cString + return buffer +} + +const emptyDescribePortal = writer.addCString('P').flush(code.describe) +const emptyDescribeStatement = writer.addCString('S').flush(code.describe) + +const describe = (msg: PortalOpts): Buffer => { + return msg.name + ? cstringMessage(code.describe, `${msg.type}${msg.name || ''}`) + : msg.type === 'P' + ? emptyDescribePortal + : emptyDescribeStatement +} + +const close = (msg: PortalOpts): Buffer => { + const text = `${msg.type}${msg.name || ''}` + return cstringMessage(code.close, text) +} + +const copyData = (chunk: Buffer): Buffer => { + return writer.add(chunk).flush(code.copyFromChunk) +} + +const copyFail = (message: string): Buffer => { + return cstringMessage(code.copyFail, message) +} + +const codeOnlyBuffer = (code: code): Buffer => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]) + +const flushBuffer = codeOnlyBuffer(code.flush) +const syncBuffer = codeOnlyBuffer(code.sync) +const endBuffer = codeOnlyBuffer(code.end) +const copyDoneBuffer = codeOnlyBuffer(code.copyDone) + +const serialize = { + startup, + password, + requestSsl, + sendSASLInitialResponseMessage, + sendSCRAMClientFinalMessage, + query, + parse, + bind, + execute, + describe, + close, + flush: () => flushBuffer, + sync: () => syncBuffer, + end: () => endBuffer, + copyData, + copyDone: () => copyDoneBuffer, + copyFail, + cancel, +} + +export { serialize } diff --git a/node_modules/pg-protocol/src/testing/buffer-list.ts b/node_modules/pg-protocol/src/testing/buffer-list.ts new file mode 100644 index 0000000..bef75d4 --- /dev/null +++ b/node_modules/pg-protocol/src/testing/buffer-list.ts @@ -0,0 +1,67 @@ +export default class BufferList { + constructor(public buffers: Buffer[] = []) {} + + public add(buffer: Buffer, front?: boolean) { + this.buffers[front ? 'unshift' : 'push'](buffer) + return this + } + + public addInt16(val: number, front?: boolean) { + return this.add(Buffer.from([val >>> 8, val >>> 0]), front) + } + + public getByteLength() { + return this.buffers.reduce(function (previous, current) { + return previous + current.length + }, 0) + } + + public addInt32(val: number, first?: boolean) { + return this.add( + Buffer.from([(val >>> 24) & 0xff, (val >>> 16) & 0xff, (val >>> 8) & 0xff, (val >>> 0) & 0xff]), + first + ) + } + + public addCString(val: string, front?: boolean) { + const len = Buffer.byteLength(val) + const buffer = Buffer.alloc(len + 1) + buffer.write(val) + buffer[len] = 0 + return this.add(buffer, front) + } + + public addString(val: string, front?: boolean) { + const len = Buffer.byteLength(val) + const buffer = Buffer.alloc(len) + buffer.write(val) + return this.add(buffer, front) + } + + public addChar(char: string, first?: boolean) { + return this.add(Buffer.from(char, 'utf8'), first) + } + + public addByte(byte: number) { + return this.add(Buffer.from([byte])) + } + + public join(appendLength?: boolean, char?: string): Buffer { + let length = this.getByteLength() + if (appendLength) { + this.addInt32(length + 4, true) + return this.join(false, char) + } + if (char) { + this.addChar(char, true) + length++ + } + const result = Buffer.alloc(length) + let index = 0 + this.buffers.forEach(function (buffer) { + buffer.copy(result, index, 0) + index += buffer.length + }) + return result + } +} diff --git a/node_modules/pg-protocol/src/testing/test-buffers.ts b/node_modules/pg-protocol/src/testing/test-buffers.ts new file mode 100644 index 0000000..1f0d71f --- /dev/null +++ b/node_modules/pg-protocol/src/testing/test-buffers.ts @@ -0,0 +1,166 @@ +// https://www.postgresql.org/docs/current/protocol-message-formats.html +import BufferList from './buffer-list' + +const buffers = { + readyForQuery: function () { + return new BufferList().add(Buffer.from('I')).join(true, 'Z') + }, + + authenticationOk: function () { + return new BufferList().addInt32(0).join(true, 'R') + }, + + authenticationCleartextPassword: function () { + return new BufferList().addInt32(3).join(true, 'R') + }, + + authenticationMD5Password: function () { + return new BufferList() + .addInt32(5) + .add(Buffer.from([1, 2, 3, 4])) + .join(true, 'R') + }, + + authenticationSASL: function () { + return new BufferList().addInt32(10).addCString('SCRAM-SHA-256').addCString('').join(true, 'R') + }, + + authenticationSASLContinue: function () { + return new BufferList().addInt32(11).addString('data').join(true, 'R') + }, + + authenticationSASLFinal: function () { + return new BufferList().addInt32(12).addString('data').join(true, 'R') + }, + + parameterStatus: function (name: string, value: string) { + return new BufferList().addCString(name).addCString(value).join(true, 'S') + }, + + backendKeyData: function (processID: number, secretKey: number) { + return new BufferList().addInt32(processID).addInt32(secretKey).join(true, 'K') + }, + + commandComplete: function (string: string) { + return new BufferList().addCString(string).join(true, 'C') + }, + + rowDescription: function (fields: any[]) { + fields = fields || [] + const buf = new BufferList() + buf.addInt16(fields.length) + fields.forEach(function (field) { + buf + .addCString(field.name) + .addInt32(field.tableID || 0) + .addInt16(field.attributeNumber || 0) + .addInt32(field.dataTypeID || 0) + .addInt16(field.dataTypeSize || 0) + .addInt32(field.typeModifier || 0) + .addInt16(field.formatCode || 0) + }) + return buf.join(true, 'T') + }, + + parameterDescription: function (dataTypeIDs: number[]) { + dataTypeIDs = dataTypeIDs || [] + const buf = new BufferList() + buf.addInt16(dataTypeIDs.length) + dataTypeIDs.forEach(function (dataTypeID) { + buf.addInt32(dataTypeID) + }) + return buf.join(true, 't') + }, + + dataRow: function (columns: any[]) { + columns = columns || [] + const buf = new BufferList() + buf.addInt16(columns.length) + columns.forEach(function (col) { + if (col == null) { + buf.addInt32(-1) + } else { + const strBuf = Buffer.from(col, 'utf8') + buf.addInt32(strBuf.length) + buf.add(strBuf) + } + }) + return buf.join(true, 'D') + }, + + error: function (fields: any) { + return buffers.errorOrNotice(fields).join(true, 'E') + }, + + notice: function (fields: any) { + return buffers.errorOrNotice(fields).join(true, 'N') + }, + + errorOrNotice: function (fields: any) { + fields = fields || [] + const buf = new BufferList() + fields.forEach(function (field: any) { + buf.addChar(field.type) + buf.addCString(field.value) + }) + return buf.add(Buffer.from([0])) // terminator + }, + + parseComplete: function () { + return new BufferList().join(true, '1') + }, + + bindComplete: function () { + return new BufferList().join(true, '2') + }, + + notification: function (id: number, channel: string, payload: string) { + return new BufferList().addInt32(id).addCString(channel).addCString(payload).join(true, 'A') + }, + + emptyQuery: function () { + return new BufferList().join(true, 'I') + }, + + portalSuspended: function () { + return new BufferList().join(true, 's') + }, + + closeComplete: function () { + return new BufferList().join(true, '3') + }, + + copyIn: function (cols: number) { + const list = new BufferList() + // text mode + .addByte(0) + // column count + .addInt16(cols) + for (let i = 0; i < cols; i++) { + list.addInt16(i) + } + return list.join(true, 'G') + }, + + copyOut: function (cols: number) { + const list = new BufferList() + // text mode + .addByte(0) + // column count + .addInt16(cols) + for (let i = 0; i < cols; i++) { + list.addInt16(i) + } + return list.join(true, 'H') + }, + + copyData: function (bytes: Buffer) { + return new BufferList().add(bytes).join(true, 'd') + }, + + copyDone: function () { + return new BufferList().join(true, 'c') + }, +} + +export default buffers diff --git a/node_modules/pg-protocol/src/types/chunky.d.ts b/node_modules/pg-protocol/src/types/chunky.d.ts new file mode 100644 index 0000000..7389bda --- /dev/null +++ b/node_modules/pg-protocol/src/types/chunky.d.ts @@ -0,0 +1 @@ +declare module 'chunky' diff --git a/node_modules/pg-types/.travis.yml b/node_modules/pg-types/.travis.yml new file mode 100644 index 0000000..dd6b033 --- /dev/null +++ b/node_modules/pg-types/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - '4' + - 'lts/*' + - 'node' +env: + - PGUSER=postgres diff --git a/node_modules/pg-types/Makefile b/node_modules/pg-types/Makefile new file mode 100644 index 0000000..d7ec83d --- /dev/null +++ b/node_modules/pg-types/Makefile @@ -0,0 +1,14 @@ +.PHONY: publish-patch test + +test: + npm test + +patch: test + npm version patch -m "Bump version" + git push origin master --tags + npm publish + +minor: test + npm version minor -m "Bump version" + git push origin master --tags + npm publish diff --git a/node_modules/pg-types/README.md b/node_modules/pg-types/README.md new file mode 100644 index 0000000..54a3f2c --- /dev/null +++ b/node_modules/pg-types/README.md @@ -0,0 +1,75 @@ +# pg-types + +This is the code that turns all the raw text from postgres into JavaScript types for [node-postgres](https://github.com/brianc/node-postgres.git) + +## use + +This module is consumed and exported from the root `pg` object of node-postgres. To access it, do the following: + +```js +var types = require('pg').types +``` + +Generally what you'll want to do is override how a specific data-type is parsed and turned into a JavaScript type. By default the PostgreSQL backend server returns everything as strings. Every data type corresponds to a unique `OID` within the server, and these `OIDs` are sent back with the query response. So, you need to match a particluar `OID` to a function you'd like to use to take the raw text input and produce a valid JavaScript object as a result. `null` values are never parsed. + +Let's do something I commonly like to do on projects: return 64-bit integers `(int8)` as JavaScript integers. Because JavaScript doesn't have support for 64-bit integers node-postgres cannot confidently parse `int8` data type results as numbers because if you have a _huge_ number it will overflow and the result you'd get back from node-postgres would not be the result in the datbase. That would be a __very bad thing__ so node-postgres just returns `int8` results as strings and leaves the parsing up to you. Let's say that you know you don't and wont ever have numbers greater than `int4` in your database, but you're tired of recieving results from the `COUNT(*)` function as strings (because that function returns `int8`). You would do this: + +```js +var types = require('pg').types +types.setTypeParser(20, function(val) { + return parseInt(val) +}) +``` + +__boom__: now you get numbers instead of strings. + +Just as another example -- not saying this is a good idea -- let's say you want to return all dates from your database as [moment](http://momentjs.com/docs/) objects. Okay, do this: + +```js +var types = require('pg').types +var moment = require('moment') +var parseFn = function(val) { + return val === null ? null : moment(val) +} +types.setTypeParser(types.builtins.TIMESTAMPTZ, parseFn) +types.setTypeParser(types.builtins.TIMESTAMP, parseFn) +``` +_note: I've never done that with my dates, and I'm not 100% sure moment can parse all the date strings returned from postgres. It's just an example!_ + +If you're thinking "gee, this seems pretty handy, but how can I get a list of all the OIDs in the database and what they correspond to?!?!?!" worry not: + +```bash +$ psql -c "select typname, oid, typarray from pg_type order by oid" +``` + +If you want to find out the OID of a specific type: + +```bash +$ psql -c "select typname, oid, typarray from pg_type where typname = 'daterange' order by oid" +``` + +:smile: + +## license + +The MIT License (MIT) + +Copyright (c) 2014 Brian M. Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/pg-types/index.d.ts b/node_modules/pg-types/index.d.ts new file mode 100644 index 0000000..4bebcbe --- /dev/null +++ b/node_modules/pg-types/index.d.ts @@ -0,0 +1,137 @@ +export enum TypeId { + BOOL = 16, + BYTEA = 17, + CHAR = 18, + INT8 = 20, + INT2 = 21, + INT4 = 23, + REGPROC = 24, + TEXT = 25, + OID = 26, + TID = 27, + XID = 28, + CID = 29, + JSON = 114, + XML = 142, + PG_NODE_TREE = 194, + SMGR = 210, + PATH = 602, + POLYGON = 604, + CIDR = 650, + FLOAT4 = 700, + FLOAT8 = 701, + ABSTIME = 702, + RELTIME = 703, + TINTERVAL = 704, + CIRCLE = 718, + MACADDR8 = 774, + MONEY = 790, + MACADDR = 829, + INET = 869, + ACLITEM = 1033, + BPCHAR = 1042, + VARCHAR = 1043, + DATE = 1082, + TIME = 1083, + TIMESTAMP = 1114, + TIMESTAMPTZ = 1184, + INTERVAL = 1186, + TIMETZ = 1266, + BIT = 1560, + VARBIT = 1562, + NUMERIC = 1700, + REFCURSOR = 1790, + REGPROCEDURE = 2202, + REGOPER = 2203, + REGOPERATOR = 2204, + REGCLASS = 2205, + REGTYPE = 2206, + UUID = 2950, + TXID_SNAPSHOT = 2970, + PG_LSN = 3220, + PG_NDISTINCT = 3361, + PG_DEPENDENCIES = 3402, + TSVECTOR = 3614, + TSQUERY = 3615, + GTSVECTOR = 3642, + REGCONFIG = 3734, + REGDICTIONARY = 3769, + JSONB = 3802, + REGNAMESPACE = 4089, + REGROLE = 4096 +} + +export type builtinsTypes = + 'BOOL' | + 'BYTEA' | + 'CHAR' | + 'INT8' | + 'INT2' | + 'INT4' | + 'REGPROC' | + 'TEXT' | + 'OID' | + 'TID' | + 'XID' | + 'CID' | + 'JSON' | + 'XML' | + 'PG_NODE_TREE' | + 'SMGR' | + 'PATH' | + 'POLYGON' | + 'CIDR' | + 'FLOAT4' | + 'FLOAT8' | + 'ABSTIME' | + 'RELTIME' | + 'TINTERVAL' | + 'CIRCLE' | + 'MACADDR8' | + 'MONEY' | + 'MACADDR' | + 'INET' | + 'ACLITEM' | + 'BPCHAR' | + 'VARCHAR' | + 'DATE' | + 'TIME' | + 'TIMESTAMP' | + 'TIMESTAMPTZ' | + 'INTERVAL' | + 'TIMETZ' | + 'BIT' | + 'VARBIT' | + 'NUMERIC' | + 'REFCURSOR' | + 'REGPROCEDURE' | + 'REGOPER' | + 'REGOPERATOR' | + 'REGCLASS' | + 'REGTYPE' | + 'UUID' | + 'TXID_SNAPSHOT' | + 'PG_LSN' | + 'PG_NDISTINCT' | + 'PG_DEPENDENCIES' | + 'TSVECTOR' | + 'TSQUERY' | + 'GTSVECTOR' | + 'REGCONFIG' | + 'REGDICTIONARY' | + 'JSONB' | + 'REGNAMESPACE' | + 'REGROLE'; + +export type TypesBuiltins = {[key in builtinsTypes]: TypeId}; + +export type TypeFormat = 'text' | 'binary'; + +export const builtins: TypesBuiltins; + +export function setTypeParser (id: TypeId, parseFn: ((value: string) => any)): void; +export function setTypeParser (id: TypeId, format: TypeFormat, parseFn: (value: string) => any): void; + +export const getTypeParser: (id: TypeId, format?: TypeFormat) => any + +export const arrayParser: (source: string, transform: (entry: any) => any) => any[]; diff --git a/node_modules/pg-types/index.js b/node_modules/pg-types/index.js new file mode 100644 index 0000000..952d8c2 --- /dev/null +++ b/node_modules/pg-types/index.js @@ -0,0 +1,47 @@ +var textParsers = require('./lib/textParsers'); +var binaryParsers = require('./lib/binaryParsers'); +var arrayParser = require('./lib/arrayParser'); +var builtinTypes = require('./lib/builtins'); + +exports.getTypeParser = getTypeParser; +exports.setTypeParser = setTypeParser; +exports.arrayParser = arrayParser; +exports.builtins = builtinTypes; + +var typeParsers = { + text: {}, + binary: {} +}; + +//the empty parse function +function noParse (val) { + return String(val); +}; + +//returns a function used to convert a specific type (specified by +//oid) into a result javascript type +//note: the oid can be obtained via the following sql query: +//SELECT oid FROM pg_type WHERE typname = 'TYPE_NAME_HERE'; +function getTypeParser (oid, format) { + format = format || 'text'; + if (!typeParsers[format]) { + return noParse; + } + return typeParsers[format][oid] || noParse; +}; + +function setTypeParser (oid, format, parseFn) { + if(typeof format == 'function') { + parseFn = format; + format = 'text'; + } + typeParsers[format][oid] = parseFn; +}; + +textParsers.init(function(oid, converter) { + typeParsers.text[oid] = converter; +}); + +binaryParsers.init(function(oid, converter) { + typeParsers.binary[oid] = converter; +}); diff --git a/node_modules/pg-types/index.test-d.ts b/node_modules/pg-types/index.test-d.ts new file mode 100644 index 0000000..d530e6e --- /dev/null +++ b/node_modules/pg-types/index.test-d.ts @@ -0,0 +1,21 @@ +import * as types from '.'; +import { expectType } from 'tsd'; + +// builtins +expectType(types.builtins); + +// getTypeParser +const noParse = types.getTypeParser(types.builtins.NUMERIC, 'text'); +const numericParser = types.getTypeParser(types.builtins.NUMERIC, 'binary'); +expectType(noParse('noParse')); +expectType(numericParser([200, 1, 0, 15])); + +// getArrayParser +const value = types.arrayParser('{1,2,3}', (num) => parseInt(num)); +expectType(value); + +//setTypeParser +types.setTypeParser(types.builtins.INT8, parseInt); +types.setTypeParser(types.builtins.FLOAT8, parseFloat); +types.setTypeParser(types.builtins.FLOAT8, 'binary', (data) => data[0]); +types.setTypeParser(types.builtins.FLOAT8, 'text', parseFloat); diff --git a/node_modules/pg-types/lib/arrayParser.js b/node_modules/pg-types/lib/arrayParser.js new file mode 100644 index 0000000..81ccffb --- /dev/null +++ b/node_modules/pg-types/lib/arrayParser.js @@ -0,0 +1,11 @@ +var array = require('postgres-array'); + +module.exports = { + create: function (source, transform) { + return { + parse: function() { + return array.parse(source, transform); + } + }; + } +}; diff --git a/node_modules/pg-types/lib/binaryParsers.js b/node_modules/pg-types/lib/binaryParsers.js new file mode 100644 index 0000000..e12c2f4 --- /dev/null +++ b/node_modules/pg-types/lib/binaryParsers.js @@ -0,0 +1,257 @@ +var parseInt64 = require('pg-int8'); + +var parseBits = function(data, bits, offset, invert, callback) { + offset = offset || 0; + invert = invert || false; + callback = callback || function(lastValue, newValue, bits) { return (lastValue * Math.pow(2, bits)) + newValue; }; + var offsetBytes = offset >> 3; + + var inv = function(value) { + if (invert) { + return ~value & 0xff; + } + + return value; + }; + + // read first (maybe partial) byte + var mask = 0xff; + var firstBits = 8 - (offset % 8); + if (bits < firstBits) { + mask = (0xff << (8 - bits)) & 0xff; + firstBits = bits; + } + + if (offset) { + mask = mask >> (offset % 8); + } + + var result = 0; + if ((offset % 8) + bits >= 8) { + result = callback(0, inv(data[offsetBytes]) & mask, firstBits); + } + + // read bytes + var bytes = (bits + offset) >> 3; + for (var i = offsetBytes + 1; i < bytes; i++) { + result = callback(result, inv(data[i]), 8); + } + + // bits to read, that are not a complete byte + var lastBits = (bits + offset) % 8; + if (lastBits > 0) { + result = callback(result, inv(data[bytes]) >> (8 - lastBits), lastBits); + } + + return result; +}; + +var parseFloatFromBits = function(data, precisionBits, exponentBits) { + var bias = Math.pow(2, exponentBits - 1) - 1; + var sign = parseBits(data, 1); + var exponent = parseBits(data, exponentBits, 1); + + if (exponent === 0) { + return 0; + } + + // parse mantissa + var precisionBitsCounter = 1; + var parsePrecisionBits = function(lastValue, newValue, bits) { + if (lastValue === 0) { + lastValue = 1; + } + + for (var i = 1; i <= bits; i++) { + precisionBitsCounter /= 2; + if ((newValue & (0x1 << (bits - i))) > 0) { + lastValue += precisionBitsCounter; + } + } + + return lastValue; + }; + + var mantissa = parseBits(data, precisionBits, exponentBits + 1, false, parsePrecisionBits); + + // special cases + if (exponent == (Math.pow(2, exponentBits + 1) - 1)) { + if (mantissa === 0) { + return (sign === 0) ? Infinity : -Infinity; + } + + return NaN; + } + + // normale number + return ((sign === 0) ? 1 : -1) * Math.pow(2, exponent - bias) * mantissa; +}; + +var parseInt16 = function(value) { + if (parseBits(value, 1) == 1) { + return -1 * (parseBits(value, 15, 1, true) + 1); + } + + return parseBits(value, 15, 1); +}; + +var parseInt32 = function(value) { + if (parseBits(value, 1) == 1) { + return -1 * (parseBits(value, 31, 1, true) + 1); + } + + return parseBits(value, 31, 1); +}; + +var parseFloat32 = function(value) { + return parseFloatFromBits(value, 23, 8); +}; + +var parseFloat64 = function(value) { + return parseFloatFromBits(value, 52, 11); +}; + +var parseNumeric = function(value) { + var sign = parseBits(value, 16, 32); + if (sign == 0xc000) { + return NaN; + } + + var weight = Math.pow(10000, parseBits(value, 16, 16)); + var result = 0; + + var digits = []; + var ndigits = parseBits(value, 16); + for (var i = 0; i < ndigits; i++) { + result += parseBits(value, 16, 64 + (16 * i)) * weight; + weight /= 10000; + } + + var scale = Math.pow(10, parseBits(value, 16, 48)); + return ((sign === 0) ? 1 : -1) * Math.round(result * scale) / scale; +}; + +var parseDate = function(isUTC, value) { + var sign = parseBits(value, 1); + var rawValue = parseBits(value, 63, 1); + + // discard usecs and shift from 2000 to 1970 + var result = new Date((((sign === 0) ? 1 : -1) * rawValue / 1000) + 946684800000); + + if (!isUTC) { + result.setTime(result.getTime() + result.getTimezoneOffset() * 60000); + } + + // add microseconds to the date + result.usec = rawValue % 1000; + result.getMicroSeconds = function() { + return this.usec; + }; + result.setMicroSeconds = function(value) { + this.usec = value; + }; + result.getUTCMicroSeconds = function() { + return this.usec; + }; + + return result; +}; + +var parseArray = function(value) { + var dim = parseBits(value, 32); + + var flags = parseBits(value, 32, 32); + var elementType = parseBits(value, 32, 64); + + var offset = 96; + var dims = []; + for (var i = 0; i < dim; i++) { + // parse dimension + dims[i] = parseBits(value, 32, offset); + offset += 32; + + // ignore lower bounds + offset += 32; + } + + var parseElement = function(elementType) { + // parse content length + var length = parseBits(value, 32, offset); + offset += 32; + + // parse null values + if (length == 0xffffffff) { + return null; + } + + var result; + if ((elementType == 0x17) || (elementType == 0x14)) { + // int/bigint + result = parseBits(value, length * 8, offset); + offset += length * 8; + return result; + } + else if (elementType == 0x19) { + // string + result = value.toString(this.encoding, offset >> 3, (offset += (length << 3)) >> 3); + return result; + } + else { + console.log("ERROR: ElementType not implemented: " + elementType); + } + }; + + var parse = function(dimension, elementType) { + var array = []; + var i; + + if (dimension.length > 1) { + var count = dimension.shift(); + for (i = 0; i < count; i++) { + array[i] = parse(dimension, elementType); + } + dimension.unshift(count); + } + else { + for (i = 0; i < dimension[0]; i++) { + array[i] = parseElement(elementType); + } + } + + return array; + }; + + return parse(dims, elementType); +}; + +var parseText = function(value) { + return value.toString('utf8'); +}; + +var parseBool = function(value) { + if(value === null) return null; + return (parseBits(value, 8) > 0); +}; + +var init = function(register) { + register(20, parseInt64); + register(21, parseInt16); + register(23, parseInt32); + register(26, parseInt32); + register(1700, parseNumeric); + register(700, parseFloat32); + register(701, parseFloat64); + register(16, parseBool); + register(1114, parseDate.bind(null, false)); + register(1184, parseDate.bind(null, true)); + register(1000, parseArray); + register(1007, parseArray); + register(1016, parseArray); + register(1008, parseArray); + register(1009, parseArray); + register(25, parseText); +}; + +module.exports = { + init: init +}; diff --git a/node_modules/pg-types/lib/builtins.js b/node_modules/pg-types/lib/builtins.js new file mode 100644 index 0000000..f0c134a --- /dev/null +++ b/node_modules/pg-types/lib/builtins.js @@ -0,0 +1,73 @@ +/** + * Following query was used to generate this file: + + SELECT json_object_agg(UPPER(PT.typname), PT.oid::int4 ORDER BY pt.oid) + FROM pg_type PT + WHERE typnamespace = (SELECT pgn.oid FROM pg_namespace pgn WHERE nspname = 'pg_catalog') -- Take only builting Postgres types with stable OID (extension types are not guaranted to be stable) + AND typtype = 'b' -- Only basic types + AND typelem = 0 -- Ignore aliases + AND typisdefined -- Ignore undefined types + */ + +module.exports = { + BOOL: 16, + BYTEA: 17, + CHAR: 18, + INT8: 20, + INT2: 21, + INT4: 23, + REGPROC: 24, + TEXT: 25, + OID: 26, + TID: 27, + XID: 28, + CID: 29, + JSON: 114, + XML: 142, + PG_NODE_TREE: 194, + SMGR: 210, + PATH: 602, + POLYGON: 604, + CIDR: 650, + FLOAT4: 700, + FLOAT8: 701, + ABSTIME: 702, + RELTIME: 703, + TINTERVAL: 704, + CIRCLE: 718, + MACADDR8: 774, + MONEY: 790, + MACADDR: 829, + INET: 869, + ACLITEM: 1033, + BPCHAR: 1042, + VARCHAR: 1043, + DATE: 1082, + TIME: 1083, + TIMESTAMP: 1114, + TIMESTAMPTZ: 1184, + INTERVAL: 1186, + TIMETZ: 1266, + BIT: 1560, + VARBIT: 1562, + NUMERIC: 1700, + REFCURSOR: 1790, + REGPROCEDURE: 2202, + REGOPER: 2203, + REGOPERATOR: 2204, + REGCLASS: 2205, + REGTYPE: 2206, + UUID: 2950, + TXID_SNAPSHOT: 2970, + PG_LSN: 3220, + PG_NDISTINCT: 3361, + PG_DEPENDENCIES: 3402, + TSVECTOR: 3614, + TSQUERY: 3615, + GTSVECTOR: 3642, + REGCONFIG: 3734, + REGDICTIONARY: 3769, + JSONB: 3802, + REGNAMESPACE: 4089, + REGROLE: 4096 +}; diff --git a/node_modules/pg-types/lib/textParsers.js b/node_modules/pg-types/lib/textParsers.js new file mode 100644 index 0000000..b1218bf --- /dev/null +++ b/node_modules/pg-types/lib/textParsers.js @@ -0,0 +1,215 @@ +var array = require('postgres-array') +var arrayParser = require('./arrayParser'); +var parseDate = require('postgres-date'); +var parseInterval = require('postgres-interval'); +var parseByteA = require('postgres-bytea'); + +function allowNull (fn) { + return function nullAllowed (value) { + if (value === null) return value + return fn(value) + } +} + +function parseBool (value) { + if (value === null) return value + return value === 'TRUE' || + value === 't' || + value === 'true' || + value === 'y' || + value === 'yes' || + value === 'on' || + value === '1'; +} + +function parseBoolArray (value) { + if (!value) return null + return array.parse(value, parseBool) +} + +function parseBaseTenInt (string) { + return parseInt(string, 10) +} + +function parseIntegerArray (value) { + if (!value) return null + return array.parse(value, allowNull(parseBaseTenInt)) +} + +function parseBigIntegerArray (value) { + if (!value) return null + return array.parse(value, allowNull(function (entry) { + return parseBigInteger(entry).trim() + })) +} + +var parsePointArray = function(value) { + if(!value) { return null; } + var p = arrayParser.create(value, function(entry) { + if(entry !== null) { + entry = parsePoint(entry); + } + return entry; + }); + + return p.parse(); +}; + +var parseFloatArray = function(value) { + if(!value) { return null; } + var p = arrayParser.create(value, function(entry) { + if(entry !== null) { + entry = parseFloat(entry); + } + return entry; + }); + + return p.parse(); +}; + +var parseStringArray = function(value) { + if(!value) { return null; } + + var p = arrayParser.create(value); + return p.parse(); +}; + +var parseDateArray = function(value) { + if (!value) { return null; } + + var p = arrayParser.create(value, function(entry) { + if (entry !== null) { + entry = parseDate(entry); + } + return entry; + }); + + return p.parse(); +}; + +var parseIntervalArray = function(value) { + if (!value) { return null; } + + var p = arrayParser.create(value, function(entry) { + if (entry !== null) { + entry = parseInterval(entry); + } + return entry; + }); + + return p.parse(); +}; + +var parseByteAArray = function(value) { + if (!value) { return null; } + + return array.parse(value, allowNull(parseByteA)); +}; + +var parseInteger = function(value) { + return parseInt(value, 10); +}; + +var parseBigInteger = function(value) { + var valStr = String(value); + if (/^\d+$/.test(valStr)) { return valStr; } + return value; +}; + +var parseJsonArray = function(value) { + if (!value) { return null; } + + return array.parse(value, allowNull(JSON.parse)); +}; + +var parsePoint = function(value) { + if (value[0] !== '(') { return null; } + + value = value.substring( 1, value.length - 1 ).split(','); + + return { + x: parseFloat(value[0]) + , y: parseFloat(value[1]) + }; +}; + +var parseCircle = function(value) { + if (value[0] !== '<' && value[1] !== '(') { return null; } + + var point = '('; + var radius = ''; + var pointParsed = false; + for (var i = 2; i < value.length - 1; i++){ + if (!pointParsed) { + point += value[i]; + } + + if (value[i] === ')') { + pointParsed = true; + continue; + } else if (!pointParsed) { + continue; + } + + if (value[i] === ','){ + continue; + } + + radius += value[i]; + } + var result = parsePoint(point); + result.radius = parseFloat(radius); + + return result; +}; + +var init = function(register) { + register(20, parseBigInteger); // int8 + register(21, parseInteger); // int2 + register(23, parseInteger); // int4 + register(26, parseInteger); // oid + register(700, parseFloat); // float4/real + register(701, parseFloat); // float8/double + register(16, parseBool); + register(1082, parseDate); // date + register(1114, parseDate); // timestamp without timezone + register(1184, parseDate); // timestamp + register(600, parsePoint); // point + register(651, parseStringArray); // cidr[] + register(718, parseCircle); // circle + register(1000, parseBoolArray); + register(1001, parseByteAArray); + register(1005, parseIntegerArray); // _int2 + register(1007, parseIntegerArray); // _int4 + register(1028, parseIntegerArray); // oid[] + register(1016, parseBigIntegerArray); // _int8 + register(1017, parsePointArray); // point[] + register(1021, parseFloatArray); // _float4 + register(1022, parseFloatArray); // _float8 + register(1231, parseFloatArray); // _numeric + register(1014, parseStringArray); //char + register(1015, parseStringArray); //varchar + register(1008, parseStringArray); + register(1009, parseStringArray); + register(1040, parseStringArray); // macaddr[] + register(1041, parseStringArray); // inet[] + register(1115, parseDateArray); // timestamp without time zone[] + register(1182, parseDateArray); // _date + register(1185, parseDateArray); // timestamp with time zone[] + register(1186, parseInterval); + register(1187, parseIntervalArray); + register(17, parseByteA); + register(114, JSON.parse.bind(JSON)); // json + register(3802, JSON.parse.bind(JSON)); // jsonb + register(199, parseJsonArray); // json[] + register(3807, parseJsonArray); // jsonb[] + register(3907, parseStringArray); // numrange[] + register(2951, parseStringArray); // uuid[] + register(791, parseStringArray); // money[] + register(1183, parseStringArray); // time[] + register(1270, parseStringArray); // timetz[] +}; + +module.exports = { + init: init +}; diff --git a/node_modules/pg-types/package.json b/node_modules/pg-types/package.json new file mode 100644 index 0000000..5f18026 --- /dev/null +++ b/node_modules/pg-types/package.json @@ -0,0 +1,42 @@ +{ + "name": "pg-types", + "version": "2.2.0", + "description": "Query result type converters for node-postgres", + "main": "index.js", + "scripts": { + "test": "tape test/*.js | tap-spec && npm run test-ts", + "test-ts": "if-node-version '>= 8' tsd" + }, + "repository": { + "type": "git", + "url": "git://github.com/brianc/node-pg-types.git" + }, + "keywords": [ + "postgres", + "PostgreSQL", + "pg" + ], + "author": "Brian M. Carlson", + "license": "MIT", + "bugs": { + "url": "https://github.com/brianc/node-pg-types/issues" + }, + "homepage": "https://github.com/brianc/node-pg-types", + "devDependencies": { + "if-node-version": "^1.1.1", + "pff": "^1.0.0", + "tap-spec": "^4.0.0", + "tape": "^4.0.0", + "tsd": "^0.7.4" + }, + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } +} diff --git a/node_modules/pg-types/test/index.js b/node_modules/pg-types/test/index.js new file mode 100644 index 0000000..b7d05cd --- /dev/null +++ b/node_modules/pg-types/test/index.js @@ -0,0 +1,24 @@ + +var test = require('tape') +var printf = require('pff') +var getTypeParser = require('../').getTypeParser +var types = require('./types') + +test('types', function (t) { + Object.keys(types).forEach(function (typeName) { + var type = types[typeName] + t.test(typeName, function (t) { + var parser = getTypeParser(type.id, type.format) + type.tests.forEach(function (tests) { + var input = tests[0] + var expected = tests[1] + var result = parser(input) + if (typeof expected === 'function') { + return expected(t, result) + } + t.equal(result, expected) + }) + t.end() + }) + }) +}) diff --git a/node_modules/pg-types/test/types.js b/node_modules/pg-types/test/types.js new file mode 100644 index 0000000..af708a5 --- /dev/null +++ b/node_modules/pg-types/test/types.js @@ -0,0 +1,597 @@ +'use strict' + +exports['string/varchar'] = { + format: 'text', + id: 1043, + tests: [ + ['bang', 'bang'] + ] +} + +exports['integer/int4'] = { + format: 'text', + id: 23, + tests: [ + ['2147483647', 2147483647] + ] +} + +exports['smallint/int2'] = { + format: 'text', + id: 21, + tests: [ + ['32767', 32767] + ] +} + +exports['bigint/int8'] = { + format: 'text', + id: 20, + tests: [ + ['9223372036854775807', '9223372036854775807'] + ] +} + +exports.oid = { + format: 'text', + id: 26, + tests: [ + ['103', 103] + ] +} + +var bignum = '31415926535897932384626433832795028841971693993751058.16180339887498948482045868343656381177203091798057628' +exports.numeric = { + format: 'text', + id: 1700, + tests: [ + [bignum, bignum] + ] +} + +exports['real/float4'] = { + format: 'text', + id: 700, + tests: [ + ['123.456', 123.456] + ] +} + +exports['double precision / float 8'] = { + format: 'text', + id: 701, + tests: [ + ['12345678.12345678', 12345678.12345678] + ] +} + +exports.boolean = { + format: 'text', + id: 16, + tests: [ + ['TRUE', true], + ['t', true], + ['true', true], + ['y', true], + ['yes', true], + ['on', true], + ['1', true], + ['f', false], + [null, null] + ] +} + +exports.timestamptz = { + format: 'text', + id: 1184, + tests: [ + [ + '2010-10-31 14:54:13.74-05:30', + dateEquals(2010, 9, 31, 20, 24, 13, 740) + ], + [ + '2011-01-23 22:05:00.68-06', + dateEquals(2011, 0, 24, 4, 5, 0, 680) + ], + [ + '2010-10-30 14:11:12.730838Z', + dateEquals(2010, 9, 30, 14, 11, 12, 730) + ], + [ + '2010-10-30 13:10:01+05', + dateEquals(2010, 9, 30, 8, 10, 1, 0) + ] + ] +} + +exports.timestamp = { + format: 'text', + id: 1114, + tests: [ + [ + '2010-10-31 00:00:00', + function (t, value) { + t.equal( + value.toUTCString(), + new Date(2010, 9, 31, 0, 0, 0, 0, 0).toUTCString() + ) + t.equal( + value.toString(), + new Date(2010, 9, 31, 0, 0, 0, 0, 0, 0).toString() + ) + } + ] + ] +} + +exports.date = { + format: 'text', + id: 1082, + tests: [ + ['2010-10-31', function (t, value) { + var now = new Date(2010, 9, 31) + dateEquals( + 2010, + now.getUTCMonth(), + now.getUTCDate(), + now.getUTCHours(), 0, 0, 0)(t, value) + t.equal(value.getHours(), now.getHours()) + }] + ] +} + +exports.inet = { + format: 'text', + id: 869, + tests: [ + ['8.8.8.8', '8.8.8.8'], + ['2001:4860:4860::8888', '2001:4860:4860::8888'], + ['127.0.0.1', '127.0.0.1'], + ['fd00:1::40e', 'fd00:1::40e'], + ['1.2.3.4', '1.2.3.4'] + ] +} + +exports.cidr = { + format: 'text', + id: 650, + tests: [ + ['172.16.0.0/12', '172.16.0.0/12'], + ['fe80::/10', 'fe80::/10'], + ['fc00::/7', 'fc00::/7'], + ['192.168.0.0/24', '192.168.0.0/24'], + ['10.0.0.0/8', '10.0.0.0/8'] + ] +} + +exports.macaddr = { + format: 'text', + id: 829, + tests: [ + ['08:00:2b:01:02:03', '08:00:2b:01:02:03'], + ['16:10:9f:0d:66:00', '16:10:9f:0d:66:00'] + ] +} + +exports.numrange = { + format: 'text', + id: 3906, + tests: [ + ['[,]', '[,]'], + ['(,)', '(,)'], + ['(,]', '(,]'], + ['[1,)', '[1,)'], + ['[,1]', '[,1]'], + ['(1,2)', '(1,2)'], + ['(1,20.5]', '(1,20.5]'] + ] +} + +exports.interval = { + format: 'text', + id: 1186, + tests: [ + ['01:02:03', function (t, value) { + t.equal(value.toPostgres(), '3 seconds 2 minutes 1 hours') + t.deepEqual(value, {hours: 1, minutes: 2, seconds: 3}) + }], + ['01:02:03.456', function (t, value) { + t.deepEqual(value, {hours: 1, minutes:2, seconds: 3, milliseconds: 456}) + }], + ['1 year -32 days', function (t, value) { + t.equal(value.toPostgres(), '-32 days 1 years') + t.deepEqual(value, {years: 1, days: -32}) + }], + ['1 day -00:00:03', function (t, value) { + t.equal(value.toPostgres(), '-3 seconds 1 days') + t.deepEqual(value, {days: 1, seconds: -3}) + }] + ] +} + +exports.bytea = { + format: 'text', + id: 17, + tests: [ + ['foo\\000\\200\\\\\\377', function (t, value) { + var buffer = new Buffer([102, 111, 111, 0, 128, 92, 255]) + t.ok(buffer.equals(value)) + }], + ['', function (t, value) { + var buffer = new Buffer(0) + t.ok(buffer.equals(value)) + }] + ] +} + +exports['array/boolean'] = { + format: 'text', + id: 1000, + tests: [ + ['{true,false}', function (t, value) { + t.deepEqual(value, [true, false]) + }] + ] +} + +exports['array/char'] = { + format: 'text', + id: 1014, + tests: [ + ['{foo,bar}', function (t, value) { + t.deepEqual(value, ['foo', 'bar']) + }] + ] +} + +exports['array/varchar'] = { + format: 'text', + id: 1015, + tests: [ + ['{foo,bar}', function (t, value) { + t.deepEqual(value, ['foo', 'bar']) + }] + ] +} + +exports['array/text'] = { + format: 'text', + id: 1008, + tests: [ + ['{foo}', function (t, value) { + t.deepEqual(value, ['foo']) + }] + ] +} + +exports['array/bytea'] = { + format: 'text', + id: 1001, + tests: [ + ['{"\\\\x00000000"}', function (t, value) { + var buffer = new Buffer('00000000', 'hex') + t.ok(Array.isArray(value)) + t.equal(value.length, 1) + t.ok(buffer.equals(value[0])) + }], + ['{NULL,"\\\\x4e554c4c"}', function (t, value) { + var buffer = new Buffer('4e554c4c', 'hex') + t.ok(Array.isArray(value)) + t.equal(value.length, 2) + t.equal(value[0], null) + t.ok(buffer.equals(value[1])) + }], + ] +} + +exports['array/numeric'] = { + format: 'text', + id: 1231, + tests: [ + ['{1.2,3.4}', function (t, value) { + t.deepEqual(value, [1.2, 3.4]) + }] + ] +} + +exports['array/int2'] = { + format: 'text', + id: 1005, + tests: [ + ['{-32768, -32767, 32766, 32767}', function (t, value) { + t.deepEqual(value, [-32768, -32767, 32766, 32767]) + }] + ] +} + +exports['array/int4'] = { + format: 'text', + id: 1005, + tests: [ + ['{-2147483648, -2147483647, 2147483646, 2147483647}', function (t, value) { + t.deepEqual(value, [-2147483648, -2147483647, 2147483646, 2147483647]) + }] + ] +} + +exports['array/int8'] = { + format: 'text', + id: 1016, + tests: [ + [ + '{-9223372036854775808, -9223372036854775807, 9223372036854775806, 9223372036854775807}', + function (t, value) { + t.deepEqual(value, [ + '-9223372036854775808', + '-9223372036854775807', + '9223372036854775806', + '9223372036854775807' + ]) + } + ] + ] +} + +exports['array/json'] = { + format: 'text', + id: 199, + tests: [ + [ + '{{1,2},{[3],"[4,5]"},{null,NULL}}', + function (t, value) { + t.deepEqual(value, [ + [1, 2], + [[3], [4, 5]], + [null, null], + ]) + } + ] + ] +} + +exports['array/jsonb'] = { + format: 'text', + id: 3807, + tests: exports['array/json'].tests +} + +exports['array/point'] = { + format: 'text', + id: 1017, + tests: [ + ['{"(25.1,50.5)","(10.1,40)"}', function (t, value) { + t.deepEqual(value, [{x: 25.1, y: 50.5}, {x: 10.1, y: 40}]) + }] + ] +} + +exports['array/oid'] = { + format: 'text', + id: 1028, + tests: [ + ['{25864,25860}', function (t, value) { + t.deepEqual(value, [25864, 25860]) + }] + ] +} + +exports['array/float4'] = { + format: 'text', + id: 1021, + tests: [ + ['{1.2, 3.4}', function (t, value) { + t.deepEqual(value, [1.2, 3.4]) + }] + ] +} + +exports['array/float8'] = { + format: 'text', + id: 1022, + tests: [ + ['{-12345678.1234567, 12345678.12345678}', function (t, value) { + t.deepEqual(value, [-12345678.1234567, 12345678.12345678]) + }] + ] +} + +exports['array/date'] = { + format: 'text', + id: 1182, + tests: [ + ['{2014-01-01,2015-12-31}', function (t, value) { + var expecteds = [new Date(2014, 0, 1), new Date(2015, 11, 31)] + t.equal(value.length, 2) + value.forEach(function (date, index) { + var expected = expecteds[index] + dateEquals( + expected.getUTCFullYear(), + expected.getUTCMonth(), + expected.getUTCDate(), + expected.getUTCHours(), 0, 0, 0)(t, date) + }) + }] + ] +} + +exports['array/interval'] = { + format: 'text', + id: 1187, + tests: [ + ['{01:02:03,1 day -00:00:03}', function (t, value) { + var expecteds = [{hours: 1, minutes: 2, seconds: 3}, + {days: 1, seconds: -3}] + t.equal(value.length, 2) + t.deepEqual(value, expecteds); + }] + ] +} + +exports['array/inet'] = { + format: 'text', + id: 1041, + tests: [ + ['{8.8.8.8}', function (t, value) { + t.deepEqual(value, ['8.8.8.8']); + }], + ['{2001:4860:4860::8888}', function (t, value) { + t.deepEqual(value, ['2001:4860:4860::8888']); + }], + ['{127.0.0.1,fd00:1::40e,1.2.3.4}', function (t, value) { + t.deepEqual(value, ['127.0.0.1', 'fd00:1::40e', '1.2.3.4']); + }] + ] +} + +exports['array/cidr'] = { + format: 'text', + id: 651, + tests: [ + ['{172.16.0.0/12}', function (t, value) { + t.deepEqual(value, ['172.16.0.0/12']); + }], + ['{fe80::/10}', function (t, value) { + t.deepEqual(value, ['fe80::/10']); + }], + ['{10.0.0.0/8,fc00::/7,192.168.0.0/24}', function (t, value) { + t.deepEqual(value, ['10.0.0.0/8', 'fc00::/7', '192.168.0.0/24']); + }] + ] +} + +exports['array/macaddr'] = { + format: 'text', + id: 1040, + tests: [ + ['{08:00:2b:01:02:03,16:10:9f:0d:66:00}', function (t, value) { + t.deepEqual(value, ['08:00:2b:01:02:03', '16:10:9f:0d:66:00']); + }] + ] +} + +exports['array/numrange'] = { + format: 'text', + id: 3907, + tests: [ + ['{"[1,2]","(4.5,8)","[10,40)","(-21.2,60.3]"}', function (t, value) { + t.deepEqual(value, ['[1,2]', '(4.5,8)', '[10,40)', '(-21.2,60.3]']); + }], + ['{"[,20]","[3,]","[,]","(,35)","(1,)","(,)"}', function (t, value) { + t.deepEqual(value, ['[,20]', '[3,]', '[,]', '(,35)', '(1,)', '(,)']); + }], + ['{"[,20)","[3,)","[,)","[,35)","[1,)","[,)"}', function (t, value) { + t.deepEqual(value, ['[,20)', '[3,)', '[,)', '[,35)', '[1,)', '[,)']); + }] + ] +} + +exports['binary-string/varchar'] = { + format: 'binary', + id: 1043, + tests: [ + ['bang', 'bang'] + ] +} + +exports['binary-integer/int4'] = { + format: 'binary', + id: 23, + tests: [ + [[0, 0, 0, 100], 100] + ] +} + +exports['binary-smallint/int2'] = { + format: 'binary', + id: 21, + tests: [ + [[0, 101], 101] + ] +} + +exports['binary-bigint/int8'] = { + format: 'binary', + id: 20, + tests: [ + [new Buffer([0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), '9223372036854775807'] + ] +} + +exports['binary-oid'] = { + format: 'binary', + id: 26, + tests: [ + [[0, 0, 0, 103], 103] + ] +} + +exports['binary-numeric'] = { + format: 'binary', + id: 1700, + tests: [ + [ + [0, 2, 0, 0, 0, 0, 0, hex('0x64'), 0, 12, hex('0xd'), hex('0x48'), 0, 0, 0, 0], + 12.34 + ] + ] +} + +exports['binary-real/float4'] = { + format: 'binary', + id: 700, + tests: [ + [['0x41', '0x48', '0x00', '0x00'].map(hex), 12.5] + ] +} + +exports['binary-boolean'] = { + format: 'binary', + id: 16, + tests: [ + [[1], true], + [[0], false], + [null, null] + ] +} + +exports['binary-string'] = { + format: 'binary', + id: 25, + tests: [ + [ + new Buffer(['0x73', '0x6c', '0x61', '0x64', '0x64', '0x61'].map(hex)), + 'sladda' + ] + ] +} + +exports.point = { + format: 'text', + id: 600, + tests: [ + ['(25.1,50.5)', function (t, value) { + t.deepEqual(value, {x: 25.1, y: 50.5}) + }] + ] +} + +exports.circle = { + format: 'text', + id: 718, + tests: [ + ['<(25,10),5>', function (t, value) { + t.deepEqual(value, {x: 25, y: 10, radius: 5}) + }] + ] +} + +function hex (string) { + return parseInt(string, 16) +} + +function dateEquals () { + var timestamp = Date.UTC.apply(Date, arguments) + return function (t, value) { + t.equal(value.toUTCString(), new Date(timestamp).toUTCString()) + } +} diff --git a/node_modules/pg/LICENSE b/node_modules/pg/LICENSE new file mode 100644 index 0000000..5c14056 --- /dev/null +++ b/node_modules/pg/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010 - 2021 Brian Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/pg/README.md b/node_modules/pg/README.md new file mode 100644 index 0000000..7524237 --- /dev/null +++ b/node_modules/pg/README.md @@ -0,0 +1,95 @@ +# node-postgres + +[![Build Status](https://secure.travis-ci.org/brianc/node-postgres.svg?branch=master)](http://travis-ci.org/brianc/node-postgres) +NPM version +NPM downloads + +Non-blocking PostgreSQL client for Node.js. Pure JavaScript and optional native libpq bindings. + +## Install + +```sh +$ npm install pg +``` + +--- + +## :star: [Documentation](https://node-postgres.com) :star: + +### Features + +- Pure JavaScript client and native libpq bindings share _the same API_ +- Connection pooling +- Extensible JS ↔ PostgreSQL data-type coercion +- Supported PostgreSQL features + - Parameterized queries + - Named statements with query plan caching + - Async notifications with `LISTEN/NOTIFY` + - Bulk import & export with `COPY TO/COPY FROM` + +### Extras + +node-postgres is by design pretty light on abstractions. These are some handy modules we've been using over the years to complete the picture. +The entire list can be found on our [wiki](https://github.com/brianc/node-postgres/wiki/Extras). + +## Support + +node-postgres is free software. If you encounter a bug with the library please open an issue on the [GitHub repo](https://github.com/brianc/node-postgres). If you have questions unanswered by the documentation please open an issue pointing out how the documentation was unclear & I will do my best to make it better! + +When you open an issue please provide: + +- version of Node +- version of Postgres +- smallest possible snippet of code to reproduce the problem + +You can also follow me [@brianc](https://bsky.app/profile/brianc.bsky.social) on bluesky if that's your thing for updates on node-postgres with nearly zero non node-postgres content. My old twitter/x account is no longer used. + +## Sponsorship :two_hearts: + +node-postgres's continued development has been made possible in part by generous financial support from [the community](https://github.com/brianc/node-postgres/blob/master/SPONSORS.md). + +If you or your company are benefiting from node-postgres and would like to help keep the project financially sustainable [please consider supporting](https://github.com/sponsors/brianc) its development. + +### Featured sponsor + +Special thanks to [medplum](https://medplum.com) for their generous and thoughtful support of node-postgres! + +![medplum](https://raw.githubusercontent.com/medplum/medplum-logo/refs/heads/main/medplum-logo.png) + +## Contributing + +**:heart: contributions!** + +I will **happily** accept your pull request if it: + +- **has tests** +- looks reasonable +- does not break backwards compatibility + +If your change involves breaking backwards compatibility please please point that out in the pull request & we can discuss & plan when and how to release it and what type of documentation or communicate it will require. + +## Troubleshooting and FAQ + +The causes and solutions to common errors can be found among the [Frequently Asked Questions (FAQ)](https://github.com/brianc/node-postgres/wiki/FAQ) + +## License + +Copyright (c) 2010-2020 Brian Carlson (brian.m.carlson@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/pg/esm/index.mjs b/node_modules/pg/esm/index.mjs new file mode 100644 index 0000000..587d80c --- /dev/null +++ b/node_modules/pg/esm/index.mjs @@ -0,0 +1,20 @@ +// ESM wrapper for pg +import pg from '../lib/index.js' + +// Re-export all the properties +export const Client = pg.Client +export const Pool = pg.Pool +export const Connection = pg.Connection +export const types = pg.types +export const Query = pg.Query +export const DatabaseError = pg.DatabaseError +export const escapeIdentifier = pg.escapeIdentifier +export const escapeLiteral = pg.escapeLiteral +export const Result = pg.Result +export const TypeOverrides = pg.TypeOverrides + +// Also export the defaults +export const defaults = pg.defaults + +// Re-export the default +export default pg diff --git a/node_modules/pg/lib/client.js b/node_modules/pg/lib/client.js new file mode 100644 index 0000000..d6c5719 --- /dev/null +++ b/node_modules/pg/lib/client.js @@ -0,0 +1,766 @@ +const EventEmitter = require('events').EventEmitter +const utils = require('./utils') +const nodeUtils = require('util') +const sasl = require('./crypto/sasl') +const TypeOverrides = require('./type-overrides') + +const ConnectionParameters = require('./connection-parameters') +const Query = require('./query') +const defaults = require('./defaults') +const Connection = require('./connection') +const crypto = require('./crypto/utils') + +const activeQueryDeprecationNotice = nodeUtils.deprecate( + () => {}, + 'Client.activeQuery is deprecated and will be removed in pg@9.0' +) + +const queryQueueDeprecationNotice = nodeUtils.deprecate( + () => {}, + 'Client.queryQueue is deprecated and will be removed in pg@9.0.' +) + +const pgPassDeprecationNotice = nodeUtils.deprecate( + () => {}, + 'pgpass support is deprecated and will be removed in pg@9.0. ' + + 'You can provide an async function as the password property to the Client/Pool constructor that returns a password instead. Within this function you can call the pgpass module in your own code.' +) + +const byoPromiseDeprecationNotice = nodeUtils.deprecate( + () => {}, + 'Passing a custom Promise implementation to the Client/Pool constructor is deprecated and will be removed in pg@9.0.' +) + +const queryQueueLengthDeprecationNotice = nodeUtils.deprecate( + () => {}, + 'Calling client.query() when the client is already executing a query is deprecated and will be removed in pg@9.0. Use async/await or an external async flow control mechanism instead.' +) + +function coerceNumberOrDefault(value, defaultValue) { + if (typeof value === 'number') { + return Number.isFinite(value) ? value : defaultValue + } + if (typeof value === 'string' && value.trim() !== '') { + const n = Number(value) + return Number.isFinite(n) ? n : defaultValue + } + return defaultValue +} + +class Client extends EventEmitter { + constructor(config) { + super() + + this.connectionParameters = new ConnectionParameters(config) + this.user = this.connectionParameters.user + this.database = this.connectionParameters.database + this.port = this.connectionParameters.port + this.host = this.connectionParameters.host + + // "hiding" the password so it doesn't show up in stack traces + // or if the client is console.logged + Object.defineProperty(this, 'password', { + configurable: true, + enumerable: false, + writable: true, + value: this.connectionParameters.password, + }) + + this.replication = this.connectionParameters.replication + + const c = config || {} + + if (c.Promise) { + byoPromiseDeprecationNotice() + } + this._Promise = c.Promise || global.Promise + this._types = new TypeOverrides(c.types) + this._ending = false + this._ended = false + this._connecting = false + this._connected = false + this._connectionError = false + this._queryable = true + this._activeQuery = null + this._txStatus = null + + this.enableChannelBinding = Boolean(c.enableChannelBinding) // set true to use SCRAM-SHA-256-PLUS when offered + this.scramMaxIterations = coerceNumberOrDefault(c.scramMaxIterations, sasl.DEFAULT_MAX_SCRAM_ITERATIONS) + this.connection = + c.connection || + new Connection({ + stream: c.stream, + ssl: this.connectionParameters.ssl, + keepAlive: c.keepAlive || false, + keepAliveInitialDelayMillis: c.keepAliveInitialDelayMillis || 0, + encoding: this.connectionParameters.client_encoding || 'utf8', + }) + this._queryQueue = [] + this.binary = c.binary || defaults.binary + this.processID = null + this.secretKey = null + this.ssl = this.connectionParameters.ssl || false + // As with Password, make SSL->Key (the private key) non-enumerable. + // It won't show up in stack traces + // or if the client is console.logged + if (this.ssl && this.ssl.key) { + Object.defineProperty(this.ssl, 'key', { + enumerable: false, + }) + } + + this._connectionTimeoutMillis = c.connectionTimeoutMillis || 0 + } + + get activeQuery() { + activeQueryDeprecationNotice() + return this._activeQuery + } + + set activeQuery(val) { + activeQueryDeprecationNotice() + this._activeQuery = val + } + + _getActiveQuery() { + return this._activeQuery + } + + _errorAllQueries(err) { + const enqueueError = (query) => { + process.nextTick(() => { + query.handleError(err, this.connection) + }) + } + + const activeQuery = this._getActiveQuery() + if (activeQuery) { + enqueueError(activeQuery) + this._activeQuery = null + } + + this._queryQueue.forEach(enqueueError) + this._queryQueue.length = 0 + } + + _connect(callback) { + const self = this + const con = this.connection + this._connectionCallback = callback + + if (this._connecting || this._connected) { + const err = new Error('Client has already been connected. You cannot reuse a client.') + process.nextTick(() => { + callback(err) + }) + return + } + this._connecting = true + + if (this._connectionTimeoutMillis > 0) { + this.connectionTimeoutHandle = setTimeout(() => { + con._ending = true + con.stream.destroy(new Error('timeout expired')) + }, this._connectionTimeoutMillis) + + if (this.connectionTimeoutHandle.unref) { + this.connectionTimeoutHandle.unref() + } + } + + if (this.host && this.host.indexOf('/') === 0) { + con.connect(this.host + '/.s.PGSQL.' + this.port) + } else { + con.connect(this.port, this.host) + } + + // once connection is established send startup message + con.on('connect', function () { + if (self.ssl) { + con.requestSsl() + } else { + con.startup(self.getStartupConf()) + } + }) + + con.on('sslconnect', function () { + con.startup(self.getStartupConf()) + }) + + this._attachListeners(con) + + con.once('end', () => { + const error = this._ending ? new Error('Connection terminated') : new Error('Connection terminated unexpectedly') + + clearTimeout(this.connectionTimeoutHandle) + this._errorAllQueries(error) + this._ended = true + + if (!this._ending) { + // if the connection is ended without us calling .end() + // on this client then we have an unexpected disconnection + // treat this as an error unless we've already emitted an error + // during connection. + if (this._connecting && !this._connectionError) { + if (this._connectionCallback) { + this._connectionCallback(error) + } else { + this._handleErrorEvent(error) + } + } else if (!this._connectionError) { + this._handleErrorEvent(error) + } + } + + process.nextTick(() => { + this.emit('end') + }) + }) + } + + connect(callback) { + if (callback) { + this._connect(callback) + return + } + + return new this._Promise((resolve, reject) => { + this._connect((error) => { + if (error) { + reject(error) + } else { + resolve(this) + } + }) + }) + } + + _attachListeners(con) { + // password request handling + con.on('authenticationCleartextPassword', this._handleAuthCleartextPassword.bind(this)) + // password request handling + con.on('authenticationMD5Password', this._handleAuthMD5Password.bind(this)) + // password request handling (SASL) + con.on('authenticationSASL', this._handleAuthSASL.bind(this)) + con.on('authenticationSASLContinue', this._handleAuthSASLContinue.bind(this)) + con.on('authenticationSASLFinal', this._handleAuthSASLFinal.bind(this)) + con.on('backendKeyData', this._handleBackendKeyData.bind(this)) + con.on('error', this._handleErrorEvent.bind(this)) + con.on('errorMessage', this._handleErrorMessage.bind(this)) + con.on('readyForQuery', this._handleReadyForQuery.bind(this)) + con.on('notice', this._handleNotice.bind(this)) + con.on('rowDescription', this._handleRowDescription.bind(this)) + con.on('dataRow', this._handleDataRow.bind(this)) + con.on('portalSuspended', this._handlePortalSuspended.bind(this)) + con.on('emptyQuery', this._handleEmptyQuery.bind(this)) + con.on('commandComplete', this._handleCommandComplete.bind(this)) + con.on('parseComplete', this._handleParseComplete.bind(this)) + con.on('copyInResponse', this._handleCopyInResponse.bind(this)) + con.on('copyData', this._handleCopyData.bind(this)) + con.on('notification', this._handleNotification.bind(this)) + } + + _getPassword(cb) { + const con = this.connection + if (typeof this.password === 'function') { + this._Promise + .resolve() + .then(() => this.password(this.connectionParameters)) + .then((pass) => { + if (pass !== undefined) { + if (typeof pass !== 'string') { + con.emit('error', new TypeError('Password must be a string')) + return + } + this.connectionParameters.password = this.password = pass + } else { + this.connectionParameters.password = this.password = null + } + cb() + }) + .catch((err) => { + con.emit('error', err) + }) + } else if (this.password !== null) { + cb() + } else { + try { + const pgPass = require('pgpass') + pgPass(this.connectionParameters, (pass) => { + if (undefined !== pass) { + pgPassDeprecationNotice() + this.connectionParameters.password = this.password = pass + } + cb() + }) + } catch (e) { + this.emit('error', e) + } + } + } + + _handleAuthCleartextPassword(msg) { + this._getPassword(() => { + this.connection.password(this.password) + }) + } + + _handleAuthMD5Password(msg) { + this._getPassword(async () => { + try { + const hashedPassword = await crypto.postgresMd5PasswordHash(this.user, this.password, msg.salt) + this.connection.password(hashedPassword) + } catch (e) { + this.emit('error', e) + } + }) + } + + _handleAuthSASL(msg) { + this._getPassword(() => { + try { + this.saslSession = sasl.startSession( + msg.mechanisms, + this.enableChannelBinding && this.connection.stream, + this.scramMaxIterations + ) + this.connection.sendSASLInitialResponseMessage(this.saslSession.mechanism, this.saslSession.response) + } catch (err) { + this.connection.emit('error', err) + } + }) + } + + async _handleAuthSASLContinue(msg) { + try { + await sasl.continueSession( + this.saslSession, + this.password, + msg.data, + this.enableChannelBinding && this.connection.stream + ) + this.connection.sendSCRAMClientFinalMessage(this.saslSession.response) + } catch (err) { + this.connection.emit('error', err) + } + } + + _handleAuthSASLFinal(msg) { + try { + sasl.finalizeSession(this.saslSession, msg.data) + this.saslSession = null + } catch (err) { + this.connection.emit('error', err) + } + } + + _handleBackendKeyData(msg) { + this.processID = msg.processID + this.secretKey = msg.secretKey + } + + _handleReadyForQuery(msg) { + if (this._connecting) { + this._connecting = false + this._connected = true + clearTimeout(this.connectionTimeoutHandle) + + // process possible callback argument to Client#connect + if (this._connectionCallback) { + this._connectionCallback(null, this) + // remove callback for proper error handling + // after the connect event + this._connectionCallback = null + } + this.emit('connect') + } + const activeQuery = this._getActiveQuery() + this._activeQuery = null + this._txStatus = msg?.status ?? null + this.readyForQuery = true + if (activeQuery) { + activeQuery.handleReadyForQuery(this.connection) + } + this._pulseQueryQueue() + } + + // if we receive an error event or error message + // during the connection process we handle it here + _handleErrorWhileConnecting(err) { + if (this._connectionError) { + // TODO(bmc): this is swallowing errors - we shouldn't do this + return + } + this._connectionError = true + clearTimeout(this.connectionTimeoutHandle) + if (this._connectionCallback) { + return this._connectionCallback(err) + } + this.emit('error', err) + } + + // if we're connected and we receive an error event from the connection + // this means the socket is dead - do a hard abort of all queries and emit + // the socket error on the client as well + _handleErrorEvent(err) { + if (this._connecting) { + return this._handleErrorWhileConnecting(err) + } + this._queryable = false + this._errorAllQueries(err) + this.emit('error', err) + } + + // handle error messages from the postgres backend + _handleErrorMessage(msg) { + if (this._connecting) { + return this._handleErrorWhileConnecting(msg) + } + const activeQuery = this._getActiveQuery() + + if (!activeQuery) { + this._handleErrorEvent(msg) + return + } + + this._activeQuery = null + activeQuery.handleError(msg, this.connection) + } + + _handleRowDescription(msg) { + const activeQuery = this._getActiveQuery() + if (activeQuery == null) { + const error = new Error('Received unexpected rowDescription message from backend.') + this._handleErrorEvent(error) + return + } + // delegate rowDescription to active query + activeQuery.handleRowDescription(msg) + } + + _handleDataRow(msg) { + const activeQuery = this._getActiveQuery() + if (activeQuery == null) { + const error = new Error('Received unexpected dataRow message from backend.') + this._handleErrorEvent(error) + return + } + // delegate dataRow to active query + activeQuery.handleDataRow(msg) + } + + _handlePortalSuspended(msg) { + const activeQuery = this._getActiveQuery() + if (activeQuery == null) { + const error = new Error('Received unexpected portalSuspended message from backend.') + this._handleErrorEvent(error) + return + } + // delegate portalSuspended to active query + activeQuery.handlePortalSuspended(this.connection) + } + + _handleEmptyQuery(msg) { + const activeQuery = this._getActiveQuery() + if (activeQuery == null) { + const error = new Error('Received unexpected emptyQuery message from backend.') + this._handleErrorEvent(error) + return + } + // delegate emptyQuery to active query + activeQuery.handleEmptyQuery(this.connection) + } + + _handleCommandComplete(msg) { + const activeQuery = this._getActiveQuery() + if (activeQuery == null) { + const error = new Error('Received unexpected commandComplete message from backend.') + this._handleErrorEvent(error) + return + } + // delegate commandComplete to active query + activeQuery.handleCommandComplete(msg, this.connection) + } + + _handleParseComplete() { + const activeQuery = this._getActiveQuery() + if (activeQuery == null) { + const error = new Error('Received unexpected parseComplete message from backend.') + this._handleErrorEvent(error) + return + } + // if a prepared statement has a name and properly parses + // we track that its already been executed so we don't parse + // it again on the same client + if (activeQuery.name) { + this.connection.parsedStatements[activeQuery.name] = activeQuery.text + } + } + + _handleCopyInResponse(msg) { + const activeQuery = this._getActiveQuery() + if (activeQuery == null) { + const error = new Error('Received unexpected copyInResponse message from backend.') + this._handleErrorEvent(error) + return + } + activeQuery.handleCopyInResponse(this.connection) + } + + _handleCopyData(msg) { + const activeQuery = this._getActiveQuery() + if (activeQuery == null) { + const error = new Error('Received unexpected copyData message from backend.') + this._handleErrorEvent(error) + return + } + activeQuery.handleCopyData(msg, this.connection) + } + + _handleNotification(msg) { + this.emit('notification', msg) + } + + _handleNotice(msg) { + this.emit('notice', msg) + } + + getStartupConf() { + const params = this.connectionParameters + + const data = { + user: params.user, + database: params.database, + } + + const appName = params.application_name || params.fallback_application_name + if (appName) { + data.application_name = appName + } + if (params.replication) { + data.replication = '' + params.replication + } + if (params.statement_timeout) { + data.statement_timeout = String(parseInt(params.statement_timeout, 10)) + } + if (params.lock_timeout) { + data.lock_timeout = String(parseInt(params.lock_timeout, 10)) + } + if (params.idle_in_transaction_session_timeout) { + data.idle_in_transaction_session_timeout = String(parseInt(params.idle_in_transaction_session_timeout, 10)) + } + if (params.options) { + data.options = params.options + } + + return data + } + + cancel(client, query) { + if (client.activeQuery === query) { + const con = this.connection + + if (this.host && this.host.indexOf('/') === 0) { + con.connect(this.host + '/.s.PGSQL.' + this.port) + } else { + con.connect(this.port, this.host) + } + + // once connection is established send cancel message + con.on('connect', function () { + con.cancel(client.processID, client.secretKey) + }) + } else if (client._queryQueue.indexOf(query) !== -1) { + client._queryQueue.splice(client._queryQueue.indexOf(query), 1) + } + } + + setTypeParser(oid, format, parseFn) { + return this._types.setTypeParser(oid, format, parseFn) + } + + getTypeParser(oid, format) { + return this._types.getTypeParser(oid, format) + } + + // escapeIdentifier and escapeLiteral moved to utility functions & exported + // on PG + // re-exported here for backwards compatibility + escapeIdentifier(str) { + return utils.escapeIdentifier(str) + } + + escapeLiteral(str) { + return utils.escapeLiteral(str) + } + + _pulseQueryQueue() { + if (this.readyForQuery === true) { + this._activeQuery = this._queryQueue.shift() + const activeQuery = this._getActiveQuery() + if (activeQuery) { + this.readyForQuery = false + this.hasExecuted = true + + const queryError = activeQuery.submit(this.connection) + if (queryError) { + process.nextTick(() => { + activeQuery.handleError(queryError, this.connection) + this.readyForQuery = true + this._pulseQueryQueue() + }) + } + } else if (this.hasExecuted) { + this._activeQuery = null + this.emit('drain') + } + } + } + + query(config, values, callback) { + // can take in strings, config object or query object + let query + let result + + if (config == null) { + throw new TypeError('Client was passed a null or undefined query') + } + + if (typeof config.submit === 'function') { + result = query = config + if (!query.callback) { + if (typeof values === 'function') { + query.callback = values + } else if (callback) { + query.callback = callback + } + } + } else { + query = new Query(config, values, callback) + if (!query.callback) { + result = new this._Promise((resolve, reject) => { + query.callback = (err, res) => (err ? reject(err) : resolve(res)) + }).catch((err) => { + // replace the stack trace that leads to `TCP.onStreamRead` with one that leads back to the + // application that created the query + Error.captureStackTrace(err) + throw err + }) + } else if (typeof query.callback !== 'function') { + throw new TypeError('callback is not a function') + } + } + + const readTimeout = config.query_timeout || this.connectionParameters.query_timeout + if (readTimeout) { + const queryCallback = query.callback || (() => {}) + + const readTimeoutTimer = setTimeout(() => { + const error = new Error('Query read timeout') + + process.nextTick(() => { + query.handleError(error, this.connection) + }) + + queryCallback(error) + + // we already returned an error, + // just do nothing if query completes + query.callback = () => {} + + // Remove from queue + const index = this._queryQueue.indexOf(query) + if (index > -1) { + this._queryQueue.splice(index, 1) + } + + this._pulseQueryQueue() + }, readTimeout) + + query.callback = (err, res) => { + clearTimeout(readTimeoutTimer) + queryCallback(err, res) + } + } + + if (this.binary && !query.binary) { + query.binary = true + } + + if (query._result && !query._result._types) { + query._result._types = this._types + } + + if (!this._queryable) { + process.nextTick(() => { + query.handleError(new Error('Client has encountered a connection error and is not queryable'), this.connection) + }) + return result + } + + if (this._ending) { + process.nextTick(() => { + query.handleError(new Error('Client was closed and is not queryable'), this.connection) + }) + return result + } + + if (this._queryQueue.length > 0) { + queryQueueLengthDeprecationNotice() + } + this._queryQueue.push(query) + this._pulseQueryQueue() + return result + } + + ref() { + this.connection.ref() + } + + unref() { + this.connection.unref() + } + + getTransactionStatus() { + return this._txStatus + } + + end(cb) { + this._ending = true + + // if we have never connected, then end is a noop, callback immediately + if (!this.connection._connecting || this._ended) { + if (cb) { + cb() + return + } else { + return this._Promise.resolve() + } + } + + if (this._getActiveQuery() || !this._queryable) { + // if we have an active query we need to force a disconnect + // on the socket - otherwise a hung query could block end forever + this.connection.stream.destroy() + } else { + this.connection.end() + } + + if (cb) { + this.connection.once('end', cb) + } else { + return new this._Promise((resolve) => { + this.connection.once('end', resolve) + }) + } + } + get queryQueue() { + queryQueueDeprecationNotice() + return this._queryQueue + } +} + +// expose a Query constructor +Client.Query = Query + +module.exports = Client diff --git a/node_modules/pg/lib/connection-parameters.js b/node_modules/pg/lib/connection-parameters.js new file mode 100644 index 0000000..c153932 --- /dev/null +++ b/node_modules/pg/lib/connection-parameters.js @@ -0,0 +1,171 @@ +'use strict' + +const dns = require('dns') + +const defaults = require('./defaults') + +const parse = require('pg-connection-string').parse // parses a connection string + +const val = function (key, config, envVar) { + if (config[key]) { + return config[key] + } + + if (envVar === undefined) { + envVar = process.env['PG' + key.toUpperCase()] + } else if (envVar === false) { + // do nothing ... use false + } else { + envVar = process.env[envVar] + } + + return envVar || defaults[key] +} + +const readSSLConfigFromEnvironment = function () { + switch (process.env.PGSSLMODE) { + case 'disable': + return false + case 'prefer': + case 'require': + case 'verify-ca': + case 'verify-full': + return true + case 'no-verify': + return { rejectUnauthorized: false } + } + return defaults.ssl +} + +// Convert arg to a string, surround in single quotes, and escape single quotes and backslashes +const quoteParamValue = function (value) { + return "'" + ('' + value).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'" +} + +const add = function (params, config, paramName) { + const value = config[paramName] + if (value !== undefined && value !== null) { + params.push(paramName + '=' + quoteParamValue(value)) + } +} + +class ConnectionParameters { + constructor(config) { + // if a string is passed, it is a raw connection string so we parse it into a config + config = typeof config === 'string' ? parse(config) : config || {} + + // if the config has a connectionString defined, parse IT into the config we use + // this will override other default values with what is stored in connectionString + if (config.connectionString) { + config = Object.assign({}, config, parse(config.connectionString)) + } + + this.user = val('user', config) + this.database = val('database', config) + + if (this.database === undefined) { + this.database = this.user + } + + this.port = parseInt(val('port', config), 10) + this.host = val('host', config) + + // "hiding" the password so it doesn't show up in stack traces + // or if the client is console.logged + Object.defineProperty(this, 'password', { + configurable: true, + enumerable: false, + writable: true, + value: val('password', config), + }) + + this.binary = val('binary', config) + this.options = val('options', config) + + this.ssl = typeof config.ssl === 'undefined' ? readSSLConfigFromEnvironment() : config.ssl + + if (typeof this.ssl === 'string') { + if (this.ssl === 'true') { + this.ssl = true + } + } + // support passing in ssl=no-verify via connection string + if (this.ssl === 'no-verify') { + this.ssl = { rejectUnauthorized: false } + } + if (this.ssl && this.ssl.key) { + Object.defineProperty(this.ssl, 'key', { + enumerable: false, + }) + } + + this.client_encoding = val('client_encoding', config) + this.replication = val('replication', config) + // a domain socket begins with '/' + this.isDomainSocket = !(this.host || '').indexOf('/') + + this.application_name = val('application_name', config, 'PGAPPNAME') + this.fallback_application_name = val('fallback_application_name', config, false) + this.statement_timeout = val('statement_timeout', config, false) + this.lock_timeout = val('lock_timeout', config, false) + this.idle_in_transaction_session_timeout = val('idle_in_transaction_session_timeout', config, false) + this.query_timeout = val('query_timeout', config, false) + + if (config.connectionTimeoutMillis === undefined) { + this.connect_timeout = process.env.PGCONNECT_TIMEOUT || 0 + } else { + this.connect_timeout = Math.floor(config.connectionTimeoutMillis / 1000) + } + + if (config.keepAlive === false) { + this.keepalives = 0 + } else if (config.keepAlive === true) { + this.keepalives = 1 + } + + if (typeof config.keepAliveInitialDelayMillis === 'number') { + this.keepalives_idle = Math.floor(config.keepAliveInitialDelayMillis / 1000) + } + } + + getLibpqConnectionString(cb) { + const params = [] + add(params, this, 'user') + add(params, this, 'password') + add(params, this, 'port') + add(params, this, 'application_name') + add(params, this, 'fallback_application_name') + add(params, this, 'connect_timeout') + add(params, this, 'options') + + const ssl = typeof this.ssl === 'object' ? this.ssl : this.ssl ? { sslmode: this.ssl } : {} + add(params, ssl, 'sslmode') + add(params, ssl, 'sslca') + add(params, ssl, 'sslkey') + add(params, ssl, 'sslcert') + add(params, ssl, 'sslrootcert') + + if (this.database) { + params.push('dbname=' + quoteParamValue(this.database)) + } + if (this.replication) { + params.push('replication=' + quoteParamValue(this.replication)) + } + if (this.host) { + params.push('host=' + quoteParamValue(this.host)) + } + if (this.isDomainSocket) { + return cb(null, params.join(' ')) + } + if (this.client_encoding) { + params.push('client_encoding=' + quoteParamValue(this.client_encoding)) + } + dns.lookup(this.host, function (err, address) { + if (err) return cb(err, null) + params.push('hostaddr=' + quoteParamValue(address)) + return cb(null, params.join(' ')) + }) + } +} + +module.exports = ConnectionParameters diff --git a/node_modules/pg/lib/connection.js b/node_modules/pg/lib/connection.js new file mode 100644 index 0000000..027f939 --- /dev/null +++ b/node_modules/pg/lib/connection.js @@ -0,0 +1,221 @@ +'use strict' + +const EventEmitter = require('events').EventEmitter + +const { parse, serialize } = require('pg-protocol') +const { getStream, getSecureStream } = require('./stream') + +const flushBuffer = serialize.flush() +const syncBuffer = serialize.sync() +const endBuffer = serialize.end() + +// TODO(bmc) support binary mode at some point +class Connection extends EventEmitter { + constructor(config) { + super() + config = config || {} + + this.stream = config.stream || getStream(config.ssl) + if (typeof this.stream === 'function') { + this.stream = this.stream(config) + } + + this._keepAlive = config.keepAlive + this._keepAliveInitialDelayMillis = config.keepAliveInitialDelayMillis + this.parsedStatements = {} + this.ssl = config.ssl || false + this._ending = false + this._emitMessage = false + const self = this + this.on('newListener', function (eventName) { + if (eventName === 'message') { + self._emitMessage = true + } + }) + } + + connect(port, host) { + const self = this + + this._connecting = true + this.stream.setNoDelay(true) + this.stream.connect(port, host) + + this.stream.once('connect', function () { + if (self._keepAlive) { + self.stream.setKeepAlive(true, self._keepAliveInitialDelayMillis) + } + self.emit('connect') + }) + + const reportStreamError = function (error) { + // errors about disconnections should be ignored during disconnect + if (self._ending && (error.code === 'ECONNRESET' || error.code === 'EPIPE')) { + return + } + self.emit('error', error) + } + this.stream.on('error', reportStreamError) + + this.stream.on('close', function () { + self.emit('end') + }) + + if (!this.ssl) { + return this.attachListeners(this.stream) + } + + this.stream.once('data', function (buffer) { + const responseCode = buffer.toString('utf8') + switch (responseCode) { + case 'S': // Server supports SSL connections, continue with a secure connection + break + case 'N': // Server does not support SSL connections + self.stream.end() + return self.emit('error', new Error('The server does not support SSL connections')) + default: + // Any other response byte, including 'E' (ErrorResponse) indicating a server error + self.stream.end() + return self.emit('error', new Error('There was an error establishing an SSL connection')) + } + const options = { + socket: self.stream, + } + + if (self.ssl !== true) { + Object.assign(options, self.ssl) + + if ('key' in self.ssl) { + options.key = self.ssl.key + } + } + + const net = require('net') + if (net.isIP && net.isIP(host) === 0) { + options.servername = host + } + try { + self.stream = getSecureStream(options) + } catch (err) { + return self.emit('error', err) + } + self.attachListeners(self.stream) + self.stream.on('error', reportStreamError) + + self.emit('sslconnect') + }) + } + + attachListeners(stream) { + parse(stream, (msg) => { + const eventName = msg.name === 'error' ? 'errorMessage' : msg.name + if (this._emitMessage) { + this.emit('message', msg) + } + this.emit(eventName, msg) + }) + } + + requestSsl() { + this.stream.write(serialize.requestSsl()) + } + + startup(config) { + this.stream.write(serialize.startup(config)) + } + + cancel(processID, secretKey) { + this._send(serialize.cancel(processID, secretKey)) + } + + password(password) { + this._send(serialize.password(password)) + } + + sendSASLInitialResponseMessage(mechanism, initialResponse) { + this._send(serialize.sendSASLInitialResponseMessage(mechanism, initialResponse)) + } + + sendSCRAMClientFinalMessage(additionalData) { + this._send(serialize.sendSCRAMClientFinalMessage(additionalData)) + } + + _send(buffer) { + if (!this.stream.writable) { + return false + } + return this.stream.write(buffer) + } + + query(text) { + this._send(serialize.query(text)) + } + + // send parse message + parse(query) { + this._send(serialize.parse(query)) + } + + // send bind message + bind(config) { + this._send(serialize.bind(config)) + } + + // send execute message + execute(config) { + this._send(serialize.execute(config)) + } + + flush() { + if (this.stream.writable) { + this.stream.write(flushBuffer) + } + } + + sync() { + this._ending = true + this._send(syncBuffer) + } + + ref() { + this.stream.ref() + } + + unref() { + this.stream.unref() + } + + end() { + // 0x58 = 'X' + this._ending = true + if (!this._connecting || !this.stream.writable) { + this.stream.end() + return + } + return this.stream.write(endBuffer, () => { + this.stream.end() + }) + } + + close(msg) { + this._send(serialize.close(msg)) + } + + describe(msg) { + this._send(serialize.describe(msg)) + } + + sendCopyFromChunk(chunk) { + this._send(serialize.copyData(chunk)) + } + + endCopyFrom() { + this._send(serialize.copyDone()) + } + + sendCopyFail(msg) { + this._send(serialize.copyFail(msg)) + } +} + +module.exports = Connection diff --git a/node_modules/pg/lib/crypto/cert-signatures.js b/node_modules/pg/lib/crypto/cert-signatures.js new file mode 100644 index 0000000..8d8df34 --- /dev/null +++ b/node_modules/pg/lib/crypto/cert-signatures.js @@ -0,0 +1,122 @@ +function x509Error(msg, cert) { + return new Error('SASL channel binding: ' + msg + ' when parsing public certificate ' + cert.toString('base64')) +} + +function readASN1Length(data, index) { + let length = data[index++] + if (length < 0x80) return { length, index } + + const lengthBytes = length & 0x7f + if (lengthBytes > 4) throw x509Error('bad length', data) + + length = 0 + for (let i = 0; i < lengthBytes; i++) { + length = (length << 8) | data[index++] + } + + return { length, index } +} + +function readASN1OID(data, index) { + if (data[index++] !== 0x6) throw x509Error('non-OID data', data) // 6 = OID + + const { length: OIDLength, index: indexAfterOIDLength } = readASN1Length(data, index) + index = indexAfterOIDLength + const lastIndex = index + OIDLength + + const byte1 = data[index++] + let oid = ((byte1 / 40) >> 0) + '.' + (byte1 % 40) + + while (index < lastIndex) { + // loop over numbers in OID + let value = 0 + while (index < lastIndex) { + // loop over bytes in number + const nextByte = data[index++] + value = (value << 7) | (nextByte & 0x7f) + if (nextByte < 0x80) break + } + oid += '.' + value + } + + return { oid, index } +} + +function expectASN1Seq(data, index) { + if (data[index++] !== 0x30) throw x509Error('non-sequence data', data) // 30 = Sequence + return readASN1Length(data, index) +} + +function signatureAlgorithmHashFromCertificate(data, index) { + // read this thread: https://www.postgresql.org/message-id/17760-b6c61e752ec07060%40postgresql.org + if (index === undefined) index = 0 + index = expectASN1Seq(data, index).index + const { length: certInfoLength, index: indexAfterCertInfoLength } = expectASN1Seq(data, index) + index = indexAfterCertInfoLength + certInfoLength // skip over certificate info + index = expectASN1Seq(data, index).index // skip over signature length field + const { oid, index: indexAfterOID } = readASN1OID(data, index) + switch (oid) { + // RSA + case '1.2.840.113549.1.1.4': + return 'MD5' + case '1.2.840.113549.1.1.5': + return 'SHA-1' + case '1.2.840.113549.1.1.11': + return 'SHA-256' + case '1.2.840.113549.1.1.12': + return 'SHA-384' + case '1.2.840.113549.1.1.13': + return 'SHA-512' + case '1.2.840.113549.1.1.14': + return 'SHA-224' + case '1.2.840.113549.1.1.15': + return 'SHA512-224' + case '1.2.840.113549.1.1.16': + return 'SHA512-256' + // ECDSA + case '1.2.840.10045.4.1': + return 'SHA-1' + case '1.2.840.10045.4.3.1': + return 'SHA-224' + case '1.2.840.10045.4.3.2': + return 'SHA-256' + case '1.2.840.10045.4.3.3': + return 'SHA-384' + case '1.2.840.10045.4.3.4': + return 'SHA-512' + // RSASSA-PSS: hash is indicated separately + case '1.2.840.113549.1.1.10': { + index = indexAfterOID + index = expectASN1Seq(data, index).index + if (data[index++] !== 0xa0) throw x509Error('non-tag data', data) // a0 = constructed tag 0 + index = readASN1Length(data, index).index // skip over tag length field + index = expectASN1Seq(data, index).index // skip over sequence length field + const { oid: hashOID } = readASN1OID(data, index) + switch (hashOID) { + // standalone hash OIDs + case '1.2.840.113549.2.5': + return 'MD5' + case '1.3.14.3.2.26': + return 'SHA-1' + case '2.16.840.1.101.3.4.2.1': + return 'SHA-256' + case '2.16.840.1.101.3.4.2.2': + return 'SHA-384' + case '2.16.840.1.101.3.4.2.3': + return 'SHA-512' + } + throw x509Error('unknown hash OID ' + hashOID, data) + } + // Ed25519 -- see https: return//github.com/openssl/openssl/issues/15477 + case '1.3.101.110': + case '1.3.101.112': // ph + return 'SHA-512' + // Ed448 -- still not in pg 17.2 (if supported, digest would be SHAKE256 x 64 bytes) + case '1.3.101.111': + case '1.3.101.113': // ph + throw x509Error('Ed448 certificate channel binding is not currently supported by Postgres') + } + throw x509Error('unknown OID ' + oid, data) +} + +module.exports = { signatureAlgorithmHashFromCertificate } diff --git a/node_modules/pg/lib/crypto/sasl.js b/node_modules/pg/lib/crypto/sasl.js new file mode 100644 index 0000000..ea63b24 --- /dev/null +++ b/node_modules/pg/lib/crypto/sasl.js @@ -0,0 +1,262 @@ +'use strict' +const crypto = require('./utils') +const { signatureAlgorithmHashFromCertificate } = require('./cert-signatures') + +// SASLprep (RFC 4013) — minimal in-tree implementation. +// +// Per RFC 5802 §2.2, the SCRAM-SHA-256 client must normalize the password via +// SASLprep before feeding it into PBKDF2. PostgreSQL's server applies the same +// SASLprep when computing the stored verifier, and libpq does the same client +// side, so passwords whose NFKC form differs from the raw form +// would otherwise authenticate against psql/libpq but fail against pg with `28P01`. +// +// We deliberately implement only the three steps that change the byte content: +// 1. RFC 3454 Table C.1.2 (non-ASCII space) → U+0020 SPACE. +// 2. RFC 3454 Table B.1 (commonly mapped to nothing) → empty. +// 3. NFKC normalization. +// We skip the prohibition (RFC 4013 §2.3) and bidi (RFC 3454 §6) checks. +// libpq is forgiving on those paths and Postgres's own SASLprep matches that +// leniency for legacy roles, so omitting the rejection logic keeps existing +// roles working without adding complexity. +function saslprep(password) { + // RFC 3454 Table C.1.2 — non-ASCII space characters, mapped to U+0020. + const nonAsciiSpace = /[\u00A0\u1680\u2000-\u200B\u202F\u205F\u3000]/g + // RFC 3454 Table B.1 — "commonly mapped to nothing". The set intentionally + // contains zero-width joiners and variation selectors — the very characters + // ESLint's no-misleading-character-class warns about — because they combine + // with their neighbors and the RFC strips them for that reason. + // eslint-disable-next-line no-misleading-character-class + const mappedToNothing = /[\u00AD\u034F\u1806\u180B\u180C\u180D\u200C\u200D\u2060\uFE00-\uFE0F\uFEFF]/g + return password.replace(nonAsciiSpace, ' ').replace(mappedToNothing, '').normalize('NFKC') +} + +const DEFAULT_MAX_SCRAM_ITERATIONS = 100000 + +function startSession(mechanisms, stream, scramMaxIterations = DEFAULT_MAX_SCRAM_ITERATIONS) { + const candidates = ['SCRAM-SHA-256'] + if (stream) candidates.unshift('SCRAM-SHA-256-PLUS') // higher-priority, so placed first + + const mechanism = candidates.find((candidate) => mechanisms.includes(candidate)) + + if (!mechanism) { + throw new Error('SASL: Only mechanism(s) ' + candidates.join(' and ') + ' are supported') + } + + if (mechanism === 'SCRAM-SHA-256-PLUS' && typeof stream.getPeerCertificate !== 'function') { + // this should never happen if we are really talking to a Postgres server + throw new Error('SASL: Mechanism SCRAM-SHA-256-PLUS requires a certificate') + } + + const clientNonce = crypto.randomBytes(18).toString('base64') + const gs2Header = mechanism === 'SCRAM-SHA-256-PLUS' ? 'p=tls-server-end-point' : stream ? 'y' : 'n' + + return { + mechanism, + clientNonce, + response: gs2Header + ',,n=*,r=' + clientNonce, + message: 'SASLInitialResponse', + scramMaxIterations, + } +} + +async function continueSession(session, password, serverData, stream) { + if (session.message !== 'SASLInitialResponse') { + throw new Error('SASL: Last message was not SASLInitialResponse') + } + if (typeof password !== 'string') { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string') + } + if (password === '') { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a non-empty string') + } + if (typeof serverData !== 'string') { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: serverData must be a string') + } + + const sv = parseServerFirstMessage(serverData) + + if (!sv.nonce.startsWith(session.clientNonce)) { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not start with client nonce') + } else if (sv.nonce.length === session.clientNonce.length) { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short') + } + + const scramMaxIterations = + typeof session.scramMaxIterations === 'number' ? session.scramMaxIterations : DEFAULT_MAX_SCRAM_ITERATIONS + // a value of 0 disables the iteration count check + if (scramMaxIterations !== 0 && sv.iteration > scramMaxIterations) { + throw new Error( + 'SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count ' + + sv.iteration + + ' exceeds scramMaxIterations of ' + + scramMaxIterations + ) + } + + const clientFirstMessageBare = 'n=*,r=' + session.clientNonce + const serverFirstMessage = 'r=' + sv.nonce + ',s=' + sv.salt + ',i=' + sv.iteration + + // without channel binding: + let channelBinding = stream ? 'eSws' : 'biws' // 'y,,' or 'n,,', base64-encoded + + // override if channel binding is in use: + if (session.mechanism === 'SCRAM-SHA-256-PLUS') { + const peerCert = stream.getPeerCertificate().raw + let hashName = signatureAlgorithmHashFromCertificate(peerCert) + if (hashName === 'MD5' || hashName === 'SHA-1') hashName = 'SHA-256' + const certHash = await crypto.hashByName(hashName, peerCert) + const bindingData = Buffer.concat([Buffer.from('p=tls-server-end-point,,'), Buffer.from(certHash)]) + channelBinding = bindingData.toString('base64') + } + + const clientFinalMessageWithoutProof = 'c=' + channelBinding + ',r=' + sv.nonce + const authMessage = clientFirstMessageBare + ',' + serverFirstMessage + ',' + clientFinalMessageWithoutProof + + const saltBytes = Buffer.from(sv.salt, 'base64') + const saltedPassword = await crypto.deriveKey(saslprep(password), saltBytes, sv.iteration) + const clientKey = await crypto.hmacSha256(saltedPassword, 'Client Key') + const storedKey = await crypto.sha256(clientKey) + const clientSignature = await crypto.hmacSha256(storedKey, authMessage) + const clientProof = xorBuffers(Buffer.from(clientKey), Buffer.from(clientSignature)).toString('base64') + const serverKey = await crypto.hmacSha256(saltedPassword, 'Server Key') + const serverSignatureBytes = await crypto.hmacSha256(serverKey, authMessage) + + session.message = 'SASLResponse' + session.serverSignature = Buffer.from(serverSignatureBytes).toString('base64') + session.response = clientFinalMessageWithoutProof + ',p=' + clientProof +} + +function finalizeSession(session, serverData) { + if (session.message !== 'SASLResponse') { + throw new Error('SASL: Last message was not SASLResponse') + } + if (typeof serverData !== 'string') { + throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: serverData must be a string') + } + + const { serverSignature } = parseServerFinalMessage(serverData) + + if (serverSignature !== session.serverSignature) { + throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match') + } +} + +/** + * printable = %x21-2B / %x2D-7E + * ;; Printable ASCII except ",". + * ;; Note that any "printable" is also + * ;; a valid "value". + */ +function isPrintableChars(text) { + if (typeof text !== 'string') { + throw new TypeError('SASL: text must be a string') + } + return text + .split('') + .map((_, i) => text.charCodeAt(i)) + .every((c) => (c >= 0x21 && c <= 0x2b) || (c >= 0x2d && c <= 0x7e)) +} + +/** + * base64-char = ALPHA / DIGIT / "/" / "+" + * + * base64-4 = 4base64-char + * + * base64-3 = 3base64-char "=" + * + * base64-2 = 2base64-char "==" + * + * base64 = *base64-4 [base64-3 / base64-2] + */ +function isBase64(text) { + return /^(?:[a-zA-Z0-9+/]{4})*(?:[a-zA-Z0-9+/]{2}==|[a-zA-Z0-9+/]{3}=)?$/.test(text) +} + +function parseAttributePairs(text) { + if (typeof text !== 'string') { + throw new TypeError('SASL: attribute pairs text must be a string') + } + + return new Map( + text.split(',').map((attrValue) => { + if (!/^.=/.test(attrValue)) { + throw new Error('SASL: Invalid attribute pair entry') + } + const name = attrValue[0] + const value = attrValue.substring(2) + return [name, value] + }) + ) +} + +function parseServerFirstMessage(data) { + const attrPairs = parseAttributePairs(data) + + const nonce = attrPairs.get('r') + if (!nonce) { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing') + } else if (!isPrintableChars(nonce)) { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters') + } + const salt = attrPairs.get('s') + if (!salt) { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing') + } else if (!isBase64(salt)) { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64') + } + const iterationText = attrPairs.get('i') + if (!iterationText) { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing') + } else if (!/^[1-9][0-9]*$/.test(iterationText)) { + throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count') + } + const iteration = parseInt(iterationText, 10) + + return { + nonce, + salt, + iteration, + } +} + +function parseServerFinalMessage(serverData) { + const attrPairs = parseAttributePairs(serverData) + const error = attrPairs.get('e') + const serverSignature = attrPairs.get('v') + + if (error) { + throw new Error(`SASL: SCRAM-SERVER-FINAL-MESSAGE: server returned error: "${error}"`) + } + + if (!serverSignature) { + throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing') + } else if (!isBase64(serverSignature)) { + throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature must be base64') + } + return { + serverSignature, + } +} + +function xorBuffers(a, b) { + if (!Buffer.isBuffer(a)) { + throw new TypeError('first argument must be a Buffer') + } + if (!Buffer.isBuffer(b)) { + throw new TypeError('second argument must be a Buffer') + } + if (a.length !== b.length) { + throw new Error('Buffer lengths must match') + } + if (a.length === 0) { + throw new Error('Buffers cannot be empty') + } + return Buffer.from(a.map((_, i) => a[i] ^ b[i])) +} + +module.exports = { + startSession, + continueSession, + finalizeSession, + DEFAULT_MAX_SCRAM_ITERATIONS, +} diff --git a/node_modules/pg/lib/crypto/utils.js b/node_modules/pg/lib/crypto/utils.js new file mode 100644 index 0000000..65aa4a1 --- /dev/null +++ b/node_modules/pg/lib/crypto/utils.js @@ -0,0 +1,89 @@ +const nodeCrypto = require('crypto') + +module.exports = { + postgresMd5PasswordHash, + randomBytes, + deriveKey, + sha256, + hashByName, + hmacSha256, + md5, +} + +/** + * The Web Crypto API - grabbed from the Node.js library or the global + * @type Crypto + */ +// eslint-disable-next-line no-undef +const webCrypto = nodeCrypto.webcrypto || globalThis.crypto +/** + * The SubtleCrypto API for low level crypto operations. + * @type SubtleCrypto + */ +const subtleCrypto = webCrypto.subtle +const textEncoder = new TextEncoder() + +/** + * + * @param {*} length + * @returns + */ +function randomBytes(length) { + return webCrypto.getRandomValues(Buffer.alloc(length)) +} + +async function md5(string) { + try { + return nodeCrypto.createHash('md5').update(string, 'utf-8').digest('hex') + } catch (e) { + // `createHash()` failed so we are probably not in Node.js, use the WebCrypto API instead. + // Note that the MD5 algorithm on WebCrypto is not available in Node.js. + // This is why we cannot just use WebCrypto in all environments. + const data = typeof string === 'string' ? textEncoder.encode(string) : string + const hash = await subtleCrypto.digest('MD5', data) + return Array.from(new Uint8Array(hash)) + .map((b) => b.toString(16).padStart(2, '0')) + .join('') + } +} + +// See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html +async function postgresMd5PasswordHash(user, password, salt) { + const inner = await md5(password + user) + const outer = await md5(Buffer.concat([Buffer.from(inner), salt])) + return 'md5' + outer +} + +/** + * Create a SHA-256 digest of the given data + * @param {Buffer} data + */ +async function sha256(text) { + return await subtleCrypto.digest('SHA-256', text) +} + +async function hashByName(hashName, text) { + return await subtleCrypto.digest(hashName, text) +} + +/** + * Sign the message with the given key + * @param {ArrayBuffer} keyBuffer + * @param {string} msg + */ +async function hmacSha256(keyBuffer, msg) { + const key = await subtleCrypto.importKey('raw', keyBuffer, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']) + return await subtleCrypto.sign('HMAC', key, textEncoder.encode(msg)) +} + +/** + * Derive a key from the password and salt + * @param {string} password + * @param {Uint8Array} salt + * @param {number} iterations + */ +async function deriveKey(password, salt, iterations) { + const key = await subtleCrypto.importKey('raw', textEncoder.encode(password), 'PBKDF2', false, ['deriveBits']) + const params = { name: 'PBKDF2', hash: 'SHA-256', salt: salt, iterations: iterations } + return await subtleCrypto.deriveBits(params, key, 32 * 8, ['deriveBits']) +} diff --git a/node_modules/pg/lib/defaults.js b/node_modules/pg/lib/defaults.js new file mode 100644 index 0000000..673696f --- /dev/null +++ b/node_modules/pg/lib/defaults.js @@ -0,0 +1,91 @@ +'use strict' + +let user +try { + user = process.platform === 'win32' ? process.env.USERNAME : process.env.USER +} catch { + // ignore, e.g., Deno without --allow-env +} + +module.exports = { + // database host. defaults to localhost + host: 'localhost', + + // database user's name + user, + + // name of database to connect + database: undefined, + + // database user's password + password: null, + + // a Postgres connection string to be used instead of setting individual connection items + // NOTE: Setting this value will cause it to override any other value (such as database or user) defined + // in the defaults object. + connectionString: undefined, + + // database port + port: 5432, + + // number of rows to return at a time from a prepared statement's + // portal. 0 will return all rows at once + rows: 0, + + // binary result mode + binary: false, + + // Connection pool options - see https://github.com/brianc/node-pg-pool + + // number of connections to use in connection pool + // 0 will disable connection pooling + max: 10, + + // max milliseconds a client can go unused before it is removed + // from the pool and destroyed + idleTimeoutMillis: 30000, + + client_encoding: '', + + ssl: false, + + application_name: undefined, + + fallback_application_name: undefined, + + options: undefined, + + parseInputDatesAsUTC: false, + + // max milliseconds any query using this connection will execute for before timing out in error. + // false=unlimited + statement_timeout: false, + + // Abort any statement that waits longer than the specified duration in milliseconds while attempting to acquire a lock. + // false=unlimited + lock_timeout: false, + + // Terminate any session with an open transaction that has been idle for longer than the specified duration in milliseconds + // false=unlimited + idle_in_transaction_session_timeout: false, + + // max milliseconds to wait for query to complete (client side) + query_timeout: false, + + connect_timeout: 0, + + keepalives: 1, + + keepalives_idle: 0, +} + +const pgTypes = require('pg-types') +// save default parsers +const parseBigInteger = pgTypes.getTypeParser(20, 'text') +const parseBigIntegerArray = pgTypes.getTypeParser(1016, 'text') + +// parse int8 so you can get your count values as actual numbers +module.exports.__defineSetter__('parseInt8', function (val) { + pgTypes.setTypeParser(20, 'text', val ? pgTypes.getTypeParser(23, 'text') : parseBigInteger) + pgTypes.setTypeParser(1016, 'text', val ? pgTypes.getTypeParser(1007, 'text') : parseBigIntegerArray) +}) diff --git a/node_modules/pg/lib/index.js b/node_modules/pg/lib/index.js new file mode 100644 index 0000000..e8b7461 --- /dev/null +++ b/node_modules/pg/lib/index.js @@ -0,0 +1,73 @@ +'use strict' + +const Client = require('./client') +const defaults = require('./defaults') +const Connection = require('./connection') +const Result = require('./result') +const utils = require('./utils') +const Pool = require('pg-pool') +const TypeOverrides = require('./type-overrides') +const { DatabaseError } = require('pg-protocol') +const { escapeIdentifier, escapeLiteral } = require('./utils') + +const poolFactory = (Client) => { + return class BoundPool extends Pool { + constructor(options) { + super(options, Client) + } + } +} + +const PG = function (clientConstructor) { + this.defaults = defaults + this.Client = clientConstructor + this.Query = this.Client.Query + this.Pool = poolFactory(this.Client) + this._pools = [] + this.Connection = Connection + this.types = require('pg-types') + this.DatabaseError = DatabaseError + this.TypeOverrides = TypeOverrides + this.escapeIdentifier = escapeIdentifier + this.escapeLiteral = escapeLiteral + this.Result = Result + this.utils = utils +} + +let clientConstructor = Client + +let forceNative = false +try { + forceNative = !!process.env.NODE_PG_FORCE_NATIVE +} catch { + // ignore, e.g., Deno without --allow-env +} + +if (forceNative) { + clientConstructor = require('./native') +} + +module.exports = new PG(clientConstructor) + +// lazy require native module...the native module may not have installed +Object.defineProperty(module.exports, 'native', { + configurable: true, + enumerable: false, + get() { + let native = null + try { + native = new PG(require('./native')) + } catch (err) { + if (err.code !== 'MODULE_NOT_FOUND') { + throw err + } + } + + // overwrite module.exports.native so that getter is never called again + Object.defineProperty(module.exports, 'native', { + value: native, + }) + + return native + }, +}) diff --git a/node_modules/pg/lib/native/client.js b/node_modules/pg/lib/native/client.js new file mode 100644 index 0000000..fa17d9f --- /dev/null +++ b/node_modules/pg/lib/native/client.js @@ -0,0 +1,329 @@ +const nodeUtils = require('util') +// eslint-disable-next-line +var Native +// eslint-disable-next-line no-useless-catch +try { + // Wrap this `require()` in a try-catch to avoid upstream bundlers from complaining that this might not be available since it is an optional import + Native = require('pg-native') +} catch (e) { + throw e +} +const TypeOverrides = require('../type-overrides') +const EventEmitter = require('events').EventEmitter +const util = require('util') +const ConnectionParameters = require('../connection-parameters') + +const NativeQuery = require('./query') + +const queryQueueLengthDeprecationNotice = nodeUtils.deprecate( + () => {}, + 'Calling client.query() when the client is already executing a query is deprecated and will be removed in pg@9.0. Use async/await or an external async flow control mechanism instead.' +) + +const Client = (module.exports = function (config) { + EventEmitter.call(this) + config = config || {} + + this._Promise = config.Promise || global.Promise + this._types = new TypeOverrides(config.types) + + this.native = new Native({ + types: this._types, + }) + + this._queryQueue = [] + this._ending = false + this._connecting = false + this._connected = false + this._queryable = true + + // keep these on the object for legacy reasons + // for the time being. TODO: deprecate all this jazz + const cp = (this.connectionParameters = new ConnectionParameters(config)) + if (config.nativeConnectionString) cp.nativeConnectionString = config.nativeConnectionString + this.user = cp.user + + // "hiding" the password so it doesn't show up in stack traces + // or if the client is console.logged + Object.defineProperty(this, 'password', { + configurable: true, + enumerable: false, + writable: true, + value: cp.password, + }) + this.database = cp.database + this.host = cp.host + this.port = cp.port + + // a hash to hold named queries + this.namedQueries = {} +}) + +Client.Query = NativeQuery + +util.inherits(Client, EventEmitter) + +Client.prototype._errorAllQueries = function (err) { + const enqueueError = (query) => { + process.nextTick(() => { + query.native = this.native + query.handleError(err) + }) + } + + if (this._hasActiveQuery()) { + enqueueError(this._activeQuery) + this._activeQuery = null + } + + this._queryQueue.forEach(enqueueError) + this._queryQueue.length = 0 +} + +// connect to the backend +// pass an optional callback to be called once connected +// or with an error if there was a connection error +Client.prototype._connect = function (cb) { + const self = this + + if (this._connecting) { + process.nextTick(() => cb(new Error('Client has already been connected. You cannot reuse a client.'))) + return + } + + this._connecting = true + + this.connectionParameters.getLibpqConnectionString(function (err, conString) { + if (self.connectionParameters.nativeConnectionString) conString = self.connectionParameters.nativeConnectionString + if (err) return cb(err) + self.native.connect(conString, function (err) { + if (err) { + self.native.end() + return cb(err) + } + + // set internal states to connected + self._connected = true + + // handle connection errors from the native layer + self.native.on('error', function (err) { + self._queryable = false + self._errorAllQueries(err) + self.emit('error', err) + }) + + self.native.on('notification', function (msg) { + self.emit('notification', { + channel: msg.relname, + payload: msg.extra, + }) + }) + + // signal we are connected now + self.emit('connect') + self._pulseQueryQueue(true) + + cb(null, this) + }) + }) +} + +Client.prototype.connect = function (callback) { + if (callback) { + this._connect(callback) + return + } + + return new this._Promise((resolve, reject) => { + this._connect((error) => { + if (error) { + reject(error) + } else { + resolve(this) + } + }) + }) +} + +// send a query to the server +// this method is highly overloaded to take +// 1) string query, optional array of parameters, optional function callback +// 2) object query with { +// string query +// optional array values, +// optional function callback instead of as a separate parameter +// optional string name to name & cache the query plan +// optional string rowMode = 'array' for an array of results +// } +Client.prototype.query = function (config, values, callback) { + let query + let result + let readTimeout + let readTimeoutTimer + let queryCallback + + if (config === null || config === undefined) { + throw new TypeError('Client was passed a null or undefined query') + } else if (typeof config.submit === 'function') { + readTimeout = config.query_timeout || this.connectionParameters.query_timeout + result = query = config + // accept query(new Query(...), (err, res) => { }) style + if (typeof values === 'function') { + config.callback = values + } + } else { + readTimeout = config.query_timeout || this.connectionParameters.query_timeout + query = new NativeQuery(config, values, callback) + if (!query.callback) { + let resolveOut, rejectOut + result = new this._Promise((resolve, reject) => { + resolveOut = resolve + rejectOut = reject + }).catch((err) => { + Error.captureStackTrace(err) + throw err + }) + query.callback = (err, res) => (err ? rejectOut(err) : resolveOut(res)) + } + } + + if (readTimeout) { + queryCallback = query.callback || (() => {}) + + readTimeoutTimer = setTimeout(() => { + const error = new Error('Query read timeout') + + process.nextTick(() => { + query.handleError(error, this.connection) + }) + + queryCallback(error) + + // we already returned an error, + // just do nothing if query completes + query.callback = () => {} + + // Remove from queue + const index = this._queryQueue.indexOf(query) + if (index > -1) { + this._queryQueue.splice(index, 1) + } + + this._pulseQueryQueue() + }, readTimeout) + + query.callback = (err, res) => { + clearTimeout(readTimeoutTimer) + queryCallback(err, res) + } + } + + if (!this._queryable) { + query.native = this.native + process.nextTick(() => { + query.handleError(new Error('Client has encountered a connection error and is not queryable')) + }) + return result + } + + if (this._ending) { + query.native = this.native + process.nextTick(() => { + query.handleError(new Error('Client was closed and is not queryable')) + }) + return result + } + + if (this._queryQueue.length > 0) { + queryQueueLengthDeprecationNotice() + } + + this._queryQueue.push(query) + this._pulseQueryQueue() + return result +} + +// disconnect from the backend server +Client.prototype.end = function (cb) { + const self = this + + this._ending = true + + if (this._connecting && !this._connected) { + this.once('connect', () => { + this.end(() => {}) + }) + } + let result + if (!cb) { + result = new this._Promise(function (resolve, reject) { + cb = (err) => (err ? reject(err) : resolve()) + }) + } + + this.native.end(function () { + self._connected = false + + self._errorAllQueries(new Error('Connection terminated')) + + process.nextTick(() => { + self.emit('end') + if (cb) cb() + }) + }) + return result +} + +Client.prototype._hasActiveQuery = function () { + return this._activeQuery && this._activeQuery.state !== 'error' && this._activeQuery.state !== 'end' +} + +Client.prototype._pulseQueryQueue = function (initialConnection) { + if (!this._connected) { + return + } + if (this._hasActiveQuery()) { + return + } + const query = this._queryQueue.shift() + if (!query) { + if (!initialConnection) { + this.emit('drain') + } + return + } + this._activeQuery = query + query.submit(this) + const self = this + query.once('_done', function () { + self._pulseQueryQueue() + }) +} + +// attempt to cancel an in-progress query +Client.prototype.cancel = function (query) { + if (this._activeQuery === query) { + this.native.cancel(function () {}) + } else if (this._queryQueue.indexOf(query) !== -1) { + this._queryQueue.splice(this._queryQueue.indexOf(query), 1) + } +} + +Client.prototype.ref = function () {} +Client.prototype.unref = function () {} + +Client.prototype.setTypeParser = function (oid, format, parseFn) { + return this._types.setTypeParser(oid, format, parseFn) +} + +Client.prototype.getTypeParser = function (oid, format) { + return this._types.getTypeParser(oid, format) +} + +Client.prototype.isConnected = function () { + return this._connected +} + +Client.prototype.getTransactionStatus = function () { + return this.native.getTransactionStatus() +} diff --git a/node_modules/pg/lib/native/index.js b/node_modules/pg/lib/native/index.js new file mode 100644 index 0000000..eead422 --- /dev/null +++ b/node_modules/pg/lib/native/index.js @@ -0,0 +1,2 @@ +'use strict' +module.exports = require('./client') diff --git a/node_modules/pg/lib/native/query.js b/node_modules/pg/lib/native/query.js new file mode 100644 index 0000000..e02294f --- /dev/null +++ b/node_modules/pg/lib/native/query.js @@ -0,0 +1,165 @@ +'use strict' + +const EventEmitter = require('events').EventEmitter +const util = require('util') +const utils = require('../utils') + +const NativeQuery = (module.exports = function (config, values, callback) { + EventEmitter.call(this) + config = utils.normalizeQueryConfig(config, values, callback) + this.text = config.text + this.values = config.values + this.name = config.name + this.queryMode = config.queryMode + this.callback = config.callback + this.state = 'new' + this._arrayMode = config.rowMode === 'array' + + // if the 'row' event is listened for + // then emit them as they come in + // without setting singleRowMode to true + // this has almost no meaning because libpq + // reads all rows into memory before returning any + this._emitRowEvents = false + this.on( + 'newListener', + function (event) { + if (event === 'row') this._emitRowEvents = true + }.bind(this) + ) +}) + +util.inherits(NativeQuery, EventEmitter) + +const errorFieldMap = { + sqlState: 'code', + statementPosition: 'position', + messagePrimary: 'message', + context: 'where', + schemaName: 'schema', + tableName: 'table', + columnName: 'column', + dataTypeName: 'dataType', + constraintName: 'constraint', + sourceFile: 'file', + sourceLine: 'line', + sourceFunction: 'routine', +} + +NativeQuery.prototype.handleError = function (err) { + // copy pq error fields into the error object + const fields = this.native.pq.resultErrorFields() + if (fields) { + for (const key in fields) { + const normalizedFieldName = errorFieldMap[key] || key + err[normalizedFieldName] = fields[key] + } + } + if (this.callback) { + this.callback(err) + } else { + this.emit('error', err) + } + this.state = 'error' +} + +NativeQuery.prototype.then = function (onSuccess, onFailure) { + return this._getPromise().then(onSuccess, onFailure) +} + +NativeQuery.prototype.catch = function (callback) { + return this._getPromise().catch(callback) +} + +NativeQuery.prototype._getPromise = function () { + if (this._promise) return this._promise + this._promise = new Promise( + function (resolve, reject) { + this._once('end', resolve) + this._once('error', reject) + }.bind(this) + ) + return this._promise +} + +NativeQuery.prototype.submit = function (client) { + this.state = 'running' + const self = this + this.native = client.native + client.native.arrayMode = this._arrayMode + + let after = function (err, rows, results) { + client.native.arrayMode = false + setImmediate(function () { + self.emit('_done') + }) + + // handle possible query error + if (err) { + return self.handleError(err) + } + + // emit row events for each row in the result + if (self._emitRowEvents) { + if (results.length > 1) { + rows.forEach((rowOfRows, i) => { + rowOfRows.forEach((row) => { + self.emit('row', row, results[i]) + }) + }) + } else { + rows.forEach(function (row) { + self.emit('row', row, results) + }) + } + } + + // handle successful result + self.state = 'end' + self.emit('end', results) + if (self.callback) { + self.callback(null, results) + } + } + + if (process.domain) { + after = process.domain.bind(after) + } + + // named query + if (this.name) { + if (this.name.length > 63) { + console.error('Warning! Postgres only supports 63 characters for query names.') + console.error('You supplied %s (%s)', this.name, this.name.length) + console.error('This can cause conflicts and silent errors executing queries') + } + const values = (this.values || []).map(utils.prepareValue) + + // check if the client has already executed this named query + // if so...just execute it again - skip the planning phase + if (client.namedQueries[this.name]) { + if (this.text && client.namedQueries[this.name] !== this.text) { + const err = new Error(`Prepared statements must be unique - '${this.name}' was used for a different statement`) + return after(err) + } + return client.native.execute(this.name, values, after) + } + // plan the named query the first time, then execute it + return client.native.prepare(this.name, this.text, values.length, function (err) { + if (err) return after(err) + client.namedQueries[self.name] = self.text + return self.native.execute(self.name, values, after) + }) + } else if (this.values) { + if (!Array.isArray(this.values)) { + const err = new Error('Query values must be an array') + return after(err) + } + const vals = this.values.map(utils.prepareValue) + client.native.query(this.text, vals, after) + } else if (this.queryMode === 'extended') { + client.native.query(this.text, [], after) + } else { + client.native.query(this.text, after) + } +} diff --git a/node_modules/pg/lib/query.js b/node_modules/pg/lib/query.js new file mode 100644 index 0000000..64aab5f --- /dev/null +++ b/node_modules/pg/lib/query.js @@ -0,0 +1,252 @@ +'use strict' + +const { EventEmitter } = require('events') + +const Result = require('./result') +const utils = require('./utils') + +class Query extends EventEmitter { + constructor(config, values, callback) { + super() + + config = utils.normalizeQueryConfig(config, values, callback) + + this.text = config.text + this.values = config.values + this.rows = config.rows + this.types = config.types + this.name = config.name + this.queryMode = config.queryMode + this.binary = config.binary + // use unique portal name each time + this.portal = config.portal || '' + this.callback = config.callback + this._rowMode = config.rowMode + if (process.domain && config.callback) { + this.callback = process.domain.bind(config.callback) + } + this._result = new Result(this._rowMode, this.types) + + // potential for multiple results + this._results = this._result + this._canceledDueToError = false + } + + requiresPreparation() { + if (this.queryMode === 'extended') { + return true + } + + // named queries must always be prepared + if (this.name) { + return true + } + // always prepare if there are max number of rows expected per + // portal execution + if (this.rows) { + return true + } + // don't prepare empty text queries + if (!this.text) { + return false + } + // prepare if there are values + if (!this.values) { + return false + } + return this.values.length > 0 + } + + _checkForMultirow() { + // if we already have a result with a command property + // then we've already executed one query in a multi-statement simple query + // turn our results into an array of results + if (this._result.command) { + if (!Array.isArray(this._results)) { + this._results = [this._result] + } + this._result = new Result(this._rowMode, this._result._types) + this._results.push(this._result) + } + } + + // associates row metadata from the supplied + // message with this query object + // metadata used when parsing row results + handleRowDescription(msg) { + this._checkForMultirow() + this._result.addFields(msg.fields) + this._accumulateRows = this.callback || !this.listeners('row').length + } + + handleDataRow(msg) { + let row + + if (this._canceledDueToError) { + return + } + + try { + row = this._result.parseRow(msg.fields) + } catch (err) { + this._canceledDueToError = err + return + } + + this.emit('row', row, this._result) + if (this._accumulateRows) { + this._result.addRow(row) + } + } + + handleCommandComplete(msg, connection) { + this._checkForMultirow() + this._result.addCommandComplete(msg) + // need to sync after each command complete of a prepared statement + // if we were using a row count which results in multiple calls to _getRows + if (this.rows) { + connection.sync() + } + } + + // if a named prepared statement is created with empty query text + // the backend will send an emptyQuery message but *not* a command complete message + // since we pipeline sync immediately after execute we don't need to do anything here + // unless we have rows specified, in which case we did not pipeline the initial sync call + handleEmptyQuery(connection) { + if (this.rows) { + connection.sync() + } + } + + handleError(err, connection) { + // need to sync after error during a prepared statement + if (this._canceledDueToError) { + err = this._canceledDueToError + this._canceledDueToError = false + } + // if callback supplied do not emit error event as uncaught error + // events will bubble up to node process + if (this.callback) { + return this.callback(err) + } + this.emit('error', err) + } + + handleReadyForQuery(con) { + if (this._canceledDueToError) { + return this.handleError(this._canceledDueToError, con) + } + if (this.callback) { + try { + this.callback(null, this._results) + } catch (err) { + process.nextTick(() => { + throw err + }) + } + } + this.emit('end', this._results) + } + + submit(connection) { + if (typeof this.text !== 'string' && typeof this.name !== 'string') { + return new Error('A query must have either text or a name. Supplying neither is unsupported.') + } + const previous = connection.parsedStatements[this.name] + if (this.text && previous && this.text !== previous) { + return new Error(`Prepared statements must be unique - '${this.name}' was used for a different statement`) + } + if (this.values && !Array.isArray(this.values)) { + return new Error('Query values must be an array') + } + if (this.requiresPreparation()) { + // If we're using the extended query protocol we fire off several separate commands + // to the backend. On some versions of node & some operating system versions + // the network stack writes each message separately instead of buffering them together + // causing the client & network to send more slowly. Corking & uncorking the stream + // allows node to buffer up the messages internally before sending them all off at once. + // note: we're checking for existence of cork/uncork because some versions of streams + // might not have this (cloudflare?) + connection.stream.cork && connection.stream.cork() + try { + this.prepare(connection) + } finally { + // while unlikely for this.prepare to throw, if it does & we don't uncork this stream + // this client becomes unresponsive, so put in finally block "just in case" + connection.stream.uncork && connection.stream.uncork() + } + } else { + connection.query(this.text) + } + return null + } + + hasBeenParsed(connection) { + return this.name && connection.parsedStatements[this.name] + } + + handlePortalSuspended(connection) { + this._getRows(connection, this.rows) + } + + _getRows(connection, rows) { + connection.execute({ + portal: this.portal, + rows: rows, + }) + // if we're not reading pages of rows send the sync command + // to indicate the pipeline is finished + if (!rows) { + connection.sync() + } else { + // otherwise flush the call out to read more rows + connection.flush() + } + } + + // http://developer.postgresql.org/pgdocs/postgres/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY + prepare(connection) { + // TODO refactor this poor encapsulation + if (!this.hasBeenParsed(connection)) { + connection.parse({ + text: this.text, + name: this.name, + types: this.types, + }) + } + + // because we're mapping user supplied values to + // postgres wire protocol compatible values it could + // throw an exception, so try/catch this section + try { + connection.bind({ + portal: this.portal, + statement: this.name, + values: this.values, + binary: this.binary, + valueMapper: utils.prepareValue, + }) + } catch (err) { + this.handleError(err, connection) + return + } + + connection.describe({ + type: 'P', + name: this.portal || '', + }) + + this._getRows(connection, this.rows) + } + + handleCopyInResponse(connection) { + connection.sendCopyFail('No source stream defined') + } + + handleCopyData(msg, connection) { + // noop + } +} + +module.exports = Query diff --git a/node_modules/pg/lib/result.js b/node_modules/pg/lib/result.js new file mode 100644 index 0000000..329fbf9 --- /dev/null +++ b/node_modules/pg/lib/result.js @@ -0,0 +1,109 @@ +'use strict' + +const types = require('pg-types') + +const matchRegexp = /^([A-Za-z]+)(?: (\d+))?(?: (\d+))?/ + +// result object returned from query +// in the 'end' event and also +// passed as second argument to provided callback +class Result { + constructor(rowMode, types) { + this.command = null + this.rowCount = null + this.oid = null + this.rows = [] + this.fields = [] + this._parsers = undefined + this._types = types + this.RowCtor = null + this.rowAsArray = rowMode === 'array' + if (this.rowAsArray) { + this.parseRow = this._parseRowAsArray + } + this._prebuiltEmptyResultObject = null + } + + // adds a command complete message + addCommandComplete(msg) { + let match + if (msg.text) { + // pure javascript + match = matchRegexp.exec(msg.text) + } else { + // native bindings + match = matchRegexp.exec(msg.command) + } + if (match) { + this.command = match[1] + if (match[3]) { + // COMMAND OID ROWS + this.oid = parseInt(match[2], 10) + this.rowCount = parseInt(match[3], 10) + } else if (match[2]) { + // COMMAND ROWS + this.rowCount = parseInt(match[2], 10) + } + } + } + + _parseRowAsArray(rowData) { + const row = new Array(rowData.length) + for (let i = 0, len = rowData.length; i < len; i++) { + const rawValue = rowData[i] + if (rawValue !== null) { + row[i] = this._parsers[i](rawValue) + } else { + row[i] = null + } + } + return row + } + + parseRow(rowData) { + const row = { ...this._prebuiltEmptyResultObject } + for (let i = 0, len = rowData.length; i < len; i++) { + const rawValue = rowData[i] + const field = this.fields[i].name + if (rawValue !== null) { + const v = this.fields[i].format === 'binary' ? Buffer.from(rawValue) : rawValue + row[field] = this._parsers[i](v) + } else { + row[field] = null + } + } + return row + } + + addRow(row) { + this.rows.push(row) + } + + addFields(fieldDescriptions) { + // clears field definitions + // multiple query statements in 1 action can result in multiple sets + // of rowDescriptions...eg: 'select NOW(); select 1::int;' + // you need to reset the fields + this.fields = fieldDescriptions + if (this.fields.length) { + this._parsers = new Array(fieldDescriptions.length) + } + + const row = Object.create(null) + + for (let i = 0; i < fieldDescriptions.length; i++) { + const desc = fieldDescriptions[i] + row[desc.name] = null + + if (this._types) { + this._parsers[i] = this._types.getTypeParser(desc.dataTypeID, desc.format || 'text') + } else { + this._parsers[i] = types.getTypeParser(desc.dataTypeID, desc.format || 'text') + } + } + + this._prebuiltEmptyResultObject = { ...row } + } +} + +module.exports = Result diff --git a/node_modules/pg/lib/stream.js b/node_modules/pg/lib/stream.js new file mode 100644 index 0000000..edc3018 --- /dev/null +++ b/node_modules/pg/lib/stream.js @@ -0,0 +1,83 @@ +const { getStream, getSecureStream } = getStreamFuncs() + +module.exports = { + /** + * Get a socket stream compatible with the current runtime environment. + * @returns {Duplex} + */ + getStream, + /** + * Get a TLS secured socket, compatible with the current environment, + * using the socket and other settings given in `options`. + * @returns {Duplex} + */ + getSecureStream, +} + +/** + * The stream functions that work in Node.js + */ +function getNodejsStreamFuncs() { + function getStream(ssl) { + const net = require('net') + return new net.Socket() + } + + function getSecureStream(options) { + const tls = require('tls') + return tls.connect(options) + } + return { + getStream, + getSecureStream, + } +} + +/** + * The stream functions that work in Cloudflare Workers + */ +function getCloudflareStreamFuncs() { + function getStream(ssl) { + const { CloudflareSocket } = require('pg-cloudflare') + return new CloudflareSocket(ssl) + } + + function getSecureStream(options) { + options.socket.startTls(options) + return options.socket + } + return { + getStream, + getSecureStream, + } +} + +/** + * Are we running in a Cloudflare Worker? + * + * @returns true if the code is currently running inside a Cloudflare Worker. + */ +function isCloudflareRuntime() { + // Since 2022-03-21 the `global_navigator` compatibility flag is on for Cloudflare Workers + // which means that `navigator.userAgent` will be defined. + // eslint-disable-next-line no-undef + if (typeof navigator === 'object' && navigator !== null && typeof navigator.userAgent === 'string') { + // eslint-disable-next-line no-undef + return navigator.userAgent === 'Cloudflare-Workers' + } + // In case `navigator` or `navigator.userAgent` is not defined then try a more sneaky approach + if (typeof Response === 'function') { + const resp = new Response(null, { cf: { thing: true } }) + if (typeof resp.cf === 'object' && resp.cf !== null && resp.cf.thing) { + return true + } + } + return false +} + +function getStreamFuncs() { + if (isCloudflareRuntime()) { + return getCloudflareStreamFuncs() + } + return getNodejsStreamFuncs() +} diff --git a/node_modules/pg/lib/type-overrides.js b/node_modules/pg/lib/type-overrides.js new file mode 100644 index 0000000..9d219e5 --- /dev/null +++ b/node_modules/pg/lib/type-overrides.js @@ -0,0 +1,35 @@ +'use strict' + +const types = require('pg-types') + +function TypeOverrides(userTypes) { + this._types = userTypes || types + this.text = {} + this.binary = {} +} + +TypeOverrides.prototype.getOverrides = function (format) { + switch (format) { + case 'text': + return this.text + case 'binary': + return this.binary + default: + return {} + } +} + +TypeOverrides.prototype.setTypeParser = function (oid, format, parseFn) { + if (typeof format === 'function') { + parseFn = format + format = 'text' + } + this.getOverrides(format)[oid] = parseFn +} + +TypeOverrides.prototype.getTypeParser = function (oid, format) { + format = format || 'text' + return this.getOverrides(format)[oid] || this._types.getTypeParser(oid, format) +} + +module.exports = TypeOverrides diff --git a/node_modules/pg/lib/utils.js b/node_modules/pg/lib/utils.js new file mode 100644 index 0000000..638b439 --- /dev/null +++ b/node_modules/pg/lib/utils.js @@ -0,0 +1,207 @@ +'use strict' + +const defaults = require('./defaults') + +const { isDate } = require('util/types') + +function escapeElement(elementRepresentation) { + const escaped = elementRepresentation.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + + return '"' + escaped + '"' +} + +// convert a JS array to a postgres array literal +// uses comma separator so won't work for types like box that use +// a different array separator. +function arrayString(val) { + let result = '{' + for (let i = 0; i < val.length; i++) { + if (i > 0) { + result += ',' + } + let item = val[i] + if (item == null) { + result += 'NULL' + } else if (Array.isArray(item)) { + result += arrayString(item) + } else if (ArrayBuffer.isView(item)) { + if (!(item instanceof Buffer)) { + item = Buffer.from(item.buffer, item.byteOffset, item.byteLength) + } + result += '\\\\x' + item.toString('hex') + } else { + result += escapeElement(prepareValue(item)) + } + } + result += '}' + return result +} + +// converts values from javascript types +// to their 'raw' counterparts for use as a postgres parameter +// note: you can override this function to provide your own conversion mechanism +// for complex types, etc... +const prepareValue = function (val, seen) { + // null and undefined are both null for postgres + if (val == null) { + return null + } + if (typeof val === 'object') { + if (val instanceof Buffer) { + return val + } + if (ArrayBuffer.isView(val)) { + return Buffer.from(val.buffer, val.byteOffset, val.byteLength) + } + if (isDate(val)) { + if (defaults.parseInputDatesAsUTC) { + return dateToStringUTC(val) + } else { + return dateToString(val) + } + } + if (Array.isArray(val)) { + return arrayString(val) + } + + return prepareObject(val, seen) + } + return val.toString() +} + +function prepareObject(val, seen) { + if (val && typeof val.toPostgres === 'function') { + seen = seen || [] + if (seen.indexOf(val) !== -1) { + throw new Error('circular reference detected while preparing "' + val + '" for query') + } + seen.push(val) + + return prepareValue(val.toPostgres(prepareValue), seen) + } + return JSON.stringify(val) +} + +function dateToString(date) { + let offset = -date.getTimezoneOffset() + + let year = date.getFullYear() + const isBCYear = year < 1 + if (isBCYear) year = Math.abs(year) + 1 // negative years are 1 off their BC representation + + let ret = + String(year).padStart(4, '0') + + '-' + + String(date.getMonth() + 1).padStart(2, '0') + + '-' + + String(date.getDate()).padStart(2, '0') + + 'T' + + String(date.getHours()).padStart(2, '0') + + ':' + + String(date.getMinutes()).padStart(2, '0') + + ':' + + String(date.getSeconds()).padStart(2, '0') + + '.' + + String(date.getMilliseconds()).padStart(3, '0') + + if (offset < 0) { + ret += '-' + offset *= -1 + } else { + ret += '+' + } + + ret += String(Math.floor(offset / 60)).padStart(2, '0') + ':' + String(offset % 60).padStart(2, '0') + if (isBCYear) ret += ' BC' + return ret +} + +function dateToStringUTC(date) { + let year = date.getUTCFullYear() + const isBCYear = year < 1 + if (isBCYear) year = Math.abs(year) + 1 // negative years are 1 off their BC representation + + let ret = + String(year).padStart(4, '0') + + '-' + + String(date.getUTCMonth() + 1).padStart(2, '0') + + '-' + + String(date.getUTCDate()).padStart(2, '0') + + 'T' + + String(date.getUTCHours()).padStart(2, '0') + + ':' + + String(date.getUTCMinutes()).padStart(2, '0') + + ':' + + String(date.getUTCSeconds()).padStart(2, '0') + + '.' + + String(date.getUTCMilliseconds()).padStart(3, '0') + + ret += '+00:00' + if (isBCYear) ret += ' BC' + return ret +} + +function normalizeQueryConfig(config, values, callback) { + // can take in strings or config objects + config = typeof config === 'string' ? { text: config } : config + if (values) { + if (typeof values === 'function') { + config.callback = values + } else { + config.values = values + } + } + if (callback) { + config.callback = callback + } + return config +} + +// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c +const escapeIdentifier = function (str) { + return '"' + str.replace(/"/g, '""') + '"' +} + +const escapeLiteral = function (str) { + let hasBackslash = false + let escaped = "'" + + if (str == null) { + return "''" + } + + if (typeof str !== 'string') { + return "''" + } + + for (let i = 0; i < str.length; i++) { + const c = str[i] + if (c === "'") { + escaped += c + c + } else if (c === '\\') { + escaped += c + c + hasBackslash = true + } else { + escaped += c + } + } + + escaped += "'" + + if (hasBackslash === true) { + escaped = ' E' + escaped + } + + return escaped +} + +module.exports = { + prepareValue: function prepareValueWrapper(value) { + // this ensures that extra arguments do not get passed into prepareValue + // by accident, eg: from calling values.map(utils.prepareValue) + return prepareValue(value) + }, + normalizeQueryConfig, + escapeIdentifier, + escapeLiteral, +} diff --git a/node_modules/pg/package.json b/node_modules/pg/package.json new file mode 100644 index 0000000..43c64d7 --- /dev/null +++ b/node_modules/pg/package.json @@ -0,0 +1,76 @@ +{ + "name": "pg", + "version": "8.21.0", + "description": "PostgreSQL client - pure javascript & libpq with the same API", + "keywords": [ + "database", + "libpq", + "pg", + "postgre", + "postgres", + "postgresql", + "rdbms" + ], + "homepage": "https://github.com/brianc/node-postgres", + "repository": { + "type": "git", + "url": "git://github.com/brianc/node-postgres.git", + "directory": "packages/pg" + }, + "author": "Brian Carlson ", + "main": "./lib", + "exports": { + ".": { + "import": "./esm/index.mjs", + "require": "./lib/index.js", + "default": "./lib/index.js" + }, + "./package.json": { + "default": "./package.json" + }, + "./lib/*": "./lib/*.js", + "./lib/*.js": "./lib/*.js" + }, + "dependencies": { + "pg-connection-string": "^2.13.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.14.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "devDependencies": { + "@cloudflare/vitest-pool-workers": "0.8.23", + "@cloudflare/workers-types": "^4.20230404.0", + "async": "2.6.4", + "bluebird": "3.7.2", + "co": "4.6.0", + "pg-copy-streams": "0.3.0", + "typescript": "^6.0.3", + "vitest": "~3.0.9", + "wrangler": "^3.x" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + }, + "scripts": { + "test": "make test-all" + }, + "files": [ + "lib", + "esm", + "SPONSORS.md" + ], + "license": "MIT", + "engines": { + "node": ">= 16.0.0" + }, + "gitHead": "544b1ce8152bc280e398dc1e8a66920abe6a640e" +} diff --git a/node_modules/pgpass/README.md b/node_modules/pgpass/README.md new file mode 100644 index 0000000..bbc5193 --- /dev/null +++ b/node_modules/pgpass/README.md @@ -0,0 +1,74 @@ +# pgpass + +[![Build Status](https://github.com/hoegaarden/pgpass/workflows/CI/badge.svg?branch=master)](https://github.com/hoegaarden/pgpass/actions?query=workflow%3ACI+branch%3Amaster) + +## Install + +```sh +npm install pgpass +``` + +## Usage +```js +var pgPass = require('pgpass'); + +var connInfo = { + 'host' : 'pgserver' , + 'user' : 'the_user_name' , +}; + +pgPass(connInfo, function(pass){ + conn_info.password = pass; + // connect to postgresql server +}); +``` + +## Description + +This module tries to read the `~/.pgpass` file (or the equivalent for windows systems). If the environment variable `PGPASSFILE` is set, this file is used instead. If everything goes right, the password from said file is passed to the callback; if the password cannot be read `undefined` is passed to the callback. + +Cases where `undefined` is returned: + +- the environment variable `PGPASSWORD` is set +- the file cannot be read (wrong permissions, no such file, ...) +- for non windows systems: the file is write-/readable by the group or by other users +- there is no matching line for the given connection info + +There should be no need to use this module directly; it is already included in `node-postgres`. + +## Configuration + +The module reads the environment variable `PGPASS_NO_DEESCAPE` to decide if the the read tokens from the password file should be de-escaped or not. Default is to do de-escaping. For further information on this see [this commit](https://github.com/postgres/postgres/commit/8d15e3ec4fcb735875a8a70a09ec0c62153c3329). + + +## Tests + +There are tests in `./test/`; including linting and coverage testing. Running `npm test` runs: + +- `jshint` +- `mocha` tests +- `jscoverage` and `mocha -R html-cov` + +You can see the coverage report in `coverage.html`. + + +## Development, Patches, Bugs, ... + +If you find Bugs or have improvements, please feel free to open a issue on GitHub. If you provide a pull request, I'm more than happy to merge them, just make sure to add tests for your changes. + +## Links + +- https://github.com/hoegaarden/node-pgpass +- http://www.postgresql.org/docs/current/static/libpq-pgpass.html +- https://wiki.postgresql.org/wiki/Pgpass +- https://github.com/postgres/postgres/blob/master/src/interfaces/libpq/fe-connect.c + +## License + +Copyright (c) 2013-2016 Hannes Hörl + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/pgpass/lib/helper.js b/node_modules/pgpass/lib/helper.js new file mode 100644 index 0000000..f988460 --- /dev/null +++ b/node_modules/pgpass/lib/helper.js @@ -0,0 +1,233 @@ +'use strict'; + +var path = require('path') + , Stream = require('stream').Stream + , split = require('split2') + , util = require('util') + , defaultPort = 5432 + , isWin = (process.platform === 'win32') + , warnStream = process.stderr +; + + +var S_IRWXG = 56 // 00070(8) + , S_IRWXO = 7 // 00007(8) + , S_IFMT = 61440 // 00170000(8) + , S_IFREG = 32768 // 0100000(8) +; +function isRegFile(mode) { + return ((mode & S_IFMT) == S_IFREG); +} + +var fieldNames = [ 'host', 'port', 'database', 'user', 'password' ]; +var nrOfFields = fieldNames.length; +var passKey = fieldNames[ nrOfFields -1 ]; + + +function warn() { + var isWritable = ( + warnStream instanceof Stream && + true === warnStream.writable + ); + + if (isWritable) { + var args = Array.prototype.slice.call(arguments).concat("\n"); + warnStream.write( util.format.apply(util, args) ); + } +} + + +Object.defineProperty(module.exports, 'isWin', { + get : function() { + return isWin; + } , + set : function(val) { + isWin = val; + } +}); + + +module.exports.warnTo = function(stream) { + var old = warnStream; + warnStream = stream; + return old; +}; + +module.exports.getFileName = function(rawEnv){ + var env = rawEnv || process.env; + var file = env.PGPASSFILE || ( + isWin ? + path.join( env.APPDATA || './' , 'postgresql', 'pgpass.conf' ) : + path.join( env.HOME || './', '.pgpass' ) + ); + return file; +}; + +module.exports.usePgPass = function(stats, fname) { + if (Object.prototype.hasOwnProperty.call(process.env, 'PGPASSWORD')) { + return false; + } + + if (isWin) { + return true; + } + + fname = fname || ''; + + if (! isRegFile(stats.mode)) { + warn('WARNING: password file "%s" is not a plain file', fname); + return false; + } + + if (stats.mode & (S_IRWXG | S_IRWXO)) { + /* If password file is insecure, alert the user and ignore it. */ + warn('WARNING: password file "%s" has group or world access; permissions should be u=rw (0600) or less', fname); + return false; + } + + return true; +}; + + +var matcher = module.exports.match = function(connInfo, entry) { + return fieldNames.slice(0, -1).reduce(function(prev, field, idx){ + if (idx == 1) { + // the port + if ( Number( connInfo[field] || defaultPort ) === Number( entry[field] ) ) { + return prev && true; + } + } + return prev && ( + entry[field] === '*' || + entry[field] === connInfo[field] + ); + }, true); +}; + + +module.exports.getPassword = function(connInfo, stream, cb) { + var pass; + var lineStream = stream.pipe(split()); + + function onLine(line) { + var entry = parseLine(line); + if (entry && isValidEntry(entry) && matcher(connInfo, entry)) { + pass = entry[passKey]; + lineStream.end(); // -> calls onEnd(), but pass is set now + } + } + + var onEnd = function() { + stream.destroy(); + cb(pass); + }; + + var onErr = function(err) { + stream.destroy(); + warn('WARNING: error on reading file: %s', err); + cb(undefined); + }; + + stream.on('error', onErr); + lineStream + .on('data', onLine) + .on('end', onEnd) + .on('error', onErr) + ; + +}; + + +var parseLine = module.exports.parseLine = function(line) { + if (line.length < 11 || line.match(/^\s+#/)) { + return null; + } + + var curChar = ''; + var prevChar = ''; + var fieldIdx = 0; + var startIdx = 0; + var endIdx = 0; + var obj = {}; + var isLastField = false; + var addToObj = function(idx, i0, i1) { + var field = line.substring(i0, i1); + + if (! Object.hasOwnProperty.call(process.env, 'PGPASS_NO_DEESCAPE')) { + field = field.replace(/\\([:\\])/g, '$1'); + } + + obj[ fieldNames[idx] ] = field; + }; + + for (var i = 0 ; i < line.length-1 ; i += 1) { + curChar = line.charAt(i+1); + prevChar = line.charAt(i); + + isLastField = (fieldIdx == nrOfFields-1); + + if (isLastField) { + addToObj(fieldIdx, startIdx); + break; + } + + if (i >= 0 && curChar == ':' && prevChar !== '\\') { + addToObj(fieldIdx, startIdx, i+1); + + startIdx = i+2; + fieldIdx += 1; + } + } + + obj = ( Object.keys(obj).length === nrOfFields ) ? obj : null; + + return obj; +}; + + +var isValidEntry = module.exports.isValidEntry = function(entry){ + var rules = { + // host + 0 : function(x){ + return x.length > 0; + } , + // port + 1 : function(x){ + if (x === '*') { + return true; + } + x = Number(x); + return ( + isFinite(x) && + x > 0 && + x < 9007199254740992 && + Math.floor(x) === x + ); + } , + // database + 2 : function(x){ + return x.length > 0; + } , + // username + 3 : function(x){ + return x.length > 0; + } , + // password + 4 : function(x){ + return x.length > 0; + } + }; + + for (var idx = 0 ; idx < fieldNames.length ; idx += 1) { + var rule = rules[idx]; + var value = entry[ fieldNames[idx] ] || ''; + + var res = rule(value); + if (!res) { + return false; + } + } + + return true; +}; + diff --git a/node_modules/pgpass/lib/index.js b/node_modules/pgpass/lib/index.js new file mode 100644 index 0000000..ecfcf30 --- /dev/null +++ b/node_modules/pgpass/lib/index.js @@ -0,0 +1,23 @@ +'use strict'; + +var path = require('path') + , fs = require('fs') + , helper = require('./helper.js') +; + + +module.exports = function(connInfo, cb) { + var file = helper.getFileName(); + + fs.stat(file, function(err, stat){ + if (err || !helper.usePgPass(stat, file)) { + return cb(undefined); + } + + var st = fs.createReadStream(file); + + helper.getPassword(connInfo, st, cb); + }); +}; + +module.exports.warnTo = helper.warnTo; diff --git a/node_modules/pgpass/package.json b/node_modules/pgpass/package.json new file mode 100644 index 0000000..22bfe84 --- /dev/null +++ b/node_modules/pgpass/package.json @@ -0,0 +1,41 @@ +{ + "name": "pgpass", + "version": "1.0.5", + "description": "Module for reading .pgpass", + "main": "lib/index", + "scripts": { + "pretest": "chmod 600 ./test/_pgpass", + "_hint": "jshint --exclude node_modules --verbose lib test", + "_test": "mocha --recursive -R list", + "_covered_test": "nyc --reporter html --reporter text \"$npm_execpath\" run _test", + "test": "\"$npm_execpath\" run _hint && \"$npm_execpath\" run _covered_test" + }, + "author": "Hannes Hörl ", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + }, + "devDependencies": { + "jshint": "^2.12.0", + "mocha": "^8.2.0", + "nyc": "^15.1.0", + "pg": "^8.4.1", + "pg-escape": "^0.2.0", + "pg-native": "3.0.0", + "resumer": "0.0.0", + "tmp": "^0.2.1", + "which": "^2.0.2" + }, + "keywords": [ + "postgres", + "pg", + "pgpass", + "password", + "postgresql" + ], + "bugs": "https://github.com/hoegaarden/pgpass/issues", + "repository": { + "type": "git", + "url": "https://github.com/hoegaarden/pgpass.git" + } +} diff --git a/node_modules/postgres-array/index.d.ts b/node_modules/postgres-array/index.d.ts new file mode 100644 index 0000000..88665bd --- /dev/null +++ b/node_modules/postgres-array/index.d.ts @@ -0,0 +1,4 @@ + +export function parse(source: string): string[]; +export function parse(source: string, transform: (value: string) => T): T[]; + diff --git a/node_modules/postgres-array/index.js b/node_modules/postgres-array/index.js new file mode 100644 index 0000000..18bfd16 --- /dev/null +++ b/node_modules/postgres-array/index.js @@ -0,0 +1,97 @@ +'use strict' + +exports.parse = function (source, transform) { + return new ArrayParser(source, transform).parse() +} + +class ArrayParser { + constructor (source, transform) { + this.source = source + this.transform = transform || identity + this.position = 0 + this.entries = [] + this.recorded = [] + this.dimension = 0 + } + + isEof () { + return this.position >= this.source.length + } + + nextCharacter () { + var character = this.source[this.position++] + if (character === '\\') { + return { + value: this.source[this.position++], + escaped: true + } + } + return { + value: character, + escaped: false + } + } + + record (character) { + this.recorded.push(character) + } + + newEntry (includeEmpty) { + var entry + if (this.recorded.length > 0 || includeEmpty) { + entry = this.recorded.join('') + if (entry === 'NULL' && !includeEmpty) { + entry = null + } + if (entry !== null) entry = this.transform(entry) + this.entries.push(entry) + this.recorded = [] + } + } + + consumeDimensions () { + if (this.source[0] === '[') { + while (!this.isEof()) { + var char = this.nextCharacter() + if (char.value === '=') break + } + } + } + + parse (nested) { + var character, parser, quote + this.consumeDimensions() + while (!this.isEof()) { + character = this.nextCharacter() + if (character.value === '{' && !quote) { + this.dimension++ + if (this.dimension > 1) { + parser = new ArrayParser(this.source.substr(this.position - 1), this.transform) + this.entries.push(parser.parse(true)) + this.position += parser.position - 2 + } + } else if (character.value === '}' && !quote) { + this.dimension-- + if (!this.dimension) { + this.newEntry() + if (nested) return this.entries + } + } else if (character.value === '"' && !character.escaped) { + if (quote) this.newEntry(true) + quote = !quote + } else if (character.value === ',' && !quote) { + this.newEntry() + } else { + this.record(character.value) + } + } + if (this.dimension !== 0) { + throw new Error('array dimension not balanced') + } + return this.entries + } +} + +function identity (value) { + return value +} diff --git a/node_modules/postgres-array/license b/node_modules/postgres-array/license new file mode 100644 index 0000000..25c6247 --- /dev/null +++ b/node_modules/postgres-array/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Ben Drucker (bendrucker.me) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/postgres-array/package.json b/node_modules/postgres-array/package.json new file mode 100644 index 0000000..d6aa94e --- /dev/null +++ b/node_modules/postgres-array/package.json @@ -0,0 +1,35 @@ +{ + "name": "postgres-array", + "main": "index.js", + "version": "2.0.0", + "description": "Parse postgres array columns", + "license": "MIT", + "repository": "bendrucker/postgres-array", + "author": { + "name": "Ben Drucker", + "email": "bvdrucker@gmail.com", + "url": "bendrucker.me" + }, + "engines": { + "node": ">=4" + }, + "scripts": { + "test": "standard && tape test.js" + }, + "types": "index.d.ts", + "keywords": [ + "postgres", + "array", + "parser" + ], + "dependencies": {}, + "devDependencies": { + "standard": "^12.0.1", + "tape": "^4.0.0" + }, + "files": [ + "index.js", + "index.d.ts", + "readme.md" + ] +} diff --git a/node_modules/postgres-array/readme.md b/node_modules/postgres-array/readme.md new file mode 100644 index 0000000..b74b369 --- /dev/null +++ b/node_modules/postgres-array/readme.md @@ -0,0 +1,43 @@ +# postgres-array [![Build Status](https://travis-ci.org/bendrucker/postgres-array.svg?branch=master)](https://travis-ci.org/bendrucker/postgres-array) + +> Parse postgres array columns + + +## Install + +``` +$ npm install --save postgres-array +``` + + +## Usage + +```js +var postgresArray = require('postgres-array') + +postgresArray.parse('{1,2,3}', (value) => parseInt(value, 10)) +//=> [1, 2, 3] +``` + +## API + +#### `parse(input, [transform])` -> `array` + +##### input + +*Required* +Type: `string` + +A Postgres array string. + +##### transform + +Type: `function` +Default: `identity` + +A function that transforms non-null values inserted into the array. + + +## License + +MIT © [Ben Drucker](http://bendrucker.me) diff --git a/node_modules/postgres-bytea/index.js b/node_modules/postgres-bytea/index.js new file mode 100644 index 0000000..b0dc4c7 --- /dev/null +++ b/node_modules/postgres-bytea/index.js @@ -0,0 +1,33 @@ +'use strict' + +var bufferFrom = Buffer.from || Buffer + +module.exports = function parseBytea (input) { + if (/^\\x/.test(input)) { + // new 'hex' style response (pg >9.0) + return bufferFrom(input.substr(2), 'hex') + } + var output = '' + var i = 0 + while (i < input.length) { + if (input[i] !== '\\') { + output += input[i] + ++i + } else { + if (/[0-7]{3}/.test(input.substr(i + 1, 3))) { + output += String.fromCharCode(parseInt(input.substr(i + 1, 3), 8)) + i += 4 + } else { + var backslashes = 1 + while (i + backslashes < input.length && input[i + backslashes] === '\\') { + backslashes++ + } + for (var k = 0; k < Math.floor(backslashes / 2); ++k) { + output += '\\' + } + i += Math.floor(backslashes / 2) * 2 + } + } + } + return bufferFrom(output, 'binary') +} diff --git a/node_modules/postgres-bytea/license b/node_modules/postgres-bytea/license new file mode 100644 index 0000000..25c6247 --- /dev/null +++ b/node_modules/postgres-bytea/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Ben Drucker (bendrucker.me) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/postgres-bytea/package.json b/node_modules/postgres-bytea/package.json new file mode 100644 index 0000000..344b9a0 --- /dev/null +++ b/node_modules/postgres-bytea/package.json @@ -0,0 +1,34 @@ +{ + "name": "postgres-bytea", + "main": "index.js", + "version": "1.0.1", + "description": "Postgres bytea parser", + "license": "MIT", + "repository": "bendrucker/postgres-bytea", + "author": { + "name": "Ben Drucker", + "email": "bvdrucker@gmail.com", + "url": "bendrucker.me" + }, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "standard && tape test.js" + }, + "keywords": [ + "bytea", + "postgres", + "binary", + "parser" + ], + "dependencies": {}, + "devDependencies": { + "tape": "^4.0.0", + "standard": "^4.0.0" + }, + "files": [ + "index.js", + "readme.md" + ] +} diff --git a/node_modules/postgres-bytea/readme.md b/node_modules/postgres-bytea/readme.md new file mode 100644 index 0000000..4939c3b --- /dev/null +++ b/node_modules/postgres-bytea/readme.md @@ -0,0 +1,34 @@ +# postgres-bytea [![Build Status](https://travis-ci.org/bendrucker/postgres-bytea.svg?branch=master)](https://travis-ci.org/bendrucker/postgres-bytea) + +> Postgres bytea parser + + +## Install + +``` +$ npm install --save postgres-bytea +``` + + +## Usage + +```js +var bytea = require('postgres-bytea'); +bytea('\\000\\100\\200') +//=> buffer +``` + +## API + +#### `bytea(input)` -> `buffer` + +##### input + +*Required* +Type: `string` + +A Postgres bytea binary string. + +## License + +MIT © [Ben Drucker](http://bendrucker.me) diff --git a/node_modules/postgres-date/index.js b/node_modules/postgres-date/index.js new file mode 100644 index 0000000..5dc73fb --- /dev/null +++ b/node_modules/postgres-date/index.js @@ -0,0 +1,116 @@ +'use strict' + +var DATE_TIME = /(\d{1,})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?.*?( BC)?$/ +var DATE = /^(\d{1,})-(\d{2})-(\d{2})( BC)?$/ +var TIME_ZONE = /([Z+-])(\d{2})?:?(\d{2})?:?(\d{2})?/ +var INFINITY = /^-?infinity$/ + +module.exports = function parseDate (isoDate) { + if (INFINITY.test(isoDate)) { + // Capitalize to Infinity before passing to Number + return Number(isoDate.replace('i', 'I')) + } + var matches = DATE_TIME.exec(isoDate) + + if (!matches) { + // Force YYYY-MM-DD dates to be parsed as local time + return getDate(isoDate) || null + } + + var isBC = !!matches[8] + var year = parseInt(matches[1], 10) + if (isBC) { + year = bcYearToNegativeYear(year) + } + + var month = parseInt(matches[2], 10) - 1 + var day = matches[3] + var hour = parseInt(matches[4], 10) + var minute = parseInt(matches[5], 10) + var second = parseInt(matches[6], 10) + + var ms = matches[7] + ms = ms ? 1000 * parseFloat(ms) : 0 + + var date + var offset = timeZoneOffset(isoDate) + if (offset != null) { + date = new Date(Date.UTC(year, month, day, hour, minute, second, ms)) + + // Account for years from 0 to 99 being interpreted as 1900-1999 + // by Date.UTC / the multi-argument form of the Date constructor + if (is0To99(year)) { + date.setUTCFullYear(year) + } + + if (offset !== 0) { + date.setTime(date.getTime() - offset) + } + } else { + date = new Date(year, month, day, hour, minute, second, ms) + + if (is0To99(year)) { + date.setFullYear(year) + } + } + + return date +} + +function getDate (isoDate) { + var matches = DATE.exec(isoDate) + if (!matches) { + return + } + + var year = parseInt(matches[1], 10) + var isBC = !!matches[4] + if (isBC) { + year = bcYearToNegativeYear(year) + } + + var month = parseInt(matches[2], 10) - 1 + var day = matches[3] + // YYYY-MM-DD will be parsed as local time + var date = new Date(year, month, day) + + if (is0To99(year)) { + date.setFullYear(year) + } + + return date +} + +// match timezones: +// Z (UTC) +// -05 +// +06:30 +function timeZoneOffset (isoDate) { + if (isoDate.endsWith('+00')) { + return 0 + } + + var zone = TIME_ZONE.exec(isoDate.split(' ')[1]) + if (!zone) return + var type = zone[1] + + if (type === 'Z') { + return 0 + } + var sign = type === '-' ? -1 : 1 + var offset = parseInt(zone[2], 10) * 3600 + + parseInt(zone[3] || 0, 10) * 60 + + parseInt(zone[4] || 0, 10) + + return offset * sign * 1000 +} + +function bcYearToNegativeYear (year) { + // Account for numerical difference between representations of BC years + // See: https://github.com/bendrucker/postgres-date/issues/5 + return -(year - 1) +} + +function is0To99 (num) { + return num >= 0 && num < 100 +} diff --git a/node_modules/postgres-date/license b/node_modules/postgres-date/license new file mode 100644 index 0000000..25c6247 --- /dev/null +++ b/node_modules/postgres-date/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Ben Drucker (bendrucker.me) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/postgres-date/package.json b/node_modules/postgres-date/package.json new file mode 100644 index 0000000..6fddec7 --- /dev/null +++ b/node_modules/postgres-date/package.json @@ -0,0 +1,33 @@ +{ + "name": "postgres-date", + "main": "index.js", + "version": "1.0.7", + "description": "Postgres date column parser", + "license": "MIT", + "repository": "bendrucker/postgres-date", + "author": { + "name": "Ben Drucker", + "email": "bvdrucker@gmail.com", + "url": "bendrucker.me" + }, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "standard && tape test.js" + }, + "keywords": [ + "postgres", + "date", + "parser" + ], + "dependencies": {}, + "devDependencies": { + "standard": "^14.0.0", + "tape": "^5.0.0" + }, + "files": [ + "index.js", + "readme.md" + ] +} diff --git a/node_modules/postgres-date/readme.md b/node_modules/postgres-date/readme.md new file mode 100644 index 0000000..095431a --- /dev/null +++ b/node_modules/postgres-date/readme.md @@ -0,0 +1,49 @@ +# postgres-date [![Build Status](https://travis-ci.org/bendrucker/postgres-date.svg?branch=master)](https://travis-ci.org/bendrucker/postgres-date) [![Greenkeeper badge](https://badges.greenkeeper.io/bendrucker/postgres-date.svg)](https://greenkeeper.io/) + +> Postgres date output parser + +This package parses [date/time outputs](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT) from Postgres into Javascript `Date` objects. Its goal is to match Postgres behavior and preserve data accuracy. + +If you find a case where a valid Postgres output results in incorrect parsing (including loss of precision), please [create a pull request](https://github.com/bendrucker/postgres-date/compare) and provide a failing test. + +**Supported Postgres Versions:** `>= 9.6` + +All prior versions of Postgres are likely compatible but not officially supported. + +## Install + +``` +$ npm install --save postgres-date +``` + + +## Usage + +```js +var parse = require('postgres-date') +parse('2011-01-23 22:15:51Z') +// => 2011-01-23T22:15:51.000Z +``` + +## API + +#### `parse(isoDate)` -> `date` + +##### isoDate + +*Required* +Type: `string` + +A date string from Postgres. + +## Releases + +The following semantic versioning increments will be used for changes: + +* **Major**: Removal of support for Node.js versions or Postgres versions (not expected) +* **Minor**: Unused, since Postgres returns dates in standard ISO 8601 format +* **Patch**: Any fix for parsing behavior + +## License + +MIT © [Ben Drucker](http://bendrucker.me) diff --git a/node_modules/postgres-interval/index.d.ts b/node_modules/postgres-interval/index.d.ts new file mode 100644 index 0000000..f82b4c3 --- /dev/null +++ b/node_modules/postgres-interval/index.d.ts @@ -0,0 +1,20 @@ +declare namespace PostgresInterval { + export interface IPostgresInterval { + years?: number; + months?: number; + days?: number; + hours?: number; + minutes?: number; + seconds?: number; + milliseconds?: number; + + toPostgres(): string; + + toISO(): string; + toISOString(): string; + } +} + +declare function PostgresInterval(raw: string): PostgresInterval.IPostgresInterval; + +export = PostgresInterval; diff --git a/node_modules/postgres-interval/index.js b/node_modules/postgres-interval/index.js new file mode 100644 index 0000000..8ecca80 --- /dev/null +++ b/node_modules/postgres-interval/index.js @@ -0,0 +1,125 @@ +'use strict' + +var extend = require('xtend/mutable') + +module.exports = PostgresInterval + +function PostgresInterval (raw) { + if (!(this instanceof PostgresInterval)) { + return new PostgresInterval(raw) + } + extend(this, parse(raw)) +} +var properties = ['seconds', 'minutes', 'hours', 'days', 'months', 'years'] +PostgresInterval.prototype.toPostgres = function () { + var filtered = properties.filter(this.hasOwnProperty, this) + + // In addition to `properties`, we need to account for fractions of seconds. + if (this.milliseconds && filtered.indexOf('seconds') < 0) { + filtered.push('seconds') + } + + if (filtered.length === 0) return '0' + return filtered + .map(function (property) { + var value = this[property] || 0 + + // Account for fractional part of seconds, + // remove trailing zeroes. + if (property === 'seconds' && this.milliseconds) { + value = (value + this.milliseconds / 1000).toFixed(6).replace(/\.?0+$/, '') + } + + return value + ' ' + property + }, this) + .join(' ') +} + +var propertiesISOEquivalent = { + years: 'Y', + months: 'M', + days: 'D', + hours: 'H', + minutes: 'M', + seconds: 'S' +} +var dateProperties = ['years', 'months', 'days'] +var timeProperties = ['hours', 'minutes', 'seconds'] +// according to ISO 8601 +PostgresInterval.prototype.toISOString = PostgresInterval.prototype.toISO = function () { + var datePart = dateProperties + .map(buildProperty, this) + .join('') + + var timePart = timeProperties + .map(buildProperty, this) + .join('') + + return 'P' + datePart + 'T' + timePart + + function buildProperty (property) { + var value = this[property] || 0 + + // Account for fractional part of seconds, + // remove trailing zeroes. + if (property === 'seconds' && this.milliseconds) { + value = (value + this.milliseconds / 1000).toFixed(6).replace(/0+$/, '') + } + + return value + propertiesISOEquivalent[property] + } +} + +var NUMBER = '([+-]?\\d+)' +var YEAR = NUMBER + '\\s+years?' +var MONTH = NUMBER + '\\s+mons?' +var DAY = NUMBER + '\\s+days?' +var TIME = '([+-])?([\\d]*):(\\d\\d):(\\d\\d)\\.?(\\d{1,6})?' +var INTERVAL = new RegExp([YEAR, MONTH, DAY, TIME].map(function (regexString) { + return '(' + regexString + ')?' +}) + .join('\\s*')) + +// Positions of values in regex match +var positions = { + years: 2, + months: 4, + days: 6, + hours: 9, + minutes: 10, + seconds: 11, + milliseconds: 12 +} +// We can use negative time +var negatives = ['hours', 'minutes', 'seconds', 'milliseconds'] + +function parseMilliseconds (fraction) { + // add omitted zeroes + var microseconds = fraction + '000000'.slice(fraction.length) + return parseInt(microseconds, 10) / 1000 +} + +function parse (interval) { + if (!interval) return {} + var matches = INTERVAL.exec(interval) + var isNegative = matches[8] === '-' + return Object.keys(positions) + .reduce(function (parsed, property) { + var position = positions[property] + var value = matches[position] + // no empty string + if (!value) return parsed + // milliseconds are actually microseconds (up to 6 digits) + // with omitted trailing zeroes. + value = property === 'milliseconds' + ? parseMilliseconds(value) + : parseInt(value, 10) + // no zeros + if (!value) return parsed + if (isNegative && ~negatives.indexOf(property)) { + value *= -1 + } + parsed[property] = value + return parsed + }, {}) +} diff --git a/node_modules/postgres-interval/license b/node_modules/postgres-interval/license new file mode 100644 index 0000000..25c6247 --- /dev/null +++ b/node_modules/postgres-interval/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Ben Drucker (bendrucker.me) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/postgres-interval/package.json b/node_modules/postgres-interval/package.json new file mode 100644 index 0000000..95520a0 --- /dev/null +++ b/node_modules/postgres-interval/package.json @@ -0,0 +1,36 @@ +{ + "name": "postgres-interval", + "main": "index.js", + "version": "1.2.0", + "description": "Parse Postgres interval columns", + "license": "MIT", + "repository": "bendrucker/postgres-interval", + "author": { + "name": "Ben Drucker", + "email": "bvdrucker@gmail.com", + "url": "bendrucker.me" + }, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "standard && tape test.js" + }, + "keywords": [ + "postgres", + "interval", + "parser" + ], + "dependencies": { + "xtend": "^4.0.0" + }, + "devDependencies": { + "tape": "^4.0.0", + "standard": "^12.0.1" + }, + "files": [ + "index.js", + "index.d.ts", + "readme.md" + ] +} diff --git a/node_modules/postgres-interval/readme.md b/node_modules/postgres-interval/readme.md new file mode 100644 index 0000000..53cda4a --- /dev/null +++ b/node_modules/postgres-interval/readme.md @@ -0,0 +1,48 @@ +# postgres-interval [![Build Status](https://travis-ci.org/bendrucker/postgres-interval.svg?branch=master)](https://travis-ci.org/bendrucker/postgres-interval) [![Greenkeeper badge](https://badges.greenkeeper.io/bendrucker/postgres-interval.svg)](https://greenkeeper.io/) + +> Parse Postgres interval columns + + +## Install + +``` +$ npm install --save postgres-interval +``` + + +## Usage + +```js +var parse = require('postgres-interval') +var interval = parse('01:02:03') +//=> {hours: 1, minutes: 2, seconds: 3} +interval.toPostgres() +// 3 seconds 2 minutes 1 hours +interval.toISO() +// P0Y0M0DT1H2M3S +``` + +## API + +#### `parse(pgInterval)` -> `interval` + +##### pgInterval + +*Required* +Type: `string` + +A Postgres interval string. + +#### `interval.toPostgres()` -> `string` + +Returns an interval string. This allows the interval object to be passed into prepared statements. + +#### `interval.toISOString()` -> `string` + +Returns an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) compliant string. + +Also available as `interval.toISO()` for backwards compatibility. + +## License + +MIT © [Ben Drucker](http://bendrucker.me) diff --git a/node_modules/semver/LICENSE b/node_modules/semver/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/semver/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/semver/README.md b/node_modules/semver/README.md new file mode 100644 index 0000000..5e03abc --- /dev/null +++ b/node_modules/semver/README.md @@ -0,0 +1,680 @@ +semver(1) -- The semantic versioner for npm +=========================================== + +## Install + +```bash +npm install semver +```` + +## Usage + +As a node module: + +```js +const semver = require('semver') + +semver.valid('1.2.3') // '1.2.3' +semver.valid('a.b.c') // null +semver.clean(' =v1.2.3 ') // '1.2.3' +semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true +semver.gt('1.2.3', '9.8.7') // false +semver.lt('1.2.3', '9.8.7') // true +semver.minVersion('>=1.0.0') // '1.0.0' +semver.valid(semver.coerce('v2')) // '2.0.0' +semver.valid(semver.coerce('42.6.7.9.3-alpha')) // '42.6.7' +``` + +You can also just load the module for the function that you care about if +you'd like to minimize your footprint. + +```js +// load the whole API at once in a single object +const semver = require('semver') + +// or just load the bits you need +// all of them listed here, just pick and choose what you want + +// classes +const SemVer = require('semver/classes/semver') +const Comparator = require('semver/classes/comparator') +const Range = require('semver/classes/range') + +// functions for working with versions +const semverParse = require('semver/functions/parse') +const semverValid = require('semver/functions/valid') +const semverClean = require('semver/functions/clean') +const semverInc = require('semver/functions/inc') +const semverDiff = require('semver/functions/diff') +const semverMajor = require('semver/functions/major') +const semverMinor = require('semver/functions/minor') +const semverPatch = require('semver/functions/patch') +const semverPrerelease = require('semver/functions/prerelease') +const semverCompare = require('semver/functions/compare') +const semverRcompare = require('semver/functions/rcompare') +const semverCompareLoose = require('semver/functions/compare-loose') +const semverCompareBuild = require('semver/functions/compare-build') +const semverSort = require('semver/functions/sort') +const semverRsort = require('semver/functions/rsort') +const semverTruncate = require('semver/functions/truncate') + +// low-level comparators between versions +const semverGt = require('semver/functions/gt') +const semverLt = require('semver/functions/lt') +const semverEq = require('semver/functions/eq') +const semverNeq = require('semver/functions/neq') +const semverGte = require('semver/functions/gte') +const semverLte = require('semver/functions/lte') +const semverCmp = require('semver/functions/cmp') +const semverCoerce = require('semver/functions/coerce') + +// working with ranges +const semverSatisfies = require('semver/functions/satisfies') +const semverMaxSatisfying = require('semver/ranges/max-satisfying') +const semverMinSatisfying = require('semver/ranges/min-satisfying') +const semverToComparators = require('semver/ranges/to-comparators') +const semverMinVersion = require('semver/ranges/min-version') +const semverValidRange = require('semver/ranges/valid') +const semverOutside = require('semver/ranges/outside') +const semverGtr = require('semver/ranges/gtr') +const semverLtr = require('semver/ranges/ltr') +const semverIntersects = require('semver/ranges/intersects') +const semverSimplifyRange = require('semver/ranges/simplify') +const semverRangeSubset = require('semver/ranges/subset') +``` + +As a command-line utility: + +``` +$ semver -h + +A JavaScript implementation of the https://semver.org/ specification +Copyright Isaac Z. Schlueter + +Usage: semver [options] [ [...]] +Prints valid versions sorted by SemVer precedence + +Options: +-r --range + Print versions that match the specified range. + +-i --increment [] + Increment a version by the specified level. Level can + be one of: major, minor, patch, premajor, preminor, + prepatch, prerelease, or release. Default level is 'patch'. + Only one version may be specified. + +--preid + Identifier to be used to prefix premajor, preminor, + prepatch or prerelease version increments. + +-l --loose + Interpret versions and ranges loosely + +-n <0|1|false> + Base number for prerelease identifier (default: 0). + Use false to omit the number altogether. + +-p --include-prerelease + Always include prerelease versions in range matching + +-c --coerce + Coerce a string into SemVer if possible + (does not imply --loose) + +--rtl + Coerce version strings right to left + +--ltr + Coerce version strings left to right (default) + +Program exits successfully if any valid version satisfies +all supplied ranges, and prints all satisfying versions. + +If no satisfying versions are found, then exits failure. + +Versions are printed in ascending order, so supplying +multiple versions to the utility will just sort them. +``` + +## Versions + +A "version" is described by the `v2.0.0` specification found at +. + +A leading `"="` or `"v"` character is stripped off and ignored. +Support for stripping a leading "v" is kept for compatibility with `v1.0.0` of the SemVer +specification but should not be used anymore. + +## Ranges + +A `version range` is a set of `comparators` that specify versions +that satisfy the range. + +A `comparator` is composed of an `operator` and a `version`. The set +of primitive `operators` is: + +* `<` Less than +* `<=` Less than or equal to +* `>` Greater than +* `>=` Greater than or equal to +* `=` Equal. If no operator is specified, then equality is assumed, + so this operator is optional but MAY be included. + +For example, the comparator `>=1.2.7` would match the versions +`1.2.7`, `1.2.8`, `2.5.3`, and `1.3.9`, but not the versions `1.2.6` +or `1.1.0`. The comparator `>1` is equivalent to `>=2.0.0` and +would match the versions `2.0.0` and `3.1.0`, but not the versions +`1.0.1` or `1.1.0`. + +Comparators can be joined by whitespace to form a `comparator set`, +which is satisfied by the **intersection** of all of the comparators +it includes. + +A range is composed of one or more comparator sets, joined by `||`. A +version matches a range if and only if every comparator in at least +one of the `||`-separated comparator sets is satisfied by the version. + +For example, the range `>=1.2.7 <1.3.0` would match the versions +`1.2.7`, `1.2.8`, and `1.2.99`, but not the versions `1.2.6`, `1.3.0`, +or `1.1.0`. + +The range `1.2.7 || >=1.2.9 <2.0.0` would match the versions `1.2.7`, +`1.2.9`, and `1.4.6`, but not the versions `1.2.8` or `2.0.0`. + +### Prerelease Tags + +If a version has a prerelease tag (for example, `1.2.3-alpha.3`) then +it will only be allowed to satisfy comparator sets if at least one +comparator with the same `[major, minor, patch]` tuple also has a +prerelease tag. + +For example, the range `>1.2.3-alpha.3` would be allowed to match the +version `1.2.3-alpha.7`, but it would *not* be satisfied by +`3.4.5-alpha.9`, even though `3.4.5-alpha.9` is technically "greater +than" `1.2.3-alpha.3` according to the SemVer sort rules. The version +range only accepts prerelease tags on the `1.2.3` version. +Version `3.4.5` *would* satisfy the range because it does not have a +prerelease flag, and `3.4.5` is greater than `1.2.3-alpha.7`. + +The purpose of this behavior is twofold. First, prerelease versions +frequently are updated very quickly, and contain many breaking changes +that are (by the author's design) not yet fit for public consumption. +Therefore, by default, they are excluded from range-matching +semantics. + +Second, a user who has opted into using a prerelease version has +indicated the intent to use *that specific* set of +alpha/beta/rc versions. By including a prerelease tag in the range, +the user is indicating that they are aware of the risk. However, it +is still not appropriate to assume that they have opted into taking a +similar risk on the *next* set of prerelease versions. + +Note that this behavior can be suppressed (treating all prerelease +versions as if they were normal versions, for range-matching) +by setting the `includePrerelease` flag on the options +object to any +[functions](https://github.com/npm/node-semver#functions) that do +range matching. + +#### Prerelease Identifiers + +The method `.inc` takes an additional `identifier` string argument that +will append the value of the string as a prerelease identifier: + +```javascript +semver.inc('1.2.3', 'prerelease', 'beta') +// '1.2.4-beta.0' +``` + +command-line example: + +```bash +$ semver 1.2.3 -i prerelease --preid beta +1.2.4-beta.0 +``` + +Which then can be used to increment further: + +```bash +$ semver 1.2.4-beta.0 -i prerelease +1.2.4-beta.1 +``` + +To get out of the prerelease phase, use the `release` option: + +```bash +$ semver 1.2.4-beta.1 -i release +1.2.4 +``` + +#### Prerelease Identifier Base + +The method `.inc` takes an optional parameter 'identifierBase' string +that will let you let your prerelease number as zero-based or one-based. +Set to `false` to omit the prerelease number altogether. +If you do not specify this parameter, it will default to zero-based. + +```javascript +semver.inc('1.2.3', 'prerelease', 'beta', '1') +// '1.2.4-beta.1' +``` + +```javascript +semver.inc('1.2.3', 'prerelease', 'beta', false) +// '1.2.4-beta' +``` + +command-line example: + +```bash +$ semver 1.2.3 -i prerelease --preid beta -n 1 +1.2.4-beta.1 +``` + +```bash +$ semver 1.2.3 -i prerelease --preid beta -n false +1.2.4-beta +``` + +### Advanced Range Syntax + +Advanced range syntax desugars to primitive comparators in +deterministic ways. + +Advanced ranges may be combined in the same way as primitive +comparators using white space or `||`. + +#### Hyphen Ranges `X.Y.Z - A.B.C` + +Specifies an inclusive set. + +* `1.2.3 - 2.3.4` := `>=1.2.3 <=2.3.4` + +If a partial version is provided as the first version in the inclusive +range, then the missing pieces are replaced with zeroes. + +* `1.2 - 2.3.4` := `>=1.2.0 <=2.3.4` + +If a partial version is provided as the second version in the +inclusive range, then all versions that start with the supplied parts +of the tuple are accepted, but nothing that would be greater than the +provided tuple parts. + +* `1.2.3 - 2.3` := `>=1.2.3 <2.4.0-0` +* `1.2.3 - 2` := `>=1.2.3 <3.0.0-0` + +#### X-Ranges `1.2.x` `1.X` `1.2.*` `*` + +Any of `X`, `x`, or `*` may be used to "stand in" for one of the +numeric values in the `[major, minor, patch]` tuple. + +* `*` := `>=0.0.0` (Any non-prerelease version satisfies, unless + `includePrerelease` is specified, in which case any version at all + satisfies) +* `1.x` := `>=1.0.0 <2.0.0-0` (Matching major version) +* `1.2.x` := `>=1.2.0 <1.3.0-0` (Matching major and minor versions) + +A partial version range is treated as an X-Range, so the special +character is in fact optional. + +* `""` (empty string) := `*` := `>=0.0.0` +* `1` := `1.x.x` := `>=1.0.0 <2.0.0-0` +* `1.2` := `1.2.x` := `>=1.2.0 <1.3.0-0` + +#### Tilde Ranges `~1.2.3` `~1.2` `~1` + +Allows patch-level changes if a minor version is specified on the +comparator. Allows minor-level changes if not. + +* `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0-0` +* `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0-0` (Same as `1.2.x`) +* `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0-0` (Same as `1.x`) +* `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0-0` +* `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0-0` (Same as `0.2.x`) +* `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0-0` (Same as `0.x`) +* `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0-0` Note that prereleases in + the `1.2.3` version will be allowed, if they are greater than or + equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but + `1.2.4-beta.2` would not, because it is a prerelease of a + different `[major, minor, patch]` tuple. + +#### Caret Ranges `^1.2.3` `^0.2.5` `^0.0.4` + +Allows changes that do not modify the left-most non-zero element in the +`[major, minor, patch]` tuple. In other words, this allows patch and +minor updates for versions `1.0.0` and above, patch updates for +versions `0.X >=0.1.0`, and *no* updates for versions `0.0.X`. + +Many authors treat a `0.x` version as if the `x` were the major +"breaking-change" indicator. + +Caret ranges are ideal when an author may make breaking changes +between `0.2.4` and `0.3.0` releases, which is a common practice. +However, it presumes that there will *not* be breaking changes between +`0.2.4` and `0.2.5`. It allows for changes that are presumed to be +additive (but non-breaking), according to commonly observed practices. + +* `^1.2.3` := `>=1.2.3 <2.0.0-0` +* `^0.2.3` := `>=0.2.3 <0.3.0-0` +* `^0.0.3` := `>=0.0.3 <0.0.4-0` +* `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0-0` Note that prereleases in + the `1.2.3` version will be allowed, if they are greater than or + equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but + `1.2.4-beta.2` would not, because it is a prerelease of a + different `[major, minor, patch]` tuple. +* `^0.0.3-beta` := `>=0.0.3-beta <0.0.4-0` Note that prereleases in the + `0.0.3` version *only* will be allowed, if they are greater than or + equal to `beta`. So, `0.0.3-pr.2` would be allowed. + +When parsing caret ranges, a missing `patch` value desugars to the +number `0`, but will allow flexibility within that value, even if the +major and minor versions are both `0`. + +* `^1.2.x` := `>=1.2.0 <2.0.0-0` +* `^0.0.x` := `>=0.0.0 <0.1.0-0` +* `^0.0` := `>=0.0.0 <0.1.0-0` + +A missing `minor` and `patch` values will desugar to zero, but also +allow flexibility within those values, even if the major version is +zero. + +* `^1.x` := `>=1.0.0 <2.0.0-0` +* `^0.x` := `>=0.0.0 <1.0.0-0` + +### Range Grammar + +Putting all this together, here is a Backus-Naur grammar for ranges, +for the benefit of parser authors: + +```bnf +range-set ::= range ( logical-or range ) * +logical-or ::= ( ' ' ) * '||' ( ' ' ) * +range ::= hyphen | simple ( ' ' simple ) * | '' +hyphen ::= partial ' - ' partial +simple ::= primitive | partial | tilde | caret +primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial +partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )? +xr ::= 'x' | 'X' | '*' | nr +nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) * +tilde ::= '~' partial +caret ::= '^' partial +qualifier ::= ( '-' pre )? ( '+' build )? +pre ::= prepart ( '.' prepart ) * +prepart ::= nr | alphanumid +build ::= buildid ( '.' buildid ) * +alphanumid ::= ( ['0'-'9'] ) * [-A-Za-z] [-0-9A-Za-z] * +buildid ::= [-0-9A-Za-z]+ +``` + +Note: Prerelease identifiers (`pre`) use `nr` for numeric parts, which +disallows leading zeros (e.g., `1.2.3-00` is invalid). Build metadata +identifiers (`build`) allow any alphanumeric string including leading +zeros (e.g., `1.2.3+00` is valid). This matches the +[SemVer 2.0.0 specification](https://semver.org/#spec-item-9). + +## Functions + +All methods and classes take a final `options` object argument. All +options in this object are `false` by default. The options supported +are: + +- `loose`: Be more forgiving about not-quite-valid semver strings. + (Any resulting output will always be 100% strict compliant, of + course.) For backwards compatibility reasons, if the `options` + argument is a boolean value instead of an object, it is interpreted + to be the `loose` param. +- `includePrerelease`: Set to suppress the [default + behavior](https://github.com/npm/node-semver#prerelease-tags) of + excluding prerelease tagged versions from ranges unless they are + explicitly opted into. + +Strict-mode Comparators and Ranges will be strict about the SemVer +strings that they parse. + +* `valid(v)`: Return the parsed version, or null if it's not valid. +* `inc(v, releaseType, options, identifier, identifierBase)`: + Return the version incremented by the release + type (`major`, `premajor`, `minor`, `preminor`, `patch`, + `prepatch`, `prerelease`, or `release`), or null if it's not valid + * `premajor` in one call will bump the version up to the next major + version and down to a prerelease of that major version. + `preminor`, and `prepatch` work the same way. + * If called from a non-prerelease version, `prerelease` will work the + same as `prepatch`. It increments the patch version and then makes a + prerelease. If the input version is already a prerelease it simply + increments it. + * `release` will remove any prerelease part of the version. + * `identifier` can be used to prefix `premajor`, `preminor`, + `prepatch`, or `prerelease` version increments. `identifierBase` + is the base to be used for the `prerelease` identifier. +* `prerelease(v)`: Returns an array of prerelease components, or null + if none exist. Example: `prerelease('1.2.3-alpha.1') -> ['alpha', 1]` +* `major(v)`: Return the major version number. +* `minor(v)`: Return the minor version number. +* `patch(v)`: Return the patch version number. +* `intersects(r1, r2, loose)`: Return true if the two supplied ranges + or comparators intersect. +* `parse(v)`: Attempt to parse a string as a semantic version, returning either + a `SemVer` object or `null`. +* `truncate(v, releaseType)`: Return the version with components _lower_ + than `releaseType` dropped off, e.g.: + * `major` removes build & prerelease info and sets minor & patch to 0. + * `minor` removes build & prerelease info, and sets patch to 0 + * `patch` removes build & prerelease info + * All prerelease types remove build info only + +### Comparison + +* `gt(v1, v2)`: `v1 > v2` +* `gte(v1, v2)`: `v1 >= v2` +* `lt(v1, v2)`: `v1 < v2` +* `lte(v1, v2)`: `v1 <= v2` +* `eq(v1, v2)`: `v1 == v2` This is true if they're logically equivalent, + even if they're not the same string. You already know how to + compare strings. +* `neq(v1, v2)`: `v1 != v2` The opposite of `eq`. +* `cmp(v1, comparator, v2)`: Pass in a comparison string, and it'll call + the corresponding function above. `"==="` and `"!=="` do simple + string comparison, but are included for completeness. Throws if an + invalid comparison string is provided. +* `compare(v1, v2)`: Return `0` if `v1 == v2`, or `1` if `v1` is greater, or `-1` if + `v2` is greater. Sorts in ascending order if passed to `Array.sort()`. +* `rcompare(v1, v2)`: The reverse of `compare`. Sorts an array of versions + in descending order when passed to `Array.sort()`. +* `compareBuild(v1, v2)`: The same as `compare` but considers `build` when two versions + are equal. Sorts in ascending order if passed to `Array.sort()`. +* `compareLoose(v1, v2)`: Short for `compare(v1, v2, { loose: true })`. +* `diff(v1, v2)`: Returns the difference between two versions by the release type + (`major`, `premajor`, `minor`, `preminor`, `patch`, `prepatch`, or `prerelease`), + or null if the versions are the same. + +### Sorting + +* `sort(versions)`: Returns a sorted array of versions based on the `compareBuild` + function. +* `rsort(versions)`: The reverse of `sort`. Returns an array of versions based on + the `compareBuild` function in descending order. + +### Comparators + +* `intersects(comparator)`: Return true if the comparators intersect + +### Ranges + +* `validRange(range)`: Return the valid range or null if it's not valid. +* `satisfies(version, range)`: Return true if the version satisfies the + range. +* `maxSatisfying(versions, range)`: Return the highest version in the list + that satisfies the range, or `null` if none of them do. +* `minSatisfying(versions, range)`: Return the lowest version in the list + that satisfies the range, or `null` if none of them do. +* `minVersion(range)`: Return the lowest version that can match + the given range. +* `gtr(version, range)`: Return `true` if the version is greater than all the + versions possible in the range. +* `ltr(version, range)`: Return `true` if the version is less than all the + versions possible in the range. +* `outside(version, range, hilo)`: Return true if the version is outside + the bounds of the range in either the high or low direction. The + `hilo` argument must be either the string `'>'` or `'<'`. (This is + the function called by `gtr` and `ltr`.) +* `intersects(range)`: Return true if any of the range comparators intersect. +* `simplifyRange(versions, range)`: Return a "simplified" range that + matches the same items in the `versions` list as the range specified. Note + that it does *not* guarantee that it would match the same versions in all + cases, only for the set of versions provided. This is useful when + generating ranges by joining together multiple versions with `||` + programmatically, to provide the user with something a bit more + ergonomic. If the provided range is shorter in string-length than the + generated range, then that is returned. +* `subset(subRange, superRange)`: Return `true` if the `subRange` range is + entirely contained by the `superRange` range. + +Note that, since ranges may be non-contiguous, a version might not be +greater than a range, less than a range, *or* satisfy a range! For +example, the range `1.2 <1.2.9 || >2.0.0` would have a hole from `1.2.9` +until `2.0.0`, so version `1.2.10` would not be greater than the +range (because `2.0.1` satisfies, which is higher), nor less than the +range (since `1.2.8` satisfies, which is lower), and it also does not +satisfy the range. + +If you want to know if a version satisfies or does not satisfy a +range, use the `satisfies(version, range)` function. + +### Coercion + +* `coerce(version, options)`: Coerces a string to semver if possible + +This aims to provide a very forgiving translation of a non-semver string to +semver. It looks for the first digit in a string and consumes all +remaining characters which satisfy at least a partial semver (e.g., `1`, +`1.2`, `1.2.3`) up to the max permitted length (256 characters). Longer +versions are simply truncated (`4.6.3.9.2-alpha2` becomes `4.6.3`). All +surrounding text is simply ignored (`v3.4 replaces v3.3.1` becomes +`3.4.0`). Only text which lacks digits will fail coercion (`version one` +is not valid). The maximum length for any semver component considered for +coercion is 16 characters; longer components will be ignored +(`10000000000000000.4.7.4` becomes `4.7.4`). The maximum value for any +semver component is `Number.MAX_SAFE_INTEGER || (2**53 - 1)`; higher value +components are invalid (`9999999999999999.4.7.4` is likely invalid). + +If the `options.rtl` flag is set, then `coerce` will return the right-most +coercible tuple that does not share an ending index with a longer coercible +tuple. For example, `1.2.3.4` will return `2.3.4` in rtl mode, not +`4.0.0`. `1.2.3/4` will return `4.0.0`, because the `4` is not a part of +any other overlapping SemVer tuple. + +If the `options.includePrerelease` flag is set, then the `coerce` result will contain +prerelease and build parts of a version. For example, `1.2.3.4-rc.1+rev.2` +will preserve prerelease `rc.1` and build `rev.2` in the result. + +### Clean + +* `clean(version)`: Clean a string to be a valid semver if possible + +This will return a cleaned and trimmed semver version. If the provided +version is not valid a null will be returned. This does not work for +ranges. + +ex. +* `s.clean(' = v 2.1.5foo')`: `null` +* `s.clean(' = v 2.1.5foo', { loose: true })`: `'2.1.5-foo'` +* `s.clean(' = v 2.1.5-foo')`: `null` +* `s.clean(' = v 2.1.5-foo', { loose: true })`: `'2.1.5-foo'` +* `s.clean('=v2.1.5')`: `'2.1.5'` +* `s.clean(' =v2.1.5')`: `'2.1.5'` +* `s.clean(' 2.1.5 ')`: `'2.1.5'` +* `s.clean('~1.0.0')`: `null` + +## Constants + +As a convenience, helper constants are exported to provide information about what `node-semver` supports: + +### `RELEASE_TYPES` + +- major +- premajor +- minor +- preminor +- patch +- prepatch +- prerelease + +``` +const semver = require('semver'); + +if (semver.RELEASE_TYPES.includes(arbitraryUserInput)) { + console.log('This is a valid release type!'); +} else { + console.warn('This is NOT a valid release type!'); +} +``` + +### `SEMVER_SPEC_VERSION` + +2.0.0 + +``` +const semver = require('semver'); + +console.log('We are currently using the semver specification version:', semver.SEMVER_SPEC_VERSION); +``` + +## Exported Modules + + + +You may pull in just the part of this semver utility that you need if you +are sensitive to packing and tree-shaking concerns. The main +`require('semver')` export uses getter functions to lazily load the parts +of the API that are used. + +The following modules are available: + +* `require('semver')` +* `require('semver/classes')` +* `require('semver/classes/comparator')` +* `require('semver/classes/range')` +* `require('semver/classes/semver')` +* `require('semver/functions/clean')` +* `require('semver/functions/cmp')` +* `require('semver/functions/coerce')` +* `require('semver/functions/compare')` +* `require('semver/functions/compare-build')` +* `require('semver/functions/compare-loose')` +* `require('semver/functions/diff')` +* `require('semver/functions/eq')` +* `require('semver/functions/gt')` +* `require('semver/functions/gte')` +* `require('semver/functions/inc')` +* `require('semver/functions/lt')` +* `require('semver/functions/lte')` +* `require('semver/functions/major')` +* `require('semver/functions/minor')` +* `require('semver/functions/neq')` +* `require('semver/functions/parse')` +* `require('semver/functions/patch')` +* `require('semver/functions/prerelease')` +* `require('semver/functions/rcompare')` +* `require('semver/functions/rsort')` +* `require('semver/functions/satisfies')` +* `require('semver/functions/sort')` +* `require('semver/functions/truncate')` +* `require('semver/functions/valid')` +* `require('semver/ranges/gtr')` +* `require('semver/ranges/intersects')` +* `require('semver/ranges/ltr')` +* `require('semver/ranges/max-satisfying')` +* `require('semver/ranges/min-satisfying')` +* `require('semver/ranges/min-version')` +* `require('semver/ranges/outside')` +* `require('semver/ranges/simplify')` +* `require('semver/ranges/subset')` +* `require('semver/ranges/to-comparators')` +* `require('semver/ranges/valid')` + diff --git a/node_modules/semver/bin/semver.js b/node_modules/semver/bin/semver.js new file mode 100644 index 0000000..9ae8aad --- /dev/null +++ b/node_modules/semver/bin/semver.js @@ -0,0 +1,195 @@ +#!/usr/bin/env node +// Standalone semver comparison program. +// Exits successfully and prints matching version(s) if +// any supplied version is valid and passes all tests. + +'use strict' + +const argv = process.argv.slice(2) + +let versions = [] + +const range = [] + +let inc = null + +const version = require('../package.json').version + +let loose = false + +let includePrerelease = false + +let coerce = false + +let rtl = false + +let identifier + +let identifierBase + +const semver = require('../') +const parseOptions = require('../internal/parse-options') + +let reverse = false + +let options = {} + +const main = () => { + if (!argv.length) { + return help() + } + while (argv.length) { + let a = argv.shift() + const indexOfEqualSign = a.indexOf('=') + if (indexOfEqualSign !== -1) { + const value = a.slice(indexOfEqualSign + 1) + a = a.slice(0, indexOfEqualSign) + argv.unshift(value) + } + + switch (a) { + case '-rv': case '-rev': case '--rev': case '--reverse': + reverse = true + break + case '-l': case '--loose': + loose = true + break + case '-p': case '--include-prerelease': + includePrerelease = true + break + case '-v': case '--version': + versions.push(argv.shift()) + break + case '-i': case '--inc': case '--increment': + if (semver.RELEASE_TYPES.includes(argv[0]) || (argv[0] === 'release')) { + inc = { value: argv.shift(), maybeErrantValue: null, option: a } + } else { + inc = { value: 'patch', maybeErrantValue: argv[0], option: a } + } + break + case '--preid': + identifier = argv.shift() + break + case '-r': case '--range': + range.push(argv.shift()) + break + case '-n': + identifierBase = argv.shift() + if (identifierBase === 'false') { + identifierBase = false + } + break + case '-c': case '--coerce': + coerce = true + break + case '--rtl': + rtl = true + break + case '--ltr': + rtl = false + break + case '-h': case '--help': case '-?': + return help() + default: + versions.push(a) + break + } + } + + options = parseOptions({ loose, includePrerelease, rtl }) + + if ( + inc && + versions.includes(inc.maybeErrantValue) && + !semver.valid(inc.maybeErrantValue, options) + ) { + console.warn(`Invalid value for ${inc.option}; defaulting to 'patch'. This may become a failure in future major versions.`) + } + + versions = versions.map((v) => { + return coerce ? (semver.coerce(v, options) || { version: v }).version : v + }).filter((v) => { + return semver.valid(v, options) + }) + if (!versions.length) { + return fail() + } + if (inc && (versions.length !== 1 || range.length)) { + return failInc() + } + + for (let i = 0, l = range.length; i < l; i++) { + versions = versions.filter((v) => { + return semver.satisfies(v, range[i], options) + }) + if (!versions.length) { + return fail() + } + } + versions + .sort((a, b) => semver[reverse ? 'rcompare' : 'compare'](a, b, options)) + .map(v => semver.clean(v, options)) + .map(v => inc ? semver.inc(v, inc.value, options, identifier, identifierBase) : v) + .forEach(v => console.log(v)) +} + +const failInc = () => { + console.error('--inc can only be used on a single version with no range') + fail() +} + +const fail = () => process.exit(1) + +const help = () => console.log( +`SemVer ${version} + +A JavaScript implementation of the https://semver.org/ specification +Copyright Isaac Z. Schlueter + +Usage: semver [options] [ [...]] +Prints valid versions sorted by SemVer precedence + +Options: +-r --range + Print versions that match the specified range. + +-i --increment [] + Increment a version by the specified level. Level can + be one of: major, minor, patch, premajor, preminor, + prepatch, prerelease, or release. Default level is 'patch'. + Only one version may be specified. + +--preid + Identifier to be used to prefix premajor, preminor, + prepatch or prerelease version increments. + +-l --loose + Interpret versions and ranges loosely + +-p --include-prerelease + Always include prerelease versions in range matching + +-c --coerce + Coerce a string into SemVer if possible + (does not imply --loose) + +--rtl + Coerce version strings right to left + +--ltr + Coerce version strings left to right (default) + +-n + Base number to be used for the prerelease identifier. + Can be either 0 or 1, or false to omit the number altogether. + Defaults to 0. + +Program exits successfully if any valid version satisfies +all supplied ranges, and prints all satisfying versions. + +If no satisfying versions are found, then exits failure. + +Versions are printed in ascending order, so supplying +multiple versions to the utility will just sort them.`) + +main() diff --git a/node_modules/semver/classes/comparator.js b/node_modules/semver/classes/comparator.js new file mode 100644 index 0000000..647c1f0 --- /dev/null +++ b/node_modules/semver/classes/comparator.js @@ -0,0 +1,143 @@ +'use strict' + +const ANY = Symbol('SemVer ANY') +// hoisted class for cyclic dependency +class Comparator { + static get ANY () { + return ANY + } + + constructor (comp, options) { + options = parseOptions(options) + + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } + + comp = comp.trim().split(/\s+/).join(' ') + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) + + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } + + debug('comp', this) + } + + parse (comp) { + const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] + const m = comp.match(r) + + if (!m) { + throw new TypeError(`Invalid comparator: ${comp}`) + } + + this.operator = m[1] !== undefined ? m[1] : '' + if (this.operator === '=') { + this.operator = '' + } + + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } + } + + toString () { + return this.value + } + + test (version) { + debug('Comparator.test', version, this.options.loose) + + if (this.semver === ANY || version === ANY) { + return true + } + + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } + + return cmp(version, this.operator, this.semver, this.options) + } + + intersects (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') + } + + if (this.operator === '') { + if (this.value === '') { + return true + } + return new Range(comp.value, options).test(this.value) + } else if (comp.operator === '') { + if (comp.value === '') { + return true + } + return new Range(this.value, options).test(comp.semver) + } + + options = parseOptions(options) + + // Special cases where nothing can possibly be lower + if (options.includePrerelease && + (this.value === '<0.0.0-0' || comp.value === '<0.0.0-0')) { + return false + } + if (!options.includePrerelease && + (this.value.startsWith('<0.0.0') || comp.value.startsWith('<0.0.0'))) { + return false + } + + // Same direction increasing (> or >=) + if (this.operator.startsWith('>') && comp.operator.startsWith('>')) { + return true + } + // Same direction decreasing (< or <=) + if (this.operator.startsWith('<') && comp.operator.startsWith('<')) { + return true + } + // same SemVer and both sides are inclusive (<= or >=) + if ( + (this.semver.version === comp.semver.version) && + this.operator.includes('=') && comp.operator.includes('=')) { + return true + } + // opposite directions less than + if (cmp(this.semver, '<', comp.semver, options) && + this.operator.startsWith('>') && comp.operator.startsWith('<')) { + return true + } + // opposite directions greater than + if (cmp(this.semver, '>', comp.semver, options) && + this.operator.startsWith('<') && comp.operator.startsWith('>')) { + return true + } + return false + } +} + +module.exports = Comparator + +const parseOptions = require('../internal/parse-options') +const { safeRe: re, t } = require('../internal/re') +const cmp = require('../functions/cmp') +const debug = require('../internal/debug') +const SemVer = require('./semver') +const Range = require('./range') diff --git a/node_modules/semver/classes/index.js b/node_modules/semver/classes/index.js new file mode 100644 index 0000000..91c24ec --- /dev/null +++ b/node_modules/semver/classes/index.js @@ -0,0 +1,7 @@ +'use strict' + +module.exports = { + SemVer: require('./semver.js'), + Range: require('./range.js'), + Comparator: require('./comparator.js'), +} diff --git a/node_modules/semver/classes/range.js b/node_modules/semver/classes/range.js new file mode 100644 index 0000000..c2e605e --- /dev/null +++ b/node_modules/semver/classes/range.js @@ -0,0 +1,564 @@ +'use strict' + +const SPACE_CHARACTERS = /\s+/g + +// hoisted class for cyclic dependency +class Range { + constructor (range, options) { + options = parseOptions(options) + + if (range instanceof Range) { + if ( + range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease + ) { + return range + } else { + return new Range(range.raw, options) + } + } + + if (range instanceof Comparator) { + // just put it in the set and return + this.raw = range.value + this.set = [[range]] + this.formatted = undefined + return this + } + + this.options = options + this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease + + // First reduce all whitespace as much as possible so we do not have to rely + // on potentially slow regexes like \s*. This is then stored and used for + // future error messages as well. + this.raw = range.trim().replace(SPACE_CHARACTERS, ' ') + + // First, split on || + this.set = this.raw + .split('||') + // map the range to a 2d array of comparators + .map(r => this.parseRange(r.trim())) + // throw out any comparator lists that are empty + // this generally means that it was not a valid range, which is allowed + // in loose mode, but will still throw if the WHOLE range is invalid. + .filter(c => c.length) + + if (!this.set.length) { + throw new TypeError(`Invalid SemVer Range: ${this.raw}`) + } + + // if we have any that are not the null set, throw out null sets. + if (this.set.length > 1) { + // keep the first one, in case they're all null sets + const first = this.set[0] + this.set = this.set.filter(c => !isNullSet(c[0])) + if (this.set.length === 0) { + this.set = [first] + } else if (this.set.length > 1) { + // if we have any that are *, then the range is just * + for (const c of this.set) { + if (c.length === 1 && isAny(c[0])) { + this.set = [c] + break + } + } + } + } + + this.formatted = undefined + } + + get range () { + if (this.formatted === undefined) { + this.formatted = '' + for (let i = 0; i < this.set.length; i++) { + if (i > 0) { + this.formatted += '||' + } + const comps = this.set[i] + for (let k = 0; k < comps.length; k++) { + if (k > 0) { + this.formatted += ' ' + } + this.formatted += comps[k].toString().trim() + } + } + } + return this.formatted + } + + format () { + return this.range + } + + toString () { + return this.range + } + + parseRange (range) { + // strip build metadata so it can't bleed into the version + range = range.replace(BUILDSTRIPRE, '') + + // memoize range parsing for performance. + // this is a very hot path, and fully deterministic. + const memoOpts = + (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | + (this.options.loose && FLAG_LOOSE) + const memoKey = memoOpts + ':' + range + const cached = cache.get(memoKey) + if (cached) { + return cached + } + + const loose = this.options.loose + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] + range = range.replace(hr, hyphenReplace(this.options.includePrerelease)) + debug('hyphen replace', range) + + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range) + + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[t.TILDETRIM], tildeTrimReplace) + debug('tilde trim', range) + + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[t.CARETTRIM], caretTrimReplace) + debug('caret trim', range) + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + let rangeList = range + .split(' ') + .map(comp => parseComparator(comp, this.options)) + .join(' ') + .split(/\s+/) + // >=0.0.0 is equivalent to * + .map(comp => replaceGTE0(comp, this.options)) + + if (loose) { + // in loose mode, throw out any that are not valid comparators + rangeList = rangeList.filter(comp => { + debug('loose invalid filter', comp, this.options) + return !!comp.match(re[t.COMPARATORLOOSE]) + }) + } + debug('range list', rangeList) + + // if any comparators are the null set, then replace with JUST null set + // if more than one comparator, remove any * comparators + // also, don't include the same comparator more than once + const rangeMap = new Map() + const comparators = rangeList.map(comp => new Comparator(comp, this.options)) + for (const comp of comparators) { + if (isNullSet(comp)) { + return [comp] + } + rangeMap.set(comp.value, comp) + } + if (rangeMap.size > 1 && rangeMap.has('')) { + rangeMap.delete('') + } + + const result = [...rangeMap.values()] + cache.set(memoKey, result) + return result + } + + intersects (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') + } + + return this.set.some((thisComparators) => { + return ( + isSatisfiable(thisComparators, options) && + range.set.some((rangeComparators) => { + return ( + isSatisfiable(rangeComparators, options) && + thisComparators.every((thisComparator) => { + return rangeComparators.every((rangeComparator) => { + return thisComparator.intersects(rangeComparator, options) + }) + }) + ) + }) + ) + }) + } + + // if ANY of the sets match ALL of its comparators, then pass + test (version) { + if (!version) { + return false + } + + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } + + for (let i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version, this.options)) { + return true + } + } + return false + } +} + +module.exports = Range + +const LRU = require('../internal/lrucache') +const cache = new LRU() + +const parseOptions = require('../internal/parse-options') +const Comparator = require('./comparator') +const debug = require('../internal/debug') +const SemVer = require('./semver') +const { + safeRe: re, + src, + t, + comparatorTrimReplace, + tildeTrimReplace, + caretTrimReplace, +} = require('../internal/re') +const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require('../internal/constants') + +// unbounded global build-metadata stripper used by parseRange +const BUILDSTRIPRE = new RegExp(src[t.BUILD], 'g') + +const isNullSet = c => c.value === '<0.0.0-0' +const isAny = c => c.value === '' + +// take a set of comparators and determine whether there +// exists a version which can satisfy it +const isSatisfiable = (comparators, options) => { + let result = true + const remainingComparators = comparators.slice() + let testComparator = remainingComparators.pop() + + while (result && remainingComparators.length) { + result = remainingComparators.every((otherComparator) => { + return testComparator.intersects(otherComparator, options) + }) + + testComparator = remainingComparators.pop() + } + + return result +} + +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +const parseComparator = (comp, options) => { + comp = comp.replace(re[t.BUILD], '') + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp +} + +const isX = id => !id || id.toLowerCase() === 'x' || id === '*' + +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0 +// ~0.0.1 --> >=0.0.1 <0.1.0-0 +const replaceTildes = (comp, options) => { + return comp + .trim() + .split(/\s+/) + .map((c) => replaceTilde(c, options)) + .join(' ') +} + +const replaceTilde = (comp, options) => { + const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] + return comp.replace(r, (_, M, m, p, pr) => { + debug('tilde', comp, _, M, m, p, pr) + let ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = `>=${M}.0.0 <${+M + 1}.0.0-0` + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0-0 + ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0` + } else if (pr) { + debug('replaceTilde pr', pr) + ret = `>=${M}.${m}.${p}-${pr + } <${M}.${+m + 1}.0-0` + } else { + // ~1.2.3 == >=1.2.3 <1.3.0-0 + ret = `>=${M}.${m}.${p + } <${M}.${+m + 1}.0-0` + } + + debug('tilde return', ret) + return ret + }) +} + +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0 +// ^1.2.3 --> >=1.2.3 <2.0.0-0 +// ^1.2.0 --> >=1.2.0 <2.0.0-0 +// ^0.0.1 --> >=0.0.1 <0.0.2-0 +// ^0.1.0 --> >=0.1.0 <0.2.0-0 +const replaceCarets = (comp, options) => { + return comp + .trim() + .split(/\s+/) + .map((c) => replaceCaret(c, options)) + .join(' ') +} + +const replaceCaret = (comp, options) => { + debug('caret', comp, options) + const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] + const z = options.includePrerelease ? '-0' : '' + return comp.replace(r, (_, M, m, p, pr) => { + debug('caret', comp, _, M, m, p, pr) + let ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0` + } else if (isX(p)) { + if (M === '0') { + ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0` + } else { + ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0` + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = `>=${M}.${m}.${p}-${pr + } <${M}.${m}.${+p + 1}-0` + } else { + ret = `>=${M}.${m}.${p}-${pr + } <${M}.${+m + 1}.0-0` + } + } else { + ret = `>=${M}.${m}.${p}-${pr + } <${+M + 1}.0.0-0` + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = `>=${M}.${m}.${p + }${z} <${M}.${m}.${+p + 1}-0` + } else { + ret = `>=${M}.${m}.${p + }${z} <${M}.${+m + 1}.0-0` + } + } else { + ret = `>=${M}.${m}.${p + } <${+M + 1}.0.0-0` + } + } + + debug('caret return', ret) + return ret + }) +} + +const replaceXRanges = (comp, options) => { + debug('replaceXRanges', comp, options) + return comp + .split(/\s+/) + .map((c) => replaceXRange(c, options)) + .join(' ') +} + +const replaceXRange = (comp, options) => { + comp = comp.trim() + const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] + return comp.replace(r, (ret, gtlt, M, m, p, pr) => { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + const xM = isX(M) + const xm = xM || isX(m) + const xp = xm || isX(p) + const anyX = xp + + if (gtlt === '=' && anyX) { + gtlt = '' + } + + // if we're including prereleases in the match, then we need + // to fix this to -0, the lowest possible prerelease value + pr = options.includePrerelease ? '-0' : '' + + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0-0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 + + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } + + if (gtlt === '<') { + pr = '-0' + } + + ret = `${gtlt + M}.${m}.${p}${pr}` + } else if (xm) { + ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0` + } else if (xp) { + ret = `>=${M}.${m}.0${pr + } <${M}.${+m + 1}.0-0` + } + + debug('xRange return', ret) + + return ret + }) +} + +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +const replaceStars = (comp, options) => { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp + .trim() + .replace(re[t.STAR], '') +} + +const replaceGTE0 = (comp, options) => { + debug('replaceGTE0', comp, options) + return comp + .trim() + .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '') +} + +// This function is passed to string.replace(re[t.HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0-0 +// TODO build? +const hyphenReplace = incPr => ($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr) => { + if (isX(fM)) { + from = '' + } else if (isX(fm)) { + from = `>=${fM}.0.0${incPr ? '-0' : ''}` + } else if (isX(fp)) { + from = `>=${fM}.${fm}.0${incPr ? '-0' : ''}` + } else if (fpr) { + from = `>=${from}` + } else { + from = `>=${from}${incPr ? '-0' : ''}` + } + + if (isX(tM)) { + to = '' + } else if (isX(tm)) { + to = `<${+tM + 1}.0.0-0` + } else if (isX(tp)) { + to = `<${tM}.${+tm + 1}.0-0` + } else if (tpr) { + to = `<=${tM}.${tm}.${tp}-${tpr}` + } else if (incPr) { + to = `<${tM}.${tm}.${+tp + 1}-0` + } else { + to = `<=${to}` + } + + return `${from} ${to}`.trim() +} + +const testSet = (set, version, options) => { + for (let i = 0; i < set.length; i++) { + if (!set[i].test(version)) { + return false + } + } + + if (version.prerelease.length && !options.includePrerelease) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (let i = 0; i < set.length; i++) { + debug(set[i].semver) + if (set[i].semver === Comparator.ANY) { + continue + } + + if (set[i].semver.prerelease.length > 0) { + const allowed = set[i].semver + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) { + return true + } + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false + } + + return true +} diff --git a/node_modules/semver/classes/semver.js b/node_modules/semver/classes/semver.js new file mode 100644 index 0000000..92254be --- /dev/null +++ b/node_modules/semver/classes/semver.js @@ -0,0 +1,333 @@ +'use strict' + +const debug = require('../internal/debug') +const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants') +const { safeRe: re, t } = require('../internal/re') + +const parseOptions = require('../internal/parse-options') +const { compareIdentifiers } = require('../internal/identifiers') +class SemVer { + constructor (version, options) { + options = parseOptions(options) + + if (version instanceof SemVer) { + if (version.loose === !!options.loose && + version.includePrerelease === !!options.includePrerelease) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`) + } + + if (version.length > MAX_LENGTH) { + throw new TypeError( + `version is longer than ${MAX_LENGTH} characters` + ) + } + + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose + // this isn't actually relevant for versions, but keep it so that we + // don't run into trouble passing this.options around. + this.includePrerelease = !!options.includePrerelease + + const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) + + if (!m) { + throw new TypeError(`Invalid Version: ${version}`) + } + + this.raw = version + + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] + + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map((id) => { + if (/^[0-9]+$/.test(id)) { + const num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num + } + } + return id + }) + } + + this.build = m[5] ? m[5].split('.') : [] + this.format() + } + + format () { + this.version = `${this.major}.${this.minor}.${this.patch}` + if (this.prerelease.length) { + this.version += `-${this.prerelease.join('.')}` + } + return this.version + } + + toString () { + return this.version + } + + compare (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + if (typeof other === 'string' && other === this.version) { + return 0 + } + other = new SemVer(other, this.options) + } + + if (other.version === this.version) { + return 0 + } + + return this.compareMain(other) || this.comparePre(other) + } + + compareMain (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + if (this.major < other.major) { + return -1 + } + if (this.major > other.major) { + return 1 + } + if (this.minor < other.minor) { + return -1 + } + if (this.minor > other.minor) { + return 1 + } + if (this.patch < other.patch) { + return -1 + } + if (this.patch > other.patch) { + return 1 + } + return 0 + } + + comparePre (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + let i = 0 + do { + const a = this.prerelease[i] + const b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) + } + } while (++i) + } + + compareBuild (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + let i = 0 + do { + const a = this.build[i] + const b = other.build[i] + debug('build compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) + } + } while (++i) + } + + // preminor will bump the version up to the next minor release, and immediately + // down to pre-release. premajor and prepatch work the same way. + inc (release, identifier, identifierBase) { + if (release.startsWith('pre')) { + if (!identifier && identifierBase === false) { + throw new Error('invalid increment argument: identifier is empty') + } + // Avoid an invalid semver results + if (identifier) { + const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE]) + if (!match || match[1] !== identifier) { + throw new Error(`invalid identifier: ${identifier}`) + } + } + } + + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier, identifierBase) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier, identifierBase) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier, identifierBase) + this.inc('pre', identifier, identifierBase) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier, identifierBase) + } + this.inc('pre', identifier, identifierBase) + break + case 'release': + if (this.prerelease.length === 0) { + throw new Error(`version ${this.raw} is not a prerelease`) + } + this.prerelease.length = 0 + break + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if ( + this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0 + ) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction. + case 'pre': { + const base = Number(identifierBase) ? 1 : 0 + + if (this.prerelease.length === 0) { + this.prerelease = [base] + } else { + let i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } + } + if (i === -1) { + // didn't increment anything + if (identifier === this.prerelease.join('.') && identifierBase === false) { + throw new Error('invalid increment argument: identifier already exists') + } + this.prerelease.push(base) + } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + let prerelease = [identifier, base] + if (identifierBase === false) { + prerelease = [identifier] + } + if (compareIdentifiers(this.prerelease[0], identifier) === 0) { + if (isNaN(this.prerelease[1])) { + this.prerelease = prerelease + } + } else { + this.prerelease = prerelease + } + } + break + } + default: + throw new Error(`invalid increment argument: ${release}`) + } + this.raw = this.format() + if (this.build.length) { + this.raw += `+${this.build.join('.')}` + } + return this + } +} + +module.exports = SemVer diff --git a/node_modules/semver/functions/clean.js b/node_modules/semver/functions/clean.js new file mode 100644 index 0000000..79703d6 --- /dev/null +++ b/node_modules/semver/functions/clean.js @@ -0,0 +1,8 @@ +'use strict' + +const parse = require('./parse') +const clean = (version, options) => { + const s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null +} +module.exports = clean diff --git a/node_modules/semver/functions/cmp.js b/node_modules/semver/functions/cmp.js new file mode 100644 index 0000000..77487dc --- /dev/null +++ b/node_modules/semver/functions/cmp.js @@ -0,0 +1,54 @@ +'use strict' + +const eq = require('./eq') +const neq = require('./neq') +const gt = require('./gt') +const gte = require('./gte') +const lt = require('./lt') +const lte = require('./lte') + +const cmp = (a, op, b, loose) => { + switch (op) { + case '===': + if (typeof a === 'object') { + a = a.version + } + if (typeof b === 'object') { + b = b.version + } + return a === b + + case '!==': + if (typeof a === 'object') { + a = a.version + } + if (typeof b === 'object') { + b = b.version + } + return a !== b + + case '': + case '=': + case '==': + return eq(a, b, loose) + + case '!=': + return neq(a, b, loose) + + case '>': + return gt(a, b, loose) + + case '>=': + return gte(a, b, loose) + + case '<': + return lt(a, b, loose) + + case '<=': + return lte(a, b, loose) + + default: + throw new TypeError(`Invalid operator: ${op}`) + } +} +module.exports = cmp diff --git a/node_modules/semver/functions/coerce.js b/node_modules/semver/functions/coerce.js new file mode 100644 index 0000000..cfe0275 --- /dev/null +++ b/node_modules/semver/functions/coerce.js @@ -0,0 +1,62 @@ +'use strict' + +const SemVer = require('../classes/semver') +const parse = require('./parse') +const { safeRe: re, t } = require('../internal/re') + +const coerce = (version, options) => { + if (version instanceof SemVer) { + return version + } + + if (typeof version === 'number') { + version = String(version) + } + + if (typeof version !== 'string') { + return null + } + + options = options || {} + + let match = null + if (!options.rtl) { + match = version.match(options.includePrerelease ? re[t.COERCEFULL] : re[t.COERCE]) + } else { + // Find the right-most coercible string that does not share + // a terminus with a more left-ward coercible string. + // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4' + // With includePrerelease option set, '1.2.3.4-rc' wants to coerce '2.3.4-rc', not '2.3.4' + // + // Walk through the string checking with a /g regexp + // Manually set the index so as to pick up overlapping matches. + // Stop when we get a match that ends at the string end, since no + // coercible string can be more right-ward without the same terminus. + const coerceRtlRegex = options.includePrerelease ? re[t.COERCERTLFULL] : re[t.COERCERTL] + let next + while ((next = coerceRtlRegex.exec(version)) && + (!match || match.index + match[0].length !== version.length) + ) { + if (!match || + next.index + next[0].length !== match.index + match[0].length) { + match = next + } + coerceRtlRegex.lastIndex = next.index + next[1].length + next[2].length + } + // leave it in a clean state + coerceRtlRegex.lastIndex = -1 + } + + if (match === null) { + return null + } + + const major = match[2] + const minor = match[3] || '0' + const patch = match[4] || '0' + const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : '' + const build = options.includePrerelease && match[6] ? `+${match[6]}` : '' + + return parse(`${major}.${minor}.${patch}${prerelease}${build}`, options) +} +module.exports = coerce diff --git a/node_modules/semver/functions/compare-build.js b/node_modules/semver/functions/compare-build.js new file mode 100644 index 0000000..99157cf --- /dev/null +++ b/node_modules/semver/functions/compare-build.js @@ -0,0 +1,9 @@ +'use strict' + +const SemVer = require('../classes/semver') +const compareBuild = (a, b, loose) => { + const versionA = new SemVer(a, loose) + const versionB = new SemVer(b, loose) + return versionA.compare(versionB) || versionA.compareBuild(versionB) +} +module.exports = compareBuild diff --git a/node_modules/semver/functions/compare-loose.js b/node_modules/semver/functions/compare-loose.js new file mode 100644 index 0000000..7531634 --- /dev/null +++ b/node_modules/semver/functions/compare-loose.js @@ -0,0 +1,5 @@ +'use strict' + +const compare = require('./compare') +const compareLoose = (a, b) => compare(a, b, true) +module.exports = compareLoose diff --git a/node_modules/semver/functions/compare.js b/node_modules/semver/functions/compare.js new file mode 100644 index 0000000..63d8090 --- /dev/null +++ b/node_modules/semver/functions/compare.js @@ -0,0 +1,7 @@ +'use strict' + +const SemVer = require('../classes/semver') +const compare = (a, b, loose) => + new SemVer(a, loose).compare(new SemVer(b, loose)) + +module.exports = compare diff --git a/node_modules/semver/functions/diff.js b/node_modules/semver/functions/diff.js new file mode 100644 index 0000000..c99ab51 --- /dev/null +++ b/node_modules/semver/functions/diff.js @@ -0,0 +1,60 @@ +'use strict' + +const parse = require('./parse.js') + +const diff = (version1, version2) => { + const v1 = parse(version1, null, true) + const v2 = parse(version2, null, true) + const comparison = v1.compare(v2) + + if (comparison === 0) { + return null + } + + const v1Higher = comparison > 0 + const highVersion = v1Higher ? v1 : v2 + const lowVersion = v1Higher ? v2 : v1 + const highHasPre = !!highVersion.prerelease.length + const lowHasPre = !!lowVersion.prerelease.length + + if (lowHasPre && !highHasPre) { + // Going from prerelease -> no prerelease requires some special casing + + // If the low version has only a major, then it will always be a major + // Some examples: + // 1.0.0-1 -> 1.0.0 + // 1.0.0-1 -> 1.1.1 + // 1.0.0-1 -> 2.0.0 + if (!lowVersion.patch && !lowVersion.minor) { + return 'major' + } + + // If the main part has no difference + if (lowVersion.compareMain(highVersion) === 0) { + if (lowVersion.minor && !lowVersion.patch) { + return 'minor' + } + return 'patch' + } + } + + // add the `pre` prefix if we are going to a prerelease version + const prefix = highHasPre ? 'pre' : '' + + if (v1.major !== v2.major) { + return prefix + 'major' + } + + if (v1.minor !== v2.minor) { + return prefix + 'minor' + } + + if (v1.patch !== v2.patch) { + return prefix + 'patch' + } + + // high and low are prereleases + return 'prerelease' +} + +module.exports = diff diff --git a/node_modules/semver/functions/eq.js b/node_modules/semver/functions/eq.js new file mode 100644 index 0000000..5f0eead --- /dev/null +++ b/node_modules/semver/functions/eq.js @@ -0,0 +1,5 @@ +'use strict' + +const compare = require('./compare') +const eq = (a, b, loose) => compare(a, b, loose) === 0 +module.exports = eq diff --git a/node_modules/semver/functions/gt.js b/node_modules/semver/functions/gt.js new file mode 100644 index 0000000..84a57dd --- /dev/null +++ b/node_modules/semver/functions/gt.js @@ -0,0 +1,5 @@ +'use strict' + +const compare = require('./compare') +const gt = (a, b, loose) => compare(a, b, loose) > 0 +module.exports = gt diff --git a/node_modules/semver/functions/gte.js b/node_modules/semver/functions/gte.js new file mode 100644 index 0000000..7c52bdf --- /dev/null +++ b/node_modules/semver/functions/gte.js @@ -0,0 +1,5 @@ +'use strict' + +const compare = require('./compare') +const gte = (a, b, loose) => compare(a, b, loose) >= 0 +module.exports = gte diff --git a/node_modules/semver/functions/inc.js b/node_modules/semver/functions/inc.js new file mode 100644 index 0000000..ff999e9 --- /dev/null +++ b/node_modules/semver/functions/inc.js @@ -0,0 +1,21 @@ +'use strict' + +const SemVer = require('../classes/semver') + +const inc = (version, release, options, identifier, identifierBase) => { + if (typeof (options) === 'string') { + identifierBase = identifier + identifier = options + options = undefined + } + + try { + return new SemVer( + version instanceof SemVer ? version.version : version, + options + ).inc(release, identifier, identifierBase).version + } catch (er) { + return null + } +} +module.exports = inc diff --git a/node_modules/semver/functions/lt.js b/node_modules/semver/functions/lt.js new file mode 100644 index 0000000..2fb32a0 --- /dev/null +++ b/node_modules/semver/functions/lt.js @@ -0,0 +1,5 @@ +'use strict' + +const compare = require('./compare') +const lt = (a, b, loose) => compare(a, b, loose) < 0 +module.exports = lt diff --git a/node_modules/semver/functions/lte.js b/node_modules/semver/functions/lte.js new file mode 100644 index 0000000..da9ee8f --- /dev/null +++ b/node_modules/semver/functions/lte.js @@ -0,0 +1,5 @@ +'use strict' + +const compare = require('./compare') +const lte = (a, b, loose) => compare(a, b, loose) <= 0 +module.exports = lte diff --git a/node_modules/semver/functions/major.js b/node_modules/semver/functions/major.js new file mode 100644 index 0000000..e6d08dc --- /dev/null +++ b/node_modules/semver/functions/major.js @@ -0,0 +1,5 @@ +'use strict' + +const SemVer = require('../classes/semver') +const major = (a, loose) => new SemVer(a, loose).major +module.exports = major diff --git a/node_modules/semver/functions/minor.js b/node_modules/semver/functions/minor.js new file mode 100644 index 0000000..9e70ffd --- /dev/null +++ b/node_modules/semver/functions/minor.js @@ -0,0 +1,5 @@ +'use strict' + +const SemVer = require('../classes/semver') +const minor = (a, loose) => new SemVer(a, loose).minor +module.exports = minor diff --git a/node_modules/semver/functions/neq.js b/node_modules/semver/functions/neq.js new file mode 100644 index 0000000..84326b7 --- /dev/null +++ b/node_modules/semver/functions/neq.js @@ -0,0 +1,5 @@ +'use strict' + +const compare = require('./compare') +const neq = (a, b, loose) => compare(a, b, loose) !== 0 +module.exports = neq diff --git a/node_modules/semver/functions/parse.js b/node_modules/semver/functions/parse.js new file mode 100644 index 0000000..d544d33 --- /dev/null +++ b/node_modules/semver/functions/parse.js @@ -0,0 +1,18 @@ +'use strict' + +const SemVer = require('../classes/semver') +const parse = (version, options, throwErrors = false) => { + if (version instanceof SemVer) { + return version + } + try { + return new SemVer(version, options) + } catch (er) { + if (!throwErrors) { + return null + } + throw er + } +} + +module.exports = parse diff --git a/node_modules/semver/functions/patch.js b/node_modules/semver/functions/patch.js new file mode 100644 index 0000000..7675162 --- /dev/null +++ b/node_modules/semver/functions/patch.js @@ -0,0 +1,5 @@ +'use strict' + +const SemVer = require('../classes/semver') +const patch = (a, loose) => new SemVer(a, loose).patch +module.exports = patch diff --git a/node_modules/semver/functions/prerelease.js b/node_modules/semver/functions/prerelease.js new file mode 100644 index 0000000..b8fe1db --- /dev/null +++ b/node_modules/semver/functions/prerelease.js @@ -0,0 +1,8 @@ +'use strict' + +const parse = require('./parse') +const prerelease = (version, options) => { + const parsed = parse(version, options) + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null +} +module.exports = prerelease diff --git a/node_modules/semver/functions/rcompare.js b/node_modules/semver/functions/rcompare.js new file mode 100644 index 0000000..8e1c222 --- /dev/null +++ b/node_modules/semver/functions/rcompare.js @@ -0,0 +1,5 @@ +'use strict' + +const compare = require('./compare') +const rcompare = (a, b, loose) => compare(b, a, loose) +module.exports = rcompare diff --git a/node_modules/semver/functions/rsort.js b/node_modules/semver/functions/rsort.js new file mode 100644 index 0000000..5d3d200 --- /dev/null +++ b/node_modules/semver/functions/rsort.js @@ -0,0 +1,5 @@ +'use strict' + +const compareBuild = require('./compare-build') +const rsort = (list, loose) => list.sort((a, b) => compareBuild(b, a, loose)) +module.exports = rsort diff --git a/node_modules/semver/functions/satisfies.js b/node_modules/semver/functions/satisfies.js new file mode 100644 index 0000000..a0264a2 --- /dev/null +++ b/node_modules/semver/functions/satisfies.js @@ -0,0 +1,12 @@ +'use strict' + +const Range = require('../classes/range') +const satisfies = (version, range, options) => { + try { + range = new Range(range, options) + } catch (er) { + return false + } + return range.test(version) +} +module.exports = satisfies diff --git a/node_modules/semver/functions/sort.js b/node_modules/semver/functions/sort.js new file mode 100644 index 0000000..edb24b1 --- /dev/null +++ b/node_modules/semver/functions/sort.js @@ -0,0 +1,5 @@ +'use strict' + +const compareBuild = require('./compare-build') +const sort = (list, loose) => list.sort((a, b) => compareBuild(a, b, loose)) +module.exports = sort diff --git a/node_modules/semver/functions/truncate.js b/node_modules/semver/functions/truncate.js new file mode 100644 index 0000000..8314e4e --- /dev/null +++ b/node_modules/semver/functions/truncate.js @@ -0,0 +1,48 @@ +'use strict' + +const parse = require('./parse') +const constants = require('../internal/constants') +const SemVer = require('../classes/semver') + +const truncate = (version, truncation, options) => { + if (!constants.RELEASE_TYPES.includes(truncation)) { + return null + } + + const clonedVersion = cloneInputVersion(version, options) + return clonedVersion && doTruncation(clonedVersion, truncation) +} + +const cloneInputVersion = (version, options) => { + const versionStringToParse = ( + version instanceof SemVer ? version.version : version + ) + + return parse(versionStringToParse, options) +} + +const doTruncation = (version, truncation) => { + if (isPrerelease(truncation)) { + return version.version + } + + version.prerelease = [] + + switch (truncation) { + case 'major': + version.minor = 0 + version.patch = 0 + break + case 'minor': + version.patch = 0 + break + } + + return version.format() +} + +const isPrerelease = (type) => { + return type.startsWith('pre') +} + +module.exports = truncate diff --git a/node_modules/semver/functions/valid.js b/node_modules/semver/functions/valid.js new file mode 100644 index 0000000..0db67ed --- /dev/null +++ b/node_modules/semver/functions/valid.js @@ -0,0 +1,8 @@ +'use strict' + +const parse = require('./parse') +const valid = (version, options) => { + const v = parse(version, options) + return v ? v.version : null +} +module.exports = valid diff --git a/node_modules/semver/index.js b/node_modules/semver/index.js new file mode 100644 index 0000000..bc1f608 --- /dev/null +++ b/node_modules/semver/index.js @@ -0,0 +1,93 @@ +'use strict' + +// just pre-load all the stuff that index.js lazily exports +const internalRe = require('./internal/re') +const constants = require('./internal/constants') +const SemVer = require('./classes/semver') +const identifiers = require('./internal/identifiers') +const parse = require('./functions/parse') +const valid = require('./functions/valid') +const clean = require('./functions/clean') +const inc = require('./functions/inc') +const diff = require('./functions/diff') +const major = require('./functions/major') +const minor = require('./functions/minor') +const patch = require('./functions/patch') +const prerelease = require('./functions/prerelease') +const compare = require('./functions/compare') +const rcompare = require('./functions/rcompare') +const compareLoose = require('./functions/compare-loose') +const compareBuild = require('./functions/compare-build') +const sort = require('./functions/sort') +const rsort = require('./functions/rsort') +const gt = require('./functions/gt') +const lt = require('./functions/lt') +const eq = require('./functions/eq') +const neq = require('./functions/neq') +const gte = require('./functions/gte') +const lte = require('./functions/lte') +const cmp = require('./functions/cmp') +const coerce = require('./functions/coerce') +const truncate = require('./functions/truncate') +const Comparator = require('./classes/comparator') +const Range = require('./classes/range') +const satisfies = require('./functions/satisfies') +const toComparators = require('./ranges/to-comparators') +const maxSatisfying = require('./ranges/max-satisfying') +const minSatisfying = require('./ranges/min-satisfying') +const minVersion = require('./ranges/min-version') +const validRange = require('./ranges/valid') +const outside = require('./ranges/outside') +const gtr = require('./ranges/gtr') +const ltr = require('./ranges/ltr') +const intersects = require('./ranges/intersects') +const simplifyRange = require('./ranges/simplify') +const subset = require('./ranges/subset') +module.exports = { + parse, + valid, + clean, + inc, + diff, + major, + minor, + patch, + prerelease, + compare, + rcompare, + compareLoose, + compareBuild, + sort, + rsort, + gt, + lt, + eq, + neq, + gte, + lte, + cmp, + coerce, + truncate, + Comparator, + Range, + satisfies, + toComparators, + maxSatisfying, + minSatisfying, + minVersion, + validRange, + outside, + gtr, + ltr, + intersects, + simplifyRange, + subset, + SemVer, + re: internalRe.re, + src: internalRe.src, + tokens: internalRe.t, + SEMVER_SPEC_VERSION: constants.SEMVER_SPEC_VERSION, + RELEASE_TYPES: constants.RELEASE_TYPES, + compareIdentifiers: identifiers.compareIdentifiers, + rcompareIdentifiers: identifiers.rcompareIdentifiers, +} diff --git a/node_modules/semver/internal/constants.js b/node_modules/semver/internal/constants.js new file mode 100644 index 0000000..6d1db91 --- /dev/null +++ b/node_modules/semver/internal/constants.js @@ -0,0 +1,37 @@ +'use strict' + +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +const SEMVER_SPEC_VERSION = '2.0.0' + +const MAX_LENGTH = 256 +const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || +/* istanbul ignore next */ 9007199254740991 + +// Max safe segment length for coercion. +const MAX_SAFE_COMPONENT_LENGTH = 16 + +// Max safe length for a build identifier. The max length minus 6 characters for +// the shortest version with a build 0.0.0+BUILD. +const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 + +const RELEASE_TYPES = [ + 'major', + 'premajor', + 'minor', + 'preminor', + 'patch', + 'prepatch', + 'prerelease', +] + +module.exports = { + MAX_LENGTH, + MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, + MAX_SAFE_INTEGER, + RELEASE_TYPES, + SEMVER_SPEC_VERSION, + FLAG_INCLUDE_PRERELEASE: 0b001, + FLAG_LOOSE: 0b010, +} diff --git a/node_modules/semver/internal/debug.js b/node_modules/semver/internal/debug.js new file mode 100644 index 0000000..20d1e9d --- /dev/null +++ b/node_modules/semver/internal/debug.js @@ -0,0 +1,11 @@ +'use strict' + +const debug = ( + typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG) +) ? (...args) => console.error('SEMVER', ...args) + : () => {} + +module.exports = debug diff --git a/node_modules/semver/internal/identifiers.js b/node_modules/semver/internal/identifiers.js new file mode 100644 index 0000000..d053472 --- /dev/null +++ b/node_modules/semver/internal/identifiers.js @@ -0,0 +1,29 @@ +'use strict' + +const numeric = /^[0-9]+$/ +const compareIdentifiers = (a, b) => { + if (typeof a === 'number' && typeof b === 'number') { + return a === b ? 0 : a < b ? -1 : 1 + } + + const anum = numeric.test(a) + const bnum = numeric.test(b) + + if (anum && bnum) { + a = +a + b = +b + } + + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} + +const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a) + +module.exports = { + compareIdentifiers, + rcompareIdentifiers, +} diff --git a/node_modules/semver/internal/lrucache.js b/node_modules/semver/internal/lrucache.js new file mode 100644 index 0000000..b8bf526 --- /dev/null +++ b/node_modules/semver/internal/lrucache.js @@ -0,0 +1,42 @@ +'use strict' + +class LRUCache { + constructor () { + this.max = 1000 + this.map = new Map() + } + + get (key) { + const value = this.map.get(key) + if (value === undefined) { + return undefined + } else { + // Remove the key from the map and add it to the end + this.map.delete(key) + this.map.set(key, value) + return value + } + } + + delete (key) { + return this.map.delete(key) + } + + set (key, value) { + const deleted = this.delete(key) + + if (!deleted && value !== undefined) { + // If cache is full, delete the least recently used item + if (this.map.size >= this.max) { + const firstKey = this.map.keys().next().value + this.delete(firstKey) + } + + this.map.set(key, value) + } + + return this + } +} + +module.exports = LRUCache diff --git a/node_modules/semver/internal/parse-options.js b/node_modules/semver/internal/parse-options.js new file mode 100644 index 0000000..5295454 --- /dev/null +++ b/node_modules/semver/internal/parse-options.js @@ -0,0 +1,17 @@ +'use strict' + +// parse out just the options we care about +const looseOption = Object.freeze({ loose: true }) +const emptyOpts = Object.freeze({ }) +const parseOptions = options => { + if (!options) { + return emptyOpts + } + + if (typeof options !== 'object') { + return looseOption + } + + return options +} +module.exports = parseOptions diff --git a/node_modules/semver/internal/re.js b/node_modules/semver/internal/re.js new file mode 100644 index 0000000..2ec2c22 --- /dev/null +++ b/node_modules/semver/internal/re.js @@ -0,0 +1,223 @@ +'use strict' + +const { + MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, + MAX_LENGTH, +} = require('./constants') +const debug = require('./debug') +exports = module.exports = {} + +// The actual regexps go on exports.re +const re = exports.re = [] +const safeRe = exports.safeRe = [] +const src = exports.src = [] +const safeSrc = exports.safeSrc = [] +const t = exports.t = {} +let R = 0 + +const LETTERDASHNUMBER = '[a-zA-Z0-9-]' + +// Replace some greedy regex tokens to prevent regex dos issues. These regex are +// used internally via the safeRe object since all inputs in this library get +// normalized first to trim and collapse all extra whitespace. The original +// regexes are exported for userland consumption and lower level usage. A +// future breaking change could export the safer regex only with a note that +// all input should have extra whitespace removed. +const safeRegexReplacements = [ + ['\\s', 1], + ['\\d', MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], +] + +const makeSafeRegex = (value) => { + for (const [token, max] of safeRegexReplacements) { + value = value + .split(`${token}*`).join(`${token}{0,${max}}`) + .split(`${token}+`).join(`${token}{1,${max}}`) + } + return value +} + +const createToken = (name, value, isGlobal) => { + const safe = makeSafeRegex(value) + const index = R++ + debug(name, index, value) + t[name] = index + src[index] = value + safeSrc[index] = safe + re[index] = new RegExp(value, isGlobal ? 'g' : undefined) + safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined) +} + +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*') +createToken('NUMERICIDENTIFIERLOOSE', '\\d+') + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`) + +// ## Main Version +// Three dot-separated numeric identifiers. + +createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` + + `(${src[t.NUMERICIDENTIFIER]})\\.` + + `(${src[t.NUMERICIDENTIFIER]})`) + +createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` + + `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` + + `(${src[t.NUMERICIDENTIFIERLOOSE]})`) + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. +// Non-numeric identifiers include numeric identifiers but can be longer. +// Therefore non-numeric identifiers must go first. + +createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER] +}|${src[t.NUMERICIDENTIFIER]})`) + +createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NONNUMERICIDENTIFIER] +}|${src[t.NUMERICIDENTIFIERLOOSE]})`) + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER] +}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`) + +createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE] +}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`) + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`) + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER] +}(?:\\.${src[t.BUILDIDENTIFIER]})*))`) + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +createToken('FULLPLAIN', `v?${src[t.MAINVERSION] +}${src[t.PRERELEASE]}?${ + src[t.BUILD]}?`) + +createToken('FULL', `^${src[t.FULLPLAIN]}$`) + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE] +}${src[t.PRERELEASELOOSE]}?${ + src[t.BUILD]}?`) + +createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`) + +createToken('GTLT', '((?:<|>)?=?)') + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifier, meaning "any version" +// Only the first item is strictly required. +createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`) +createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`) + +createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` + + `(?:\\.(${src[t.XRANGEIDENTIFIER]})` + + `(?:\\.(${src[t.XRANGEIDENTIFIER]})` + + `(?:${src[t.PRERELEASE]})?${ + src[t.BUILD]}?` + + `)?)?`) + +createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` + + `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` + + `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` + + `(?:${src[t.PRERELEASELOOSE]})?${ + src[t.BUILD]}?` + + `)?)?`) + +createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`) +createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`) + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +createToken('COERCEPLAIN', `${'(^|[^\\d])' + + '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` + + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` + + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`) +createToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\d])`) +createToken('COERCEFULL', src[t.COERCEPLAIN] + + `(?:${src[t.PRERELEASE]})?` + + `(?:${src[t.BUILD]})?` + + `(?:$|[^\\d])`) +createToken('COERCERTL', src[t.COERCE], true) +createToken('COERCERTLFULL', src[t.COERCEFULL], true) + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +createToken('LONETILDE', '(?:~>?)') + +createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true) +exports.tildeTrimReplace = '$1~' + +createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`) +createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`) + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +createToken('LONECARET', '(?:\\^)') + +createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true) +exports.caretTrimReplace = '$1^' + +createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`) +createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`) + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`) +createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`) + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT] +}\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true) +exports.comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` + + `\\s+-\\s+` + + `(${src[t.XRANGEPLAIN]})` + + `\\s*$`) + +createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` + + `\\s+-\\s+` + + `(${src[t.XRANGEPLAINLOOSE]})` + + `\\s*$`) + +// Star ranges basically just allow anything at all. +createToken('STAR', '(<|>)?=?\\s*\\*') +// >=0.0.0 is like a star +createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$') +createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$') diff --git a/node_modules/semver/package.json b/node_modules/semver/package.json new file mode 100644 index 0000000..6edb9ab --- /dev/null +++ b/node_modules/semver/package.json @@ -0,0 +1,78 @@ +{ + "name": "semver", + "version": "7.8.1", + "description": "The semantic version parser used by npm.", + "main": "index.js", + "scripts": { + "test": "tap", + "snap": "tap", + "lint": "npm run eslint", + "postlint": "template-oss-check", + "lintfix": "npm run eslint -- --fix", + "posttest": "npm run lint", + "template-oss-apply": "template-oss-apply --force", + "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"" + }, + "devDependencies": { + "@npmcli/eslint-config": "^6.0.0", + "@npmcli/template-oss": "5.0.0", + "benchmark": "^2.1.4", + "tap": "^16.0.0" + }, + "license": "ISC", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/node-semver.git" + }, + "bin": { + "semver": "bin/semver.js" + }, + "files": [ + "bin/", + "lib/", + "classes/", + "functions/", + "internal/", + "ranges/", + "index.js", + "preload.js", + "range.bnf" + ], + "tap": { + "timeout": 30, + "coverage-map": "map.js", + "nyc-arg": [ + "--exclude", + "tap-snapshots/**" + ] + }, + "engines": { + "node": ">=10" + }, + "author": "GitHub Inc.", + "templateOSS": { + "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", + "version": "5.0.0", + "engines": ">=10", + "distPaths": [ + "classes/", + "functions/", + "internal/", + "ranges/", + "index.js", + "preload.js", + "range.bnf" + ], + "allowPaths": [ + "/classes/", + "/functions/", + "/internal/", + "/ranges/", + "/index.js", + "/preload.js", + "/range.bnf", + "/benchmarks" + ], + "publish": "true" + } +} diff --git a/node_modules/semver/preload.js b/node_modules/semver/preload.js new file mode 100644 index 0000000..e6c47b9 --- /dev/null +++ b/node_modules/semver/preload.js @@ -0,0 +1,4 @@ +'use strict' + +// XXX remove in v8 or beyond +module.exports = require('./index.js') diff --git a/node_modules/semver/range.bnf b/node_modules/semver/range.bnf new file mode 100644 index 0000000..a7a4bc3 --- /dev/null +++ b/node_modules/semver/range.bnf @@ -0,0 +1,17 @@ +range-set ::= range ( logical-or range ) * +logical-or ::= ( ' ' ) * '||' ( ' ' ) * +range ::= hyphen | simple ( ' ' simple ) * | '' +hyphen ::= partial ' - ' partial +simple ::= primitive | partial | tilde | caret +primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial +partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )? +xr ::= 'x' | 'X' | '*' | nr +nr ::= '0' | [1-9] ( [0-9] ) * +tilde ::= '~' partial +caret ::= '^' partial +qualifier ::= ( '-' pre )? ( '+' build )? +pre ::= prepart ( '.' prepart ) * +prepart ::= nr | alphanumid +build ::= buildid ( '.' buildid ) * +alphanumid ::= ( [0-9] ) * [A-Za-z-] [-0-9A-Za-z] * +buildid ::= [-0-9A-Za-z]+ diff --git a/node_modules/semver/ranges/gtr.js b/node_modules/semver/ranges/gtr.js new file mode 100644 index 0000000..0e7601f --- /dev/null +++ b/node_modules/semver/ranges/gtr.js @@ -0,0 +1,6 @@ +'use strict' + +// Determine if version is greater than all the versions possible in the range. +const outside = require('./outside') +const gtr = (version, range, options) => outside(version, range, '>', options) +module.exports = gtr diff --git a/node_modules/semver/ranges/intersects.js b/node_modules/semver/ranges/intersects.js new file mode 100644 index 0000000..917be7e --- /dev/null +++ b/node_modules/semver/ranges/intersects.js @@ -0,0 +1,9 @@ +'use strict' + +const Range = require('../classes/range') +const intersects = (r1, r2, options) => { + r1 = new Range(r1, options) + r2 = new Range(r2, options) + return r1.intersects(r2, options) +} +module.exports = intersects diff --git a/node_modules/semver/ranges/ltr.js b/node_modules/semver/ranges/ltr.js new file mode 100644 index 0000000..aa5e568 --- /dev/null +++ b/node_modules/semver/ranges/ltr.js @@ -0,0 +1,6 @@ +'use strict' + +const outside = require('./outside') +// Determine if version is less than all the versions possible in the range +const ltr = (version, range, options) => outside(version, range, '<', options) +module.exports = ltr diff --git a/node_modules/semver/ranges/max-satisfying.js b/node_modules/semver/ranges/max-satisfying.js new file mode 100644 index 0000000..01fe5ae --- /dev/null +++ b/node_modules/semver/ranges/max-satisfying.js @@ -0,0 +1,27 @@ +'use strict' + +const SemVer = require('../classes/semver') +const Range = require('../classes/range') + +const maxSatisfying = (versions, range, options) => { + let max = null + let maxSV = null + let rangeObj = null + try { + rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach((v) => { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!max || maxSV.compare(v) === -1) { + // compare(max, v, true) + max = v + maxSV = new SemVer(max, options) + } + } + }) + return max +} +module.exports = maxSatisfying diff --git a/node_modules/semver/ranges/min-satisfying.js b/node_modules/semver/ranges/min-satisfying.js new file mode 100644 index 0000000..af89c8e --- /dev/null +++ b/node_modules/semver/ranges/min-satisfying.js @@ -0,0 +1,26 @@ +'use strict' + +const SemVer = require('../classes/semver') +const Range = require('../classes/range') +const minSatisfying = (versions, range, options) => { + let min = null + let minSV = null + let rangeObj = null + try { + rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach((v) => { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!min || minSV.compare(v) === 1) { + // compare(min, v, true) + min = v + minSV = new SemVer(min, options) + } + } + }) + return min +} +module.exports = minSatisfying diff --git a/node_modules/semver/ranges/min-version.js b/node_modules/semver/ranges/min-version.js new file mode 100644 index 0000000..09a65aa --- /dev/null +++ b/node_modules/semver/ranges/min-version.js @@ -0,0 +1,63 @@ +'use strict' + +const SemVer = require('../classes/semver') +const Range = require('../classes/range') +const gt = require('../functions/gt') + +const minVersion = (range, loose) => { + range = new Range(range, loose) + + let minver = new SemVer('0.0.0') + if (range.test(minver)) { + return minver + } + + minver = new SemVer('0.0.0-0') + if (range.test(minver)) { + return minver + } + + minver = null + for (let i = 0; i < range.set.length; ++i) { + const comparators = range.set[i] + + let setMin = null + comparators.forEach((comparator) => { + // Clone to avoid manipulating the comparator's semver object. + const compver = new SemVer(comparator.semver.version) + switch (comparator.operator) { + case '>': + if (compver.prerelease.length === 0) { + compver.patch++ + } else { + compver.prerelease.push(0) + } + compver.raw = compver.format() + /* fallthrough */ + case '': + case '>=': + if (!setMin || gt(compver, setMin)) { + setMin = compver + } + break + case '<': + case '<=': + /* Ignore maximum versions */ + break + /* istanbul ignore next */ + default: + throw new Error(`Unexpected operation: ${comparator.operator}`) + } + }) + if (setMin && (!minver || gt(minver, setMin))) { + minver = setMin + } + } + + if (minver && range.test(minver)) { + return minver + } + + return null +} +module.exports = minVersion diff --git a/node_modules/semver/ranges/outside.js b/node_modules/semver/ranges/outside.js new file mode 100644 index 0000000..ca74421 --- /dev/null +++ b/node_modules/semver/ranges/outside.js @@ -0,0 +1,82 @@ +'use strict' + +const SemVer = require('../classes/semver') +const Comparator = require('../classes/comparator') +const { ANY } = Comparator +const Range = require('../classes/range') +const satisfies = require('../functions/satisfies') +const gt = require('../functions/gt') +const lt = require('../functions/lt') +const lte = require('../functions/lte') +const gte = require('../functions/gte') + +const outside = (version, range, hilo, options) => { + version = new SemVer(version, options) + range = new Range(range, options) + + let gtfn, ltefn, ltfn, comp, ecomp + switch (hilo) { + case '>': + gtfn = gt + ltefn = lte + ltfn = lt + comp = '>' + ecomp = '>=' + break + case '<': + gtfn = lt + ltefn = gte + ltfn = gt + comp = '<' + ecomp = '<=' + break + default: + throw new TypeError('Must provide a hilo val of "<" or ">"') + } + + // If it satisfies the range it is not outside + if (satisfies(version, range, options)) { + return false + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (let i = 0; i < range.set.length; ++i) { + const comparators = range.set[i] + + let high = null + let low = null + + comparators.forEach((comparator) => { + if (comparator.semver === ANY) { + comparator = new Comparator('>=0.0.0') + } + high = high || comparator + low = low || comparator + if (gtfn(comparator.semver, high.semver, options)) { + high = comparator + } else if (ltfn(comparator.semver, low.semver, options)) { + low = comparator + } + }) + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false + } + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false + } + } + return true +} + +module.exports = outside diff --git a/node_modules/semver/ranges/simplify.js b/node_modules/semver/ranges/simplify.js new file mode 100644 index 0000000..262732e --- /dev/null +++ b/node_modules/semver/ranges/simplify.js @@ -0,0 +1,49 @@ +'use strict' + +// given a set of versions and a range, create a "simplified" range +// that includes the same versions that the original range does +// If the original range is shorter than the simplified one, return that. +const satisfies = require('../functions/satisfies.js') +const compare = require('../functions/compare.js') +module.exports = (versions, range, options) => { + const set = [] + let first = null + let prev = null + const v = versions.sort((a, b) => compare(a, b, options)) + for (const version of v) { + const included = satisfies(version, range, options) + if (included) { + prev = version + if (!first) { + first = version + } + } else { + if (prev) { + set.push([first, prev]) + } + prev = null + first = null + } + } + if (first) { + set.push([first, null]) + } + + const ranges = [] + for (const [min, max] of set) { + if (min === max) { + ranges.push(min) + } else if (!max && min === v[0]) { + ranges.push('*') + } else if (!max) { + ranges.push(`>=${min}`) + } else if (min === v[0]) { + ranges.push(`<=${max}`) + } else { + ranges.push(`${min} - ${max}`) + } + } + const simplified = ranges.join(' || ') + const original = typeof range.raw === 'string' ? range.raw : String(range) + return simplified.length < original.length ? simplified : range +} diff --git a/node_modules/semver/ranges/subset.js b/node_modules/semver/ranges/subset.js new file mode 100644 index 0000000..a949832 --- /dev/null +++ b/node_modules/semver/ranges/subset.js @@ -0,0 +1,249 @@ +'use strict' + +const Range = require('../classes/range.js') +const Comparator = require('../classes/comparator.js') +const { ANY } = Comparator +const satisfies = require('../functions/satisfies.js') +const compare = require('../functions/compare.js') + +// Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff: +// - Every simple range `r1, r2, ...` is a null set, OR +// - Every simple range `r1, r2, ...` which is not a null set is a subset of +// some `R1, R2, ...` +// +// Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff: +// - If c is only the ANY comparator +// - If C is only the ANY comparator, return true +// - Else if in prerelease mode, return false +// - else replace c with `[>=0.0.0]` +// - If C is only the ANY comparator +// - if in prerelease mode, return true +// - else replace C with `[>=0.0.0]` +// - Let EQ be the set of = comparators in c +// - If EQ is more than one, return true (null set) +// - Let GT be the highest > or >= comparator in c +// - Let LT be the lowest < or <= comparator in c +// - If GT and LT, and GT.semver > LT.semver, return true (null set) +// - If any C is a = range, and GT or LT are set, return false +// - If EQ +// - If GT, and EQ does not satisfy GT, return true (null set) +// - If LT, and EQ does not satisfy LT, return true (null set) +// - If EQ satisfies every C, return true +// - Else return false +// - If GT +// - If GT.semver is lower than any > or >= comp in C, return false +// - If GT is >=, and GT.semver does not satisfy every C, return false +// - If GT.semver has a prerelease, and not in prerelease mode +// - If no C has a prerelease and the GT.semver tuple, return false +// - If LT +// - If LT.semver is greater than any < or <= comp in C, return false +// - If LT is <=, and LT.semver does not satisfy every C, return false +// - If LT.semver has a prerelease, and not in prerelease mode +// - If no C has a prerelease and the LT.semver tuple, return false +// - Else return true + +const subset = (sub, dom, options = {}) => { + if (sub === dom) { + return true + } + + sub = new Range(sub, options) + dom = new Range(dom, options) + let sawNonNull = false + + OUTER: for (const simpleSub of sub.set) { + for (const simpleDom of dom.set) { + const isSub = simpleSubset(simpleSub, simpleDom, options) + sawNonNull = sawNonNull || isSub !== null + if (isSub) { + continue OUTER + } + } + // the null set is a subset of everything, but null simple ranges in + // a complex range should be ignored. so if we saw a non-null range, + // then we know this isn't a subset, but if EVERY simple range was null, + // then it is a subset. + if (sawNonNull) { + return false + } + } + return true +} + +const minimumVersionWithPreRelease = [new Comparator('>=0.0.0-0')] +const minimumVersion = [new Comparator('>=0.0.0')] + +const simpleSubset = (sub, dom, options) => { + if (sub === dom) { + return true + } + + if (sub.length === 1 && sub[0].semver === ANY) { + if (dom.length === 1 && dom[0].semver === ANY) { + return true + } else if (options.includePrerelease) { + sub = minimumVersionWithPreRelease + } else { + sub = minimumVersion + } + } + + if (dom.length === 1 && dom[0].semver === ANY) { + if (options.includePrerelease) { + return true + } else { + dom = minimumVersion + } + } + + const eqSet = new Set() + let gt, lt + for (const c of sub) { + if (c.operator === '>' || c.operator === '>=') { + gt = higherGT(gt, c, options) + } else if (c.operator === '<' || c.operator === '<=') { + lt = lowerLT(lt, c, options) + } else { + eqSet.add(c.semver) + } + } + + if (eqSet.size > 1) { + return null + } + + let gtltComp + if (gt && lt) { + gtltComp = compare(gt.semver, lt.semver, options) + if (gtltComp > 0) { + return null + } else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<=')) { + return null + } + } + + // will iterate one or zero times + for (const eq of eqSet) { + if (gt && !satisfies(eq, String(gt), options)) { + return null + } + + if (lt && !satisfies(eq, String(lt), options)) { + return null + } + + for (const c of dom) { + if (!satisfies(eq, String(c), options)) { + return false + } + } + + return true + } + + let higher, lower + let hasDomLT, hasDomGT + // if the subset has a prerelease, we need a comparator in the superset + // with the same tuple and a prerelease, or it's not a subset + let needDomLTPre = lt && + !options.includePrerelease && + lt.semver.prerelease.length ? lt.semver : false + let needDomGTPre = gt && + !options.includePrerelease && + gt.semver.prerelease.length ? gt.semver : false + // exception: <1.2.3-0 is the same as <1.2.3 + if (needDomLTPre && needDomLTPre.prerelease.length === 1 && + lt.operator === '<' && needDomLTPre.prerelease[0] === 0) { + needDomLTPre = false + } + + for (const c of dom) { + hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>=' + hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<=' + if (gt) { + if (needDomGTPre) { + if (c.semver.prerelease && c.semver.prerelease.length && + c.semver.major === needDomGTPre.major && + c.semver.minor === needDomGTPre.minor && + c.semver.patch === needDomGTPre.patch) { + needDomGTPre = false + } + } + if (c.operator === '>' || c.operator === '>=') { + higher = higherGT(gt, c, options) + if (higher === c && higher !== gt) { + return false + } + } else if (gt.operator === '>=' && !c.test(gt.semver)) { + return false + } + } + if (lt) { + if (needDomLTPre) { + if (c.semver.prerelease && c.semver.prerelease.length && + c.semver.major === needDomLTPre.major && + c.semver.minor === needDomLTPre.minor && + c.semver.patch === needDomLTPre.patch) { + needDomLTPre = false + } + } + if (c.operator === '<' || c.operator === '<=') { + lower = lowerLT(lt, c, options) + if (lower === c && lower !== lt) { + return false + } + } else if (lt.operator === '<=' && !c.test(lt.semver)) { + return false + } + } + if (!c.operator && (lt || gt) && gtltComp !== 0) { + return false + } + } + + // if there was a < or >, and nothing in the dom, then must be false + // UNLESS it was limited by another range in the other direction. + // Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0 + if (gt && hasDomLT && !lt && gtltComp !== 0) { + return false + } + + if (lt && hasDomGT && !gt && gtltComp !== 0) { + return false + } + + // we needed a prerelease range in a specific tuple, but didn't get one + // then this isn't a subset. eg >=1.2.3-pre is not a subset of >=1.0.0, + // because it includes prereleases in the 1.2.3 tuple + if (needDomGTPre || needDomLTPre) { + return false + } + + return true +} + +// >=1.2.3 is lower than >1.2.3 +const higherGT = (a, b, options) => { + if (!a) { + return b + } + const comp = compare(a.semver, b.semver, options) + return comp > 0 ? a + : comp < 0 ? b + : b.operator === '>' && a.operator === '>=' ? b + : a +} + +// <=1.2.3 is higher than <1.2.3 +const lowerLT = (a, b, options) => { + if (!a) { + return b + } + const comp = compare(a.semver, b.semver, options) + return comp < 0 ? a + : comp > 0 ? b + : b.operator === '<' && a.operator === '<=' ? b + : a +} + +module.exports = subset diff --git a/node_modules/semver/ranges/to-comparators.js b/node_modules/semver/ranges/to-comparators.js new file mode 100644 index 0000000..5be2519 --- /dev/null +++ b/node_modules/semver/ranges/to-comparators.js @@ -0,0 +1,10 @@ +'use strict' + +const Range = require('../classes/range') + +// Mostly just for testing and legacy API reasons +const toComparators = (range, options) => + new Range(range, options).set + .map(comp => comp.map(c => c.value).join(' ').trim().split(' ')) + +module.exports = toComparators diff --git a/node_modules/semver/ranges/valid.js b/node_modules/semver/ranges/valid.js new file mode 100644 index 0000000..cc6b0e9 --- /dev/null +++ b/node_modules/semver/ranges/valid.js @@ -0,0 +1,13 @@ +'use strict' + +const Range = require('../classes/range') +const validRange = (range, options) => { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, options).range || '*' + } catch (er) { + return null + } +} +module.exports = validRange diff --git a/node_modules/split2/LICENSE b/node_modules/split2/LICENSE new file mode 100644 index 0000000..a91afe5 --- /dev/null +++ b/node_modules/split2/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2014-2018, Matteo Collina + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/split2/README.md b/node_modules/split2/README.md new file mode 100644 index 0000000..36f03ab --- /dev/null +++ b/node_modules/split2/README.md @@ -0,0 +1,85 @@ +# Split2(matcher, mapper, options) + +![ci](https://github.com/mcollina/split2/workflows/ci/badge.svg) + +Break up a stream and reassemble it so that each line is a chunk. +`split2` is inspired by [@dominictarr](https://github.com/dominictarr) [`split`](https://github.com/dominictarr/split) module, +and it is totally API compatible with it. +However, it is based on Node.js core [`Transform`](https://nodejs.org/api/stream.html#stream_new_stream_transform_options). + +`matcher` may be a `String`, or a `RegExp`. Example, read every line in a file ... + +``` js + fs.createReadStream(file) + .pipe(split2()) + .on('data', function (line) { + //each chunk now is a separate line! + }) + +``` + +`split` takes the same arguments as `string.split` except it defaults to '/\r?\n/', and the optional `limit` paremeter is ignored. +[String#split](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/split) + +`split` takes an optional options object on it's third argument, which +is directly passed as a +[Transform](https://nodejs.org/api/stream.html#stream_new_stream_transform_options) +option. + +Additionally, the `.maxLength` and `.skipOverflow` options are implemented, which set limits on the internal +buffer size and the stream's behavior when the limit is exceeded. There is no limit unless `maxLength` is set. When +the internal buffer size exceeds `maxLength`, the stream emits an error by default. You may also set `skipOverflow` to +true to suppress the error and instead skip past any lines that cause the internal buffer to exceed `maxLength`. + +Calling `.destroy` will make the stream emit `close`. Use this to perform cleanup logic + +``` js +var splitFile = function(filename) { + var file = fs.createReadStream(filename) + + return file + .pipe(split2()) + .on('close', function() { + // destroy the file stream in case the split stream was destroyed + file.destroy() + }) +} + +var stream = splitFile('my-file.txt') + +stream.destroy() // will destroy the input file stream +``` + +# NDJ - Newline Delimited Json + +`split2` accepts a function which transforms each line. + +``` js +fs.createReadStream(file) + .pipe(split2(JSON.parse)) + .on('data', function (obj) { + //each chunk now is a js object + }) + .on("error", function(error) { + //handling parsing errors + }) +``` + +However, in [@dominictarr](https://github.com/dominictarr) [`split`](https://github.com/dominictarr/split) the mapper +is wrapped in a try-catch, while here it is not: if your parsing logic can throw, wrap it yourself. Otherwise, you can also use the stream error handling when mapper function throw. + +# License + +Copyright (c) 2014-2021, Matteo Collina + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/split2/bench.js b/node_modules/split2/bench.js new file mode 100644 index 0000000..15ec5df --- /dev/null +++ b/node_modules/split2/bench.js @@ -0,0 +1,27 @@ +'use strict' + +const split = require('./') +const bench = require('fastbench') +const binarySplit = require('binary-split') +const fs = require('fs') + +function benchSplit (cb) { + fs.createReadStream('package.json') + .pipe(split()) + .on('end', cb) + .resume() +} + +function benchBinarySplit (cb) { + fs.createReadStream('package.json') + .pipe(binarySplit()) + .on('end', cb) + .resume() +} + +const run = bench([ + benchSplit, + benchBinarySplit +], 10000) + +run(run) diff --git a/node_modules/split2/index.js b/node_modules/split2/index.js new file mode 100644 index 0000000..9b59f6c --- /dev/null +++ b/node_modules/split2/index.js @@ -0,0 +1,141 @@ +/* +Copyright (c) 2014-2021, Matteo Collina + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +'use strict' + +const { Transform } = require('stream') +const { StringDecoder } = require('string_decoder') +const kLast = Symbol('last') +const kDecoder = Symbol('decoder') + +function transform (chunk, enc, cb) { + let list + if (this.overflow) { // Line buffer is full. Skip to start of next line. + const buf = this[kDecoder].write(chunk) + list = buf.split(this.matcher) + + if (list.length === 1) return cb() // Line ending not found. Discard entire chunk. + + // Line ending found. Discard trailing fragment of previous line and reset overflow state. + list.shift() + this.overflow = false + } else { + this[kLast] += this[kDecoder].write(chunk) + list = this[kLast].split(this.matcher) + } + + this[kLast] = list.pop() + + for (let i = 0; i < list.length; i++) { + try { + push(this, this.mapper(list[i])) + } catch (error) { + return cb(error) + } + } + + this.overflow = this[kLast].length > this.maxLength + if (this.overflow && !this.skipOverflow) { + cb(new Error('maximum buffer reached')) + return + } + + cb() +} + +function flush (cb) { + // forward any gibberish left in there + this[kLast] += this[kDecoder].end() + + if (this[kLast]) { + try { + push(this, this.mapper(this[kLast])) + } catch (error) { + return cb(error) + } + } + + cb() +} + +function push (self, val) { + if (val !== undefined) { + self.push(val) + } +} + +function noop (incoming) { + return incoming +} + +function split (matcher, mapper, options) { + // Set defaults for any arguments not supplied. + matcher = matcher || /\r?\n/ + mapper = mapper || noop + options = options || {} + + // Test arguments explicitly. + switch (arguments.length) { + case 1: + // If mapper is only argument. + if (typeof matcher === 'function') { + mapper = matcher + matcher = /\r?\n/ + // If options is only argument. + } else if (typeof matcher === 'object' && !(matcher instanceof RegExp) && !matcher[Symbol.split]) { + options = matcher + matcher = /\r?\n/ + } + break + + case 2: + // If mapper and options are arguments. + if (typeof matcher === 'function') { + options = mapper + mapper = matcher + matcher = /\r?\n/ + // If matcher and options are arguments. + } else if (typeof mapper === 'object') { + options = mapper + mapper = noop + } + } + + options = Object.assign({}, options) + options.autoDestroy = true + options.transform = transform + options.flush = flush + options.readableObjectMode = true + + const stream = new Transform(options) + + stream[kLast] = '' + stream[kDecoder] = new StringDecoder('utf8') + stream.matcher = matcher + stream.mapper = mapper + stream.maxLength = options.maxLength + stream.skipOverflow = options.skipOverflow || false + stream.overflow = false + stream._destroy = function (err, cb) { + // Weird Node v12 bug that we need to work around + this._writableState.errorEmitted = false + cb(err) + } + + return stream +} + +module.exports = split diff --git a/node_modules/split2/package.json b/node_modules/split2/package.json new file mode 100644 index 0000000..e04bcc8 --- /dev/null +++ b/node_modules/split2/package.json @@ -0,0 +1,39 @@ +{ + "name": "split2", + "version": "4.2.0", + "description": "split a Text Stream into a Line Stream, using Stream 3", + "main": "index.js", + "scripts": { + "lint": "standard --verbose", + "unit": "nyc --lines 100 --branches 100 --functions 100 --check-coverage --reporter=text tape test.js", + "coverage": "nyc --reporter=html --reporter=cobertura --reporter=text tape test/test.js", + "test:report": "npm run lint && npm run unit:report", + "test": "npm run lint && npm run unit", + "legacy": "tape test.js" + }, + "pre-commit": [ + "test" + ], + "website": "https://github.com/mcollina/split2", + "repository": { + "type": "git", + "url": "https://github.com/mcollina/split2.git" + }, + "bugs": { + "url": "http://github.com/mcollina/split2/issues" + }, + "engines": { + "node": ">= 10.x" + }, + "author": "Matteo Collina ", + "license": "ISC", + "devDependencies": { + "binary-split": "^1.0.3", + "callback-stream": "^1.1.0", + "fastbench": "^1.0.0", + "nyc": "^15.0.1", + "pre-commit": "^1.1.2", + "standard": "^17.0.0", + "tape": "^5.0.0" + } +} diff --git a/node_modules/split2/test.js b/node_modules/split2/test.js new file mode 100644 index 0000000..a7f9838 --- /dev/null +++ b/node_modules/split2/test.js @@ -0,0 +1,409 @@ +'use strict' + +const test = require('tape') +const split = require('./') +const callback = require('callback-stream') +const strcb = callback.bind(null, { decodeStrings: false }) +const objcb = callback.bind(null, { objectMode: true }) + +test('split two lines on end', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.end('hello\nworld') +}) + +test('split two lines on two writes', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.write('hello') + input.write('\nworld') + input.end() +}) + +test('split four lines on three writes', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world', 'bye', 'world']) + })) + + input.write('hello\nwor') + input.write('ld\nbye\nwo') + input.write('rld') + input.end() +}) + +test('accumulate multiple writes', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['helloworld']) + })) + + input.write('hello') + input.write('world') + input.end() +}) + +test('split using a custom string matcher', function (t) { + t.plan(2) + + const input = split('~') + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.end('hello~world') +}) + +test('split using a custom regexp matcher', function (t) { + t.plan(2) + + const input = split(/~/) + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.end('hello~world') +}) + +test('support an option argument', function (t) { + t.plan(2) + + const input = split({ highWaterMark: 2 }) + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.end('hello\nworld') +}) + +test('support a mapper function', function (t) { + t.plan(2) + + const a = { a: '42' } + const b = { b: '24' } + + const input = split(JSON.parse) + + input.pipe(objcb(function (err, list) { + t.error(err) + t.deepEqual(list, [a, b]) + })) + + input.write(JSON.stringify(a)) + input.write('\n') + input.end(JSON.stringify(b)) +}) + +test('split lines windows-style', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.end('hello\r\nworld') +}) + +test('splits a buffer', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.end(Buffer.from('hello\nworld')) +}) + +test('do not end on undefined', function (t) { + t.plan(2) + + const input = split(function (line) { }) + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, []) + })) + + input.end(Buffer.from('hello\nworld')) +}) + +test('has destroy method', function (t) { + t.plan(1) + + const input = split(function (line) { }) + + input.on('close', function () { + t.ok(true, 'close emitted') + t.end() + }) + + input.destroy() +}) + +test('support custom matcher and mapper', function (t) { + t.plan(4) + + const a = { a: '42' } + const b = { b: '24' } + const input = split('~', JSON.parse) + + t.equal(input.matcher, '~') + t.equal(typeof input.mapper, 'function') + + input.pipe(objcb(function (err, list) { + t.notOk(err, 'no errors') + t.deepEqual(list, [a, b]) + })) + + input.write(JSON.stringify(a)) + input.write('~') + input.end(JSON.stringify(b)) +}) + +test('support custom matcher and options', function (t) { + t.plan(6) + + const input = split('~', { highWaterMark: 1024 }) + + t.equal(input.matcher, '~') + t.equal(typeof input.mapper, 'function') + t.equal(input._readableState.highWaterMark, 1024) + t.equal(input._writableState.highWaterMark, 1024) + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.end('hello~world') +}) + +test('support mapper and options', function (t) { + t.plan(6) + + const a = { a: '42' } + const b = { b: '24' } + const input = split(JSON.parse, { highWaterMark: 1024 }) + + t.ok(input.matcher instanceof RegExp, 'matcher is RegExp') + t.equal(typeof input.mapper, 'function') + t.equal(input._readableState.highWaterMark, 1024) + t.equal(input._writableState.highWaterMark, 1024) + + input.pipe(objcb(function (err, list) { + t.error(err) + t.deepEqual(list, [a, b]) + })) + + input.write(JSON.stringify(a)) + input.write('\n') + input.end(JSON.stringify(b)) +}) + +test('split utf8 chars', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['烫烫烫', '锟斤拷']) + })) + + const buf = Buffer.from('烫烫烫\r\n锟斤拷', 'utf8') + for (let i = 0; i < buf.length; ++i) { + input.write(buf.slice(i, i + 1)) + } + input.end() +}) + +test('split utf8 chars 2by2', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['烫烫烫', '烫烫烫']) + })) + + const str = '烫烫烫\r\n烫烫烫' + const buf = Buffer.from(str, 'utf8') + for (let i = 0; i < buf.length; i += 2) { + input.write(buf.slice(i, i + 2)) + } + input.end() +}) + +test('split lines when the \n comes at the end of a chunk', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.write('hello\n') + input.end('world') +}) + +test('truncated utf-8 char', function (t) { + t.plan(2) + + const input = split() + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['烫' + Buffer.from('e7', 'hex').toString()]) + })) + + const str = '烫烫' + const buf = Buffer.from(str, 'utf8') + + input.write(buf.slice(0, 3)) + input.end(buf.slice(3, 4)) +}) + +test('maximum buffer limit', function (t) { + t.plan(1) + + const input = split({ maxLength: 2 }) + input.on('error', function (err) { + t.ok(err) + }) + + input.resume() + + input.write('hey') +}) + +test('readable highWaterMark', function (t) { + const input = split() + t.equal(input._readableState.highWaterMark, 16) + t.end() +}) + +test('maxLength < chunk size', function (t) { + t.plan(2) + + const input = split({ maxLength: 2 }) + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['a', 'b']) + })) + + input.end('a\nb') +}) + +test('maximum buffer limit w/skip', function (t) { + t.plan(2) + + const input = split({ maxLength: 2, skipOverflow: true }) + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['a', 'b', 'c']) + })) + + input.write('a\n123') + input.write('456') + input.write('789\nb\nc') + input.end() +}) + +test("don't modify the options object", function (t) { + t.plan(2) + + const options = {} + const input = split(options) + + input.pipe(strcb(function (err, list) { + t.error(err) + t.same(options, {}) + })) + + input.end() +}) + +test('mapper throws flush', function (t) { + t.plan(1) + const error = new Error() + const input = split(function () { + throw error + }) + + input.on('error', (err, list) => { + t.same(err, error) + }) + input.end('hello') +}) + +test('mapper throws on transform', function (t) { + t.plan(1) + + const error = new Error() + const input = split(function (l) { + throw error + }) + + input.on('error', (err) => { + t.same(err, error) + }) + input.write('a') + input.write('\n') + input.end('b') +}) + +test('supports Symbol.split', function (t) { + t.plan(2) + + const input = split({ + [Symbol.split] (str) { + return str.split('~') + } + }) + + input.pipe(strcb(function (err, list) { + t.error(err) + t.deepEqual(list, ['hello', 'world']) + })) + + input.end('hello~world') +}) diff --git a/node_modules/xtend/.jshintrc b/node_modules/xtend/.jshintrc new file mode 100644 index 0000000..77887b5 --- /dev/null +++ b/node_modules/xtend/.jshintrc @@ -0,0 +1,30 @@ +{ + "maxdepth": 4, + "maxstatements": 200, + "maxcomplexity": 12, + "maxlen": 80, + "maxparams": 5, + + "curly": true, + "eqeqeq": true, + "immed": true, + "latedef": false, + "noarg": true, + "noempty": true, + "nonew": true, + "undef": true, + "unused": "vars", + "trailing": true, + + "quotmark": true, + "expr": true, + "asi": true, + + "browser": false, + "esnext": true, + "devel": false, + "node": false, + "nonstandard": false, + + "predef": ["require", "module", "__dirname", "__filename"] +} diff --git a/node_modules/xtend/LICENSE b/node_modules/xtend/LICENSE new file mode 100644 index 0000000..0099f4f --- /dev/null +++ b/node_modules/xtend/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) +Copyright (c) 2012-2014 Raynos. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/xtend/README.md b/node_modules/xtend/README.md new file mode 100644 index 0000000..4a2703c --- /dev/null +++ b/node_modules/xtend/README.md @@ -0,0 +1,32 @@ +# xtend + +[![browser support][3]][4] + +[![locked](http://badges.github.io/stability-badges/dist/locked.svg)](http://github.com/badges/stability-badges) + +Extend like a boss + +xtend is a basic utility library which allows you to extend an object by appending all of the properties from each object in a list. When there are identical properties, the right-most property takes precedence. + +## Examples + +```js +var extend = require("xtend") + +// extend returns a new object. Does not mutate arguments +var combination = extend({ + a: "a", + b: "c" +}, { + b: "b" +}) +// { a: "a", b: "b" } +``` + +## Stability status: Locked + +## MIT Licensed + + + [3]: http://ci.testling.com/Raynos/xtend.png + [4]: http://ci.testling.com/Raynos/xtend diff --git a/node_modules/xtend/immutable.js b/node_modules/xtend/immutable.js new file mode 100644 index 0000000..94889c9 --- /dev/null +++ b/node_modules/xtend/immutable.js @@ -0,0 +1,19 @@ +module.exports = extend + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +function extend() { + var target = {} + + for (var i = 0; i < arguments.length; i++) { + var source = arguments[i] + + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target +} diff --git a/node_modules/xtend/mutable.js b/node_modules/xtend/mutable.js new file mode 100644 index 0000000..72debed --- /dev/null +++ b/node_modules/xtend/mutable.js @@ -0,0 +1,17 @@ +module.exports = extend + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +function extend(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] + + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target +} diff --git a/node_modules/xtend/package.json b/node_modules/xtend/package.json new file mode 100644 index 0000000..f7a39d1 --- /dev/null +++ b/node_modules/xtend/package.json @@ -0,0 +1,55 @@ +{ + "name": "xtend", + "version": "4.0.2", + "description": "extend like a boss", + "keywords": [ + "extend", + "merge", + "options", + "opts", + "object", + "array" + ], + "author": "Raynos ", + "repository": "git://github.com/Raynos/xtend.git", + "main": "immutable", + "scripts": { + "test": "node test" + }, + "dependencies": {}, + "devDependencies": { + "tape": "~1.1.0" + }, + "homepage": "https://github.com/Raynos/xtend", + "contributors": [ + { + "name": "Jake Verbaten" + }, + { + "name": "Matt Esch" + } + ], + "bugs": { + "url": "https://github.com/Raynos/xtend/issues", + "email": "raynos2@gmail.com" + }, + "license": "MIT", + "testling": { + "files": "test.js", + "browsers": [ + "ie/7..latest", + "firefox/16..latest", + "firefox/nightly", + "chrome/22..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest" + ] + }, + "engines": { + "node": ">=0.4" + } +} diff --git a/node_modules/xtend/test.js b/node_modules/xtend/test.js new file mode 100644 index 0000000..b895b42 --- /dev/null +++ b/node_modules/xtend/test.js @@ -0,0 +1,103 @@ +var test = require("tape") +var extend = require("./") +var mutableExtend = require("./mutable") + +test("merge", function(assert) { + var a = { a: "foo" } + var b = { b: "bar" } + + assert.deepEqual(extend(a, b), { a: "foo", b: "bar" }) + assert.end() +}) + +test("replace", function(assert) { + var a = { a: "foo" } + var b = { a: "bar" } + + assert.deepEqual(extend(a, b), { a: "bar" }) + assert.end() +}) + +test("undefined", function(assert) { + var a = { a: undefined } + var b = { b: "foo" } + + assert.deepEqual(extend(a, b), { a: undefined, b: "foo" }) + assert.deepEqual(extend(b, a), { a: undefined, b: "foo" }) + assert.end() +}) + +test("handle 0", function(assert) { + var a = { a: "default" } + var b = { a: 0 } + + assert.deepEqual(extend(a, b), { a: 0 }) + assert.deepEqual(extend(b, a), { a: "default" }) + assert.end() +}) + +test("is immutable", function (assert) { + var record = {} + + extend(record, { foo: "bar" }) + assert.equal(record.foo, undefined) + assert.end() +}) + +test("null as argument", function (assert) { + var a = { foo: "bar" } + var b = null + var c = void 0 + + assert.deepEqual(extend(b, a, c), { foo: "bar" }) + assert.end() +}) + +test("mutable", function (assert) { + var a = { foo: "bar" } + + mutableExtend(a, { bar: "baz" }) + + assert.equal(a.bar, "baz") + assert.end() +}) + +test("null prototype", function(assert) { + var a = { a: "foo" } + var b = Object.create(null) + b.b = "bar"; + + assert.deepEqual(extend(a, b), { a: "foo", b: "bar" }) + assert.end() +}) + +test("null prototype mutable", function (assert) { + var a = { foo: "bar" } + var b = Object.create(null) + b.bar = "baz"; + + mutableExtend(a, b) + + assert.equal(a.bar, "baz") + assert.end() +}) + +test("prototype pollution", function (assert) { + var a = {} + var maliciousPayload = '{"__proto__":{"oops":"It works!"}}' + + assert.strictEqual(a.oops, undefined) + extend({}, maliciousPayload) + assert.strictEqual(a.oops, undefined) + assert.end() +}) + +test("prototype pollution mutable", function (assert) { + var a = {} + var maliciousPayload = '{"__proto__":{"oops":"It works!"}}' + + assert.strictEqual(a.oops, undefined) + mutableExtend({}, maliciousPayload) + assert.strictEqual(a.oops, undefined) + assert.end() +}) diff --git a/package-lock.json b/package-lock.json index 9ef0bad..ab00616 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,10 @@ "version": "1.0.0", "dependencies": { "@temporalio/client": "^1.16.2", + "bcryptjs": "^3.0.3", "express": "^4.18.2", + "jsonwebtoken": "^9.0.3", + "pg": "^8.21.0", "uuid": "^14.0.0" } }, @@ -240,6 +243,15 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, "node_modules/body-parser": { "version": "1.20.5", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", @@ -264,6 +276,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -418,6 +436,15 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -731,12 +758,103 @@ "node": ">=8" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", @@ -878,6 +996,135 @@ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, + "node_modules/pg": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", + "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", + "license": "MIT", + "peer": true, + "dependencies": { + "pg-connection-string": "^2.13.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.14.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", + "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", + "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/proto3-json-serializer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", @@ -1001,6 +1248,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", @@ -1124,6 +1383,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -1244,6 +1512,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 4442830..c61f6eb 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,10 @@ }, "dependencies": { "@temporalio/client": "^1.16.2", + "bcryptjs": "^3.0.3", "express": "^4.18.2", + "jsonwebtoken": "^9.0.3", + "pg": "^8.21.0", "uuid": "^14.0.0" } } diff --git a/register.html b/register.html index 2e64dbf..d34e221 100644 --- a/register.html +++ b/register.html @@ -3,56 +3,301 @@ - Clinic Registration | Curio HMS + Register Your Clinic | Curio HMS + - +
-
+

Register Your Clinic

Join Bharat's first WhatsApp-native HMS.

- -
+ + +
+
+
1
+
Clinic Info
+
+
+
2
+
Your Account
+
+
+
3
+
Choose Plan
+
+
+ + + + +
- - + +
- - + +
+
+
+ + +
+
+ + +
+
+ +
+ + +
- - + +
- - + +
- - - - +
+
+ + +
+
+ + +
+
+ +
+ + +
+

Choose your subscription plan:

+
+ + + +
+ +
+ + +
+
+
🎉
+

Registration Submitted!

+

+ Your clinic registration is under review. Our team will approve your account within 24 hours. + You will receive your login credentials via WhatsApp. +

+ + ← Back to Login + +
+
+ +
diff --git a/super-admin.html b/super-admin.html index 0e86de9..5246909 100644 --- a/super-admin.html +++ b/super-admin.html @@ -3,117 +3,795 @@ - Super Admin | Curio Platform - - - + Platform Command Center | Curio + + + + + - -