diff --git a/src/package-lock.json b/src/package-lock.json index 9aac624..07c397c 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@event-driven-io/pongo-core", - "version": "0.3.0", + "version": "0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@event-driven-io/pongo-core", - "version": "0.3.0", + "version": "0.4.0", "workspaces": [ "packages/dumbo", "packages/pongo" @@ -246,9 +246,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", + "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -936,9 +936,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { @@ -1014,9 +1014,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", + "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==", "cpu": [ "arm" ], @@ -1027,9 +1027,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz", + "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==", "cpu": [ "arm64" ], @@ -1040,9 +1040,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz", + "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==", "cpu": [ "arm64" ], @@ -1053,9 +1053,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz", + "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==", "cpu": [ "x64" ], @@ -1066,9 +1066,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz", + "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==", "cpu": [ "arm" ], @@ -1079,9 +1079,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz", + "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==", "cpu": [ "arm" ], @@ -1092,9 +1092,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz", + "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==", "cpu": [ "arm64" ], @@ -1105,9 +1105,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz", + "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==", "cpu": [ "arm64" ], @@ -1118,9 +1118,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz", + "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==", "cpu": [ "ppc64" ], @@ -1131,9 +1131,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz", + "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==", "cpu": [ "riscv64" ], @@ -1144,9 +1144,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz", + "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==", "cpu": [ "s390x" ], @@ -1157,9 +1157,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz", + "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==", "cpu": [ "x64" ], @@ -1170,9 +1170,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz", + "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==", "cpu": [ "x64" ], @@ -1183,9 +1183,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz", + "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==", "cpu": [ "arm64" ], @@ -1196,9 +1196,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz", + "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==", "cpu": [ "ia32" ], @@ -1209,9 +1209,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz", + "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==", "cpu": [ "x64" ], @@ -1222,36 +1222,39 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.1.tgz", - "integrity": "sha512-qdiJS5a/QGCff7VUFIqd0hDdWly9rDp8lhVmXVrS11aazX8LOTRLHAXkkEeONNsS43EcCd7gax9LLoOz4vlFQA==", - "dev": true + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.3.tgz", + "integrity": "sha512-D45PMaBaeDHxww+EkcDQtDAtzv00Gcsp72ukBtaLSmqRvh0WgGMq3Al0rl1QQBZfuneO75NXMIzEZGFitThWbg==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.4" + } }, "node_modules/@shikijs/transformers": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.10.1.tgz", - "integrity": "sha512-0gLtcFyi6R6zcUkFajUEp1Qiv7lHBSFgOz4tQvS8nFsYCQSLI1/9pM+Me8jEIPXv7XLKAoUjw6InL+Sv+BHw/A==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.10.3.tgz", + "integrity": "sha512-MNjsyye2WHVdxfZUSr5frS97sLGe6G1T+1P41QjyBFJehZphMcr4aBlRLmq6OSPBslYe9byQPVvt/LJCOfxw8Q==", "dev": true, "dependencies": { - "shiki": "1.10.1" + "shiki": "1.10.3" } }, "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==", + "version": "10.10.3", + "resolved": "https://registry.npmjs.org/@testcontainers/mongodb/-/mongodb-10.10.3.tgz", + "integrity": "sha512-uwa4WwJXFPSXX/jPKCqRF+21StfxC7cKWjKsOHvw6vCJB6eZlQ9kfzkpFUNYIXFW7Q9iL1Piqek8lV4+9lCzkA==", "dev": true, "dependencies": { - "testcontainers": "^10.10.1" + "testcontainers": "^10.10.3" } }, "node_modules/@testcontainers/postgresql": { - "version": "10.10.1", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.10.1.tgz", - "integrity": "sha512-Wsc/OGT9vcwLJ34PPJ9lMngQY4SkyJaRePSXaFzXVzZS39TZdUUXiPAhdrZw36MQJsslZluqiwl95EsXJIIsuA==", + "version": "10.10.3", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.10.3.tgz", + "integrity": "sha512-k887VJjbbSyHr4eTRVhoBit9A+7WDYx/EU8XdwJ0swuECB1hOjMuvpCX/AlXLk+bD6dNrE/0lvKW6SwqFTXo1A==", "dev": true, "dependencies": { - "testcontainers": "^10.10.1" + "testcontainers": "^10.10.3" } }, "node_modules/@tsconfig/node10": { @@ -1303,6 +1306,15 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/linkify-it": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", @@ -1390,6 +1402,12 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -3077,9 +3095,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -3862,13 +3880,10 @@ "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", - "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/magic-string": { "version": "0.30.10", @@ -5007,9 +5022,9 @@ } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", + "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -5022,22 +5037,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.18.1", + "@rollup/rollup-android-arm64": "4.18.1", + "@rollup/rollup-darwin-arm64": "4.18.1", + "@rollup/rollup-darwin-x64": "4.18.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.1", + "@rollup/rollup-linux-arm-musleabihf": "4.18.1", + "@rollup/rollup-linux-arm64-gnu": "4.18.1", + "@rollup/rollup-linux-arm64-musl": "4.18.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1", + "@rollup/rollup-linux-riscv64-gnu": "4.18.1", + "@rollup/rollup-linux-s390x-gnu": "4.18.1", + "@rollup/rollup-linux-x64-gnu": "4.18.1", + "@rollup/rollup-linux-x64-musl": "4.18.1", + "@rollup/rollup-win32-arm64-msvc": "4.18.1", + "@rollup/rollup-win32-ia32-msvc": "4.18.1", + "@rollup/rollup-win32-x64-msvc": "4.18.1", "fsevents": "~2.3.2" } }, @@ -5088,9 +5103,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/search-insights": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", - "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.15.0.tgz", + "integrity": "sha512-ch2sPCUDD4sbPQdknVl9ALSi9H7VyoeVbsxznYz6QV55jJ8CI3EtwpO1i84keN4+hF5IeHWIeGvc08530JkVXQ==", "dev": true, "peer": true }, @@ -5137,12 +5152,13 @@ } }, "node_modules/shiki": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.10.1.tgz", - "integrity": "sha512-uafV7WCgN4YYrccH6yxpnps6k38sSTlFRrwc4jycWmhWxJIm9dPrk+XkY1hZ2t0I7jmacMNb15Lf2fspa/Y3lg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.10.3.tgz", + "integrity": "sha512-eneCLncGuvPdTutJuLyUGS8QNPAVFO5Trvld2wgEq1e002mwctAhJKeMGWtWVXOIEzmlcLRqcgPSorR6AVzOmQ==", "dev": true, "dependencies": { - "@shikijs/core": "1.10.1" + "@shikijs/core": "1.10.3", + "@types/hast": "^3.0.4" } }, "node_modules/signal-exit": { @@ -5526,9 +5542,9 @@ } }, "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==", + "version": "10.10.3", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.10.3.tgz", + "integrity": "sha512-QuHKgGbMo+rM+AvrHNzQFAu8/D37Od1sQCW8lNR5+KvGM82mDJndTkpPXiUaFpVIZ99wNQfhZbZwSTBULerUiQ==", "dependencies": { "@balena/dockerignore": "^1.0.2", "@types/dockerode": "^3.3.29", @@ -5548,9 +5564,9 @@ } }, "node_modules/text-decoder": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.0.tgz", - "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", + "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", "dependencies": { "b4a": "^1.6.4" } @@ -6636,9 +6652,9 @@ }, "packages/dumbo": { "name": "@event-driven-io/dumbo", - "version": "0.1.0", + "version": "0.2.0", "dependencies": { - "@event-driven-io/dumbo": "^0.1.0" + "@event-driven-io/dumbo": "^0.2.0" }, "devDependencies": { "@types/node": "20.11.30" @@ -6656,7 +6672,7 @@ }, "packages/pongo": { "name": "@event-driven-io/pongo", - "version": "0.3.0", + "version": "0.4.0", "dependencies": { "@types/pg-connection-string": "^2.0.0", "pg-connection-string": "^2.6.4" @@ -6665,7 +6681,7 @@ "@types/node": "20.11.30" }, "peerDependencies": { - "@event-driven-io/dumbo": "^0.1.0", + "@event-driven-io/dumbo": "^0.2.0", "@types/mongodb": "^4.0.7", "@types/pg": "^8.11.6", "@types/pg-format": "^1.0.5", diff --git a/src/package.json b/src/package.json index 18da028..190559a 100644 --- a/src/package.json +++ b/src/package.json @@ -1,6 +1,6 @@ { "name": "@event-driven-io/pongo-core", - "version": "0.3.0", + "version": "0.4.0", "description": "Pongo - Mongo with strong consistency on top of Postgres", "type": "module", "engines": { diff --git a/src/packages/dumbo/package.json b/src/packages/dumbo/package.json index c23f5f7..e7c3d94 100644 --- a/src/packages/dumbo/package.json +++ b/src/packages/dumbo/package.json @@ -1,6 +1,6 @@ { "name": "@event-driven-io/dumbo", - "version": "0.1.0", + "version": "0.2.0", "description": "Dumbo - tools for dealing with PostgreSQL", "type": "module", "scripts": { @@ -60,6 +60,6 @@ "@types/node": "20.11.30" }, "dependencies": { - "@event-driven-io/dumbo": "^0.1.0" + "@event-driven-io/dumbo": "^0.2.0" } } diff --git a/src/packages/pongo/package.json b/src/packages/pongo/package.json index 0a3d30f..e2ca0c3 100644 --- a/src/packages/pongo/package.json +++ b/src/packages/pongo/package.json @@ -1,6 +1,6 @@ { "name": "@event-driven-io/pongo", - "version": "0.3.0", + "version": "0.4.0", "description": "Pongo - Mongo with strong consistency on top of Postgres", "type": "module", "scripts": { @@ -47,7 +47,7 @@ "dist" ], "peerDependencies": { - "@event-driven-io/dumbo": "^0.1.0", + "@event-driven-io/dumbo": "^0.2.0", "@types/mongodb": "^4.0.7", "@types/pg": "^8.11.6", "@types/pg-format": "^1.0.5", diff --git a/src/packages/pongo/src/e2e/compatibilityTest.e2e.spec.ts b/src/packages/pongo/src/e2e/compatibilityTest.e2e.spec.ts index 1078c05..9fbaae7 100644 --- a/src/packages/pongo/src/e2e/compatibilityTest.e2e.spec.ts +++ b/src/packages/pongo/src/e2e/compatibilityTest.e2e.spec.ts @@ -7,8 +7,13 @@ import { type StartedPostgreSqlContainer, } from '@testcontainers/postgresql'; import assert from 'assert'; -import { Db as MongoDb, MongoClient as OriginalMongoClient } from 'mongodb'; +import { + Db as MongoDb, + ObjectId, + MongoClient as OriginalMongoClient, +} from 'mongodb'; import { after, before, describe, it } from 'node:test'; +import { v4 as uuid } from 'uuid'; import { MongoClient, type Db } from '../'; type History = { street: string }; @@ -20,6 +25,7 @@ type Address = { }; type User = { + _id?: ObjectId; name: string; age: number; address?: Address; @@ -406,6 +412,51 @@ void describe('MongoDB Compatibility Tests', () => { }); }); + void describe('Replace Operations', () => { + void it('should replace a document in both PostgreSQL and MongoDB', async () => { + const pongoCollection = pongoDb.collection('updateOne'); + const mongoCollection = mongoDb.collection('updateOne'); + const doc = { name: 'Roger', age: 30 }; + + const pongoInsertResult = await pongoCollection.insertOne(doc); + const mongoInsertResult = await mongoCollection.insertOne(doc); + + const replacement = { name: 'Not Roger', age: 100, tags: ['tag2'] }; + + await pongoCollection.replaceOne( + { _id: pongoInsertResult.insertedId }, + replacement, + ); + await mongoCollection.replaceOne( + { _id: mongoInsertResult.insertedId }, + replacement, + ); + + const pongoDoc = await pongoCollection.findOne({ + _id: pongoInsertResult.insertedId, + }); + const mongoDoc = await mongoCollection.findOne({ + _id: mongoInsertResult.insertedId, + }); + + assert.strictEqual(mongoDoc?.name, replacement.name); + assert.deepEqual(mongoDoc?.age, replacement.age); + assert.deepEqual(mongoDoc?.tags, replacement.tags); + assert.deepStrictEqual( + { + name: pongoDoc!.name, + age: pongoDoc!.age, + tags: pongoDoc!.tags, + }, + { + name: mongoDoc.name, + age: mongoDoc.age, + tags: mongoDoc.tags, + }, + ); + }); + }); + void describe('Delete Operations', () => { void it('should delete a document from both PostgreSQL and MongoDB', async () => { const pongoCollection = pongoDb.collection('testCollection'); @@ -859,4 +910,105 @@ void describe('MongoDB Compatibility Tests', () => { ); }); }); + + void describe('Handle Operations', () => { + void it('should insert a new document if it does not exist', async () => { + const pongoCollection = pongoDb.collection('handleCollection'); + const nonExistingId = uuid() as unknown as ObjectId; + + const newDoc: User = { name: 'John', age: 25 }; + + const handle = (_existing: User | null) => newDoc; + + const resultPongo = await pongoCollection.handle(nonExistingId, handle); + assert.deepStrictEqual(resultPongo, { ...newDoc, _id: nonExistingId }); + + const pongoDoc = await pongoCollection.findOne({ + _id: nonExistingId, + }); + + assert.deepStrictEqual(pongoDoc, { ...newDoc, _id: nonExistingId }); + }); + + void it('should replace an existing document', async () => { + const pongoCollection = pongoDb.collection('handleCollection'); + + const existingDoc: User = { name: 'John', age: 25 }; + const updatedDoc: User = { name: 'John', age: 30 }; + + const pongoInsertResult = await pongoCollection.insertOne(existingDoc); + + const handle = (_existing: User | null) => updatedDoc; + + const resultPongo = await pongoCollection.handle( + pongoInsertResult.insertedId, + handle, + ); + + assert.deepStrictEqual(resultPongo, { + ...updatedDoc, + }); + + const pongoDoc = await pongoCollection.findOne({ + _id: pongoInsertResult.insertedId, + }); + + assert.deepStrictEqual(pongoDoc, { + ...updatedDoc, + _id: pongoInsertResult.insertedId, + }); + }); + + void it('should delete an existing document if the handler returns null', async () => { + const pongoCollection = pongoDb.collection('handleCollection'); + + const existingDoc: User = { name: 'John', age: 25 }; + + const pongoInsertResult = await pongoCollection.insertOne(existingDoc); + + const handle = (_existing: User | null) => null; + + const resultPongo = await pongoCollection.handle( + pongoInsertResult.insertedId, + handle, + ); + + assert.strictEqual(resultPongo, null); + + const pongoDoc = await pongoCollection.findOne({ + _id: pongoInsertResult.insertedId, + }); + + assert.strictEqual(pongoDoc, null); + }); + + void it('should do nothing if the handler returns the existing document unchanged', async () => { + const pongoCollection = pongoDb.collection('handleCollection'); + + const existingDoc: User = { name: 'John', age: 25 }; + + const pongoInsertResult = await pongoCollection.insertOne(existingDoc); + + const handle = (existing: User | null) => existing; + + const resultPongo = await pongoCollection.handle( + pongoInsertResult.insertedId, + handle, + ); + + assert.deepStrictEqual(resultPongo, { + ...existingDoc, + _id: pongoInsertResult.insertedId, + }); + + const pongoDoc = await pongoCollection.findOne({ + _id: pongoInsertResult.insertedId, + }); + + assert.deepStrictEqual(pongoDoc, { + ...existingDoc, + _id: pongoInsertResult.insertedId, + }); + }); + }); }); diff --git a/src/packages/pongo/src/main/dbClient.ts b/src/packages/pongo/src/main/dbClient.ts index 6cf70f9..1bb06af 100644 --- a/src/packages/pongo/src/main/dbClient.ts +++ b/src/packages/pongo/src/main/dbClient.ts @@ -1,10 +1,10 @@ import { postgresClient, type PongoClientOptions } from '../postgres'; -import type { PongoCollection } from './typing/operations'; +import type { PongoCollection, PongoDocument } from './typing/operations'; export interface DbClient { connect(): Promise; close(): Promise; - collection: (name: string) => PongoCollection; + collection: (name: string) => PongoCollection; } export const getDbClient = (options: PongoClientOptions): DbClient => { diff --git a/src/packages/pongo/src/main/pongoClient.ts b/src/packages/pongo/src/main/pongoClient.ts index e415423..985b78c 100644 --- a/src/packages/pongo/src/main/pongoClient.ts +++ b/src/packages/pongo/src/main/pongoClient.ts @@ -25,7 +25,7 @@ export const pongoClient = (connectionString: string): PongoClient => { return ( dbClients.get(dbName) ?? dbClients - .set(dbName, getDbClient({ connectionString, database: dbName })) + .set(dbName, getDbClient({ connectionString, dbName: dbName })) .get(dbName)! ); }, diff --git a/src/packages/pongo/src/main/typing/operations.ts b/src/packages/pongo/src/main/typing/operations.ts index ff9e34d..7505fda 100644 --- a/src/packages/pongo/src/main/typing/operations.ts +++ b/src/packages/pongo/src/main/typing/operations.ts @@ -7,10 +7,12 @@ export interface PongoClient { } export interface PongoDb { - collection(name: string): PongoCollection; + collection(name: string): PongoCollection; } -export interface PongoCollection { +export interface PongoCollection { + readonly dbName: string; + readonly collectionName: string; createCollection(): Promise; insertOne(document: T): Promise; insertMany(documents: T[]): Promise; @@ -18,6 +20,10 @@ export interface PongoCollection { filter: PongoFilter, update: PongoUpdate, ): Promise; + replaceOne( + filter: PongoFilter, + document: WithoutId, + ): Promise; updateMany( filter: PongoFilter, update: PongoUpdate, @@ -26,12 +32,17 @@ export interface PongoCollection { deleteMany(filter: PongoFilter): Promise; findOne(filter: PongoFilter): Promise; find(filter: PongoFilter): Promise; + drop(): Promise; + rename(newName: string): Promise>; + handle(id: string, handle: DocumentHandler): Promise; } export type HasId = { _id: string }; export type WithId = T & HasId; +export type WithoutId = Omit; + export type PongoFilter = | { [P in keyof T]?: T[P] | PongoFilterOperator; @@ -91,3 +102,9 @@ export interface PongoDeleteManyResult { acknowledged: boolean; deletedCount: number; } + +export type PongoDocument = Record; + +export type DocumentHandler = + | ((document: T | null) => T | null) + | ((document: T | null) => Promise); diff --git a/src/packages/pongo/src/mongo/mongoCollection.ts b/src/packages/pongo/src/mongo/mongoCollection.ts index 3dab536..eb6798b 100644 --- a/src/packages/pongo/src/mongo/mongoCollection.ts +++ b/src/packages/pongo/src/mongo/mongoCollection.ts @@ -41,6 +41,7 @@ import type { ModifyResult, Collection as MongoCollection, FindCursor as MongoFindCursor, + ObjectId, OperationOptions, OptionalUnlessRequiredId, OrderedBulkOperation, @@ -58,7 +59,12 @@ import type { WriteConcern, } from 'mongodb'; import type { Key } from 'readline'; -import type { PongoCollection, PongoFilter, PongoUpdate } from '../main'; +import type { + DocumentHandler, + PongoCollection, + PongoFilter, + PongoUpdate, +} from '../main'; import { FindCursor } from './findCursor'; export class Collection implements MongoCollection { @@ -68,28 +74,28 @@ export class Collection implements MongoCollection { this.collection = collection; } get dbName(): string { - throw new Error('Method not implemented.'); + return this.collection.dbName; } get collectionName(): string { - throw new Error('Method not implemented.'); + return this.collection.collectionName; } get namespace(): string { - throw new Error('Method not implemented.'); + return `${this.dbName}.${this.collectionName}`; } get readConcern(): ReadConcern | undefined { - throw new Error('Method not implemented.'); + return undefined; } get readPreference(): ReadPreference | undefined { - throw new Error('Method not implemented.'); + return undefined; } get bsonOptions(): BSONSerializeOptions { - throw new Error('Method not implemented.'); + return {}; } get writeConcern(): WriteConcern | undefined { - throw new Error('Method not implemented.'); + return undefined; } get hint(): Hint | undefined { - throw new Error('Method not implemented.'); + return undefined; } set hint(v: Hint | undefined) { throw new Error('Method not implemented.'); @@ -140,11 +146,14 @@ export class Collection implements MongoCollection { }; } replaceOne( - _filter: Filter, - _: WithoutId, + filter: Filter, + document: WithoutId, _options?: ReplaceOptions | undefined, ): Promise> { - throw new Error('Method not implemented.'); + return this.collection.replaceOne( + filter as unknown as PongoFilter, + document, + ); } async updateMany( filter: Filter, @@ -190,14 +199,16 @@ export class Collection implements MongoCollection { deletedCount: result.deletedCount, }; } - rename( - _newName: string, + async rename( + newName: string, _options?: RenameOptions | undefined, ): Promise> { - throw new Error('Method not implemented.'); + await this.collection.rename(newName); + + return this as unknown as Collection; } drop(_options?: DropCollectionOptions | undefined): Promise { - throw new Error('Method not implemented.'); + return this.collection.drop(); } findOne(): Promise | null>; findOne(filter: Filter): Promise | null>; @@ -496,4 +507,7 @@ export class Collection implements MongoCollection { async createCollection(): Promise { await this.collection.createCollection(); } + async handle(id: ObjectId, handle: DocumentHandler): Promise { + return this.collection.handle(id.toString(), handle); + } } diff --git a/src/packages/pongo/src/mongo/mongoDb.ts b/src/packages/pongo/src/mongo/mongoDb.ts index c59f168..a883442 100644 --- a/src/packages/pongo/src/mongo/mongoDb.ts +++ b/src/packages/pongo/src/mongo/mongoDb.ts @@ -1,11 +1,19 @@ -import { Collection as MongoCollection, type Document } from 'mongodb'; -import type { PongoDb } from '../main'; +import { + Collection as MongoCollection, + ObjectId, + type Document, +} from 'mongodb'; +import type { DocumentHandler, PongoDb } from '../main'; import { Collection } from './mongoCollection'; export class Db { constructor(private pongoDb: PongoDb) {} - collection(collectionName: string): MongoCollection { + collection( + collectionName: string, + ): MongoCollection & { + handle(id: ObjectId, handle: DocumentHandler): Promise; + } { 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 a7d1fdf..648af40 100644 --- a/src/packages/pongo/src/postgres/client.ts +++ b/src/packages/pongo/src/postgres/client.ts @@ -1,25 +1,34 @@ -import { endPool, getPool } from '@event-driven-io/dumbo'; +import { + endPool, + getDatabaseNameOrDefault, + getPool, +} from '@event-driven-io/dumbo'; import pg from 'pg'; -import { type DbClient } from '../main'; +import { type DbClient, type PongoDocument } from '../main'; import { postgresCollection } from './postgresCollection'; export type PongoClientOptions = { connectionString: string; - database?: string | undefined; + dbName?: string | undefined; client?: pg.PoolClient; }; export const postgresClient = (options: PongoClientOptions): DbClient => { - const { connectionString, database, client } = options; + const { connectionString, dbName, client } = options; const managesPoolLifetime = !client; - const clientOrPool = client ?? getPool({ connectionString, database }); + const poolOrClient = + client ?? getPool({ connectionString, database: dbName }); return { connect: () => Promise.resolve(), close: () => managesPoolLifetime - ? endPool({ connectionString, database }) + ? endPool({ connectionString, database: dbName }) : Promise.resolve(), - collection: (name: string) => postgresCollection(name, clientOrPool), + collection: (name: string) => + postgresCollection(name, { + dbName: dbName ?? getDatabaseNameOrDefault(connectionString), + poolOrClient, + }), }; }; diff --git a/src/packages/pongo/src/postgres/postgresCollection.ts b/src/packages/pongo/src/postgres/postgresCollection.ts index 1dc5538..d78cb20 100644 --- a/src/packages/pongo/src/postgres/postgresCollection.ts +++ b/src/packages/pongo/src/postgres/postgresCollection.ts @@ -1,30 +1,39 @@ -import { executeSQL, sql, type SQL } from '@event-driven-io/dumbo'; +import { executeSQL, single, sql, type SQL } from '@event-driven-io/dumbo'; import pg from 'pg'; import format from 'pg-format'; import { v4 as uuid } from 'uuid'; import { + type DocumentHandler, type PongoCollection, type PongoDeleteResult, + type PongoDocument, type PongoFilter, type PongoInsertManyResult, type PongoInsertOneResult, type PongoUpdate, type PongoUpdateResult, type WithId, + type WithoutId, } from '../main'; import { constructFilterQuery } from './filter'; import { buildUpdateQuery } from './update'; -export const postgresCollection = ( +export const postgresCollection = ( collectionName: string, - pool: pg.Pool | pg.PoolClient, + { + dbName, + poolOrClient: clientOrPool, + }: { dbName: string; poolOrClient: pg.Pool | pg.PoolClient }, ): PongoCollection => { - const execute = (sql: SQL) => executeSQL(pool, sql); + const execute = (sql: SQL) => + executeSQL(clientOrPool, sql); const SqlFor = collectionSQLBuilder(collectionName); const createCollection = execute(SqlFor.createCollection()); - return { + const collection = { + dbName, + collectionName, createCollection: async () => { await createCollection; }, @@ -66,6 +75,17 @@ export const postgresCollection = ( ? { acknowledged: true, modifiedCount: result.rowCount } : { acknowledged: false, modifiedCount: 0 }; }, + replaceOne: async ( + filter: PongoFilter, + document: WithoutId, + ): Promise => { + await createCollection; + + const result = await execute(SqlFor.replaceOne(filter, document)); + return result.rowCount + ? { acknowledged: true, modifiedCount: result.rowCount } + : { acknowledged: false, modifiedCount: 0 }; + }, updateMany: async ( filter: PongoFilter, update: PongoUpdate, @@ -99,13 +119,61 @@ export const postgresCollection = ( const result = await execute(SqlFor.findOne(filter)); return (result.rows[0]?.data ?? null) as T | null; }, + handle: async ( + id: string, + handle: DocumentHandler, + ): Promise => { + await createCollection; + + const byId: PongoFilter = { _id: id }; + + const existing = await collection.findOne(byId); + + const result = await handle(existing); + + if (!existing && result) { + const newDoc = { ...result, _id: id }; + await collection.insertOne({ ...newDoc, _id: id }); + return newDoc; + } + + if (existing && !result) { + await collection.deleteOne(byId); + return null; + } + + if (existing && result) await collection.replaceOne(byId, result); + + return result; + }, find: async (filter: PongoFilter): Promise => { await createCollection; const result = await execute(SqlFor.find(filter)); return result.rows.map((row) => row.data as T); }, + countDocuments: async (filter: PongoFilter = {}): Promise => { + await createCollection; + + const { count } = await single( + execute<{ count: number }>(SqlFor.countDocuments(filter)), + ); + return count; + }, + drop: async (): Promise => { + await createCollection; + const result = await execute(SqlFor.drop()); + return (result?.rowCount ?? 0) > 0; + }, + rename: async (newName: string): Promise> => { + await createCollection; + await execute(SqlFor.rename(newName)); + collectionName = newName; + return collection; + }, }; + + return collection; }; export const collectionSQLBuilder = (collectionName: string) => ({ @@ -143,6 +211,16 @@ export const collectionSQLBuilder = (collectionName: string) => ({ collectionName, ); }, + replaceOne: (filter: PongoFilter, document: WithoutId): SQL => { + const filterQuery = constructFilterQuery(filter); + + return sql( + `UPDATE %I SET data = %L || jsonb_build_object('_id', data->>'_id') WHERE %s`, + collectionName, + JSON.stringify(document), + filterQuery, + ); + }, updateMany: (filter: PongoFilter, update: PongoUpdate): SQL => { const filterQuery = constructFilterQuery(filter); const updateQuery = buildUpdateQuery(update); @@ -174,4 +252,16 @@ export const collectionSQLBuilder = (collectionName: string) => ({ const filterQuery = constructFilterQuery(filter); return sql('SELECT data FROM %I WHERE %s', collectionName, filterQuery); }, + countDocuments: (filter: PongoFilter): SQL => { + const filterQuery = constructFilterQuery(filter); + return sql( + 'SELECT COUNT(1) as count FROM %I WHERE %s', + collectionName, + filterQuery, + ); + }, + rename: (newName: string): SQL => + sql('ALTER TABLE %I RENAME TO %I', collectionName, newName), + drop: (targetName: string = collectionName): SQL => + sql('DROP TABLE IF EXISTS %I', targetName), });