chore(): start moving some packages to the core directory (#7215)

This commit is contained in:
Adrien de Peretti authored and GitHub committed 2024-05-03 13:37:41 +02:00
1 parent fdee748eed
commit bbccd6481d
1436 files changed
+275 -756

No files matched your search

+422
View File
@@ -0,0 +1,422 @@
# @medusajs/utils
## 1.11.9
### Patch Changes
- [#6922](https://github.com/medusajs/medusa/pull/6922) [`20e8df914e`](https://github.com/medusajs/medusa/commit/20e8df914ec5fdf8d562d4fa84f72c58c7056195) Thanks [@sradevski](https://github.com/sradevski)! - Return normalized DB errors from the mikro ORM repository
- [#6836](https://github.com/medusajs/medusa/pull/6836) [`e0b02a1012`](https://github.com/medusajs/medusa/commit/e0b02a1012981c29830d7779f59ebe805bbfd137) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(utils): custom serialization that allows for non self ref
- [#6864](https://github.com/medusajs/medusa/pull/6864) [`e944a627f0`](https://github.com/medusajs/medusa/commit/e944a627f074fb39a56f4bc7b3d6d315736ebf7c) Thanks [@adrien2p](https://github.com/adrien2p)! - feat: region payment providers management workflows/api
- [`cc557c8752`](https://github.com/medusajs/medusa/commit/cc557c8752fd0554f5a1b58522d9a88dc43a8509) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Order endpoints and Cart totals
- [#6842](https://github.com/medusajs/medusa/pull/6842) [`5d9aea053c`](https://github.com/medusajs/medusa/commit/5d9aea053ce6e04f242f86fb9053c13dec515d5b) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(utils): Mikro orm prop filtering should check existence
- [#6973](https://github.com/medusajs/medusa/pull/6973) [`232322d035`](https://github.com/medusajs/medusa/commit/232322d03515f81e56867ff8c765b8409399ee68) Thanks [@adrien2p](https://github.com/adrien2p)! - Chores: Do not disable foreign keys by default
- [#7153](https://github.com/medusajs/medusa/pull/7153) [`4b57c5d286`](https://github.com/medusajs/medusa/commit/4b57c5d286f9dc6e2098c67e9fecb0d93175b5a1) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Remote query supporting context
- [#6813](https://github.com/medusajs/medusa/pull/6813) [`a6562d2a41`](https://github.com/medusajs/medusa/commit/a6562d2a41453cbe7aa43be352c4924e3e4c79d5) Thanks [@sradevski](https://github.com/sradevski)! - Added an upsertWithReplace method to the mikro orm repository
- Updated dependencies [[`0c0b425de7`](https://github.com/medusajs/medusa/commit/0c0b425de7b154b80b712ab17b16215cf62d1e83), [`8d356217bd`](https://github.com/medusajs/medusa/commit/8d356217bd31c97a196e861ee243822a4d924df7), [`27f4f0d724`](https://github.com/medusajs/medusa/commit/27f4f0d7243367c2dfc6012bf1f6b7400a77ec7b), [`e944a627f0`](https://github.com/medusajs/medusa/commit/e944a627f074fb39a56f4bc7b3d6d315736ebf7c), [`1a48fe0282`](https://github.com/medusajs/medusa/commit/1a48fe0282a8bc1f8548a4736255e457d173da09), [`86f499de2f`](https://github.com/medusajs/medusa/commit/86f499de2f31356ab36ad5e93f27345443b3e5f6), [`09a2220569`](https://github.com/medusajs/medusa/commit/09a22205693da62fbf8fd450535d5024cb9c01d1), [`78f603e4f1`](https://github.com/medusajs/medusa/commit/78f603e4f18c9d16f4b58a2189c959026453d8b2), [`cc557c8752`](https://github.com/medusajs/medusa/commit/cc557c8752fd0554f5a1b58522d9a88dc43a8509), [`58c68f6715`](https://github.com/medusajs/medusa/commit/58c68f67156e993255fbc25d91db15ae23bc95c0), [`82a176e30e`](https://github.com/medusajs/medusa/commit/82a176e30e47a7d11caaf31c3023bd8db588b465), [`11517f0faf`](https://github.com/medusajs/medusa/commit/11517f0fafdf00af256240448b58d149d8b6f600), [`62b9dcc6c1`](https://github.com/medusajs/medusa/commit/62b9dcc6c1ce46aadb7944215006c12da3c9f619), [`e26cda4b6a`](https://github.com/medusajs/medusa/commit/e26cda4b6afb7fb25f0b0a7a7ce20b7f914d35db), [`bc06ad2db4`](https://github.com/medusajs/medusa/commit/bc06ad2db48c999023ab823fefc1375196976e9b), [`18f3aacee6`](https://github.com/medusajs/medusa/commit/18f3aacee6752854d377faa806f4cc67bc71456b), [`38c971f111`](https://github.com/medusajs/medusa/commit/38c971f111af69f176e7e9892eb59f5bae831fa7), [`45c49e89f2`](https://github.com/medusajs/medusa/commit/45c49e89f28123ef622fc1c07253bae94fd74875), [`528ef4ca90`](https://github.com/medusajs/medusa/commit/528ef4ca90bb2cf6173dccc9fd6a9f9932ff9b76), [`65794f4bb5`](https://github.com/medusajs/medusa/commit/65794f4bb56e4fd3f0ccb7656a948f856f05324e), [`93ef94cad3`](https://github.com/medusajs/medusa/commit/93ef94cad3ddc5b6973b4e48e422b0aa0e6ddbbe), [`4cf71af07d`](https://github.com/medusajs/medusa/commit/4cf71af07d1807c83df3889c1774f82cbd1b9a6f), [`4b57c5d286`](https://github.com/medusajs/medusa/commit/4b57c5d286f9dc6e2098c67e9fecb0d93175b5a1), [`c78915c7c5`](https://github.com/medusajs/medusa/commit/c78915c7c5e91a99c1b1bae932656c8d86b17daf), [`18f3aacee6`](https://github.com/medusajs/medusa/commit/18f3aacee6752854d377faa806f4cc67bc71456b), [`f175cac4af`](https://github.com/medusajs/medusa/commit/f175cac4af63b71066a8398ecf9beaa6f28b20cc), [`0a9b9b073d`](https://github.com/medusajs/medusa/commit/0a9b9b073dd2d3f4aa5e5cb1c16e2221a7200e0d), [`a6562d2a41`](https://github.com/medusajs/medusa/commit/a6562d2a41453cbe7aa43be352c4924e3e4c79d5), [`00e6b21bb5`](https://github.com/medusajs/medusa/commit/00e6b21bb50dbc886bc37ad052a1c40ce865294e), [`8fd1488938`](https://github.com/medusajs/medusa/commit/8fd148893850eb66c5eae00c4ca9391a80ea2eb9), [`1c6ba4468e`](https://github.com/medusajs/medusa/commit/1c6ba4468eab1440931c88929affd5b4c593f377)]:
- @medusajs/types@1.11.16
## 1.11.8
### Patch Changes
- [#6796](https://github.com/medusajs/medusa/pull/6796) [`9073d7aba3`](https://github.com/medusajs/medusa/commit/9073d7aba3419e4dc0a206473291a46ebd79b8c1) Thanks [@riqwan](https://github.com/riqwan)! - feat(core-flows,medusa,types,utils): rename psma to prices
- [#6751](https://github.com/medusajs/medusa/pull/6751) [`4974f5e455`](https://github.com/medusajs/medusa/commit/4974f5e4557bd64a328a881ec02b91e15485bd23) Thanks [@riqwan](https://github.com/riqwan)! - feat(utils): autogenerates create and update methods when dto is provided
- [#6772](https://github.com/medusajs/medusa/pull/6772) [`1ef9c78cea`](https://github.com/medusajs/medusa/commit/1ef9c78cea080c3b7c136f909c6cddec9d8f0c62) Thanks [@adrien2p](https://github.com/adrien2p)! - chore: v2 - deprecate extra in favor of driver options
- Updated dependencies [[`deab12e27e`](https://github.com/medusajs/medusa/commit/deab12e27e8249e26d24d7bc904c18195679ff24), [`56481e683d`](https://github.com/medusajs/medusa/commit/56481e683d33ff98f0d4c4e144873bb23f993c9c), [`9073d7aba3`](https://github.com/medusajs/medusa/commit/9073d7aba3419e4dc0a206473291a46ebd79b8c1), [`05e857d256`](https://github.com/medusajs/medusa/commit/05e857d25657b5576a891c9b48c19c1759c70701), [`1ef9c78cea`](https://github.com/medusajs/medusa/commit/1ef9c78cea080c3b7c136f909c6cddec9d8f0c62)]:
- @medusajs/types@1.11.15
## 1.11.7
### Patch Changes
- [#6648](https://github.com/medusajs/medusa/pull/6648) [`9288f53327`](https://github.com/medusajs/medusa/commit/9288f53327b8ce617af92ed8d14d9459cbfeb13c) Thanks [@riqwan](https://github.com/riqwan)! - feat(core-flows,medusa,utils): added price list prices upsert and delete workflows + endpoints
- [#6504](https://github.com/medusajs/medusa/pull/6504) [`56cbf88115`](https://github.com/medusajs/medusa/commit/56cbf88115994adea7037c3f2814f0c96af3cfc0) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(utils): Soft delete should allow self referencing circular deps
- [#6473](https://github.com/medusajs/medusa/pull/6473) [`36a61658f9`](https://github.com/medusajs/medusa/commit/36a61658f969a7b19c84a1e621ad1464927cafb1) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(utils): Fix big number decorator and cleanup
- [#6436](https://github.com/medusajs/medusa/pull/6436) [`c319edb8e0`](https://github.com/medusajs/medusa/commit/c319edb8e0ecd13d086652147667916e5abab2d8) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(utils): Add default ordering to the internal service factory list/listAndCount
- [#6531](https://github.com/medusajs/medusa/pull/6531) [`0b9fcb6324`](https://github.com/medusajs/medusa/commit/0b9fcb6324eee9f2556c7e6317775fae93b12a47) Thanks [@riqwan](https://github.com/riqwan)! - feat(utils): consolidate promotion utils + refactor
- [#6525](https://github.com/medusajs/medusa/pull/6525) [`b3d826497b`](https://github.com/medusajs/medusa/commit/b3d826497b3dae5e1b26b7924706c24fd5e87ca5) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(utils): Improve big number decorator
- [#6386](https://github.com/medusajs/medusa/pull/6386) [`a86c87fe14`](https://github.com/medusajs/medusa/commit/a86c87fe1442afce9285e39255914e01012b4449) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(utils): make psql index util return index instead of constraint for unique indicies becuase partial constraints don't exist :'(
- [#6692](https://github.com/medusajs/medusa/pull/6692) [`640eccd5dd`](https://github.com/medusajs/medusa/commit/640eccd5ddbb163e0f987ce6c772f1129c2e2632) Thanks [@riqwan](https://github.com/riqwan)! - feat(core-flows,medusa,types,utils): add rules to promotion endpoints + workflow
- [#6499](https://github.com/medusajs/medusa/pull/6499) [`8ea37d03c9`](https://github.com/medusajs/medusa/commit/8ea37d03c914a5004a3e42770668b2d1f7f8f564) Thanks [@riqwan](https://github.com/riqwan)! - fix(utils): bignumber util considers nullable options when setting value
- [#6399](https://github.com/medusajs/medusa/pull/6399) [`339a946f38`](https://github.com/medusajs/medusa/commit/339a946f389033c21e05338f9dbf07d88e140533) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Initial Order module implementation
- [#6648](https://github.com/medusajs/medusa/pull/6648) [`9288f53327`](https://github.com/medusajs/medusa/commit/9288f53327b8ce617af92ed8d14d9459cbfeb13c) Thanks [@riqwan](https://github.com/riqwan)! - feat(core-flows,medusa,pricing,types,utils): added price list workflows + endpoints
- [#6441](https://github.com/medusajs/medusa/pull/6441) [`8dad2b51a2`](https://github.com/medusajs/medusa/commit/8dad2b51a26c4c3c14a6c95f70424c8bef2ad63e) Thanks [@riqwan](https://github.com/riqwan)! - feat(medusa-react,medusa,utils): fix login for medusa v2 admin next dashboard
- [#6289](https://github.com/medusajs/medusa/pull/6289) [`a6d7070dd6`](https://github.com/medusajs/medusa/commit/a6d7070dd669c21ea19d70434d42c2f8167dc309) Thanks [@riqwan](https://github.com/riqwan)! - feat(types): add util to transform get response to an update request
- [#6489](https://github.com/medusajs/medusa/pull/6489) [`168f02f138`](https://github.com/medusajs/medusa/commit/168f02f138ad101e1013f2c8c3f8dc19de12accf) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(util): Detect circular dependencies on soft delete
- [#6514](https://github.com/medusajs/medusa/pull/6514) [`f5c2256286`](https://github.com/medusajs/medusa/commit/f5c22562867f412040f8bc6c55ab5de3a3735e62) Thanks [@riqwan](https://github.com/riqwan)! - feat(core-flows,medusa,types,utils): adds update cart API with promotions
- [#6592](https://github.com/medusajs/medusa/pull/6592) [`000eb61e33`](https://github.com/medusajs/medusa/commit/000eb61e33e0302db95ee6ad1656ea9b430ed471) Thanks [@riqwan](https://github.com/riqwan)! - feat(pricing,medusa,utils): added list + get endpoints for price lists
- [#6524](https://github.com/medusajs/medusa/pull/6524) [`62a7bcc30c`](https://github.com/medusajs/medusa/commit/62a7bcc30cbc7b234b2b51d7858439951a84edeb) Thanks [@adrien2p](https://github.com/adrien2p)! - Feat(fulfillment): service provider registration + fulfillment management
- [#6700](https://github.com/medusajs/medusa/pull/6700) [`8f8a4f9b13`](https://github.com/medusajs/medusa/commit/8f8a4f9b1353087d98f6cc75346d43a7f49901a8) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Version all modules to allow for initial testing
- [#6396](https://github.com/medusajs/medusa/pull/6396) [`6500f18b9b`](https://github.com/medusajs/medusa/commit/6500f18b9b80c5c9c473489e7e740d55dca74303) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(): Prevent from soft deleting all entities
- [#6311](https://github.com/medusajs/medusa/pull/6311) [`ce39b9b66e`](https://github.com/medusajs/medusa/commit/ce39b9b66e8c277ec0691ea6d0a950003be09cc1) Thanks [@olivermrbl](https://github.com/olivermrbl)! - feat(payment-stripe): new Stripe payment provider
- [#6308](https://github.com/medusajs/medusa/pull/6308) [`a6a4b3f01a`](https://github.com/medusajs/medusa/commit/a6a4b3f01a6d2bd97b1580c59134279a1b033a5d) Thanks [@fPolic](https://github.com/fPolic)! - feat(types, utils): payment module - provider service
- [#6435](https://github.com/medusajs/medusa/pull/6435) [`56b0b45304`](https://github.com/medusajs/medusa/commit/56b0b4530401a6ec5aa155874d371e45bb388fe2) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - BigNumberField decorator
- [#6698](https://github.com/medusajs/medusa/pull/6698) [`cc1b66842c`](https://github.com/medusajs/medusa/commit/cc1b66842cbb37c6eab84e2d8b74844c214f38d7) Thanks [@riqwan](https://github.com/riqwan)! - feat(core-flows,medusa,types,utils): add/remove fulfillment shipping option rules
- [#6383](https://github.com/medusajs/medusa/pull/6383) [`e85463b2a7`](https://github.com/medusajs/medusa/commit/e85463b2a717751de2e21c39a4c745449b31affe) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(): Fix database test utils and utils
- Updated dependencies [[`1fd0457c15`](https://github.com/medusajs/medusa/commit/1fd0457c153b2ef7657c052878d8e5364e1b324a), [`d4b921f3db`](https://github.com/medusajs/medusa/commit/d4b921f3dbe0a38f1565a8de759996c70798d58e), [`ac86362e81`](https://github.com/medusajs/medusa/commit/ac86362e81d8523cb8e3dfad026fc94658513018), [`e4acde1aa2`](https://github.com/medusajs/medusa/commit/e4acde1aa2eb57f07e6692fe8b61f728948b9a96), [`1a661adf3e`](https://github.com/medusajs/medusa/commit/1a661adf3ef4991aa6e237dd894b6a5c47cd4aca), [`04a532e5ef`](https://github.com/medusajs/medusa/commit/04a532e5efabbf75b1e4155520b1da175b686ffc), [`640eccd5dd`](https://github.com/medusajs/medusa/commit/640eccd5ddbb163e0f987ce6c772f1129c2e2632), [`339a946f38`](https://github.com/medusajs/medusa/commit/339a946f389033c21e05338f9dbf07d88e140533), [`ac829fc67f`](https://github.com/medusajs/medusa/commit/ac829fc67f7495b08f28e55923c59f0fd6320311), [`d9d5afc3cf`](https://github.com/medusajs/medusa/commit/d9d5afc3cfc29221d0e65bff7b78474a8fb8f31f), [`c3c4f49fc2`](https://github.com/medusajs/medusa/commit/c3c4f49fc2126f950e69e291ca939ca88a15afd3), [`9288f53327`](https://github.com/medusajs/medusa/commit/9288f53327b8ce617af92ed8d14d9459cbfeb13c), [`0d46abf0ff`](https://github.com/medusajs/medusa/commit/0d46abf0ffa4c5e03bf7d2a9cdf1db828a76bea8), [`fafde4f54d`](https://github.com/medusajs/medusa/commit/fafde4f54d3ef75a7d382e6cbf94e38b3deae99b), [`0c705d7bd4`](https://github.com/medusajs/medusa/commit/0c705d7bd41a768c48017ae95b3c8414d96c6acb), [`1d91b7429b`](https://github.com/medusajs/medusa/commit/1d91b7429beebd6f09d5027f7f7e1fe74ce3a8ff), [`1ed5f918c3`](https://github.com/medusajs/medusa/commit/1ed5f918c31794a70aca4a4e4cd83cf456593baa), [`c20eb15cd9`](https://github.com/medusajs/medusa/commit/c20eb15cd9b1bd90c8d01f68eca6f0f181cd902d), [`e5945479e0`](https://github.com/medusajs/medusa/commit/e5945479e091d9560ae3e7240306a31031ef4584), [`f5c2256286`](https://github.com/medusajs/medusa/commit/f5c22562867f412040f8bc6c55ab5de3a3735e62), [`62a7bcc30c`](https://github.com/medusajs/medusa/commit/62a7bcc30cbc7b234b2b51d7858439951a84edeb), [`8f8a4f9b13`](https://github.com/medusajs/medusa/commit/8f8a4f9b1353087d98f6cc75346d43a7f49901a8), [`ce39b9b66e`](https://github.com/medusajs/medusa/commit/ce39b9b66e8c277ec0691ea6d0a950003be09cc1), [`a6a4b3f01a`](https://github.com/medusajs/medusa/commit/a6a4b3f01a6d2bd97b1580c59134279a1b033a5d), [`4d51f095b3`](https://github.com/medusajs/medusa/commit/4d51f095b3f98f468cefb760512563f7b77bb9cf), [`56b0b45304`](https://github.com/medusajs/medusa/commit/56b0b4530401a6ec5aa155874d371e45bb388fe2), [`cc1b66842c`](https://github.com/medusajs/medusa/commit/cc1b66842cbb37c6eab84e2d8b74844c214f38d7), [`24fb102a56`](https://github.com/medusajs/medusa/commit/24fb102a564b1253d1f8b039bb1e435cc5312fbb)]:
- @medusajs/types@1.11.14
## 1.11.6
### Patch Changes
- [#6328](https://github.com/medusajs/medusa/pull/6328) [`3fd68d197`](https://github.com/medusajs/medusa/commit/3fd68d1979828e3cde6e028e9e6ab0d0fadcb77b) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(fulfillment): Initialize models work
- Updated dependencies [[`3fd68d197`](https://github.com/medusajs/medusa/commit/3fd68d1979828e3cde6e028e9e6ab0d0fadcb77b)]:
- @medusajs/types@1.11.13
## 1.11.5
### Patch Changes
- [#6319](https://github.com/medusajs/medusa/pull/6319) [`12054f5c0`](https://github.com/medusajs/medusa/commit/12054f5c01915899223ddc6da734151b31fbb23b) Thanks [@adrien2p](https://github.com/adrien2p)! - Feat/shipping method init
- [#6263](https://github.com/medusajs/medusa/pull/6263) [`45134e4d1`](https://github.com/medusajs/medusa/commit/45134e4d11cfcdc08dbd10aae687bfbe9e848ab9) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Workflows passing MedusaContext as argument
- [#6218](https://github.com/medusajs/medusa/pull/6218) [`884428a1b`](https://github.com/medusajs/medusa/commit/884428a1b573e499d7659aefed639bf797147428) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Feat: Event Aggregator
- [#6314](https://github.com/medusajs/medusa/pull/6314) [`882aa549b`](https://github.com/medusajs/medusa/commit/882aa549bdcc6f378934eab2a7c485df354f46aa) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(utils): return array or single entity based on input for modules created by internal module service factory
- Updated dependencies [[`12054f5c0`](https://github.com/medusajs/medusa/commit/12054f5c01915899223ddc6da734151b31fbb23b), [`3db2f95e65`](https://github.com/medusajs/medusa/commit/3db2f95e65909f4fff432990b48be74509052e83), [`96ba49329`](https://github.com/medusajs/medusa/commit/96ba49329b6b05922c90f0c55f16455cb40aa5ca), [`45134e4d1`](https://github.com/medusajs/medusa/commit/45134e4d11cfcdc08dbd10aae687bfbe9e848ab9), [`82c728bec`](https://github.com/medusajs/medusa/commit/82c728bec7232a245a67dca0b01b28572ebea75d), [`e749dd653`](https://github.com/medusajs/medusa/commit/e749dd653c755bfc3632b134d32c15ceaee0a852), [`884428a1b`](https://github.com/medusajs/medusa/commit/884428a1b573e499d7659aefed639bf797147428), [`d1c18a309`](https://github.com/medusajs/medusa/commit/d1c18a3090d71c68a98343fdbb53516f416504c5)]:
- @medusajs/types@1.11.12
## 1.11.4
### Patch Changes
- [#6094](https://github.com/medusajs/medusa/pull/6094) [`68ddd866a5`](https://github.com/medusajs/medusa/commit/68ddd866a5ff9414e2db5b80d75acc5e81948540) Thanks [@riqwan](https://github.com/riqwan)! - feat(utils,types): add registerUsages for promotion's computed actions
- [#6062](https://github.com/medusajs/medusa/pull/6062) [`72bc52231c`](https://github.com/medusajs/medusa/commit/72bc52231ca3a72fa6d197a248fe07a938ed0d85) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(utils): Update base repository to infer primary keys and support composite
- [#6152](https://github.com/medusajs/medusa/pull/6152) [`99045848f`](https://github.com/medusajs/medusa/commit/99045848fd3e863359c7878d9bc05271ed083a0e) Thanks [@riqwan](https://github.com/riqwan)! - feat(medusa,types,core-flows,utils): added delete endpoints for campaigns and promotions
- [#6021](https://github.com/medusajs/medusa/pull/6021) [`a9b4214503`](https://github.com/medusajs/medusa/commit/a9b42145032ee88aa922a11fe03e777b140c68f4) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(utils, product): Attempt to simplify module scripts export
- [#6127](https://github.com/medusajs/medusa/pull/6127) [`5e655dd59`](https://github.com/medusajs/medusa/commit/5e655dd59bda4ffface28db38021ba71cae6de10) Thanks [@adrien2p](https://github.com/adrien2p)! - chore: Hide repository creation if they are not custom + add upsert support by default
- [#6050](https://github.com/medusajs/medusa/pull/6050) [`b782d3bcb7`](https://github.com/medusajs/medusa/commit/b782d3bcb7e8088a962584b9a55200dd29c2161c) Thanks [@riqwan](https://github.com/riqwan)! - feat(utils,types): added item/shipping adjustments for order/items/shipping_methods
- [#6128](https://github.com/medusajs/medusa/pull/6128) [`302323916`](https://github.com/medusajs/medusa/commit/302323916b6d8eaf571cd59b5fc92a913af207de) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Modules: Workflows Engine in-memory and Redis
- [#6130](https://github.com/medusajs/medusa/pull/6130) [`da5cc4cf7`](https://github.com/medusajs/medusa/commit/da5cc4cf7f7f0ef40d409704a95b025ce95477f4) Thanks [@riqwan](https://github.com/riqwan)! - feat(core-flows,medusa,utils): promotion and campaign create/update endpoint
- [#6161](https://github.com/medusajs/medusa/pull/6161) [`daecd82a7`](https://github.com/medusajs/medusa/commit/daecd82a7cdf7315599f464999690414c20d6748) Thanks [@riqwan](https://github.com/riqwan)! - feat(utils): update joiner config for campaigns
- [#6112](https://github.com/medusajs/medusa/pull/6112) [`06b33a9b4`](https://github.com/medusajs/medusa/commit/06b33a9b4525b77b1b14b35b973209700945654e) Thanks [@olivermrbl](https://github.com/olivermrbl)! - feat(cart): Line item adjustments
- [#6035](https://github.com/medusajs/medusa/pull/6035) [`b6ac768698`](https://github.com/medusajs/medusa/commit/b6ac768698a3b49d0162cb49e628386f3352d034) Thanks [@adrien2p](https://github.com/adrien2p)! - chore: Attempt to abstract the modules repository
- [#6087](https://github.com/medusajs/medusa/pull/6087) [`130c641e5c`](https://github.com/medusajs/medusa/commit/130c641e5c91cf831de64fb87aebbfdc4d23530d) Thanks [@adrien2p](https://github.com/adrien2p)! - chore: Abstract module services
- [#6063](https://github.com/medusajs/medusa/pull/6063) [`fade8ea7bf`](https://github.com/medusajs/medusa/commit/fade8ea7bf560343ecbde116d226ac44053cdb8e) Thanks [@riqwan](https://github.com/riqwan)! - feat(utils,types): campaigns and campaign budgets + services CRUD
- Updated dependencies [[`68ddd866a5`](https://github.com/medusajs/medusa/commit/68ddd866a5ff9414e2db5b80d75acc5e81948540), [`99045848f`](https://github.com/medusajs/medusa/commit/99045848fd3e863359c7878d9bc05271ed083a0e), [`af7af7374`](https://github.com/medusajs/medusa/commit/af7af737455daa0f330840a9678e6339e519dfe6), [`fc6b1772a7`](https://github.com/medusajs/medusa/commit/fc6b1772a71582bb48602c5cac7b2297e9d267a9), [`d85fee42e`](https://github.com/medusajs/medusa/commit/d85fee42ee7f661310584dfee5741d6c53b989bb), [`5e655dd59`](https://github.com/medusajs/medusa/commit/5e655dd59bda4ffface28db38021ba71cae6de10), [`b132ff7669`](https://github.com/medusajs/medusa/commit/b132ff76693148b3a06373c168e8dd5e02970757), [`e28fa7fbdf`](https://github.com/medusajs/medusa/commit/e28fa7fbdf45c5b1fa19848db731132a0bf1757d), [`a12c28b7d5`](https://github.com/medusajs/medusa/commit/a12c28b7d5faed733bebbb4963dff50b9c8a33bc), [`b782d3bcb7`](https://github.com/medusajs/medusa/commit/b782d3bcb7e8088a962584b9a55200dd29c2161c), [`7f7cb2a263`](https://github.com/medusajs/medusa/commit/7f7cb2a263c26baf540b05a40ab3732ffeb0c73c), [`302323916`](https://github.com/medusajs/medusa/commit/302323916b6d8eaf571cd59b5fc92a913af207de), [`ce81cade88`](https://github.com/medusajs/medusa/commit/ce81cade887659cefe9638e3c1c2807378191c62), [`fd78f5e24`](https://github.com/medusajs/medusa/commit/fd78f5e24263f5e158c3b7d11fbf0a4436e9c17a), [`192bc336cc`](https://github.com/medusajs/medusa/commit/192bc336cc2b6ec3820d94524c046dcd3c4ac7d9), [`06b33a9b4`](https://github.com/medusajs/medusa/commit/06b33a9b4525b77b1b14b35b973209700945654e), [`130c641e5c`](https://github.com/medusajs/medusa/commit/130c641e5c91cf831de64fb87aebbfdc4d23530d), [`fade8ea7bf`](https://github.com/medusajs/medusa/commit/fade8ea7bf560343ecbde116d226ac44053cdb8e), [`8472460f53`](https://github.com/medusajs/medusa/commit/8472460f533322cc4535199aa768ac163021bc79), [`68d8daccd`](https://github.com/medusajs/medusa/commit/68d8daccd2a8508a13e211130e49017198b51fab)]:
- @medusajs/types@1.11.11
## 1.11.3
### Patch Changes
- [#5957](https://github.com/medusajs/medusa/pull/5957) [`42cc8ae3f`](https://github.com/medusajs/medusa/commit/42cc8ae3f89ed7d642e51654d1a3cca011f13155) Thanks [@riqwan](https://github.com/riqwan)! - feat(types,utils): added promotion create with rules
- [#5869](https://github.com/medusajs/medusa/pull/5869) [`45996d58a2`](https://github.com/medusajs/medusa/commit/45996d58a2665d72335faad11bea958f8da74195) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(medusa, interfaces, utils, webshiper): Uniformise class checks
- [#5728](https://github.com/medusajs/medusa/pull/5728) [`9cc787cac4`](https://github.com/medusajs/medusa/commit/9cc787cac4bf1c5d8edf1c4b548bb3205100e822) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(workflows-sdk): Configurable retries upon step creation
- [#5858](https://github.com/medusajs/medusa/pull/5858) [`355075097`](https://github.com/medusajs/medusa/commit/3550750975a0c9359fd887929377733606ef03af) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(pricing, types): add soft deletion for money-amounts
feat(utils): cascade soft delete across 1:1 relationships
- [#6020](https://github.com/medusajs/medusa/pull/6020) [`fbee006e5`](https://github.com/medusajs/medusa/commit/fbee006e512ef2d56ffb23eeabad8b51b56be285) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(medusa, orchestration, utils, workflows-sdk): add transaction options support and cleanup
- [#5929](https://github.com/medusajs/medusa/pull/5929) [`c41f3002f`](https://github.com/medusajs/medusa/commit/c41f3002f3118b1f195c5c822fe0f400091d115b) Thanks [@adrien2p](https://github.com/adrien2p)! - Fix/utils object from string path
- [#5985](https://github.com/medusajs/medusa/pull/5985) [`d16d10619`](https://github.com/medusajs/medusa/commit/d16d10619dfbd3966a4709753de3d8cc37c6f2eb) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(medusa-test-utils, utils, link-modules, pricing, product): upgrade mikro-orm version
- [#5945](https://github.com/medusajs/medusa/pull/5945) [`890e76a5c`](https://github.com/medusajs/medusa/commit/890e76a5c53039576c42ca4d46af6f6977cdebd1) Thanks [@riqwan](https://github.com/riqwan)! - feat(types,utils): add promotions create with application method
- [#5810](https://github.com/medusajs/medusa/pull/5810) [`fe007d01b`](https://github.com/medusajs/medusa/commit/fe007d01bd827f0e09ee545e48cef18913540c68) Thanks [@fPolic](https://github.com/fPolic)! - feat: sales channel <> order link
- [#5459](https://github.com/medusajs/medusa/pull/5459) [`76332ca6c`](https://github.com/medusajs/medusa/commit/76332ca6c153a786acc07d3f06ff45c3b9346fd3) Thanks [@fPolic](https://github.com/fPolic)! - feat: SalesChannel <> Cart joiner config
- Updated dependencies [[`6d1e3cc02`](https://github.com/medusajs/medusa/commit/6d1e3cc0285ef157fd6486060e8b32c00c01aa80), [`42cc8ae3f`](https://github.com/medusajs/medusa/commit/42cc8ae3f89ed7d642e51654d1a3cca011f13155), [`355075097`](https://github.com/medusajs/medusa/commit/3550750975a0c9359fd887929377733606ef03af), [`dc46ee118`](https://github.com/medusajs/medusa/commit/dc46ee1189c3eb719355da6a1d701c14a77e4578), [`925feea04`](https://github.com/medusajs/medusa/commit/925feea04a8222285175c33577548e50516069a7), [`3f6d79961`](https://github.com/medusajs/medusa/commit/3f6d79961dec1c5eb8950f8eacd94a5d87a4acde), [`c1c470e6b`](https://github.com/medusajs/medusa/commit/c1c470e6b8646c5f0b4bca56a8e785f6c34e1fef), [`890e76a5c`](https://github.com/medusajs/medusa/commit/890e76a5c53039576c42ca4d46af6f6977cdebd1)]:
- @medusajs/types@1.11.10
## 1.11.2
### Patch Changes
- [#5752](https://github.com/medusajs/medusa/pull/5752) [`079f0da83`](https://github.com/medusajs/medusa/commit/079f0da83f482562bbb525807ee1a7e32993b4da) Thanks [@riqwan](https://github.com/riqwan)! - feat(core-flows,pricing,medusa,pricing,types,utils): Price List Prices can have their own rules
- [#5755](https://github.com/medusajs/medusa/pull/5755) [`8f25ed8a1`](https://github.com/medusajs/medusa/commit/8f25ed8a10fe23e9342dc3d03545546b4ad4d6da) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(link-modules, pricing, product, utils): Should be able to set some custom database config even in shared mode
- Updated dependencies [[`079f0da83`](https://github.com/medusajs/medusa/commit/079f0da83f482562bbb525807ee1a7e32993b4da)]:
- @medusajs/types@1.11.9
## 1.11.1
### Patch Changes
- [#5713](https://github.com/medusajs/medusa/pull/5713) [`18afe0b9a`](https://github.com/medusajs/medusa/commit/18afe0b9addb33ec2e3b285651b4eb1ef8065845) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(stock-location, inventory, medusa, types, utils): allow buildQuery to take null as an argument in order to prevent default pagination
- [#5732](https://github.com/medusajs/medusa/pull/5732) [`de8f74867`](https://github.com/medusajs/medusa/commit/de8f748674bfd19b3dbadb9695d9080aa91940de) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(link-modules, utils): remove limtis if primary key exists in filter for buildquery and no limit is present
- [#5536](https://github.com/medusajs/medusa/pull/5536) [`dc5750dd6`](https://github.com/medusajs/medusa/commit/dc5750dd665a91d35c0246ba83c7f90ec74907f4) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(medusa,types,workflows,utils,product,pricing): PricingModule Integration of PriceLists into Core
- Updated dependencies [[`a39ce125c`](https://github.com/medusajs/medusa/commit/a39ce125cc96f14732d5a6301313d2376484fa23), [`18afe0b9a`](https://github.com/medusajs/medusa/commit/18afe0b9addb33ec2e3b285651b4eb1ef8065845), [`6025c702f`](https://github.com/medusajs/medusa/commit/6025c702f37d43e18af32bd716f33410d95efd19), [`fc1ef29ed`](https://github.com/medusajs/medusa/commit/fc1ef29ed935e192f0943a2bf4b8fbb05ce6890d), [`0df1c7d42`](https://github.com/medusajs/medusa/commit/0df1c7d4273545bc717555611b9294a5c222e5ae), [`dc5750dd6`](https://github.com/medusajs/medusa/commit/dc5750dd665a91d35c0246ba83c7f90ec74907f4)]:
- @medusajs/types@1.11.8
## 1.11.0
### Minor Changes
- [#5603](https://github.com/medusajs/medusa/pull/5603) [`cedab5833`](https://github.com/medusajs/medusa/commit/cedab583395275444001f0268e4b9ccab9b2b262) Thanks [@riqwan](https://github.com/riqwan)! - feat(workflows,medusa,utils): add medusa v2 feature flag
### Patch Changes
- Updated dependencies [[`61aef4aaa`](https://github.com/medusajs/medusa/commit/61aef4aaa7295f178c61c9a891f03a0a48b15c81), [`1772e80ed`](https://github.com/medusajs/medusa/commit/1772e80ed1ecab27741be80204f5df92eaa3f2b4)]:
- @medusajs/types@1.11.7
## 1.10.5
### Patch Changes
- [`c39bf69a5`](https://github.com/medusajs/medusa/commit/c39bf69a5e5cae75d7fa12aa6022b10903557a32) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Add missing version bumps
- [#5496](https://github.com/medusajs/medusa/pull/5496) [`154c9b43b`](https://github.com/medusajs/medusa/commit/154c9b43bde1fdff562aba9da8a79af2660b29b3) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(medusa, modules-sdk, types, utils): Re work modules loading and remove legacy functions
- Updated dependencies [[`91615f9c4`](https://github.com/medusajs/medusa/commit/91615f9c459a2d8cb842561c5edb335680d30298), [`154c9b43b`](https://github.com/medusajs/medusa/commit/154c9b43bde1fdff562aba9da8a79af2660b29b3)]:
- @medusajs/types@1.11.6
## 1.10.4
### Patch Changes
- [#5294](https://github.com/medusajs/medusa/pull/5294) [`378ca1b36`](https://github.com/medusajs/medusa/commit/378ca1b36e909a67e39c69ea5ca94ec58a345878) Thanks [@riqwan](https://github.com/riqwan)! - feat(pricing,types,utils): Move calculate pricing query to a repository + rule type validation
- [#5337](https://github.com/medusajs/medusa/pull/5337) [`e47461d95`](https://github.com/medusajs/medusa/commit/e47461d95caecf3a447ee9fa0b0950340b93f282) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(orchestration): prevent throwing on already defined workflow
- [#5301](https://github.com/medusajs/medusa/pull/5301) [`66413d094`](https://github.com/medusajs/medusa/commit/66413d094e916debbdb74b68800c96ca2c9302c9) Thanks [@srindom](https://github.com/srindom)! - fix: move create inventory workflow to @medusajs/workflows
- Updated dependencies [[`378ca1b36`](https://github.com/medusajs/medusa/commit/378ca1b36e909a67e39c69ea5ca94ec58a345878), [`69cf7215f`](https://github.com/medusajs/medusa/commit/69cf7215f1f730ffb332129e65211470be1f88f1), [`453297f52`](https://github.com/medusajs/medusa/commit/453297f525bd9f3aaa95bf0b28ff6cd31e6696b4), [`66413d094`](https://github.com/medusajs/medusa/commit/66413d094e916debbdb74b68800c96ca2c9302c9)]:
- @medusajs/types@1.11.4
## 1.10.3
### Patch Changes
- [#5216](https://github.com/medusajs/medusa/pull/5216) [`eeceec791`](https://github.com/medusajs/medusa/commit/eeceec791c141996cf7fd06555afb6e738b52840) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(utils): Convert remote query object to string array of fields and relations from GQL schema
- [#5242](https://github.com/medusajs/medusa/pull/5242) [`130cbc1f4`](https://github.com/medusajs/medusa/commit/130cbc1f437af211b6d05f80128d90138abcd38d) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Modules exporting schema with entities and fields
- [#5213](https://github.com/medusajs/medusa/pull/5213) [`cb569c2df`](https://github.com/medusajs/medusa/commit/cb569c2dfe2d83e1ff72a49f2331450a83b73325) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(utils): Utils to create an object select, relation from a string[]
- Updated dependencies [[`130cbc1f4`](https://github.com/medusajs/medusa/commit/130cbc1f437af211b6d05f80128d90138abcd38d), [`c5703a476`](https://github.com/medusajs/medusa/commit/c5703a4765a55da697885438cf3089d923669f21)]:
- @medusajs/types@1.11.3
## 1.10.2
### Patch Changes
- [#5161](https://github.com/medusajs/medusa/pull/5161) [`cc4169a94`](https://github.com/medusajs/medusa/commit/cc4169a94c7c5f5bf4d04f7b6e815b409a0a8192) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(utils): Provide an utils that allows to convert an array of fields to a complete remote query object
- [#5214](https://github.com/medusajs/medusa/pull/5214) [`1e7db5a5c`](https://github.com/medusajs/medusa/commit/1e7db5a5cb7c955e72c52e64df8a16b1607eef70) Thanks [@riqwan](https://github.com/riqwan)! - feat(types,pricing,utils): Exact match based on context + fallback on rule priority if not
- Updated dependencies [[`1e7db5a5c`](https://github.com/medusajs/medusa/commit/1e7db5a5cb7c955e72c52e64df8a16b1607eef70)]:
- @medusajs/types@1.11.2
## 1.10.1
### Patch Changes
- [#4961](https://github.com/medusajs/medusa/pull/4961) [`6273b4b16`](https://github.com/medusajs/medusa/commit/6273b4b160493463e1199e5db4e9cfa4cff6fbe4) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(utils, module-sdk, medusa, types): Extract pg connection utils to utils package
- Updated dependencies [[`4fa675ec2`](https://github.com/medusajs/medusa/commit/4fa675ec25b3d6fccd881c4f5a5b91f0e9e13e82), [`6273b4b16`](https://github.com/medusajs/medusa/commit/6273b4b160493463e1199e5db4e9cfa4cff6fbe4), [`30863fee5`](https://github.com/medusajs/medusa/commit/30863fee529ed035f161c749fda3cd64fa48efb1), [`3d68be2b6`](https://github.com/medusajs/medusa/commit/3d68be2b6b93ae928f5c955e102ebdf2c34fb364), [`a87d07655`](https://github.com/medusajs/medusa/commit/a87d07655bd8a1da8b90feb739daddd09295f724), [`edf90eecb`](https://github.com/medusajs/medusa/commit/edf90eecb487f6e031f2e2d0899de5ca2504cb12), [`107aaa371`](https://github.com/medusajs/medusa/commit/107aaa371c444843874d125bf8bd493ef89f5756), [`834da5c41`](https://github.com/medusajs/medusa/commit/834da5c41a7c043373f72239b6fdbf7815d9b4aa)]:
- @medusajs/types@1.11.1
## 1.10.0
### Minor Changes
- [#4695](https://github.com/medusajs/medusa/pull/4695) [`4d16acf5f`](https://github.com/medusajs/medusa/commit/4d16acf5f096b5656b645f510f9c971e7c2dc9ef) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Medusa App Loader
- [#4695](https://github.com/medusajs/medusa/pull/4695) [`4d16acf5f`](https://github.com/medusajs/medusa/commit/4d16acf5f096b5656b645f510f9c971e7c2dc9ef) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - introduce @medusajs/link-modules
### Patch Changes
- [#4860](https://github.com/medusajs/medusa/pull/4860) [`460161a69`](https://github.com/medusajs/medusa/commit/460161a69f22cf6d561952e92e7d9b56912113e6) Thanks [@riqwan](https://github.com/riqwan)! - feat(pricing, types, utils, medusa-sdk): Pricing Module Setup + Currency service
- [#4909](https://github.com/medusajs/medusa/pull/4909) [`fcb6b4f51`](https://github.com/medusajs/medusa/commit/fcb6b4f510dba2757570625acb5da9476b7544fd) Thanks [@riqwan](https://github.com/riqwan)! - feat(pricing, utils, types): adds money amount to pricing module
- [#4915](https://github.com/medusajs/medusa/pull/4915) [`87bade096`](https://github.com/medusajs/medusa/commit/87bade096e3d536f29ddc57dbc4c04e5d7a46e4b) Thanks [@riqwan](https://github.com/riqwan)! - fix(utils, product, pricing, link-modules): add missing dependencies for utils + fix migration path issue
- Updated dependencies [[`460161a69`](https://github.com/medusajs/medusa/commit/460161a69f22cf6d561952e92e7d9b56912113e6), [`fcb6b4f51`](https://github.com/medusajs/medusa/commit/fcb6b4f510dba2757570625acb5da9476b7544fd), [`66bd9a835`](https://github.com/medusajs/medusa/commit/66bd9a835c61b139af7051e5faf6c9de3c7134bb), [`4d16acf5f`](https://github.com/medusajs/medusa/commit/4d16acf5f096b5656b645f510f9c971e7c2dc9ef), [`4d16acf5f`](https://github.com/medusajs/medusa/commit/4d16acf5f096b5656b645f510f9c971e7c2dc9ef), [`4d16acf5f`](https://github.com/medusajs/medusa/commit/4d16acf5f096b5656b645f510f9c971e7c2dc9ef)]:
- @medusajs/types@1.11.0
## 1.9.7
### Patch Changes
- [#4825](https://github.com/medusajs/medusa/pull/4825) [`c53fa6cd3`](https://github.com/medusajs/medusa/commit/c53fa6cd3be8b1af5625fc2db7871f9f69e761ad) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Fix mikro-orm connection loader
## 1.9.6
### Patch Changes
- [#4626](https://github.com/medusajs/medusa/pull/4626) [`3f3a84262`](https://github.com/medusajs/medusa/commit/3f3a84262ce9cbd911923278a54e301fbe9a4634) Thanks [@adrien2p](https://github.com/adrien2p)! - [WIP] feat(types, product, utils, medusa): Include shared connection for modules
- [#4733](https://github.com/medusajs/medusa/pull/4733) [`30ce35b16`](https://github.com/medusajs/medusa/commit/30ce35b163afa25f4e1d8d1bd392f401a3b413df) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app, utils, medusa-cli): add database options + remove util from `@medusajs/utils`
## 1.9.5
### Patch Changes
- [#4701](https://github.com/medusajs/medusa/pull/4701) [`5c60aad17`](https://github.com/medusajs/medusa/commit/5c60aad177a99574ffff5ebdc02ce9dc86ef9af9) Thanks [@olivermrbl](https://github.com/olivermrbl)! - feat(medusa, utils): Allow object feature flags
- [#4631](https://github.com/medusajs/medusa/pull/4631) [`4073b7313`](https://github.com/medusajs/medusa/commit/4073b73130c874dc7d2240726224a01b7b19b1a1) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(product): Move mikro orm utils to the utils package
## 1.9.4
### Patch Changes
- [#4623](https://github.com/medusajs/medusa/pull/4623) [`107ae23a3`](https://github.com/medusajs/medusa/commit/107ae23a3f41ef0d676e9d03f53dafc7c1af6118) Thanks [@riqwan](https://github.com/riqwan)! - fix(utils, types, medusa-plugin-meilisearch, medusa-plugin-algolia): move SoftDeletableFilterKey, variantKeys, indexTypes from types to utils
## 1.9.3
### Patch Changes
- [#4535](https://github.com/medusajs/medusa/pull/4535) [`131477faf`](https://github.com/medusajs/medusa/commit/131477faf0409c49d4aacf26ea591e33b2fa22fd) Thanks [@riqwan](https://github.com/riqwan)! - feat(product,types,utils): Add tags, types, categories, collection and options CRUD to product module services
## 1.9.2
### Patch Changes
- [#4098](https://github.com/medusajs/medusa/pull/4098) [`499c3478c`](https://github.com/medusajs/medusa/commit/499c3478c910c8b922a15cc6f4d9fbad122a347f) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - feat: Remote Joiner
- [#4389](https://github.com/medusajs/medusa/pull/4389) [`9dcdc0041`](https://github.com/medusajs/medusa/commit/9dcdc0041a2b08cc0723343dd8d9127d9977b086) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(medusa, utils): fix the way selects are consumed alongside the relations
- [#4398](https://github.com/medusajs/medusa/pull/4398) [`9760d4a96`](https://github.com/medusajs/medusa/commit/9760d4a96c27f6f89a8c3f3b6e73b17547f97f2a) Thanks [@riqwan](https://github.com/riqwan)! - feat(medusa, utils): improve devx for core entity customizations
## 1.9.1
### Patch Changes
- [#4273](https://github.com/medusajs/medusa/pull/4273) [`f98ba5bde`](https://github.com/medusajs/medusa/commit/f98ba5bde83ba785eead31b0c9eb9f135d664178) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app,medusa-cli): Allow clearing project
- [#4161](https://github.com/medusajs/medusa/pull/4161) [`14c0f62f8`](https://github.com/medusajs/medusa/commit/14c0f62f84704a4c87beff3daaff60a52f5c88b8) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(product): Experimental product module
## 1.9.0
### Minor Changes
- [#4146](https://github.com/medusajs/medusa/pull/4146) [`db4199530`](https://github.com/medusajs/medusa/commit/db419953075e0907b8c4d27ab5188e9bd3e3d72b) Thanks [@fPolic](https://github.com/fPolic)! - chore(medusa, utils, inventory, stock-location): clear deps in the utils package
### Patch Changes
- [#4026](https://github.com/medusajs/medusa/pull/4026) [`a91987fab`](https://github.com/medusajs/medusa/commit/a91987fab33745f9864eab21bd1c27e8e3e24571) Thanks [@olivermrbl](https://github.com/olivermrbl)! - feat(medusa): Remove sqlite support
- [#4148](https://github.com/medusajs/medusa/pull/4148) [`c0e527d6e`](https://github.com/medusajs/medusa/commit/c0e527d6e0a67d0c53577a0b9c3d16ee8dc5740f) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(medusa): Align build query utils
## 1.8.5
### Patch Changes
- [#4046](https://github.com/medusajs/medusa/pull/4046) [`cdbac2c84`](https://github.com/medusajs/medusa/commit/cdbac2c8403a3c15c0e11993f6b7dab268fa5c08) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(medusa, medusa-utils): Add support for multiple where condition on the same column
- [#4072](https://github.com/medusajs/medusa/pull/4072) [`6511959e2`](https://github.com/medusajs/medusa/commit/6511959e23177f3b4831915db0e8e788bc9047fa) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Revert to official Typeorm package
## 1.8.4
### Patch Changes
- [#4032](https://github.com/medusajs/medusa/pull/4032) [`1ea57c3a6`](https://github.com/medusajs/medusa/commit/1ea57c3a69a5377a8dd0821df819743ded4a222b) Thanks [@pevey](https://github.com/pevey)! - update class-validator to 0.14.0 in utils package
## 1.8.3
### Patch Changes
- [#4002](https://github.com/medusajs/medusa/pull/4002) [`0e488e71b`](https://github.com/medusajs/medusa/commit/0e488e71b186f7d08b18c4c6ba409ef3cadb8152) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(medusa, event-bus-redis, event-bus-local): Revert retrieveSubscribers as the wildcard prevent us from filtering
- [#3981](https://github.com/medusajs/medusa/pull/3981) [`d539c6fee`](https://github.com/medusajs/medusa/commit/d539c6feeba8ee431f9a655b6cd4e9102cba2b25) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Bump Typeorm to Medusa fork
## 1.8.2
### Patch Changes
- [#3835](https://github.com/medusajs/medusa/pull/3835) [`af710f1b4`](https://github.com/medusajs/medusa/commit/af710f1b48a4545a5064029a557013af34c4c100) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(medusa): Bulk create variant and pass transaction to the inventory service context methods
- [#3905](https://github.com/medusajs/medusa/pull/3905) [`491566df6`](https://github.com/medusajs/medusa/commit/491566df6b7ced35f655f810961422945e10ecd0) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(medusa,utils): Searching indexing product subscriber
## 1.8.1
### Patch Changes
- [#3778](https://github.com/medusajs/medusa/pull/3778) [`654a54622`](https://github.com/medusajs/medusa/commit/654a54622303139e7180538bd686630ad9a46cfd) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Bump Typeorm
- [#3738](https://github.com/medusajs/medusa/pull/3738) [`abdb74d99`](https://github.com/medusajs/medusa/commit/abdb74d997f49f994bff49787a396179982843b0) Thanks [@riqwan](https://github.com/riqwan)! - chore(medusa, utils): rename buildLegacyFieldsListFrom to objectToStringPath
## 1.8.0
### Patch Changes
- [#3685](https://github.com/medusajs/medusa/pull/3685) [`8ddb3952c`](https://github.com/medusajs/medusa/commit/8ddb3952c045e6c05c8d0f6922f0d4ba30cf3bd4) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Fix RC package versions
- [#3688](https://github.com/medusajs/medusa/pull/3688) [`a0c919a8d`](https://github.com/medusajs/medusa/commit/a0c919a8d01ca5edf62336de48e9a112e3822f38) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore(medusa-cli): Add missing utils dep
- [#3510](https://github.com/medusajs/medusa/pull/3510) [`74bc4b16a`](https://github.com/medusajs/medusa/commit/74bc4b16a07f78668003ca930bf2a0d928897ceb) Thanks [@olivermrbl](https://github.com/olivermrbl)! - feat(medusa-plugin-algolia): Revamp Algolia search plugin
- [#3531](https://github.com/medusajs/medusa/pull/3531) [`4e9d257d3`](https://github.com/medusajs/medusa/commit/4e9d257d3bf76703ef5be8ca054cc9f0f7339def) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Remove dependency on @medusajs/medusa from Inventory and Stock-Location Modules
## 0.0.2-rc.2
### Patch Changes
- [#3688](https://github.com/medusajs/medusa/pull/3688) [`a0c919a8d`](https://github.com/medusajs/medusa/commit/a0c919a8d01ca5edf62336de48e9a112e3822f38) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore(medusa-cli): Add missing utils dep
## 0.0.2-rc.1
### Patch Changes
- chore: Fix RC package versions
## 0.0.2-rc.0
### Patch Changes
- [#3510](https://github.com/medusajs/medusa/pull/3510) [`74bc4b16a`](https://github.com/medusajs/medusa/commit/74bc4b16a07f78668003ca930bf2a0d928897ceb) Thanks [@olivermrbl](https://github.com/olivermrbl)! - feat(medusa-plugin-algolia): Revamp Algolia search plugin
- [#3531](https://github.com/medusajs/medusa/pull/3531) [`4e9d257d3`](https://github.com/medusajs/medusa/commit/4e9d257d3bf76703ef5be8ca054cc9f0f7339def) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Remove dependency on @medusajs/medusa from Inventory and Stock-Location Modules
+13
View File
@@ -0,0 +1,13 @@
module.exports = {
transform: {
"^.+\\.[jt]s?$": [
"ts-jest",
{
tsconfig: "tsconfig.spec.json",
isolatedModules: true,
},
],
},
testEnvironment: `node`,
moduleFileExtensions: [`js`, `ts`],
}
+49
View File
@@ -0,0 +1,49 @@
{
"name": "@medusajs/utils",
"version": "1.11.9",
"description": "Medusa utilities functions shared by Medusa core and Modules",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa",
"directory": "packages/utils"
},
"engines": {
"node": ">=16"
},
"publishConfig": {
"access": "public"
},
"files": [
"dist"
],
"author": "Medusa",
"license": "MIT",
"devDependencies": {
"@types/express": "^4.17.17",
"cross-env": "^5.2.1",
"express": "^4.18.2",
"jest": "^29.6.3",
"rimraf": "^5.0.1",
"ts-jest": "^29.1.1",
"typescript": "^5.1.6"
},
"dependencies": {
"@medusajs/types": "^1.11.16",
"@mikro-orm/core": "5.9.7",
"@mikro-orm/migrations": "5.9.7",
"@mikro-orm/postgresql": "5.9.7",
"awilix": "^8.0.1",
"bignumber.js": "^9.1.2",
"knex": "2.4.2",
"ulid": "^2.3.0"
},
"scripts": {
"prepublishOnly": "cross-env NODE_ENV=production tsc --build",
"build": "rimraf dist && tsc --build",
"watch": "tsc --build --watch",
"test": "jest --silent --bail --maxWorkers=50% --forceExit --testPathIgnorePatterns='/integration-tests/' -- src/**/__tests__/**/*.ts",
"test:integration": "jest --silent --bail --maxWorkers=50% --forceExit -- src/**/integration-tests/__tests__/**/*.ts"
}
}
@@ -0,0 +1,15 @@
/**
* @enum
*
* The API key's type.
*/
export enum ApiKeyType {
/**
* Publishable key that is tied to eg. a sales channel
*/
PUBLISHABLE = "publishable",
/**
* Secret key that allows access to the admin API
*/
SECRET = "secret",
}
+1
View File
@@ -0,0 +1 @@
export * from "./api-key-type"
@@ -0,0 +1,65 @@
import { AuthProviderScope, AuthenticationResponse } from "@medusajs/types"
import { MedusaError } from "../common"
export abstract class AbstractAuthModuleProvider {
public static PROVIDER: string
public static DISPLAY_NAME: string
protected readonly container_: any
protected scopeConfig_: AuthProviderScope
protected scope_: string
private readonly scopes_: Record<string, AuthProviderScope>
public get provider() {
return (this.constructor as typeof AbstractAuthModuleProvider).PROVIDER
}
public get displayName() {
return (this.constructor as typeof AbstractAuthModuleProvider).DISPLAY_NAME
}
protected constructor(
{ scopes },
config: { provider: string; displayName: string }
) {
this.container_ = arguments[0]
this.scopes_ = scopes
;(this.constructor as typeof AbstractAuthModuleProvider).PROVIDER ??=
config.provider
;(this.constructor as typeof AbstractAuthModuleProvider).DISPLAY_NAME ??=
config.displayName
}
private validateScope(scope) {
if (!this.scopes_[scope]) {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
`Scope "${scope}" is not valid for provider ${this.provider}`
)
}
}
public withScope(scope: string) {
this.validateScope(scope)
const cloned = new (this.constructor as any)(this.container_)
cloned.scope_ = scope
cloned.scopeConfig_ = this.scopes_[scope]
return cloned
}
abstract authenticate(
data: Record<string, unknown>
): Promise<AuthenticationResponse>
public validateCallback(
data: Record<string, unknown>
): Promise<AuthenticationResponse> {
throw new Error(
`Callback authentication not implemented for provider ${this.provider}`
)
}
}
+1
View File
@@ -0,0 +1 @@
export * from "./abstract-auth-provider"
+17
View File
@@ -0,0 +1,17 @@
export * as DALUtils from "./dal"
export * as DecoratorUtils from "./decorators"
export * as DefaultsUtils from "./defaults"
export * as EventBusUtils from "./event-bus"
export * as FeatureFlagUtils from "./feature-flags"
export * as FulfillmentUtils from "./fulfillment"
export * as ModulesSdkUtils from "./modules-sdk"
export * as OrchestrationUtils from "./orchestration"
export * as OrderUtils from "./order"
export * as ProductUtils from "./product"
export * as PromotionUtils from "./promotion"
export * as SearchUtils from "./search"
export * as ShippingProfileUtils from "./shipping"
export * as UserUtils from "./user"
export * as InventoryUtils from "./inventory"
export * as LinkUtils from "./link"
export * as ApiKeyUtils from "./api-key"
@@ -0,0 +1,18 @@
import { arrayIntersection } from "../array-intersection"
describe("arrayIntersection", function () {
it("should return the intersection between two arrays", function () {
const output = arrayIntersection(["foo", "bar"], ["bar", "baz"])
expect(output).toEqual(["bar"])
})
it("should return an empty array if there is no intersection", function () {
const output = arrayIntersection(["bar", "baz"], ["foo", "boo"])
expect(output).toEqual([])
})
it("should return an all items when the arrays are equivalent", function () {
const output = arrayIntersection(["bar", "baz"], ["baz", "bar"])
expect(output).toEqual(["baz", "bar"])
})
})
@@ -0,0 +1,91 @@
import { buildSelects } from "../build-query"
describe("buildSelects", () => {
it("successfully build back select object shape to list", () => {
const q = buildSelects([
"order",
"order.items",
"order.swaps",
"order.swaps.additional_items",
"order.discounts",
"order.discounts.rule",
"order.claims",
"order.claims.additional_items",
"additional_items",
"additional_items.variant",
"return_order",
"return_order.items",
"return_order.shipping_method",
"return_order.shipping_method.tax_lines",
])
expect(q).toEqual({
order: {
items: true,
swaps: {
additional_items: true,
},
discounts: {
rule: true,
},
claims: {
additional_items: true,
},
},
additional_items: {
variant: true,
},
return_order: {
items: true,
shipping_method: {
tax_lines: true,
},
},
})
})
})
describe("buildSelects", () => {
it("successfully build back select object shape to list", () => {
const q = buildSelects([
"order",
"order.items",
"order.swaps",
"order.swaps.additional_items",
"order.discounts",
"order.discounts.rule",
"order.claims",
"order.claims.additional_items",
"additional_items",
"additional_items.variant",
"return_order",
"return_order.items",
"return_order.shipping_method",
"return_order.shipping_method.tax_lines",
])
expect(q).toEqual({
order: {
items: true,
swaps: {
additional_items: true,
},
discounts: {
rule: true,
},
claims: {
additional_items: true,
},
},
additional_items: {
variant: true,
},
return_order: {
items: true,
shipping_method: {
tax_lines: true,
},
},
})
})
})
@@ -0,0 +1,59 @@
import { convertItemResponseToUpdateRequest } from "../convert-item-response-to-update-request"
describe("convertItemResponseToUpdateRequest", function () {
it("should return true or false for different types of data", function () {
const expectations = [
{
item: {
id: "test-id",
test_attr: "test-name",
relation_object_with_params: {
id: "test-relation-object-id",
test_attr: "test-object-name",
},
relation_object_without_params: {
id: "test-relation-object-without-params-id",
},
relation_array: [
{
id: "test-relation-array-id",
test_attr: "test-array-name",
},
],
},
selects: [
"id",
"test_attr",
"relation_object_with_params.id",
"relation_object_with_params.test_attr",
"relation_object_without_params.id",
"relation_array.id",
"relation_array.test_attr",
],
relations: [
"relation_object_with_params",
"relation_object_without_params",
"relation_array",
],
output: {
id: "test-id",
test_attr: "test-name",
relation_object_with_params: { test_attr: "test-object-name" },
relation_array: [{ id: "test-relation-array-id" }],
relation_object_without_params_id:
"test-relation-object-without-params-id",
},
},
]
expectations.forEach((expectation) => {
const response = convertItemResponseToUpdateRequest(
expectation.item,
expectation.selects,
expectation.relations
)
expect(response).toEqual(expectation.output)
})
})
})
@@ -0,0 +1,92 @@
import { createPsqlIndexStatementHelper } from "../create-psql-index-helper"
describe("createPsqlIndexStatementHelper", function () {
it("should generate a simple index", function () {
const options = {
name: "index_name",
tableName: "table_name",
columns: "column_name",
}
const indexStatement = createPsqlIndexStatementHelper(options)
expect(indexStatement + "").toEqual(
`CREATE INDEX IF NOT EXISTS "${options.name}" ON "${options.tableName}" (${options.columns})`
)
})
it("should generate a simple index and auto compose its name", function () {
const options = {
tableName: "table_name",
columns: "column_name",
}
const indexStatement = createPsqlIndexStatementHelper(options)
expect(indexStatement + "").toEqual(
`CREATE INDEX IF NOT EXISTS "IDX_table_name_column_name" ON "${options.tableName}" (${options.columns})`
)
})
it("should generate a composite index", function () {
const options = {
name: "index_name",
tableName: "table_name",
columns: ["column_name_1", "column_name_2"],
}
const indexStatement = createPsqlIndexStatementHelper(options)
expect(indexStatement.expression).toEqual(
`CREATE INDEX IF NOT EXISTS "${options.name}" ON "${
options.tableName
}" (${options.columns.join(", ")})`
)
})
it("should generate an index with where clauses", function () {
const options = {
name: "index_name",
tableName: "table_name",
columns: ["column_name_1", "column_name_2"],
where: "column_name_1 IS NOT NULL",
}
const indexStatement = createPsqlIndexStatementHelper(options)
expect(indexStatement.expression).toEqual(
`CREATE INDEX IF NOT EXISTS "${options.name}" ON "${
options.tableName
}" (${options.columns.join(", ")}) WHERE ${options.where}`
)
})
it("should generate an index with where clauses and index type", function () {
const options = {
name: "index_name",
tableName: "table_name",
columns: ["column_name_1", "column_name_2"],
type: "GIN",
where: "column_name_1 IS NOT NULL",
}
const indexStatement = createPsqlIndexStatementHelper(options)
expect(indexStatement.toString()).toEqual(
`CREATE INDEX IF NOT EXISTS "${options.name}" ON "${
options.tableName
}" USING GIN (${options.columns.join(", ")}) WHERE ${options.where}`
)
})
it("should generate unique constraint", function () {
const options = {
tableName: "table_name",
columns: ["column_name_1", "column_name_2"],
unique: true,
where: "column_name_1 IS NOT NULL",
}
const indexStatement = createPsqlIndexStatementHelper(options)
expect(indexStatement.expression).toEqual(
`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_table_name_column_name_1_column_name_2_unique" ON "${
options.tableName
}" (${options.columns.join(", ")}) WHERE ${options.where}`
)
})
})
@@ -0,0 +1,74 @@
import { deepCopy } from "../deep-copy"
class TestA {
prop1: any
prop2: any
constructor(prop1: any, prop2: any) {
this.prop1 = prop1
this.prop2 = prop2
}
}
class TestWrapper {
prop1: any
prop2: any
constructor(prop1: any, prop2: any) {
this.prop1 = prop1
this.prop2 = prop2
}
factory() {
return new TestA(deepCopy(this.prop1), deepCopy(this.prop2))
}
}
class TestWrapperWithoutDeepCopy {
prop1: any
prop2: any
constructor(prop1: any, prop2: any) {
this.prop1 = prop1
this.prop2 = prop2
}
factory() {
return new TestA(this.prop1, this.prop2)
}
}
describe("deepCopy", () => {
it("should deep copy an object", () => {
const prop1 = {
prop1: 1,
}
const prop2 = {
prop1: 3,
}
const wrapperWithoutDeepCopy = new TestWrapperWithoutDeepCopy(prop1, prop2)
let factory1 = wrapperWithoutDeepCopy.factory()
let factory2 = wrapperWithoutDeepCopy.factory()
factory1.prop1.prop1 = 2
expect(wrapperWithoutDeepCopy.prop1).toEqual({ prop1: 2 })
expect(factory1.prop1).toEqual({ prop1: 2 })
expect(factory2.prop1).toEqual({ prop1: 2 })
prop1.prop1 = 4
prop2.prop1 = 4
const wrapper = new TestWrapper(prop1, prop2)
factory1 = wrapper.factory()
factory2 = wrapper.factory()
factory1.prop1.prop1 = 2
expect(wrapper.prop1).toEqual({ prop1: 4 })
expect(factory1.prop1).toEqual({ prop1: 2 })
expect(factory2.prop1).toEqual({ prop1: 4 })
})
})
@@ -0,0 +1,33 @@
import { deepEqualObj } from "../deep-equal-obj"
describe("deepEqualObj", function () {
it("should return true if objects are equal", function () {
const object1 = {
foo: "bar",
bar: "foo",
xar: { foo: "bar", wor: { bar: "foo", ror: ["test", "test1"] } },
}
const object2 = {
foo: "bar",
bar: "foo",
xar: { foo: "bar", wor: { bar: "foo", ror: ["test", "test1"] } },
}
expect(deepEqualObj(object1, object2)).toBe(true)
})
it("should return false if objects are not equal", function () {
const object1 = {
foo: "bar",
bar: "foo",
xar: { foo: "bar", wor: { bar: "foo", ror: ["test", "test1"] } },
}
const object2 = {
foo: "bar",
bar: "foo",
xar: { foo: "bar", wor: { bar: "foo", ror: ["test", "test1_"] } },
}
expect(deepEqualObj(object1, object2)).toBe(false)
})
})
@@ -0,0 +1,203 @@
import { deepFlatMap } from "../deep-flat-map"
describe("deepFlatMap", function () {
it("should return flat map of nested objects", function () {
const data = [
{
id: "sales_channel_1",
stock_locations: [
{
id: "location_1",
fulfillment_sets: [
{
id: "fset_1",
name: "Test 123",
service_zones: [
{
id: "zone_123",
shipping_options: [
{
id: "so_zone_123 1111",
calculated_price: {
calculated_amount: 3000,
},
},
{
id: "so_zone_123 22222",
calculated_price: {
calculated_amount: 6000,
},
},
],
},
{
id: "zone_567",
shipping_options: [
{
id: "zone 567 11111",
calculated_price: {
calculated_amount: 1230,
},
},
{
id: "zone 567 22222",
calculated_price: {
calculated_amount: 1230,
},
},
],
},
],
},
],
},
{
id: "location_2",
fulfillment_sets: [
{
id: "fset_2",
name: "fset name 2",
service_zones: [
{
id: "zone_ABC",
shipping_options: [
{
id: "zone_abc_unique",
calculated_price: {
calculated_amount: 70,
},
},
],
},
],
},
],
},
],
},
{
id: "sales_channel_2",
stock_locations: [
{
id: "location_5",
fulfillment_sets: [
{
id: "fset_aaa",
name: "Test aaa",
service_zones: [
{
id: "zone_aaa",
shipping_options: [
{
id: "so_zone_aaa aaaa",
calculated_price: {
calculated_amount: 500,
},
},
{
id: "so_zone_aaa bbbb",
calculated_price: {
calculated_amount: 12,
},
},
],
},
],
},
],
},
],
},
]
const result = deepFlatMap(
data,
"stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price",
({
root_,
stock_locations,
fulfillment_sets,
service_zones,
shipping_options,
calculated_price,
}) => {
return {
sales_channel_id: root_.id,
stock_location_id: stock_locations.id,
fulfillment_set_id: fulfillment_sets.id,
fulfillment_set_name: fulfillment_sets.name,
service_zone_id: service_zones.id,
shipping_option_id: shipping_options.id,
price: calculated_price.calculated_amount,
}
}
)
expect(result).toEqual([
{
sales_channel_id: "sales_channel_1",
stock_location_id: "location_1",
fulfillment_set_id: "fset_1",
fulfillment_set_name: "Test 123",
service_zone_id: "zone_123",
shipping_option_id: "so_zone_123 1111",
price: 3000,
},
{
sales_channel_id: "sales_channel_1",
stock_location_id: "location_1",
fulfillment_set_id: "fset_1",
fulfillment_set_name: "Test 123",
service_zone_id: "zone_123",
shipping_option_id: "so_zone_123 22222",
price: 6000,
},
{
sales_channel_id: "sales_channel_1",
stock_location_id: "location_1",
fulfillment_set_id: "fset_1",
fulfillment_set_name: "Test 123",
service_zone_id: "zone_567",
shipping_option_id: "zone 567 11111",
price: 1230,
},
{
sales_channel_id: "sales_channel_1",
stock_location_id: "location_1",
fulfillment_set_id: "fset_1",
fulfillment_set_name: "Test 123",
service_zone_id: "zone_567",
shipping_option_id: "zone 567 22222",
price: 1230,
},
{
sales_channel_id: "sales_channel_1",
stock_location_id: "location_2",
fulfillment_set_id: "fset_2",
fulfillment_set_name: "fset name 2",
service_zone_id: "zone_ABC",
shipping_option_id: "zone_abc_unique",
price: 70,
},
{
sales_channel_id: "sales_channel_2",
stock_location_id: "location_5",
fulfillment_set_id: "fset_aaa",
fulfillment_set_name: "Test aaa",
service_zone_id: "zone_aaa",
shipping_option_id: "so_zone_aaa aaaa",
price: 500,
},
{
sales_channel_id: "sales_channel_2",
stock_location_id: "location_5",
fulfillment_set_id: "fset_aaa",
fulfillment_set_name: "Test aaa",
service_zone_id: "zone_aaa",
shipping_option_id: "so_zone_aaa bbbb",
price: 12,
},
])
})
})
@@ -0,0 +1,20 @@
import { getDuplicates } from "../get-duplicates"
describe("getDuplicates", function () {
it("should return an empty array if there are no duplicates", function () {
const output = getDuplicates(["foo", "bar", "baz"])
expect(output).toHaveLength(0)
})
it("should return a singular duplicate", function () {
const output = getDuplicates(["foo", "bar", "baz", "baz", "baz"])
expect(output).toHaveLength(1)
expect(output[0]).toEqual("baz")
})
it("should return all duplicates in the array", function () {
const output = getDuplicates(["foo", "bar", "bar", "baz", "baz", "baz"])
expect(output).toHaveLength(2)
expect(output).toEqual(expect.arrayContaining(["baz", "bar"]))
})
})
@@ -0,0 +1,62 @@
import { getSelectsAndRelationsFromObjectArray } from "../get-selects-and-relations-from-object-array"
describe("getSelectsAndRelationsFromObjectArray", function () {
it("should return true or false for different types of data", function () {
const expectations = [
{
input: [
{
attr_string: "string",
attr_boolean: true,
attr_null: null,
attr_undefined: undefined,
attr_object: {
attr_string: "string",
attr_boolean: true,
attr_null: null,
attr_undefined: undefined,
},
attr_array: [
{
attr_object: {
attr_string: "string",
attr_boolean: true,
attr_null: null,
attr_undefined: undefined,
},
},
{
attr_object: {
attr_string: "string",
},
},
],
},
],
output: {
selects: [
"attr_string",
"attr_boolean",
"attr_null",
"attr_undefined",
"attr_object.attr_string",
"attr_object.attr_boolean",
"attr_object.attr_null",
"attr_object.attr_undefined",
"attr_array.attr_object.attr_string",
"attr_array.attr_object.attr_boolean",
"attr_array.attr_object.attr_null",
"attr_array.attr_object.attr_undefined",
],
relations: ["attr_object", "attr_array", "attr_array.attr_object"],
},
},
]
expectations.forEach((expectation) => {
expect(getSelectsAndRelationsFromObjectArray(expectation.input)).toEqual(
expectation.output
)
})
})
})
@@ -0,0 +1,50 @@
import { groupBy } from "../group-by"
const array = [
{
id: "test-id-1",
property: "test-id-1-property-1",
},
{
id: "test-id-1",
property: "test-id-1-property-2",
},
{
id: "test-id-2",
property: "test-id-2-property-1",
},
{
id: "test-id-2",
property: "test-id-2-property-2",
},
{
id: "test-id-3",
property: "test-id-3-property-1",
},
]
const mapToObject = (map: Map<any, any>) => Object.fromEntries(map.entries())
describe("groupBy", function () {
it("should return a map grouped by an identifier", function () {
const response = mapToObject(groupBy(array, "id"))
expect(response).toEqual({
"test-id-1": [
{ id: "test-id-1", property: "test-id-1-property-1" },
{ id: "test-id-1", property: "test-id-1-property-2" },
],
"test-id-2": [
{ id: "test-id-2", property: "test-id-2-property-1" },
{ id: "test-id-2", property: "test-id-2-property-2" },
],
"test-id-3": [{ id: "test-id-3", property: "test-id-3-property-1" }],
})
})
it("should return empty map if identifier is not found in array", function () {
const response = mapToObject(groupBy(array, "doesnotexist"))
expect(response).toEqual({})
})
})
@@ -0,0 +1,107 @@
import {
DatabaseErrorCode,
handlePostgresDatabaseError,
} from "../handle-postgres-database-error"
import { EOL } from "os"
describe("handlePostgresDataError", function () {
it("should throw a specific message on database does not exists", function () {
const error = new Error("database does not exist")
Object.assign(error, { code: DatabaseErrorCode.databaseDoesNotExist })
let outputError: any
try {
handlePostgresDatabaseError(error)
} catch (e) {
outputError = e
}
expect(outputError.message).toEqual(
`The specified PostgreSQL database does not exist. Please create it and try again.${EOL}${error.message}`
)
})
it("should throw a specific message on database connection failure", function () {
const error = new Error("database does not exist")
Object.assign(error, { code: DatabaseErrorCode.connectionFailure })
let outputError: any
try {
handlePostgresDatabaseError(error)
} catch (e) {
outputError = e
}
expect(outputError.message).toEqual(
`Failed to establish a connection to PostgreSQL. Please ensure the following is true and try again:
- You have a PostgreSQL database running
- You have passed the correct credentials in medusa-config.js
- You have formatted the database connection string correctly. See below:
"postgres://[username]:[password]@[host]:[post]/[db_name]" - If there is no password, you can omit it from the connection string
${EOL}
${error.message}`
)
})
it("should throw a specific message on database wrong credentials", function () {
const error = new Error("database does not exist")
Object.assign(error, { code: DatabaseErrorCode.wrongCredentials })
let outputError: any
try {
handlePostgresDatabaseError(error)
} catch (e) {
outputError = e
}
expect(outputError.message).toEqual(
`The specified credentials does not exists for the specified PostgreSQL database.${EOL}${error.message}`
)
})
it("should throw a specific message on database not found", function () {
const error = new Error("database does not exist")
Object.assign(error, { code: DatabaseErrorCode.notFound })
let outputError: any
try {
handlePostgresDatabaseError(error)
} catch (e) {
outputError = e
}
expect(outputError.message).toEqual(
`The specified connection string for your PostgreSQL database might have illegal characters. Please check that it only contains allowed characters [a-zA-Z0-9]${EOL}${error.message}`
)
})
it("should throw a specific message on database migration missing", function () {
const error = new Error("database does not exist")
Object.assign(error, { code: DatabaseErrorCode.migrationMissing })
let outputError: any
try {
handlePostgresDatabaseError(error)
} catch (e) {
outputError = e
}
expect(outputError.message).toEqual(
`Migrations missing. Please run 'medusa migrations run' and try again.`
)
})
it("should re throw unhandled error code", function () {
const error = new Error("database does not exist")
Object.assign(error, { code: "test" })
let outputError: any
try {
handlePostgresDatabaseError(error)
} catch (e) {
outputError = e
}
expect(outputError.message).toEqual("database does not exist")
})
})
@@ -0,0 +1,21 @@
import { validateEmail } from "../is-email"
describe("validateEmail", () => {
it("successfully validates an email", () => {
expect(validateEmail("test@email.com")).toBe("test@email.com")
expect(validateEmail("test.test@email.com")).toBe("test.test@email.com")
expect(validateEmail("test.test123@email.com")).toBe(
"test.test123@email.com"
)
})
it("throws on an invalidates email", () => {
expect.assertions(1)
try {
validateEmail("not-an-email")
} catch (e) {
expect(e.message).toBe("The email is not valid")
}
})
})
@@ -0,0 +1,61 @@
import { isPresent } from "../is-present"
describe("isPresent", function () {
it("should return true or false for different types of data", function () {
const expectations = [
{
input: null,
output: false,
},
{
input: undefined,
output: false,
},
{
input: "Testing",
output: true,
},
{
input: "",
output: false,
},
{
input: {},
output: false,
},
{
input: { test: 1 },
output: true,
},
{
input: [],
output: false,
},
{
input: [{ test: 1 }],
output: true,
},
{
input: new Map([["test", "test"]]),
output: true,
},
{
input: new Map([]),
output: false,
},
{
input: new Set(["test"]),
output: true,
},
{
input: new Set([]),
output: false,
},
]
expectations.forEach((expectation) => {
expect(isPresent(expectation.input)).toEqual(expectation.output)
})
})
})
@@ -0,0 +1,43 @@
import { mapObjectTo, MapToConfig } from "../map-object-to"
const input = {
a: [{ id: "1" }, { id: "2" }],
b: [{ id: "3" }, { id: "4", handle: "handle1" }],
c: [{ id: "5", sku: "sku1" }, { id: "6" }],
}
const mapToConfig: MapToConfig = {
a: [{ mapTo: "a.id", valueFrom: "id" }],
b: [
{ mapTo: "b.id", valueFrom: "id" },
{ mapTo: "b.handle", valueFrom: "handle" },
],
c: [
{ mapTo: "c.id", valueFrom: "id" },
{ mapTo: "c.sku", valueFrom: "sku" },
],
}
describe("mapObjectTo", function () {
it("should return a new object with the keys remapped and the values picked from the original object based on the map config", function () {
const remappedObject = mapObjectTo(input, mapToConfig)
expect(remappedObject).toEqual({
"a.id": ["1", "2"],
"b.id": ["3", "4"],
"b.handle": ["handle1"],
"c.id": ["5", "6"],
"c.sku": ["sku1"],
})
})
it("should return a new object with only the picked properties", function () {
const remappedObject = mapObjectTo(input, mapToConfig, {
pick: ["a.id"],
})
expect(remappedObject).toEqual({
"a.id": ["1", "2"],
})
})
})
@@ -0,0 +1,42 @@
import { objectToStringPath } from "../object-to-string-path"
describe("objectToStringPath", function () {
it("should return only the properties path of the properties that are set to true", function () {
const res = objectToStringPath(
{
product: true,
variants: {
title: true,
prices: {
amount: true,
},
},
},
{
includeParentPropertyFields: false,
}
)
expect(res).toEqual(["product", "variants.title", "variants.prices.amount"])
})
it("should return a string path from an object including properties that are object and contains other properties set to true", function () {
const res = objectToStringPath({
product: true,
variants: {
title: true,
prices: {
amount: true,
},
},
})
expect(res).toEqual([
"product",
"variants",
"variants.title",
"variants.prices",
"variants.prices.amount",
])
})
})
@@ -0,0 +1,12 @@
import { partitionArray } from "../../../dist"
describe("partitionArray", function () {
it("should split array according to predicate", function () {
const res = partitionArray([1, 2, 3, 4, 5], (x) => x % 2 === 0)
expect(res).toEqual([
[2, 4],
[1, 3, 5],
])
})
})
@@ -0,0 +1,111 @@
import { pickValueFromObject } from "../pick-value-from-object"
describe("pickValueFromObject", function () {
it("should return true or false for different types of data", function () {
const expectations = [
{
input: {
1: "attribute.another_attribute",
2: {
attribute: {
another_attribute: "test",
},
},
},
output: "test",
},
{
input: {
1: "attribute.another_attribute.array_attribute",
2: {
attribute: {
another_attribute: [
{
array_attribute: "test 1",
},
{
array_attribute: "test 2",
},
],
},
},
},
output: ["test 1", "test 2"],
},
{
input: {
1: "attribute.another_attribute.array_attribute.deep_array_attribute",
2: {
attribute: {
another_attribute: [
{
array_attribute: [
{
deep_array_attribute: "test 1",
},
{
deep_array_attribute: "test 2",
},
],
},
{
array_attribute: [],
},
],
},
},
},
output: ["test 1", "test 2"],
},
{
input: {
1: "attribute.another_attribute.array_attribute",
2: {
attribute: {
another_attribute: [
{
array_attribute: [
{
deep_array_attribute: "test 1",
},
{
deep_array_attribute: "test 2",
},
],
},
{
array_attribute: [],
},
],
},
},
},
output: [
{
deep_array_attribute: "test 1",
},
{
deep_array_attribute: "test 2",
},
],
},
{
input: {
1: "attribute.missing_attribute",
2: {
attribute: {
another_attribute: "test",
},
},
},
output: undefined,
},
]
expectations.forEach((expectation) => {
expect(
pickValueFromObject(expectation.input["1"], expectation.input["2"])
).toEqual(expectation.output)
})
})
})
@@ -0,0 +1,33 @@
import { pluralize } from "../plurailze"
describe("pluralize", function () {
it("should pluralize any words", function () {
const words = [
"apple",
"box",
"day",
"country",
"baby",
"knife",
"hero",
"potato",
"address",
]
const expectedOutput = [
"apples",
"boxes",
"days",
"countries",
"babies",
"knives",
"heroes",
"potatoes",
"addresses",
]
words.forEach((word, index) => {
expect(pluralize(word)).toBe(expectedOutput[index])
})
})
})
@@ -0,0 +1,58 @@
import { promiseAll } from "../promise-all"
import { EOL } from "os"
describe("promiseAll", function () {
it("should throw an error if any of the promises throw", async function () {
const res = await promiseAll([
Promise.resolve(1),
(async () => {
throw new Error("error")
})(),
Promise.resolve(3),
]).catch((e) => e)
expect(res.message).toBe("error")
})
it("should throw errors if any of the promises throw and aggregate them", async function () {
const res = await promiseAll(
[
Promise.resolve(1),
(async () => {
throw new Error("error")
})(),
(async () => {
throw new Error("error2")
})(),
Promise.resolve(3),
],
{
aggregateErrors: true,
}
).catch((e) => e)
expect(res.message).toBe(["error", "error2"].join(EOL))
})
it("should return all values if all promises are fulfilled", async function () {
const res = await promiseAll([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3),
])
expect(res).toEqual([1, 2, 3])
})
it("should return all values if all promises are fulfilled including waiting for nested promises", async function () {
const res = await promiseAll([
Promise.resolve(1),
(async () => {
await promiseAll([Promise.resolve(1), Promise.resolve(2)])
})(),
Promise.resolve(3),
])
expect(res).toEqual([1, undefined, 3])
})
})
@@ -0,0 +1,148 @@
import { remoteQueryObjectFromString } from "../remote-query-object-from-string"
const fields = [
"id",
"created_at",
"updated_at",
"deleted_at",
"url",
"metadata",
"tags.id",
"tags.created_at",
"tags.updated_at",
"tags.deleted_at",
"tags.value",
"options.id",
"options.created_at",
"options.updated_at",
"options.deleted_at",
"options.title",
"options.product_id",
"options.metadata",
"options.values.id",
"options.values.created_at",
"options.values.updated_at",
"options.values.deleted_at",
"options.values.value",
"options.values.option_id",
"options.values.variant_id",
"options.values.metadata",
]
describe("remoteQueryObjectFromString", function () {
it("should return a remote query object", function () {
const output = remoteQueryObjectFromString({
entryPoint: "product",
variables: {
q: "name",
options: {
name: "option_name",
},
"options.values": {
value: 123,
},
},
fields,
})
expect(output).toEqual({
product: {
__args: {
q: "name",
},
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"url",
"metadata",
],
isServiceAccess: false,
tags: {
fields: ["id", "created_at", "updated_at", "deleted_at", "value"],
},
options: {
__args: {
name: "option_name",
},
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"title",
"product_id",
"metadata",
],
values: {
__args: {
value: 123,
},
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"value",
"option_id",
"variant_id",
"metadata",
],
},
},
},
})
})
it("should return a remote query object using service entry point", function () {
const output = remoteQueryObjectFromString({
service: "product",
variables: {},
fields,
})
expect(output).toEqual({
product: {
__args: {},
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"url",
"metadata",
],
isServiceAccess: true,
tags: {
fields: ["id", "created_at", "updated_at", "deleted_at", "value"],
},
options: {
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"title",
"product_id",
"metadata",
],
values: {
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"value",
"option_id",
"variant_id",
"metadata",
],
},
},
},
})
})
})
@@ -0,0 +1,219 @@
import { remoteQueryObjectToString } from "../remote-query-object-to-string"
const remoteQueryObject = {
fields: [
"id",
"title",
"subtitle",
"status",
"external_id",
"description",
"handle",
"is_giftcard",
"discountable",
"thumbnail",
"collection_id",
"type_id",
"weight",
"length",
"height",
"width",
"hs_code",
"origin_country",
"mid_code",
"material",
"created_at",
"updated_at",
"deleted_at",
"metadata",
],
images: {
fields: ["id", "created_at", "updated_at", "deleted_at", "url", "metadata"],
},
tags: {
fields: ["id", "created_at", "updated_at", "deleted_at", "value"],
},
type: {
fields: ["id", "created_at", "updated_at", "deleted_at", "value"],
},
collection: {
fields: ["title", "handle", "id", "created_at", "updated_at", "deleted_at"],
},
options: {
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"title",
"product_id",
"metadata",
],
values: {
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"value",
"option_id",
"variant_id",
"metadata",
],
},
},
variants: {
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"title",
"product_id",
"sku",
"barcode",
"ean",
"upc",
"variant_rank",
"inventory_quantity",
"allow_backorder",
"manage_inventory",
"hs_code",
"origin_country",
"mid_code",
"material",
"weight",
"length",
"height",
"width",
"metadata",
],
options: {
fields: [
"id",
"created_at",
"updated_at",
"deleted_at",
"value",
"option_id",
"variant_id",
"metadata",
],
},
},
profile: {
fields: ["id", "created_at", "updated_at", "deleted_at", "name", "type"],
},
}
describe("remoteQueryObjectToString", function () {
it("should return a string array of fields/relations", function () {
const output = remoteQueryObjectToString(remoteQueryObject)
expect(output).toEqual([
"id",
"title",
"subtitle",
"status",
"external_id",
"description",
"handle",
"is_giftcard",
"discountable",
"thumbnail",
"collection_id",
"type_id",
"weight",
"length",
"height",
"width",
"hs_code",
"origin_country",
"mid_code",
"material",
"created_at",
"updated_at",
"deleted_at",
"metadata",
"images.id",
"images.created_at",
"images.updated_at",
"images.deleted_at",
"images.url",
"images.metadata",
"tags.id",
"tags.created_at",
"tags.updated_at",
"tags.deleted_at",
"tags.value",
"type.id",
"type.created_at",
"type.updated_at",
"type.deleted_at",
"type.value",
"collection.title",
"collection.handle",
"collection.id",
"collection.created_at",
"collection.updated_at",
"collection.deleted_at",
"options.id",
"options.created_at",
"options.updated_at",
"options.deleted_at",
"options.title",
"options.product_id",
"options.metadata",
"options.values.id",
"options.values.created_at",
"options.values.updated_at",
"options.values.deleted_at",
"options.values.value",
"options.values.option_id",
"options.values.variant_id",
"options.values.metadata",
"variants.id",
"variants.created_at",
"variants.updated_at",
"variants.deleted_at",
"variants.title",
"variants.product_id",
"variants.sku",
"variants.barcode",
"variants.ean",
"variants.upc",
"variants.variant_rank",
"variants.inventory_quantity",
"variants.allow_backorder",
"variants.manage_inventory",
"variants.hs_code",
"variants.origin_country",
"variants.mid_code",
"variants.material",
"variants.weight",
"variants.length",
"variants.height",
"variants.width",
"variants.metadata",
"variants.options.id",
"variants.options.created_at",
"variants.options.updated_at",
"variants.options.deleted_at",
"variants.options.value",
"variants.options.option_id",
"variants.options.variant_id",
"variants.options.metadata",
"profile.id",
"profile.created_at",
"profile.updated_at",
"profile.deleted_at",
"profile.name",
"profile.type",
])
})
})
@@ -0,0 +1,22 @@
import { removeUndefined } from "../remove-undefined"
describe("removeUndefined", function () {
it("should remove all undefined fields from an object", function () {
const withUndefined = {
foo: undefined,
bar: "baz",
foo2: null,
}
expect(withUndefined.hasOwnProperty("foo")).toBe(true)
const output = removeUndefined(withUndefined)
expect(output.hasOwnProperty("foo")).toBe(false)
expect(output.hasOwnProperty("bar")).toBe(true)
expect(output.hasOwnProperty("foo2")).toBe(true)
})
it("should return an empty object as-is", function () {
const output = removeUndefined({})
expect(output).toEqual({})
})
})
@@ -0,0 +1,68 @@
import { stringToSelectRelationObject } from "../string-to-select-relation-object"
const fields = [
"id",
"created_at",
"updated_at",
"deleted_at",
"url",
"metadata",
"tags.id",
"tags.created_at",
"tags.updated_at",
"tags.deleted_at",
"tags.value",
"options.id",
"options.created_at",
"options.updated_at",
"options.deleted_at",
"options.title",
"options.product_id",
"options.metadata",
"options.values.id",
"options.values.created_at",
"options.values.updated_at",
"options.values.deleted_at",
"options.values.value",
"options.values.option_id",
"options.values.variant_id",
"options.values.metadata",
]
describe("stringToSelectRelationObject", function () {
it("should return an object containing the select and relation properties", function () {
const output = stringToSelectRelationObject(fields)
expect(output).toEqual({
select: [
"id",
"created_at",
"updated_at",
"deleted_at",
"url",
"metadata",
"tags.id",
"tags.created_at",
"tags.updated_at",
"tags.deleted_at",
"tags.value",
"options.id",
"options.created_at",
"options.updated_at",
"options.deleted_at",
"options.title",
"options.product_id",
"options.metadata",
"options.values.id",
"options.values.created_at",
"options.values.updated_at",
"options.values.deleted_at",
"options.values.value",
"options.values.option_id",
"options.values.variant_id",
"options.values.metadata",
],
relations: ["tags", "options", "options.values"],
})
})
})
@@ -0,0 +1,36 @@
import { toCamelCase } from "../to-camel-case"
describe("toCamelCase", function () {
it("should convert all cases to camel case", function () {
const expectations = [
{
input: "testing-camelize",
output: "testingCamelize",
},
{
input: "testing-Camelize",
output: "testingCamelize",
},
{
input: "TESTING-CAMELIZE",
output: "testingCamelize",
},
{
input: "this_is-A-test",
output: "thisIsATest",
},
{
input: "this_is-A-test ANOTHER",
output: "thisIsATestAnother",
},
{
input: "testingAlreadyCamelized",
output: "testingAlreadyCamelized",
},
]
expectations.forEach((expectation) => {
expect(toCamelCase(expectation.input)).toEqual(expectation.output)
})
})
})
@@ -0,0 +1,36 @@
import { upperCaseFirst } from "../upper-case-first"
describe("upperCaseFirst", function () {
it("should convert first letter of the word to capital letter", function () {
const expectations = [
{
input: "testing capitalize",
output: "Testing capitalize",
},
{
input: "testing",
output: "Testing",
},
{
input: "Testing",
output: "Testing",
},
{
input: "TESTING",
output: "TESTING",
},
{
input: "t",
output: "T",
},
{
input: "",
output: "",
},
]
expectations.forEach((expectation) => {
expect(upperCaseFirst(expectation.input)).toEqual(expectation.output)
})
})
})
@@ -0,0 +1,32 @@
export function generatePostgresAlterColummnIfExistStatement(
tableName: string,
columns: string[],
alterExpression: string
) {
let script = `
DO $$
DECLARE
current_column text;
BEGIN`
columns.forEach((column) => {
script += `
current_column := '${column}';
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = '${tableName}'
AND column_name = current_column
) THEN
EXECUTE format('ALTER TABLE %I ALTER COLUMN %I ${alterExpression}', '${tableName}', current_column);
ELSE
RAISE NOTICE 'Column % does not exist or alteration condition not met.', current_column;
END IF;`
})
script += `
END$$;
`
return script
}
@@ -0,0 +1,15 @@
type ArrayDifferenceElement = string | number
export function arrayDifference<TElement = ArrayDifferenceElement>(
mainArray: TElement[],
differingArray: TElement[]
): TElement[] {
const mainArraySet = new Set(mainArray)
const differingArraySet = new Set(differingArray)
const difference = [...mainArraySet].filter(
(element) => !differingArraySet.has(element)
)
return difference
}
@@ -0,0 +1,17 @@
type ArrayIntersectionElement = string | number
export function arrayIntersection<TElement = ArrayIntersectionElement>(
firstArray: TElement[],
secondArray: TElement[]
): TElement[] {
const firstArraySet = new Set(firstArray)
const res = new Set<TElement>()
secondArray.forEach((element) => {
if (firstArraySet.has(element)) {
res.add(element)
}
})
return Array.from(res)
}
@@ -0,0 +1,70 @@
// Those utils are used in a typeorm context and we can't be sure that they can be used elsewhere
import { objectFromStringPath } from "./object-from-string-path"
type Order = {
[key: string]: "ASC" | "DESC" | Order
}
type Selects = {
[key: string]: boolean | Selects
}
type Relations = {
[key: string]: boolean | Relations
}
export function buildSelects(selectCollection: string[]): Selects {
return buildRelationsOrSelect(selectCollection)
}
export function buildRelations(relationCollection: string[]): Relations {
return buildRelationsOrSelect(relationCollection)
}
function buildRelationsOrSelect(collection: string[]): Selects | Relations {
return objectFromStringPath(collection)
}
/**
* Convert an order of dot string into a nested object
* @example
* input: { id: "ASC", "items.title": "ASC", "items.variant.title": "ASC" }
* output: {
* "id": "ASC",
* "items": {
* "id": "ASC",
* "variant": {
* "title": "ASC"
* }
* },
* }
* @param orderBy
*/
export function buildOrder<T>(orderBy: { [k: string]: "ASC" | "DESC" }): Order {
const output: Order = {}
const orderKeys = Object.keys(orderBy)
for (const order of orderKeys) {
if (order.indexOf(".") > -1) {
const nestedOrder = order.split(".")
let parent = output
while (nestedOrder.length > 1) {
const nestedRelation = nestedOrder.shift() as string
parent = (parent[nestedRelation] as Order) ??= {}
}
parent[nestedOrder[0]] = orderBy[order]
continue
}
output[order] = orderBy[order]
}
return output
}
@@ -0,0 +1,2 @@
export const camelToSnakeCase = (string) =>
string.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase()
@@ -0,0 +1,9 @@
export const ContainerRegistrationKeys = {
PG_CONNECTION: "__pg_connection__",
MANAGER: "manager",
CONFIG_MODULE: "configModule",
LOGGER: "logger",
REMOTE_QUERY: "remoteQuery",
REMOTE_LINK: "remoteLink",
FEATURE_FLAG_ROUTER: "featureFlagRouter",
}
@@ -0,0 +1,104 @@
import { isObject } from "../common/is-object"
interface ItemRecord extends Record<string, any> {
id: string
}
export function convertItemResponseToUpdateRequest(
item: ItemRecord,
selects: string[],
relations: string[],
fromManyRelationships: boolean = false
): ItemRecord {
const newItem: ItemRecord = {
id: item.id,
}
// If item is a child of a many relationship, we just need to pass in the id of the item
if (fromManyRelationships) {
return newItem
}
for (const [key, value] of Object.entries(item)) {
if (relations.includes(key)) {
const relation = item[key]
// If the relationship is an object, its either a one to one or many to one relationship
// We typically don't update the parent from the child relationship, we can skip this for now.
// This can be focused on solely for one to one relationships
if (isObject(relation)) {
// If "id" is the only one in the object, underscorize the relation. This is assuming that
// the relationship itself was changed to another item and now we need to revert it to the old item.
if (Object.keys(relation).length === 1 && "id" in relation) {
newItem[`${key}_id`] = relation.id
}
// If attributes of the relation have been updated, we can assume that this
// was an update operation on the relation. We revert what was updated.
if (Object.keys(relation).length > 1) {
// The ID can be figured out from the relationship, we can delete the ID here
if ("id" in relation) {
delete relation.id
}
// we just need the selects for the relation, filter it out and remove the parent scope
const filteredSelects = selects
.filter((s) => s.startsWith(key) && !s.includes("id"))
.map(shiftFirstPath)
// Add the filtered selects to the sanitized object
for (const filteredSelect of filteredSelects) {
newItem[key] = newItem[key] || {}
newItem[key][filteredSelect] = relation[filteredSelect]
}
}
continue
}
// If the relation is an array, we can expect this to be a one to many or many to many
// relationships. Recursively call the function until all relations are converted
if (Array.isArray(relation)) {
const newRelationsArray: ItemRecord[] = []
for (const rel of relation) {
// Scope selects and relations to ones that are relevant to the current relation
const filteredRelations = relations
.filter((r) => r.startsWith(key))
.map(shiftFirstPath)
const filteredSelects = selects
.filter((s) => s.startsWith(key))
.map(shiftFirstPath)
newRelationsArray.push(
convertItemResponseToUpdateRequest(
rel,
filteredSelects,
filteredRelations,
true
)
)
}
newItem[key] = newRelationsArray
}
}
// if the key exists in the selects, we add them to the new sanitized array.
// sanitisation is done because MikroORM adds relationship attributes and other default attributes
// which we do not want to add to the update request
if (selects.includes(key) && !fromManyRelationships) {
newItem[key] = value
}
}
return newItem
}
function shiftFirstPath(select) {
const selectArray = select.split(".")
selectArray.shift()
return selectArray.join(".")
}
@@ -0,0 +1,9 @@
import { ContainerLike } from "@medusajs/types"
export function createContainerLike(obj): ContainerLike {
return {
resolve(key: string) {
return obj[key]
},
}
}
@@ -0,0 +1,74 @@
import { Index } from "@mikro-orm/core"
/**
* Create a PSQL index statement
* @param name The name of the index, if not provided it will be generated in the format IDX_tableName_columnName
* @param tableName The name of the table
* @param columns The columns to index
* @param type The type of index (e.g GIN, GIST, BTREE, etc)
* @param where The where clause
* @param unique If the index should be a unique index
*
* @example
* createPsqlIndexStatementHelper({
* name: "idx_user_email",
* tableName: "user",
* columns: "email",
* type: "btree",
* where: "email IS NOT NULL"
* });
*
* // expression: CREATE INDEX IF NOT EXISTS idx_user_email ON user USING btree (email) WHERE email IS NOT NULL;
*
* createPsqlIndexStatementHelper({
* name: "idx_user_email",
* tableName: "user",
* columns: "email"
* });
*
* // expression: CREATE INDEX IF NOT EXISTS idx_user_email ON user (email);
*
*/
export function createPsqlIndexStatementHelper({
name,
tableName,
columns,
type,
where,
unique,
}: {
name?: string
tableName: string
columns: string | string[]
type?: string
where?: string
unique?: boolean
}) {
const columnsName = Array.isArray(columns) ? columns.join("_") : columns
columns = Array.isArray(columns) ? columns.join(", ") : columns
name = name || `IDX_${tableName}_${columnsName}${unique ? "_unique" : ""}`
const typeStr = type ? ` USING ${type}` : ""
const optionsStr = where ? ` WHERE ${where}` : ""
const uniqueStr = unique ? "UNIQUE " : ""
const expression = `CREATE ${uniqueStr}INDEX IF NOT EXISTS "${name}" ON "${tableName}"${typeStr} (${columns})${optionsStr}`
return {
toString: () => {
return expression
},
valueOf: () => {
return expression
},
name,
expression,
MikroORMIndex: (options?: Parameters<typeof Index>[0]) => {
return Index({
name,
expression,
...options,
})
},
}
}
@@ -0,0 +1,3 @@
export function deduplicate<T = any>(collection: T[]): T[] {
return [...new Set(collection)]
}
@@ -0,0 +1,35 @@
import { isObject } from "./is-object"
/**
* In most casees, JSON.parse(JSON.stringify(obj)) is enough to deep copy an object.
* But in some cases, it's not enough. For example, if the object contains a function or a proxy, it will be lost after JSON.parse(JSON.stringify(obj)).
*
* @param obj
*/
export function deepCopy<T extends Record<any, any> = Record<any, any>>(
obj: T | T[]
): T | T[] {
if (obj === null || typeof obj !== "object") {
return obj
}
if (Array.isArray(obj)) {
const copy: any[] = []
for (let i = 0; i < obj.length; i++) {
copy[i] = deepCopy(obj[i])
}
return copy
}
if (isObject(obj)) {
const copy: Record<any, any> = {}
for (let attr in obj) {
if (obj.hasOwnProperty(attr)) {
copy[attr] = deepCopy(obj[attr])
}
}
return copy
}
return obj
}
@@ -0,0 +1,24 @@
export function deepEqualObj(obj1: object, obj2: object): boolean {
if (typeof obj1 !== typeof obj2) {
return false
}
if (typeof obj1 !== "object" || obj1 === null) {
return obj1 === obj2
}
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
for (const key of obj1Keys) {
if (!obj2Keys.includes(key) || !deepEqualObj(obj1[key], obj2[key])) {
return false
}
}
return true
}
@@ -0,0 +1,103 @@
import { isDefined } from "./is-defined"
import { isObject } from "./is-object"
/**
* @description
* This function is used to flatten nested objects and arrays
*
* @example
*
* ```ts
* const data = {
* root_level_property: "root level",
* products: [
* {
* id: "1",
* name: "product 1",
* variants: [
* { id: "1.1", name: "variant 1.1" },
* { id: "1.2", name: "variant 1.2" },
* ],
* },
* {
* id: "2",
* name: "product 2",
* variants: [
* { id: "2.1", name: "variant 2.1" },
* { id: "2.2", name: "variant 2.2" },
* ],
* },
* ],
* }
*
* const flat = deepFlatMap(
* data,
* "products.variants",
* ({ root_, products, variants }) => {
* return {
* root_level_property: root_.root_level_property,
* product_id: products.id,
* product_name: products.name,
* variant_id: variants.id,
* variant_name: variants.name,
* }
* }
* )
* ```
*/
export function deepFlatMap(
data: any,
path: string,
callback: (context: Record<string, any>) => any
) {
const ROOT_LEVEL = "root_"
const keys = path.split(".")
keys.unshift(ROOT_LEVEL)
const lastKey = keys[keys.length - 1]
const stack: {
element: any
path: string[]
context: Record<string, any>
}[] = [{ element: { [ROOT_LEVEL]: data }, path: keys, context: {} }]
const results: any[] = []
while (stack.length > 0) {
const { element, path, context } = stack.shift()!
const currentKey = path[0]
const remainingPath = path.slice(1)
if (!isDefined(element[currentKey])) {
callback({ ...context })
continue
}
if (remainingPath.length === 0) {
if (Array.isArray(element[currentKey])) {
element[currentKey].forEach((item) => {
results.push(callback({ ...context, [lastKey]: item }))
})
} else if (isObject(element[currentKey])) {
results.push(callback({ ...context, [lastKey]: element[currentKey] }))
}
} else {
if (Array.isArray(element[currentKey])) {
element[currentKey].forEach((item) => {
stack.push({
element: item,
path: remainingPath,
context: { ...context, [currentKey]: item },
})
})
} else if (isObject(element[currentKey])) {
stack.push({
element: element[currentKey],
path: remainingPath,
context: { ...context, [currentKey]: element[currentKey] },
})
}
}
}
return results
}
+55
View File
@@ -0,0 +1,55 @@
/**
* @typedef MedusaErrorType
*
*/
export const MedusaErrorTypes = {
/** Errors stemming from the database */
DB_ERROR: "database_error",
DUPLICATE_ERROR: "duplicate_error",
INVALID_ARGUMENT: "invalid_argument",
INVALID_DATA: "invalid_data",
UNAUTHORIZED: "unauthorized",
NOT_FOUND: "not_found",
NOT_ALLOWED: "not_allowed",
UNEXPECTED_STATE: "unexpected_state",
CONFLICT: "conflict",
PAYMENT_AUTHORIZATION_ERROR: "payment_authorization_error",
}
export const MedusaErrorCodes = {
INSUFFICIENT_INVENTORY: "insufficient_inventory",
CART_INCOMPATIBLE_STATE: "cart_incompatible_state",
}
/**
* Standardized error to be used across Medusa project.
* @extends Error
*/
export class MedusaError extends Error {
public type: string
public message: string
public code?: string
public date: Date
public static Types = MedusaErrorTypes
public static Codes = MedusaErrorCodes
/**
* Creates a standardized error to be used across Medusa project.
* @param {string} type - type of error
* @param {string} message - message to go along with error
* @param {string} code - code of error
* @param {Array} params - params
*/
constructor(type: string, message: string, code?: string, ...params: any) {
super(...params)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, MedusaError)
}
this.type = type
this.code = code
this.message = message
this.date = new Date()
}
}
@@ -0,0 +1,16 @@
import { ulid } from "ulid"
/**
* Generate a composed id based on the input parameters and return either the is if it exists or the generated one.
* @param idProperty
* @param prefix
*/
export function generateEntityId(idProperty?: string, prefix?: string): string {
if (idProperty) {
return idProperty
}
const id = ulid()
prefix = prefix ? `${prefix}_` : ""
return `${prefix}${id}`
}
@@ -0,0 +1,17 @@
import { MapToConfig } from "./map-object-to"
export function generateLinkableKeysMap(
linkableKeys: Record<string, string>
): MapToConfig {
const entityLinkableKeysMap: MapToConfig = {}
Object.entries(linkableKeys).forEach(([key, value]) => {
entityLinkableKeysMap[value] ??= []
entityLinkableKeysMap[value].push({
mapTo: key,
valueFrom: key.split("_").pop()!,
})
})
return entityLinkableKeysMap
}
@@ -0,0 +1,32 @@
import { join } from "path"
/**
* Attempts to resolve the config file in a given root directory.
* @param {string} rootDir - the directory to find the config file in.
* @param {string} configName - the name of the config file.
* @return {object} an object containing the config module and its path as well as an error property if the config couldn't be loaded.
*/
function getConfigFile<TConfig = unknown>(
rootDir: string,
configName: string
): { configModule: TConfig; configFilePath: string; error?: any } {
const configPath = join(rootDir, configName)
let configFilePath = ``
let configModule
let err
try {
configFilePath = require.resolve(configPath)
configModule = require(configFilePath)
} catch (e) {
err = e
}
if (configModule && typeof configModule.default === "object") {
configModule = configModule.default
}
return { configModule, configFilePath, error: err }
}
export default getConfigFile
@@ -0,0 +1,16 @@
// This function is intentionally not generic, as we will likely need a comparator function in that case, which will make it more complex than necessary
// Revisit if there is such use-case in the future
export const getDuplicates = (collection: string[]): string[] => {
const uniqueElements = new Set<string>()
const duplicates = new Set<string>()
collection.forEach((item) => {
if (uniqueElements.has(item)) {
duplicates.add(item)
} else {
uniqueElements.add(item)
}
})
return Array.from(duplicates)
}
@@ -0,0 +1,15 @@
import { isDate } from "./is-date"
import { MedusaError } from "./errors"
export const GetIsoStringFromDate = (date: Date | string) => {
if (!isDate(date)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Cannot format date to ISO string: ${date}`
)
}
date = new Date(date)
return date.toISOString()
}
@@ -0,0 +1,14 @@
import { join } from "path"
export const getMedusaVersion = (): string => {
try {
return require(join(
process.cwd(),
`node_modules`,
`@medusajs/medusa`,
`package.json`
)).version
} catch (e) {
return ``
}
}
@@ -0,0 +1,58 @@
import { deduplicate } from "./deduplicate"
import { isObject } from "./is-object"
export function getSelectsAndRelationsFromObjectArray(
dataArray: object[],
options: { objectFields: string[] } = {
objectFields: [],
},
prefix?: string
): {
selects: string[]
relations: string[]
} {
const selects: string[] = []
const relations: string[] = []
for (const data of dataArray) {
for (const [key, value] of Object.entries(data)) {
if (isObject(value) && !options.objectFields.includes(key)) {
relations.push(setKey(key, prefix))
const res = getSelectsAndRelationsFromObjectArray(
[value],
options,
setKey(key, prefix)
)
selects.push(...res.selects)
relations.push(...res.relations)
} else if (Array.isArray(value)) {
relations.push(setKey(key, prefix))
const res = getSelectsAndRelationsFromObjectArray(
value,
options,
setKey(key, prefix)
)
selects.push(...res.selects)
relations.push(...res.relations)
} else {
selects.push(setKey(key, prefix))
}
}
}
const uniqueSelects: string[] = deduplicate(selects)
const uniqueRelations: string[] = deduplicate(relations)
return {
selects: uniqueSelects,
relations: uniqueRelations,
}
}
function setKey(key: string, prefix?: string) {
if (prefix) {
return `${prefix}.${key}`
} else {
return key
}
}
@@ -0,0 +1,19 @@
/**
* Get the difference between two sets. The difference is the elements that are in the original set but not in the compare set.
* @param orignalSet
* @param compareSet
*/
export function getSetDifference<T>(
orignalSet: Set<T>,
compareSet: Set<T>
): Set<T> {
const difference = new Set<T>()
orignalSet.forEach((element) => {
if (!compareSet.has(element)) {
difference.add(element)
}
})
return difference
}
@@ -0,0 +1,20 @@
export function groupBy(
array: Record<any, any>[],
attribute: string | number
): Map<any, any> {
return array.reduce<Map<any, any>>((map, obj) => {
const key = obj[attribute]
if (!key) {
return map
}
if (!map.get(key)) {
map.set(key, [])
}
map.get(key).push(obj)
return map
}, new Map())
}
@@ -0,0 +1,49 @@
import { EOL } from "os"
export const DatabaseErrorCode = {
databaseDoesNotExist: "3D000",
connectionFailure: "ECONNREFUSED",
wrongCredentials: "28000",
notFound: "ENOTFOUND",
migrationMissing: "42P01",
}
export function handlePostgresDatabaseError(err: any): never {
if (DatabaseErrorCode.databaseDoesNotExist === err.code) {
throw new Error(
`The specified PostgreSQL database does not exist. Please create it and try again.${EOL}${err.message}`
)
}
if (DatabaseErrorCode.connectionFailure === err.code) {
throw new Error(
`Failed to establish a connection to PostgreSQL. Please ensure the following is true and try again:
- You have a PostgreSQL database running
- You have passed the correct credentials in medusa-config.js
- You have formatted the database connection string correctly. See below:
"postgres://[username]:[password]@[host]:[post]/[db_name]" - If there is no password, you can omit it from the connection string
${EOL}
${err.message}`
)
}
if (DatabaseErrorCode.wrongCredentials === err.code) {
throw new Error(
`The specified credentials does not exists for the specified PostgreSQL database.${EOL}${err.message}`
)
}
if (DatabaseErrorCode.notFound === err.code) {
throw new Error(
`The specified connection string for your PostgreSQL database might have illegal characters. Please check that it only contains allowed characters [a-zA-Z0-9]${EOL}${err.message}`
)
}
if (DatabaseErrorCode.migrationMissing === err.code) {
throw new Error(
`Migrations missing. Please run 'medusa migrations run' and try again.`
)
}
throw err
}
+60
View File
@@ -0,0 +1,60 @@
export * from "./alter-columns-helper"
export * from "./array-difference"
export * from "./array-intersection"
export * from "./build-query"
export * from "./camel-to-snake-case"
export * from "./container"
export * from "./convert-item-response-to-update-request"
export * from "./create-container-like"
export * from "./create-psql-index-helper"
export * from "./deduplicate"
export * from "./deep-copy"
export * from "./deep-equal-obj"
export * from "./deep-flat-map"
export * from "./errors"
export * from "./generate-entity-id"
export * from "./generate-linkable-keys-map"
export * from "./get-config-file"
export * from "./get-duplicates"
export * from "./get-iso-string-from-date"
export * from "./get-selects-and-relations-from-object-array"
export * from "./get-set-difference"
export * from "./group-by"
export * from "./handle-postgres-database-error"
export * from "./is-big-number"
export * from "./is-date"
export * from "./is-defined"
export * from "./is-email"
export * from "./is-object"
export * from "./is-present"
export * from "./is-string"
export * from "./lower-case-first"
export * from "./map-object-to"
export * from "./medusa-container"
export * from "./object-from-string-path"
export * from "./object-to-string-path"
export * from "./optional-numeric-serializer"
export * from "./partition-array"
export * from "./pick-deep"
export * from "./pick-value-from-object"
export * from "./plurailze"
export * from "./prefix-array-items"
export * from "./promise-all"
export * from "./remote-query-object-from-string"
export * from "./remote-query-object-to-string"
export * from "./remove-nullisih"
export * from "./remove-undefined"
export * from "./rules"
export * from "./selector-constraints-to-string"
export * from "./set-metadata"
export * from "./simple-hash"
export * from "./string-or-regex-equals"
export * from "./string-to-select-relation-object"
export * from "./stringify-circular"
export * from "./to-camel-case"
export * from "./to-kebab-case"
export * from "./to-pascal-case"
export * from "./transaction"
export * from "./trim-zeros"
export * from "./upper-case-first"
export * from "./wrap-handler"
@@ -0,0 +1,6 @@
import { BigNumberRawValue } from "@medusajs/types"
import { isObject } from "./is-object"
export function isBigNumber(obj: any): obj is BigNumberRawValue {
return isObject(obj) && "value" in obj
}
@@ -0,0 +1,3 @@
export function isDate(value: any): value is Date {
return value !== null && !isNaN(new Date(value).valueOf())
}
@@ -0,0 +1,5 @@
export function isDefined<T = undefined | unknown>(
val: T
): val is T extends undefined ? never : T {
return typeof val !== "undefined"
}
@@ -0,0 +1,30 @@
import { MedusaError } from "./errors"
const EMAIL_REGEX =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
/**
* Check whether provided string is an email.
* @param email - string to check
*/
function isEmail(email: string) {
return email.toLowerCase().match(EMAIL_REGEX)
}
/**
* Used to validate user email.
* @param {string} email - email to validate
* @return {string} the validated email
*/
export function validateEmail(email: string): string {
const validatedEmail = isEmail(email)
if (!validatedEmail) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"The email is not valid"
)
}
return email.toLowerCase()
}
@@ -0,0 +1,3 @@
export function isObject(obj: any): obj is object {
return obj != null && obj?.constructor?.name === "Object"
}
@@ -0,0 +1,23 @@
import { isDefined } from "./is-defined"
import { isObject } from "./is-object"
import { isString } from "./is-string"
export function isPresent(value: any): boolean {
if (!isDefined(value) || value === null) {
return false
}
if (isString(value) || Array.isArray(value)) {
return value.length > 0
}
if (value instanceof Map || value instanceof Set) {
return value.size > 0
}
if (isObject(value)) {
return Object.keys(value).length > 0
}
return true
}
@@ -0,0 +1,3 @@
export function isString(val: any): val is string {
return val != null && typeof val === "string"
}
@@ -0,0 +1,3 @@
export function lowerCaseFirst(str: string): string {
return str.charAt(0).toLowerCase() + str.slice(1)
}
@@ -0,0 +1,53 @@
type RemapInputObject = Record<string, unknown[]>
type RemapConfig = { mapTo: string; valueFrom: string }
export type MapToConfig = {
[key: string]: RemapConfig[]
}
/**
* Create a new object with the keys remapped and the values picked from the original object based
* on the map config
*
* @param object input object
* @param mapTo configuration to map the output object
* @param removeIfNotRemapped if true, the keys that are not remapped will be removed from the output object
* @param pick if provided, only the keys in the array will be picked from the output object
*/
export function mapObjectTo<
TResult = any,
T extends RemapInputObject = RemapInputObject
>(
object: T,
mapTo: MapToConfig,
{
removeIfNotRemapped,
pick,
}: { removeIfNotRemapped?: boolean; pick?: string[] } = {}
): TResult {
removeIfNotRemapped ??= false
const newObject: Record<string, any> = {}
for (const key in object) {
const remapConfig = mapTo[key as string]!
if (!remapConfig) {
if (!removeIfNotRemapped) {
newObject[key] = object[key]
}
continue
}
remapConfig.forEach((config) => {
if (pick?.length && !pick.includes(config.mapTo)) {
return
}
newObject[config.mapTo] = object[key]
.map((obj: any) => obj[config.valueFrom])
.filter(Boolean)
})
}
return newObject as TResult
}
@@ -0,0 +1,57 @@
import { MedusaContainer } from "@medusajs/types"
import {
asFunction,
asValue,
AwilixContainer,
ClassOrFunctionReturning,
createContainer,
Resolver,
} from "awilix"
function asArray(
resolvers: (ClassOrFunctionReturning<unknown> | Resolver<unknown>)[]
): { resolve: (container: AwilixContainer) => unknown[] } {
return {
resolve: (container: AwilixContainer) =>
resolvers.map((resolver) => container.build(resolver)),
}
}
function registerAdd(
this: MedusaContainer,
name: string,
registration: typeof asFunction | typeof asValue
) {
const storeKey = name + "_STORE"
if (this.registrations[storeKey] === undefined) {
this.register(storeKey, asValue([] as Resolver<unknown>[]))
}
const store = this.resolve(storeKey) as (
| ClassOrFunctionReturning<unknown>
| Resolver<unknown>
)[]
if (this.registrations[name] === undefined) {
this.register(name, asArray(store))
}
store.unshift(registration)
return this
}
export function createMedusaContainer(...args): MedusaContainer {
const container = createContainer.apply(null, args) as MedusaContainer
container.registerAdd = registerAdd.bind(container)
const originalScope = container.createScope
container.createScope = () => {
const scoped = originalScope() as MedusaContainer
scoped.registerAdd = registerAdd.bind(scoped)
return scoped
}
return container
}
@@ -0,0 +1,78 @@
/**
* Convert a collection of dot string into a nested object
* @example
* input: [
* order,
* order.items,
* order.swaps,
* order.swaps.additional_items,
* order.discounts,
* order.discounts.rule,
* order.claims,
* order.claims.additional_items,
* additional_items,
* additional_items.variant,
* return_order,
* return_order.items,
* return_order.shipping_method,
* return_order.shipping_method.tax_lines
* ]
* output: {
* "order": {
* "items": true,
* "swaps": {
* "additional_items": true
* },
* "discounts": {
* "rule": true
* },
* "claims": {
* "additional_items": true
* }
* },
* "additional_items": {
* "variant": true
* },
* "return_order": {
* "items": true,
* "shipping_method": {
* "tax_lines": true
* }
* }
* }
* @param collection
*/
export function objectFromStringPath(
collection: string[]
): Record<string, any> {
collection = collection.sort()
const output: Record<string, any> = {}
for (const relation of collection) {
if (!relation) {
continue
}
if (relation.indexOf(".") > -1) {
const nestedRelations = relation.split(".")
let parent = output
while (nestedRelations.length > 1) {
const nestedRelation = nestedRelations.shift() as string
parent = parent[nestedRelation] =
parent[nestedRelation] !== true &&
typeof parent[nestedRelation] === "object"
? parent[nestedRelation]
: {}
}
parent[nestedRelations[0]] = true
continue
}
output[relation] = output[relation] ?? true
}
return output
}
@@ -0,0 +1,77 @@
import { isObject } from "./is-object"
/**
* Converts a structure of find options to an
* array of string paths
* @example
* // With `includeTruePropertiesOnly` default value set to false
* const result = objectToStringPath({
* test: {
* test1: true,
* test2: true,
* test3: {
* test4: true
* },
* },
* test2: true
* })
* console.log(result)
* // output: ['test', 'test.test1', 'test.test2', 'test.test3', 'test.test3.test4', 'test2']
*
* @example
* // With `includeTruePropertiesOnly` set to true
* const result = objectToStringPath({
* test: {
* test1: true,
* test2: true,
* test3: {
* test4: true
* },
* },
* test2: true
* }, {
* includeTruePropertiesOnly: true
* })
* console.log(result)
* // output: ['test.test1', 'test.test2', 'test.test3.test4', 'test2']
*
* @param {InputObject} input
* @param {boolean} includeParentPropertyFields If set to true (example 1), all properties will be included as well as the parents,
* otherwise (example 2) all properties path set to true will included, excluded the parents
*/
export function objectToStringPath(
input: object = {},
{ includeParentPropertyFields }: { includeParentPropertyFields: boolean } = {
includeParentPropertyFields: true,
}
): string[] {
if (!isObject(input) || !Object.keys(input).length) {
return []
}
const output: Set<string> = includeParentPropertyFields
? new Set(Object.keys(input))
: new Set()
for (const key of Object.keys(input)) {
if (isObject(input[key])) {
const deepRes = objectToStringPath(input[key], {
includeParentPropertyFields,
})
const items = deepRes.reduce((acc, val) => {
acc.push(`${key}.${val}`)
return acc
}, [] as string[])
items.forEach((item) => output.add(item))
continue
}
if (isObject(key) || input[key] === true) {
output.add(key)
}
}
return Array.from(output)
}
@@ -0,0 +1,4 @@
import { isDefined } from "./is-defined"
export const optionalNumericSerializer = (value) =>
isDefined(value) && value !== null ? Number(value) : value
@@ -0,0 +1,30 @@
/**
* Partitions an array into two arrays based on a predicate function
* @example
* const result = partitionArray([1, 2, 3, 4, 5], (x) => x % 2 === 0)
*
* console.log(result)
*
* // output: [[2, 4], [1, 3, 5]]
*
* @param {T} input input array of type T
* @param {(T) => boolean} predicate function to use when split array elements
*/
export const partitionArray = <T>(
input: T[],
predicate: (T) => boolean
): [T[], T[]] => {
return input.reduce(
([pos, neg], currentElement) => {
if (predicate(currentElement)) {
pos.push(currentElement)
} else {
neg.push(currentElement)
}
return [pos, neg]
},
[[], []] as [T[], T[]]
)
}
@@ -0,0 +1,63 @@
import { isObject } from "./is-object"
export function pickDeep<T extends object = object>(
input: object,
fields: Array<number | string>,
prefix: string = ""
): T {
if (!input) {
return input
}
return Object.entries(input).reduce((nextInput, [key, value]) => {
const fieldKey = withPrefix(key, prefix)
const fieldMatches = fields.includes(fieldKey)
const partialKeyMatch =
fields.filter((field) => field.toString().startsWith(`${fieldKey}.`))
.length > 0
const valueIsObject = isObject(value)
const valueIsArray = Array.isArray(value)
if (fieldMatches && (valueIsObject || valueIsArray)) {
nextInput[key] = value
return nextInput
}
if (!fieldMatches && !partialKeyMatch) {
return nextInput
}
if (valueIsArray) {
nextInput[key] = value.map((arrItem) => {
if (isObject(arrItem)) {
return pickDeep(arrItem, fields, withPrefix(key, prefix))
}
return arrItem
})
return nextInput
} else if (valueIsObject) {
if (Object.keys(value).length) {
nextInput[key] = pickDeep(value, fields, withPrefix(key, prefix))
}
return nextInput
}
if (fieldMatches) {
nextInput[key] = value
}
return nextInput
}, {} as T)
}
function withPrefix(key: string, prefix: string): string {
if (prefix.length) {
return `${prefix}.${key}`
} else {
return key
}
}
@@ -0,0 +1,37 @@
import { isObject } from "./is-object"
export function pickValueFromObject(
path: string,
object: Record<any, any>
): any {
const segments = path.split(".")
let result: any = undefined
for (const segment of segments) {
const segmentsLeft = [...segments].splice(1, segments.length - 1)
const segmentOutput = object[segment]
if (segmentsLeft.length === 0) {
result = segmentOutput
break
}
if (isObject(segmentOutput)) {
result = pickValueFromObject(segmentsLeft.join("."), segmentOutput)
break
}
if (Array.isArray(segmentOutput)) {
result = segmentOutput
.map((segmentOutput_) =>
pickValueFromObject(segmentsLeft.join("."), segmentOutput_)
)
.flat()
break
}
result = segmentOutput
}
return result
}
@@ -0,0 +1,27 @@
/**
* Some library provide pluralize function with language specific rules.
* This is a simple implementation of pluralize function.
* @param word
*/
export function pluralize(word: string): string {
// Add basic rules for forming plurals
if (
//word.endsWith("s") ||
word.endsWith("sh") ||
word.endsWith("ss") ||
word.endsWith("ch") ||
word.endsWith("x") ||
word.endsWith("o") ||
word.endsWith("z")
) {
return word + "es"
} else if (word.endsWith("y") && !"aeiou".includes(word[word.length - 2])) {
return word.slice(0, -1) + "ies"
} else if (word.endsWith("es")) {
return word
} else if (word.endsWith("fe")) {
return word.slice(0, -2) + "ves"
} else {
return word + "s"
}
}
@@ -0,0 +1,8 @@
/**
* Prefixes an array of strings with a specified string
* @param array
* @param prefix
*/
export function prefixArrayItems(array: string[], prefix: string): string[] {
return array.map((arrEl) => `${prefix}${arrEl}`)
}
@@ -0,0 +1,40 @@
import { EOL } from "os"
const getMessageError = (state: PromiseRejectedResult) =>
state.reason.message ?? state.reason
const isRejected = (
state: PromiseSettledResult<unknown>
): state is PromiseRejectedResult => {
return state.status === "rejected"
}
const getValue = (state: PromiseFulfilledResult<unknown>) => state.value
/**
* Promise.allSettled with error handling, safe alternative to Promise.all
* @param promises
* @param aggregateErrors
*/
export async function promiseAll<T extends readonly unknown[] | []>(
promises: T,
{ aggregateErrors } = { aggregateErrors: false }
): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }> {
const states = await Promise.allSettled(promises)
const rejected = (states as PromiseSettledResult<unknown>[]).filter(
isRejected
)
if (rejected.length) {
if (aggregateErrors) {
throw new Error(rejected.map(getMessageError).join(EOL))
}
throw rejected[0].reason // Re throw the error itself
}
return (states as PromiseFulfilledResult<unknown>[]).map(
getValue
) as unknown as Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>
}
@@ -0,0 +1,155 @@
import { isObject } from "./is-object"
/**
* Convert a string fields array to a remote query object
* @param config - The configuration object
*
* @example
* const fields = [
* "id",
* "created_at",
* "updated_at",
* "deleted_at",
* "url",
* "metadata",
* "tags.id",
* "tags.created_at",
* "tags.updated_at",
* "tags.deleted_at",
* "tags.value",
* "options.id",
* "options.created_at",
* "options.updated_at",
* "options.deleted_at",
* "options.title",
* "options.product_id",
* "options.metadata",
* "options.values.id",
* "options.values.created_at",
* "options.values.updated_at",
* "options.values.deleted_at",
* "options.values.value",
* "options.values.option_id",
* "options.values.variant_id",
* "options.values.metadata",
* ]
*
* const remoteQueryObject = remoteQueryObjectFromString({
* entryPoint: "product",
* variables: {},
* fields,
* })
*
* console.log(remoteQueryObject)
* // {
* // product: {
* // __args: {},
* // fields: [
* // "id",
* // "created_at",
* // "updated_at",
* // "deleted_at",
* // "url",
* // "metadata",
* // ],
* //
* // tags: {
* // fields: ["id", "created_at", "updated_at", "deleted_at", "value"],
* // },
* //
* // options: {
* // fields: [
* // "id",
* // "created_at",
* // "updated_at",
* // "deleted_at",
* // "title",
* // "product_id",
* // "metadata",
* // ],
* // values: {
* // fields: [
* // "id",
* // "created_at",
* // "updated_at",
* // "deleted_at",
* // "value",
* // "option_id",
* // "variant_id",
* // "metadata",
* // ],
* // },
* // },
* // },
* // }
*/
export function remoteQueryObjectFromString(
config:
| {
entryPoint: string
variables?: any
fields: string[]
}
| {
service: string
variables?: any
fields: string[]
}
): object {
const { entryPoint, service, variables, fields } = {
...config,
entryPoint: "entryPoint" in config ? config.entryPoint : undefined,
service: "service" in config ? config.service : undefined,
}
const entryKey = (entryPoint ?? service) as string
const remoteJoinerConfig: object = {
[entryKey]: {
fields: [],
isServiceAccess: !!service, // specifies if the entry point is a service
},
}
const usedVariables = new Set()
for (const field of fields) {
if (!field.includes(".")) {
remoteJoinerConfig[entryKey]["fields"].push(field)
continue
}
const fieldSegments = field.split(".")
const fieldProperty = fieldSegments.pop()
let combinedPath = ""
const deepConfigRef = fieldSegments.reduce((acc, curr) => {
combinedPath = combinedPath ? combinedPath + "." + curr : curr
if (isObject(variables) && combinedPath in variables) {
acc[curr] ??= {}
acc[curr]["__args"] = variables[combinedPath]
usedVariables.add(combinedPath)
} else {
acc[curr] ??= {}
}
return acc[curr]
}, remoteJoinerConfig[entryKey])
deepConfigRef["fields"] ??= []
deepConfigRef["fields"].push(fieldProperty)
}
const topLevelArgs = {}
for (const key of Object.keys(variables ?? {})) {
if (!usedVariables.has(key)) {
topLevelArgs[key] = variables[key]
}
}
remoteJoinerConfig[entryKey]["__args"] = topLevelArgs ?? {}
return remoteJoinerConfig
}
@@ -0,0 +1,51 @@
/**
* Transform a remote query object to a string array containing the chain of fields and relations
*
* @param fields
* @param parent
*
* @example
*
* const remoteQueryObject = {
* fields: [
* "id",
* "title",
* ],
* images: {
* fields: ["id", "created_at", "updated_at", "deleted_at", "url", "metadata"],
* },
* }
*
* const fields = remoteQueryObjectToString(remoteQueryObject)
*
* console.log(fields)
* // ["id", "title", "images.id", "images.created_at", "images.updated_at", "images.deleted_at", "images.url", "images.metadata"]
*/
export function remoteQueryObjectToString(
fields: object,
parent?: string
): string[] {
return Object.keys(fields).reduce((acc, key) => {
if (key === "fields") {
if (parent) {
fields[key].map((fieldKey) => acc.push(`${parent}.${fieldKey}`))
} else {
fields[key].map((fieldKey) => acc.push(fieldKey))
}
return acc
}
if (typeof fields[key] === "object") {
acc = acc.concat(
remoteQueryObjectToString(
fields[key],
parent ? `${parent}.${key}` : key
)
)
return acc
}
return acc
}, [] as string[])
}
@@ -0,0 +1,18 @@
import { isDefined } from "./is-defined"
export function removeNullish<T = unknown>(
obj: Record<string, T>
): Record<string, T> {
return Object.entries(obj).reduce(
(resultObject, [currentKey, currentValue]) => {
if (!isDefined(currentValue) || currentValue === null) {
return resultObject
}
resultObject[currentKey] = currentValue
return resultObject
},
{}
)
}
@@ -0,0 +1,4 @@
// useful in cases where presence of undefined is not desired (eg. in microORM operations)
export const removeUndefined = <T extends Record<string, any>>(obj: T): T => {
return JSON.parse(JSON.stringify(obj)) as T
}
+10
View File
@@ -0,0 +1,10 @@
export enum RuleOperator {
IN = "in",
EQ = "eq",
NE = "ne",
GT = "gt",
GTE = "gte",
LT = "lt",
LTE = "lte",
NIN = "nin",
}
@@ -0,0 +1,16 @@
export function selectorConstraintsToString(
selector: object | object[]
): string {
const selectors = Array.isArray(selector) ? selector : [selector]
return selectors
.map((selector_) => {
return Object.entries(selector_)
.map(
([key, value]: [string, any]) =>
`${key}: ${value._type ? `${value._type}(${value._value})` : value}`
)
.join(", ")
})
.join(" or ")
}
@@ -0,0 +1,45 @@
import { MedusaError } from "./errors"
/**
* Dedicated method to set metadata.
* @param obj - the entity to apply metadata to.
* @param metadata - the metadata to set
* @return resolves to the updated result.
*/
export function setMetadata(
obj: { metadata: Record<string, unknown> | null },
metadata: Record<string, unknown>
): Record<string, unknown> {
const existing = obj.metadata || {}
const newData = {}
for (const [key, value] of Object.entries(metadata)) {
if (typeof key !== "string") {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
"Key type is invalid. Metadata keys must be strings"
)
}
/**
* We reserve the empty string as a way to delete a key.
* If the value is an empty string, we don't
* set it, and if it exists in the existing metadata, we
* unset the field.
*/
if (value === "") {
if (key in existing) {
delete existing[key]
}
continue
}
newData[key] = value
}
return {
...existing,
...newData,
}
}
@@ -0,0 +1,8 @@
// DJB2 hash function
export function simpleHash(text: string): string {
let hash = 5381
for (let i = 0; i < text.length; i++) {
hash = (hash << 5) + hash + text.charCodeAt(i)
}
return hash.toString(16)
}
@@ -0,0 +1,9 @@
export const stringEqualsOrRegexMatch = (
stringOrRegex: string | RegExp,
testString: string
) => {
if (stringOrRegex instanceof RegExp) {
return stringOrRegex.test(testString)
}
return stringOrRegex === testString
}
@@ -0,0 +1,97 @@
/**
* Convert a string fields array to a specific object such as { select, relation }
* @param fields
*
* @example
* const fields = [
* "id",
* "created_at",
* "updated_at",
* "deleted_at",
* "url",
* "metadata",
* "tags.id",
* "tags.created_at",
* "tags.updated_at",
* "tags.deleted_at",
* "tags.value",
* "options.id",
* "options.created_at",
* "options.updated_at",
* "options.deleted_at",
* "options.title",
* "options.product_id",
* "options.metadata",
* "options.values.id",
* "options.values.created_at",
* "options.values.updated_at",
* "options.values.deleted_at",
* "options.values.value",
* "options.values.option_id",
* "options.values.variant_id",
* "options.values.metadata",
* ]
*
* const remoteQueryObject = stringToSelectRelationObject(fields)
*
* console.log(remoteQueryObject)
* // {
* // select: [
* // "id",
* // "created_at",
* // "updated_at",
* // "deleted_at",
* // "url",
* // "metadata",
* // "tags.id",
* // "tags.created_at",
* // "tags.updated_at",
* // "tags.deleted_at",
* // "tags.value",
* // "options.id",
* // "options.created_at",
* // "options.updated_at",
* // "options.deleted_at",
* // "options.title",
* // "options.product_id",
* // "options.metadata",
* // "options.values.id",
* // "options.values.created_at",
* // "options.values.updated_at",
* // "options.values.deleted_at",
* // "options.values.value",
* // "options.values.option_id",
* // "options.values.variant_id",
* // "options.values.metadata",
* // ],
* // relations: ["tags", "options", "options.values"],
* // }
*/
export function stringToSelectRelationObject(fields: string[]): {
select: string[]
relations: string[]
} {
const tempResult = {
select: new Set<string>(),
relations: new Set<string>(),
}
for (const field of fields) {
tempResult.select.add(field)
if (!field.includes(".")) {
continue
}
const segments = field.split(".")
segments.pop()
const relationPath = segments.join(".")
tempResult.relations.add(relationPath)
}
return {
select: Array.from(tempResult.select),
relations: Array.from(tempResult.relations),
}
}
@@ -0,0 +1,62 @@
const isObject = (value: any): value is object =>
typeof value === "object" &&
value != null &&
!(value instanceof Boolean) &&
!(value instanceof Date) &&
!(value instanceof Number) &&
!(value instanceof RegExp) &&
!(value instanceof String)
const isPrimitive = (val) => {
return val !== Object(val)
}
function decycle(object: any, replacer?: Function | null) {
const objects = new WeakMap()
function deepCopy(value, path) {
let oldPath
let newObj
if (replacer != null) {
value = replacer(value)
}
if (isObject(value)) {
oldPath = objects.get(value)
if (oldPath !== undefined) {
return { $ref: oldPath }
}
objects.set(value, path)
if (Array.isArray(value)) {
newObj = []
value.forEach((el, idx) => {
newObj[idx] = deepCopy(el, path + "[" + idx + "]")
})
} else {
newObj = {}
Object.keys(value).forEach((name) => {
newObj[name] = deepCopy(
value[name],
path + "[" + JSON.stringify(name) + "]"
)
})
}
return newObj
}
return !isPrimitive(value) ? value + "" : value
}
return deepCopy(object, "$")
}
export function stringifyCircular(
object: any,
replacer?: Function | null,
space?: number
): string {
return JSON.stringify(decycle(object, replacer), null, space)
}
@@ -0,0 +1,7 @@
export function toCamelCase(str: string): string {
return /^([a-z]+)(([A-Z]([a-z]+))+)$/.test(str)
? str
: str
.toLowerCase()
.replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase())
}
@@ -0,0 +1,5 @@
export const kebabCase = (string) =>
string
.replace(/([a-z])([A-Z])/g, "$1-$2")
.replace(/[\s_]+/g, "-")
.toLowerCase()
@@ -0,0 +1,5 @@
export function toPascalCase(s: string): string {
return s.replace(/(^\w|_\w)/g, (match) =>
match.replace(/_/g, "").toUpperCase()
)
}
@@ -0,0 +1,3 @@
export function doNotForceTransaction(): boolean {
return false
}
@@ -0,0 +1,2 @@
export * from "./do-not-force-transaction"
export * from "./should-force-transaction"
@@ -0,0 +1,5 @@
import { MODULE_RESOURCE_TYPE } from "@medusajs/types"
export function shouldForceTransaction(target: any): boolean {
return target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED
}
@@ -0,0 +1,18 @@
export function trimZeros(value: string) {
const [whole, fraction] = value.split(".")
if (fraction) {
const exp = fraction.split("e")
const decimal = exp[0].replace(/0+$/, "")
const expStr = exp.length > 1 ? `e${exp[1]}` : ""
if (!decimal) {
return whole + expStr
}
return `${whole}.${decimal}` + expStr
}
return whole
}
@@ -0,0 +1,3 @@
export function upperCaseFirst(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1)
}
@@ -0,0 +1,37 @@
import { NextFunction, Request, RequestHandler, Response } from "express"
type handler = (req: Request, res: Response) => Promise<void>
export const wrapHandler = (fn: handler): RequestHandler => {
return async (req: Request, res: Response, next: NextFunction) => {
const req_ = req as Request & { errors?: Error[] }
if (req_?.errors?.length) {
return res.status(400).json({
errors: req_.errors,
message:
"Provided request body contains errors. Please check the data and retry the request",
})
}
try {
return await fn(req, res)
} catch (err) {
next(err)
}
}
}
/**
* @schema MultipleErrors
* title: "Multiple Errors"
* type: object
* properties:
* errors:
* type: array
* description: Array of errors
* items:
* $ref: "#/components/schemas/Error"
* message:
* type: string
* default: "Provided request body contains errors. Please check the data and retry the request"
*/
+10
View File
@@ -0,0 +1,10 @@
export * from "./mikro-orm/big-number-field"
export * from "./mikro-orm/mikro-orm-create-connection"
export * from "./mikro-orm/mikro-orm-free-text-search-filter"
export * from "./mikro-orm/mikro-orm-repository"
export * from "./mikro-orm/mikro-orm-soft-deletable-filter"
export * from "./mikro-orm/mikro-orm-serializer"
export * from "./mikro-orm/utils"
export * from "./mikro-orm/decorators/searchable"
export * from "./repositories"
export * from "./utils"
@@ -0,0 +1,351 @@
import {
Collection,
Entity,
ManyToOne,
OneToMany,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { Searchable } from "../decorators/searchable"
// Circular dependency one level
@Entity()
class RecursiveEntity1 {
constructor(props: { id: string; deleted_at: Date | null }) {
this.id = props.id
this.deleted_at = props.deleted_at
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@OneToMany(() => RecursiveEntity2, (entity2) => entity2.entity1, {
cascade: ["soft-remove"] as any,
})
entity2 = new Collection<RecursiveEntity2>(this)
}
@Entity()
class RecursiveEntity2 {
constructor(props: {
id: string
deleted_at: Date | null
entity1: RecursiveEntity1
}) {
this.id = props.id
this.deleted_at = props.deleted_at
this.entity1 = props.entity1
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@ManyToOne(() => RecursiveEntity1, {
cascade: ["soft-remove"] as any,
})
entity1: RecursiveEntity1
}
// No circular dependency
@Entity()
class Entity1 {
constructor(props: { id: string; deleted_at: Date | null }) {
this.id = props.id
this.deleted_at = props.deleted_at
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@OneToMany(() => Entity2, (entity2) => entity2.entity1, {
cascade: ["soft-remove"] as any,
})
entity2 = new Collection<Entity2>(this)
}
@Entity()
class Entity2 {
constructor(props: {
id: string
deleted_at: Date | null
entity1: Entity1
}) {
this.id = props.id
this.deleted_at = props.deleted_at
this.entity1 = props.entity1
this.entity1_id = props.entity1.id
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@ManyToOne(() => Entity1, { mapToPk: true })
entity1_id: string
@ManyToOne(() => Entity1, { persist: false })
entity1: Entity1
}
// Circular dependency deep level
@Entity()
class DeepRecursiveEntity1 {
constructor(props: { id: string; deleted_at: Date | null }) {
this.id = props.id
this.deleted_at = props.deleted_at
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@OneToMany(() => DeepRecursiveEntity2, (entity2) => entity2.entity1, {
cascade: ["soft-remove"] as any,
})
entity2 = new Collection<DeepRecursiveEntity2>(this)
}
@Entity()
class DeepRecursiveEntity2 {
constructor(props: {
id: string
deleted_at: Date | null
entity1: DeepRecursiveEntity1
entity3: DeepRecursiveEntity3
}) {
this.id = props.id
this.deleted_at = props.deleted_at
this.entity3 = props.entity3
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@ManyToOne(() => DeepRecursiveEntity1)
entity1: DeepRecursiveEntity1
@ManyToOne(() => DeepRecursiveEntity3, {
cascade: ["soft-remove"] as any,
})
entity3: DeepRecursiveEntity3
}
@Entity()
class DeepRecursiveEntity3 {
constructor(props: {
id: string
deleted_at: Date | null
entity1: DeepRecursiveEntity1
}) {
this.id = props.id
this.deleted_at = props.deleted_at
this.entity1 = props.entity1
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@ManyToOne(() => DeepRecursiveEntity1, {
cascade: ["soft-remove"] as any,
})
entity1: DeepRecursiveEntity1
}
@Entity()
class DeepRecursiveEntity4 {
constructor(props: {
id: string
deleted_at: Date | null
entity1: DeepRecursiveEntity1
}) {
this.id = props.id
this.deleted_at = props.deleted_at
this.entity1 = props.entity1
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@ManyToOne(() => DeepRecursiveEntity1)
entity1: DeepRecursiveEntity1
}
// Internal circular dependency
@Entity()
class InternalCircularDependencyEntity1 {
constructor(props: {
id: string
deleted_at: Date | null
parent?: InternalCircularDependencyEntity1
}) {
this.id = props.id
this.deleted_at = props.deleted_at
if (props.parent) {
this.parent = props.parent
}
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@OneToMany(
() => InternalCircularDependencyEntity1,
(entity) => entity.parent,
{
cascade: ["soft-remove"] as any,
}
)
children = new Collection<InternalCircularDependencyEntity1>(this)
@ManyToOne(() => InternalCircularDependencyEntity1)
parent: InternalCircularDependencyEntity1
}
// With un decorated prop
@Entity()
class Entity1WithUnDecoratedProp {
constructor(props: { id: string; deleted_at: Date | null }) {
this.id = props.id
this.deleted_at = props.deleted_at
}
unknownProp: string
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@OneToMany(() => Entity2WithUnDecoratedProp, (entity2) => entity2.entity1, {
cascade: ["soft-remove"] as any,
})
entity2 = new Collection<Entity2WithUnDecoratedProp>(this)
}
@Entity()
class Entity2WithUnDecoratedProp {
constructor(props: {
id: string
deleted_at: Date | null
entity1: Entity1WithUnDecoratedProp
}) {
this.id = props.id
this.deleted_at = props.deleted_at
this.entity1 = props.entity1
this.entity1_id = props.entity1.id
}
unknownProp: string
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@ManyToOne(() => Entity1WithUnDecoratedProp, { mapToPk: true })
entity1_id: string
@ManyToOne(() => Entity1WithUnDecoratedProp, { persist: false })
entity1: Entity1WithUnDecoratedProp
}
// Searchable fields
@Entity()
class SearchableEntity1 {
constructor(props: { id: string; deleted_at: Date | null }) {
this.id = props.id
this.deleted_at = props.deleted_at
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@Searchable()
@Property()
searchableField: string
@Searchable()
@OneToMany(() => SearchableEntity2, (entity2) => entity2.entity1)
entity2 = new Collection<SearchableEntity2>(this)
}
@Entity()
class SearchableEntity2 {
constructor(props: {
id: string
deleted_at: Date | null
entity1: SearchableEntity1
}) {
this.id = props.id
this.deleted_at = props.deleted_at
this.entity1 = props.entity1
this.entity1_id = props.entity1.id
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@Searchable()
@Property()
searchableField: string
@ManyToOne(() => SearchableEntity1, { mapToPk: true })
entity1_id: string
@ManyToOne(() => SearchableEntity1, { persist: false })
entity1: SearchableEntity1
}
export {
RecursiveEntity1,
RecursiveEntity2,
Entity1,
Entity2,
DeepRecursiveEntity1,
DeepRecursiveEntity2,
DeepRecursiveEntity3,
DeepRecursiveEntity4,
InternalCircularDependencyEntity1,
Entity1WithUnDecoratedProp,
Entity2WithUnDecoratedProp,
SearchableEntity1,
SearchableEntity2,
}
@@ -0,0 +1,78 @@
import { BigNumberRawValue } from "@medusajs/types"
import { Entity, MikroORM, PrimaryKey } from "@mikro-orm/core"
import { BigNumber } from "../../../totals/big-number"
import { MikroOrmBigNumberProperty } from "../big-number-field"
@Entity()
class TestAmount {
@PrimaryKey()
id: string
@MikroOrmBigNumberProperty()
amount: BigNumber | number
raw_amount: BigNumberRawValue
@MikroOrmBigNumberProperty({ nullable: true })
nullable_amount: BigNumber | number | null = null
raw_nullable_amount: BigNumberRawValue | null = null
}
describe("@MikroOrmBigNumberProperty", () => {
let orm!: MikroORM
beforeEach(async () => {
orm = await MikroORM.init({
entities: [TestAmount],
dbName: "test",
type: "postgresql",
})
})
afterEach(async () => {
await orm.close()
})
it("should correctly assign and update BigNumber values", () => {
const testAmount = new TestAmount()
expect(testAmount.amount).toBeUndefined()
expect(testAmount.raw_amount).toBeUndefined()
testAmount.amount = 100
expect(testAmount.amount).toEqual(100)
expect(testAmount.raw_amount).toEqual({
value: "100",
precision: 20,
})
try {
;(testAmount as any).amount = null
} catch (e) {
expect(e.message).toEqual(
"Invalid BigNumber value: null. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue"
)
}
testAmount.nullable_amount = null
expect(testAmount.nullable_amount).toEqual(null)
// Update the amount
testAmount.amount = 200
expect(testAmount.amount).toEqual(200)
expect(testAmount.raw_amount).toEqual({
value: "200",
precision: 20,
})
// Update with big number
testAmount.amount = new BigNumber(300, { precision: 5 })
expect(testAmount.amount).toEqual(300)
expect(testAmount.raw_amount).toEqual({ value: "300", precision: 5 })
})
})
Loaded 100 of 1436 files, more files were not shown because too many files have changed in this diff. Show more