diff --git a/src/package-lock.json b/src/package-lock.json index c8ddc58..65fc23d 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -10,8 +10,13 @@ "workspaces": [ "packages/pongo" ], + "dependencies": { + "testcontainers": "^10.10.1" + }, "devDependencies": { "@faker-js/faker": "8.4.1", + "@testcontainers/mongodb": "^10.10.1", + "@testcontainers/postgresql": "^10.10.1", "@types/mongodb": "^4.0.7", "@types/node": "20.11.30", "@types/pg": "^8.11.6", @@ -247,6 +252,11 @@ "node": ">=6.0.0" } }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==" + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -1221,6 +1231,24 @@ "shiki": "1.10.1" } }, + "node_modules/@testcontainers/mongodb": { + "version": "10.10.1", + "resolved": "https://registry.npmjs.org/@testcontainers/mongodb/-/mongodb-10.10.1.tgz", + "integrity": "sha512-pcl3ncms9MMz86Jb5oLB3ORStsKoH0HRJ3axVOgD/EsFLPiHQJF+oMQwdghYysZqVINR2RGNWNs+coW0o6sXaQ==", + "dev": true, + "dependencies": { + "testcontainers": "^10.10.1" + } + }, + "node_modules/@testcontainers/postgresql": { + "version": "10.10.1", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.10.1.tgz", + "integrity": "sha512-Wsc/OGT9vcwLJ34PPJ9lMngQY4SkyJaRePSXaFzXVzZS39TZdUUXiPAhdrZw36MQJsslZluqiwl95EsXJIIsuA==", + "dev": true, + "dependencies": { + "testcontainers": "^10.10.1" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -1245,6 +1273,25 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "3.3.29", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.29.tgz", + "integrity": "sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -1287,7 +1334,6 @@ "version": "20.11.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -1309,6 +1355,30 @@ "integrity": "sha512-i+oEEJEC+1I3XAhgqtVp45Faj8MBbV0Aoq4rHsHD7avgLjyDkaWKObd514g0Q/DOUkdxU0P4CQ0iq2KR4SoJcw==", "dev": true }, + "node_modules/@types/ssh2": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.0.tgz", + "integrity": "sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==", + "dependencies": { + "@types/node": "^18.11.18" + } + }, + "node_modules/@types/ssh2-streams": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz", + "integrity": "sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.39.tgz", + "integrity": "sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -1967,6 +2037,110 @@ "node": ">= 8" } }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1988,11 +2162,101 @@ "node": ">=8" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" + }, + "node_modules/b4a": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bare-events": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", + "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", + "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", + "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", + "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "optional": true, + "dependencies": { + "streamx": "^2.18.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -2015,11 +2279,20 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -2045,6 +2318,46 @@ "node": ">=16.20.1" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buildcheck": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", + "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/bundle-require": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-4.2.1.tgz", @@ -2060,6 +2373,14 @@ "esbuild": ">=0.17" } }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -2130,6 +2451,11 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "node_modules/close-with-grace": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/close-with-grace/-/close-with-grace-1.3.0.tgz", @@ -2163,11 +2489,24 @@ "node": ">= 6" } }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/copy-anything": { "version": "3.0.5", @@ -2184,6 +2523,48 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2214,7 +2595,6 @@ "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -2254,6 +2634,55 @@ "node": ">=8" } }, + "node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/docker-modem": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.8.tgz", + "integrity": "sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.11.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.5.tgz", + "integrity": "sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "docker-modem": "^3.0.0", + "tar-fs": "~2.0.1" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode/node_modules/tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2278,6 +2707,14 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -2593,6 +3030,11 @@ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -2727,11 +3169,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -2747,6 +3193,17 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -2840,6 +3297,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2870,6 +3332,25 @@ "node": ">=10.17.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -2909,7 +3390,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2918,8 +3398,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -3005,6 +3484,11 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3098,6 +3582,44 @@ "json-buffer": "3.0.1" } }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3153,6 +3675,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "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==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3165,6 +3707,11 @@ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "dev": true }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + }, "node_modules/lru-cache": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz", @@ -3292,6 +3839,22 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "node_modules/mongodb": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz", @@ -3351,8 +3914,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mz": { "version": "2.7.0", @@ -3365,6 +3927,12 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nan": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -3389,11 +3957,48 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3475,7 +4080,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -3567,7 +4171,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3967,6 +4570,50 @@ "node": ">=6.0.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3996,6 +4643,11 @@ } ] }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" + }, "node_modules/read-package-json-fast": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", @@ -4009,6 +4661,38 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4039,6 +4723,14 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4172,6 +4864,30 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/search-insights": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", @@ -4316,6 +5032,11 @@ "node": ">=0.10.0" } }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==" + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -4325,6 +5046,62 @@ "node": ">= 10.x" } }, + "node_modules/ssh-remote-port-forward": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", + "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", + "dependencies": { + "@types/ssh2": "^0.5.48", + "ssh2": "^1.4.0" + } + }, + "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { + "version": "0.5.52", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", + "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", + "dependencies": { + "@types/node": "*", + "@types/ssh2-streams": "*" + } + }, + "node_modules/ssh2": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz", + "integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==", + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.9", + "nan": "^2.18.0" + } + }, + "node_modules/streamx": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", + "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "dependencies": { + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4513,6 +5290,74 @@ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", "dev": true }, + "node_modules/tar-fs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/tar-fs/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/testcontainers": { + "version": "10.10.1", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.10.1.tgz", + "integrity": "sha512-bw86BLq2/ljJ/gLg3PBsyhYOoDBkyo87/MnpWLavYTAyWR7feGFnAA87qRLq2SSCOSFLHsgCA8+u7Eg+opSmTQ==", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.29", + "archiver": "^5.3.2", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.3.5", + "docker-compose": "^0.24.8", + "dockerode": "^3.3.5", + "get-port": "^5.1.1", + "node-fetch": "^2.7.0", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.0.6", + "tmp": "^0.2.3" + } + }, + "node_modules/text-decoder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.0.tgz", + "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4540,6 +5385,14 @@ "node": ">=0.8" } }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4731,6 +5584,11 @@ "fsevents": "~2.3.3" } }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4771,8 +5629,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/uri-js": { "version": "4.4.1", @@ -4783,6 +5640,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -5464,8 +6326,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/xtend": { "version": "4.0.2", @@ -5480,7 +6341,6 @@ "version": "2.4.5", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", - "dev": true, "bin": { "yaml": "bin.mjs" }, @@ -5509,6 +6369,79 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/zip-stream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/zip-stream/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "packages/pongo": { "name": "@event-driven-io/pongo", "version": "0.0.1", diff --git a/src/package.json b/src/package.json index 0fa5e98..1148b20 100644 --- a/src/package.json +++ b/src/package.json @@ -63,6 +63,8 @@ ], "devDependencies": { "@faker-js/faker": "8.4.1", + "@testcontainers/mongodb": "^10.10.1", + "@testcontainers/postgresql": "^10.10.1", "@types/mongodb": "^4.0.7", "@types/node": "20.11.30", "@types/pg": "^8.11.6", @@ -85,12 +87,14 @@ "vitepress": "1.0.1" }, "peerDependencies": { + "close-with-grace": "^1.3.0", "pg": "^8.12.0", - "pg-format": "^1.0.4", - "close-with-grace": "^1.3.0" + "pg-format": "^1.0.4" }, "workspaces": [ "packages/pongo" ], - "dependencies": {} + "dependencies": { + "testcontainers": "^10.10.1" + } } diff --git a/src/packages/pongo/src/e2e/compatibilityTest.e2e.spec.ts b/src/packages/pongo/src/e2e/compatibilityTest.e2e.spec.ts new file mode 100644 index 0000000..3225e03 --- /dev/null +++ b/src/packages/pongo/src/e2e/compatibilityTest.e2e.spec.ts @@ -0,0 +1,217 @@ +import { + MongoDBContainer, + type StartedMongoDBContainer, +} from '@testcontainers/mongodb'; +import { + PostgreSqlContainer, + type StartedPostgreSqlContainer, +} from '@testcontainers/postgresql'; +import assert from 'assert'; +import { Db as MongoDb, MongoClient as OriginalMongoClient } from 'mongodb'; +import { after, before, describe, it } from 'node:test'; +import MongoClient from '../mongo/mongoClient'; +import type { Db } from '../mongo/mongoDb'; +import { endAllPools } from '../postgres'; + +type User = { name: string; age: number }; + +void describe('MongoDB Compatibility Tests', () => { + let postgres: StartedPostgreSqlContainer; + let postgresConnectionString: string; + let pongoClient: MongoClient; + + let mongo: StartedMongoDBContainer; + let mongoConnectionString: string; + let mongoClient: OriginalMongoClient; + + let pongoDb: Db; + let mongoDb: MongoDb; + + before(async () => { + postgres = await new PostgreSqlContainer().start(); + postgresConnectionString = postgres.getConnectionUri(); + pongoClient = new MongoClient(postgresConnectionString); + await pongoClient.connect(); + + mongo = await new MongoDBContainer('mongo:6.0.12').start(); + mongoConnectionString = mongo.getConnectionString(); + mongoClient = new OriginalMongoClient(mongoConnectionString, { + directConnection: true, + }); + await mongoClient.connect(); + + const dbName = postgres.getDatabase(); + + pongoDb = pongoClient.db(dbName); + mongoDb = mongoClient.db(dbName); + }); + + after(async () => { + try { + await endAllPools(); + await postgres.stop(); + } catch (error) { + console.log(error); + } + try { + await mongoClient.close(); + await mongo.stop(); + } catch (error) { + console.log(error); + } + }); + + void describe('Insert Operations', () => { + void it('should insert a document into both PostgreSQL and MongoDB', async () => { + const pongoCollection = pongoDb.collection('testCollection'); + const mongoCollection = mongoDb.collection('testCollection'); + + const doc = { name: 'Alice', age: 25 }; + + const pongoInsertResult = await pongoCollection.insertOne(doc); + const mongoInsertResult = await mongoCollection.insertOne(doc); + + assert(pongoInsertResult.insertedId); + assert(mongoInsertResult.insertedId); + + const pongoDoc = await pongoCollection.findOne({ + _id: pongoInsertResult.insertedId, + }); + const mongoDoc = await mongoCollection.findOne({ + _id: mongoInsertResult.insertedId, + }); + + assert.deepStrictEqual( + { + name: pongoDoc!.name, + age: pongoDoc!.age, + }, + { + name: mongoDoc!.name, + age: mongoDoc!.age, + }, + ); + }); + }); + + void describe('Update Operations', () => { + void it('should update a document in both PostgreSQL and MongoDB', async () => { + const pongoCollection = pongoDb.collection('testCollection'); + const mongoCollection = mongoDb.collection('testCollection'); + const doc = { name: 'Bob', age: 30 }; + + const pongoInsertResult = await pongoCollection.insertOne(doc); + const mongoInsertResult = await mongoCollection.insertOne(doc); + + const update = { $set: { age: 31 } }; + + await pongoCollection.updateOne( + { _id: pongoInsertResult.insertedId }, + update, + ); + await mongoCollection.updateOne( + { _id: mongoInsertResult.insertedId }, + update, + ); + + const pongoDoc = await pongoCollection.findOne({ + _id: pongoInsertResult.insertedId, + }); + const mongoDoc = await mongoCollection.findOne({ + _id: mongoInsertResult.insertedId, + }); + + assert.deepStrictEqual( + { + name: pongoDoc!.name, + age: 31, + }, + { + name: mongoDoc!.name, + age: 31, + }, + ); + }); + }); + + void describe('Delete Operations', () => { + void it('should delete a document from both PostgreSQL and MongoDB', async () => { + const pongoCollection = pongoDb.collection('testCollection'); + const mongoCollection = mongoDb.collection('testCollection'); + const doc = { name: 'Charlie', age: 35 }; + + const pongoInsertResult = await pongoCollection.insertOne(doc); + const mongoInsertResult = await mongoCollection.insertOne(doc); + + await pongoCollection.deleteOne({ _id: pongoInsertResult.insertedId }); + await mongoCollection.deleteOne({ _id: mongoInsertResult.insertedId }); + + const pongoDoc = await pongoCollection.findOne({ + _id: pongoInsertResult.insertedId, + }); + const mongoDoc = await mongoCollection.findOne({ + _id: mongoInsertResult.insertedId, + }); + + assert.strictEqual(pongoDoc, null); + assert.strictEqual(mongoDoc, null); + }); + }); + + void describe('Find Operations', () => { + void it('should find documents with a filter in both PostgreSQL and MongoDB', async () => { + const pongoCollection = pongoDb.collection('testCollection'); + const mongoCollection = mongoDb.collection('testCollection'); + const docs = [ + { name: 'David', age: 40 }, + { name: 'Eve', age: 45 }, + { name: 'Frank', age: 50 }, + ]; + + await pongoCollection.insertOne(docs[0]!); + await pongoCollection.insertOne(docs[1]!); + await pongoCollection.insertOne(docs[2]!); + + await mongoCollection.insertOne(docs[0]!); + await mongoCollection.insertOne(docs[1]!); + await mongoCollection.insertOne(docs[2]!); + + const pongoDocs = await pongoCollection + .find({ age: { $gte: 45 } }) + .toArray(); + const mongoDocs = await mongoCollection + .find({ age: { $gte: 45 } }) + .toArray(); + + assert.strictEqual(pongoDocs.length, 2); + + assert.deepStrictEqual( + pongoDocs.map((d) => ({ name: d.name, age: d.age })), + mongoDocs.map((d) => ({ name: d.name, age: d.age })), + ); + }); + + void it('should find one document with a filter in both PostgreSQL and MongoDB', async () => { + const pongoCollection = pongoDb.collection('testCollection'); + const mongoCollection = mongoDb.collection('testCollection'); + const doc = { name: 'Grace', age: 55 }; + + await pongoCollection.insertOne(doc); + await mongoCollection.insertOne(doc); + + const pongoDoc = await pongoCollection.findOne({ name: 'Grace' }); + const mongoDoc = await mongoCollection.findOne({ name: 'Grace' }); + + assert.deepStrictEqual( + { + name: pongoDoc!.name, + age: pongoDoc!.age, + }, + { + name: mongoDoc!.name, + age: mongoDoc!.age, + }, + ); + }); + }); +}); diff --git a/src/packages/pongo/src/main/client.ts b/src/packages/pongo/src/main/client.ts index b3ccadc..64b1bf9 100644 --- a/src/packages/pongo/src/main/client.ts +++ b/src/packages/pongo/src/main/client.ts @@ -4,10 +4,15 @@ import type { PongoClient, PongoDb } from './typing'; export const pongoClient = (connectionString: string): PongoClient => { const dbClient = getDbClient(connectionString); - return { - connect: () => dbClient.connect(), + const pongoClient: PongoClient = { + connect: async () => { + await dbClient.connect(); + return pongoClient; + }, close: () => dbClient.close(), db: (dbName?: string): PongoDb => dbName ? getDbClient(connectionString, dbName) : dbClient, }; + + return pongoClient; }; diff --git a/src/packages/pongo/src/main/typing.ts b/src/packages/pongo/src/main/typing.ts index 19049d1..c563bd3 100644 --- a/src/packages/pongo/src/main/typing.ts +++ b/src/packages/pongo/src/main/typing.ts @@ -1,5 +1,5 @@ export interface PongoClient { - connect(): Promise; + connect(): Promise; close(): Promise; @@ -12,7 +12,7 @@ export interface PongoDb { export interface PongoCollection { createCollection(): Promise; - insertOne(document: T): Promise; + insertOne(document: T): Promise; updateOne( filter: PongoFilter, update: PongoUpdate, @@ -44,15 +44,17 @@ export type PongoUpdate = { $push?: { [P in keyof T]?: T[P] }; }; -export interface PongoInsertResult { +export interface PongoInsertOneResult { insertedId: string | null; - insertedCount: number | null; + acknowledged: boolean; } export interface PongoUpdateResult { - modifiedCount: number | null; + acknowledged: boolean; + modifiedCount: number; } export interface PongoDeleteResult { - deletedCount: number | null; + acknowledged: boolean; + deletedCount: number; } diff --git a/src/packages/pongo/src/mongo/findCursor.ts b/src/packages/pongo/src/mongo/findCursor.ts new file mode 100644 index 0000000..4faca55 --- /dev/null +++ b/src/packages/pongo/src/mongo/findCursor.ts @@ -0,0 +1,37 @@ +export class FindCursor { + private findDocumentsPromise: Promise; + private documents: T[] | null = null; + private index: number = 0; + + constructor(documents: Promise) { + this.findDocumentsPromise = documents; + } + + async toArray(): Promise { + return this.findDocuments(); + } + + async forEach(callback: (doc: T) => void): Promise { + const docs = await this.findDocuments(); + + for (const doc of docs) { + callback(doc); + } + return Promise.resolve(); + } + + hasNext(): boolean { + if (this.documents === null) throw Error('Error while fetching documents'); + return this.index < this.documents.length; + } + + async next(): Promise { + const docs = await this.findDocuments(); + return this.hasNext() ? docs[this.index++] ?? null : null; + } + + private async findDocuments(): Promise { + this.documents = await this.findDocumentsPromise; + return this.documents; + } +} diff --git a/src/packages/pongo/src/mongo/index.ts b/src/packages/pongo/src/mongo/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/packages/pongo/src/mongo/mongoClient.ts b/src/packages/pongo/src/mongo/mongoClient.ts new file mode 100644 index 0000000..e5d2bc0 --- /dev/null +++ b/src/packages/pongo/src/mongo/mongoClient.ts @@ -0,0 +1,24 @@ +// src/MongoClientShim.ts +import { pongoClient, type PongoClient } from '../main'; +import { Db } from './mongoDb'; + +export default class MongoClient { + private pongoClient: PongoClient; + + constructor(connectionString: string) { + this.pongoClient = pongoClient(connectionString); + } + + async connect() { + await this.pongoClient.connect(); + return this; + } + + async close() { + await this.pongoClient.close(); + } + + db(dbName: string): Db { + return new Db(this.pongoClient.db(dbName)); + } +} diff --git a/src/packages/pongo/src/mongo/mongoCollection.ts b/src/packages/pongo/src/mongo/mongoCollection.ts new file mode 100644 index 0000000..d57d345 --- /dev/null +++ b/src/packages/pongo/src/mongo/mongoCollection.ts @@ -0,0 +1,476 @@ +import type { + AbstractCursorOptions, + AggregateOptions, + AggregationCursor, + AnyBulkWriteOperation, + BSONSerializeOptions, + BulkWriteOptions, + BulkWriteResult, + ChangeStream, + ChangeStreamDocument, + ChangeStreamOptions, + CommandOperationOptions, + CountDocumentsOptions, + CountOptions, + CreateIndexesOptions, + DeleteOptions, + DeleteResult, + Document, + DropCollectionOptions, + EnhancedOmit, + EstimatedDocumentCountOptions, + Filter, + FindOneAndDeleteOptions, + FindOneAndReplaceOptions, + FindOneAndUpdateOptions, + FindOptions, + Flatten, + Hint, + IndexDescription, + IndexDescriptionCompact, + IndexDescriptionInfo, + IndexInformationOptions, + IndexSpecification, + InferIdType, + InsertManyResult, + InsertOneOptions, + InsertOneResult, + ListIndexesCursor, + ListSearchIndexesCursor, + ListSearchIndexesOptions, + ModifyResult, + Collection as MongoCollection, + FindCursor as MongoFindCursor, + OperationOptions, + OptionalUnlessRequiredId, + OrderedBulkOperation, + ReadConcern, + ReadPreference, + RenameOptions, + ReplaceOptions, + SearchIndexDescription, + UnorderedBulkOperation, + UpdateFilter, + UpdateOptions, + UpdateResult, + WithId, + WithoutId, + WriteConcern, +} from 'mongodb'; +import type { Key } from 'readline'; +import type { PongoCollection, PongoFilter, PongoUpdate } from '../main'; +import { FindCursor } from './findCursor'; + +export class Collection implements MongoCollection { + private collection: PongoCollection; + + constructor(collection: PongoCollection) { + this.collection = collection; + } + get dbName(): string { + throw new Error('Method not implemented.'); + } + get collectionName(): string { + throw new Error('Method not implemented.'); + } + get namespace(): string { + throw new Error('Method not implemented.'); + } + get readConcern(): ReadConcern | undefined { + throw new Error('Method not implemented.'); + } + get readPreference(): ReadPreference | undefined { + throw new Error('Method not implemented.'); + } + get bsonOptions(): BSONSerializeOptions { + throw new Error('Method not implemented.'); + } + get writeConcern(): WriteConcern | undefined { + throw new Error('Method not implemented.'); + } + get hint(): Hint | undefined { + throw new Error('Method not implemented.'); + } + set hint(v: Hint | undefined) { + throw new Error('Method not implemented.'); + } + async insertOne( + doc: OptionalUnlessRequiredId, + _options?: InsertOneOptions | undefined, + ): Promise> { + const result = await this.collection.insertOne(doc as T); + return { + acknowledged: result.acknowledged, + insertedId: result.insertedId as unknown as InferIdType, + }; + } + insertMany( + _docs: OptionalUnlessRequiredId[], + _options?: BulkWriteOptions | undefined, + ): Promise> { + throw new Error('Method not implemented.'); + } + bulkWrite( + _operations: AnyBulkWriteOperation[], + _options?: BulkWriteOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + async updateOne( + filter: Filter, + update: Document[] | UpdateFilter, + _options?: UpdateOptions | undefined, + ): Promise> { + const result = await this.collection.updateOne( + filter as unknown as PongoFilter, + update as unknown as PongoUpdate, + ); + + return { + acknowledged: result.acknowledged, + matchedCount: result.modifiedCount, + modifiedCount: result.modifiedCount, + upsertedCount: result.modifiedCount, + upsertedId: null, + }; + } + replaceOne( + _filter: Filter, + _: WithoutId, + _options?: ReplaceOptions | undefined, + ): Promise> { + throw new Error('Method not implemented.'); + } + updateMany( + _filter: Filter, + _update: Document[] | UpdateFilter, + _options?: UpdateOptions | undefined, + ): Promise> { + throw new Error('Method not implemented.'); + } + async deleteOne( + filter?: Filter | undefined, + _options?: DeleteOptions | undefined, + ): Promise { + const result = await this.collection.deleteOne( + filter as unknown as PongoFilter, + ); + + return { + acknowledged: result.acknowledged, + deletedCount: result.deletedCount, + }; + } + deleteMany( + _filter?: Filter | undefined, + _options?: DeleteOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + rename( + _newName: string, + _options?: RenameOptions | undefined, + ): Promise> { + throw new Error('Method not implemented.'); + } + drop(_options?: DropCollectionOptions | undefined): Promise { + throw new Error('Method not implemented.'); + } + findOne(): Promise | null>; + findOne(filter: Filter): Promise | null>; + findOne( + filter: Filter, + options: FindOptions, + ): Promise | null>; + findOne(): Promise; + findOne(filter: Filter): Promise; + findOne( + filter: Filter, + options?: FindOptions | undefined, + ): Promise; + async findOne( + filter?: unknown, + _options?: unknown, + ): Promise | T | null> { + return this.collection.findOne(filter as PongoFilter); + } + find(): MongoFindCursor>; + find( + filter: Filter, + options?: FindOptions | undefined, + ): MongoFindCursor>; + find( + filter: Filter, + options?: FindOptions | undefined, + ): MongoFindCursor; + find( + filter?: unknown, + _options?: unknown, + ): MongoFindCursor> | MongoFindCursor { + return new FindCursor( + this.collection.find(filter as PongoFilter), + ) as unknown as MongoFindCursor; + } + options(_options?: OperationOptions | undefined): Promise { + throw new Error('Method not implemented.'); + } + isCapped(_options?: OperationOptions | undefined): Promise { + throw new Error('Method not implemented.'); + } + createIndex( + _indexSpec: IndexSpecification, + _options?: CreateIndexesOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + createIndexes( + _indexSpecs: IndexDescription[], + _options?: CreateIndexesOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + dropIndex( + _indexName: string, + _options?: CommandOperationOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + dropIndexes( + _options?: CommandOperationOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + listIndexes(_options?: AbstractCursorOptions | undefined): ListIndexesCursor { + throw new Error('Method not implemented.'); + } + indexExists( + _indexes: string | string[], + _options?: AbstractCursorOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + indexInformation( + options: IndexInformationOptions & { full: true }, + ): Promise; + indexInformation( + options: IndexInformationOptions & { full?: false | undefined }, + ): Promise; + indexInformation( + options: IndexInformationOptions, + ): Promise; + indexInformation(): Promise; + indexInformation( + _options?: unknown, + ): + | Promise + | Promise + | Promise< + | import('mongodb').IndexDescriptionCompact + | import('mongodb').IndexDescriptionInfo[] + > { + throw new Error('Method not implemented.'); + } + estimatedDocumentCount( + _options?: EstimatedDocumentCountOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + countDocuments( + _filter?: Filter | undefined, + _options?: CountDocumentsOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + distinct>( + key: Key, + ): Promise[Key]>[]>; + distinct>( + key: Key, + filter: Filter, + ): Promise[Key]>[]>; + distinct>( + key: Key, + filter: Filter, + options: CommandOperationOptions, + ): Promise[Key]>[]>; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + distinct(key: string): Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + distinct(key: string, filter: Filter): Promise; + distinct( + key: string, + filter: Filter, + options: CommandOperationOptions, // eslint-disable-next-line @typescript-eslint/no-explicit-any + ): Promise; + distinct( + _key: unknown, + _filter?: unknown, + _options?: unknown, + ): // eslint-disable-next-line @typescript-eslint/no-explicit-any + | Promise + | Promise[Key]>[]> { + throw new Error('Method not implemented.'); + } + indexes( + options: IndexInformationOptions & { full?: true | undefined }, + ): Promise; + indexes( + options: IndexInformationOptions & { full: false }, + ): Promise; + indexes( + options: IndexInformationOptions, + ): Promise; + indexes( + options?: AbstractCursorOptions | undefined, + ): Promise; + indexes( + _options?: unknown, + ): + | Promise + | Promise + | Promise< + | import('mongodb').IndexDescriptionCompact + | import('mongodb').IndexDescriptionInfo[] + > { + throw new Error('Method not implemented.'); + } + findOneAndDelete( + filter: Filter, + options: FindOneAndDeleteOptions & { includeResultMetadata: true }, + ): Promise>; + findOneAndDelete( + filter: Filter, + options: FindOneAndDeleteOptions & { includeResultMetadata: false }, + ): Promise | null>; + findOneAndDelete( + filter: Filter, + options: FindOneAndDeleteOptions, + ): Promise | null>; + findOneAndDelete(filter: Filter): Promise | null>; + findOneAndDelete( + _filter: unknown, + _options?: unknown, + ): + | Promise | null> + | Promise> { + throw new Error('Method not implemented.'); + } + findOneAndReplace( + filter: Filter, + replacement: WithoutId, + options: FindOneAndReplaceOptions & { includeResultMetadata: true }, + ): Promise>; + findOneAndReplace( + filter: Filter, + replacement: WithoutId, + options: FindOneAndReplaceOptions & { includeResultMetadata: false }, + ): Promise | null>; + findOneAndReplace( + filter: Filter, + replacement: WithoutId, + options: FindOneAndReplaceOptions, + ): Promise | null>; + findOneAndReplace( + filter: Filter, + replacement: WithoutId, + ): Promise | null>; + findOneAndReplace( + _filter: unknown, + _replacement: unknown, + _options?: unknown, + ): + | Promise | null> + | Promise> { + throw new Error('Method not implemented.'); + } + findOneAndUpdate( + filter: Filter, + update: UpdateFilter, + options: FindOneAndUpdateOptions & { includeResultMetadata: true }, + ): Promise>; + findOneAndUpdate( + filter: Filter, + update: UpdateFilter, + options: FindOneAndUpdateOptions & { includeResultMetadata: false }, + ): Promise | null>; + findOneAndUpdate( + filter: Filter, + update: UpdateFilter, + options: FindOneAndUpdateOptions, + ): Promise | null>; + findOneAndUpdate( + filter: Filter, + update: UpdateFilter, + ): Promise | null>; + findOneAndUpdate( + _filter: unknown, + _update: unknown, + _options?: unknown, + ): + | Promise | null> + | Promise> { + throw new Error('Method not implemented.'); + } + aggregate( + _pipeline?: Document[] | undefined, + _options?: AggregateOptions | undefined, + ): AggregationCursor { + throw new Error('Method not implemented.'); + } + watch< + TLocal extends Document = T, + TChange extends Document = ChangeStreamDocument, + >( + _pipeline?: Document[] | undefined, + _options?: ChangeStreamOptions | undefined, + ): ChangeStream { + throw new Error('Method not implemented.'); + } + initializeUnorderedBulkOp( + _options?: BulkWriteOptions | undefined, + ): UnorderedBulkOperation { + throw new Error('Method not implemented.'); + } + initializeOrderedBulkOp( + _options?: BulkWriteOptions | undefined, + ): OrderedBulkOperation { + throw new Error('Method not implemented.'); + } + count( + _filter?: Filter | undefined, + _options?: CountOptions | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + listSearchIndexes( + options?: ListSearchIndexesOptions | undefined, + ): ListSearchIndexesCursor; + listSearchIndexes( + name: string, + options?: ListSearchIndexesOptions | undefined, + ): ListSearchIndexesCursor; + listSearchIndexes( + _name?: unknown, + _options?: unknown, + ): import('mongodb').ListSearchIndexesCursor { + throw new Error('Method not implemented.'); + } + createSearchIndex(_description: SearchIndexDescription): Promise { + throw new Error('Method not implemented.'); + } + createSearchIndexes( + _descriptions: SearchIndexDescription[], + ): Promise { + throw new Error('Method not implemented.'); + } + dropSearchIndex(_name: string): Promise { + throw new Error('Method not implemented.'); + } + updateSearchIndex(_name: string, _definition: Document): Promise { + throw new Error('Method not implemented.'); + } + + async createCollection(): Promise { + await this.collection.createCollection(); + } +} diff --git a/src/packages/pongo/src/mongo/mongoDb.ts b/src/packages/pongo/src/mongo/mongoDb.ts new file mode 100644 index 0000000..c59f168 --- /dev/null +++ b/src/packages/pongo/src/mongo/mongoDb.ts @@ -0,0 +1,11 @@ +import { Collection as MongoCollection, type Document } from 'mongodb'; +import type { PongoDb } from '../main'; +import { Collection } from './mongoCollection'; + +export class Db { + constructor(private pongoDb: PongoDb) {} + + collection(collectionName: string): MongoCollection { + return new Collection(this.pongoDb.collection(collectionName)); + } +} diff --git a/src/packages/pongo/src/postgres/client.ts b/src/packages/pongo/src/postgres/client.ts index 1ab35ab..d7816b7 100644 --- a/src/packages/pongo/src/postgres/client.ts +++ b/src/packages/pongo/src/postgres/client.ts @@ -1,18 +1,18 @@ -import type { Pool } from 'pg'; +import pg from 'pg'; import { v4 as uuid } from 'uuid'; import { type DbClient, type PongoCollection, type PongoDeleteResult, type PongoFilter, - type PongoInsertResult, + type PongoInsertOneResult, type PongoUpdate, type PongoUpdateResult, } from '../main'; +import { sql } from './execute'; import { constructFilterQuery } from './filter'; import { getPool } from './pool'; import { constructUpdateQuery } from './update'; -import { sql } from './execute'; export const postgresClient = ( connectionString: string, @@ -29,34 +29,34 @@ export const postgresClient = ( export const postgresCollection = ( collectionName: string, - pool: Pool, + pool: pg.Pool, ): PongoCollection => { const createCollection = async (): Promise => { await sql( pool, - 'CREATE TABLE IF NOT EXISTS %I (id UUID PRIMARY KEY, data JSONB)', + 'CREATE TABLE IF NOT EXISTS %I (_id UUID PRIMARY KEY, data JSONB)', collectionName, ); }; return { createCollection, - insertOne: async (document: T): Promise => { + insertOne: async (document: T): Promise => { await createCollection(); const id = uuid(); const result = await sql( pool, - 'INSERT INTO %I (id, data) VALUES (%L, %L)', + 'INSERT INTO %I (_id, data) VALUES (%L, %L)', collectionName, id, JSON.stringify({ ...document, _id: id }), ); return result.rowCount - ? { insertedId: id, insertedCount: result.rowCount } - : { insertedId: null, insertedCount: null }; + ? { insertedId: id, acknowledged: true } + : { insertedId: null, acknowledged: false }; }, updateOne: async ( filter: PongoFilter, @@ -72,7 +72,9 @@ export const postgresCollection = ( updateQuery, filterQuery, ); - return { modifiedCount: result.rowCount }; + return result.rowCount + ? { acknowledged: true, modifiedCount: result.rowCount } + : { acknowledged: false, modifiedCount: 0 }; }, deleteOne: async (filter: PongoFilter): Promise => { const filterQuery = constructFilterQuery(filter); @@ -82,7 +84,9 @@ export const postgresCollection = ( collectionName, filterQuery, ); - return { deletedCount: result.rowCount }; + return result.rowCount + ? { acknowledged: true, deletedCount: result.rowCount } + : { acknowledged: false, deletedCount: 0 }; }, findOne: async (filter: PongoFilter): Promise => { const filterQuery = constructFilterQuery(filter); @@ -98,7 +102,7 @@ export const postgresCollection = ( const filterQuery = constructFilterQuery(filter); const result = await sql( pool, - 'SELECT data FROM %I WHERE %s LIMIT 1', + 'SELECT data FROM %I WHERE %s', collectionName, filterQuery, ); diff --git a/src/packages/pongo/src/postgres/execute/index.ts b/src/packages/pongo/src/postgres/execute/index.ts index a7e3d5c..15a4ea4 100644 --- a/src/packages/pongo/src/postgres/execute/index.ts +++ b/src/packages/pongo/src/postgres/execute/index.ts @@ -1,11 +1,11 @@ -import type { QueryResultRow, Pool, QueryResult, PoolClient } from 'pg'; +import type pg from 'pg'; import format from 'pg-format'; -export const sql = async ( - pool: Pool, +export const sql = async ( + pool: pg.Pool, sqlText: string, ...params: unknown[] -): Promise> => { +): Promise> => { const client = await pool.connect(); try { const query = format(sqlText, ...params); @@ -16,8 +16,8 @@ export const sql = async ( }; export const execute = async ( - pool: Pool, - handle: (client: PoolClient) => Promise, + pool: pg.Pool, + handle: (client: pg.PoolClient) => Promise, ) => { const client = await pool.connect(); try { diff --git a/src/packages/pongo/src/postgres/filter/index.ts b/src/packages/pongo/src/postgres/filter/index.ts index cfb0719..3ecd3a2 100644 --- a/src/packages/pongo/src/postgres/filter/index.ts +++ b/src/packages/pongo/src/postgres/filter/index.ts @@ -1,3 +1,4 @@ +// src/utils.ts import format from 'pg-format'; import type { PongoFilter } from '../../main'; @@ -6,12 +7,24 @@ export const constructFilterQuery = (filter: PongoFilter): string => { if (typeof value === 'object' && !Array.isArray(value)) { return constructComplexFilterQuery(key, value as Record); } else { - return format('data->>%I = %L', key, value); + return constructSimpleFilterQuery(key, value); } }); return filters.join(' AND '); }; +const constructSimpleFilterQuery = (key: string, value: unknown): string => { + if (isUUID(value)) { + return format(`(data->>'%I')::text = %L::text`, key, value); + } else if (isDate(value)) { + return format(`(data->>'%I')::timestamp = %L::timestamp`, key, value); + } else if (isNumber(value)) { + return format(`(data->>'%I')::numeric = %L`, key, value); + } else { + return format(`(data->>'%I') = %L`, key, value); + } +}; + export const constructComplexFilterQuery = ( key: string, value: Record, @@ -19,28 +32,32 @@ export const constructComplexFilterQuery = ( const subFilters = Object.entries(value).map(([operator, val]) => { switch (operator) { case '$eq': - return format('data->>%I = %L', key, val); + return constructSimpleFilterQuery(key, val); case '$gt': - return format('data->>%I > %L', key, val); + return constructComparisonFilterQuery(key, val, '>'); case '$gte': - return format('data->>%I >= %L', key, val); + return constructComparisonFilterQuery(key, val, '>='); case '$lt': - return format('data->>%I < %L', key, val); + return constructComparisonFilterQuery(key, val, '<'); case '$lte': - return format('data->>%I <= %L', key, val); + return constructComparisonFilterQuery(key, val, '<='); case '$ne': - return format('data->>%I != %L', key, val); + return constructSimpleFilterQuery(key, val).replace('=', '!='); case '$in': return format( - 'data->>%I IN (%s)', + `(data->>'%I') IN (%s)`, key, - (val as unknown[]).map((v) => format('%L', v)).join(', '), + (val as unknown[]) + .map((v) => constructSimpleFilterQueryValue(key, v)) + .join(', '), ); case '$nin': return format( - 'data->>%I NOT IN (%s)', + `(data->>'%I') NOT IN (%s)`, key, - (val as unknown[]).map((v) => format('%L', v)).join(', '), + (val as unknown[]) + .map((v) => constructSimpleFilterQueryValue(key, v)) + .join(', '), ); default: throw new Error(`Unsupported operator: ${operator}`); @@ -48,3 +65,50 @@ export const constructComplexFilterQuery = ( }); return subFilters.join(' AND '); }; + +const constructComparisonFilterQuery = ( + key: string, + value: unknown, + operator: string, +): string => { + if (isUUID(value)) { + return format(`(data->>'%I')::text ${operator} %L::text`, key, value); + } else if (isDate(value)) { + return format( + `(data->>'%I')::timestamp ${operator} %L::timestamp`, + key, + value, + ); + } else if (isNumber(value)) { + return format(`(data->>'%I')::numeric ${operator} %s`, key, value); + } else { + return format(`(data->>'%I') ${operator} %L`, key, value); + } +}; + +const constructSimpleFilterQueryValue = ( + key: string, + value: unknown, +): string => { + if (isUUID(value)) { + return format('%L::text', value); + } else if (isDate(value)) { + return format('%L::timestamp', value); + } else if (isNumber(value)) { + return format('%L', value); + } else { + return format('%L', value); + } +}; + +const isUUID = (value: unknown): boolean => { + return typeof value === 'string' && /^[0-9a-fA-F-]{36}$/.test(value); +}; + +const isDate = (value: unknown): boolean => { + return typeof value === 'string' && !isNaN(Date.parse(value)); +}; + +const isNumber = (value: unknown): boolean => { + return typeof value === 'number'; +}; diff --git a/src/packages/pongo/src/postgres/pool.ts b/src/packages/pongo/src/postgres/pool.ts index a860a4a..db3e118 100644 --- a/src/packages/pongo/src/postgres/pool.ts +++ b/src/packages/pongo/src/postgres/pool.ts @@ -1,10 +1,10 @@ -import { Pool, type PoolConfig } from 'pg'; +import pg from 'pg'; -const pools: Map = new Map(); +const pools: Map = new Map(); export const getPool = ( - connectionStringOrOptions: string | PoolConfig, -): Pool => { + connectionStringOrOptions: string | pg.PoolConfig, +): pg.Pool => { const connectionString = typeof connectionStringOrOptions === 'string' ? connectionStringOrOptions @@ -17,7 +17,7 @@ export const getPool = ( return ( pools.get(connectionString) ?? - pools.set(connectionString, new Pool(poolOptions)).get(connectionString)! + pools.set(connectionString, new pg.Pool(poolOptions)).get(connectionString)! ); };