From ac2f4f852e21491c36ec223e5e104e0086e1ed64 Mon Sep 17 00:00:00 2001 From: mertalev <101130780+mertalev@users.noreply.github.com> Date: Sun, 17 Mar 2024 15:30:52 -0400 Subject: [PATCH] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wip use prisma for paginated queries remove migration file redundant spread simplified extend use bigint for comparison handle deleted assets in extension Squashed commit of the following: commit 64aac239f00ec02fefab85a626f568d0d9999e63 Author: Alex Date: Thu Mar 21 18:00:22 2024 -0500 chore: consolidate readme files (#8171) commit d6823b128ce536cdf8d268d19d0af25e071ffd12 Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Thu Mar 21 23:59:21 2024 +0100 fix(server): validation events actually throwing an error (#8172) * fix validation events * add e2e test commit 508f32c08a5a0de1a45e00a8872cb3bd984bc347 Author: martin <74269598+martabal@users.noreply.github.com> Date: Thu Mar 21 21:01:08 2024 +0100 feat(web): improvements to slideshow (#8032) * feat: improvements to slideshow * feat: pause video with slideshow bar * pr feedback * fix: remove dispatch * fix: simplify * pr feedback * pr feedback --------- Co-authored-by: Alex Tran commit 8ed6ed4d2b840c28a4af6a65b1d6fe06d8c53ce5 Author: Ethan Margaillan Date: Thu Mar 21 19:39:33 2024 +0100 feat(web): rework context menus: add icons and reorder items (#8090) commit 1abb0bdae830401bd510953fa3952a3e65f9a840 Author: Fynn Petersen-Frey <10599762+fyfrey@users.noreply.github.com> Date: Thu Mar 21 17:51:03 2024 +0100 feat(mobile): faster image loader (#8140) Co-authored-by: Alex Tran commit 5ef62155461eead476495a79be01c6c6ab9927c8 Author: martyfuhry Date: Thu Mar 21 12:31:18 2024 -0400 chore(mobile): Bump to Flutter 3.19.0 (#7167) * Bump to Flutter 3.19.0 * Ran pub upgrade --major-versions and removed isar_version alias Wrong http version * Updated share_plus to fix android build * Updates github actions to 3.19.0 * upgrade to 3.19.3 * upgrade to 3.19.3 --------- Co-authored-by: Alex Tran commit 95fb9c4365cbd944936ce3ed399f47abc6e915ea Author: waclaw66 Date: Thu Mar 21 18:23:06 2024 +0200 fix(mobile): spacing fixes of #8087 (#8163) fix(mobile): spacing fix of https://github.com/immich-app/immich/pull/8087 commit fa0a5107c24a63baec7e929349fc478a1340d6b7 Author: aviv926 <51673860+aviv926@users.noreply.github.com> Date: Thu Mar 21 17:05:45 2024 +0200 fix(docs): Immich quota claim note (#8151) * Add a note about immich_quota_claim. * Fix * PR feedback * npm run format:fix * use ยน commit dc3c32943127aaf0d562f1dfc6720898f1dafbd1 Author: Jason Rasmussen Date: Thu Mar 21 09:36:10 2024 -0500 chore: remove unused type (#8157) commit 2a9f2b4515d1f055b395f5360e8bec928c2c5e69 Author: Jason Rasmussen Date: Thu Mar 21 09:08:29 2024 -0500 refactor: app modules, main.ts (#8156) commit 793049388b7ed2aa8d3a05819f660b61edf73709 Author: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Thu Mar 21 14:44:54 2024 +0100 refactor(web): cleanup notification components (#8150) * refactor(web): cleanup notification components * use counter for ID commit 382b63954ce198cdc57763fac11b812ae07a0fc5 Author: Jason Rasmussen Date: Thu Mar 21 08:07:47 2024 -0500 refactor: asset v1, app.utils (#8152) commit 87ccba7f9ddf5154558f5b785fc27b64d4de89f4 Author: Ben Basten <45583362+ben-basten@users.noreply.github.com> Date: Thu Mar 21 12:24:19 2024 +0000 feat(web): keyboard access for search dropdown, combobox fixes (#8079) * feat(web): keyboard access for search dropdown Also: fixing cosmetic issue with combobox component. * fix: revert changing required field * fix: create new focusChange action * fix: combobox usability improvements * handle escape key on the clear button * move focus to input when clear button is clicked * leave the dropdown closed if the user has already closed the dropdown and tabs over to the clear button * activate the combobox if a user tabs backwards onto the clear button * rename focusChange to focusOutside * small fixes * do not activate combobox on backwards tabbing * simplify classes in "No results" option * prevent dropdown option from being preselected when clear button is clicked * fix: remove unused event dispatcher interface commit e21c96c0efc4f6040402f086620f45121915b9b2 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Thu Mar 21 07:14:44 2024 -0500 chore(deps): update redis:6.2-alpine docker digest to 3fcb624 (#8137) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 4de0b2f44e50daa9e42e05e1813d30323ade1543 Author: Ethan Margaillan Date: Thu Mar 21 13:14:13 2024 +0100 feat(web): add ctrl+a / ctrl+d shortcuts to select / deselect all assets (#8105) * feat(web): use ctrl+a / ctrl+d to select / deselect all assets * fix(web): use shortcutList for ctrl+a / ctrl+d * fix(web): remove useless get() * feat(web): asset interaction store can now select many assets at once commit b588a87d4aad333bc2a7c462f9674b5a7c770d65 Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Thu Mar 21 12:59:49 2024 +0100 chore(server): rename domain `repositories` -> `interfaces` (#8147) rename domain repositories commit 44ed1f091957f061c837767cc1d5dafc36d592e5 Author: Alex Date: Thu Mar 21 00:18:38 2024 -0500 fix(web): asset-grid padding/margin left fix (#8125) use media query for grid padding/margin size commit 16d0df796c3dec465096db83d409aef85c1185bd Author: Jason Rasmussen Date: Wed Mar 20 22:15:09 2024 -0500 refactor: infra folder (#8138) commit 9fd5d2ad9ca8ea7feda7973ee431fad1b03aa538 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 22:59:15 2024 -0400 fix(deps): update machine-learning (#8057) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 28ad004b01c4f23981e784c08a23811a99d84658 Author: Kirill <44521162+kirilldem@users.noreply.github.com> Date: Thu Mar 21 03:58:52 2024 +0100 Update remote-machine-learning.md (#8038) * Update remote-machine-learning.md provide an example to use cuda or another container * Update docs/docs/guides/remote-machine-learning.md Co-authored-by: aviv926 <51673860+aviv926@users.noreply.github.com> * Update docs/docs/guides/remote-machine-learning.md --------- Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com> Co-authored-by: aviv926 <51673860+aviv926@users.noreply.github.com> commit ef4a492cb14424de3a791004af6604a795e1eae9 Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Thu Mar 21 00:07:30 2024 +0100 chore(server): move services (#8133) move services commit 6d9e7694b1361527660fa4f30d506964ed9daeb9 Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed Mar 20 23:53:07 2024 +0100 chore(server): move dtos (#8131) move dtos commit 0c13c63bb62ab6e16730a1bab337a57dc12acd5a Author: Jason Rasmussen Date: Wed Mar 20 16:46:59 2024 -0500 refactor: infra/domain module (#8130) commit 907eb869bc8804de505da827869164f7f7982639 Author: Jason Rasmussen Date: Wed Mar 20 16:22:47 2024 -0500 chore: move apps and test utils (#8129) commit c1402eee8e0020994c488c22b175fd693e40be3d Author: Jason Rasmussen Date: Wed Mar 20 16:02:51 2024 -0500 chore: migrate database files (#8126) commit 84f7ca855a01c5c8d1bd2ce8c8355f17bb8a6125 Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed Mar 20 21:42:58 2024 +0100 chore(server): move domain interfaces (#8124) move domain interfaces commit 2dcce0335250a593e7990ad5717ec997cfc143a7 Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed Mar 20 21:25:33 2024 +0100 chore(server): move commands (#8121) move commands commit 96a22ec3c1912766d46b1ddfabb17e4248523aa4 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 16:21:27 2024 -0400 chore(deps): update base-image to v20240319 (major) (#8115) chore(deps): update base-image to v20240319 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 4b29bccc7c693983d25279e43cebf27ebd27f95f Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed Mar 20 21:20:38 2024 +0100 chore(server): move cores (#8120) move cores commit 40e079a247f29dedb7d3e55a7b7954a873300d28 Author: Jason Rasmussen Date: Wed Mar 20 15:15:01 2024 -0500 chore: move controllers and middleware (#8119) commit 81f0265095779a5bb4ba8bea53893aea9a2552af Author: Jason Rasmussen Date: Wed Mar 20 15:04:03 2024 -0500 chore: organize config, validation, decorators (#8118) * refactor: validation * refactor: utilities * refactor: config commit 92cc647cf6e3b0927146b6ee034a111a623fa3c6 Author: Jason Rasmussen Date: Wed Mar 20 14:50:01 2024 -0500 chore: renovate grouping (#8113) commit 048d437b0b7f6b21312f0e5c20e99a2c9dde50fa Author: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Wed Mar 20 20:40:41 2024 +0100 fix(web): prevent duplicate time bucket loads (#8091) commit ec9a6bca144c565e2e29e0ae34f8da01a663e94d Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 15:38:58 2024 -0400 chore(deps): update dependency socket.io-client to v4.7.5 (#8111) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit bd5952b94324d61b024ee4ac1e32e398eaa8a871 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 15:35:07 2024 -0400 chore(deps): update vitest monorepo to v1.4.0 (#8112) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 3f0d54c752a3c50a6f12365ed7d431c24f7831d3 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 15:34:12 2024 -0400 fix(deps): update server (#8067) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit dab4595a4efb958425adc85bc0092a5582cd091b Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 14:09:10 2024 -0500 chore(deps): update redis:6.2-alpine docker digest to fd35357 (#8001) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 6d9ca82b193695e4d3ba9690f52fbd1841b89683 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 14:08:01 2024 -0500 chore(deps): update web (#8066) * chore(deps): update web * fix: linting --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jason Rasmussen commit 373a03e8191d44a94ac1e2b663c1956979e916b7 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 14:06:58 2024 -0500 chore(deps): update dependency @types/node to v20.11.28 (#8110) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit d97b0259fad7c975ccb6072ffa91dd857fc0bc60 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 14:38:48 2024 -0400 chore(deps): update node.js to bf77dc2 (#8063) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 2267ca1949c4c4d548cd246794393cd61586e6ad Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 14:38:28 2024 -0400 chore(deps): update node.js to 8765147 (#8058) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 29be53e70d6c0e98d5011e03e0cd50991f26d9c9 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 14:37:22 2024 -0400 chore(deps): update prom/prometheus docker digest to 5ccad47 (#8071) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 851fe4a49f2f921405bc1ff9713464a72aee390d Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Wed Mar 20 14:33:41 2024 -0400 chore(deps): update dependency @types/node to v20.11.28 (#8064) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> commit 30f499cf2e68c5bdfde866193523dbb1232ae3b4 Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed Mar 20 19:32:04 2024 +0100 chore(server): use absolute import paths (#8080) update server to use absolute import paths commit 591a641d8d83e4a3c0d7fb0028b671e80a93b653 Author: Alex Date: Wed Mar 20 10:00:35 2024 -0500 chore: post release tasks commit 5b314ffd46f0438f3a60e241240d435c8598e8cf Author: Alex The Bot Date: Wed Mar 20 14:50:57 2024 +0000 Version v1.99.0 commit 0b078c9f9913b057486b033a8a9114b02428f35c Author: Alex Date: Wed Mar 20 09:46:31 2024 -0500 fix(web): Share button visible when viewing album has only shared link (#8100) commit 0d5584ecbb507332d95facc3c3e87f3f51222e8e Author: Alex Date: Wed Mar 20 09:28:19 2024 -0500 fix(web): shift-select again (#8098) commit 5e090646ba4cedf2b045677a22e1407eff27c289 Author: waclaw66 Date: Wed Mar 20 16:26:09 2024 +0200 fix(mobile): missing "Add name" translation (#8087) fix(mobile): missing "Add name" translation, positioning commit c4e910dd3d0087ca7dda2daa2a344dcc25582a97 Author: Mert <101130780+mertalev@users.noreply.github.com> Date: Wed Mar 20 10:20:46 2024 -0400 docs(server): add documentation for prometheus metrics (#8084) * add monitoring doc * wording * indent * note instead of tip * Update docs/docs/features/monitoring.md Co-authored-by: bo0tzz * Update docs/docs/features/monitoring.md Co-authored-by: bo0tzz --------- Co-authored-by: Alex Tran Co-authored-by: bo0tzz commit 5a2394af7cdaef46a0990913a7f6f18ffbdd16da Author: Alex Date: Wed Mar 20 09:16:20 2024 -0500 fix(web): shift-select (#8093) * fix(web): shift-select * remove unused code * proper fix commit 48e32269f42d8ec4ab01111110b79605ad9c3db6 Author: Alex Date: Wed Mar 20 09:16:00 2024 -0500 chore: add prometheus.yml to release artifact (#8096) commit dd9d90d21e404f4312d58dcd48b5c424aef782fb Author: Zack Pollard Date: Wed Mar 20 06:31:52 2024 -0600 test: temporarily disable flaky audit e2e test until #7436 is fixed (#8089) commit 0544c687b939f0375c35aed49c42a2fbb336ad74 Author: Ethan Margaillan Date: Wed Mar 20 13:29:30 2024 +0100 fix(web): missing margin on people page (#8081) commit e810aae21240be2baea365a99e2b61e2eb1e0996 Author: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Wed Mar 20 13:24:08 2024 +0100 fix(web): show search page errors and use feature flag (#8088) commit 9c6a26de9ff7a5e796a56696e9ab8e944a95e793 Author: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Wed Mar 20 05:41:31 2024 +0100 chore(web): add asset store unit tests (#8077) chore(web): asset store unit tests commit e6f2bb9f89c8f9b3010eec2d31f3c684d7d3bbae Author: Jonathan Jogenfors Date: Wed Mar 20 05:40:28 2024 +0100 fix(server): use extension in originalFileName for libraries (#8083) * use file base * fix: test * fix: e2e-job tests --------- Co-authored-by: Alex Tran commit f908bd4a645306c89c5f90be41dbc7d5273fc308 Author: Ethan Margaillan Date: Wed Mar 20 05:28:13 2024 +0100 fix(web): prevent drag-n-drop upload overlay from showing when not dragging files (#8082) commit 7395b03b1f99f659b3bc20e31bc1d6bf32cc21fa Author: Thariq Shanavas Date: Tue Mar 19 22:12:36 2024 -0600 fix(docs) minor security warning raised by Borg (#8075) * Fix minor borg security warning * Update template-backup-script.md * removed one unnecessary step * Clarified optional steps * Update template-backup-script.md commit 63b4fc6f6582396918803555f34bcfce82a4ace8 Author: Alex Date: Tue Mar 19 23:07:26 2024 -0500 chore(mobile): svg logo (#8074) * chore(mobile): anti-aliasing logo * use svg * adjust height * better sizing commit f392fe7702ebb09773bf8cb6a08a369ef80f5ce5 Author: Mert <101130780+mertalev@users.noreply.github.com> Date: Tue Mar 19 23:23:57 2024 -0400 fix(server): "view all" for cities only showing 12 cities (#8035) * view all cities * increase limit * rename endpoint * optimize query * remove pagination * update sql * linting * revert sort by count in explore page for now * fix query * fix * update sql * move to search, add partner support * update sql * pr feedback * euphemism * parameters as separate variable * move comment * update sql * linting commit 2daed747cd62a0ec86dac8a71e070d31b1a95686 Author: Mert <101130780+mertalev@users.noreply.github.com> Date: Tue Mar 19 22:42:10 2024 -0400 chore(server): change `save` -> `update` in asset repository (#8055) * `save` -> `update` * change return type * include relations * fix tests * remove when mocks * fix * stricter typing * simpler type commit 9e4bab74944273699e5db9b54d9be5b45f96cd2a Author: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue Mar 19 14:31:56 2024 +0000 feat(mobile): drag to select assets (#8004) fear(mobile): drag to select assets Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex commit 9274c0701b5656d27add8e67ba1c0aa66ad512fd Author: waclaw66 Date: Tue Mar 19 16:22:44 2024 +0200 fix(mobile): do not show hidden people (#8072) * fix(mobile): do not show hidden people * dart format fix commit 0bc773fd0094874f5b135c67339f7654ac150014 Author: Alex Date: Tue Mar 19 08:40:14 2024 -0500 refactor(mobile): backup album selection (#8053) * feat(mobile): include album with 0 assets as album option for backup * Show icon instead of thumbnail * Handle backupProgress state transition correctly to always load the backup info * remove todo comment commit c6d24085176b9da263ed0d3ad659267cc802ce8d Author: Ben Basten <45583362+ben-basten@users.noreply.github.com> Date: Tue Mar 19 12:56:41 2024 +0000 feat(web): combobox accessibility improvements (#8007) * bump skip link z index, to prevent overlap with the search box * combobox refactor initial commit * pull label into the combobox component * feat(web): combobox accessibility improvements * fix: replace crypto.randomUUID, fix border UI bug, simpler focus handling (#2) * fix: handle changes in the selected option * fix: better escape key handling in search bar * fix: remove broken tailwind classes Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> * fix: remove custom "outclick" handler logic * fix: use focusout instead of custom key handlers to detect focus change * fix: move escape key handling to the window Also add escape key handling to the input box, to make sure that the "recent searches" dropdown gets closed too. * fix: better input event handling Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> * fix: highlighting selected dropdown element --------- Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> commit 033f83a55a23442185e69f84f7d5840d85fb2ad2 Author: Jan <17313367+JW-CH@users.noreply.github.com> Date: Tue Mar 19 13:47:33 2024 +0100 fix(docs): update authelia OIDC link (#8070) commit 51841d627c33f55c9113efb45efd66a26af3d487 Author: Alex Date: Mon Mar 18 22:39:49 2024 -0500 fix(web): load panorama in shared link (#8060) * fix(web): load panorama in shared link * remove console log commit 50924f0b3dddb1fa86aec8e0f57b01ff562794bb Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Mon Mar 18 19:49:31 2024 -0400 chore(deps): update dependency @types/node to v20.11.27 (#8012) * chore(deps): update dependency @types/node to v20.11.27 * fixes * fixes --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel Dietzler Co-authored-by: Marty Fuhry commit 4aae1da8418e0475f4f3d8eb8cb20fd48d34d27e Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Mon Mar 18 22:56:39 2024 +0100 fix(web): repair page typo (#8051) fix typo commit 1a2554548a2ed44d619f0804773ee9b38d22f941 Author: bo0tzz Date: Mon Mar 18 22:54:30 2024 +0100 chore: Simplify install script (#8048) * chore: Simplify install script The default .env file now contains a set UPLOAD_LOCATION already * fix: Remove leftover line commit 40262c30cb7d43e059a3dbe0a146a5dd988ab0e6 Author: Jason Rasmussen Date: Mon Mar 18 15:59:53 2024 -0500 refactor(server): library service (#8050) * refactor: library service * chore: open api * fix: checks commit 761e7fdd2d914c172ef08084f286ff53483581d4 Author: Alex Date: Mon Mar 18 14:46:52 2024 -0500 feat(server): memory includes partners assets on timeline (#7993) * feat(server): memory includes partners assets on timeline * remove unsued code, generate sql * fix test * add test commit cd8a124b25429ac6a4b5598fdf3fa645a8f238fc Author: aviv926 <51673860+aviv926@users.noreply.github.com> Date: Mon Mar 18 16:00:11 2024 +0200 feat(docs): User management new options (#8029) * User Management * Add photo commit 148428a564ad5876ceb6b3991a7571e65dc15b38 Author: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Sun Mar 17 20:16:02 2024 +0100 feat(server): use nestjs events to validate config (#7986) * use events for config validation * chore: better types * add unit tests --------- Co-authored-by: Jason Rasmussen commit 14da671bf9a7cab9e0d436816cca39a38bf40416 Author: Tyler Brockett Date: Sun Mar 17 11:41:55 2024 -0700 fix(docs): add microservices to IMMICH_CONFIG_FILE env var documentation (#8017) commit e8f0f82db05244fc07253ddb3b643ca8fabba89b Author: Davide <22103897+dvdblg@users.noreply.github.com> Date: Sun Mar 17 18:48:59 2024 +0100 feat(ml): add cache_dir option to OpenVINO EP (#8018) * add cache_dir option to OpenVINO EP * update provider options test to include cache_dir * use forward slash instead of string concatenation * fix cache_dir placement in provider options assertion commit b8278404a091b33d41d901255733dfe11a70ed9d Author: Alex Date: Sun Mar 17 10:46:42 2024 -0500 chore(docs): update readme (#8021) commit 45671b0b8b978470eae703248df11c64f760aae7 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Sat Mar 16 15:34:49 2024 -0500 chore(deps): update typescript-eslint monorepo to v7.2.0 (#8008) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> refactor search, kysely extension set max use class provider directly fix remove deprecated endpoint add truncated date migration fix get by date query fix typing fix typing move with* helpers refactor timeline service fix facial recognition fix test we have `withDeleted` at home remove `isExternal`, `isReadOnly` fixes and improvements --- server/Dockerfile | 6 +- server/package-lock.json | 3791 +++++++++++++++--- server/package.json | 8 +- server/src/database.config.ts | 22 +- server/src/dtos/duplicate.dto.ts | 13 - server/src/interfaces/asset.interface.ts | 59 +- server/src/interfaces/search.interface.ts | 2 +- server/src/main.ts | 1 + server/src/prisma/find-non-deleted.ts | 32 + server/src/prisma/generated/types.ts | 291 ++ server/src/prisma/kysely.ts | 16 + server/src/prisma/metrics.ts | 17 + server/src/prisma/schema.prisma | 465 +++ server/src/repositories/asset.repository.ts | 858 ++-- server/src/repositories/index.ts | 2 + server/src/repositories/prisma.repository.ts | 44 + server/src/repositories/search.repository.ts | 433 +- server/src/services/asset-v1.service.ts | 2 +- server/src/services/asset.service.spec.ts | 6 + server/src/services/asset.service.ts | 60 +- server/src/services/duplicate.service.ts | 7 +- server/src/services/person.service.ts | 8 +- server/src/services/timeline.service.spec.ts | 30 +- server/src/services/timeline.service.ts | 72 +- server/src/utils/database.ts | 29 + server/src/utils/pagination.ts | 5 +- server/tsconfig.json | 3 +- 27 files changed, 4930 insertions(+), 1352 deletions(-) create mode 100644 server/src/prisma/find-non-deleted.ts create mode 100644 server/src/prisma/generated/types.ts create mode 100644 server/src/prisma/kysely.ts create mode 100644 server/src/prisma/metrics.ts create mode 100644 server/src/prisma/schema.prisma create mode 100644 server/src/repositories/prisma.repository.ts diff --git a/server/Dockerfile b/server/Dockerfile index d31ed90ad67a7..06abc670ca624 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -10,13 +10,17 @@ RUN npm ci && \ rm -rf node_modules/@img/sharp-libvips* && \ rm -rf node_modules/@img/sharp-linuxmusl-x64 COPY server . + +WORKDIR /usr/src/app/server +RUN npm run prisma:generate + +WORKDIR /usr/src/app ENV PATH="${PATH}:/usr/src/app/bin" \ IMMICH_ENV=development \ NVIDIA_DRIVER_CAPABILITIES=all \ NVIDIA_VISIBLE_DEVICES=all ENTRYPOINT ["tini", "--", "/bin/sh"] - FROM dev AS prod RUN npm run build diff --git a/server/package-lock.json b/server/package-lock.json index 8939386afb45d..b3f59d989e8a0 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -24,6 +24,7 @@ "@opentelemetry/context-async-hooks": "^1.24.0", "@opentelemetry/exporter-prometheus": "^0.51.0", "@opentelemetry/sdk-node": "^0.51.0", + "@prisma/client": "^5.11.0", "@react-email/components": "^0.0.17", "@socket.io/postgres-adapter": "^0.3.1", "archiver": "^7.0.0", @@ -43,6 +44,7 @@ "ioredis": "^5.3.2", "joi": "^17.10.0", "js-yaml": "^4.1.0", + "kysely": "^0.27.3", "lodash": "^4.17.21", "luxon": "^3.4.2", "mnemonist": "^0.39.8", @@ -53,6 +55,7 @@ "openid-client": "^5.4.3", "pg": "^8.11.3", "picomatch": "^4.0.0", + "prisma-extension-kysely": "^2.1.0", "react-email": "^2.1.2", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", @@ -95,6 +98,8 @@ "mock-fs": "^5.2.0", "prettier": "^3.0.2", "prettier-plugin-organize-imports": "^3.2.3", + "prisma": "^5.11.0", + "prisma-kysely": "^1.8.0", "rimraf": "^5.0.1", "source-map-support": "^0.5.21", "sql-formatter": "^15.0.0", @@ -321,6 +326,21 @@ "node": ">=0.12.0" } }, + "node_modules/@antfu/ni": { + "version": "0.21.8", + "resolved": "https://registry.npmjs.org/@antfu/ni/-/ni-0.21.8.tgz", + "integrity": "sha512-90X8pU2szlvw0AJo9EZMbYc2eQKkmO7mAdC4tD4r5co2Mm56MT37MIG8EyB7p4WRheuzGxuLDxJ63mF6+Zajiw==", + "dev": true, + "bin": { + "na": "bin/na.mjs", + "nci": "bin/nci.mjs", + "ni": "bin/ni.mjs", + "nlx": "bin/nlx.mjs", + "nr": "bin/nr.mjs", + "nu": "bin/nu.mjs", + "nun": "bin/nun.mjs" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -475,6 +495,39 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "dev": true, + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "dev": true, + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", + "dev": true + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "dev": true + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -1821,6 +1874,19 @@ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==" }, + "node_modules/@mrleebo/prisma-ast": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.7.0.tgz", + "integrity": "sha512-GTPkYf1meO2UXXIrz/SIDFWz+P4kXo2PTt36LYh/oNxV1PieYi7ZgenQk4IV0ut71Je3Z8ZoNZ8Tr7v2c1X1pg==", + "dev": true, + "dependencies": { + "chevrotain": "^10.5.0", + "lilconfig": "^2.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@msgpack/msgpack": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz", @@ -3663,10 +3729,7 @@ "@opentelemetry/semantic-conventions": "1.24.1" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.9.0" + "node": ">= 14" } }, "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": { @@ -3709,10 +3772,7 @@ "@opentelemetry/semantic-conventions": "1.24.1" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" + "node": ">= 10" } }, "node_modules/@opentelemetry/sdk-trace-node": { @@ -3728,10 +3788,7 @@ "semver": "^7.5.2" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" + "node": ">= 14" } }, "node_modules/@opentelemetry/semantic-conventions": { @@ -3739,7 +3796,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz", "integrity": "sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==", "engines": { - "node": ">=14" + "node": ">=8" } }, "node_modules/@opentelemetry/sql-common": { @@ -3761,19 +3818,10 @@ "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-10.0.0.tgz", "integrity": "sha512-8ZAjoj/irCuvUlyEinQ/HB6A8hP3bD1dgTOZvfl1b9nAwqniutFDHOQRcGM6Crea68bOwPj010f0Z4KkmuLHEA==" }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@pkgr/core": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", - "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" @@ -3787,363 +3835,625 @@ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==" }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + "node_modules/@prisma/client": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.14.0.tgz", + "integrity": "sha512-akMSuyvLKeoU4LeyBAUdThP/uhVP3GuLygFE3MlYzaCb3/J8SfsYBE5PkaFuLuVpLyA6sFoW+16z/aPhNAESqg==", + "hasInstallScript": true, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + "node_modules/@prisma/debug": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.13.0.tgz", + "integrity": "sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==", + "devOptional": true }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + "node_modules/@prisma/engines": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.13.0.tgz", + "integrity": "sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "5.13.0", + "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "@prisma/fetch-engine": "5.13.0", + "@prisma/get-platform": "5.13.0" + } }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + "node_modules/@prisma/engines-version": { + "version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz", + "integrity": "sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==", + "devOptional": true }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "node_modules/@prisma/fetch-engine": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz", + "integrity": "sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==", + "devOptional": true, "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@prisma/debug": "5.13.0", + "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "@prisma/get-platform": "5.13.0" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + "node_modules/@prisma/generator-helper": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-5.3.1.tgz", + "integrity": "sha512-zrYS0iHLgPlOJjYnd5KvVMMvSS+ktOL39EwooS5EnyvfzwfzxlKCeOUgxTfiKYs0WUWqzEvyNAYtramYgSknsQ==", + "dev": true, + "dependencies": { + "@prisma/debug": "5.3.1", + "@types/cross-spawn": "6.0.2", + "cross-spawn": "7.0.3", + "kleur": "4.1.5" + } }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + "node_modules/@prisma/generator-helper/node_modules/@prisma/debug": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.3.1.tgz", + "integrity": "sha512-eYrxqslEKf+wpMFIIHgbcNYuZBXUdiJLA85Or3TwOhgPIN1ZoXT9CwJph3ynW8H1Xg0LkdYLwVmuULCwiMoU5A==", + "dev": true, + "dependencies": { + "@types/debug": "4.1.8", + "debug": "4.3.4", + "strip-ansi": "6.0.1" + } }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + "node_modules/@prisma/get-platform": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.13.0.tgz", + "integrity": "sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.13.0" + } }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + "node_modules/@prisma/internals": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/internals/-/internals-5.3.1.tgz", + "integrity": "sha512-zkW73hPHHNrMD21PeYgCTBfMu71vzJf+WtfydtJbS0JVJKyLfOel0iWSQg7wjNeQfccKp+NdHJ/5rTJ4NEUzgA==", + "dev": true, + "dependencies": { + "@antfu/ni": "0.21.8", + "@opentelemetry/api": "1.4.1", + "@prisma/debug": "5.3.1", + "@prisma/engines": "5.3.1", + "@prisma/fetch-engine": "5.3.1", + "@prisma/generator-helper": "5.3.1", + "@prisma/get-platform": "5.3.1", + "@prisma/prisma-schema-wasm": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59", + "archiver": "5.3.2", + "arg": "5.0.2", + "checkpoint-client": "1.1.27", + "cli-truncate": "2.1.0", + "dotenv": "16.0.3", + "escape-string-regexp": "4.0.0", + "execa": "5.1.1", + "find-up": "5.0.0", + "fp-ts": "2.16.1", + "fs-extra": "11.1.1", + "fs-jetpack": "5.1.0", + "global-dirs": "3.0.1", + "globby": "11.1.0", + "indent-string": "4.0.0", + "is-windows": "1.0.2", + "is-wsl": "2.2.0", + "kleur": "4.1.5", + "new-github-issue-url": "0.2.1", + "node-fetch": "2.7.0", + "npm-packlist": "5.1.3", + "open": "7.4.2", + "p-map": "4.0.0", + "prompts": "2.4.2", + "read-pkg-up": "7.0.1", + "replace-string": "3.1.0", + "resolve": "1.22.4", + "string-width": "4.2.3", + "strip-ansi": "6.0.1", + "strip-indent": "3.0.0", + "temp-dir": "2.0.0", + "tempy": "1.0.1", + "terminal-link": "2.1.1", + "tmp": "0.2.1", + "ts-pattern": "4.3.0" + } + }, + "node_modules/@prisma/internals/node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + "node_modules/@prisma/internals/node_modules/@prisma/debug": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.3.1.tgz", + "integrity": "sha512-eYrxqslEKf+wpMFIIHgbcNYuZBXUdiJLA85Or3TwOhgPIN1ZoXT9CwJph3ynW8H1Xg0LkdYLwVmuULCwiMoU5A==", + "dev": true, + "dependencies": { + "@types/debug": "4.1.8", + "debug": "4.3.4", + "strip-ansi": "6.0.1" + } }, - "node_modules/@radix-ui/colors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-1.0.1.tgz", - "integrity": "sha512-xySw8f0ZVsAEP+e7iLl3EvcBXX7gsIlC1Zso/sPBW9gIWerBTgz6axrjU+MZ39wD+WFi5h5zdWpsg3+hwt2Qsg==" + "node_modules/@prisma/internals/node_modules/@prisma/engines": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.3.1.tgz", + "integrity": "sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==", + "dev": true, + "hasInstallScript": true }, - "node_modules/@radix-ui/primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "node_modules/@prisma/internals/node_modules/@prisma/fetch-engine": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.3.1.tgz", + "integrity": "sha512-w1yk1YiK8N82Pobdq58b85l6e8akyrkxuzwV9DoiUTRf3gpsuhJJesHc4Yi0WzUC9/3znizl1UfCsI6dhkj3Vw==", + "dev": true, + "dependencies": { + "@prisma/debug": "5.3.1", + "@prisma/get-platform": "5.3.1", + "execa": "5.1.1", + "find-cache-dir": "3.3.2", + "fs-extra": "11.1.1", + "hasha": "5.2.2", + "http-proxy-agent": "7.0.0", + "https-proxy-agent": "7.0.2", + "kleur": "4.1.5", + "node-fetch": "2.7.0", + "p-filter": "2.1.0", + "p-map": "4.0.0", + "p-retry": "4.6.2", + "progress": "2.0.3", + "rimraf": "3.0.2", + "temp-dir": "2.0.0", + "tempy": "1.0.1" + } + }, + "node_modules/@prisma/internals/node_modules/@prisma/get-platform": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.3.1.tgz", + "integrity": "sha512-3IiZY2BUjKnAuZ0569zppZE6/rZbVAM09//c2nvPbbkGG9MqrirA8fbhhF7tfVmhyVfdmVCHnf/ujWPHJ8B46Q==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" + "@prisma/debug": "5.3.1", + "escape-string-regexp": "4.0.0", + "execa": "5.1.1", + "fs-jetpack": "5.1.0", + "kleur": "4.1.5", + "replace-string": "3.1.0", + "strip-ansi": "6.0.1", + "tempy": "1.0.1", + "terminal-link": "2.1.1", + "ts-pattern": "4.3.0" } }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", - "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "node_modules/@prisma/internals/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "debug": "^4.3.4" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">= 14" } }, - "node_modules/@radix-ui/react-collapsible": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz", - "integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==", + "node_modules/@prisma/internals/node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "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" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">= 10" } }, - "node_modules/@radix-ui/react-collection": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", - "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "node_modules/@prisma/internals/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==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "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" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">= 6" } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "node_modules/@prisma/internals/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==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "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/@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "node_modules/@prisma/internals/node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/@prisma/internals/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==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">= 10" } }, - "node_modules/@radix-ui/react-direction": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", - "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "node_modules/@prisma/internals/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==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">= 10" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", - "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "node_modules/@prisma/internals/node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@prisma/internals/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": ">=10" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "node_modules/@prisma/internals/node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@prisma/internals/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", - "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "node_modules/@prisma/internals/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" + "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" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": "*" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "node_modules/@prisma/internals/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "agent-base": "^7.0.2", + "debug": "4" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">= 14" } }, - "node_modules/@radix-ui/react-popover": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz", - "integrity": "sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==", + "node_modules/@prisma/internals/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/@prisma/internals/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "path-key": "^3.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", - "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", + "node_modules/@prisma/internals/node_modules/resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-rect": "1.0.1", - "@radix-ui/react-use-size": "1.0.1", - "@radix-ui/rect": "1.0.1" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "bin": { + "resolve": "bin/resolve" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", - "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "node_modules/@prisma/internals/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@prisma/internals/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==", + "dev": true + }, + "node_modules/@prisma/internals/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==", + "dev": true + }, + "node_modules/@prisma/internals/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==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/@prisma/internals/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@prisma/internals/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==", + "dev": true, + "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/@prisma/internals/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@prisma/internals/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==", + "dev": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@prisma/internals/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==", + "dev": true, + "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/@prisma/prisma-schema-wasm": { + "version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59", + "resolved": "https://registry.npmjs.org/@prisma/prisma-schema-wasm/-/prisma-schema-wasm-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz", + "integrity": "sha512-+zUI7NQDXfcNnU8HgrAj4jRMv8yRfITLzcfv0Urf0adKimM+hkkVG4rX38i9zWMlxekkEBw7NLFx3Gxxy8d3iQ==", + "dev": true + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@radix-ui/colors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-1.0.1.tgz", + "integrity": "sha512-xySw8f0ZVsAEP+e7iLl3EvcBXX7gsIlC1Zso/sPBW9gIWerBTgz6axrjU+MZ39wD+WFi5h5zdWpsg3+hwt2Qsg==" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" @@ -4163,13 +4473,19 @@ } } }, - "node_modules/@radix-ui/react-presence": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", - "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "node_modules/@radix-ui/react-collapsible": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz", + "integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==", "dependencies": { "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { @@ -4187,12 +4503,15 @@ } } }, - "node_modules/@radix-ui/react-primitive": { + "node_modules/@radix-ui/react-collection": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", "dependencies": { "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.0.2" }, "peerDependencies": { @@ -4210,44 +4529,46 @@ } } }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", - "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1" + "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { "optional": true } } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "node_modules/@radix-ui/react-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" + "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", @@ -4259,15 +4580,17 @@ } } }, - "node_modules/@radix-ui/react-toggle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", - "integrity": "sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==", + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" }, "peerDependencies": { "@types/react": "*", @@ -4284,19 +4607,32 @@ } } }, - "node_modules/@radix-ui/react-toggle-group": { + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz", - "integrity": "sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", "dependencies": { "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-toggle": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" + "@radix-ui/react-use-callback-ref": "1.0.1" }, "peerDependencies": { "@types/react": "*", @@ -4313,51 +4649,45 @@ } } }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz", - "integrity": "sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg==", + "node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", "dependencies": { "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.2", - "@radix-ui/react-portal": "1.0.3", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-visually-hidden": "1.0.3" + "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", - "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "node_modules/@radix-ui/react-popover": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz", + "integrity": "sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" }, "peerDependencies": { "@types/react": "*", @@ -4374,10 +4704,10 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", - "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "node_modules/@radix-ui/react-popper": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", + "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", "dependencies": { "@babel/runtime": "^7.13.10", "@floating-ui/react-dom": "^2.0.0", @@ -4406,10 +4736,10 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", - "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" @@ -4429,65 +4759,91 @@ } } }, - "node_modules/@radix-ui/react-use-callback-ref": { + "node_modules/@radix-ui/react-presence": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", "dependencies": { - "@babel/runtime": "^7.13.10" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", "dependencies": { "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" + "@radix-ui/react-slot": "1.0.2" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", + "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", "dependencies": { "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", "dependencies": { - "@babel/runtime": "^7.13.10" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", @@ -4499,49 +4855,78 @@ } } }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", - "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "node_modules/@radix-ui/react-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", + "integrity": "sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==", "dependencies": { "@babel/runtime": "^7.13.10", - "@radix-ui/rect": "1.0.1" + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", - "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz", + "integrity": "sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==", "dependencies": { "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-toggle": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", - "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "node_modules/@radix-ui/react-tooltip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz", + "integrity": "sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg==", "dependencies": { "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.2", + "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" }, "peerDependencies": { "@types/react": "*", @@ -4558,24 +4943,235 @@ } } }, - "node_modules/@radix-ui/rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", - "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", + "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/@react-email/body": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.8.tgz", - "integrity": "sha512-gqdkNYlIaIw0OdpWu8KjIcQSIFvx7t2bZpXVxMMvBS859Ia1+1X3b5RNbjI3S1ZqLddUf7owOHkO4MiXGE+nxg==", + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/@react-email/button": { - "version": "0.0.15", + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", + "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", + "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@react-email/body": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.8.tgz", + "integrity": "sha512-gqdkNYlIaIw0OdpWu8KjIcQSIFvx7t2bZpXVxMMvBS859Ia1+1X3b5RNbjI3S1ZqLddUf7owOHkO4MiXGE+nxg==", + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/@react-email/button": { + "version": "0.0.15", "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.0.15.tgz", "integrity": "sha512-9Zi6SO3E8PoHYDfcJTecImiHLyitYWmIRs0HE3Ogra60ZzlWP2EXu+AZqwQnhXuq+9pbgwBWNWxB5YPetNPTNA==", "engines": { @@ -5532,6 +6128,24 @@ "@types/node": "*" } }, + "node_modules/@types/cross-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.2.tgz", + "integrity": "sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/docker-modem": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", @@ -5721,6 +6335,12 @@ "@types/node": "*" } }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, "node_modules/@types/multer": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", @@ -5883,6 +6503,12 @@ "@types/node": "*" } }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -6557,6 +7183,19 @@ "node": ">= 6.0.0" } }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -6888,6 +7527,15 @@ "node": "*" } }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -7414,17 +8062,110 @@ "node": "*" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/checkpoint-client": { + "version": "1.1.27", + "resolved": "https://registry.npmjs.org/checkpoint-client/-/checkpoint-client-1.1.27.tgz", + "integrity": "sha512-xstymfUalJOv6ZvTtmkwP4ORJN36ikT4PvrIoLe3wstbYf87XIXCcZrSmbFQOjyB0v1qbBnCsAscDpfdZlCkFA==", + "dev": true, "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", + "ci-info": "3.8.0", + "env-paths": "2.2.1", + "make-dir": "4.0.0", + "ms": "2.1.3", + "node-fetch": "2.6.12", + "uuid": "9.0.0" + } + }, + "node_modules/checkpoint-client/node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/checkpoint-client/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/checkpoint-client/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/checkpoint-client/node_modules/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/checkpoint-client/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "dev": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "engines": { @@ -7509,6 +8250,15 @@ "node": ">=0.8.0" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -7566,6 +8316,22 @@ "@colors/colors": "1.5.0" } }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-width": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", @@ -7698,6 +8464,12 @@ "node": ">= 6" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "node_modules/compress-commons": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", @@ -8010,6 +8782,15 @@ "node": ">= 8" } }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -8111,6 +8892,63 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "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/del/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -8509,6 +9347,15 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -9316,6 +10163,23 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -9456,6 +10320,12 @@ "node": ">= 0.6" } }, + "node_modules/fp-ts": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.1.tgz", + "integrity": "sha512-by7U5W8dkIzcvDofUcO42yl9JbnHTEDBrzu3pt5fKT+Z4Oy85I21K80EYJYdjQGC2qum4Vo55Ag57iiIK4FYuA==", + "dev": true + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -9519,6 +10389,36 @@ "node": ">=12" } }, + "node_modules/fs-jetpack": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-5.1.0.tgz", + "integrity": "sha512-Xn4fDhLydXkuzepZVsr02jakLlmoARPy+YWIclo4kh0GyNGUHnTqeH/w/qIsVn50dFxtp8otPL2t/HcPJBbxUA==", + "dev": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/fs-jetpack/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" + } + }, + "node_modules/fs-jetpack/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -9821,6 +10721,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/globals": { "version": "13.22.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", @@ -9970,6 +10894,31 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/hasown": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", @@ -10057,6 +11006,31 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -10127,26 +11101,59 @@ "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "minimatch": "^5.0.1" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/import-in-the-middle": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", - "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", - "dependencies": { + "node_modules/ignore-walk/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" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", + "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", + "dependencies": { "acorn": "^8.8.2", "acorn-import-assertions": "^1.9.0", "cjs-module-lexer": "^1.2.2", @@ -10303,6 +11310,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -10346,6 +11368,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -10376,6 +11407,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -10649,6 +11701,23 @@ "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/kysely": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.3.tgz", + "integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -11420,6 +12489,15 @@ "@nestjs/core": "^9.0.0 || ^10.0.0" } }, + "node_modules/new-github-issue-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/new-github-issue-url/-/new-github-issue-url-0.2.1.tgz", + "integrity": "sha512-md4cGoxuT4T4d/HDOXbrUHkTKrp/vp+m3aOA7XXVYwNsUNMK49g3SQicTSeV5GIz/5QVGAeYRAOlyp9OvlgsYA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/next": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", @@ -11600,6 +12678,85 @@ "node": ">=0.10.0" } }, + "node_modules/npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/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" + } + }, + "node_modules/npm-packlist/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-packlist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", @@ -11721,6 +12878,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/openid-client": { "version": "5.6.5", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz", @@ -11805,6 +12978,27 @@ "node": ">=0.10.0" } }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-filter/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11833,6 +13027,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -12138,6 +13369,70 @@ "node": ">= 6" } }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/pkg-types": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz", @@ -12439,14 +13734,54 @@ "node": ">=6" } }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "engines": { - "node": ">=6" - } - }, + "node_modules/prisma": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz", + "integrity": "sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "5.13.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/prisma-extension-kysely": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/prisma-extension-kysely/-/prisma-extension-kysely-2.1.0.tgz", + "integrity": "sha512-s1hujYjrNzfQc9Z79s93464mkTkkt9ZPqPELDRFe9jEmiIlM/JRXZtqIyyN3FM0GDkN/BDPVtpPtdC52XnjAcQ==", + "peerDependencies": { + "@prisma/client": "latest" + } + }, + "node_modules/prisma-kysely": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/prisma-kysely/-/prisma-kysely-1.8.0.tgz", + "integrity": "sha512-VpNpolZ8RXRgfU+j4R+fPZmX8EE95w3vJ2tt7+FwuiQc0leNTfLK5QLf3KbbPDes2rfjh3g20AjDxefQIo5GIA==", + "dev": true, + "dependencies": { + "@mrleebo/prisma-ast": "^0.7.0", + "@prisma/generator-helper": "5.3.1", + "@prisma/internals": "5.3.1", + "typescript": "^5.2.2", + "zod": "^3.22.2" + }, + "bin": { + "prisma-kysely": "dist/bin.js" + } + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -12460,6 +13795,37 @@ "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/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -13751,6 +15117,12 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "dev": true + }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -13790,6 +15162,18 @@ "node": ">=0.10" } }, + "node_modules/replace-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/replace-string/-/replace-string-3.1.0.tgz", + "integrity": "sha512-yPpxc4ZR2makceA9hy/jHNqc7QVkd4Je/N0WRHm6bs3PtivPuPynxE5ejU/mp5EhnCv8+uZL7vhz8rkluSlx+Q==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14391,6 +15775,12 @@ "node": ">= 10" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -14400,6 +15790,20 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/slice-source": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/slice-source/-/slice-source-0.4.1.tgz", @@ -14818,6 +16222,19 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -15020,6 +16437,62 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", + "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", + "dev": true, + "dependencies": { + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/terser": { "version": "5.27.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", @@ -15529,6 +17002,12 @@ } } }, + "node_modules/ts-pattern": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.3.0.tgz", + "integrity": "sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg==", + "dev": true + }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -15882,6 +17361,18 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -16588,6 +18079,15 @@ "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } + }, + "node_modules/zod": { + "version": "3.23.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.7.tgz", + "integrity": "sha512-NBeIoqbtOiUMomACV/y+V3Qfs9+Okr18vR5c/5pHClPpufWOrsx8TENboDPe265lFdfewX2yBtNTLPvnmCxwog==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -16731,6 +18231,12 @@ } } }, + "@antfu/ni": { + "version": "0.21.8", + "resolved": "https://registry.npmjs.org/@antfu/ni/-/ni-0.21.8.tgz", + "integrity": "sha512-90X8pU2szlvw0AJo9EZMbYc2eQKkmO7mAdC4tD4r5co2Mm56MT37MIG8EyB7p4WRheuzGxuLDxJ63mF6+Zajiw==", + "dev": true + }, "@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -16848,6 +18354,39 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "dev": true, + "requires": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "dev": true, + "requires": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", + "dev": true + }, + "@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "dev": true + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -17567,6 +19106,16 @@ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==" }, + "@mrleebo/prisma-ast": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.7.0.tgz", + "integrity": "sha512-GTPkYf1meO2UXXIrz/SIDFWz+P4kXo2PTt36LYh/oNxV1PieYi7ZgenQk4IV0ut71Je3Z8ZoNZ8Tr7v2c1X1pg==", + "dev": true, + "requires": { + "chevrotain": "^10.5.0", + "lilconfig": "^2.1.0" + } + }, "@msgpack/msgpack": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz", @@ -18698,85 +20247,544 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.51.1.tgz", "integrity": "sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==", "requires": { - "@opentelemetry/api-logs": "0.51.1", - "@types/shimmer": "^1.0.2", - "import-in-the-middle": "1.7.4", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" + "@opentelemetry/api-logs": "0.51.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.7.4", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + } + }, + "import-in-the-middle": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.4.tgz", + "integrity": "sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==", + "requires": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + } + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.24.1.tgz", + "integrity": "sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==", + "requires": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/resources": "1.24.1", + "@opentelemetry/semantic-conventions": "1.24.1" + } + }, + "@opentelemetry/sdk-trace-node": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.24.1.tgz", + "integrity": "sha512-/FZX8uWaGIAwsDhqI8VvQ+qWtfMNlXjaFYGc+vmxgdRFppCSSIRwrPyIhJO1qx61okyYhoyxVEZAfoiNxrfJCg==", + "requires": { + "@opentelemetry/context-async-hooks": "1.24.1", + "@opentelemetry/core": "1.24.1", + "@opentelemetry/propagator-b3": "1.24.1", + "@opentelemetry/propagator-jaeger": "1.24.1", + "@opentelemetry/sdk-trace-base": "1.24.1", + "semver": "^7.5.2" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz", + "integrity": "sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==" + }, + "@opentelemetry/sql-common": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", + "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", + "requires": { + "@opentelemetry/core": "^1.1.0" + } + }, + "@photostructure/tz-lookup": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-10.0.0.tgz", + "integrity": "sha512-8ZAjoj/irCuvUlyEinQ/HB6A8hP3bD1dgTOZvfl1b9nAwqniutFDHOQRcGM6Crea68bOwPj010f0Z4KkmuLHEA==" + }, + "@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true + }, + "@polka/url": { + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==" + }, + "@prisma/client": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.14.0.tgz", + "integrity": "sha512-akMSuyvLKeoU4LeyBAUdThP/uhVP3GuLygFE3MlYzaCb3/J8SfsYBE5PkaFuLuVpLyA6sFoW+16z/aPhNAESqg==", + "requires": {} + }, + "@prisma/debug": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.13.0.tgz", + "integrity": "sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==", + "devOptional": true + }, + "@prisma/engines": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.13.0.tgz", + "integrity": "sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.13.0", + "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "@prisma/fetch-engine": "5.13.0", + "@prisma/get-platform": "5.13.0" + } + }, + "@prisma/engines-version": { + "version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz", + "integrity": "sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==", + "devOptional": true + }, + "@prisma/fetch-engine": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz", + "integrity": "sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.13.0", + "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "@prisma/get-platform": "5.13.0" + } + }, + "@prisma/generator-helper": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-5.3.1.tgz", + "integrity": "sha512-zrYS0iHLgPlOJjYnd5KvVMMvSS+ktOL39EwooS5EnyvfzwfzxlKCeOUgxTfiKYs0WUWqzEvyNAYtramYgSknsQ==", + "dev": true, + "requires": { + "@prisma/debug": "5.3.1", + "@types/cross-spawn": "6.0.2", + "cross-spawn": "7.0.3", + "kleur": "4.1.5" + }, + "dependencies": { + "@prisma/debug": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.3.1.tgz", + "integrity": "sha512-eYrxqslEKf+wpMFIIHgbcNYuZBXUdiJLA85Or3TwOhgPIN1ZoXT9CwJph3ynW8H1Xg0LkdYLwVmuULCwiMoU5A==", + "dev": true, + "requires": { + "@types/debug": "4.1.8", + "debug": "4.3.4", + "strip-ansi": "6.0.1" + } + } + } + }, + "@prisma/get-platform": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.13.0.tgz", + "integrity": "sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.13.0" + } + }, + "@prisma/internals": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/internals/-/internals-5.3.1.tgz", + "integrity": "sha512-zkW73hPHHNrMD21PeYgCTBfMu71vzJf+WtfydtJbS0JVJKyLfOel0iWSQg7wjNeQfccKp+NdHJ/5rTJ4NEUzgA==", + "dev": true, + "requires": { + "@antfu/ni": "0.21.8", + "@opentelemetry/api": "1.4.1", + "@prisma/debug": "5.3.1", + "@prisma/engines": "5.3.1", + "@prisma/fetch-engine": "5.3.1", + "@prisma/generator-helper": "5.3.1", + "@prisma/get-platform": "5.3.1", + "@prisma/prisma-schema-wasm": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59", + "archiver": "5.3.2", + "arg": "5.0.2", + "checkpoint-client": "1.1.27", + "cli-truncate": "2.1.0", + "dotenv": "16.0.3", + "escape-string-regexp": "4.0.0", + "execa": "5.1.1", + "find-up": "5.0.0", + "fp-ts": "2.16.1", + "fs-extra": "11.1.1", + "fs-jetpack": "5.1.0", + "global-dirs": "3.0.1", + "globby": "11.1.0", + "indent-string": "4.0.0", + "is-windows": "1.0.2", + "is-wsl": "2.2.0", + "kleur": "4.1.5", + "new-github-issue-url": "0.2.1", + "node-fetch": "2.7.0", + "npm-packlist": "5.1.3", + "open": "7.4.2", + "p-map": "4.0.0", + "prompts": "2.4.2", + "read-pkg-up": "7.0.1", + "replace-string": "3.1.0", + "resolve": "1.22.4", + "string-width": "4.2.3", + "strip-ansi": "6.0.1", + "strip-indent": "3.0.0", + "temp-dir": "2.0.0", + "tempy": "1.0.1", + "terminal-link": "2.1.1", + "tmp": "0.2.1", + "ts-pattern": "4.3.0" + }, + "dependencies": { + "@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "dev": true + }, + "@prisma/debug": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.3.1.tgz", + "integrity": "sha512-eYrxqslEKf+wpMFIIHgbcNYuZBXUdiJLA85Or3TwOhgPIN1ZoXT9CwJph3ynW8H1Xg0LkdYLwVmuULCwiMoU5A==", + "dev": true, + "requires": { + "@types/debug": "4.1.8", + "debug": "4.3.4", + "strip-ansi": "6.0.1" + } + }, + "@prisma/engines": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.3.1.tgz", + "integrity": "sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==", + "dev": true + }, + "@prisma/fetch-engine": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.3.1.tgz", + "integrity": "sha512-w1yk1YiK8N82Pobdq58b85l6e8akyrkxuzwV9DoiUTRf3gpsuhJJesHc4Yi0WzUC9/3znizl1UfCsI6dhkj3Vw==", + "dev": true, + "requires": { + "@prisma/debug": "5.3.1", + "@prisma/get-platform": "5.3.1", + "execa": "5.1.1", + "find-cache-dir": "3.3.2", + "fs-extra": "11.1.1", + "hasha": "5.2.2", + "http-proxy-agent": "7.0.0", + "https-proxy-agent": "7.0.2", + "kleur": "4.1.5", + "node-fetch": "2.7.0", + "p-filter": "2.1.0", + "p-map": "4.0.0", + "p-retry": "4.6.2", + "progress": "2.0.3", + "rimraf": "3.0.2", + "temp-dir": "2.0.0", + "tempy": "1.0.1" + } + }, + "@prisma/get-platform": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.3.1.tgz", + "integrity": "sha512-3IiZY2BUjKnAuZ0569zppZE6/rZbVAM09//c2nvPbbkGG9MqrirA8fbhhF7tfVmhyVfdmVCHnf/ujWPHJ8B46Q==", + "dev": true, + "requires": { + "@prisma/debug": "5.3.1", + "escape-string-regexp": "4.0.0", + "execa": "5.1.1", + "fs-jetpack": "5.1.0", + "kleur": "4.1.5", + "replace-string": "3.1.0", + "strip-ansi": "6.0.1", + "tempy": "1.0.1", + "terminal-link": "2.1.1", + "ts-pattern": "4.3.0" + } + }, + "agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "requires": { + "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" + } + }, + "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==", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "requires": { + "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" + } + } + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + } + }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "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" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dev": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" } }, - "import-in-the-middle": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.4.tgz", - "integrity": "sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==", + "zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, "requires": { - "acorn": "^8.8.2", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^1.2.2", - "module-details-from-path": "^1.0.3" + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "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" + } + } } } } }, - "@opentelemetry/sdk-trace-base": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.24.1.tgz", - "integrity": "sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==", - "requires": { - "@opentelemetry/core": "1.24.1", - "@opentelemetry/resources": "1.24.1", - "@opentelemetry/semantic-conventions": "1.24.1" - } - }, - "@opentelemetry/sdk-trace-node": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.24.1.tgz", - "integrity": "sha512-/FZX8uWaGIAwsDhqI8VvQ+qWtfMNlXjaFYGc+vmxgdRFppCSSIRwrPyIhJO1qx61okyYhoyxVEZAfoiNxrfJCg==", - "requires": { - "@opentelemetry/context-async-hooks": "1.24.1", - "@opentelemetry/core": "1.24.1", - "@opentelemetry/propagator-b3": "1.24.1", - "@opentelemetry/propagator-jaeger": "1.24.1", - "@opentelemetry/sdk-trace-base": "1.24.1", - "semver": "^7.5.2" - } - }, - "@opentelemetry/semantic-conventions": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz", - "integrity": "sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==" - }, - "@opentelemetry/sql-common": { - "version": "0.40.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", - "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", - "requires": { - "@opentelemetry/core": "^1.1.0" - } - }, - "@photostructure/tz-lookup": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-10.0.0.tgz", - "integrity": "sha512-8ZAjoj/irCuvUlyEinQ/HB6A8hP3bD1dgTOZvfl1b9nAwqniutFDHOQRcGM6Crea68bOwPj010f0Z4KkmuLHEA==" - }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true - }, - "@pkgr/core": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", - "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "@prisma/prisma-schema-wasm": { + "version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59", + "resolved": "https://registry.npmjs.org/@prisma/prisma-schema-wasm/-/prisma-schema-wasm-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz", + "integrity": "sha512-+zUI7NQDXfcNnU8HgrAj4jRMv8yRfITLzcfv0Urf0adKimM+hkkVG4rX38i9zWMlxekkEBw7NLFx3Gxxy8d3iQ==", "dev": true }, - "@polka/url": { - "version": "1.0.0-next.25", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", - "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==" - }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -19825,6 +21833,24 @@ "@types/node": "*" } }, + "@types/cross-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.2.tgz", + "integrity": "sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dev": true, + "requires": { + "@types/ms": "*" + } + }, "@types/docker-modem": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", @@ -20014,6 +22040,12 @@ "@types/node": "*" } }, + "@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, "@types/multer": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", @@ -20163,6 +22195,12 @@ "@types/node": "*" } }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, "@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -20689,6 +22727,16 @@ "debug": "4" } }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -20925,6 +22973,12 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -21293,6 +23347,72 @@ "get-func-name": "^2.0.2" } }, + "checkpoint-client": { + "version": "1.1.27", + "resolved": "https://registry.npmjs.org/checkpoint-client/-/checkpoint-client-1.1.27.tgz", + "integrity": "sha512-xstymfUalJOv6ZvTtmkwP4ORJN36ikT4PvrIoLe3wstbYf87XIXCcZrSmbFQOjyB0v1qbBnCsAscDpfdZlCkFA==", + "dev": true, + "requires": { + "ci-info": "3.8.0", + "env-paths": "2.2.1", + "make-dir": "4.0.0", + "ms": "2.1.3", + "node-fetch": "2.6.12", + "uuid": "9.0.0" + }, + "dependencies": { + "ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true + }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true + } + } + }, + "chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "dev": true, + "requires": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, "chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -21361,6 +23481,12 @@ } } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -21397,6 +23523,16 @@ "string-width": "^4.2.0" } }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, "cli-width": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", @@ -21498,6 +23634,12 @@ "repeat-string": "^1.6.1" } }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "compress-commons": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", @@ -21722,6 +23864,12 @@ "which": "^2.0.1" } }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -21788,6 +23936,47 @@ "has-property-descriptors": "^1.0.1" } }, + "del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "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" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -22093,6 +24282,12 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -22700,6 +24895,17 @@ } } }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -22800,6 +25006,12 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, + "fp-ts": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.1.tgz", + "integrity": "sha512-by7U5W8dkIzcvDofUcO42yl9JbnHTEDBrzu3pt5fKT+Z4Oy85I21K80EYJYdjQGC2qum4Vo55Ag57iiIK4FYuA==", + "dev": true + }, "fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -22836,6 +25048,35 @@ "universalify": "^2.0.0" } }, + "fs-jetpack": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-5.1.0.tgz", + "integrity": "sha512-Xn4fDhLydXkuzepZVsr02jakLlmoARPy+YWIclo4kh0GyNGUHnTqeH/w/qIsVn50dFxtp8otPL2t/HcPJBbxUA==", + "dev": true, + "requires": { + "minimatch": "^5.1.0" + }, + "dependencies": { + "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, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -23053,6 +25294,23 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, + "global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "requires": { + "ini": "2.0.0" + }, + "dependencies": { + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + } + } + }, "globals": { "version": "13.22.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", @@ -23154,6 +25412,24 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, "hasown": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", @@ -23219,6 +25495,27 @@ "toidentifier": "1.0.1" } }, + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "dependencies": { + "agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + } + } + }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -23260,6 +25557,35 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==" }, + "ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "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, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -23396,6 +25722,12 @@ "has": "^1.0.3" } }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -23424,6 +25756,12 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -23439,6 +25777,21 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -23644,6 +25997,17 @@ "json-buffer": "3.0.1" } }, + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true + }, + "kysely": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.3.tgz", + "integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==" + }, "lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -24243,6 +26607,12 @@ "response-time": "^2.3.2" } }, + "new-github-issue-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/new-github-issue-url/-/new-github-issue-url-0.2.1.tgz", + "integrity": "sha512-md4cGoxuT4T4d/HDOXbrUHkTKrp/vp+m3aOA7XXVYwNsUNMK49g3SQicTSeV5GIz/5QVGAeYRAOlyp9OvlgsYA==", + "dev": true + }, "next": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", @@ -24354,6 +26724,66 @@ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" }, + "npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^2.0.0" + } + }, + "npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true + }, + "npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "requires": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "dependencies": { + "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, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", @@ -24441,6 +26871,16 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, "openid-client": { "version": "5.6.5", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz", @@ -24506,6 +26946,23 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" }, + "p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "requires": { + "p-map": "^2.0.0" + }, + "dependencies": { + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -24522,6 +26979,33 @@ "p-limit": "^3.0.2" } }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "dependencies": { + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + } + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -24747,6 +27231,54 @@ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, "pkg-types": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz", @@ -24919,6 +27451,34 @@ } } }, + "prisma": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz", + "integrity": "sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==", + "devOptional": true, + "requires": { + "@prisma/engines": "5.13.0" + } + }, + "prisma-extension-kysely": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/prisma-extension-kysely/-/prisma-extension-kysely-2.1.0.tgz", + "integrity": "sha512-s1hujYjrNzfQc9Z79s93464mkTkkt9ZPqPELDRFe9jEmiIlM/JRXZtqIyyN3FM0GDkN/BDPVtpPtdC52XnjAcQ==", + "requires": {} + }, + "prisma-kysely": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/prisma-kysely/-/prisma-kysely-1.8.0.tgz", + "integrity": "sha512-VpNpolZ8RXRgfU+j4R+fPZmX8EE95w3vJ2tt7+FwuiQc0leNTfLK5QLf3KbbPDes2rfjh3g20AjDxefQIo5GIA==", + "dev": true, + "requires": { + "@mrleebo/prisma-ast": "^0.7.0", + "@prisma/generator-helper": "5.3.1", + "@prisma/internals": "5.3.1", + "typescript": "^5.2.2", + "zod": "^3.22.2" + } + }, "prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", @@ -24934,6 +27494,30 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "dependencies": { + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + } + } + }, "proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -25693,6 +28277,12 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "dev": true + }, "regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -25722,6 +28312,12 @@ "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true }, + "replace-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/replace-string/-/replace-string-3.1.0.tgz", + "integrity": "sha512-yPpxc4ZR2makceA9hy/jHNqc7QVkd4Je/N0WRHm6bs3PtivPuPynxE5ejU/mp5EhnCv8+uZL7vhz8rkluSlx+Q==", + "dev": true + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -26175,12 +28771,29 @@ "totalist": "^3.0.0" } }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, "slice-source": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/slice-source/-/slice-source-0.4.1.tgz", @@ -26504,6 +29117,16 @@ "has-flag": "^4.0.0" } }, + "supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -26644,6 +29267,43 @@ "streamx": "^2.15.0" } }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true + }, + "tempy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", + "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", + "dev": true, + "requires": { + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true + } + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, "terser": { "version": "5.27.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", @@ -27035,6 +29695,12 @@ "yn": "3.1.1" } }, + "ts-pattern": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.3.0.tgz", + "integrity": "sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg==", + "dev": true + }, "tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -27217,6 +29883,15 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -27650,6 +30325,12 @@ } } } + }, + "zod": { + "version": "3.23.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.7.tgz", + "integrity": "sha512-NBeIoqbtOiUMomACV/y+V3Qfs9+Okr18vR5c/5pHClPpufWOrsx8TENboDPe265lFdfewX2yBtNTLPvnmCxwog==", + "dev": true } } } diff --git a/server/package.json b/server/package.json index ad73a99ea071c..b993144d01154 100644 --- a/server/package.json +++ b/server/package.json @@ -30,7 +30,8 @@ "typeorm:schema:drop": "typeorm query -d ./dist/database.config.js 'DROP schema public cascade; CREATE schema public;'", "typeorm:schema:reset": "npm run typeorm:schema:drop && npm run typeorm:migrations:run", "sql:generate": "node ./dist/utils/sql.js", - "email:dev": "email dev -p 3050 --dir src/emails" + "email:dev": "email dev -p 3050 --dir src/emails", + "prisma:generate": "prisma generate --schema=./src/prisma/schema.prisma" }, "dependencies": { "@nestjs/bullmq": "^10.0.1", @@ -49,6 +50,7 @@ "@opentelemetry/exporter-prometheus": "^0.51.0", "@opentelemetry/sdk-node": "^0.51.0", "@react-email/components": "^0.0.17", + "@prisma/client": "^5.11.0", "@socket.io/postgres-adapter": "^0.3.1", "archiver": "^7.0.0", "async-lock": "^1.4.0", @@ -67,6 +69,7 @@ "ioredis": "^5.3.2", "joi": "^17.10.0", "js-yaml": "^4.1.0", + "kysely": "^0.27.3", "lodash": "^4.17.21", "luxon": "^3.4.2", "mnemonist": "^0.39.8", @@ -77,6 +80,7 @@ "openid-client": "^5.4.3", "pg": "^8.11.3", "picomatch": "^4.0.0", + "prisma-extension-kysely": "^2.1.0", "react-email": "^2.1.2", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", @@ -119,6 +123,8 @@ "mock-fs": "^5.2.0", "prettier": "^3.0.2", "prettier-plugin-organize-imports": "^3.2.3", + "prisma": "^5.11.0", + "prisma-kysely": "^1.8.0", "rimraf": "^5.0.1", "source-map-support": "^0.5.21", "sql-formatter": "^15.0.0", diff --git a/server/src/database.config.ts b/server/src/database.config.ts index 9b7e16ae58d5b..a5bc6e23dae94 100644 --- a/server/src/database.config.ts +++ b/server/src/database.config.ts @@ -2,16 +2,16 @@ import { DatabaseExtension } from 'src/interfaces/database.interface'; import { DataSource } from 'typeorm'; import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js'; -const url = process.env.DB_URL; -const urlOrParts = url - ? { url } - : { - host: process.env.DB_HOSTNAME || 'database', - port: Number.parseInt(process.env.DB_PORT || '5432'), - username: process.env.DB_USERNAME || 'postgres', - password: process.env.DB_PASSWORD || 'postgres', - database: process.env.DB_DATABASE_NAME || 'immich', - }; +let url = process.env.DB_URL; +if (!url) { + const host = process.env.DB_HOSTNAME || 'database'; + const port = Number.parseInt(process.env.DB_PORT || '5432'); + const username = process.env.DB_USERNAME || 'postgres'; + const password = process.env.DB_PASSWORD || 'postgres'; + const database = process.env.DB_DATABASE_NAME || 'immich'; + url = `postgres://${username}:${password}@${host}:${port}/${database}`; + process.env.DB_URL = url; +} /* eslint unicorn/prefer-module: "off" -- We can fix this when migrating to ESM*/ export const databaseConfig: PostgresConnectionOptions = { @@ -23,7 +23,7 @@ export const databaseConfig: PostgresConnectionOptions = { synchronize: false, connectTimeoutMS: 10_000, // 10 seconds parseInt8: true, - ...urlOrParts, + url, }; /** diff --git a/server/src/dtos/duplicate.dto.ts b/server/src/dtos/duplicate.dto.ts index cdfeed056f260..166f18ce8f6fc 100644 --- a/server/src/dtos/duplicate.dto.ts +++ b/server/src/dtos/duplicate.dto.ts @@ -1,19 +1,6 @@ -import { groupBy } from 'lodash'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; export class DuplicateResponseDto { duplicateId!: string; assets!: AssetResponseDto[]; } - -export function mapDuplicateResponse(assets: AssetResponseDto[]): DuplicateResponseDto[] { - const result = []; - - const grouped = groupBy(assets, (a) => a.duplicateId); - - for (const [duplicateId, assets] of Object.entries(grouped)) { - result.push({ duplicateId, assets }); - } - - return result; -} diff --git a/server/src/interfaces/asset.interface.ts b/server/src/interfaces/asset.interface.ts index 4b9ff031e54bf..d151e5c4a223b 100644 --- a/server/src/interfaces/asset.interface.ts +++ b/server/src/interfaces/asset.interface.ts @@ -1,3 +1,4 @@ +import { Prisma } from '@prisma/client'; import { AssetOrder } from 'src/entities/album.entity'; import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; @@ -5,7 +6,6 @@ import { ExifEntity } from 'src/entities/exif.entity'; import { ReverseGeocodeResult } from 'src/interfaces/metadata.interface'; import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.interface'; import { Paginated, PaginationOptions } from 'src/utils/pagination'; -import { FindOptionsOrder, FindOptionsRelations, FindOptionsSelect } from 'typeorm'; export type AssetStats = Record; @@ -64,7 +64,7 @@ export interface AssetBuilderOptions { isDuplicate?: boolean; albumId?: string; personId?: string; - userIds?: string[]; + userIds: string[]; withStacked?: boolean; exifInfo?: boolean; assetType?: AssetType; @@ -80,22 +80,6 @@ export interface TimeBucketItem { count: number; } -export type AssetCreate = Pick< - AssetEntity, - | 'deviceAssetId' - | 'ownerId' - | 'libraryId' - | 'deviceId' - | 'type' - | 'originalPath' - | 'fileCreatedAt' - | 'localDateTime' - | 'fileModifiedAt' - | 'checksum' - | 'originalFileName' -> & - Partial; - export type AssetWithoutRelations = Omit< AssetEntity, | 'livePhotoVideo' @@ -111,7 +95,23 @@ export type AssetWithoutRelations = Omit< | 'tags' >; -export type AssetUpdateOptions = Pick & Partial; +export type AssetCreate = Pick< + AssetEntity, + | 'deviceAssetId' + | 'ownerId' + | 'libraryId' + | 'deviceId' + | 'type' + | 'originalPath' + | 'fileCreatedAt' + | 'localDateTime' + | 'fileModifiedAt' + | 'checksum' + | 'originalFileName' +> & + Partial; + +export type AssetUpdateOptions = Pick & Partial; export type AssetUpdateAllOptions = Omit, 'id'>; @@ -151,28 +151,25 @@ export interface AssetUpdateDuplicateOptions { duplicateIds: string[]; } +export interface AssetDuplicateGroup { + duplicateId: string; + assets: AssetEntity[]; +} + export type AssetPathEntity = Pick; export const IAssetRepository = 'IAssetRepository'; export interface IAssetRepository { create(asset: AssetCreate): Promise; - getByIds( - ids: string[], - relations?: FindOptionsRelations, - select?: FindOptionsSelect, - ): Promise; + getByIds(ids: string[], relations?: Prisma.AssetsInclude): Promise; getByIdsWithAllRelations(ids: string[]): Promise; getByDayOfYear(ownerIds: string[], monthDay: MonthDay): Promise; getByChecksum(libraryId: string, checksum: Buffer): Promise; getUploadAssetIdByChecksum(ownerId: string, checksum: Buffer): Promise; getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated; getByUserId(pagination: PaginationOptions, userId: string, options?: AssetSearchOptions): Paginated; - getById( - id: string, - relations?: FindOptionsRelations, - order?: FindOptionsOrder, - ): Promise; + getById(id: string, relations?: Prisma.AssetsInclude): Promise; getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated; getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated; getRandom(userId: string, count: number): Promise; @@ -185,7 +182,7 @@ export interface IAssetRepository { getAllByDeviceId(userId: string, deviceId: string): Promise; updateAll(ids: string[], options: Partial): Promise; updateDuplicates(options: AssetUpdateDuplicateOptions): Promise; - update(asset: AssetUpdateOptions): Promise; + update(asset: AssetUpdateOptions): Promise; remove(asset: AssetEntity): Promise; softDeleteAll(ids: string[]): Promise; restoreAll(ids: string[]): Promise; @@ -198,7 +195,7 @@ export interface IAssetRepository { upsertJobStatus(...jobStatus: Partial[]): Promise; getAssetIdByCity(userId: string, options: AssetExploreFieldOptions): Promise>; getAssetIdByTag(userId: string, options: AssetExploreFieldOptions): Promise>; - getDuplicates(options: AssetBuilderOptions): Promise; + getDuplicates(userIds: string[]): Promise; getAllForUserFullSync(options: AssetFullSyncOptions): Promise; getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise; } diff --git a/server/src/interfaces/search.interface.ts b/server/src/interfaces/search.interface.ts index 57523aa940576..8487c1236b9a9 100644 --- a/server/src/interfaces/search.interface.ts +++ b/server/src/interfaces/search.interface.ts @@ -149,7 +149,7 @@ export type SmartSearchOptions = SearchDateOptions & export interface FaceEmbeddingSearch extends SearchEmbeddingOptions { hasPerson?: boolean; numResults: number; - maxDistance?: number; + maxDistance: number; } export interface AssetDuplicateSearch { diff --git a/server/src/main.ts b/server/src/main.ts index 167c772690180..49c75b95a8e25 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -3,6 +3,7 @@ import { Worker } from 'node:worker_threads'; import { ImmichAdminModule } from 'src/app.module'; import { LogLevel } from 'src/config'; import { getWorkers } from 'src/utils/workers'; + const immichApp = process.argv[2] || process.env.IMMICH_APP; if (process.argv[2] === immichApp) { diff --git a/server/src/prisma/find-non-deleted.ts b/server/src/prisma/find-non-deleted.ts new file mode 100644 index 0000000000000..3a2638493a313 --- /dev/null +++ b/server/src/prisma/find-non-deleted.ts @@ -0,0 +1,32 @@ +import { Prisma } from '@prisma/client'; + +const excludeDeleted = ({ args, query }: { args: any; query: any }) => { + if (args.where === undefined) { + args.where = { deletedAt: null }; + } else if ( + args.where.deletedAt === undefined && + !args.where.OR?.some(({ deletedAt }: any) => deletedAt !== undefined) && + !args.where.AND?.some(({ deletedAt }: any) => deletedAt !== undefined) + ) { + args.where.deletedAt = null; + } + + return query(args); +}; + +const findNonDeleted = { + findFirst: excludeDeleted, + findFirstOrThrow: excludeDeleted, + findMany: excludeDeleted, + findUnique: excludeDeleted, + findUniqueOrThrow: excludeDeleted, +}; + +export const findNonDeletedExtension = Prisma.defineExtension({ + query: { + albums: findNonDeleted, + assets: findNonDeleted, + libraries: findNonDeleted, + users: findNonDeleted, + }, +}); diff --git a/server/src/prisma/generated/types.ts b/server/src/prisma/generated/types.ts new file mode 100644 index 0000000000000..dd8809307dc4f --- /dev/null +++ b/server/src/prisma/generated/types.ts @@ -0,0 +1,291 @@ +import type { ColumnType } from "kysely"; +export type Generated = T extends ColumnType + ? ColumnType + : ColumnType; +export type Timestamp = ColumnType; + +export type Activity = { + id: Generated; + createdAt: Generated; + updatedAt: Generated; + albumId: string; + userId: string; + assetId: string | null; + comment: string | null; + isLiked: Generated; +}; +export type Albums = { + id: Generated; + ownerId: string; + albumName: Generated; + createdAt: Generated; + albumThumbnailAssetId: string | null; + updatedAt: Generated; + description: Generated; + deletedAt: Timestamp | null; + isActivityEnabled: Generated; + order: Generated; +}; +export type AlbumsAssetsAssets = { + albumsId: string; + assetsId: string; +}; +export type AlbumsSharedUsersUsers = { + albumsId: string; + usersId: string; +}; +export type ApiKeys = { + name: string; + key: string; + userId: string; + createdAt: Generated; + updatedAt: Generated; + id: Generated; +}; +export type AssetFaces = { + assetId: string; + personId: string | null; + imageWidth: Generated; + imageHeight: Generated; + boundingBoxX1: Generated; + boundingBoxY1: Generated; + boundingBoxX2: Generated; + boundingBoxY2: Generated; + id: Generated; +}; +export type AssetJobStatus = { + assetId: string; + facesRecognizedAt: Timestamp | null; + metadataExtractedAt: Timestamp | null; + duplicatesDetectedAt: Timestamp | null; +}; +export type Assets = { + id: Generated; + deviceAssetId: string; + ownerId: string; + deviceId: string; + type: string; + originalPath: string; + previewPath: string | null; + fileCreatedAt: Timestamp; + fileModifiedAt: Timestamp; + isFavorite: Generated; + duration: string | null; + thumbnailPath: Generated; + encodedVideoPath: Generated; + checksum: Buffer; + isVisible: Generated; + livePhotoVideoId: string | null; + updatedAt: Generated; + createdAt: Generated; + isArchived: Generated; + originalFileName: string; + sidecarPath: string | null; + thumbhash: Buffer | null; + isOffline: Generated; + libraryId: string; + deletedAt: Timestamp | null; + localDateTime: Timestamp; + stackId: string | null; + duplicateId: string | null; +}; +export type AssetStack = { + id: Generated; + primaryAssetId: string; +}; +export type Audit = { + id: Generated; + entityType: string; + entityId: string; + action: string; + ownerId: string; + createdAt: Generated; +}; +export type Exif = { + assetId: string; + make: string | null; + model: string | null; + exifImageWidth: number | null; + exifImageHeight: number | null; + fileSizeInByte: string | null; + orientation: string | null; + dateTimeOriginal: Timestamp | null; + modifyDate: Timestamp | null; + lensModel: string | null; + fNumber: number | null; + focalLength: number | null; + iso: number | null; + latitude: number | null; + longitude: number | null; + city: string | null; + state: string | null; + country: string | null; + description: Generated; + fps: number | null; + exposureTime: string | null; + livePhotoCID: string | null; + timeZone: string | null; + projectionType: string | null; + profileDescription: string | null; + colorspace: string | null; + bitsPerSample: number | null; + autoStackId: string | null; +}; +export type GeodataPlaces = { + id: number; + name: string; + longitude: number; + latitude: number; + countryCode: string; + admin1Code: string | null; + admin2Code: string | null; + modificationDate: Timestamp; + admin1Name: string | null; + admin2Name: string | null; + alternateNames: string | null; +}; +export type Libraries = { + id: Generated; + name: string; + ownerId: string; + type: string; + importPaths: string[]; + exclusionPatterns: string[]; + createdAt: Generated; + updatedAt: Generated; + deletedAt: Timestamp | null; + refreshedAt: Timestamp | null; + isVisible: Generated; +}; +export type MoveHistory = { + id: Generated; + entityId: string; + pathType: string; + oldPath: string; + newPath: string; +}; +export type Partners = { + sharedById: string; + sharedWithId: string; + createdAt: Generated; + updatedAt: Generated; + inTimeline: Generated; +}; +export type Person = { + id: Generated; + createdAt: Generated; + updatedAt: Generated; + ownerId: string; + name: Generated; + thumbnailPath: Generated; + isHidden: Generated; + birthDate: Timestamp | null; + faceAssetId: string | null; +}; +export type SharedLinkAsset = { + assetsId: string; + sharedLinksId: string; +}; +export type SharedLinks = { + id: Generated; + description: string | null; + userId: string; + key: Buffer; + type: string; + createdAt: Generated; + expiresAt: Timestamp | null; + allowUpload: Generated; + albumId: string | null; + allowDownload: Generated; + showExif: Generated; + password: string | null; +}; +export type SmartInfo = { + assetId: string; + tags: string[]; + objects: string[]; +}; +export type SmartSearch = { + assetId: string; +}; +export type SocketIoAttachments = { + id: Generated; + created_at: Generated; + payload: Buffer | null; +}; +export type SystemConfig = { + key: string; + value: string | null; +}; +export type SystemMetadata = { + key: string; + value: Generated; +}; +export type TagAsset = { + assetsId: string; + tagsId: string; +}; +export type Tags = { + id: Generated; + type: string; + name: string; + userId: string; + renameTagId: string | null; +}; +export type Users = { + id: Generated; + email: string; + password: Generated; + createdAt: Generated; + profileImagePath: Generated; + isAdmin: Generated; + shouldChangePassword: Generated; + deletedAt: Timestamp | null; + oauthId: Generated; + updatedAt: Generated; + storageLabel: string | null; + memoriesEnabled: Generated; + name: Generated; + avatarColor: string | null; + quotaSizeInBytes: string | null; + quotaUsageInBytes: Generated; + status: Generated; +}; +export type UserToken = { + id: Generated; + token: string; + createdAt: Generated; + updatedAt: Generated; + userId: string; + deviceType: Generated; + deviceOS: Generated; +}; +export type DB = { + activity: Activity; + albums: Albums; + albums_assets_assets: AlbumsAssetsAssets; + albums_shared_users_users: AlbumsSharedUsersUsers; + api_keys: ApiKeys; + asset_faces: AssetFaces; + asset_job_status: AssetJobStatus; + asset_stack: AssetStack; + assets: Assets; + audit: Audit; + exif: Exif; + geodata_places: GeodataPlaces; + libraries: Libraries; + move_history: MoveHistory; + partners: Partners; + person: Person; + shared_link__asset: SharedLinkAsset; + shared_links: SharedLinks; + smart_info: SmartInfo; + smart_search: SmartSearch; + socket_io_attachments: SocketIoAttachments; + system_config: SystemConfig; + system_metadata: SystemMetadata; + tag_asset: TagAsset; + tags: Tags; + user_token: UserToken; + users: Users; +}; diff --git a/server/src/prisma/kysely.ts b/server/src/prisma/kysely.ts new file mode 100644 index 0000000000000..c151158450f2a --- /dev/null +++ b/server/src/prisma/kysely.ts @@ -0,0 +1,16 @@ +import { DeduplicateJoinsPlugin, Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler } from 'kysely'; +import kyselyExt from 'prisma-extension-kysely'; +import type { DB } from 'src/prisma/generated/types'; + +export const kyselyExtension = kyselyExt({ + kysely: (driver) => + new Kysely({ + dialect: { + createDriver: () => driver, + createAdapter: () => new PostgresAdapter(), + createIntrospector: (db) => new PostgresIntrospector(db), + createQueryCompiler: () => new PostgresQueryCompiler(), + }, + plugins: [new DeduplicateJoinsPlugin()], + }), +}); diff --git a/server/src/prisma/metrics.ts b/server/src/prisma/metrics.ts new file mode 100644 index 0000000000000..84741d61f8102 --- /dev/null +++ b/server/src/prisma/metrics.ts @@ -0,0 +1,17 @@ +import { Prisma } from '@prisma/client'; +import util from 'node:util'; + +export const metricsExtension = Prisma.defineExtension({ + query: { + $allModels: { + async $allOperations({ operation, model, args, query }) { + const start = performance.now(); + const result = await query(args); + const end = performance.now(); + const time = end - start; + console.log(util.inspect({ model, operation, args, time }, { showHidden: false, depth: null, colors: true })); + return result; + }, + }, + }, +}); diff --git a/server/src/prisma/schema.prisma b/server/src/prisma/schema.prisma new file mode 100644 index 0000000000000..0e8d66a2b2ee9 --- /dev/null +++ b/server/src/prisma/schema.prisma @@ -0,0 +1,465 @@ +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions", "relationJoins"] +} + +generator kysely { + provider = "prisma-kysely" +} + +datasource db { + provider = "postgresql" + url = env("DB_URL") + extensions = [cube, earthdistance, pg_trgm, unaccent, uuid_ossp(map: "uuid-ossp", schema: "public"), vectors(map: "vectors", schema: "vectors")] +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +model Activity { + id String @id(map: "PK_24625a1d6b1b089c8ae206fe467") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @default(now()) @db.Timestamptz(6) + albumId String @db.Uuid + userId String @db.Uuid + assetId String? @db.Uuid + comment String? + isLiked Boolean @default(false) + albums Albums @relation(fields: [albumId], references: [id], onDelete: Cascade, map: "FK_1af8519996fbfb3684b58df280b") + users Users @relation(fields: [userId], references: [id], onDelete: Cascade, map: "FK_3571467bcbe021f66e2bdce96ea") + assets Assets? @relation(fields: [assetId], references: [id], onDelete: Cascade, map: "FK_8091ea76b12338cb4428d33d782") + + @@map(name: "activity") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model Albums { + id String @id(map: "PK_7f71c7b5bc7c87b8f94c9a93a00") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + ownerId String @db.Uuid + albumName String @default("Untitled Album") @db.VarChar + createdAt DateTime @default(now()) @db.Timestamptz(6) + albumThumbnailAssetId String? @db.Uuid + updatedAt DateTime @default(now()) @db.Timestamptz(6) + description String @default("") + deletedAt DateTime? @db.Timestamptz(6) + isActivityEnabled Boolean @default(true) + order String @default("desc") @db.VarChar + activity Activity[] + assets Assets? @relation(fields: [albumThumbnailAssetId], references: [id], map: "FK_05895aa505a670300d4816debce") + users Users @relation(fields: [ownerId], references: [id], onDelete: Cascade, map: "FK_b22c53f35ef20c28c21637c85f4") + albums_assets_assets AlbumsAssetsAssets[] + albums_shared_users_users AlbumsSharedUsersUsers[] + shared_links SharedLinks[] + + @@map(name: "albums") +} + +model AlbumsAssetsAssets { + albumsId String @db.Uuid + assetsId String @db.Uuid + assets Assets @relation(fields: [assetsId], references: [id], onDelete: Cascade, map: "FK_4bd1303d199f4e72ccdf998c621") + albums Albums @relation(fields: [albumsId], references: [id], onDelete: Cascade, map: "FK_e590fa396c6898fcd4a50e40927") + + @@id([albumsId, assetsId], map: "PK_c67bc36fa845fb7b18e0e398180") + @@index([assetsId], map: "IDX_4bd1303d199f4e72ccdf998c62") + @@index([albumsId], map: "IDX_e590fa396c6898fcd4a50e4092") + @@map(name: "albums_assets_assets") +} + +model AlbumsSharedUsersUsers { + albumsId String @db.Uuid + usersId String @db.Uuid + albums Albums @relation(fields: [albumsId], references: [id], onDelete: Cascade, map: "FK_427c350ad49bd3935a50baab737") + users Users @relation(fields: [usersId], references: [id], onDelete: Cascade, map: "FK_f48513bf9bccefd6ff3ad30bd06") + + @@id([albumsId, usersId], map: "PK_7df55657e0b2e8b626330a0ebc8") + @@index([albumsId], map: "IDX_427c350ad49bd3935a50baab73") + @@index([usersId], map: "IDX_f48513bf9bccefd6ff3ad30bd0") + @@map(name: "albums_shared_users_users") +} + +model ApiKeys { + name String @db.VarChar + key String @db.VarChar + userId String @db.Uuid + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @default(now()) @db.Timestamptz(6) + id String @id(map: "PK_5c8a79801b44bd27b79228e1dad") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + users Users @relation(fields: [userId], references: [id], onDelete: Cascade, map: "FK_6c2e267ae764a9413b863a29342") + + @@map(name: "api_keys") +} + +model AssetFaces { + assetId String @db.Uuid + personId String? @db.Uuid + embedding Unsupported("vector") + imageWidth Int @default(0) + imageHeight Int @default(0) + boundingBoxX1 Int @default(0) + boundingBoxY1 Int @default(0) + boundingBoxX2 Int @default(0) + boundingBoxY2 Int @default(0) + id String @id(map: "PK_6df76ab2eb6f5b57b7c2f1fc684") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + assets Assets @relation(fields: [assetId], references: [id], onDelete: Cascade, map: "FK_02a43fd0b3c50fb6d7f0cb7282c") + person Person? @relation("asset_faces_personIdToperson", fields: [personId], references: [id], map: "FK_95ad7106dd7b484275443f580f9") + person_person_faceAssetIdToasset_faces Person[] @relation("person_faceAssetIdToasset_faces") + + @@index([assetId, personId], map: "IDX_asset_faces_assetId_personId") + @@index([assetId], map: "IDX_asset_faces_on_assetId") + @@index([personId], map: "IDX_asset_faces_personId") + @@index([personId, assetId], map: "IDX_bf339a24070dac7e71304ec530") + @@index([embedding], map: "face_index") + @@map(name: "asset_faces") +} + +model AssetJobStatus { + assetId String @id(map: "PK_420bec36fc02813bddf5c8b73d4") @db.Uuid + facesRecognizedAt DateTime? @db.Timestamptz(6) + metadataExtractedAt DateTime? @db.Timestamptz(6) + duplicatesDetectedAt DateTime? @db.Timestamptz(6) + assets Assets @relation(fields: [assetId], references: [id], onDelete: Cascade, map: "FK_420bec36fc02813bddf5c8b73d4") + + @@map(name: "asset_job_status") +} + +model AssetStack { + id String @id(map: "PK_74a27e7fcbd5852463d0af3034b") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + primaryAssetId String @unique(map: "REL_91704e101438fd0653f582426d") @db.Uuid + primaryAsset Assets @relation("asset_stack_primaryAssetIdToassets", fields: [primaryAssetId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "FK_91704e101438fd0653f582426dc") + assets Assets[] @relation("assets_stackIdToasset_stack") + + @@map(name: "asset_stack") +} + +/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. +model Assets { + id String @id(map: "PK_da96729a8b113377cfb6a62439c") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + deviceAssetId String @db.VarChar + ownerId String @db.Uuid + deviceId String @db.VarChar + type String @db.VarChar + originalPath String @db.VarChar + previewPath String? @db.VarChar + fileCreatedAt DateTime @db.Timestamptz(6) + fileModifiedAt DateTime @db.Timestamptz(6) + isFavorite Boolean @default(false) + duration String? @db.VarChar + thumbnailPath String? @default("") @db.VarChar + encodedVideoPath String? @default("") @db.VarChar + checksum Bytes + isVisible Boolean @default(true) + livePhotoVideoId String? @unique(map: "UQ_16294b83fa8c0149719a1f631ef") @db.Uuid + updatedAt DateTime @default(now()) @db.Timestamptz(6) + createdAt DateTime @default(now()) @db.Timestamptz(6) + isArchived Boolean @default(false) + originalFileName String @db.VarChar + sidecarPath String? @db.VarChar + thumbhash Bytes? + isOffline Boolean @default(false) + libraryId String @db.Uuid + deletedAt DateTime? @db.Timestamptz(6) + localDateTime DateTime @db.Timestamptz(6) + stackId String? @db.Uuid + duplicateId String? @db.Uuid + activity Activity[] + albums Albums[] + albumsAssetsAssets AlbumsAssetsAssets[] + faces AssetFaces[] + assetJobStatus AssetJobStatus? + assetStackAssetStackPrimaryAssetIdToAssets AssetStack? @relation("asset_stack_primaryAssetIdToassets") + livePhotoVideo Assets? @relation("assetsToassets", fields: [livePhotoVideoId], references: [id], map: "FK_16294b83fa8c0149719a1f631ef") + otherAssets Assets? @relation("assetsToassets") + owner Users @relation(fields: [ownerId], references: [id], onDelete: Cascade, map: "FK_2c5ac0d6fb58b238fd2068de67d") + library Libraries @relation(fields: [libraryId], references: [id], onDelete: Cascade, map: "FK_9977c3c1de01c3d848039a6b90c") + stack AssetStack? @relation("assets_stackIdToasset_stack", fields: [stackId], references: [id], map: "FK_f15d48fa3ea5e4bda05ca8ab207") + exifInfo Exif? + sharedLinks SharedLinkAsset[] + smartInfo SmartInfo? + smartSearch SmartSearch? + tags TagAsset[] + + @@unique([ownerId, libraryId, checksum], map: "UQ_assets_owner_library_checksum") + @@index([originalFileName], map: "IDX_4d66e76dada1ca180f67a205dc") + @@index([checksum], map: "IDX_8d3efe36c0755849395e6ea866") + @@index([id, stackId], map: "IDX_asset_id_stackId") + @@index([originalPath, libraryId], map: "IDX_originalPath_libraryId") + @@index([fileCreatedAt], map: "idx_asset_file_created_at") + @@map(name: "assets") +} + +model Audit { + id Int @id(map: "PK_1d3d120ddaf7bc9b1ed68ed463a") @default(autoincrement()) + entityType String @db.VarChar + entityId String @db.Uuid + action String @db.VarChar + ownerId String @db.Uuid + createdAt DateTime @default(now()) @db.Timestamptz(6) + + @@index([ownerId, createdAt], map: "IDX_ownerId_createdAt") + @@map(name: "audit") +} + +model Exif { + assetId String @id(map: "PK_c0117fdbc50b917ef9067740c44") @db.Uuid + make String? @db.VarChar + model String? @db.VarChar + exifImageWidth Int? + exifImageHeight Int? + fileSizeInByte BigInt? + orientation String? @db.VarChar + dateTimeOriginal DateTime? @db.Timestamptz(6) + modifyDate DateTime? @db.Timestamptz(6) + lensModel String? @db.VarChar + fNumber Float? + focalLength Float? + iso Int? + latitude Float? + longitude Float? + city String? @db.VarChar + state String? @db.VarChar + country String? @db.VarChar + description String @default("") + fps Float? + exposureTime String? @db.VarChar + livePhotoCID String? @db.VarChar + timeZone String? @db.VarChar + projectionType String? @db.VarChar + profileDescription String? @db.VarChar + colorspace String? @db.VarChar + bitsPerSample Int? + autoStackId String? @db.VarChar + assets Assets @relation(fields: [assetId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "FK_c0117fdbc50b917ef9067740c44") + + @@index([autoStackId], map: "IDX_auto_stack_id") + @@index([livePhotoCID], map: "IDX_live_photo_cid") + @@index([city], map: "exif_city") + @@map(name: "exif") +} + +/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. +model GeodataPlaces { + id Int @id(map: "PK_c29918988912ef4036f3d7fbff4") + name String @db.VarChar(200) + longitude Float + latitude Float + countryCode String @db.Char(2) + admin1Code String? @db.VarChar(20) + admin2Code String? @db.VarChar(80) + modificationDate DateTime @db.Date + earthCoord Unsupported("cube")? @default(dbgenerated("ll_to_earth(latitude, longitude)")) + admin1Name String? @db.VarChar + admin2Name String? @db.VarChar + alternateNames String? @db.VarChar + + @@index([earthCoord], map: "IDX_geodata_gist_earthcoord", type: Gist) + @@map(name: "geodata_places") +} + +model Libraries { + id String @id(map: "PK_505fedfcad00a09b3734b4223de") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + name String @db.VarChar + ownerId String @db.Uuid + type String @db.VarChar + importPaths String[] + exclusionPatterns String[] + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @default(now()) @db.Timestamptz(6) + deletedAt DateTime? @db.Timestamptz(6) + refreshedAt DateTime? @db.Timestamptz(6) + isVisible Boolean @default(true) + assets Assets[] + owner Users @relation(fields: [ownerId], references: [id], onDelete: Cascade, map: "FK_0f6fc2fb195f24d19b0fb0d57c1") + + @@map(name: "libraries") +} + +model MoveHistory { + id String @id(map: "PK_af608f132233acf123f2949678d") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + entityId String @db.VarChar + pathType String @db.VarChar + oldPath String @db.VarChar + newPath String @unique(map: "UQ_newPath") @db.VarChar + + @@unique([entityId, pathType], map: "UQ_entityId_pathType") + @@map(name: "move_history") +} + +model Partners { + sharedById String @db.Uuid + sharedWithId String @db.Uuid + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @default(now()) @db.Timestamptz(6) + inTimeline Boolean @default(false) + sharedBy Users @relation("partners_sharedByIdTousers", fields: [sharedById], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "FK_7e077a8b70b3530138610ff5e04") + sharedWith Users @relation("partners_sharedWithIdTousers", fields: [sharedWithId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "FK_d7e875c6c60e661723dbf372fd3") + + @@id([sharedById, sharedWithId], map: "PK_f1cc8f73d16b367f426261a8736") + @@map(name: "partners") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +model Person { + id String @id(map: "PK_5fdaf670315c4b7e70cce85daa3") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @default(now()) @db.Timestamptz(6) + ownerId String @db.Uuid + name String @default("") @db.VarChar + thumbnailPath String @default("") @db.VarChar + isHidden Boolean @default(false) + birthDate DateTime? @db.Date + faceAssetId String? @db.Uuid + asset_faces_asset_faces_personIdToperson AssetFaces[] @relation("asset_faces_personIdToperson") + asset_faces_person_faceAssetIdToasset_faces AssetFaces? @relation("person_faceAssetIdToasset_faces", fields: [faceAssetId], references: [id], onUpdate: NoAction, map: "FK_2bbabe31656b6778c6b87b61023") + users Users @relation(fields: [ownerId], references: [id], onDelete: Cascade, map: "FK_5527cc99f530a547093f9e577b6") + + @@map(name: "person") +} + +model SharedLinkAsset { + assetsId String @db.Uuid + sharedLinksId String @db.Uuid + assets Assets @relation(fields: [assetsId], references: [id], onDelete: Cascade, map: "FK_5b7decce6c8d3db9593d6111a66") + sharedLinks SharedLinks @relation(fields: [sharedLinksId], references: [id], onDelete: Cascade, map: "FK_c9fab4aa97ffd1b034f3d6581ab") + + @@id([assetsId, sharedLinksId], map: "PK_9b4f3687f9b31d1e311336b05e3") + @@index([assetsId], map: "IDX_5b7decce6c8d3db9593d6111a6") + @@index([sharedLinksId], map: "IDX_c9fab4aa97ffd1b034f3d6581a") + @@map(name: "shared_link__asset") +} + +model SharedLinks { + id String @id(map: "PK_642e2b0f619e4876e5f90a43465") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + description String? @db.VarChar + userId String @db.Uuid + key Bytes @unique(map: "UQ_sharedlink_key") + type String @db.VarChar + createdAt DateTime @default(now()) @db.Timestamptz(6) + expiresAt DateTime? @db.Timestamptz(6) + allowUpload Boolean @default(false) + albumId String? @db.Uuid + allowDownload Boolean @default(true) + showExif Boolean @default(true) + password String? @db.VarChar + assets SharedLinkAsset[] + albums Albums? @relation(fields: [albumId], references: [id], onDelete: Cascade, map: "FK_0c6ce9058c29f07cdf7014eac66") + users Users @relation(fields: [userId], references: [id], onDelete: Cascade, map: "FK_66fe3837414c5a9f1c33ca49340") + + @@index([albumId], map: "IDX_sharedlink_albumId") + @@index([key], map: "IDX_sharedlink_key") + @@map(name: "shared_links") +} + +model SmartInfo { + assetId String @id(map: "PK_5e3753aadd956110bf3ec0244ac") @db.Uuid + tags String[] + objects String[] + smartInfoTextSearchableColumn Unsupported("tsvector") @default(dbgenerated("to_tsvector('english'::regconfig, f_concat_ws(' '::text, (COALESCE(tags, ARRAY[]::text[]) || COALESCE(objects, ARRAY[]::text[]))))")) + assets Assets @relation(fields: [assetId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "FK_5e3753aadd956110bf3ec0244ac") + + @@index([tags], map: "si_tags", type: Gin) + @@index([smartInfoTextSearchableColumn], map: "smart_info_text_searchable_idx", type: Gin) + @@map(name: "smart_info") +} + +model SmartSearch { + assetId String @id @db.Uuid + embedding Unsupported("vector") + assets Assets @relation(fields: [assetId], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([embedding], map: "clip_index") + @@map(name: "smart_search") +} + +model SocketIoAttachments { + id BigInt @unique @default(autoincrement()) + created_at DateTime? @default(now()) @db.Timestamptz(6) + payload Bytes? + + @@map(name: "socket_io_attachments") +} + +model SystemConfig { + key String @id(map: "PK_aab69295b445016f56731f4d535") @db.VarChar + value String? @db.VarChar + + @@map(name: "system_config") +} + +model SystemMetadata { + key String @id(map: "PK_fa94f6857470fb5b81ec6084465") @db.VarChar + value Json @default("{}") + + @@map(name: "system_metadata") +} + +model TagAsset { + assetsId String @db.Uuid + tagsId String @db.Uuid + tags Tags @relation(fields: [tagsId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "FK_e99f31ea4cdf3a2c35c7287eb42") + assets Assets @relation(fields: [assetsId], references: [id], onDelete: Cascade, map: "FK_f8e8a9e893cb5c54907f1b798e9") + + @@id([assetsId, tagsId], map: "PK_ef5346fe522b5fb3bc96454747e") + @@index([tagsId], map: "IDX_e99f31ea4cdf3a2c35c7287eb4") + @@index([assetsId], map: "IDX_f8e8a9e893cb5c54907f1b798e") + @@index([assetsId, tagsId], map: "IDX_tag_asset_assetsId_tagsId") + @@map(name: "tag_asset") +} + +model Tags { + id String @id(map: "PK_e7dc17249a1148a1970748eda99") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + type String @db.VarChar + name String @db.VarChar + userId String @db.Uuid + renameTagId String? @db.Uuid + tags TagAsset[] + users Users @relation(fields: [userId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "FK_92e67dc508c705dd66c94615576") + + @@unique([name, userId], map: "UQ_tag_name_userId") + @@map(name: "tags") +} + +model UserToken { + id String @id(map: "PK_48cb6b5c20faa63157b3c1baf7f") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + token String @db.VarChar + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @default(now()) @db.Timestamptz(6) + userId String @db.Uuid + deviceType String @default("") @db.VarChar + deviceOS String @default("") @db.VarChar + users Users @relation(fields: [userId], references: [id], onDelete: Cascade, map: "FK_d37db50eecdf9b8ce4eedd2f918") + + @@map(name: "user_token") +} + +model Users { + id String @id(map: "PK_a3ffb1c0c8416b9fc6f907b7433") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + email String @unique(map: "UQ_97672ac88f789774dd47f7c8be3") @db.VarChar + password String @default("") @db.VarChar + createdAt DateTime @default(now()) @db.Timestamptz(6) + profileImagePath String @default("") @db.VarChar + isAdmin Boolean @default(false) + shouldChangePassword Boolean @default(true) + deletedAt DateTime? @db.Timestamptz(6) + oauthId String @default("") @db.VarChar + updatedAt DateTime @default(now()) @db.Timestamptz(6) + storageLabel String? @unique(map: "UQ_b309cf34fa58137c416b32cea3a") @db.VarChar + memoriesEnabled Boolean @default(true) + name String @default("") @db.VarChar + avatarColor String? @db.VarChar + quotaSizeInBytes BigInt? + quotaUsageInBytes BigInt @default(0) + status String @default("active") @db.VarChar + activity Activity[] + albums Albums[] + albumsSharedUsersUsers AlbumsSharedUsersUsers[] + apiKeys ApiKeys[] + assets Assets[] + libraries Libraries[] + sharedBy Partners[] @relation("partners_sharedByIdTousers") + sharedWith Partners[] @relation("partners_sharedWithIdTousers") + person Person[] + sharedLinks SharedLinks[] + tags Tags[] + userToken UserToken[] + + @@map(name: "users") +} diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index b4869b9fbb1b1..41dfb1359a136 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -1,17 +1,17 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { Prisma } from '@prisma/client'; +import { sql } from 'kysely'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; -import { AlbumEntity, AssetOrder } from 'src/entities/album.entity'; +import { AssetOrder } from 'src/entities/album.entity'; import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { LibraryType } from 'src/entities/library.entity'; -import { PartnerEntity } from 'src/entities/partner.entity'; -import { SmartInfoEntity } from 'src/entities/smart-info.entity'; import { - AssetBuilderOptions, AssetCreate, AssetDeltaSyncOptions, + AssetDuplicateGroup, AssetExploreFieldOptions, AssetFullSyncOptions, AssetPathEntity, @@ -32,130 +32,115 @@ import { WithoutProperty, } from 'src/interfaces/asset.interface'; import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.interface'; -import { OptionalBetween, searchAssetBuilder } from 'src/utils/database'; +import { PrismaRepository } from 'src/repositories/prisma.repository'; +import { searchAssetBuilder, withExif, withTimeBucket } from 'src/utils/database'; import { Instrumentation } from 'src/utils/instrumentation'; -import { Paginated, PaginationMode, PaginationOptions, paginate, paginatedBuilder } from 'src/utils/pagination'; -import { - Brackets, - FindOptionsOrder, - FindOptionsRelations, - FindOptionsSelect, - FindOptionsWhere, - In, - IsNull, - MoreThan, - Not, - Repository, -} from 'typeorm'; - -const truncateMap: Record = { - [TimeBucketSize.DAY]: 'day', - [TimeBucketSize.MONTH]: 'month', -}; - -const dateTrunc = (options: TimeBucketOptions) => - `(date_trunc('${ - truncateMap[options.size] - }', (asset."localDateTime" at time zone 'UTC')) at time zone 'UTC')::timestamptz`; +import { Paginated, PaginationMode, PaginationOptions, paginatedBuilder, paginationHelper } from 'src/utils/pagination'; +import { Repository } from 'typeorm'; @Instrumentation() @Injectable() export class AssetRepository implements IAssetRepository { constructor( @InjectRepository(AssetEntity) private repository: Repository, - @InjectRepository(ExifEntity) private exifRepository: Repository, - @InjectRepository(AssetJobStatusEntity) private jobStatusRepository: Repository, - @InjectRepository(SmartInfoEntity) private smartInfoRepository: Repository, - @InjectRepository(PartnerEntity) private partnerRepository: Repository, - @InjectRepository(AlbumEntity) private albumRepository: Repository, + private prismaRepository: PrismaRepository, ) {} - async upsertExif(exif: Partial): Promise { - await this.exifRepository.upsert(exif, { conflictPaths: ['assetId'] }); + async upsertExif(exif: Partial & { assetId: string }): Promise { + await this.prismaRepository.exif.upsert({ update: exif, create: exif, where: { assetId: exif.assetId } }); } - async upsertJobStatus(...jobStatus: Partial[]): Promise { - await this.jobStatusRepository.upsert(jobStatus, { conflictPaths: ['assetId'] }); + async upsertJobStatus(...jobStatus: Partial[] & { assetId: string }): Promise { + await this.prismaRepository.assetJobStatus.upsert({ + update: jobStatus, + create: jobStatus, + where: { assetId: jobStatus.assetId }, + }); } create(asset: AssetCreate): Promise { - return this.repository.save(asset); + const { ownerId, libraryId, livePhotoVideoId, stackId, ...assetData } = asset; + return this.prismaRepository.assets.create({ + data: { + ...assetData, + livePhotoVideo: livePhotoVideoId ? { connect: { id: livePhotoVideoId } } : undefined, + stack: stackId ? { connect: { id: stackId } } : undefined, + library: { connect: { id: libraryId } }, + owner: { connect: { id: ownerId } }, + }, + }) as any as Promise; } - @GenerateSql({ params: [[DummyValue.UUID], { day: 1, month: 1 }] }) + @GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] }) getByDayOfYear(ownerIds: string[], { day, month }: MonthDay): Promise { - return this.repository - .createQueryBuilder('entity') - .where( - `entity.ownerId IN (:...ownerIds) - AND entity.isVisible = true - AND entity.isArchived = false - AND entity.previewPath IS NOT NULL - AND EXTRACT(DAY FROM entity.localDateTime AT TIME ZONE 'UTC') = :day - AND EXTRACT(MONTH FROM entity.localDateTime AT TIME ZONE 'UTC') = :month`, - { - ownerIds, - day, - month, - }, - ) - .leftJoinAndSelect('entity.exifInfo', 'exifInfo') - .orderBy('entity.localDateTime', 'ASC') - .getMany(); + return this.prismaRepository.$kysely + .selectFrom('assets') + .selectAll() + .select((qb) => withExif(qb)) + .where('ownerId', '=', sql`any(array[${ownerIds}]::uuid[])`) + .where('isVisible', '=', true) + .where('isArchived', '=', false) + .where('previewPath', 'is not', null) + .where('deletedAt', 'is', null) + .where(sql`extract(day from "localDateTime" at time zone 'utc')`, '=', day) + .where(sql`extract(month from "localDateTime" at time zone 'utc')`, '=', month) + .orderBy('localDateTime', 'desc') + .execute() as any as Promise; } @GenerateSql({ params: [[DummyValue.UUID]] }) @ChunkedArray() - getByIds( - ids: string[], - relations?: FindOptionsRelations, - select?: FindOptionsSelect, - ): Promise { - return this.repository.find({ - where: { id: In(ids) }, - relations, - select, - withDeleted: true, - }); + getByIds(ids: string[], relations?: Prisma.AssetsInclude): Promise { + return this.prismaRepository.assets.findMany({ + where: { id: { in: ids } }, + include: { + ...relations, + library: relations?.library ? { include: { assets: true, owner: true } } : undefined, + }, + }) as any as Promise; // typeorm type assumes arbitrary level of recursion } @GenerateSql({ params: [[DummyValue.UUID]] }) @ChunkedArray() getByIdsWithAllRelations(ids: string[]): Promise { - return this.repository.find({ - where: { id: In(ids) }, - relations: { + return this.prismaRepository.assets.findMany({ + where: { id: { in: ids } }, + include: { exifInfo: true, smartInfo: true, tags: true, faces: { - person: true, - }, - stack: { - assets: true, + include: { + person: true, + }, }, + stack: { include: { assets: true } }, }, - withDeleted: true, - }); + }) as any as Promise; } @GenerateSql({ params: [DummyValue.UUID] }) async deleteAll(ownerId: string): Promise { - await this.repository.delete({ ownerId }); + await this.prismaRepository.assets.deleteMany({ where: { ownerId } }); } - getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated { - return paginate(this.repository, pagination, { + async getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated { + const items = await this.prismaRepository.assets.findMany({ where: { albums: { - id: albumId, + some: { + id: albumId, + }, }, }, - relations: { - albums: true, - exifInfo: true, + orderBy: { + fileCreatedAt: 'desc', }, + skip: pagination.skip, + take: pagination.take + 1, }); + + return paginationHelper(items as any as AssetEntity[], pagination.take); } getByUserId( @@ -166,48 +151,32 @@ export class AssetRepository implements IAssetRepository { return this.getAll(pagination, { ...options, userIds: [userId] }); } - @GenerateSql({ params: [{ take: 1, skip: 0 }, DummyValue.UUID] }) - getExternalLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated { - return paginate(this.repository, pagination, { + @GenerateSql({ params: [[DummyValue.UUID]] }) + async getExternalLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated { + const items = await this.prismaRepository.assets.findMany({ + where: { libraryId }, select: { id: true, originalPath: true, isOffline: true }, - where: { library: { id: libraryId }, isExternal: true }, + orderBy: { fileCreatedAt: 'desc' }, + skip: pagination.skip, + take: pagination.take + 1, }); - } - @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) - getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise { - return this.repository.findOne({ - where: { library: { id: libraryId }, originalPath: originalPath }, - }); + return paginationHelper(items as any as AssetPathEntity[], pagination.take); } - @GenerateSql({ params: [DummyValue.UUID, [DummyValue.STRING]] }) - @ChunkedArray({ paramIndex: 1 }) - async getPathsNotInLibrary(libraryId: string, originalPaths: string[]): Promise { - const result = await this.repository.query( - ` - WITH paths AS (SELECT unnest($2::text[]) AS path) - SELECT path FROM paths - WHERE NOT EXISTS (SELECT 1 FROM assets WHERE "libraryId" = $1 AND "originalPath" = path); - `, - [libraryId, originalPaths], - ); - return result.map((row: { path: string }) => row.path); - } - - @GenerateSql({ params: [DummyValue.UUID, [DummyValue.STRING]] }) - @ChunkedArray({ paramIndex: 1 }) - async updateOfflineLibraryAssets(libraryId: string, originalPaths: string[]): Promise { - await this.repository.update( - { library: { id: libraryId }, originalPath: Not(In(originalPaths)), isOffline: false }, - { isOffline: true }, - ); + @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) + async getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise { + const res = await this.prismaRepository.assets.findFirst({ where: { libraryId, originalPath } }); + return res as AssetEntity | null; } - getAll(pagination: PaginationOptions, options: AssetSearchOptions = {}): Paginated { + getAll( + pagination: PaginationOptions, + { orderDirection, ...options }: AssetSearchOptions = {}, + ): Paginated { let builder = this.repository.createQueryBuilder('asset'); builder = searchAssetBuilder(builder, options); - builder.orderBy('asset.createdAt', options.orderDirection ?? 'ASC'); + builder.orderBy('asset.createdAt', orderDirection ?? 'ASC'); return paginatedBuilder(builder, { mode: PaginationMode.SKIP_TAKE, skip: pagination.skip, @@ -224,76 +193,85 @@ export class AssetRepository implements IAssetRepository { */ @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) async getAllByDeviceId(ownerId: string, deviceId: string): Promise { - const items = await this.repository.find({ - select: { deviceAssetId: true }, + const items = await this.prismaRepository.assets.findMany({ where: { ownerId, deviceId, isVisible: true, }, - withDeleted: true, + select: { + deviceAssetId: true, + }, }); return items.map((asset) => asset.deviceAssetId); } @GenerateSql({ params: [DummyValue.UUID] }) - getById( - id: string, - relations: FindOptionsRelations, - order?: FindOptionsOrder, - ): Promise { - return this.repository.findOne({ - where: { id }, - relations, - // We are specifically asking for this asset. Return it even if it is soft deleted - withDeleted: true, - order, - }); + async getById(id: string, relations: Prisma.AssetsInclude): Promise { + return this.prismaRepository.assets.findFirst({ where: { id }, include: relations }) as Promise; } @GenerateSql({ params: [[DummyValue.UUID], { deviceId: DummyValue.STRING }] }) @Chunked() async updateAll(ids: string[], options: AssetUpdateAllOptions): Promise { - await this.repository.update({ id: In(ids) }, options); + await this.prismaRepository.assets.updateMany({ where: { id: { in: ids } }, data: options }); } @GenerateSql({ params: [{ targetDuplicateId: DummyValue.UUID, duplicateIds: [DummyValue.UUID], assetIds: [DummyValue.UUID] }], }) async updateDuplicates(options: AssetUpdateDuplicateOptions): Promise { - await this.repository - .createQueryBuilder() - .update() - .set({ duplicateId: options.targetDuplicateId }) - .where({ - duplicateId: In(options.duplicateIds), - }) - .orWhere({ id: In(options.assetIds) }) - .execute(); + await this.prismaRepository.assets.updateMany({ + where: { OR: [{ id: { in: options.assetIds } }, { duplicateId: { in: options.duplicateIds } }] }, + data: { duplicateId: options.targetDuplicateId }, + }); } @Chunked() async softDeleteAll(ids: string[]): Promise { - await this.repository.softDelete({ id: In(ids) }); + await this.prismaRepository.assets.updateMany({ where: { id: { in: ids } }, data: { deletedAt: new Date() } }); } @Chunked() async restoreAll(ids: string[]): Promise { - await this.repository.restore({ id: In(ids) }); + await this.prismaRepository.assets.updateMany({ where: { id: { in: ids } }, data: { deletedAt: null } }); } - async update(asset: AssetUpdateOptions): Promise { - await this.repository.update(asset.id, asset); + update(asset: AssetUpdateOptions): Promise { + const { ownerId, libraryId, livePhotoVideoId, stackId, ...assetData } = asset; + + return this.prismaRepository.assets.update({ + data: { + ...assetData, + livePhotoVideo: livePhotoVideoId ? { connect: { id: livePhotoVideoId } } : undefined, + stack: stackId ? { connect: { id: stackId } } : undefined, + library: { connect: { id: libraryId } }, + owner: { connect: { id: ownerId } }, + }, + where: { id: asset.id }, + include: { + exifInfo: true, + smartInfo: true, + tags: true, + faces: { + include: { + person: true, + }, + }, + }, + }) as any as Promise; // typeorm type assumes all relations are included } async remove(asset: AssetEntity): Promise { - await this.repository.remove(asset); + await this.prismaRepository.assets.delete({ where: { id: asset.id } }); } @GenerateSql({ params: [DummyValue.UUID, DummyValue.BUFFER] }) getByChecksum(libraryId: string, checksum: Buffer): Promise { - return this.repository.findOne({ where: { libraryId, checksum } }); + return this.prismaRepository.assets.findFirst({ + where: { libraryId, checksum: checksum }, + }) as Promise; } @GenerateSql({ params: [DummyValue.UUID, DummyValue.BUFFER] }) @@ -316,19 +294,19 @@ export class AssetRepository implements IAssetRepository { findLivePhotoMatch(options: LivePhotoSearchOptions): Promise { const { ownerId, otherAssetId, livePhotoCID, type } = options; - return this.repository.findOne({ + return this.prismaRepository.assets.findFirst({ where: { - id: Not(otherAssetId), + id: { not: otherAssetId }, ownerId, type, exifInfo: { livePhotoCID, }, }, - relations: { + include: { exifInfo: true, }, - }); + }) as Promise; } @GenerateSql( @@ -339,79 +317,64 @@ export class AssetRepository implements IAssetRepository { params: [DummyValue.PAGINATION, property], })), ) - getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated { - let relations: FindOptionsRelations = {}; - let where: FindOptionsWhere | FindOptionsWhere[] = {}; + async getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated { + let relations: Prisma.AssetsInclude = {}; + let where: Prisma.AssetsWhereInput = {}; switch (property) { case WithoutProperty.THUMBNAIL: { - where = [ - { previewPath: IsNull(), isVisible: true }, - { previewPath: '', isVisible: true }, - { thumbnailPath: IsNull(), isVisible: true }, - { thumbnailPath: '', isVisible: true }, - { thumbhash: IsNull(), isVisible: true }, - ]; + where = { + OR: [ + { previewPath: null, isVisible: true }, + { previewPath: '', isVisible: true }, + { thumbnailPath: null, isVisible: true }, + { thumbnailPath: '', isVisible: true }, + { thumbhash: null, isVisible: true }, + ], + }; break; } case WithoutProperty.ENCODED_VIDEO: { - where = [ - { type: AssetType.VIDEO, encodedVideoPath: IsNull() }, - { type: AssetType.VIDEO, encodedVideoPath: '' }, - ]; + where = { + OR: [ + { type: AssetType.VIDEO, encodedVideoPath: null }, + { type: AssetType.VIDEO, encodedVideoPath: '' }, + ], + }; break; } case WithoutProperty.EXIF: { relations = { exifInfo: true, - jobStatus: true, + assetJobStatus: true, }; where = { isVisible: true, - jobStatus: { - metadataExtractedAt: IsNull(), + assetJobStatus: { + metadataExtractedAt: null, }, }; break; } case WithoutProperty.SMART_SEARCH: { - relations = { - smartSearch: true, - }; where = { isVisible: true, - previewPath: Not(IsNull()), - smartSearch: { - embedding: IsNull(), - }, + previewPath: { not: null }, + smartSearch: null, }; break; } case WithoutProperty.DUPLICATE: { where = { - previewPath: Not(IsNull()), - isVisible: true, - smartSearch: true, - jobStatus: { - duplicatesDetectedAt: IsNull(), - }, - }; - break; - } - - case WithoutProperty.OBJECT_TAGS: { - relations = { - smartInfo: true, - }; - where = { - previewPath: Not(IsNull()), isVisible: true, - smartInfo: { - tags: IsNull(), + previewPath: { not: null }, + smartSearch: { isNot: null }, + assetJobStatus: { + duplicatesDetectedAt: null, }, }; break; @@ -420,17 +383,18 @@ export class AssetRepository implements IAssetRepository { case WithoutProperty.FACES: { relations = { faces: true, - jobStatus: true, + assetJobStatus: true, }; where = { - previewPath: Not(IsNull()), + previewPath: { not: null }, isVisible: true, faces: { - assetId: IsNull(), - personId: IsNull(), + some: { + person: null, + }, }, - jobStatus: { - facesRecognizedAt: IsNull(), + assetJobStatus: { + facesRecognizedAt: null, }, }; break; @@ -441,21 +405,24 @@ export class AssetRepository implements IAssetRepository { faces: true, }; where = { - previewPath: Not(IsNull()), + previewPath: { not: null }, isVisible: true, faces: { - assetId: Not(IsNull()), - personId: IsNull(), + some: { + person: null, + }, }, }; break; } case WithoutProperty.SIDECAR: { - where = [ - { sidecarPath: IsNull(), isVisible: true }, - { sidecarPath: '', isVisible: true }, - ]; + where = { + OR: [ + { sidecarPath: null, isVisible: true }, + { sidecarPath: '', isVisible: true }, + ], + }; break; } @@ -464,29 +431,33 @@ export class AssetRepository implements IAssetRepository { } } - return paginate(this.repository, pagination, { - relations, + const items = await this.prismaRepository.assets.findMany({ where, - order: { + orderBy: { // Ensures correct order when paginating - createdAt: 'ASC', + createdAt: 'asc', }, + skip: pagination.skip, + take: pagination.take + 1, + include: relations, }); + + return paginationHelper(items as any as AssetEntity[], pagination.take); } - getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated { - let where: FindOptionsWhere | FindOptionsWhere[] = {}; + async getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated { + let where: Prisma.AssetsWhereInput = {}; switch (property) { case WithProperty.SIDECAR: { - where = [{ sidecarPath: Not(IsNull()), isVisible: true }]; + where = { sidecarPath: { not: null }, isVisible: true }; break; } case WithProperty.IS_OFFLINE: { if (!libraryId) { throw new Error('Library id is required when finding offline assets'); } - where = [{ isOffline: true, libraryId: libraryId }]; + where = { isOffline: true, libraryId: libraryId }; break; } @@ -495,27 +466,47 @@ export class AssetRepository implements IAssetRepository { } } - return paginate(this.repository, pagination, { + const items = await this.prismaRepository.assets.findMany({ where, - order: { + orderBy: { // Ensures correct order when paginating - createdAt: 'ASC', + createdAt: 'asc', }, + skip: pagination.skip, + take: pagination.take + 1, }); + + return paginationHelper(items as any as AssetEntity[], pagination.take); } getFirstAssetForAlbumId(albumId: string): Promise { - return this.repository.findOne({ - where: { albums: { id: albumId } }, - order: { fileCreatedAt: 'DESC' }, - }); + return this.prismaRepository.assets.findFirst({ + where: { + albums: { + some: { + id: albumId, + }, + }, + }, + orderBy: { + fileCreatedAt: 'desc', + }, + }) as Promise; } getLastUpdatedAssetForAlbumId(albumId: string): Promise { - return this.repository.findOne({ - where: { albums: { id: albumId } }, - order: { updatedAt: 'DESC' }, - }); + return this.prismaRepository.assets.findFirst({ + where: { + albums: { + some: { + id: albumId, + }, + }, + }, + orderBy: { + updatedAt: 'desc', + }, + }) as Promise; } async getMapMarkers( @@ -525,37 +516,32 @@ export class AssetRepository implements IAssetRepository { ): Promise { const { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore } = options; - const where = { - isVisible: true, - isArchived, - exifInfo: { - latitude: Not(IsNull()), - longitude: Not(IsNull()), - }, - isFavorite, - fileCreatedAt: OptionalBetween(fileCreatedAfter, fileCreatedBefore), - }; - - const assets = await this.repository.find({ + const assets = await this.prismaRepository.assets.findMany({ select: { id: true, exifInfo: { - city: true, - state: true, - country: true, - latitude: true, - longitude: true, + select: { + city: true, + state: true, + country: true, + latitude: true, + longitude: true, + }, }, }, - where: [ - { ...where, ownerId: In([...ownerIds]) }, - { ...where, albums: { id: In([...albumIds]) } }, - ], - relations: { - exifInfo: true, + where: { + OR: [{ ownerId: { in: ownerIds } }, { albums: { some: { id: { in: albumIds } } } }], + isVisible: true, + isArchived, + exifInfo: { + latitude: { not: null }, + longitude: { not: null }, + }, + isFavorite, + fileCreatedAt: { gte: fileCreatedAfter, lte: fileCreatedBefore }, }, - order: { - fileCreatedAt: 'DESC', + orderBy: { + fileCreatedAt: 'desc', }, }); @@ -569,29 +555,20 @@ export class AssetRepository implements IAssetRepository { })); } - async getStatistics(ownerId: string, options: AssetStatsOptions): Promise { - const builder = this.repository - .createQueryBuilder('asset') - .select(`COUNT(asset.id)`, 'count') - .addSelect(`asset.type`, 'type') - .where('"ownerId" = :ownerId', { ownerId }) - .andWhere('asset.isVisible = true') - .groupBy('asset.type'); - - const { isArchived, isFavorite, isTrashed } = options; - if (isArchived !== undefined) { - builder.andWhere(`asset.isArchived = :isArchived`, { isArchived }); - } - - if (isFavorite !== undefined) { - builder.andWhere(`asset.isFavorite = :isFavorite`, { isFavorite }); - } - - if (isTrashed !== undefined) { - builder.withDeleted().andWhere(`asset.deletedAt is not null`); - } - - const items = await builder.getRawMany(); + async getStatistics(ownerId: string, { isArchived, isFavorite, isTrashed }: AssetStatsOptions): Promise { + const items = await this.prismaRepository.assets.groupBy({ + by: 'type', + where: { + ownerId, + isVisible: true, + isArchived, + isFavorite, + deletedAt: isTrashed ? { not: null } : null, + }, + _count: { + id: true, + }, + }); const result: AssetStats = { [AssetType.AUDIO]: 0, @@ -601,52 +578,98 @@ export class AssetRepository implements IAssetRepository { }; for (const item of items) { - result[item.type as AssetType] = Number(item.count) || 0; + result[item.type as AssetType] = item._count.id; } return result; } - @GenerateSql({ params: [DummyValue.UUID, DummyValue.NUMBER] }) - getRandom(ownerId: string, count: number): Promise { - const builder = this.getBuilder({ - userIds: [ownerId], - exifInfo: true, - }); - - return builder.orderBy('RANDOM()').limit(count).getMany(); + getRandom(ownerId: string, take: number): Promise { + return this.prismaRepository.$kysely + .selectFrom('assets') + .selectAll() + .select((eb) => withExif(eb)) + .where('ownerId', '=', ownerId) + .where('isVisible', '=', true) + .where('deletedAt', 'is', null) + .orderBy((eb) => eb.fn('random')) + .limit(take) + .execute() as any as Promise; } @GenerateSql({ params: [{ size: TimeBucketSize.MONTH }] }) - getTimeBuckets(options: TimeBucketOptions): Promise { - const truncated = dateTrunc(options); - return this.getBuilder(options) - .select(`COUNT(asset.id)::int`, 'count') - .addSelect(truncated, 'timeBucket') - .groupBy(truncated) - .orderBy(truncated, options.order === AssetOrder.ASC ? 'ASC' : 'DESC') - .getRawMany(); + async getTimeBuckets(options: TimeBucketOptions): Promise { + return this.prismaRepository.$kysely + .selectFrom('assets') + .select((eb) => withTimeBucket(eb, options.size).as('timeBucket')) + .select((eb) => eb.fn.count('id').as('count')) + .groupBy('timeBucket') + .where('ownerId', '=', sql`any(array[${options.userIds}]::uuid[])`) + .where('isVisible', '=', true) + .where('deletedAt', options.isTrashed ? 'is not' : 'is', null) + .$if(options.isArchived != null, (qb) => qb.where('isArchived', '=', options.isArchived as boolean)) + .$if(options.isFavorite != null, (qb) => qb.where('isFavorite', '=', options.isFavorite as boolean)) + .$if(!!options.assetType, (qb) => qb.where('type', '=', options.assetType as AssetType)) + .$if(!!options.personId, (qb) => + qb + .innerJoin('asset_faces as faces', 'faces.assetId', 'assets.id') + .where('faces.personId', '=', options.personId as string), + ) + .$if(!!options.albumId, (qb) => + qb + .innerJoin('albums_assets_assets as albums', 'albums.assetsId', 'assets.id') + .where('albums.albumsId', '=', options.albumId as string), + ) + .orderBy('timeBucket', 'desc') + .execute() as any as Promise; } @GenerateSql({ params: [DummyValue.TIME_BUCKET, { size: TimeBucketSize.MONTH }] }) getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise { - const truncated = dateTrunc(options); - return ( - this.getBuilder(options) - .andWhere(`${truncated} = :timeBucket`, { timeBucket: timeBucket.replace(/^[+-]/, '') }) - // First sort by the day in localtime (put it in the right bucket) - .orderBy(truncated, 'DESC') - // and then sort by the actual time - .addOrderBy('asset.fileCreatedAt', options.order === AssetOrder.ASC ? 'ASC' : 'DESC') - .getMany() - ); + return this.prismaRepository.$kysely + .selectFrom('assets') + .selectAll() + .where((eb) => eb(withTimeBucket(eb, options.size), '=', sql`${timeBucket.replace(/^[+-]/, '')}::timestamp`)) + .where('ownerId', '=', sql`any(array[${options.userIds}]::uuid[])`) + .where('isVisible', '=', true) + .where('deletedAt', options.isTrashed ? 'is not' : 'is', null) + .$if(options.isArchived != null, (qb) => qb.where('isArchived', '=', options.isArchived as boolean)) + .$if(options.isFavorite != null, (qb) => qb.where('isFavorite', '=', options.isFavorite as boolean)) + .$if(!!options.assetType, (qb) => qb.where('type', '=', options.assetType as AssetType)) + .$if(!!options.exifInfo, (qb) => qb.select((eb) => withExif(eb))) + .orderBy('fileCreatedAt', options.order === AssetOrder.ASC ? 'asc' : 'desc') + .execute() as any as Promise; } @GenerateSql({ params: [{ userIds: [DummyValue.UUID, DummyValue.UUID] }] }) - getDuplicates(options: AssetBuilderOptions): Promise { - return this.getBuilder({ ...options, isDuplicate: true }) - .orderBy('asset.duplicateId') - .getMany(); + getDuplicates(userIds: string[]): Promise { + return this.prismaRepository.$kysely + .selectFrom('assets') + .leftJoinLateral( + (qb) => + qb + .selectFrom('exif') + .select((eb) => eb.fn.toJson('exif').as('exifInfo')) + .whereRef('assets.id', '=', 'exif.assetId') + .limit(1) + .as('lat'), + (join) => join.onTrue(), + ) + .select((eb) => [ + 'duplicateId', + eb.fn + .jsonAgg( + eb.fn('jsonb_insert', [ + eb.fn('to_jsonb', [eb.table('assets'), sql`ARRAY['exifInfo']`, eb.ref('exifInfo')]), + ]), + ) + .as('assets'), + ]) + .where('ownerId', '=', sql`any(array[${userIds}]::uuid[])`) + .where('isVisible', '=', true) + .where('duplicateId', 'is not', null) + .groupBy('duplicateId') + .execute() as any as Promise; } @GenerateSql({ params: [DummyValue.UUID, { minAssetsPerField: 5, maxFields: 12 }] }) @@ -654,28 +677,44 @@ export class AssetRepository implements IAssetRepository { ownerId: string, { minAssetsPerField, maxFields }: AssetExploreFieldOptions, ): Promise> { - const cte = this.exifRepository - .createQueryBuilder('e') - .select('city') - .groupBy('city') - .having('count(city) >= :minAssetsPerField', { minAssetsPerField }); - - const items = await this.getBuilder({ - userIds: [ownerId], - exifInfo: false, - assetType: AssetType.IMAGE, - isArchived: false, - }) - .select('c.city', 'value') - .addSelect('asset.id', 'data') - .distinctOn(['c.city']) - .innerJoin('exif', 'e', 'asset.id = e."assetId"') - .addCommonTableExpression(cte, 'cities') - .innerJoin('cities', 'c', 'c.city = e.city') - .limit(maxFields) - .getRawMany(); - - return { fieldName: 'exifInfo.city', items }; + const res = await this.prismaRepository.exif.groupBy({ + by: 'city', + where: { + assets: { ownerId, isVisible: true, isArchived: false, type: AssetType.IMAGE }, + city: { not: null }, + }, + having: { + assetId: { + _count: { + gte: minAssetsPerField, + }, + }, + }, + take: maxFields, + orderBy: { + city: 'desc', + }, + }); + + const cities = res.map((item) => item.city!); + + const items = await this.prismaRepository.exif.findMany({ + where: { + city: { + in: cities, + }, + }, + select: { + city: true, + assetId: true, + }, + distinct: ['city'], + }); + + return { + fieldName: 'exifInfo.city', + items: items.map((item) => ({ value: item.city!, data: item.assetId })), + }; } @GenerateSql({ params: [DummyValue.UUID, { minAssetsPerField: 5, maxFields: 12 }] }) @@ -683,87 +722,42 @@ export class AssetRepository implements IAssetRepository { ownerId: string, { minAssetsPerField, maxFields }: AssetExploreFieldOptions, ): Promise> { - const cte = this.smartInfoRepository - .createQueryBuilder('si') - .select('unnest(tags)', 'tag') - .groupBy('tag') - .having('count(*) >= :minAssetsPerField', { minAssetsPerField }); - - const items = await this.getBuilder({ - userIds: [ownerId], - exifInfo: false, - assetType: AssetType.IMAGE, - isArchived: false, - }) - .select('unnest(si.tags)', 'value') - .addSelect('asset.id', 'data') - .distinctOn(['unnest(si.tags)']) - .innerJoin('smart_info', 'si', 'asset.id = si."assetId"') - .addCommonTableExpression(cte, 'random_tags') - .innerJoin('random_tags', 't', 'si.tags @> ARRAY[t.tag]') - .limit(maxFields) - .getRawMany(); - - return { fieldName: 'smartInfo.tags', items }; - } - - private getBuilder(options: AssetBuilderOptions) { - const builder = this.repository.createQueryBuilder('asset').where('asset.isVisible = true'); - if (options.assetType !== undefined) { - builder.andWhere('asset.type = :assetType', { assetType: options.assetType }); - } - - let stackJoined = false; - - if (options.exifInfo !== false) { - stackJoined = true; - builder - .leftJoinAndSelect('asset.exifInfo', 'exifInfo') - .leftJoinAndSelect('asset.stack', 'stack') - .leftJoinAndSelect('stack.assets', 'stackedAssets'); - } - - if (options.albumId) { - builder.leftJoin('asset.albums', 'album').andWhere('album.id = :albumId', { albumId: options.albumId }); - } - - if (options.userIds) { - builder.andWhere('asset.ownerId IN (:...userIds )', { userIds: options.userIds }); - } - - if (options.isArchived !== undefined) { - builder.andWhere('asset.isArchived = :isArchived', { isArchived: options.isArchived }); - } - - if (options.isFavorite !== undefined) { - builder.andWhere('asset.isFavorite = :isFavorite', { isFavorite: options.isFavorite }); - } - - if (options.isTrashed !== undefined) { - builder.andWhere(`asset.deletedAt ${options.isTrashed ? 'IS NOT NULL' : 'IS NULL'}`).withDeleted(); - } - - if (options.isDuplicate !== undefined) { - builder.andWhere(`asset.duplicateId ${options.isDuplicate ? 'IS NOT NULL' : 'IS NULL'}`); - } + const res = await this.prismaRepository.smartInfo.groupBy({ + by: 'tags', + where: { + assets: { ownerId, isVisible: true, isArchived: false, type: AssetType.IMAGE }, + }, + having: { + assetId: { + _count: { + gte: minAssetsPerField, + }, + }, + }, + take: maxFields, + orderBy: { + tags: 'desc', + }, + }); - if (options.personId !== undefined) { - builder - .innerJoin('asset.faces', 'faces') - .innerJoin('faces.person', 'person') - .andWhere('person.id = :personId', { personId: options.personId }); - } + const tags = res.flatMap((item) => item.tags!); - if (options.withStacked) { - if (!stackJoined) { - builder.leftJoinAndSelect('asset.stack', 'stack').leftJoinAndSelect('stack.assets', 'stackedAssets'); - } - builder.andWhere( - new Brackets((qb) => qb.where('stack.primaryAssetId = asset.id').orWhere('asset.stackId IS NULL')), - ); - } + const items = await this.prismaRepository.smartInfo.findMany({ + where: { + tags: { + hasSome: tags, + }, + }, + select: { + tags: true, + assetId: true, + }, + }); - return builder; + return { + fieldName: 'smartInfo.tags', + items: items.map((item) => ({ value: item.tags![0], data: item.assetId })), + }; } @GenerateSql({ @@ -779,35 +773,37 @@ export class AssetRepository implements IAssetRepository { }) getAllForUserFullSync(options: AssetFullSyncOptions): Promise { const { ownerId, lastCreationDate, lastId, updatedUntil, limit } = options; - const builder = this.getBuilder({ - userIds: [ownerId], - exifInfo: true, // also joins stack information - withStacked: false, // return all assets individually as expected by the app - }); - - if (lastCreationDate !== undefined && lastId !== undefined) { - builder.andWhere('(asset.fileCreatedAt, asset.id) < (:lastCreationDate, :lastId)', { - lastCreationDate, - lastId, - }); - } - - return builder - .andWhere('asset.updatedAt <= :updatedUntil', { updatedUntil }) - .orderBy('asset.fileCreatedAt', 'DESC') - .addOrderBy('asset.id', 'DESC') - .limit(limit) - .withDeleted() - .getMany(); + return this.prismaRepository.assets.findMany({ + where: { + ownerId, + isVisible: true, + updatedAt: { lte: updatedUntil }, + AND: [{ fileCreatedAt: { lt: lastCreationDate } }, { id: { lt: lastId } }], + OR: [{ deletedAt: null }, { deletedAt: { not: null } }], + }, + include: { exifInfo: true }, + orderBy: { + fileCreatedAt: 'desc', + id: 'desc', + }, + take: limit, + }) as any as Promise; } @GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE }] }) getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise { - const builder = this.getBuilder({ userIds: options.userIds, exifInfo: true, withStacked: false }) - .andWhere({ updatedAt: MoreThan(options.updatedAfter) }) - .limit(options.limit) - .withDeleted(); - - return builder.getMany(); + return this.prismaRepository.assets.findMany({ + where: { + ownerId: { in: options.userIds }, + isVisible: true, + updatedAt: { gt: options.updatedAfter }, + OR: [{ deletedAt: null }, { deletedAt: { not: null } }], + }, + include: { + exifInfo: true, + stack: true, + }, + take: options.limit, + }) as any as Promise; } } diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index 9ac9081c91472..7242e5c726a83 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -54,6 +54,7 @@ import { MoveRepository } from 'src/repositories/move.repository'; import { NotificationRepository } from 'src/repositories/notification.repository'; import { PartnerRepository } from 'src/repositories/partner.repository'; import { PersonRepository } from 'src/repositories/person.repository'; +import { PrismaRepository } from 'src/repositories/prisma.repository'; import { SearchRepository } from 'src/repositories/search.repository'; import { ServerInfoRepository } from 'src/repositories/server-info.repository'; import { SessionRepository } from 'src/repositories/session.repository'; @@ -96,4 +97,5 @@ export const repositories = [ { provide: ITagRepository, useClass: TagRepository }, { provide: IMediaRepository, useClass: MediaRepository }, { provide: IUserRepository, useClass: UserRepository }, + { provide: PrismaRepository, useClass: PrismaRepository }, ]; diff --git a/server/src/repositories/prisma.repository.ts b/server/src/repositories/prisma.repository.ts new file mode 100644 index 0000000000000..7f6cf0be864af --- /dev/null +++ b/server/src/repositories/prisma.repository.ts @@ -0,0 +1,44 @@ +import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; +import { findNonDeletedExtension } from 'src/prisma/find-non-deleted'; +import { kyselyExtension } from 'src/prisma/kysely'; +import { metricsExtension } from 'src/prisma/metrics'; + +declare global { + interface BigInt { + toJSON(): number | string; + } +} + +const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); + +BigInt.prototype.toJSON = function () { + return this.valueOf() > MAX_SAFE_INTEGER ? this.toString() : Number(this.valueOf()); +}; + +function extendClient(base: PrismaClient) { + return base.$extends(metricsExtension).$extends(findNonDeletedExtension).$extends(kyselyExtension); +} + +class UntypedExtendedClient extends PrismaClient { + constructor(options?: ConstructorParameters[0]) { + super(options); + + return extendClient(this) as this; + } +} + +const ExtendedPrismaClient = UntypedExtendedClient as unknown as new ( + options?: ConstructorParameters[0], +) => ReturnType; + +@Injectable() +export class PrismaRepository extends ExtendedPrismaClient implements OnModuleInit, OnModuleDestroy { + async onModuleInit() { + await this.$connect(); + } + + async onModuleDestroy() { + await this.$disconnect(); + } +} diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 5bc48fbf99267..1022b4aed78a1 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -1,17 +1,14 @@ import { Inject, Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { vectorExt } from 'src/database.config'; +import { Kysely, OrderByDirectionExpression, sql } from 'kysely'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity'; -import { SmartInfoEntity } from 'src/entities/smart-info.entity'; -import { SmartSearchEntity } from 'src/entities/smart-search.entity'; -import { DatabaseExtension } from 'src/interfaces/database.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AssetDuplicateResult, AssetDuplicateSearch, + AssetSearchBuilderOptions, AssetSearchOptions, FaceEmbeddingSearch, FaceSearchResult, @@ -19,40 +16,22 @@ import { SearchPaginationOptions, SmartSearchOptions, } from 'src/interfaces/search.interface'; -import { asVector, searchAssetBuilder } from 'src/utils/database'; +import { DB } from 'src/prisma/generated/types'; +import { PrismaRepository } from 'src/repositories/prisma.repository'; +import { asVector, withExif, withFaces, withPeople, withSmartInfo } from 'src/utils/database'; import { Instrumentation } from 'src/utils/instrumentation'; import { getCLIPModelInfo } from 'src/utils/misc'; -import { Paginated, PaginationMode, PaginationResult, paginatedBuilder } from 'src/utils/pagination'; +import { Paginated } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; -import { Repository, SelectQueryBuilder } from 'typeorm'; @Instrumentation() @Injectable() export class SearchRepository implements ISearchRepository { - private faceColumns: string[]; - private assetsByCityQuery: string; - constructor( - @InjectRepository(SmartInfoEntity) private repository: Repository, - @InjectRepository(AssetEntity) private assetRepository: Repository, - @InjectRepository(AssetFaceEntity) private assetFaceRepository: Repository, - @InjectRepository(SmartSearchEntity) private smartSearchRepository: Repository, - @InjectRepository(GeodataPlacesEntity) private geodataPlacesRepository: Repository, @Inject(ILoggerRepository) private logger: ILoggerRepository, + private prismaRepository: PrismaRepository, ) { this.logger.setContext(SearchRepository.name); - this.faceColumns = this.assetFaceRepository.manager.connection - .getMetadata(AssetFaceEntity) - .ownColumns.map((column) => column.propertyName) - .filter((propertyName) => propertyName !== 'embedding'); - this.assetsByCityQuery = - assetsByCityCte + - this.assetRepository - .createQueryBuilder('asset') - .innerJoinAndSelect('asset.exifInfo', 'exif') - .withDeleted() - .getQuery() + - ' INNER JOIN cte ON asset.id = cte."assetId"'; } async init(modelName: string): Promise { @@ -80,23 +59,16 @@ export class SearchRepository implements ISearchRepository { ], }) async searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions): Paginated { - let builder = this.assetRepository.createQueryBuilder('asset'); - builder = searchAssetBuilder(builder, options); - - builder.orderBy('asset.fileCreatedAt', options.orderDirection ?? 'DESC'); - return paginatedBuilder(builder, { - mode: PaginationMode.SKIP_TAKE, - skip: (pagination.page - 1) * pagination.size, - take: pagination.size, - }); - } - - private createPersonFilter(builder: SelectQueryBuilder, personIds: string[]) { - return builder - .select(`${builder.alias}."assetId"`) - .where(`${builder.alias}."personId" IN (:...personIds)`, { personIds }) - .groupBy(`${builder.alias}."assetId"`) - .having(`COUNT(DISTINCT ${builder.alias}."personId") = :personCount`, { personCount: personIds.length }); + const orderDirection = (options.orderDirection?.toLowerCase() || 'desc') as OrderByDirectionExpression; + const builder = this.searchAssetBuilder(options) + .orderBy('assets.fileCreatedAt', orderDirection) + .limit(pagination.size + 1) + .offset((pagination.page - 1) * pagination.size); + + const items = (await builder.execute()) as any as AssetEntity[]; + const hasNextPage = items.length > pagination.size; + items.splice(pagination.size); + return { items, hasNextPage }; } @GenerateSql({ @@ -112,39 +84,26 @@ export class SearchRepository implements ISearchRepository { }, ], }) - async searchSmart( - pagination: SearchPaginationOptions, - { embedding, userIds, personIds, ...options }: SmartSearchOptions, - ): Paginated { - let results: PaginationResult = { items: [], hasNextPage: false }; - - await this.assetRepository.manager.transaction(async (manager) => { - let builder = manager.createQueryBuilder(AssetEntity, 'asset'); - - if (personIds?.length) { - const assetFaceBuilder = manager.createQueryBuilder(AssetFaceEntity, 'asset_face'); - const cte = this.createPersonFilter(assetFaceBuilder, personIds); - builder - .addCommonTableExpression(cte, 'asset_face_ids') - .innerJoin('asset_face_ids', 'a', 'a."assetId" = asset.id'); - } + async searchSmart(pagination: SearchPaginationOptions, options: SmartSearchOptions): Paginated { + if (!isValidInteger(pagination.size, { min: 1, max: 1000 })) { + throw new Error(`Invalid value for 'size': ${pagination.size}`); + } - builder = searchAssetBuilder(builder, options); - builder - .innerJoin('asset.smartSearch', 'search') - .andWhere('asset.ownerId IN (:...userIds )') - .orderBy('search.embedding <=> :embedding') - .setParameters({ userIds, embedding: asVector(embedding) }); + let items: AssetEntity[] = []; + await this.prismaRepository.$transaction(async (tx) => { + await tx.$queryRawUnsafe(`SET LOCAL vectors.hnsw_ef_search = ${pagination.size + 1}`); + const builder = this.searchAssetBuilder(options, tx.$kysely) + .innerJoin('smart_search', 'assets.id', 'smart_search.assetId') + .orderBy(sql`smart_search.embedding <=> ${asVector(options.embedding)}::vector`) + .limit(pagination.size + 1) + .offset((pagination.page - 1) * pagination.size); - await manager.query(this.getRuntimeConfig(pagination.size)); - results = await paginatedBuilder(builder, { - mode: PaginationMode.LIMIT_OFFSET, - skip: (pagination.page - 1) * pagination.size, - take: pagination.size, - }); + items = (await builder.execute()) as any as AssetEntity[]; }); - return results; + const hasNextPage = items.length > pagination.size; + items.splice(pagination.size); + return { items, hasNextPage }; } @GenerateSql({ @@ -162,27 +121,28 @@ export class SearchRepository implements ISearchRepository { maxDistance, userIds, }: AssetDuplicateSearch): Promise { - const cte = this.assetRepository.createQueryBuilder('asset'); - cte - .select('search.assetId', 'assetId') - .addSelect('asset.duplicateId', 'duplicateId') - .addSelect(`search.embedding <=> :embedding`, 'distance') - .innerJoin('asset.smartSearch', 'search') - .where('asset.ownerId IN (:...userIds )') - .andWhere('asset.id != :assetId') - .andWhere('asset.isVisible = :isVisible') - .orderBy('search.embedding <=> :embedding') - .limit(64) - .setParameters({ assetId, embedding: asVector(embedding), isVisible: true, userIds }); - - const builder = this.assetRepository.manager - .createQueryBuilder() - .addCommonTableExpression(cte, 'cte') - .from('cte', 'res') - .select('res.*') - .where('res.distance <= :maxDistance', { maxDistance }); - - return builder.getRawMany() as any as Promise; + return Promise.resolve([] as unknown as AssetDuplicateResult[]); + // const cte = this.assetRepository.createQueryBuilder('asset'); + // cte + // .select('search.assetId', 'assetId') + // .addSelect('asset.duplicateId', 'duplicateId') + // .addSelect(`search.embedding <=> :embedding`, 'distance') + // .innerJoin('asset.smartSearch', 'search') + // .where('asset.ownerId IN (:...userIds )') + // .andWhere('asset.id != :assetId') + // .andWhere('asset.isVisible = :isVisible') + // .orderBy('search.embedding <=> :embedding') + // .limit(64) + // .setParameters({ assetId, embedding: asVector(embedding), isVisible: true, userIds }); + + // const builder = this.assetRepository.manager + // .createQueryBuilder() + // .addCommonTableExpression(cte, 'cte') + // .from('cte', 'res') + // .select('res.*') + // .where('res.distance <= :maxDistance', { maxDistance }); + + // return builder.getRawMany() as any as Promise; } @GenerateSql({ @@ -195,103 +155,107 @@ export class SearchRepository implements ISearchRepository { }, ], }) - async searchFaces({ + searchFaces({ userIds, embedding, numResults, maxDistance, hasPerson, }: FaceEmbeddingSearch): Promise { - if (!isValidInteger(numResults, { min: 1 })) { + if (!isValidInteger(numResults, { min: 1, max: 1000 })) { throw new Error(`Invalid value for 'numResults': ${numResults}`); } // setting this too low messes with prefilter recall numResults = Math.max(numResults, 64); - - let results: Array = []; - await this.assetRepository.manager.transaction(async (manager) => { - const cte = manager - .createQueryBuilder(AssetFaceEntity, 'faces') - .select('faces.embedding <=> :embedding', 'distance') - .innerJoin('faces.asset', 'asset') - .where('asset.ownerId IN (:...userIds )') - .orderBy('faces.embedding <=> :embedding') - .setParameters({ userIds, embedding: asVector(embedding) }); - - cte.limit(numResults); - - if (hasPerson) { - cte.andWhere('faces."personId" IS NOT NULL'); - } - - for (const col of this.faceColumns) { - cte.addSelect(`faces.${col}`, col); - } - - await manager.query(this.getRuntimeConfig(numResults)); - results = await manager - .createQueryBuilder() - .select('res.*') - .addCommonTableExpression(cte, 'cte') - .from('cte', 'res') - .where('res.distance <= :maxDistance', { maxDistance }) - .orderBy('res.distance') - .getRawMany(); + const vector = asVector(embedding); + return this.prismaRepository.$transaction(async (tx) => { + await tx.$queryRawUnsafe(`SET LOCAL vectors.hnsw_ef_search = ${numResults}`); + return tx.$kysely + .with('cte', (qb) => + qb + .selectFrom('asset_faces') + .select([ + (eb) => eb.fn.toJson(sql`asset_faces.*`).as('face'), + sql`asset_faces.embedding <=> ${vector}::vector`.as('distance'), + ]) + .innerJoin('assets', 'assets.id', 'asset_faces.assetId') + .where('assets.ownerId', '=', sql`ANY(ARRAY[${userIds}]::uuid[])`) + .$if(!!hasPerson, (qb) => qb.where('asset_faces.personId', 'is not', null)) + .orderBy(sql`asset_faces.embedding <=> ${vector}::vector`) + .limit(numResults), + ) + .selectFrom('cte') + .selectAll() + .where('cte.distance', '<=', maxDistance) + .execute() as any as Array<{ face: AssetFaceEntity; distance: number }>; }); - return results.map((row) => ({ - face: this.assetFaceRepository.create(row), - distance: row.distance, - })); } @GenerateSql({ params: [DummyValue.STRING] }) - async searchPlaces(placeName: string): Promise { - return await this.geodataPlacesRepository - .createQueryBuilder('geoplaces') - .where(`f_unaccent(name) %>> f_unaccent(:placeName)`) - .orWhere(`f_unaccent("admin2Name") %>> f_unaccent(:placeName)`) - .orWhere(`f_unaccent("admin1Name") %>> f_unaccent(:placeName)`) - .orWhere(`f_unaccent("alternateNames") %>> f_unaccent(:placeName)`) + searchPlaces(placeName: string): Promise { + const contains = '%>>' as any as 'ilike'; + return this.prismaRepository.$kysely + .selectFrom('geodata_places') + .selectAll() + .where((eb) => + eb.or([ + eb(eb.fn('f_unaccent', ['name']), contains, eb.fn('f_unaccent', [eb.val(placeName)])), + eb(eb.fn('f_unaccent', ['admin2Name']), contains, eb.fn('f_unaccent', [eb.val(placeName)])), + eb(eb.fn('f_unaccent', ['admin1Name']), contains, eb.fn('f_unaccent', [eb.val(placeName)])), + eb(eb.fn('f_unaccent', ['alternateNames']), contains, eb.fn('f_unaccent', [eb.val(placeName)])), + ]), + ) .orderBy( - ` - COALESCE(f_unaccent(name) <->>> f_unaccent(:placeName), 0.1) + - COALESCE(f_unaccent("admin2Name") <->>> f_unaccent(:placeName), 0.1) + - COALESCE(f_unaccent("admin1Name") <->>> f_unaccent(:placeName), 0.1) + - COALESCE(f_unaccent("alternateNames") <->>> f_unaccent(:placeName), 0.1) + sql` + COALESCE(f_unaccent(name) <->>> f_unaccent(${placeName}), 0.1) + + COALESCE(f_unaccent("admin2Name") <->>> f_unaccent(${placeName}), 0.1) + + COALESCE(f_unaccent("admin1Name") <->>> f_unaccent(${placeName}), 0.1) + + COALESCE(f_unaccent("alternateNames") <->>> f_unaccent(${placeName}), 0.1) `, ) - .setParameters({ placeName }) .limit(20) - .getMany(); + .execute() as Promise; } @GenerateSql({ params: [[DummyValue.UUID]] }) - async getAssetsByCity(userIds: string[]): Promise { - const parameters = [userIds, true, false, AssetType.IMAGE]; - const rawRes = await this.repository.query(this.assetsByCityQuery, parameters); - - const items: AssetEntity[] = []; - for (const res of rawRes) { - const item = { exifInfo: {} as Record } as Record; - for (const [key, value] of Object.entries(res)) { - if (key.startsWith('exif_')) { - item.exifInfo[key.replace('exif_', '')] = value; - } else { - item[key.replace('asset_', '')] = value; - } - } - items.push(item as AssetEntity); - } - - return items; + getAssetsByCity(userIds: string[]): Promise { + // the performance difference between this and the normal way is too huge to ignore, e.g. 3s vs 4ms + return this.prismaRepository.$queryRaw`WITH RECURSIVE cte AS ( + ( + SELECT city, "assetId" + FROM exif + INNER JOIN assets ON exif."assetId" = assets.id + WHERE "ownerId" = ANY(ARRAY[${userIds}]::uuid[]) AND "isVisible" = true AND "isArchived" = false AND type = 'IMAGE' + ORDER BY city + LIMIT 1 + ) + + UNION ALL + + SELECT l.city, l."assetId" + FROM cte c + , LATERAL ( + SELECT city, "assetId" + FROM exif + INNER JOIN assets ON exif."assetId" = assets.id + WHERE city > c.city AND "ownerId" = ANY(ARRAY[${userIds}]::uuid[]) AND "isVisible" = true AND "isArchived" = false AND type = 'IMAGE' + ORDER BY city + LIMIT 1 + ) l + ) + select "assets".*, json_strip_nulls(to_json(exif.*)) as "exifInfo" + from "assets" + inner join "exif" on "assets"."id" = "exif"."assetId" + inner join "cte" on "assets"."id" = "cte"."assetId"`; } async upsert(assetId: string, embedding: number[]): Promise { - await this.smartSearchRepository.upsert( - { assetId, embedding: () => asVector(embedding, true) }, - { conflictPaths: ['assetId'] }, - ); + await this.prismaRepository.$kysely + .insertInto('smart_search') + .values({ assetId, embedding: asVector(embedding, true) } as any) + .onConflict((oc) => oc.column('assetId').doUpdateSet({ embedding: asVector(embedding, true) } as any)) + .execute(); } private async updateDimSize(dimSize: number): Promise { @@ -306,27 +270,27 @@ export class SearchRepository implements ISearchRepository { this.logger.log(`Updating database CLIP dimension size to ${dimSize}.`); - await this.smartSearchRepository.manager.transaction(async (manager) => { - await manager.clear(SmartSearchEntity); - await manager.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE vector(${dimSize})`); + await this.prismaRepository.$transaction(async (tx) => { + await tx.$queryRawUnsafe(`TRUNCATE smart_search`); + await tx.$queryRawUnsafe(`ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE vector(${dimSize})`); }); this.logger.log(`Successfully updated database CLIP dimension size from ${curDimSize} to ${dimSize}.`); } deleteAllSearchEmbeddings(): Promise { - return this.smartSearchRepository.clear(); + return this.prismaRepository.$queryRawUnsafe(`TRUNCATE smart_search`); } private async getDimSize(): Promise { - const res = await this.smartSearchRepository.manager.query(` + const res = await this.prismaRepository.$queryRaw<[{ dimsize: number }]>` SELECT atttypmod as dimsize FROM pg_attribute f JOIN pg_class c ON c.oid = f.attrelid WHERE c.relkind = 'r'::char AND f.attnum > 0 AND c.relname = 'smart_search' - AND f.attname = 'embedding'`); + AND f.attname = 'embedding'`; const dimSize = res[0]['dimsize']; if (!isValidInteger(dimSize, { min: 1, max: 2 ** 16 })) { @@ -335,43 +299,90 @@ export class SearchRepository implements ISearchRepository { return dimSize; } - private getRuntimeConfig(numResults?: number): string { - if (vectorExt === DatabaseExtension.VECTOR) { - return 'SET LOCAL hnsw.ef_search = 1000;'; // mitigate post-filter recall - } - - let runtimeConfig = 'SET LOCAL vectors.enable_prefilter=on; SET LOCAL vectors.search_mode=vbase;'; - if (numResults) { - runtimeConfig += ` SET LOCAL vectors.hnsw_ef_search = ${numResults};`; - } - - return runtimeConfig; + private searchAssetBuilder(options: AssetSearchBuilderOptions, kysely: Kysely = this.prismaRepository.$kysely) { + options.isArchived ??= options.withArchived ? undefined : false; + options.withDeleted ??= !!(options.trashedAfter || options.trashedBefore); + const query = kysely + .selectFrom('assets') + .selectAll('assets') + .$if(!!options.createdBefore, (qb) => qb.where('assets.createdAt', '<=', options.createdBefore as Date)) + .$if(!!options.createdAfter, (qb) => qb.where('assets.createdAt', '>=', options.createdAfter as Date)) + .$if(!!options.updatedBefore, (qb) => qb.where('assets.updatedAt', '<=', options.updatedBefore as Date)) + .$if(!!options.updatedAfter, (qb) => qb.where('assets.updatedAt', '>=', options.updatedAfter as Date)) + .$if(!!options.trashedBefore, (qb) => qb.where('assets.deletedAt', '<=', options.trashedBefore as Date)) + .$if(!!options.trashedAfter, (qb) => qb.where('assets.deletedAt', '>=', options.trashedAfter as Date)) + .$if(!!options.takenBefore, (qb) => qb.where('assets.fileCreatedAt', '<=', options.takenBefore as Date)) + .$if(!!options.takenAfter, (qb) => qb.where('assets.fileCreatedAt', '>=', options.takenAfter as Date)) + .$if(!!options.city, (qb) => + qb.leftJoin('exif', 'exif.assetId', 'assets.id').where('exif.city', '=', options.city as string), + ) + .$if(!!options.country, (qb) => + qb.leftJoin('exif', 'exif.assetId', 'assets.id').where('exif.country', '=', options.country as string), + ) + .$if(!!options.lensModel, (qb) => + qb.leftJoin('exif', 'exif.assetId', 'assets.id').where('exif.lensModel', '=', options.lensModel as string), + ) + .$if(!!options.make, (qb) => + qb.leftJoin('exif', 'exif.assetId', 'assets.id').where('exif.make', '=', options.make as string), + ) + .$if(!!options.model, (qb) => + qb.leftJoin('exif', 'exif.assetId', 'assets.id').where('exif.model', '=', options.model as string), + ) + .$if(!!options.state, (qb) => + qb.leftJoin('exif', 'exif.assetId', 'assets.id').where('exif.state', '=', options.state as string), + ) + .$if(!!options.checksum, (qb) => qb.where('assets.checksum', '=', options.checksum as Buffer)) + .$if(!!options.deviceAssetId, (qb) => qb.where('assets.deviceAssetId', '=', options.deviceAssetId as string)) + .$if(!!options.deviceId, (qb) => qb.where('assets.deviceId', '=', options.deviceId as string)) + .$if(!!options.id, (qb) => qb.where('assets.id', '=', options.id as string)) + .$if(!!options.libraryId, (qb) => qb.where('assets.libraryId', '=', options.libraryId as string)) + .$if(!!options.userIds, (qb) => + qb.where('assets.ownerId', '=', sql`any(array[${options.userIds}]::uuid[])`), + ) + .$if(!!options.encodedVideoPath, (qb) => + qb.where('assets.encodedVideoPath', '=', options.encodedVideoPath as string), + ) + .$if(!!options.originalPath, (qb) => qb.where('assets.originalPath', '=', options.originalPath as string)) + .$if(!!options.previewPath, (qb) => qb.where('assets.previewPath', '=', options.previewPath as string)) + .$if(!!options.thumbnailPath, (qb) => qb.where('assets.thumbnailPath', '=', options.thumbnailPath as string)) + .$if(!!options.originalFileName, (qb) => + qb.where(sql`f_unaccent(assets.originalFileName)`, 'ilike', sql`f_unaccent(${options.originalFileName})`), + ) + .$if(options.isFavorite != null, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite as boolean)) + .$if(options.isOffline != null, (qb) => qb.where('assets.isOffline', '=', options.isOffline as boolean)) + .$if(options.isVisible != null, (qb) => qb.where('assets.isVisible', '=', options.isVisible as boolean)) + .$if(options.type != null, (qb) => qb.where('assets.type', '=', options.type as AssetType)) + .$if(options.isArchived != null, (qb) => qb.where('assets.isArchived', '=', options.isArchived as boolean)) + .$if(options.isEncoded != null, (qb) => qb.where('assets.encodedVideoPath', 'is not', null)) + .$if(options.isMotion != null, (qb) => qb.where('assets.livePhotoVideoId', 'is not', null)) + .$if(options.isNotInAlbum != null, (qb) => + qb + .leftJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id') + .where('albums_assets_assets.assetsId', 'is', null), + ) + .$if(!!options.withExif, (qb) => qb.select((eb) => withExif(eb))) + .$if(!!options.withSmartInfo, (qb) => qb.select((eb) => withSmartInfo(eb))) + .$if(!(!options.withFaces || options.withPeople), (qb) => + qb.select((eb) => withFaces(eb)).$if(!!options.withPeople, (qb) => qb.select((eb) => withPeople(eb) as any)), + ) + .$if(!!options.personIds && options.personIds.length > 0, (qb) => + qb.innerJoin( + (eb: any) => + eb + .selectFrom('asset_faces') + .select('asset_faces.assetId') + .where('asset_faces.personId', '=', sql`any(array[${options.personIds}]::uuid[])`) + .groupBy('asset_faces.assetId') + .having( + (eb: any) => eb.fn.count('asset_faces.personId').distinct(), + '=', + (options.personIds as string[]).length, + ) + .as('personAssetIds'), + (join) => join.onRef('personAssetIds.assetId' as any, '=', 'assets.id' as any), + ), + ) + .$if(!options.withDeleted, (qb) => qb.where('assets.deletedAt', 'is', null)); + return query; } } - -// the performance difference between this and the normal way is too huge to ignore, e.g. 3s vs 4ms -const assetsByCityCte = ` -WITH RECURSIVE cte AS ( - ( - SELECT city, "assetId" - FROM exif - INNER JOIN assets ON exif."assetId" = assets.id - WHERE "ownerId" = ANY($1::uuid[]) AND "isVisible" = $2 AND "isArchived" = $3 AND type = $4 - ORDER BY city - LIMIT 1 - ) - - UNION ALL - - SELECT l.city, l."assetId" - FROM cte c - , LATERAL ( - SELECT city, "assetId" - FROM exif - INNER JOIN assets ON exif."assetId" = assets.id - WHERE city > c.city AND "ownerId" = ANY($1::uuid[]) AND "isVisible" = $2 AND "isArchived" = $3 AND type = $4 - ORDER BY city - LIMIT 1 - ) l -) -`; diff --git a/server/src/services/asset-v1.service.ts b/server/src/services/asset-v1.service.ts index bd6f540061c29..b0eb8fb3ff701 100644 --- a/server/src/services/asset-v1.service.ts +++ b/server/src/services/asset-v1.service.ts @@ -291,7 +291,7 @@ export class AssetServiceV1 { isArchived: dto.isArchived ?? false, duration: dto.duration || null, isVisible: dto.isVisible ?? true, - livePhotoVideo: livePhotoAssetId === null ? null : ({ id: livePhotoAssetId } as AssetEntity), + livePhotoVideoId: livePhotoAssetId, originalFileName: file.originalName, sidecarPath: sidecarPath || null, isOffline: dto.isOffline ?? false, diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index ca13adf31cbe5..14108c6e0038a 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -465,14 +465,20 @@ describe(AssetService.name, () => { it('should update the asset', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); assetMock.getById.mockResolvedValue(assetStub.image); + assetMock.update.mockResolvedValue(assetStub.image); + await sut.update(authStub.admin, 'asset-1', { isFavorite: true }); + expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-1', isFavorite: true }); }); it('should update the exif description', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); assetMock.getById.mockResolvedValue(assetStub.image); + assetMock.update.mockResolvedValue(assetStub.image); + await sut.update(authStub.admin, 'asset-1', { description: 'Test description' }); + expect(assetMock.upsertExif).toHaveBeenCalledWith({ assetId: 'asset-1', description: 'Test description' }); }); }); diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index d266b1ed2f9a4..1d36421a95e42 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -241,29 +241,26 @@ export class AssetService { async get(auth: AuthDto, id: string): Promise { await this.access.requirePermission(auth, Permission.ASSET_READ, id); - const asset = await this.assetRepository.getById( - id, - { - exifInfo: true, - tags: true, - sharedLinks: true, - smartInfo: true, - owner: true, - faces: { - person: true, - }, - stack: { + const asset = await this.assetRepository.getById(id, { + exifInfo: true, + tags: true, + sharedLinks: true, + smartInfo: true, + owner: true, + faces: { + include: { person: true }, + orderBy: { boundingBoxX1: 'asc' }, + }, + stack: { + include: { assets: { - exifInfo: true, + include: { + exifInfo: true, + }, }, }, }, - { - faces: { - boundingBoxX1: 'ASC', - }, - }, - ); + }); if (!asset) { throw new BadRequestException('Asset not found'); @@ -292,16 +289,7 @@ export class AssetService { const { description, dateTimeOriginal, latitude, longitude, ...rest } = dto; await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude }); - await this.assetRepository.update({ id, ...rest }); - const asset = await this.assetRepository.getById(id, { - exifInfo: true, - owner: true, - smartInfo: true, - tags: true, - faces: { - person: true, - }, - }); + const asset = await this.assetRepository.update({ id, ...rest }); if (!asset) { throw new BadRequestException('Asset not found'); } @@ -327,14 +315,16 @@ export class AssetService { } else if (options.stackParentId) { //Creating new stack if parent doesn't have one already. If it does, then we add to the existing stack await this.access.requirePermission(auth, Permission.ASSET_UPDATE, options.stackParentId); - const primaryAsset = await this.assetRepository.getById(options.stackParentId, { stack: { assets: true } }); + const primaryAsset = await this.assetRepository.getById(options.stackParentId, { + stack: { include: { assets: true } }, + }); if (!primaryAsset) { throw new BadRequestException('Asset not found for given stackParentId'); } let stack = primaryAsset.stack; ids.push(options.stackParentId); - const assets = await this.assetRepository.getByIds(ids, { stack: { assets: true } }); + const assets = await this.assetRepository.getByIds(ids, { stack: { include: { assets: true } } }); stackIdsToCheckForDelete.push( ...new Set(assets.filter((a) => !!a.stackId && stack?.id !== a.stackId).map((a) => a.stackId!)), ); @@ -398,10 +388,10 @@ export class AssetService { const asset = await this.assetRepository.getById(id, { faces: { - person: true, + include: { person: true }, }, library: true, - stack: { assets: true }, + stack: { include: { assets: true } }, exifInfo: true, }); @@ -466,11 +456,11 @@ export class AssetService { const childIds: string[] = []; const oldParent = await this.assetRepository.getById(oldParentId, { faces: { - person: true, + include: { person: true }, }, library: true, stack: { - assets: true, + include: { assets: true }, }, }); if (!oldParent?.stackId) { diff --git a/server/src/services/duplicate.service.ts b/server/src/services/duplicate.service.ts index 95a12bd18ee00..f5fdf698cd1a6 100644 --- a/server/src/services/duplicate.service.ts +++ b/server/src/services/duplicate.service.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { DuplicateResponseDto, mapDuplicateResponse } from 'src/dtos/duplicate.dto'; +import { DuplicateResponseDto } from 'src/dtos/duplicate.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; @@ -37,9 +37,8 @@ export class DuplicateService { } async getDuplicates(auth: AuthDto): Promise { - const res = await this.assetRepository.getDuplicates({ userIds: [auth.user.id] }); - - return mapDuplicateResponse(res.map((a) => mapAsset(a, { auth }))); + const res = await this.assetRepository.getDuplicates([auth.user.id]); + return res.map(({ duplicateId, assets }) => ({ duplicateId, assets: assets.map((a) => mapAsset(a, { auth })) })); } async handleQueueSearchDuplicates({ force }: IBaseJob): Promise { diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index de0c19166756a..19b945fa28044 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -318,13 +318,7 @@ export class PersonService { return JobStatus.SKIPPED; } - const relations = { - exifInfo: true, - faces: { - person: false, - }, - }; - const [asset] = await this.assetRepository.getByIds([id], relations); + const [asset] = await this.assetRepository.getByIds([id], { exifInfo: true, faces: true }); if (!asset || !asset.previewPath || asset.faces?.length > 0) { return JobStatus.FAILED; } diff --git a/server/src/services/timeline.service.spec.ts b/server/src/services/timeline.service.spec.ts index 981fc11c3f5ab..93918bc03ce06 100644 --- a/server/src/services/timeline.service.spec.ts +++ b/server/src/services/timeline.service.spec.ts @@ -4,6 +4,7 @@ import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { TimelineService } from 'src/services/timeline.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; +import { partnerStub } from 'test/fixtures/partner.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; @@ -52,6 +53,7 @@ describe(TimelineService.name, () => { size: TimeBucketSize.DAY, timeBucket: 'bucket', albumId: 'album-id', + userIds: [authStub.admin.user.id], }); }); @@ -66,12 +68,15 @@ describe(TimelineService.name, () => { userId: authStub.admin.user.id, }), ).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })])); - expect(assetMock.getTimeBucket).toHaveBeenCalledWith('bucket', { - size: TimeBucketSize.DAY, - timeBucket: 'bucket', - isArchived: true, - userIds: [authStub.admin.user.id], - }); + expect(assetMock.getTimeBucket).toHaveBeenCalledWith( + 'bucket', + expect.objectContaining({ + size: TimeBucketSize.DAY, + timeBucket: 'bucket', + isArchived: true, + userIds: [authStub.admin.user.id], + }), + ); }); it('should return the assets for a library time bucket if user has library.read', async () => { @@ -84,11 +89,14 @@ describe(TimelineService.name, () => { userId: authStub.admin.user.id, }), ).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })])); - expect(assetMock.getTimeBucket).toHaveBeenCalledWith('bucket', { - size: TimeBucketSize.DAY, - timeBucket: 'bucket', - userIds: [authStub.admin.user.id], - }); + expect(assetMock.getTimeBucket).toHaveBeenCalledWith( + 'bucket', + expect.objectContaining({ + size: TimeBucketSize.DAY, + timeBucket: 'bucket', + userIds: [authStub.admin.user.id], + }), + ); }); it('should throw an error if withParners is true and isArchived true or undefined', async () => { diff --git a/server/src/services/timeline.service.ts b/server/src/services/timeline.service.ts index 95c4081e6a101..01ba07cd2b338 100644 --- a/server/src/services/timeline.service.ts +++ b/server/src/services/timeline.service.ts @@ -4,7 +4,7 @@ import { AssetResponseDto, SanitizedAssetResponseDto, mapAsset } from 'src/dtos/ import { AuthDto } from 'src/dtos/auth.dto'; import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto'; import { IAccessRepository } from 'src/interfaces/access.interface'; -import { IAssetRepository, TimeBucketOptions } from 'src/interfaces/asset.interface'; +import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; export class TimelineService { @@ -19,56 +19,29 @@ export class TimelineService { } async getTimeBuckets(auth: AuthDto, dto: TimeBucketDto): Promise { - await this.timeBucketChecks(auth, dto); - const timeBucketOptions = await this.buildTimeBucketOptions(auth, dto); + await this.validateDto(auth, dto); + const userIds = await this.getUserIds(auth, dto); + await this.checkPermission(auth, dto, userIds); - return this.repository.getTimeBuckets(timeBucketOptions); + return this.repository.getTimeBuckets({ ...dto, userIds }); } async getTimeBucket( auth: AuthDto, dto: TimeBucketAssetDto, ): Promise { - await this.timeBucketChecks(auth, dto); - const timeBucketOptions = await this.buildTimeBucketOptions(auth, dto); - const assets = await this.repository.getTimeBucket(dto.timeBucket, timeBucketOptions); + await this.validateDto(auth, dto); + const userIds = await this.getUserIds(auth, dto); + await this.checkPermission(auth, dto, userIds); + const assets = await this.repository.getTimeBucket(dto.timeBucket, { ...dto, userIds }); return !auth.sharedLink || auth.sharedLink?.showExif ? assets.map((asset) => mapAsset(asset, { withStack: true, auth })) : assets.map((asset) => mapAsset(asset, { stripMetadata: true, auth })); } - private async buildTimeBucketOptions(auth: AuthDto, dto: TimeBucketDto): Promise { - const { userId, ...options } = dto; - let userIds: string[] | undefined = undefined; - - if (userId) { - userIds = [userId]; - - if (dto.withPartners) { - const partners = await this.partnerRepository.getAll(auth.user.id); - const partnersIds = partners - .filter((partner) => partner.sharedBy && partner.sharedWith && partner.inTimeline) - .map((partner) => partner.sharedById); - - userIds.push(...partnersIds); - } - } - - return { ...options, userIds }; - } - - private async timeBucketChecks(auth: AuthDto, dto: TimeBucketDto) { + private async validateDto(auth: AuthDto, dto: TimeBucketDto) { if (dto.albumId) { await this.accessCore.requirePermission(auth, Permission.ALBUM_READ, [dto.albumId]); - } else { - dto.userId = dto.userId || auth.user.id; - } - - if (dto.userId) { - await this.accessCore.requirePermission(auth, Permission.TIMELINE_READ, [dto.userId]); - if (dto.isArchived !== false) { - await this.accessCore.requirePermission(auth, Permission.ARCHIVE_READ, [dto.userId]); - } } if (dto.withPartners) { @@ -83,4 +56,29 @@ export class TimelineService { } } } + + private async getUserIds(auth: AuthDto, dto: TimeBucketDto): Promise { + const userIds = [auth.user.id]; + + if (dto.withPartners) { + const partners = await this.partnerRepository.getAll(auth.user.id); + const partnersIds = partners + .filter((partner) => partner.sharedBy && partner.sharedWith && partner.inTimeline) + .map((partner) => partner.sharedById); + + userIds.push(...partnersIds); + } + + return userIds; + } + + private async checkPermission(auth: AuthDto, dto: TimeBucketDto, userIds: string[]) { + const sharedUsers = userIds.filter((id) => id !== auth.user.id); + if (sharedUsers.length > 0) { + await this.accessCore.requirePermission(auth, Permission.TIMELINE_READ, sharedUsers); + if (dto.isArchived !== false) { + await this.accessCore.requirePermission(auth, Permission.ARCHIVE_READ, sharedUsers); + } + } + } } diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index 65456e8192043..1ede7930196ea 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -1,6 +1,10 @@ +import { ExpressionBuilder, sql } from 'kysely'; +import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; import _ from 'lodash'; import { AssetEntity } from 'src/entities/asset.entity'; +import { TimeBucketSize } from 'src/interfaces/asset.interface'; import { AssetSearchBuilderOptions } from 'src/interfaces/search.interface'; +import { DB } from 'src/prisma/generated/types'; import { Between, IsNull, LessThanOrEqual, MoreThanOrEqual, Not, SelectQueryBuilder } from 'typeorm'; /** @@ -137,3 +141,28 @@ export function searchAssetBuilder( return builder; } + +export const withExif = (eb: ExpressionBuilder) => + jsonObjectFrom(eb.selectFrom('exif').selectAll().whereRef('exif.assetId', '=', 'assets.id')).as('exifInfo'); + +export const withSmartInfo = (eb: ExpressionBuilder) => + jsonObjectFrom(eb.selectFrom('smart_info').selectAll().whereRef('smart_info.assetId', '=', 'assets.id')).as( + 'smartInfo', + ); + +export const withFaces = (eb: ExpressionBuilder) => + jsonArrayFrom(eb.selectFrom('asset_faces').selectAll().whereRef('asset_faces.assetId', '=', 'assets.id')).as('faces'); + +export const withPeople = (eb: ExpressionBuilder) => + jsonObjectFrom(eb.selectFrom('person').selectAll().whereRef('asset_faces.personId', '=', 'person.id')).as('people'); + +export const withOwner = (eb: ExpressionBuilder) => + jsonObjectFrom(eb.selectFrom('users').selectAll().whereRef('users.id', '=', 'assets.ownerId')).as('owner'); + +const truncateMap: Record = { + [TimeBucketSize.DAY]: 'day', + [TimeBucketSize.MONTH]: 'month', +}; + +export const withTimeBucket = (eb: ExpressionBuilder, size: TimeBucketSize) => + eb.fn('date_trunc', [sql`${truncateMap[size]}`, sql`assets."localDateTime" at time zone 'UTC'`]); diff --git a/server/src/utils/pagination.ts b/server/src/utils/pagination.ts index dec1a9de0c313..6ca2767649c7b 100644 --- a/server/src/utils/pagination.ts +++ b/server/src/utils/pagination.ts @@ -37,7 +37,10 @@ export async function* usePagination( } } -function paginationHelper(items: Entity[], take: number): PaginationResult { +export function paginationHelper( + items: Entity[], + take: number, +): PaginationResult { const hasNextPage = items.length > take; items.splice(take); diff --git a/server/tsconfig.json b/server/tsconfig.json index 3e84bcf7d759d..55cf59a046a41 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -18,7 +18,8 @@ "preserveWatchOutput": true, "baseUrl": "./", "jsx": "react", - "types": ["vitest/globals"] + "types": ["vitest/globals"], + "noErrorTruncation": true }, "exclude": ["dist", "node_modules", "upload"] }