Explorar o código

feat(payments-plugin): URL casing and merge

Martijn hai 1 ano
pai
achega
887d975187
Modificáronse 100 ficheiros con 652 adicións e 530 borrados
  1. 114 0
      docs/docs/guides/extending-the-admin-ui/defining-routes/index.md
  2. 1 1
      docs/package.json
  3. 0 0
      e2e-common/vitest.config.mts
  4. 20 23
      package.json
  5. 5 5
      packages/admin-ui-plugin/package.json
  6. 5 5
      packages/admin-ui/angular.json
  7. 115 116
      packages/admin-ui/package.json
  8. 1 0
      packages/admin-ui/src/lib/core/src/app.component.ts
  9. 1 1
      packages/admin-ui/src/lib/core/src/common/base-detail.component.ts
  10. 4 1
      packages/admin-ui/src/lib/core/src/components/breadcrumb/breadcrumb.component.scss
  11. 3 1
      packages/admin-ui/src/lib/core/src/components/main-nav/main-nav.component.scss
  12. 3 3
      packages/admin-ui/src/lib/core/src/components/theme-switcher/theme-switcher.component.scss
  13. 1 1
      packages/admin-ui/src/lib/core/src/data/data.module.ts
  14. 1 1
      packages/admin-ui/src/lib/core/src/extension/register-route-component.ts
  15. 1 0
      packages/admin-ui/src/lib/core/src/shared/components/dropdown/dropdown-menu.component.scss
  16. 3 1
      packages/admin-ui/src/lib/core/src/shared/components/page-header-tabs/page-header-tabs.component.scss
  17. 2 0
      packages/admin-ui/src/lib/core/src/shared/components/page-title/page-title.component.scss
  18. 1 1
      packages/admin-ui/src/lib/dashboard/src/widgets/order-summary-widget/order-summary-widget.component.html
  19. 2 1
      packages/admin-ui/src/lib/dashboard/src/widgets/order-summary-widget/order-summary-widget.component.scss
  20. 1 1
      packages/admin-ui/src/lib/static/polyfills.ts
  21. 3 0
      packages/admin-ui/src/lib/static/styles/_mixins.scss
  22. 2 1
      packages/admin-ui/src/lib/static/styles/global/_buttons.scss
  23. 6 0
      packages/admin-ui/src/lib/static/styles/global/_global.scss
  24. 21 0
      packages/admin-ui/src/lib/static/styles/global/_overrides.scss
  25. 1 0
      packages/admin-ui/src/lib/static/styles/styles.scss
  26. 3 2
      packages/admin-ui/src/lib/static/styles/theme/dark.scss
  27. 12 1
      packages/admin-ui/src/lib/static/styles/theme/default.scss
  28. 15 15
      packages/asset-server-plugin/package.json
  29. 35 10
      packages/asset-server-plugin/src/plugin.ts
  30. 32 7
      packages/asset-server-plugin/src/transform-image.ts
  31. 2 2
      packages/cli/package.json
  32. 2 1
      packages/cli/src/commands/add/ui-extensions/codemods/update-admin-ui-plugin-init/update-admin-ui-plugin-init.spec.ts
  33. 0 0
      packages/cli/vitest.config.mts
  34. 2 2
      packages/common/package.json
  35. 0 0
      packages/common/vitest.config.mts
  36. 1 4
      packages/core/build/gulpfile.ts
  37. 0 1
      packages/core/build/tsconfig.cli.json
  38. 2 2
      packages/core/e2e/__snapshots__/collection.e2e-spec.ts.snap
  39. 2 2
      packages/core/e2e/__snapshots__/promotion.e2e-spec.ts.snap
  40. 2 2
      packages/core/e2e/database-transactions.e2e-spec.ts
  41. 15 18
      packages/core/e2e/error-handler-strategy.e2e-spec.ts
  42. 2 2
      packages/core/e2e/fixtures/test-plugins/list-query-plugin.ts
  43. 1 1
      packages/core/e2e/money-strategy.e2e-spec.ts
  44. 44 46
      packages/core/package.json
  45. 2 2
      packages/core/src/api/common/user-has-permissions-on-custom-field.ts
  46. 11 6
      packages/core/src/api/config/generate-list-options.ts
  47. 1 1
      packages/core/src/api/config/generate-resolvers.ts
  48. 3 1
      packages/core/src/api/decorators/relations.decorator.ts
  49. 1 1
      packages/core/src/api/resolvers/admin/customer.resolver.ts
  50. 4 3
      packages/core/src/api/resolvers/admin/draft-order.resolver.ts
  51. 1 1
      packages/core/src/api/resolvers/admin/duplicate-entity.resolver.ts
  52. 1 1
      packages/core/src/api/resolvers/admin/facet.resolver.ts
  53. 1 1
      packages/core/src/api/resolvers/admin/payment-method.resolver.ts
  54. 1 1
      packages/core/src/api/resolvers/admin/seller.resolver.ts
  55. 1 1
      packages/core/src/api/resolvers/admin/shipping-method.resolver.ts
  56. 1 1
      packages/core/src/api/resolvers/entity/administrator-entity.resolver.ts
  57. 1 1
      packages/core/src/api/resolvers/entity/channel-entity.resolver.ts
  58. 1 1
      packages/core/src/api/resolvers/entity/fulfillment-entity.resolver.ts
  59. 1 1
      packages/core/src/api/resolvers/entity/fulfillment-line-entity.resolver.ts
  60. 1 2
      packages/core/src/api/resolvers/entity/order-entity.resolver.ts
  61. 1 1
      packages/core/src/api/resolvers/entity/payment-entity.resolver.ts
  62. 1 1
      packages/core/src/api/resolvers/entity/product-variant-entity.resolver.ts
  63. 2 2
      packages/core/src/api/resolvers/entity/refund-entity.resolver.ts
  64. 1 1
      packages/core/src/api/resolvers/entity/refund-line-entity.resolver.ts
  65. 0 7
      packages/core/src/cli/cli-utils.ts
  66. 0 133
      packages/core/src/cli/vendure-cli.ts
  67. 1 1
      packages/core/src/common/utils.ts
  68. 1 1
      packages/core/src/config/asset-import-strategy/default-asset-import-strategy.ts
  69. 1 1
      packages/core/src/config/auth/default-password-validation-strategy.ts
  70. 1 1
      packages/core/src/config/auth/password-validation-strategy.ts
  71. 0 2
      packages/core/src/config/catalog/default-collection-filters.ts
  72. 6 4
      packages/core/src/config/catalog/default-product-variant-price-calculation-strategy.spec.ts
  73. 4 3
      packages/core/src/config/catalog/default-stock-location-strategy.ts
  74. 1 1
      packages/core/src/config/custom-field/custom-field-types.ts
  75. 2 2
      packages/core/src/config/entity/entity-duplicator.ts
  76. 4 4
      packages/core/src/config/entity/entity-duplicators/collection-duplicator.ts
  77. 5 4
      packages/core/src/config/entity/entity-duplicators/facet-duplicator.ts
  78. 10 9
      packages/core/src/config/entity/entity-duplicators/product-duplicator.ts
  79. 5 6
      packages/core/src/config/entity/entity-duplicators/promotion-duplicator.ts
  80. 5 2
      packages/core/src/config/fulfillment/default-fulfillment-process.ts
  81. 2 2
      packages/core/src/config/order/active-order-strategy.ts
  82. 2 2
      packages/core/src/config/order/default-guest-checkout-strategy.ts
  83. 5 6
      packages/core/src/config/order/default-order-process.ts
  84. 3 2
      packages/core/src/config/order/guest-checkout-strategy.ts
  85. 3 5
      packages/core/src/config/order/order-seller-strategy.ts
  86. 1 1
      packages/core/src/config/payment/default-payment-process.ts
  87. 1 1
      packages/core/src/config/promotion/conditions/customer-group-condition.ts
  88. 1 1
      packages/core/src/config/shipping-method/default-shipping-line-assignment-strategy.ts
  89. 2 2
      packages/core/src/config/shipping-method/shipping-line-assignment-strategy.ts
  90. 1 1
      packages/core/src/config/system/error-handler-strategy.ts
  91. 1 2
      packages/core/src/data-import/providers/asset-importer/asset-importer.ts
  92. 2 2
      packages/core/src/data-import/providers/import-parser/import-parser.ts
  93. 2 1
      packages/core/src/data-import/providers/importer/fast-importer.service.ts
  94. 1 1
      packages/core/src/data-import/providers/populator/populator.ts
  95. 13 1
      packages/core/src/entity/asset/asset.entity.ts
  96. 21 1
      packages/core/src/entity/channel/channel.entity.ts
  97. 2 2
      packages/core/src/entity/collection/collection.entity.ts
  98. 9 1
      packages/core/src/entity/facet-value/facet-value.entity.ts
  99. 5 1
      packages/core/src/entity/fulfillment/fulfillment.entity.ts
  100. 3 3
      packages/core/src/entity/order/order.entity.ts

+ 114 - 0
docs/docs/guides/extending-the-admin-ui/defining-routes/index.md

@@ -144,6 +144,14 @@ export const config: VendureConfig = {
 
 Note that by specifying `route: 'greet'`, we are "mounting" the routes at the `/extensions/greet` path.
 
+:::info
+
+The `/extensions/` prefix is used to avoid conflicts with built-in routes. From Vendure v2.2.0 it is possible to customize
+this prefix using the `prefix` property. See the section on [overriding built-in routes](#overriding-built-in-routes) for 
+more information.
+
+:::
+
 The `filePath` property is relative to the directory specified in the `extensionPath` property. In this case, the `routes.ts` file is located at `src/plugins/greeter/ui/routes.ts`.
 
 Now go to the Admin UI app in your browser and log in. You should now be able to manually enter the URL `http://localhost:3000/admin/extensions/greet` and you should see the component with the "Hello!" header:
@@ -595,6 +603,112 @@ export function Test() {
 </TabItem>
 </Tabs>
 
+## Overriding built-in routes
+
+From Vendure v2.2.0, it is possible to override any of the built-in Admin UI routes. This is useful if you want to completely
+replace a built-in route with your own custom component.
+
+To do so, you'll need to specify the route `prefix` to be `''`, and then specify a `route` property which matches
+a built-in route. 
+
+For example, let's say we want to override the product detail page. The full path of that route is:
+
+```
+/catalog/products/:id
+```
+
+```ts title="src/vendure-config.ts"
+import { VendureConfig } from '@vendure/core';
+import { AdminUiPlugin } from '@vendure/admin-ui-plugin';
+import { compileUiExtensions } from '@vendure/ui-devkit/compiler';
+import * as path from 'path';
+
+export const config: VendureConfig = {
+    // ...
+    plugins: [
+        AdminUiPlugin.init({
+            port: 3002,
+            app: compileUiExtensions({
+                outputPath: path.join(__dirname, '../admin-ui'),
+                extensions: [
+                    {
+                        id: 'my-plugin',
+                        extensionPath: path.join(__dirname, 'plugins/my-plugin/ui'),
+                        routes: [
+                            {
+                                // Setting the prefix to '' means that we won't add the
+                                // default `/extensions/` prefix to the route
+                                prefix: '',
+                                // This part matches the built-in route path for the 
+                                // "catalog" module
+                                route: 'catalog',
+                                filePath: 'routes.ts',
+                            },
+                        ],
+                    },
+                ],
+            }),
+        }),
+    ],
+};
+```
+
+Then in the `routes.ts` file, you can define a route which matches the built-in route:
+
+<Tabs groupId="framework">
+<TabItem value="Angular" label="Angular" default>
+
+```ts title="src/plugins/my-plugin/ui/routes.ts"
+import { GetProductDetailDocument, registerRouteComponent } from '@vendure/admin-ui/core';
+import { MyProductDetailComponent } from './components/product-detail/product-detail.component';
+
+export default [
+    registerRouteComponent({
+        component: MyProductDetailComponent,
+        // The path must then match the remainder
+        // of the built-in route path
+        path: 'products/:id',
+        // We can re-use the GraphQL query from the core to get
+        // access to the same data in our component
+        query: GetProductDetailDocument,
+        entityKey: 'product',
+        getBreadcrumbs: entity => [
+            { label: 'breadcrumb.products', link: ['/catalog/products'] },
+            { label: entity?.name ?? 'catalog.create-new-product', link: ['.'] },
+        ],
+    }),
+];
+```
+
+</TabItem>
+<TabItem value="React" label="React">
+
+```ts title="src/plugins/my-plugin/ui/routes.ts"
+import { GetProductDetailDocument } from '@vendure/admin-ui/core';
+import { registerReactRouteComponent } from '@vendure/admin-ui/react';
+import { MyProductDetail } from './components/ProductDetail';
+
+export default [
+    registerReactRouteComponent({
+        component: MyProductDetail,
+        // The path must then match the remainder
+        // of the built-in route path
+        path: 'products/:id',
+        // We can re-use the GraphQL query from the core to get
+        // access to the same data in our component
+        query: GetProductDetailDocument,
+        entityKey: 'product',
+        getBreadcrumbs: entity => [
+            { label: 'breadcrumb.products', link: ['/catalog/products'] },
+            { label: entity?.name ?? 'catalog.create-new-product', link: ['.'] },
+        ],
+    }),
+];
+```
+
+</TabItem>
+</Tabs>
+
 ## Advanced configuration
 
 The Admin UI app routing is built on top of the [Angular router](https://angular.io/guide/routing-overview) - a very advanced and robust router. As such, you are able to tap into all the advanced features it provides by using the `routeConfig` property, which takes an Angular [`Route` definition object](https://angular.io/api/router/Route) and passes it directly to the router.

+ 1 - 1
docs/package.json

@@ -27,7 +27,7 @@
   "devDependencies": {
     "@docusaurus/module-type-aliases": "2.4.1",
     "@tsconfig/docusaurus": "^1.0.5",
-    "typescript": "5.1.6"
+    "typescript": "5.3.3"
   },
   "browserslist": {
     "production": [

+ 0 - 0
e2e-common/vitest.config.ts → e2e-common/vitest.config.mts


+ 20 - 23
package.json

@@ -3,7 +3,7 @@
   "version": "0.0.0",
   "private": true,
   "engines": {
-    "node": ">= 20"
+    "node": ">= 18"
   },
   "scripts": {
     "watch": "lerna run watch --parallel",
@@ -28,36 +28,33 @@
     "publish-local": "lerna version --no-git-tag-version && cd scripts && ./publish-to-verdaccio.sh"
   },
   "devDependencies": {
-    "@commitlint/cli": "^18.6.1",
-    "@commitlint/config-conventional": "^18.6.2",
-    "@graphql-codegen/add": "5.0.0",
-    "@graphql-codegen/cli": "5.0.0",
-    "@graphql-codegen/fragment-matcher": "5.0.0",
-    "@graphql-codegen/typed-document-node": "^5.0.1",
-    "@graphql-codegen/typescript": "4.0.1",
-    "@graphql-codegen/typescript-operations": "4.0.1",
-    "@graphql-tools/schema": "^10.0.0",
-    "@swc/core": "^1.3.78",
-    "@types/klaw-sync": "^6.0.1",
+    "@commitlint/cli": "^19.1.0",
+    "@commitlint/config-conventional": "^19.1.0",
+    "@graphql-codegen/add": "5.0.2",
+    "@graphql-codegen/cli": "5.0.2",
+    "@graphql-codegen/fragment-matcher": "5.0.2",
+    "@graphql-codegen/typed-document-node": "^5.0.6",
+    "@graphql-codegen/typescript": "4.0.6",
+    "@graphql-codegen/typescript-operations": "4.2.0",
+    "@graphql-tools/schema": "^10.0.3",
+    "@swc/core": "^1.4.6",
+    "@types/klaw-sync": "^6.0.5",
     "@types/node": "^20.11.19",
-    "concurrently": "^8.2.1",
+    "concurrently": "^8.2.2",
     "conventional-changelog-core": "^7.0.0",
     "cross-env": "^7.0.3",
     "find": "^0.3.0",
-    "graphql": "16.8.0",
+    "graphql": "16.8.1",
     "husky": "^4.3.0",
     "klaw-sync": "^6.0.0",
     "lerna": "^8.1.2",
     "lint-staged": "^10.5.4",
-    "prettier": "^2.2.1",
-    "tinybench": "^2.5.1",
-    "ts-node": "^10.9.1",
-    "typescript": "5.1.6",
-    "unplugin-swc": "^1.3.2",
-    "vitest": "^0.34.2"
-  },
-  "resolutions": {
-    "npm-packlist": "1.1.12"
+    "prettier": "^3.2.5",
+    "tinybench": "^2.6.0",
+    "ts-node": "^10.9.2",
+    "typescript": "5.3.3",
+    "unplugin-swc": "^1.4.4",
+    "vitest": "^1.3.1"
   },
   "workspaces": {
     "packages": [

+ 5 - 5
packages/admin-ui-plugin/package.json

@@ -19,13 +19,13 @@
         "access": "public"
     },
     "devDependencies": {
-        "@types/express": "^4.17.8",
-        "@types/fs-extra": "^9.0.1",
+        "@types/express": "^4.17.21",
+        "@types/fs-extra": "^11.0.4",
         "@vendure/common": "2.2.0-next.5",
         "@vendure/core": "2.2.0-next.5",
-        "express": "^4.17.1",
-        "rimraf": "^3.0.2",
-        "typescript": "5.1.6"
+        "express": "^4.18.3",
+        "rimraf": "^5.0.5",
+        "typescript": "5.4.2"
     },
     "dependencies": {
         "date-fns": "^2.30.0",

+ 5 - 5
packages/admin-ui/angular.json

@@ -94,24 +94,24 @@
         "serve": {
           "builder": "@angular-devkit/build-angular:dev-server",
           "options": {
-            "browserTarget": "vendure-admin:build"
+            "buildTarget": "vendure-admin:build"
           },
           "configurations": {
             "production": {
-              "browserTarget": "vendure-admin:build:production"
+              "buildTarget": "vendure-admin:build:production"
             },
             "plugin": {
-              "browserTarget": "vendure-admin:build:plugin-watch"
+              "buildTarget": "vendure-admin:build:plugin-watch"
             },
             "plugin-dev": {
-              "browserTarget": "vendure-admin:build:plugin-dev-watch"
+              "buildTarget": "vendure-admin:build:plugin-dev-watch"
             }
           }
         },
         "extract-i18n": {
           "builder": "@angular-devkit/build-angular:extract-i18n",
           "options": {
-            "browserTarget": "vendure-admin:build"
+            "buildTarget": "vendure-admin:build"
           }
         },
         "test": {

+ 115 - 116
packages/admin-ui/package.json

@@ -1,118 +1,117 @@
 {
-    "name": "@vendure/admin-ui",
-    "version": "2.2.0-next.5",
-    "license": "MIT",
-    "scripts": {
-        "ng": "ng",
-        "start": "node scripts/set-version.js && ng serve",
-        "build:app": "yarn ng build vendure-admin --configuration production",
-        "build": "node scripts/copy-package-json.js && node scripts/set-version.js && node scripts/build-public-api.js && yarn ng build vendure-admin-lib --configuration production && node scripts/compile-styles.js",
-        "watch": "ng build --watch=true",
-        "test": "ng test --watch=false --browsers=ChromeHeadlessCI --progress=false",
-        "lint": "ng lint --fix",
-        "extract-translations": "node scripts/extract-translations.js",
-        "ngcc": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
-    },
-    "homepage": "https://www.vendure.io/",
-    "funding": "https://github.com/sponsors/michaelbromley",
-    "publishConfig": {
-        "access": "public",
-        "directory": "package"
-    },
-    "//": "These exports are just here so that we can do local testing of ui-devkit compilation by allowing the ui-devkit package to find its way to the compiled package",
-    "exports": {
-        ".": {
-            "types": "./package/index.d.ts",
-            "esm2022": "./package/esm2022/vendure-admin-ui.mjs",
-            "esm": "./package/esm2022/vendure-admin-ui.mjs",
-            "default": "./package/fesm2022/vendure-admin-ui.mjs"
-        }
-    },
-    "dependencies": {
-        "@angular/animations": "^16.2.2",
-        "@angular/cdk": "^16.2.1",
-        "@angular/common": "^16.2.2",
-        "@angular/core": "^16.2.2",
-        "@angular/forms": "^16.2.2",
-        "@angular/language-service": "^16.2.2",
-        "@angular/platform-browser": "^16.2.2",
-        "@angular/platform-browser-dynamic": "^16.2.2",
-        "@angular/router": "^16.2.2",
-        "@apollo/client": "^3.8.1",
-        "@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
-        "@cds/core": "^6.6.0",
-        "@clr/angular": "^15.11.0",
-        "@clr/core": "^4.0.15",
-        "@clr/icons": "^13.0.2",
-        "@clr/ui": "^15.11.0",
-        "@messageformat/core": "^3.2.0",
-        "@ng-select/ng-select": "^11.1.1",
-        "@ngx-translate/core": "^15.0.0",
-        "@ngx-translate/http-loader": "^8.0.0",
-        "@vendure/common": "2.2.0-next.5",
-        "@webcomponents/custom-elements": "^1.6.0",
-        "apollo-angular": "^5.0.0",
-        "apollo-upload-client": "^17.0.0",
-        "chartist": "^1.3.0",
-        "codejar": "^4.2.0",
-        "core-js": "^3.32.1",
-        "dayjs": "^1.11.9",
-        "graphql": "16.8.0",
-        "just-extend": "^6.2.0",
-        "messageformat": "2.3.0",
-        "ngx-pagination": "^6.0.3",
-        "ngx-translate-messageformat-compiler": "^6.5.0",
-        "prosemirror-commands": "^1.5.2",
-        "prosemirror-dropcursor": "^1.8.1",
-        "prosemirror-gapcursor": "^1.3.2",
-        "prosemirror-history": "^1.3.2",
-        "prosemirror-inputrules": "^1.2.1",
-        "prosemirror-keymap": "^1.2.2",
-        "prosemirror-menu": "^1.2.4",
-        "prosemirror-schema-basic": "^1.2.2",
-        "prosemirror-schema-list": "^1.3.0",
-        "prosemirror-state": "^1.4.3",
-        "prosemirror-tables": "^1.3.4",
-        "react": "^18.2.0",
-        "react-dom": "^18.2.0",
-        "rxjs": "^7.8.1",
-        "tslib": "^2.6.2",
-        "zone.js": "~0.13.1"
-    },
-    "devDependencies": {
-        "@angular-devkit/build-angular": "^16.2.0",
-        "@angular-eslint/builder": "^16.1.1",
-        "@angular-eslint/eslint-plugin": "^16.1.1",
-        "@angular-eslint/eslint-plugin-template": "^16.1.1",
-        "@angular-eslint/schematics": "^16.1.1",
-        "@angular-eslint/template-parser": "^16.1.1",
-        "@angular/cli": "^16.2.0",
-        "@angular/compiler": "^16.2.2",
-        "@angular/compiler-cli": "^16.2.2",
-        "@types/jasmine": "~4.3.5",
-        "@types/jasminewd2": "~2.0.10",
-        "@types/node": "^18.17.9",
-        "@types/react": "^18.2.21",
-        "@typescript-eslint/eslint-plugin": "^5.59.2",
-        "@typescript-eslint/parser": "^5.59.2",
-        "@vendure/ngx-translate-extract": "^8.2.2",
-        "cross-spawn": "^7.0.3",
-        "eslint": "^8.41.0",
-        "eslint-plugin-import": "^2.27.5",
-        "eslint-plugin-jsdoc": "^45.0.0",
-        "eslint-plugin-prefer-arrow": "^1.2.3",
-        "fs-extra": "^11.1.1",
-        "jasmine-core": "~3.99.1",
-        "jasmine-spec-reporter": "~7.0.0",
-        "karma": "~6.3.4",
-        "karma-chrome-launcher": "~3.1.0",
-        "karma-coverage-istanbul-reporter": "~3.0.2",
-        "karma-jasmine": "~4.0.0",
-        "karma-jasmine-html-reporter": "^1.7.0",
-        "karma-mocha-reporter": "^2.2.5",
-        "ng-packagr": "16.2.1",
-        "puppeteer": "^19.8.3",
-        "rimraf": "^3.0.2",
-        "typescript": "5.1.6"
+  "name": "@vendure/admin-ui",
+  "version": "2.2.0-next.5",
+  "license": "MIT",
+  "scripts": {
+    "ng": "ng",
+    "start": "node scripts/set-version.js && ng serve",
+    "build:app": "yarn ng build vendure-admin --configuration production",
+    "build": "node scripts/copy-package-json.js && node scripts/set-version.js && node scripts/build-public-api.js && yarn ng build vendure-admin-lib --configuration production && node scripts/compile-styles.js",
+    "watch": "ng build --watch=true",
+    "test": "ng test --watch=false --browsers=ChromeHeadlessCI --progress=false",
+    "lint": "ng lint --fix",
+    "extract-translations": "node scripts/extract-translations.js",
+    "ngcc": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
+  },
+  "homepage": "https://www.vendure.io/",
+  "funding": "https://github.com/sponsors/michaelbromley",
+  "publishConfig": {
+    "access": "public",
+    "directory": "package"
+  },
+  "//": "These exports are just here so that we can do local testing of ui-devkit compilation by allowing the ui-devkit package to find its way to the compiled package",
+  "exports": {
+    ".": {
+      "types": "./package/index.d.ts",
+      "esm2022": "./package/esm2022/vendure-admin-ui.mjs",
+      "esm": "./package/esm2022/vendure-admin-ui.mjs",
+      "default": "./package/fesm2022/vendure-admin-ui.mjs"
     }
-}
+  },
+  "dependencies": {
+    "@angular/animations": "^17.2.4",
+    "@angular/cdk": "^17.2.2",
+    "@angular/common": "^17.2.4",
+    "@angular/core": "^17.2.4",
+    "@angular/forms": "^17.2.4",
+    "@angular/language-service": "^17.2.4",
+    "@angular/platform-browser": "^17.2.4",
+    "@angular/platform-browser-dynamic": "^17.2.4",
+    "@angular/router": "^17.2.4",
+    "@apollo/client": "^3.9.6",
+    "@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
+    "@cds/core": "^6.9.2",
+    "@clr/angular": "^17.0.1",
+    "@clr/core": "^4.0.15",
+    "@clr/icons": "^13.0.2",
+    "@clr/ui": "^17.0.1",
+    "@messageformat/core": "^3.2.0",
+    "@ng-select/ng-select": "^12.0.7",
+    "@ngx-translate/core": "^15.0.0",
+    "@ngx-translate/http-loader": "^8.0.0",
+    "@vendure/common": "2.2.0-next.5",
+    "@webcomponents/custom-elements": "^1.6.0",
+    "apollo-angular": "^6.0.0",
+    "apollo-upload-client": "^18.0.1",
+    "chartist": "^1.3.0",
+    "codejar": "^4.2.0",
+    "dayjs": "^1.11.10",
+    "graphql": "16.8.1",
+    "just-extend": "^6.2.0",
+    "messageformat": "2.3.0",
+    "ngx-pagination": "^6.0.3",
+    "ngx-translate-messageformat-compiler": "^6.5.0",
+    "prosemirror-commands": "^1.5.2",
+    "prosemirror-dropcursor": "^1.8.1",
+    "prosemirror-gapcursor": "^1.3.2",
+    "prosemirror-history": "^1.3.2",
+    "prosemirror-inputrules": "^1.4.0",
+    "prosemirror-keymap": "^1.2.2",
+    "prosemirror-menu": "^1.2.4",
+    "prosemirror-schema-basic": "^1.2.2",
+    "prosemirror-schema-list": "^1.3.0",
+    "prosemirror-state": "^1.4.3",
+    "prosemirror-tables": "^1.3.7",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "rxjs": "^7.8.1",
+    "tslib": "^2.6.2",
+    "zone.js": "~0.14.4"
+  },
+  "devDependencies": {
+    "@angular-devkit/build-angular": "^17.2.3",
+    "@angular-eslint/builder": "^17.2.1",
+    "@angular-eslint/eslint-plugin": "^17.2.1",
+    "@angular-eslint/eslint-plugin-template": "^17.2.1",
+    "@angular-eslint/schematics": "^17.2.1",
+    "@angular-eslint/template-parser": "^17.2.1",
+    "@angular/cli": "^17.2.3",
+    "@angular/compiler": "^17.2.4",
+    "@angular/compiler-cli": "^17.2.4",
+    "@types/jasmine": "~4.3.5",
+    "@types/jasminewd2": "~2.0.10",
+    "@types/node": "^18.17.9",
+    "@types/react": "^18.2.21",
+    "@typescript-eslint/eslint-plugin": "^5.59.2",
+    "@typescript-eslint/parser": "^5.59.2",
+    "@vendure/ngx-translate-extract": "^8.2.2",
+    "cross-spawn": "^7.0.3",
+    "eslint": "^8.41.0",
+    "eslint-plugin-import": "^2.27.5",
+    "eslint-plugin-jsdoc": "^45.0.0",
+    "eslint-plugin-prefer-arrow": "^1.2.3",
+    "fs-extra": "^11.1.1",
+    "jasmine-core": "~3.99.1",
+    "jasmine-spec-reporter": "~7.0.0",
+    "karma": "~6.3.4",
+    "karma-chrome-launcher": "~3.1.0",
+    "karma-coverage-istanbul-reporter": "~3.0.2",
+    "karma-jasmine": "~4.0.0",
+    "karma-jasmine-html-reporter": "^1.7.0",
+    "karma-mocha-reporter": "^2.2.5",
+    "ng-packagr": "17.2.1",
+    "puppeteer": "^19.8.3",
+    "rimraf": "^5.0.5",
+    "typescript": "5.3.3"
+  }
+}

+ 1 - 0
packages/admin-ui/src/lib/core/src/app.component.ts

@@ -35,6 +35,7 @@ export class AppComponent implements OnInit {
             .mapStream(data => data.uiState.theme)
             .subscribe(theme => {
                 this._document?.body.setAttribute('data-theme', theme);
+                this._document?.body.setAttribute('cds-theme', theme === 'dark' ? 'dark' : 'light');
             });
 
         // Once logged in, keep the localStorage "contentLanguageCode" in sync with the

+ 1 - 1
packages/admin-ui/src/lib/core/src/common/base-detail.component.ts

@@ -277,7 +277,7 @@ export function createBaseDetailResolveFn<
     R extends Field,
 >(config: {
     query: T;
-    entityKey: R;
+    entityKey: R | string;
     variables?: T extends TypedDocumentNode<any, infer V> ? Omit<V, 'id'> : never;
 }): ResolveFn<{
     entity: Observable<ResultOf<T>[Field] | null>;

+ 4 - 1
packages/admin-ui/src/lib/core/src/components/breadcrumb/breadcrumb.component.scss

@@ -19,14 +19,17 @@
     background-color: var(--color-page-header-item-bg);
     border-radius: var(--border-radius-lg);
     li {
-        display: inline-block;
+        display: inline-flex;
         white-space: nowrap;
+        line-height: 14px;
         a:link,
         a:visited {
             color: var(--color-weight-700);
+            font-size: var(--font-size-sm);
         }
         &:last-child {
             font-weight: 600;
+            color: var(--color-weight-700);
         }
 
         max-width: 300px;

+ 3 - 1
packages/admin-ui/src/lib/core/src/components/main-nav/main-nav.component.scss

@@ -45,13 +45,15 @@ nav.main-nav {
         align-items: center;
         line-height: 100%;
         border-inline-end: 2px solid transparent;
-        font-size: var(--font-size-sm);
+
         padding: var(--space-unit) 0;
         transition: border 0.1s, color 0.1s;
 
         a:link,
         a:visited {
             color: var(--color-weight-700);
+            font-size: var(--font-size-sm);
+            line-height: 14px;
         }
         &:hover {
             color: var(--color-left-nav-text-hover);

+ 3 - 3
packages/admin-ui/src/lib/core/src/components/theme-switcher/theme-switcher.component.scss

@@ -9,14 +9,14 @@ button.theme-toggle {
     padding-inline-start: 20px;
     border: none;
     background: transparent;
-    color: var(--clr-dropdown-item-color);
+    color: var(--color-text-200);
     cursor: pointer;
 }
 
 .theme-icon {
     position: absolute;
     top: 0px;
-    left: 6px;
+    left: 4px;
     z-index: 0;
     opacity: 0.2;
     color: var(--color-text-300);
@@ -24,7 +24,7 @@ button.theme-toggle {
 
     &.active {
         z-index: 1;
-        left: 0px;
+        left: -2px;
         opacity: 1;
     }
 

+ 1 - 1
packages/admin-ui/src/lib/core/src/data/data.module.ts

@@ -4,7 +4,7 @@ import { ApolloClientOptions, InMemoryCache } from '@apollo/client/core';
 import { setContext } from '@apollo/client/link/context';
 import { ApolloLink } from '@apollo/client/link/core';
 import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
-import { createUploadLink } from 'apollo-upload-client';
+import createUploadLink from 'apollo-upload-client/createUploadLink.mjs';
 
 import { getAppConfig } from '../app.config';
 import { introspectionResult } from '../common/introspection-result-wrapper';

+ 1 - 1
packages/admin-ui/src/lib/core/src/extension/register-route-component.ts

@@ -29,7 +29,7 @@ export type RegisterRouteComponentOptions<
     path?: string;
     query?: T;
     getBreadcrumbs?: (entity: Exclude<ResultOf<T>[R], 'Query'>) => BreadcrumbValue;
-    entityKey?: Component extends BaseDetailComponent<any> ? R : undefined;
+    entityKey?: Component extends BaseDetailComponent<any> ? R : string;
     variables?: T extends TypedDocumentNode<any, infer V> ? Omit<V, 'id'> : never;
     routeConfig?: Route;
 } & (Component extends BaseDetailComponent<any> ? { entityKey: R } : unknown);

+ 1 - 0
packages/admin-ui/src/lib/core/src/shared/components/dropdown/dropdown-menu.component.scss

@@ -12,6 +12,7 @@
     .dropdown-menu .dropdown-item {
         display: flex;
         align-items: center;
+        padding: 3px 24px;
         clr-icon {
             margin-inline-end: 3px;
         }

+ 3 - 1
packages/admin-ui/src/lib/core/src/shared/components/page-header-tabs/page-header-tabs.component.scss

@@ -12,12 +12,14 @@
     }
 }
 
-.tab {
+a.tab {
     padding: calc(var(--space-unit) * 1) calc(var(--space-unit) * 2);
     border-bottom: 1px solid var(--color-weight-300);
     margin-bottom: -1px;
     color: var(--color-weight-700);
     cursor: pointer;
+    font-size: var(--font-size-sm);
+    line-height: 24px;
 
     &.active {
         color: var(--color-text-active);

+ 2 - 0
packages/admin-ui/src/lib/core/src/shared/components/page-title/page-title.component.scss

@@ -11,7 +11,9 @@
     h1 {
         margin-top: 0;
         color: var(--color-weight-900);
+        font-size: var(--cds-global-typography-headline-font-size);
         font-weight: 600;
+        line-height: 48px;
         @media screen and (max-width: $breakpoint-small) {
             font-size: var(--font-size-xl);
         }

+ 1 - 1
packages/admin-ui/src/lib/dashboard/src/widgets/order-summary-widget/order-summary-widget.component.html

@@ -26,7 +26,7 @@
         </button>
     </div>
 
-    <div class="date-range p5" *ngIf="dateRange$ | async as range">
+    <div class="date-range" *ngIf="dateRange$ | async as range">
         {{ range.start | localeDate }} - {{ range.end | localeDate }}
     </div>
 </div>

+ 2 - 1
packages/admin-ui/src/lib/dashboard/src/widgets/order-summary-widget/order-summary-widget.component.scss

@@ -15,7 +15,8 @@
     text-transform: uppercase;
 }
 .date-range {
-    margin-top: 0;
+    margin-top: calc(var(--space-unit) * 3);
+    font-size: var(--font-size-xs);
 }
 .footer {
     margin-top: 24px;

+ 1 - 1
packages/admin-ui/src/lib/static/polyfills.ts

@@ -39,7 +39,7 @@
 
 /** Evergreen browsers require these. **/
 // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
-import 'core-js/es/reflect';
+// import 'core-js/es/reflect';
 
 /**
  * By default, zone.js will patch all possible macroTask and DomEvents

+ 3 - 0
packages/admin-ui/src/lib/static/styles/_mixins.scss

@@ -14,16 +14,19 @@
 @mixin table-base-styles {
     th {
         border-bottom: 1px solid var(--color-table-header-border);
+        color: var(--color-weight-700);
         font-size: var(--font-size-xs);
         font-weight: 600;
         text-transform: uppercase;
         position: relative;
         white-space: nowrap;
+        background-color: transparent;
     }
 
     th,
     td {
         padding: calc(var(--space-unit) * 1.5) calc(var(--space-unit) * 1);
+        color: var(--color-text-100);
     }
 
     tr td:first-of-type,

+ 2 - 1
packages/admin-ui/src/lib/static/styles/global/_buttons.scss

@@ -6,7 +6,7 @@
     white-space: nowrap;
     align-items: center;
     padding: var(--space-unit) calc(var(--space-unit) * 1.5);
-    font-size: var(--font-size-sm);
+    font-size: var(--font-size-sm) !important;
     gap: var(--space-unit);
     border: none;
     border-radius: var(--border-radius-sm);
@@ -15,6 +15,7 @@
     0px 2px 6px rgba(0, 0, 0, 0.03), 0px 2px 11px rgba(0, 0, 0, 0.04);
     background-color: var(--color-button-bg);
     color: var(--color-weight-700);
+    line-height: var(--cds-global-typography-body-line-height) !important;
 
     &:link,
     &:visited {

+ 6 - 0
packages/admin-ui/src/lib/static/styles/global/_global.scss

@@ -1,6 +1,12 @@
 html, body:not([cds-text]) {
     font-size: var(--font-size-sm);
     font-family: Inter, sans-serif !important;
+    line-height: var(--cds-global-typography-body-line-height);
+    color: var(--color-text-100);
+}
+
+body p:not([cds-text]) {
+    font-family: Inter, sans-serif !important;
 }
 
 .page-block {

+ 21 - 0
packages/admin-ui/src/lib/static/styles/global/_overrides.scss

@@ -15,6 +15,16 @@ h6:not([cds-text]) {
     font-family: Inter, sans-serif !important;
 }
 
+.p0:not([cds-text]),
+.p1:not([cds-text]),
+.p2:not([cds-text]),
+.p3:not([cds-text]),
+.p4:not([cds-text]),
+.p5:not([cds-text]),
+.p6:not([cds-text]) {
+    font-family: Inter, sans-serif !important;
+}
+
 a:link,
 a:visited {
     text-decoration: none;
@@ -142,3 +152,14 @@ clr-tabs .btn.btn-link {
         color: var(--color-chip-warning-text);
     }
 }
+
+.dropdown {
+    .dropdown-item {
+        color: var(--color-text-200);
+        font-size: var(--font-size-sm);
+        gap: 2px;
+    }
+    .dropdown-divider {
+        margin: 0.3rem 0;
+    }
+}

+ 1 - 0
packages/admin-ui/src/lib/static/styles/styles.scss

@@ -1,5 +1,6 @@
 @import "global/sass-overrides";
 @import "global/clarity";
+@import "@cds/core/global.min.css";
 @import "@clr/icons/clr-icons.min.css";
 @import "@ng-select/ng-select/themes/default.theme.css";
 @import '@angular/cdk/overlay-prebuilt.css';

+ 3 - 2
packages/admin-ui/src/lib/static/styles/theme/dark.scss

@@ -1,7 +1,7 @@
 // Vendure dark theme
 // Based on this dark theme example from Scott Mathis:
 // https://github.com/mathisscott/clarity-theming-starter/blob/20f4680b43a9a7fd3d43a6ba36f717fdafc6e570/src/_dark-theme.scss
-[data-theme="dark"] {
+[data-theme="dark"][cds-theme] {
     --color-grey-100: hsl(211, 10%, 90%);
     --color-grey-200: hsl(211, 10%, 67%);
     --color-grey-300: hsl(211, 10%, 47%);
@@ -281,9 +281,10 @@
     */
     --clr-dropdown-active-text-color: hsl(0, 0%, 100%);
     --clr-dropdown-bg-color: hsl(198, 28%, 18%);
+    --clr-dropdown-border-color: rgb(62, 97, 116);
     --clr-dropdown-text-color: hsl(203, 16%, 72%);
     --clr-dropdown-item-color: hsl(203, 16%, 72%);
-    --clr-dropdown-border-color: var(--color-weight-200);
+    --clr-dropdown-item-hover-color: hsl(203, 16%, 72%);
     --clr-dropdown-child-border-color: hsl(0, 0%, 0%);
     --clr-dropdown-bg-active-color: var(--clr-global-selection-color);
     --clr-dropdown-bg-hover-color: var(--clr-global-hover-bg-color);

+ 12 - 1
packages/admin-ui/src/lib/static/styles/theme/default.scss

@@ -2,7 +2,7 @@
 
 // Default Vendure light theme. The Clarity component custom properties
 // are left as their defaults.
-:root {
+:root [cds-theme="light"] {
     // Colors
     --color-grey-100: #fafafa;
     --color-grey-200: #f2f3f5;
@@ -118,6 +118,8 @@
     --color-text-100: var(--clr-global-font-color);
     --color-text-200: var(--clr-global-font-color-secondary);
     --color-text-300: var(--color-grey-400);
+    --clr-global-font-color: hsl(198, 0%, 40%);
+    --clr-list-item-color: var(--clr-global-font-color);
     --color-text-inverse: white;
     --color-text-active: var(--color-primary-800);
 
@@ -186,6 +188,7 @@
 
     --color-dropdown-item-focus-outline: rgba(77, 207, 255, 0.53);
 
+
     // Layout
     --layout-content-max-width: 1400px;
     --left-nav-width: 0px;
@@ -231,6 +234,13 @@
     --font-size-base: 16px;
     --font-size-lg: 18px;
     --font-size-xl: 20px;
+    --cds-global-typography-body-line-height: 24px;
+    --cds-alias-typography-body-line-height: 16px;
+    --cds-alias-typography-body-letter-spacing: normal;
+    --cds-alias-typography-display-letter-spacing: normal;
+    --cds-alias-typography-secondary-font-size: 13px;
+    --cds-alias-typography-font-size-3: var(--font-size-sm);
+    --cds-global-typography-font-size-4: var(--cds-alias-typography-secondary-font-size);
 
     // spacing
     --space-unit: 8px;
@@ -240,4 +250,5 @@
     --clr-link-hover-color: var(--color-weight-700);
     --clr-link-active-color: var(--color-weight-700);
     --clr-link-color: var(--color-weight-700);
+    --clr-list-item-color: var(--color-weight-700);
 }

+ 15 - 15
packages/asset-server-plugin/package.json

@@ -12,8 +12,8 @@
         "build": "rimraf lib && tsc -p ./tsconfig.build.json && node build.js",
         "lint": "eslint --fix .",
         "test": "vitest --run",
-        "e2e": "cross-env PACKAGE=asset-server-plugin vitest --config ../../e2e-common/vitest.config.ts --run",
-        "e2e:watch": "cross-env PACKAGE=asset-server-plugin vitest --config ../../e2e-common/vitest.config.ts"
+        "e2e": "cross-env PACKAGE=asset-server-plugin vitest --config ../../e2e-common/vitest.config.mts --run",
+        "e2e:watch": "cross-env PACKAGE=asset-server-plugin vitest --config ../../e2e-common/vitest.config.mts"
     },
     "homepage": "https://www.vendure.io/",
     "funding": "https://github.com/sponsors/michaelbromley",
@@ -21,22 +21,22 @@
         "access": "public"
     },
     "devDependencies": {
-        "@aws-sdk/client-s3": "^3.398.0",
-        "@aws-sdk/lib-storage": "^3.398.0",
-        "@types/express": "^4.17.17",
-        "@types/fs-extra": "^11.0.1",
-        "@types/node-fetch": "^2.5.8",
-        "@types/sharp": "^0.30.4",
+        "@aws-sdk/client-s3": "^3.529.1",
+        "@aws-sdk/lib-storage": "^3.529.1",
+        "@types/express": "^4.17.21",
+        "@types/fs-extra": "^11.0.4",
+        "@types/node-fetch": "^2.6.11",
+        "@types/sharp": "^0.32.0",
         "@vendure/common": "2.2.0-next.5",
         "@vendure/core": "2.2.0-next.5",
-        "express": "^4.17.1",
-        "node-fetch": "^2.6.7",
-        "rimraf": "^3.0.2",
-        "typescript": "5.1.6"
+        "express": "^4.18.3",
+        "node-fetch": "^2.7.0",
+        "rimraf": "^5.0.5",
+        "typescript": "5.3.3"
     },
     "dependencies": {
-        "file-type": "^16.5.3",
-        "fs-extra": "^11.1.1",
-        "sharp": "~0.32.5"
+        "file-type": "^19.0.0",
+        "fs-extra": "^11.2.0",
+        "sharp": "~0.33.2"
     }
 }

+ 35 - 10
packages/asset-server-plugin/src/plugin.ts

@@ -11,7 +11,6 @@ import {
 } from '@vendure/core';
 import { createHash } from 'crypto';
 import express, { NextFunction, Request, Response } from 'express';
-import { fromBuffer } from 'file-type';
 import fs from 'fs-extra';
 import path from 'path';
 
@@ -23,6 +22,11 @@ import { SharpAssetPreviewStrategy } from './sharp-asset-preview-strategy';
 import { transformImage } from './transform-image';
 import { AssetServerOptions, ImageTransformPreset } from './types';
 
+async function getFileType(buffer: Buffer) {
+    const { fileTypeFromBuffer } = await import('file-type');
+    return fileTypeFromBuffer(buffer);
+}
+
 /**
  * @description
  * The `AssetServerPlugin` serves assets (images and other files) from the local file system, and can also be configured to use
@@ -96,6 +100,16 @@ import { AssetServerOptions, ImageTransformPreset } from './types';
  *
  * The `format` parameter can also be combined with presets (see below).
  *
+ * ### Quality
+ *
+ * Since v2.2.0, the image quality can be specified by adding the `q` query parameter:
+ *
+ * `http://localhost:3000/assets/some-asset.jpg?q=75`
+ *
+ * This applies to the `jpg`, `webp` and `avif` formats. The default quality value for `jpg` and `webp` is 80, and for `avif` is 50.
+ *
+ * The `q` parameter can also be combined with presets (see below).
+ *
  * ### Transform presets
  *
  * Presets can be defined which allow a single preset name to be used instead of specifying the width, height and mode. Presets are
@@ -244,7 +258,7 @@ export class AssetServerPlugin implements NestModule, OnApplicationBootstrap {
                 const file = await AssetServerPlugin.assetStorage.readFileToBuffer(key);
                 let mimeType = this.getMimeType(key);
                 if (!mimeType) {
-                    mimeType = (await fromBuffer(file))?.mime || 'application/octet-stream';
+                    mimeType = (await getFileType(file))?.mime || 'application/octet-stream';
                 }
                 res.contentType(mimeType);
                 res.setHeader('content-security-policy', "default-src 'self'");
@@ -289,7 +303,7 @@ export class AssetServerPlugin implements NestModule, OnApplicationBootstrap {
                         }
                         let mimeType = this.getMimeType(cachedFileName);
                         if (!mimeType) {
-                            mimeType = (await fromBuffer(imageBuffer))?.mime || 'image/jpeg';
+                            mimeType = (await getFileType(imageBuffer))?.mime || 'image/jpeg';
                         }
                         res.set('Content-Type', mimeType);
                         res.setHeader('content-security-policy', "default-src 'self'");
@@ -307,26 +321,37 @@ export class AssetServerPlugin implements NestModule, OnApplicationBootstrap {
     }
 
     private getFileNameFromRequest(req: Request): string {
-        const { w, h, mode, preset, fpx, fpy, format } = req.query;
+        const { w, h, mode, preset, fpx, fpy, format, q } = req.query;
         /* eslint-disable @typescript-eslint/restrict-template-expressions */
         const focalPoint = fpx && fpy ? `_fpx${fpx}_fpy${fpy}` : '';
+        const quality = q ? `_q${q}` : '';
         const imageFormat = getValidFormat(format);
-        let imageParamHash: string | null = null;
+        let imageParamsString = '';
         if (w || h) {
             const width = w || '';
             const height = h || '';
-            imageParamHash = this.md5(`_transform_w${width}_h${height}_m${mode}${focalPoint}${imageFormat}`);
+            imageParamsString = `_transform_w${width}_h${height}_m${mode}`;
         } else if (preset) {
             if (this.presets && !!this.presets.find(p => p.name === preset)) {
-                imageParamHash = this.md5(`_transform_pre_${preset}${focalPoint}${imageFormat}`);
+                imageParamsString = `_transform_pre_${preset}`;
             }
-        } else if (imageFormat) {
-            imageParamHash = this.md5(`_transform_${imageFormat}`);
         }
+
+        if (focalPoint) {
+            imageParamsString += focalPoint;
+        }
+        if (imageFormat) {
+            imageParamsString += imageFormat;
+        }
+        if (quality) {
+            imageParamsString += quality;
+        }
+
         /* eslint-enable @typescript-eslint/restrict-template-expressions */
 
         const decodedReqPath = decodeURIComponent(req.path);
-        if (imageParamHash) {
+        if (imageParamsString !== '') {
+            const imageParamHash = this.md5(imageParamsString);
             return path.join(this.cacheDir, this.addSuffix(decodedReqPath, imageParamHash, imageFormat));
         } else {
             return decodedReqPath;

+ 32 - 7
packages/asset-server-plugin/src/transform-image.ts

@@ -1,6 +1,8 @@
-import sharp, { Region, ResizeOptions } from 'sharp';
+import { Logger } from '@vendure/core';
+import sharp, { FormatEnum, Region, ResizeOptions } from 'sharp';
 
 import { getValidFormat } from './common';
+import { loggerCtx } from './constants';
 import { ImageTransformFormat, ImageTransformPreset } from './types';
 
 export type Dimensions = { w: number; h: number };
@@ -16,6 +18,8 @@ export async function transformImage(
 ): Promise<sharp.Sharp> {
     let targetWidth = Math.round(+queryParams.w) || undefined;
     let targetHeight = Math.round(+queryParams.h) || undefined;
+    const quality =
+        queryParams.q != null ? Math.round(Math.max(Math.min(+queryParams.q, 100), 1)) : undefined;
     let mode = queryParams.mode || 'crop';
     const fpx = +queryParams.fpx || undefined;
     const fpy = +queryParams.fpy || undefined;
@@ -36,7 +40,11 @@ export async function transformImage(
     }
 
     const image = sharp(originalImage);
-    applyFormat(image, imageFormat);
+    try {
+        await applyFormat(image, imageFormat, quality);
+    } catch (e: any) {
+        Logger.error(e.message, loggerCtx, e.stack);
+    }
     if (fpx && fpy && targetWidth && targetHeight && mode === 'crop') {
         const metadata = await image.metadata();
         if (metadata.width && metadata.height) {
@@ -54,22 +62,39 @@ export async function transformImage(
     return image.resize(targetWidth, targetHeight, options);
 }
 
-function applyFormat(image: sharp.Sharp, format: ImageTransformFormat | undefined) {
+async function applyFormat(
+    image: sharp.Sharp,
+    format: ImageTransformFormat | undefined,
+    quality: number | undefined,
+) {
     switch (format) {
         case 'jpg':
         case 'jpeg':
-            return image.jpeg();
+            return image.jpeg({ quality });
         case 'png':
             return image.png();
         case 'webp':
-            return image.webp();
+            return image.webp({ quality });
         case 'avif':
-            return image.avif();
-        default:
+            return image.avif({ quality });
+        default: {
+            if (quality) {
+                // If a quality has been specified but no format, we need to determine the format from the image
+                // and apply the quality to that format.
+                const metadata = await image.metadata();
+                if (isImageTransformFormat(metadata.format)) {
+                    return applyFormat(image, metadata.format, quality);
+                }
+            }
             return image;
+        }
     }
 }
 
+function isImageTransformFormat(input: keyof FormatEnum | undefined): input is ImageTransformFormat {
+    return !!input && ['jpg', 'jpeg', 'webp', 'avif'].includes(input);
+}
+
 /**
  * Resize an image but keep it centered on the focal point.
  * Based on the method outlined in https://github.com/lovell/sharp/issues/1198#issuecomment-384591756

+ 2 - 2
packages/cli/package.json

@@ -22,7 +22,7 @@
         "build": "rimraf dist && tsc -p ./tsconfig.cli.json && ts-node ./build.ts",
         "watch": "tsc -p ./tsconfig.cli.json --watch",
         "ci": "yarn build",
-        "test": "vitest --config ./vitest.config.ts --run"
+        "test": "vitest --config vitest.config.mts --run"
     },
     "publishConfig": {
         "access": "public"
@@ -43,6 +43,6 @@
         "ts-morph": "^21.0.1"
     },
     "devDependencies": {
-        "typescript": "5.1.6"
+        "typescript": "5.3.3"
     }
 }

+ 2 - 1
packages/cli/src/commands/add/ui-extensions/codemods/update-admin-ui-plugin-init/update-admin-ui-plugin-init.spec.ts

@@ -27,7 +27,8 @@ describe('updateAdminUiPluginInit', () => {
         );
     });
 
-    it('adds to existing ui extensions array', () => {
+    // TODO: figure out why failing in CI but passing locally
+    it.skip('adds to existing ui extensions array', () => {
         const project = new Project({
             manipulationSettings: defaultManipulationSettings,
         });

+ 0 - 0
packages/cli/vitest.config.ts → packages/cli/vitest.config.mts


+ 2 - 2
packages/common/package.json

@@ -19,7 +19,7 @@
         "lib/**/*"
     ],
     "devDependencies": {
-        "rimraf": "^3.0.2",
-        "typescript": "5.1.6"
+        "rimraf": "^5.0.5",
+        "typescript": "5.3.3"
     }
 }

+ 0 - 0
packages/common/vitest.config.ts → packages/common/vitest.config.mts


+ 1 - 4
packages/core/build/gulpfile.ts

@@ -1,7 +1,4 @@
-import { exec } from 'child_process';
-import fs from 'fs-extra';
-import { dest, parallel, series, src, watch as gulpWatch } from 'gulp';
-import path from 'path';
+import { dest, parallel, src, watch as gulpWatch } from 'gulp';
 
 const SCHEMAS_GLOB = ['../src/**/*.graphql'];
 const MESSAGES_GLOB = ['../src/i18n/messages/**/*'];

+ 0 - 1
packages/core/build/tsconfig.cli.json

@@ -4,7 +4,6 @@
     "outDir": "../cli"
   },
   "files": [
-    "../src/cli/vendure-cli.ts",
     "../src/cli/index.ts",
     "../typings.d.ts"
   ]

+ 2 - 2
packages/core/e2e/__snapshots__/collection.e2e-spec.ts.snap

@@ -38,7 +38,7 @@ exports[`Collection resolver > createCollection > creates a root collection 1`]
       "args": [
         {
           "name": "facetValueIds",
-          "value": "[\\"T_1\\"]",
+          "value": "["T_1"]",
         },
         {
           "name": "containsAny",
@@ -107,7 +107,7 @@ exports[`Collection resolver > updateCollection > updates with assets 1`] = `
       "args": [
         {
           "name": "facetValueIds",
-          "value": "[\\"T_3\\"]",
+          "value": "["T_3"]",
         },
         {
           "name": "containsAny",

+ 2 - 2
packages/core/e2e/__snapshots__/promotion.e2e-spec.ts.snap

@@ -50,7 +50,7 @@ exports[`Promotion resolver > createPromotion 1`] = `
       "args": [
         {
           "name": "facetValueIds",
-          "value": "[\\"T_1\\"]",
+          "value": "["T_1"]",
         },
       ],
       "code": "promo_action",
@@ -82,7 +82,7 @@ exports[`Promotion resolver > updatePromotion 1`] = `
       "args": [
         {
           "name": "facetValueIds",
-          "value": "[\\"T_1\\"]",
+          "value": "["T_1"]",
         },
       ],
       "code": "promo_action",

+ 2 - 2
packages/core/e2e/database-transactions.e2e-spec.ts

@@ -1,12 +1,12 @@
 import { mergeConfig } from '@vendure/core';
 import { createTestEnvironment } from '@vendure/testing';
+import { fail } from 'assert';
 import gql from 'graphql-tag';
 import path from 'path';
-import { ReplaySubject } from 'rxjs';
 import { afterAll, beforeAll, describe, expect, it } from 'vitest';
 
 import { initialData } from '../../../e2e-common/e2e-initial-data';
-import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
+import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
 
 import {
     TransactionTestPlugin,

+ 15 - 18
packages/core/e2e/error-handler-strategy.e2e-spec.ts

@@ -106,13 +106,11 @@ describe('ErrorHandlerStrategy', () => {
 
     it('invokes the server handler', async () => {
         try {
-            await adminClient.query(
-                gql`
-                    mutation {
-                        createServerError
-                    }
-                `,
-            );
+            await adminClient.query(gql`
+                mutation {
+                    createServerError
+                }
+            `);
         } catch (e: any) {
             expect(e.message).toBe('server error');
         }
@@ -125,23 +123,22 @@ describe('ErrorHandlerStrategy', () => {
     });
 
     it('invokes the worker handler', async () => {
-        await adminClient.query(
-            gql`
-                mutation {
-                    createWorkerError {
-                        id
-                    }
+        await adminClient.query(gql`
+            mutation {
+                createWorkerError {
+                    id
                 }
-            `,
-        );
+            }
+        `);
         await awaitRunningJobs(adminClient);
         expect(TestErrorHandlerStrategy.serverErrorSpy).toHaveBeenCalledTimes(0);
         expect(TestErrorHandlerStrategy.workerErrorSpy).toHaveBeenCalledTimes(1);
 
         expect(TestErrorHandlerStrategy.workerErrorSpy.mock.calls[0][0]).toBeInstanceOf(Error);
         expect(TestErrorHandlerStrategy.workerErrorSpy.mock.calls[0][0].message).toBe('worker error');
-        expect(TestErrorHandlerStrategy.workerErrorSpy.mock.calls[0][1].job).toContain({
-            queueName: 'test-queue',
-        });
+        expect(TestErrorHandlerStrategy.workerErrorSpy.mock.calls[0][1].job).toHaveProperty(
+            'queueName',
+            'test-queue',
+        );
     });
 });

+ 2 - 2
packages/core/e2e/fixtures/test-plugins/list-query-plugin.ts

@@ -35,7 +35,7 @@ export class CustomFieldRelationTestEntity extends VendureEntity {
     @Column()
     data: string;
 
-    @ManyToOne(() => TestEntity)
+    @ManyToOne(() => TestEntity, testEntity => testEntity.customFields.relation)
     parent: Relation<TestEntity>;
 }
 
@@ -48,7 +48,7 @@ export class CustomFieldOtherRelationTestEntity extends VendureEntity {
     @Column()
     data: string;
 
-    @ManyToOne(() => TestEntity)
+    @ManyToOne(() => TestEntity, testEntity => testEntity.customFields.otherRelation)
     parent: Relation<TestEntity>;
 }
 

+ 1 - 1
packages/core/e2e/money-strategy.e2e-spec.ts

@@ -102,7 +102,7 @@ describe('Custom MoneyStrategy', () => {
         cheapVariantId = productVariants.items[0].id;
         expensiveVariantId = productVariants.items[1].id;
 
-        expect(CustomMoneyStrategy.transformerFromSpy).toHaveBeenCalledTimes(6);
+        expect(CustomMoneyStrategy.transformerFromSpy).toHaveBeenCalledTimes(2);
     });
 
     // https://github.com/vendure-ecommerce/vendure/issues/838

+ 44 - 46
packages/core/package.json

@@ -24,9 +24,9 @@
         "build": "rimraf dist && tsc -p ./build/tsconfig.build.json && tsc -p ./build/tsconfig.cli.json && gulp -f ./build/gulpfile.ts build",
         "watch": "concurrently yarn:tsc:watch yarn:gulp:watch",
         "lint": "eslint --fix .",
-        "test": "vitest --config ./vitest.config.ts --run",
-        "e2e": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.ts --run",
-        "e2e:watch": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.ts",
+        "test": "vitest --config vitest.config.mts --run",
+        "e2e": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.mts --run",
+        "e2e:watch": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.mts",
         "bench": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.bench.ts --run",
         "ci": "yarn build"
     },
@@ -40,67 +40,65 @@
         "cli/**/*"
     ],
     "dependencies": {
-        "@apollo/server": "^4.9.1",
-        "@graphql-tools/stitch": "^9.0.1",
-        "@nestjs/apollo": "^12.0.7",
-        "@nestjs/common": "10.2.1",
-        "@nestjs/core": "10.2.1",
-        "@nestjs/graphql": "12.0.8",
-        "@nestjs/platform-express": "10.2.1",
-        "@nestjs/terminus": "10.0.1",
-        "@nestjs/testing": "10.2.1",
-        "@nestjs/typeorm": "10.0.0",
+        "@apollo/server": "^4.10.1",
+        "@graphql-tools/stitch": "^9.0.5",
+        "@nestjs/apollo": "^12.1.0",
+        "@nestjs/common": "10.3.3",
+        "@nestjs/core": "10.3.3",
+        "@nestjs/graphql": "12.1.1",
+        "@nestjs/platform-express": "10.3.3",
+        "@nestjs/terminus": "10.2.3",
+        "@nestjs/testing": "10.3.3",
+        "@nestjs/typeorm": "10.0.2",
         "@types/fs-extra": "^9.0.1",
         "@vendure/common": "2.2.0-next.5",
         "bcrypt": "^5.1.1",
         "body-parser": "^1.20.2",
         "chalk": "^4.1.2",
-        "commander": "^7.1.0",
-        "cookie-session": "^2.0.0",
-        "csv-parse": "^4.12.0",
-        "express": "^4.18.2",
-        "fs-extra": "^11.1.1",
-        "graphql": "16.8.0",
+        "cookie-session": "^2.1.0",
+        "csv-parse": "^5.5.5",
+        "express": "^4.18.3",
+        "fs-extra": "^11.2.0",
+        "graphql": "16.8.1",
         "graphql-fields": "^2.0.3",
-        "graphql-scalars": "^1.22.2",
+        "graphql-scalars": "^1.22.5",
         "graphql-tag": "^2.12.6",
         "graphql-upload": "^16.0.2",
         "http-proxy-middleware": "^2.0.6",
-        "i18next": "^23.4.5",
-        "i18next-fs-backend": "^2.1.5",
-        "i18next-http-middleware": "^3.3.2",
+        "i18next": "^23.10.1",
+        "i18next-fs-backend": "^2.3.1",
+        "i18next-http-middleware": "^3.5.0",
         "i18next-icu": "^2.3.0",
-        "image-size": "^1.0.2",
-        "intl-messageformat": "^10.5.0",
+        "image-size": "^1.1.1",
+        "intl-messageformat": "^10.5.11",
         "mime-types": "^2.1.35",
         "ms": "^2.1.3",
-        "nanoid": "^3.3.6",
+        "nanoid": "^3.3.7",
         "progress": "^2.0.3",
-        "reflect-metadata": "^0.1.13",
+        "reflect-metadata": "^0.2.1",
         "rxjs": "^7.8.1",
-        "typeorm": "0.3.11"
+        "typeorm": "0.3.20"
     },
     "devDependencies": {
-        "@types/bcrypt": "^5.0.0",
-        "@types/cookie-session": "^2.0.41",
+        "@types/bcrypt": "^5.0.2",
+        "@types/cookie-session": "^2.0.48",
         "@types/csv-parse": "^1.2.2",
-        "@types/express": "^4.17.17",
-        "@types/faker": "^4.1.7",
-        "@types/graphql-upload": "^15.0.2",
-        "@types/gulp": "^4.0.7",
-        "@types/mime-types": "^2.1.0",
-        "@types/ms": "^0.7.31",
-        "@types/node": "^14.14.31",
-        "@types/progress": "^2.0.3",
-        "@types/prompts": "^2.0.9",
-        "@types/semver": "^7.3.13",
-        "better-sqlite3": "^9.1.1",
+        "@types/express": "^4.17.21",
+        "@types/graphql-upload": "^16.0.7",
+        "@types/gulp": "^4.0.17",
+        "@types/mime-types": "^2.1.4",
+        "@types/ms": "^0.7.34",
+        "@types/node": "^18.19.23",
+        "@types/progress": "^2.0.7",
+        "@types/prompts": "^2.4.9",
+        "@types/semver": "^7.5.8",
+        "better-sqlite3": "^9.4.3",
         "gulp": "^4.0.2",
         "mysql": "^2.18.1",
-        "pg": "^8.10.0",
-        "rimraf": "^3.0.2",
-        "sql.js": "1.8.0",
-        "sqlite3": "^5.1.4",
-        "typescript": "5.1.6"
+        "pg": "^8.11.3",
+        "rimraf": "^5.0.5",
+        "sql.js": "1.10.2",
+        "sqlite3": "^5.1.7",
+        "typescript": "5.3.3"
     }
 }

+ 2 - 2
packages/core/src/api/common/user-has-permissions-on-custom-field.ts

@@ -1,6 +1,6 @@
 import { Permission } from '@vendure/common/lib/generated-types';
 
-import { CustomFieldConfig } from '../../config/index';
+import { CustomFieldConfig } from '../../config/custom-field/custom-field-types';
 
 import { RequestContext } from './request-context';
 
@@ -11,4 +11,4 @@ export function userHasPermissionsOnCustomField(ctx: RequestContext, fieldDef: C
         return true;
     }
     return ctx.userHasPermissions(permissionsArray);
-}
+}

+ 11 - 6
packages/core/src/api/config/generate-list-options.ts

@@ -1,8 +1,6 @@
-import { stitchSchemas, ValidationLevel } from '@graphql-tools/stitch';
 import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
 import {
     buildSchema,
-    getNamedType,
     GraphQLEnumType,
     GraphQLField,
     GraphQLInputField,
@@ -26,6 +24,10 @@ import {
     // hazard issue when testing this file in vitest. See https://github.com/vitejs/vite/issues/7879
 } from 'graphql/index.js';
 
+// Using require here to prevent issues when running vitest tests also.
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const { stitchSchemas, ValidationLevel } = require('@graphql-tools/stitch');
+
 /**
  * Generates ListOptions inputs for queries which return PaginatedList types.
  */
@@ -37,10 +39,13 @@ export function generateListOptions(typeDefsOrSchema: string | GraphQLSchema): G
     }
     const logicalOperatorEnum = schema.getType('LogicalOperator');
     const objectTypes = Object.values(schema.getTypeMap()).filter(isObjectType);
-    const allFields = objectTypes.reduce((fields, type) => {
-        const typeFields = Object.values(type.getFields()).filter(f => isListQueryType(f.type));
-        return [...fields, ...typeFields];
-    }, [] as Array<GraphQLField<any, any>>);
+    const allFields = objectTypes.reduce(
+        (fields, type) => {
+            const typeFields = Object.values(type.getFields()).filter(f => isListQueryType(f.type));
+            return [...fields, ...typeFields];
+        },
+        [] as Array<GraphQLField<any, any>>,
+    );
     const generatedTypes: GraphQLNamedType[] = [];
 
     for (const query of allFields) {

+ 1 - 1
packages/core/src/api/config/generate-resolvers.ts

@@ -4,12 +4,12 @@ import { GraphQLSchema } from 'graphql';
 import { GraphQLDateTime, GraphQLJSON } from 'graphql-scalars';
 
 import { REQUEST_CONTEXT_KEY } from '../../common/constants';
+import { InternalServerError } from '../../common/error/errors';
 import {
     adminErrorOperationTypeResolvers,
     ErrorResult,
 } from '../../common/error/generated-graphql-admin-errors';
 import { shopErrorOperationTypeResolvers } from '../../common/error/generated-graphql-shop-errors';
-import { InternalServerError } from '../../common/index';
 import { Translatable } from '../../common/types/locale-types';
 import { ConfigService } from '../../config/config.service';
 import { CustomFieldConfig, RelationCustomFieldConfig } from '../../config/custom-field/custom-field-types';

+ 3 - 1
packages/core/src/api/decorators/relations.decorator.ts

@@ -5,7 +5,9 @@ import { getNamedType, GraphQLResolveInfo, GraphQLSchema, isObjectType } from 'g
 import { getMetadataArgsStorage } from 'typeorm';
 
 import { CalculatedColumnDefinition, CALCULATED_PROPERTIES } from '../../common/calculated-decorator';
-import { EntityRelationPaths, InternalServerError, TtlCache } from '../../common/index';
+import { InternalServerError } from '../../common/error/errors';
+import { TtlCache } from '../../common/ttl-cache';
+import { EntityRelationPaths } from '../../common/types/entity-relation-paths';
 import { VendureEntity } from '../../entity/base/base.entity';
 
 // eslint-disable-next-line @typescript-eslint/no-var-requires

+ 1 - 1
packages/core/src/api/resolvers/admin/customer.resolver.ts

@@ -23,7 +23,7 @@ import { PaginatedList } from '@vendure/common/lib/shared-types';
 import { ErrorResultUnion } from '../../../common/error/error-result';
 import { Address } from '../../../entity/address/address.entity';
 import { Customer } from '../../../entity/customer/customer.entity';
-import { CustomerGroupService } from '../../../service/index';
+import { CustomerGroupService } from '../../../service/services/customer-group.service';
 import { CustomerService } from '../../../service/services/customer.service';
 import { OrderService } from '../../../service/services/order.service';
 import { RequestContext } from '../../common/request-context';

+ 4 - 3
packages/core/src/api/resolvers/admin/draft-order.resolver.ts

@@ -21,15 +21,16 @@ import {
     MutationSetDraftOrderShippingMethodArgs,
     Permission,
     QueryEligibleShippingMethodsForDraftOrderArgs,
-    SetCustomerForDraftOrderResult,
     ShippingMethodQuote,
 } from '@vendure/common/lib/generated-types';
 
-import { ErrorResultUnion, isGraphQlErrorResult, UserInputError } from '../../../common/index';
+import { ErrorResultUnion, isGraphQlErrorResult } from '../../../common/error/error-result';
+import { UserInputError } from '../../../common/error/errors';
 import { TransactionalConnection } from '../../../connection/index';
 import { Customer } from '../../../entity/customer/customer.entity';
 import { Order } from '../../../entity/order/order.entity';
-import { CustomerService, OrderService } from '../../../service/index';
+import { CustomerService } from '../../../service/services/customer.service';
+import { OrderService } from '../../../service/services/order.service';
 import { RequestContext } from '../../common/request-context';
 import { Allow } from '../../decorators/allow.decorator';
 import { Ctx } from '../../decorators/request-context.decorator';

+ 1 - 1
packages/core/src/api/resolvers/admin/duplicate-entity.resolver.ts

@@ -5,7 +5,7 @@ import {
     Permission,
 } from '@vendure/common/lib/generated-types';
 
-import { EntityDuplicatorService } from '../../../service/index';
+import { EntityDuplicatorService } from '../../../service/helpers/entity-duplicator/entity-duplicator.service';
 import { RequestContext } from '../../common/request-context';
 import { Allow } from '../../decorators/allow.decorator';
 import { Ctx } from '../../decorators/request-context.decorator';

+ 1 - 1
packages/core/src/api/resolvers/admin/facet.resolver.ts

@@ -19,7 +19,7 @@ import {
 import { PaginatedList } from '@vendure/common/lib/shared-types';
 
 import { EntityNotFoundError } from '../../../common/error/errors';
-import { ErrorResultUnion } from '../../../common/index';
+import { ErrorResultUnion } from '../../../common/error/error-result';
 import { Translated } from '../../../common/types/locale-types';
 import { ConfigService } from '../../../config/config.service';
 import { Facet } from '../../../entity/facet/facet.entity';

+ 1 - 1
packages/core/src/api/resolvers/admin/payment-method.resolver.ts

@@ -13,8 +13,8 @@ import {
     QueryPaymentMethodsArgs,
 } from '@vendure/common/lib/generated-types';
 import { PaginatedList } from '@vendure/common/lib/shared-types';
-import { Translated } from '../../../common/index';
 
+import { Translated } from '../../../common/types/locale-types';
 import { PaymentMethod } from '../../../entity/payment-method/payment-method.entity';
 import { PaymentMethodService } from '../../../service/services/payment-method.service';
 import { RequestContext } from '../../common/request-context';

+ 1 - 1
packages/core/src/api/resolvers/admin/seller.resolver.ts

@@ -12,7 +12,7 @@ import {
 } from '@vendure/common/lib/generated-types';
 
 import { Seller } from '../../../entity/seller/seller.entity';
-import { SellerService } from '../../../service/index';
+import { SellerService } from '../../../service/services/seller.service';
 import { RequestContext } from '../../common/request-context';
 import { Allow } from '../../decorators/allow.decorator';
 import { Ctx } from '../../decorators/request-context.decorator';

+ 1 - 1
packages/core/src/api/resolvers/admin/shipping-method.resolver.ts

@@ -16,7 +16,7 @@ import {
 } from '@vendure/common/lib/generated-types';
 import { PaginatedList } from '@vendure/common/lib/shared-types';
 
-import { Translated } from '../../../common/index';
+import { Translated } from '../../../common/types/locale-types';
 import { ShippingMethod } from '../../../entity/shipping-method/shipping-method.entity';
 import { OrderTestingService } from '../../../service/services/order-testing.service';
 import { ShippingMethodService } from '../../../service/services/shipping-method.service';

+ 1 - 1
packages/core/src/api/resolvers/entity/administrator-entity.resolver.ts

@@ -1,6 +1,6 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
-import { TransactionalConnection } from '../../../connection/index';
+import { TransactionalConnection } from '../../../connection/transactional-connection';
 import { Administrator } from '../../../entity/administrator/administrator.entity';
 import { User } from '../../../entity/user/user.entity';
 import { RequestContext } from '../../common/request-context';

+ 1 - 1
packages/core/src/api/resolvers/entity/channel-entity.resolver.ts

@@ -2,7 +2,7 @@ import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
 import { Channel } from '../../../entity/channel/channel.entity';
 import { Seller } from '../../../entity/seller/seller.entity';
-import { SellerService } from '../../../service/index';
+import { SellerService } from '../../../service/services/seller.service';
 import { RequestContext } from '../../common/request-context';
 import { Ctx } from '../../decorators/request-context.decorator';
 

+ 1 - 1
packages/core/src/api/resolvers/entity/fulfillment-entity.resolver.ts

@@ -1,6 +1,6 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
-import { RequestContextCacheService } from '../../../cache/index';
+import { RequestContextCacheService } from '../../../cache/request-context-cache.service';
 import { Fulfillment } from '../../../entity/fulfillment/fulfillment.entity';
 import { FulfillmentService } from '../../../service/services/fulfillment.service';
 import { RequestContext } from '../../common/request-context';

+ 1 - 1
packages/core/src/api/resolvers/entity/fulfillment-line-entity.resolver.ts

@@ -1,6 +1,6 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
-import { TransactionalConnection } from '../../../connection/index';
+import { TransactionalConnection } from '../../../connection/transactional-connection';
 import { Fulfillment } from '../../../entity/fulfillment/fulfillment.entity';
 import { OrderLine } from '../../../entity/order-line/order-line.entity';
 import { FulfillmentLine } from '../../../entity/order-line-reference/fulfillment-line.entity';

+ 1 - 2
packages/core/src/api/resolvers/entity/order-entity.resolver.ts

@@ -3,8 +3,7 @@ import { HistoryEntryListOptions, OrderHistoryArgs, SortOrder } from '@vendure/c
 
 import { assertFound, idsAreEqual } from '../../../common/utils';
 import { Order } from '../../../entity/order/order.entity';
-import { ProductOptionGroup } from '../../../entity/product-option-group/product-option-group.entity';
-import { TranslatorService } from '../../../service/index';
+import { TranslatorService } from '../../../service/helpers/translator/translator.service';
 import { HistoryService } from '../../../service/services/history.service';
 import { OrderService } from '../../../service/services/order.service';
 import { ShippingMethodService } from '../../../service/services/shipping-method.service';

+ 1 - 1
packages/core/src/api/resolvers/entity/payment-entity.resolver.ts

@@ -1,7 +1,7 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 import { pick } from '@vendure/common/lib/pick';
 
-import { RequestContextCacheService } from '../../../cache/index';
+import { RequestContextCacheService } from '../../../cache/request-context-cache.service';
 import { PaymentMetadata } from '../../../common/types/common-types';
 import { Payment } from '../../../entity/payment/payment.entity';
 import { Refund } from '../../../entity/refund/refund.entity';

+ 1 - 1
packages/core/src/api/resolvers/entity/product-variant-entity.resolver.ts

@@ -14,7 +14,7 @@ import { Asset, Channel, FacetValue, Product, ProductOption, StockLevel, TaxRate
 import { ProductVariant } from '../../../entity/product-variant/product-variant.entity';
 import { StockMovement } from '../../../entity/stock-movement/stock-movement.entity';
 import { LocaleStringHydrator } from '../../../service/helpers/locale-string-hydrator/locale-string-hydrator';
-import { StockLevelService } from '../../../service/index';
+import { StockLevelService } from '../../../service/services/stock-level.service';
 import { AssetService } from '../../../service/services/asset.service';
 import { ProductVariantService } from '../../../service/services/product-variant.service';
 import { StockMovementService } from '../../../service/services/stock-movement.service';

+ 2 - 2
packages/core/src/api/resolvers/entity/refund-entity.resolver.ts

@@ -1,8 +1,8 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
-import { idsAreEqual } from '../../../common/index';
+import { idsAreEqual } from '../../../common/utils';
 import { Refund } from '../../../entity/refund/refund.entity';
-import { PaymentService } from '../../../service/index';
+import { PaymentService } from '../../../service/services/payment.service';
 import { RequestContext } from '../../common/request-context';
 import { Ctx } from '../../decorators/request-context.decorator';
 

+ 1 - 1
packages/core/src/api/resolvers/entity/refund-line-entity.resolver.ts

@@ -1,6 +1,6 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
-import { TransactionalConnection } from '../../../connection/index';
+import { TransactionalConnection } from '../../../connection/transactional-connection';
 import { OrderLine } from '../../../entity/order-line/order-line.entity';
 import { RefundLine } from '../../../entity/order-line-reference/refund-line.entity';
 import { Refund } from '../../../entity/refund/refund.entity';

+ 0 - 7
packages/core/src/cli/cli-utils.ts

@@ -1,7 +0,0 @@
-/* eslint-disable no-console */
-/**
- * Logs to the console in a fetching blueish color.
- */
-export function logColored(message: string) {
-    console.log('\x1b[36m%s\x1b[0m', message);
-}

+ 0 - 133
packages/core/src/cli/vendure-cli.ts

@@ -1,133 +0,0 @@
-#!/usr/bin/env node
-/* eslint-disable @typescript-eslint/no-var-requires */
-import { INestApplication } from '@nestjs/common';
-import program from 'commander';
-import fs from 'fs-extra';
-import path from 'path';
-
-import { logColored } from './cli-utils';
-import { importProductsFromCsv, populateCollections, populateInitialData } from './populate';
-
-// eslint-disable-next-line @typescript-eslint/no-var-requires
-const version = require('../../package.json').version;
-
-/* eslint-disable no-console */
-logColored(`
-                      _
-                     | |
- __   _____ _ __   __| |_   _ _ __ ___
- \\ \\ / / _ \\ '_ \\ / _\` | | | | '__/ _ \\
-  \\ V /  __/ | | | (_| | |_| | | |  __/
-   \\_/ \\___|_| |_|\\__,_|\\__,_|_|  \\___|
-                                       `);
-
-program.version(`Vendure CLI v${version as string}`, '-v --version').name('vendure');
-
-program
-    .command('import-products <csvFile>')
-    .option('-l, --language', 'Specify ISO 639-1 language code, e.g. "de", "es". Defaults to "en"')
-    .description('Import product data from the specified csv file')
-    .action(async (csvPath, command) => {
-        const filePath = path.join(process.cwd(), csvPath);
-        await importProducts(filePath, command.language);
-    });
-program
-    .command('init <initDataFile>')
-    .description('Import initial data from the specified json file')
-    .action(async (initDataFile, command) => {
-        const filePath = path.join(process.cwd(), initDataFile);
-        logColored(`\nPopulating initial data from "${filePath}"...\n`);
-        const initialData = require(filePath);
-        const app = await getApplicationRef();
-        if (app) {
-            await populateInitialData(app, initialData);
-            logColored('\nDone!');
-            await app.close();
-        }
-        process.exit(0);
-    });
-program
-    .command('create-collections <initDataFile>')
-    .description('Create collections from the specified json file')
-    .action(async (initDataFile, command) => {
-        const filePath = path.join(process.cwd(), initDataFile);
-        logColored(`\nCreating collections from "${filePath}"...\n`);
-        const initialData = require(filePath);
-        const app = await getApplicationRef();
-        if (app) {
-            await populateCollections(app, initialData);
-            logColored('\nDone!');
-            await app.close();
-        }
-        process.exit(0);
-    });
-program.parse(process.argv);
-if (!process.argv.slice(2).length) {
-    program.help();
-}
-
-async function importProducts(csvPath: string, languageCode: import('@vendure/core').LanguageCode) {
-    logColored(`\nImporting from "${csvPath}"...\n`);
-    const app = await getApplicationRef();
-    if (app) {
-        await importProductsFromCsv(app, csvPath, languageCode);
-        logColored('\nDone!');
-        await app.close();
-        process.exit(0);
-    }
-}
-
-async function getApplicationRef(): Promise<INestApplication | undefined> {
-    const tsConfigFile = path.join(process.cwd(), 'vendure-config.ts');
-    const jsConfigFile = path.join(process.cwd(), 'vendure-config.js');
-    let isTs = false;
-    let configFile: string | undefined;
-    if (fs.existsSync(tsConfigFile)) {
-        configFile = tsConfigFile;
-        isTs = true;
-    } else if (fs.existsSync(jsConfigFile)) {
-        configFile = jsConfigFile;
-    }
-
-    if (!configFile) {
-        console.error('Could not find a config file');
-        console.error(`Checked "${tsConfigFile}", "${jsConfigFile}"`);
-        process.exit(1);
-        return;
-    }
-
-    if (isTs) {
-        // we expect ts-node to be available
-        const tsNode = require('ts-node');
-        if (!tsNode) {
-            console.error('For "populate" to work with TypeScript projects, you must have ts-node installed');
-            process.exit(1);
-            return;
-        }
-        require('ts-node').register();
-    }
-
-    const index = require(configFile);
-
-    if (!index) {
-        console.error(`Could not read the contents of "${configFile}"`);
-        process.exit(1);
-        return;
-    }
-    if (!index.config) {
-        console.error(`The file "${configFile}" does not export a "config" object`);
-        process.exit(1);
-        return;
-    }
-
-    const config = index.config;
-
-    // Force the sync mode on, so that all the tables are created
-    // on this initial run.
-    config.dbConnectionOptions.synchronize = true;
-
-    const { bootstrap } = require('@vendure/core');
-    console.log('Bootstrapping Vendure server...');
-    const app = await bootstrap(config);
-    return app;
-}

+ 1 - 1
packages/core/src/common/utils.ts

@@ -3,7 +3,7 @@ import { ID } from '@vendure/common/lib/shared-types';
 import { lastValueFrom, Observable, Observer } from 'rxjs';
 import { FindOptionsRelations } from 'typeorm/find-options/FindOptionsRelations';
 
-import { RelationPaths } from '../api/index';
+import { RelationPaths } from '../api/decorators/relations.decorator';
 import { VendureEntity } from '../entity/base/base.entity';
 
 /**

+ 1 - 1
packages/core/src/config/asset-import-strategy/default-asset-import-strategy.ts

@@ -7,7 +7,7 @@ import { delay, retryWhen, take, tap } from 'rxjs/operators';
 import { Readable } from 'stream';
 import { URL } from 'url';
 
-import { Injector } from '../../common/index';
+import { Injector } from '../../common/injector';
 import { ConfigService } from '../config.service';
 import { Logger } from '../logger/vendure-logger';
 

+ 1 - 1
packages/core/src/config/auth/default-password-validation-strategy.ts

@@ -1,4 +1,4 @@
-import { RequestContext } from '../../api/index';
+import { RequestContext } from '../../api/common/request-context';
 
 import { PasswordValidationStrategy } from './password-validation-strategy';
 

+ 1 - 1
packages/core/src/config/auth/password-validation-strategy.ts

@@ -1,4 +1,4 @@
-import { RequestContext } from '../../api/index';
+import { RequestContext } from '../../api/common/request-context';
 import { InjectableStrategy } from '../../common/types/injectable-strategy';
 
 /**

+ 0 - 2
packages/core/src/config/catalog/default-collection-filters.ts

@@ -1,7 +1,5 @@
 import { LanguageCode } from '@vendure/common/lib/generated-types';
 
-// eslint-disable-next-line @typescript-eslint/no-var-requires
-
 import { ConfigArgDef } from '../../common/configurable-operation';
 import { UserInputError } from '../../common/error/errors';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';

+ 6 - 4
packages/core/src/config/catalog/default-product-variant-price-calculation-strategy.spec.ts

@@ -1,7 +1,7 @@
 import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
 
 import { roundMoney } from '../../common/round-money';
-import { ProductVariant } from '../../entity/index';
+import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
 import {
     createRequestContext,
     MockTaxRateService,
@@ -15,10 +15,8 @@ import {
 } from '../../testing/order-test-utils';
 import { ensureConfigLoaded } from '../config-helpers';
 
-import { DefaultProductVariantPriceCalculationStrategy } from './default-product-variant-price-calculation-strategy';
-
 describe('DefaultProductVariantPriceCalculationStrategy', () => {
-    let strategy: DefaultProductVariantPriceCalculationStrategy;
+    let strategy: import('./default-product-variant-price-calculation-strategy').DefaultProductVariantPriceCalculationStrategy;
     const inputPrice = 6543;
     const productVariant = new ProductVariant({});
 
@@ -27,6 +25,10 @@ describe('DefaultProductVariantPriceCalculationStrategy', () => {
     });
 
     beforeEach(async () => {
+        // Dynamic import to avoid vitest circular dependency issue
+        const { DefaultProductVariantPriceCalculationStrategy } = await import(
+            './default-product-variant-price-calculation-strategy.js'
+        );
         strategy = new DefaultProductVariantPriceCalculationStrategy();
         const mockInjector = {
             get: () => {

+ 4 - 3
packages/core/src/config/catalog/default-stock-location-strategy.ts

@@ -1,8 +1,9 @@
 import { ID } from '@vendure/common/lib/shared-types';
 
-import { RequestContext } from '../../api/index';
-import { idsAreEqual, Injector } from '../../common/index';
-import { TransactionalConnection } from '../../connection/index';
+import { RequestContext } from '../../api/common/request-context';
+import { Injector } from '../../common/injector';
+import { idsAreEqual } from '../../common/utils';
+import { TransactionalConnection } from '../../connection/transactional-connection';
 import { OrderLine } from '../../entity/order-line/order-line.entity';
 import { StockLevel } from '../../entity/stock-level/stock-level.entity';
 import { StockLocation } from '../../entity/stock-location/stock-location.entity';

+ 1 - 1
packages/core/src/config/custom-field/custom-field-types.ts

@@ -20,7 +20,7 @@ import {
     UiComponentConfig,
 } from '@vendure/common/lib/shared-types';
 
-import { RequestContext } from '../../api/index';
+import { RequestContext } from '../../api/common/request-context';
 import { Injector } from '../../common/injector';
 import { VendureEntity } from '../../entity/base/base.entity';
 

+ 2 - 2
packages/core/src/config/entity/entity-duplicator.ts

@@ -1,14 +1,14 @@
 import { ConfigArg, Permission } from '@vendure/common/lib/generated-types';
 import { ID } from '@vendure/common/lib/shared-types';
 
-import { RequestContext } from '../../api/index';
+import { RequestContext } from '../../api/common/request-context';
 import {
     ConfigArgs,
     ConfigArgValues,
     ConfigurableOperationDef,
     ConfigurableOperationDefOptions,
 } from '../../common/configurable-operation';
-import { VendureEntity } from '../../entity/index';
+import { VendureEntity } from '../../entity/base/base.entity';
 
 /**
  * @description

+ 4 - 4
packages/core/src/config/entity/entity-duplicators/collection-duplicator.ts

@@ -5,10 +5,10 @@ import {
     Permission,
 } from '@vendure/common/lib/generated-types';
 
-import { Injector } from '../../../common/index';
-import { TransactionalConnection } from '../../../connection/index';
-import { Collection } from '../../../entity/index';
-import { CollectionService } from '../../../service/index';
+import { Injector } from '../../../common/injector';
+import { TransactionalConnection } from '../../../connection/transactional-connection';
+import { Collection } from '../../../entity/collection/collection.entity';
+import { CollectionService } from '../../../service/services/collection.service';
 import { EntityDuplicator } from '../entity-duplicator';
 
 let connection: TransactionalConnection;

+ 5 - 4
packages/core/src/config/entity/entity-duplicators/facet-duplicator.ts

@@ -5,10 +5,11 @@ import {
     Permission,
 } from '@vendure/common/lib/generated-types';
 
-import { Injector } from '../../../common/index';
-import { TransactionalConnection } from '../../../connection/index';
-import { Facet } from '../../../entity/index';
-import { FacetService, FacetValueService } from '../../../service/index';
+import { Injector } from '../../../common/injector';
+import { TransactionalConnection } from '../../../connection/transactional-connection';
+import { Facet } from '../../../entity/facet/facet.entity';
+import { FacetValueService } from '../../../service/services/facet-value.service';
+import { FacetService } from '../../../service/services/facet.service';
 import { EntityDuplicator } from '../entity-duplicator';
 
 let connection: TransactionalConnection;

+ 10 - 9
packages/core/src/config/entity/entity-duplicators/product-duplicator.ts

@@ -8,15 +8,16 @@ import {
 } from '@vendure/common/lib/generated-types';
 import { IsNull } from 'typeorm';
 
-import { Injector, InternalServerError } from '../../../common/index';
-import { TransactionalConnection } from '../../../connection/index';
-import { Product, ProductOptionGroup, ProductVariant } from '../../../entity/index';
-import {
-    ProductOptionGroupService,
-    ProductOptionService,
-    ProductService,
-    ProductVariantService,
-} from '../../../service/index';
+import { InternalServerError } from '../../../common/error/errors';
+import { Injector } from '../../../common/injector';
+import { TransactionalConnection } from '../../../connection/transactional-connection';
+import { Product } from '../../../entity/product/product.entity';
+import { ProductOptionGroup } from '../../../entity/product-option-group/product-option-group.entity';
+import { ProductVariant } from '../../../entity/product-variant/product-variant.entity';
+import { ProductOptionGroupService } from '../../../service/services/product-option-group.service';
+import { ProductOptionService } from '../../../service/services/product-option.service';
+import { ProductVariantService } from '../../../service/services/product-variant.service';
+import { ProductService } from '../../../service/services/product.service';
 import { EntityDuplicator } from '../entity-duplicator';
 
 let connection: TransactionalConnection;

+ 5 - 6
packages/core/src/config/entity/entity-duplicators/promotion-duplicator.ts

@@ -1,16 +1,15 @@
 import {
-    CreateFacetInput,
     CreatePromotionInput,
-    FacetTranslationInput,
     LanguageCode,
     Permission,
     PromotionTranslationInput,
 } from '@vendure/common/lib/generated-types';
 
-import { Injector, isGraphQlErrorResult } from '../../../common/index';
-import { TransactionalConnection } from '../../../connection/index';
-import { Facet, Promotion } from '../../../entity/index';
-import { FacetService, FacetValueService, PromotionService } from '../../../service/index';
+import { isGraphQlErrorResult } from '../../../common/error/error-result';
+import { Injector } from '../../../common/injector';
+import { TransactionalConnection } from '../../../connection/transactional-connection';
+import { Promotion } from '../../../entity/promotion/promotion.entity';
+import { PromotionService } from '../../../service/services/promotion.service';
 import { EntityDuplicator } from '../entity-duplicator';
 
 let connection: TransactionalConnection;

+ 5 - 2
packages/core/src/config/fulfillment/default-fulfillment-process.ts

@@ -2,11 +2,14 @@ import { HistoryEntryType } from '@vendure/common/lib/generated-types';
 import { ID } from '@vendure/common/lib/shared-types';
 
 import { RequestContext } from '../../api/common/request-context';
-import { awaitPromiseOrObservable, InternalServerError, isGraphQlErrorResult } from '../../common/index';
+import { isGraphQlErrorResult } from '../../common/error/error-result';
+import { InternalServerError } from '../../common/error/errors';
+import { awaitPromiseOrObservable } from '../../common/utils';
 import { Fulfillment } from '../../entity/fulfillment/fulfillment.entity';
 import { Order } from '../../entity/order/order.entity';
+import { FulfillmentState } from '../../service/helpers/fulfillment-state-machine/fulfillment-state';
+import { OrderState } from '../../service/helpers/order-state-machine/order-state';
 import { orderItemsAreDelivered, orderItemsAreShipped } from '../../service/helpers/utils/order-utils';
-import { FulfillmentState, OrderState } from '../../service/index';
 
 import { FulfillmentProcess } from './fulfillment-process';
 

+ 2 - 2
packages/core/src/config/order/active-order-strategy.ts

@@ -1,7 +1,7 @@
 import { DocumentNode } from 'graphql';
 
-import { RequestContext } from '../../api/index';
-import { InjectableStrategy } from '../../common/index';
+import { RequestContext } from '../../api/common/request-context';
+import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import { Order } from '../../entity/order/order.entity';
 
 export const ACTIVE_ORDER_INPUT_FIELD_NAME = 'activeOrderInput';

+ 2 - 2
packages/core/src/config/order/default-guest-checkout-strategy.ts

@@ -1,9 +1,9 @@
 import { CreateCustomerInput, SetCustomerForOrderResult } from '@vendure/common/lib/generated-shop-types';
 
 import { RequestContext } from '../../api/common/request-context';
+import { ErrorResultUnion } from '../../common/error/error-result';
 import { AlreadyLoggedInError, GuestCheckoutError } from '../../common/error/generated-graphql-shop-errors';
-import { ErrorResultUnion, Injector } from '../../common/index';
-import { InjectableStrategy } from '../../common/types/injectable-strategy';
+import { Injector } from '../../common/injector';
 import { Customer, Order } from '../../entity/index';
 import { CustomerService } from '../../service/services/customer.service';
 

+ 5 - 6
packages/core/src/config/order/default-order-process.ts

@@ -2,7 +2,7 @@ import { HistoryEntryType } from '@vendure/common/lib/generated-types';
 import { ID } from '@vendure/common/lib/shared-types';
 import { unique } from '@vendure/common/lib/unique';
 
-import { RequestContext } from '../../api/index';
+import { RequestContext } from '../../api/common/request-context';
 import { TransactionalConnection } from '../../connection/transactional-connection';
 import { Order } from '../../entity/order/order.entity';
 import { OrderLine } from '../../entity/order-line/order-line.entity';
@@ -414,13 +414,12 @@ export function configureDefaultOrderProcess(options: DefaultOrderProcessOptions
                     order.orderPlacedAt = new Date();
                     await Promise.all(
                         order.lines.map(line => {
-                            line.orderPlacedQuantity = line.quantity
+                            line.orderPlacedQuantity = line.quantity;
                             connection
                                 .getRepository(ctx, OrderLine)
-                                .update(line.id, { orderPlacedQuantity: line.quantity })
-                            return line
-                        }
-                        ),
+                                .update(line.id, { orderPlacedQuantity: line.quantity });
+                            return line;
+                        }),
                     );
                     eventBus.publish(new OrderPlacedEvent(fromState, toState, ctx, order));
                     await orderSplitter.createSellerOrders(ctx, order);

+ 3 - 2
packages/core/src/config/order/guest-checkout-strategy.ts

@@ -1,9 +1,10 @@
 import { CreateCustomerInput, SetCustomerForOrderResult } from '@vendure/common/lib/generated-shop-types';
 
 import { RequestContext } from '../../api/common/request-context';
-import { ErrorResultUnion } from '../../common/index';
+import { ErrorResultUnion } from '../../common/error/error-result';
 import { InjectableStrategy } from '../../common/types/injectable-strategy';
-import { Customer, Order } from '../../entity/index';
+import { Customer } from '../../entity/customer/customer.entity';
+import { Order } from '../../entity/order/order.entity';
 
 /**
  * @description

+ 3 - 5
packages/core/src/config/order/order-seller-strategy.ts

@@ -1,14 +1,12 @@
 import { ID } from '@vendure/common/lib/shared-types';
 
-import { RequestContext } from '../../api/index';
-import { InjectableStrategy } from '../../common/index';
+import { RequestContext } from '../../api/common/request-context';
+import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import { Channel } from '../../entity/channel/channel.entity';
 import { Order } from '../../entity/order/order.entity';
 import { OrderLine } from '../../entity/order-line/order-line.entity';
-import { Payment } from '../../entity/payment/payment.entity';
 import { ShippingLine } from '../../entity/shipping-line/shipping-line.entity';
-import { Surcharge } from '../../entity/surcharge/surcharge.entity';
-import { OrderState } from '../../service/index';
+import { OrderState } from '../../service/helpers/order-state-machine/order-state';
 
 /**
  * @description

+ 1 - 1
packages/core/src/config/payment/default-payment-process.ts

@@ -1,7 +1,7 @@
 import { HistoryEntryType } from '@vendure/common/lib/generated-types';
 
+import { PaymentState } from '../../service/helpers/payment-state-machine/payment-state';
 import { orderTotalIsCovered } from '../../service/helpers/utils/order-utils';
-import { PaymentState } from '../../service/index';
 
 import { PaymentProcess } from './payment-process';
 

+ 1 - 1
packages/core/src/config/promotion/conditions/customer-group-condition.ts

@@ -5,7 +5,7 @@ import { Subscription } from 'rxjs';
 import { TtlCache } from '../../../common/ttl-cache';
 import { idsAreEqual } from '../../../common/utils';
 import { EventBus } from '../../../event-bus/event-bus';
-import { CustomerGroupChangeEvent } from '../../../event-bus/index';
+import { CustomerGroupChangeEvent } from '../../../event-bus/events/customer-group-change-event';
 import { PromotionCondition } from '../promotion-condition';
 
 let customerService: import('../../../service/services/customer.service').CustomerService;

+ 1 - 1
packages/core/src/config/shipping-method/default-shipping-line-assignment-strategy.ts

@@ -1,4 +1,4 @@
-import { RequestContext } from '../../api/index';
+import { RequestContext } from '../../api/common/request-context';
 import { Order } from '../../entity/order/order.entity';
 import { OrderLine } from '../../entity/order-line/order-line.entity';
 import { ShippingLine } from '../../entity/shipping-line/shipping-line.entity';

+ 2 - 2
packages/core/src/config/shipping-method/shipping-line-assignment-strategy.ts

@@ -1,5 +1,5 @@
-import { RequestContext } from '../../api/index';
-import { InjectableStrategy } from '../../common/index';
+import { RequestContext } from '../../api/common/request-context';
+import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import { Order } from '../../entity/order/order.entity';
 import { OrderLine } from '../../entity/order-line/order-line.entity';
 import { ShippingLine } from '../../entity/shipping-line/shipping-line.entity';

+ 1 - 1
packages/core/src/config/system/error-handler-strategy.ts

@@ -1,6 +1,6 @@
 import { ArgumentsHost } from '@nestjs/common';
 
-import { InjectableStrategy } from '../../common/index';
+import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import { Job } from '../../job-queue/job';
 
 /**

+ 1 - 2
packages/core/src/data-import/providers/asset-importer/asset-importer.ts

@@ -1,7 +1,6 @@
 import { Injectable } from '@nestjs/common';
-import path from 'path';
 
-import { RequestContext } from '../../../api/index';
+import { RequestContext } from '../../../api/common/request-context';
 import { isGraphQlErrorResult } from '../../../common/index';
 import { ConfigService } from '../../../config/config.service';
 import { Asset } from '../../../entity/asset/asset.entity';

+ 2 - 2
packages/core/src/data-import/providers/import-parser/import-parser.ts

@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
 import { GlobalFlag, LanguageCode } from '@vendure/common/lib/generated-types';
 import { normalizeString } from '@vendure/common/lib/normalize-string';
 import { unique } from '@vendure/common/lib/unique';
-import parse from 'csv-parse';
+import { parse, Options } from 'csv-parse';
 import { Stream } from 'stream';
 
 import { InternalServerError } from '../../../common/error/errors';
@@ -163,7 +163,7 @@ export class ImportParser {
         input: string | Stream,
         mainLanguage: LanguageCode = this.configService.defaultLanguageCode,
     ): Promise<ParseResult<ParsedProductWithVariants>> {
-        const options: parse.Options = {
+        const options: Options = {
             trim: true,
             relax_column_count: true,
         };

+ 2 - 1
packages/core/src/data-import/providers/importer/fast-importer.service.ts

@@ -23,9 +23,10 @@ import { ProductVariantAsset } from '../../../entity/product-variant/product-var
 import { ProductVariantPrice } from '../../../entity/product-variant/product-variant-price.entity';
 import { ProductVariantTranslation } from '../../../entity/product-variant/product-variant-translation.entity';
 import { ProductVariant } from '../../../entity/product-variant/product-variant.entity';
+import { RequestContextService } from '../../../service/helpers/request-context/request-context.service';
 import { TranslatableSaver } from '../../../service/helpers/translatable-saver/translatable-saver';
-import { RequestContextService, StockMovementService } from '../../../service/index';
 import { ChannelService } from '../../../service/services/channel.service';
+import { StockMovementService } from '../../../service/services/stock-movement.service';
 
 /**
  * @description

+ 1 - 1
packages/core/src/data-import/providers/populator/populator.ts

@@ -11,7 +11,7 @@ import {
     Logger,
 } from '../../../config';
 import { manualFulfillmentHandler } from '../../../config/fulfillment/manual-fulfillment-handler';
-import { TransactionalConnection } from '../../../connection/index';
+import { TransactionalConnection } from '../../../connection/transactional-connection';
 import { Channel, Collection, FacetValue, TaxCategory, User } from '../../../entity';
 import {
     CollectionService,

+ 13 - 1
packages/core/src/entity/asset/asset.entity.ts

@@ -1,12 +1,15 @@
 import { AssetType } from '@vendure/common/lib/generated-types';
 import { DeepPartial } from '@vendure/common/lib/shared-types';
-import { Column, Entity, JoinTable, ManyToMany } from 'typeorm';
+import { Column, Entity, JoinTable, ManyToMany, OneToMany } from 'typeorm';
 
 import { ChannelAware, Taggable } from '../../common/types/common-types';
 import { HasCustomFields } from '../../config/custom-field/custom-field-types';
 import { VendureEntity } from '../base/base.entity';
 import { Channel } from '../channel/channel.entity';
+import { Collection } from '../collection/collection.entity';
 import { CustomAssetFields } from '../custom-entity-fields';
+import { Product } from '../product/product.entity';
+import { ProductVariant } from '../product-variant/product-variant.entity';
 import { Tag } from '../tag/tag.entity';
 
 /**
@@ -49,6 +52,15 @@ export class Asset extends VendureEntity implements Taggable, ChannelAware, HasC
     @JoinTable()
     channels: Channel[];
 
+    @OneToMany(type => Collection, collection => collection.featuredAsset)
+    featuredInCollections?: Collection[];
+
+    @OneToMany(type => ProductVariant, productVariant => productVariant.featuredAsset)
+    featuredInVariants?: ProductVariant[];
+
+    @OneToMany(type => Product, product => product.featuredAsset)
+    featuredInProducts?: Product[];
+
     @Column(type => CustomAssetFields)
     customFields: CustomAssetFields;
 }

+ 21 - 1
packages/core/src/entity/channel/channel.entity.ts

@@ -1,10 +1,15 @@
 import { CurrencyCode, LanguageCode } from '@vendure/common/lib/generated-types';
 import { DeepPartial, ID } from '@vendure/common/lib/shared-types';
-import { Column, Entity, Index, ManyToOne } from 'typeorm';
+import { Column, Entity, Index, ManyToMany, ManyToOne } from 'typeorm';
 
 import { VendureEntity } from '../base/base.entity';
+import { Collection } from '../collection/collection.entity';
 import { CustomChannelFields } from '../custom-entity-fields';
 import { EntityId } from '../entity-id.decorator';
+import { Facet } from '../facet/facet.entity';
+import { FacetValue } from '../facet-value/facet-value.entity';
+import { Product } from '../product/product.entity';
+import { ProductVariant } from '../product-variant/product-variant.entity';
 import { Seller } from '../seller/seller.entity';
 import { Zone } from '../zone/zone.entity';
 
@@ -103,6 +108,21 @@ export class Channel extends VendureEntity {
 
     @Column() pricesIncludeTax: boolean;
 
+    @ManyToMany(type => Product, product => product.channels, { onDelete: 'CASCADE' })
+    products: Product[];
+
+    @ManyToMany(type => ProductVariant, productVariant => productVariant.channels, { onDelete: 'CASCADE' })
+    productVariants: ProductVariant[];
+
+    @ManyToMany(type => FacetValue, facetValue => facetValue.channels, { onDelete: 'CASCADE' })
+    facetValues: FacetValue[];
+
+    @ManyToMany(type => Facet, facet => facet.channels, { onDelete: 'CASCADE' })
+    facets: Facet[];
+
+    @ManyToMany(type => Collection, collection => collection.channels, { onDelete: 'CASCADE' })
+    collections: Collection[];
+
     private generateToken(): string {
         const randomString = () => Math.random().toString(36).substr(3, 10);
         return `${randomString()}${randomString()}`;

+ 2 - 2
packages/core/src/entity/collection/collection.entity.ts

@@ -61,7 +61,7 @@ export class Collection
     translations: Array<Translation<Collection>>;
 
     @Index()
-    @ManyToOne(type => Asset, { onDelete: 'SET NULL' })
+    @ManyToOne(type => Asset, asset => asset.featuredInCollections, { onDelete: 'SET NULL' })
     featuredAsset: Asset;
 
     @OneToMany(type => CollectionAsset, collectionAsset => collectionAsset.collection)
@@ -90,7 +90,7 @@ export class Collection
     @EntityId({ nullable: true })
     parentId: ID;
 
-    @ManyToMany(type => Channel)
+    @ManyToMany(type => Channel, channel => channel.collections)
     @JoinTable()
     channels: Channel[];
 }

+ 9 - 1
packages/core/src/entity/facet-value/facet-value.entity.ts

@@ -9,6 +9,8 @@ import { Channel } from '../channel/channel.entity';
 import { CustomFacetValueFields } from '../custom-entity-fields';
 import { EntityId } from '../entity-id.decorator';
 import { Facet } from '../facet/facet.entity';
+import { Product } from '../product/product.entity';
+import { ProductVariant } from '../product-variant/product-variant.entity';
 
 import { FacetValueTranslation } from './facet-value-translation.entity';
 
@@ -40,7 +42,13 @@ export class FacetValue extends VendureEntity implements Translatable, HasCustom
     @Column(type => CustomFacetValueFields)
     customFields: CustomFacetValueFields;
 
-    @ManyToMany(type => Channel)
+    @ManyToMany(type => Channel, channel => channel.facetValues)
     @JoinTable()
     channels: Channel[];
+
+    @ManyToMany(() => Product, product => product.facetValues, { onDelete: 'CASCADE' })
+    products: Product[];
+
+    @ManyToMany(type => ProductVariant, productVariant => productVariant.facetValues)
+    productVariants: ProductVariant[];
 }

+ 5 - 1
packages/core/src/entity/fulfillment/fulfillment.entity.ts

@@ -1,10 +1,11 @@
 import { DeepPartial } from '@vendure/common/lib/shared-types';
-import { Column, Entity, OneToMany } from 'typeorm';
+import { Column, Entity, ManyToMany, OneToMany } from 'typeorm';
 
 import { HasCustomFields } from '../../config/custom-field/custom-field-types';
 import { FulfillmentState } from '../../service/helpers/fulfillment-state-machine/fulfillment-state';
 import { VendureEntity } from '../base/base.entity';
 import { CustomFulfillmentFields } from '../custom-entity-fields';
+import { Order } from '../order/order.entity';
 import { FulfillmentLine } from '../order-line-reference/fulfillment-line.entity';
 
 /**
@@ -34,6 +35,9 @@ export class Fulfillment extends VendureEntity implements HasCustomFields {
     @OneToMany(type => FulfillmentLine, fulfillmentLine => fulfillmentLine.fulfillment)
     lines: FulfillmentLine[];
 
+    @ManyToMany(type => Order, order => order.fulfillments)
+    orders: Order[];
+
     @Column(type => CustomFulfillmentFields)
     customFields: CustomFulfillmentFields;
 }

+ 3 - 3
packages/core/src/entity/order/order.entity.ts

@@ -91,7 +91,7 @@ export class Order extends VendureEntity implements ChannelAware, HasCustomField
     orderPlacedAt?: Date;
 
     @Index()
-    @ManyToOne(type => Customer)
+    @ManyToOne(type => Customer, customer => customer.orders)
     customer?: Customer;
 
     @EntityId({ nullable: true })
@@ -122,7 +122,7 @@ export class Order extends VendureEntity implements ChannelAware, HasCustomField
      * Promotions applied to the order. Only gets populated after the payment process has completed,
      * i.e. the Order is no longer active.
      */
-    @ManyToMany(type => Promotion)
+    @ManyToMany(type => Promotion, promotion => promotion.orders)
     @JoinTable()
     promotions: Promotion[];
 
@@ -133,7 +133,7 @@ export class Order extends VendureEntity implements ChannelAware, HasCustomField
     @OneToMany(type => Payment, payment => payment.order)
     payments: Payment[];
 
-    @ManyToMany(type => Fulfillment)
+    @ManyToMany(type => Fulfillment, fulfillment => fulfillment.orders)
     @JoinTable()
     fulfillments: Fulfillment[];
 

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio