Просмотр исходного кода

Merge branch 'master' into minor

Michael Bromley 2 лет назад
Родитель
Сommit
f83455cbf3
100 измененных файлов с 1845 добавлено и 242 удалено
  1. 3 3
      .github/pull_request_template.md
  2. 17 0
      CHANGELOG.md
  3. 0 3
      README.md
  4. 1 1
      docs/docs/guides/developer-guide/custom-fields/index.md
  5. 27 0
      docs/docs/guides/developer-guide/error-handling/index.mdx
  6. 2 3
      docs/docs/guides/developer-guide/plugins/index.mdx
  7. 6 4
      docs/docs/guides/extending-the-admin-ui/creating-list-views/index.md
  8. 1 1
      docs/docs/reference/admin-ui-api/action-bar/action-bar-context.md
  9. 12 1
      docs/docs/reference/admin-ui-api/action-bar/action-bar-item.md
  10. 1 1
      docs/docs/reference/admin-ui-api/action-bar/router-link-definition.md
  11. 4 2
      docs/docs/reference/admin-ui-api/nav-menu/add-nav-menu-item.md
  12. 3 1
      docs/docs/reference/admin-ui-api/nav-menu/add-nav-menu-section.md
  13. 12 1
      docs/docs/reference/admin-ui-api/nav-menu/nav-menu-section.md
  14. 1 1
      docs/docs/reference/admin-ui-api/react-extensions/register-react-data-table-component.md
  15. 3 3
      docs/docs/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-plugin.md
  16. 131 0
      docs/docs/reference/core-plugins/sentry-plugin/index.md
  17. 48 0
      docs/docs/reference/core-plugins/sentry-plugin/sentry-plugin-options.md
  18. 281 0
      docs/docs/reference/core-plugins/stellate-plugin/index.md
  19. 95 0
      docs/docs/reference/core-plugins/stellate-plugin/purge-rule.md
  20. 69 0
      docs/docs/reference/core-plugins/stellate-plugin/stellate-plugin-options.md
  21. 75 0
      docs/docs/reference/core-plugins/stellate-plugin/stellate-service.md
  22. 1 3
      docs/docs/reference/graphql-api/admin/enums.md
  23. 13 3
      docs/docs/reference/graphql-api/admin/input-types.md
  24. 1 3
      docs/docs/reference/graphql-api/admin/mutations.md
  25. 14 4
      docs/docs/reference/graphql-api/admin/object-types.md
  26. 1 3
      docs/docs/reference/graphql-api/admin/queries.md
  27. 1 3
      docs/docs/reference/graphql-api/shop/enums.md
  28. 82 3
      docs/docs/reference/graphql-api/shop/input-types.md
  29. 1 3
      docs/docs/reference/graphql-api/shop/mutations.md
  30. 26 3
      docs/docs/reference/graphql-api/shop/object-types.md
  31. 1 3
      docs/docs/reference/graphql-api/shop/queries.md
  32. 1 1
      docs/docs/reference/typescript-api/common/permission.md
  33. 9 9
      docs/docs/reference/typescript-api/configurable-operation-def/default-form-config-hash.md
  34. 1 1
      docs/docs/reference/typescript-api/custom-fields/custom-field-type.md
  35. 34 0
      docs/docs/reference/typescript-api/errors/error-result-union.md
  36. 44 0
      docs/docs/reference/typescript-api/errors/is-graph-ql-error-result.md
  37. 1 2
      docs/docs/reference/typescript-api/events/vendure-entity-event.md
  38. 1 1
      docs/docs/reference/typescript-api/orders/default-guest-checkout-strategy.md
  39. 1 1
      docs/docs/reference/typescript-api/orders/guest-checkout-strategy.md
  40. 1 1
      docs/docs/reference/typescript-api/orders/order-process.md
  41. 1 1
      docs/docs/reference/typescript-api/request/request-context-service.md
  42. 3 3
      docs/docs/reference/typescript-api/services/channel-service.md
  43. 1 1
      docs/docs/reference/typescript-api/services/collection-service.md
  44. 29 29
      docs/docs/reference/typescript-api/services/customer-service.md
  45. 1 1
      docs/docs/reference/typescript-api/services/facet-service.md
  46. 15 15
      docs/docs/reference/typescript-api/services/order-service.md
  47. 4 4
      docs/docs/reference/typescript-api/services/product-service.md
  48. 2 2
      docs/docs/reference/typescript-api/services/promotion-service.md
  49. 1 1
      docs/docs/reference/typescript-api/services/user-service.md
  50. 12 0
      docs/sidebars.js
  51. 1 1
      lerna.json
  52. 1 2
      package.json
  53. 3 3
      packages/admin-ui-plugin/package.json
  54. 1 1
      packages/admin-ui/package-lock.json
  55. 2 2
      packages/admin-ui/package.json
  56. 1 1
      packages/admin-ui/src/lib/core/src/common/version.ts
  57. 36 34
      packages/admin-ui/src/lib/core/src/shared/components/asset-preview/asset-preview.component.html
  58. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/asset-preview/asset-preview.component.scss
  59. 3 3
      packages/asset-server-plugin/package.json
  60. 2 2
      packages/cli/package.json
  61. 1 1
      packages/common/package.json
  62. 1 1
      packages/common/src/shared-types.ts
  63. 42 3
      packages/core/e2e/fixtures/test-plugins/list-query-plugin.ts
  64. 42 0
      packages/core/e2e/list-query-builder.e2e-spec.ts
  65. 2 2
      packages/core/package.json
  66. 23 1
      packages/core/src/common/error/error-result.ts
  67. 5 2
      packages/core/src/config/order/default-order-process.ts
  68. 69 0
      packages/core/src/entity/base/base.entity.spec.ts
  69. 8 2
      packages/core/src/entity/base/base.entity.ts
  70. 1 0
      packages/core/src/event-bus/index.ts
  71. 0 1
      packages/core/src/i18n/messages/en.json
  72. 10 3
      packages/core/src/service/helpers/list-query-builder/list-query-builder.ts
  73. 4 2
      packages/core/src/service/services/order.service.ts
  74. 0 3
      packages/core/src/service/services/stock-movement.service.ts
  75. 3 3
      packages/create/package.json
  76. 9 9
      packages/dev-server/package.json
  77. 3 3
      packages/elasticsearch-plugin/package.json
  78. 1 1
      packages/elasticsearch-plugin/src/types.ts
  79. 3 3
      packages/email-plugin/package.json
  80. 3 3
      packages/harden-plugin/package.json
  81. 3 3
      packages/job-queue-plugin/package.json
  82. 3 3
      packages/job-queue-plugin/src/bullmq/plugin.ts
  83. 4 4
      packages/payments-plugin/package.json
  84. 16 4
      packages/payments-plugin/src/mollie/mollie.controller.ts
  85. 10 6
      packages/payments-plugin/src/mollie/mollie.service.ts
  86. 3 0
      packages/sentry-plugin/.gitignore
  87. 7 0
      packages/sentry-plugin/README.md
  88. 4 0
      packages/sentry-plugin/index.ts
  89. 28 0
      packages/sentry-plugin/package.json
  90. 32 0
      packages/sentry-plugin/src/api/admin-test.resolver.ts
  91. 14 0
      packages/sentry-plugin/src/api/api-extensions.ts
  92. 11 0
      packages/sentry-plugin/src/api/error-test.service.ts
  93. 3 0
      packages/sentry-plugin/src/constants.ts
  94. 58 0
      packages/sentry-plugin/src/sentry-apollo-plugin.ts
  95. 25 0
      packages/sentry-plugin/src/sentry-context.middleware.ts
  96. 146 0
      packages/sentry-plugin/src/sentry-plugin.ts
  97. 27 0
      packages/sentry-plugin/src/sentry.filter.ts
  98. 40 0
      packages/sentry-plugin/src/sentry.service.ts
  99. 26 0
      packages/sentry-plugin/src/types.ts
  100. 9 0
      packages/sentry-plugin/tsconfig.build.json

+ 3 - 3
.github/pull_request_template.md

@@ -12,11 +12,11 @@ You can add screenshots here if applicable.
 
 # Checklist
 
-:pushpin: Always:
+📌 Always:
 - [ ] I have set a clear title
 - [ ] My PR is small and contains a single feature
 - [ ] I have [checked my own PR](## "Fix typo's and remove unused or commented out code")
 
-:zap: Most of the time:
+👍 Most of the time:
 - [ ] I have added or updated test cases
-- [ ] I have updated the README if needed
+- [ ] I have updated the README if needed

+ 17 - 0
CHANGELOG.md

@@ -1,3 +1,20 @@
+## <small>2.1.5 (2023-12-14)</small>
+
+
+#### Fixes
+
+* **admin-ui** Fix display of asset detail focal point buttons ([1b58aa7](https://github.com/vendure-ecommerce/vendure/commit/1b58aa7))
+* **core** Export VendureEntityEvent abstract class from index (#2556) ([c46cf74](https://github.com/vendure-ecommerce/vendure/commit/c46cf74)), closes [#2556](https://github.com/vendure-ecommerce/vendure/issues/2556)
+* **core** Fix bug when instantiating entity from object with getter ([d09452e](https://github.com/vendure-ecommerce/vendure/commit/d09452e)), closes [#2574](https://github.com/vendure-ecommerce/vendure/issues/2574)
+* **core** Fix loading multiple customField relations (#2566) ([99e04d1](https://github.com/vendure-ecommerce/vendure/commit/99e04d1)), closes [#2566](https://github.com/vendure-ecommerce/vendure/issues/2566) [#2555](https://github.com/vendure-ecommerce/vendure/issues/2555)
+* **core** OrderLineEvent includes ID of deleted OrderLine ([ee04032](https://github.com/vendure-ecommerce/vendure/commit/ee04032)), closes [#2574](https://github.com/vendure-ecommerce/vendure/issues/2574)
+* **core** Remove redundant constraint when creating allocations ([52c0841](https://github.com/vendure-ecommerce/vendure/commit/52c0841)), closes [#2563](https://github.com/vendure-ecommerce/vendure/issues/2563)
+* **core** Send the correct amount to `refundOrder` (#2559) ([b5a265f](https://github.com/vendure-ecommerce/vendure/commit/b5a265f)), closes [#2559](https://github.com/vendure-ecommerce/vendure/issues/2559)
+* **elasticsearch-plugin** Fix type to allow the promise on custom mapping definition (#2562) ([8e9ee07](https://github.com/vendure-ecommerce/vendure/commit/8e9ee07)), closes [#2562](https://github.com/vendure-ecommerce/vendure/issues/2562)
+* **payments-plugin** Fix Mollie channel awareness (#2575) ([cc4826d](https://github.com/vendure-ecommerce/vendure/commit/cc4826d)), closes [#2575](https://github.com/vendure-ecommerce/vendure/issues/2575)
+* **payments-plugin** Mollie - ignore completed state to prevent unneccesary error throwing (#2569) ([ed80c68](https://github.com/vendure-ecommerce/vendure/commit/ed80c68)), closes [#2569](https://github.com/vendure-ecommerce/vendure/issues/2569)
+* **stellate-plugin** Add stellate plugin ([2254576](https://github.com/vendure-ecommerce/vendure/commit/2254576))
+
 ## <small>2.1.4 (2023-11-24)</small>
 
 

+ 0 - 3
README.md

@@ -48,9 +48,6 @@ The root directory has a `package.json` which contains build-related dependencie
 * Generating TypeScript types from the GraphQL schema
 * Linting, formatting & testing tasks to run on git commit & push
 
-> Note:
-> When you do `yarn` for the first time, you will need to manually create the `package` folder under [/packages/admin-ui](/packages/admin-ui).
-
 ### 2. Build all packages
 
 `yarn build`

+ 1 - 1
docs/docs/guides/developer-guide/custom-fields/index.md

@@ -141,7 +141,7 @@ The following types are available for custom fields:
 | `string`       | Short string data            | url, label                                               |
 | `localeString` | Localized short strings      | localized url                                            |
 | `text`         | Long text data               | extended product info, json config object                |
-| `localText`    | Localized long text          | localized extended product info                          |
+| `localeText`   | Localized long text          | localized extended product info                          |
 | `int`          | Integer                      | product weight, customer loyalty points, monetary values |
 | `float`        | Floating point number        | product review rating                                    |
 | `boolean`      | Boolean                      | isDownloadable flag on product                           |

+ 27 - 0
docs/docs/guides/developer-guide/error-handling/index.mdx

@@ -231,6 +231,33 @@ Here's how a response would look in both the success and error result cases:
 </TabItem>
 </Tabs>
 
+### Handling ErrorResults in plugin code
+
+If you are writing a plugin which deals with internal Vendure service methods that may return ErrorResults,
+then you can use the `isGraphQlErrorResult()` function to check whether the result is an ErrorResult:
+
+```ts
+import { Injectable} from '@nestjs/common';
+import { isGraphQlErrorResult, Order, OrderService, OrderState, RequestContext } from '@vendure/core';
+
+@Injectable()
+export class MyService {
+
+    constructor(private orderService: OrderService) {}
+
+    async myMethod(ctx: RequestContext, order: Order, newState: OrderState) {
+        const transitionResult = await this.orderService.transitionToState(ctx, order.id, newState);
+        if (isGraphQlErrorResult(transitionResult)) {
+            // The transition failed with an ErrorResult
+            throw transitionResult;
+        } else {
+            // TypeScript will correctly infer the type of `transitionResult` to be `Order`
+            return transitionResult;
+        }
+    }
+}
+```
+
 ### Handling ErrorResults in client code
 
 Because we know all possible ErrorResult that may occur for a given mutation, we can handle them in an exhaustive manner. In other

+ 2 - 3
docs/docs/guides/developer-guide/plugins/index.mdx

@@ -138,7 +138,7 @@ export class MyPlugin implements NestModule {
 
 In Vendure **plugins** are used to extend the core functionality of the server. Plugins can be pre-made functionality that you can install via npm, or they can be custom plugins that you write yourself.
 
-For any unit of functionality that you need to add to your project, you'll be writing creating a Vendure plugin. By convention, plugins are stored in the `plugins` directory of your project. However, this is not a requirement, and you are free to arrange your plugin files in any way you like.
+For any unit of functionality that you need to add to your project, you'll be creating a Vendure plugin. By convention, plugins are stored in the `plugins` directory of your project. However, this is not a requirement, and you are free to arrange your plugin files in any way you like.
 
 ```txt
 ├──src
@@ -281,7 +281,6 @@ In order to make use of this custom field in a type-safe way, we can tell TypeSc
 ```
 
 ```ts title="src/plugins/wishlist-plugin/types.ts"
-import { CustomCustomerFields } from '@vendure/core/dist/entity/custom-entity-fields';
 import { WishlistItem } from './entities/wishlist-item.entity';
 
 declare module '@vendure/core/dist/entity/custom-entity-fields' {
@@ -312,7 +311,7 @@ Let's create a service to handle the wishlist functionality:
         ├── wishlist.service.ts
 ```
 
-```ts title="src/plugins/wishlist-plugin/wishlist.service.ts"
+```ts title="src/plugins/wishlist-plugin/services/wishlist.service.ts"
 import { Injectable } from '@nestjs/common';
 import {
     Customer,

+ 6 - 4
docs/docs/guides/extending-the-admin-ui/creating-list-views/index.md

@@ -185,12 +185,13 @@ This is the standard layout for any list view. The main functionality is provide
     />
     
     <!-- Here we define all the available columns -->
-    <vdr-dt2-column [heading]="'common.id' | translate" [hiddenByDefault]="true">
+    <vdr-dt2-column id="id" [heading]="'common.id' | translate" [hiddenByDefault]="true">
         <ng-template let-review="item">
             {{ review.id }}
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
+            id="created-at"
             [heading]="'common.created-at' | translate"
             [hiddenByDefault]="true"
             [sort]="sorts.get('createdAt')"
@@ -200,6 +201,7 @@ This is the standard layout for any list view. The main functionality is provide
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
+            id="updated-at"
             [heading]="'common.updated-at' | translate"
             [hiddenByDefault]="true"
             [sort]="sorts.get('updatedAt')"
@@ -208,7 +210,7 @@ This is the standard layout for any list view. The main functionality is provide
             {{ review.updatedAt | localeDate : 'short' }}
         </ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-column heading="Title" [optional]="false" [sort]="sorts.get('title')">
+    <vdr-dt2-column id="title" heading="Title" [optional]="false" [sort]="sorts.get('title')">
         <ng-template let-review="item">
             <a class="button-ghost" [routerLink]="['./', review.id]"
             ><span>{{ review.title }}</span>
@@ -216,10 +218,10 @@ This is the standard layout for any list view. The main functionality is provide
             </a>
         </ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-column heading="Rating" [sort]="sorts.get('rating')">
+    <vdr-dt2-column id="rating" heading="Rating" [sort]="sorts.get('rating')">
         <ng-template let-review="item"><my-star-rating-component [rating]="review.rating"    /></ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-column heading="Author" [sort]="sorts.get('authorName')">
+    <vdr-dt2-column id="author" heading="Author" [sort]="sorts.get('authorName')">
         <ng-template let-review="item">{{ review.authorName }}</ng-template>
     </vdr-dt2-column>
 </vdr-data-table-2>

+ 1 - 1
docs/docs/reference/admin-ui-api/action-bar/action-bar-context.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ActionBarContext
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="78" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="89" packageName="@vendure/admin-ui" />
 
 Providers available to the onClick handler of an <a href='/reference/admin-ui-api/action-bar/action-bar-item#actionbaritem'>ActionBarItem</a> or <a href='/reference/admin-ui-api/nav-menu/nav-menu-item#navmenuitem'>NavMenuItem</a>.
 

+ 12 - 1
docs/docs/reference/admin-ui-api/action-bar/action-bar-item.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ActionBarItem
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="96" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="107" packageName="@vendure/admin-ui" />
 
 A button in the ActionBar area at the top of one of the list or detail views.
 
@@ -88,7 +88,18 @@ dynamically enable/disable or show/hide the button.
 
 <MemberInfo kind="property" type={`string | string[]`}   />
 
+Control the display of this item based on the user permissions. Note: if you attempt to pass a
+<a href='/reference/typescript-api/auth/permission-definition#permissiondefinition'>PermissionDefinition</a> object, you will get a compilation error. Instead, pass the plain
+string version. For example, if the permission is defined as:
+```ts
+export const MyPermission = new PermissionDefinition('ProductReview');
+```
+then the generated permission strings will be:
 
+- `CreateProductReview`
+- `ReadProductReview`
+- `UpdateProductReview`
+- `DeleteProductReview`
 
 
 </div>

+ 1 - 1
docs/docs/reference/admin-ui-api/action-bar/router-link-definition.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## RouterLinkDefinition
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="128" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="154" packageName="@vendure/admin-ui" />
 
 A function which returns the router link for an <a href='/reference/admin-ui-api/action-bar/action-bar-item#actionbaritem'>ActionBarItem</a> or <a href='/reference/admin-ui-api/nav-menu/nav-menu-item#navmenuitem'>NavMenuItem</a>.
 

+ 4 - 2
docs/docs/reference/admin-ui-api/nav-menu/add-nav-menu-item.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## addNavMenuItem
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/extension/add-nav-menu-item.ts" sourceLine="64" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/extension/add-nav-menu-item.ts" sourceLine="68" packageName="@vendure/admin-ui" />
 
 Add a menu item to an existing section specified by `sectionId`. The id of the section
 can be found by inspecting the DOM and finding the `data-section-id` attribute.
@@ -24,6 +24,8 @@ This should be used in the NgModule `providers` array of your ui extension modul
 *Example*
 
 ```ts title="providers.ts"
+import { addNavMenuItem } from '@vendure/admin-ui/core';
+
 export default [
     addNavMenuItem({
         id: 'reviews',
@@ -33,7 +35,7 @@ export default [
     },
     'marketing'),
 ];
-``
+```
 
 ```ts title="Signature"
 function addNavMenuItem(config: NavMenuItem, sectionId: string, before?: string): Provider

+ 3 - 1
docs/docs/reference/admin-ui-api/nav-menu/add-nav-menu-section.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## addNavMenuSection
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/extension/add-nav-menu-item.ts" sourceLine="28" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/extension/add-nav-menu-item.ts" sourceLine="30" packageName="@vendure/admin-ui" />
 
 Add a section to the main nav menu. Providing the `before` argument will
 move the section before any existing section with the specified id. If
@@ -22,6 +22,8 @@ This should be used in the NgModule `providers` array of your ui extension modul
 *Example*
 
 ```ts title="providers.ts"
+import { addNavMenuSection } from '@vendure/admin-ui/core';
+
 export default [
     addNavMenuSection({
         id: 'reports',

+ 12 - 1
docs/docs/reference/admin-ui-api/nav-menu/nav-menu-section.md

@@ -60,7 +60,18 @@ interface NavMenuSection {
 
 <MemberInfo kind="property" type={`string | ((userPermissions: string[]) =&#62; boolean)`}   />
 
-Control the display of this item based on the user permissions.
+Control the display of this item based on the user permissions. Note: if you attempt to pass a
+<a href='/reference/typescript-api/auth/permission-definition#permissiondefinition'>PermissionDefinition</a> object, you will get a compilation error. Instead, pass the plain
+string version. For example, if the permission is defined as:
+```ts
+export const MyPermission = new PermissionDefinition('ProductReview');
+```
+then the generated permission strings will be:
+
+- `CreateProductReview`
+- `ReadProductReview`
+- `UpdateProductReview`
+- `DeleteProductReview`
 ### collapsible
 
 <MemberInfo kind="property" type={`boolean`}   />

+ 1 - 1
docs/docs/reference/admin-ui-api/react-extensions/register-react-data-table-component.md

@@ -42,7 +42,7 @@ export default [
         tableId: 'product-list',
         columnId: 'slug',
         props: {
-        foo: 'bar',
+          foo: 'bar',
         },
     }),
 ];

+ 3 - 3
docs/docs/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-plugin.md

@@ -32,13 +32,13 @@ in processing jobs.
 
 ## Installation
 
-`yarn add @vendure/job-queue-plugin bullmq@1`
+`yarn add @vendure/job-queue-plugin bullmq`
 
 or
 
-`npm install @vendure/job-queue-plugin bullmq@1`
+`npm install @vendure/job-queue-plugin bullmq`
 
-**Note:** The v1.x version of this plugin is designed to work with bullmq v1.x.
+**Note:** The v1.x version of this plugin is designed to work with bullmq v1.x, etc.
 
 *Example*
 

+ 131 - 0
docs/docs/reference/core-plugins/sentry-plugin/index.md

@@ -0,0 +1,131 @@
+---
+title: "SentryPlugin"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## SentryPlugin
+
+<GenerationInfo sourceFile="packages/sentry-plugin/src/sentry-plugin.ts" sourceLine="109" packageName="@vendure/sentry-plugin" />
+
+This plugin integrates the [Sentry](https://sentry.io) error tracking & performance monitoring
+service with your Vendure server. In addition to capturing errors, it also provides built-in
+support for [tracing](https://docs.sentry.io/product/sentry-basics/concepts/tracing/) as well as
+enriching your Sentry events with additional context about the request.
+
+## Pre-requisites
+
+This plugin depends on access to Sentry, which can be self-hosted or used as a cloud service.
+
+If using the hosted SaaS option, you must have a Sentry account and a project set up ([sign up here](https://sentry.io/signup/)). When setting up your project,
+select the "Node.js" platform and no framework.
+
+Once set up, you will be given a [Data Source Name (DSN)](https://docs.sentry.io/product/sentry-basics/concepts/dsn-explainer/)
+which you will need to provide to the plugin.
+
+## Installation
+
+Install this plugin as well as the `@sentry/node` package:
+
+```sh
+npm install --save @vendure/sentry-plugin @sentry/node
+```
+
+## Configuration
+
+Before using the plugin, you must configure it with the DSN provided by Sentry:
+
+```ts
+import { VendureConfig } from '@vendure/core';
+import { SentryPlugin } from '@vendure/sentry-plugin';
+
+export const config: VendureConfig = {
+    // ...
+    plugins: [
+        // ...
+        // highlight-start
+        SentryPlugin.init({
+            dsn: process.env.SENTRY_DSN,
+            // Optional configuration
+            includeErrorTestMutation: true,
+            enableTracing: true,
+            // you can also pass in any of the options from @sentry/node
+            // for instance:
+            tracesSampleRate: 1.0,
+        }),
+        // highlight-end
+    ],
+};
+```
+
+## Tracing
+
+This plugin includes built-in support for [tracing](https://docs.sentry.io/product/sentry-basics/concepts/tracing/), which allows you to see the performance of your
+GraphQL resolvers in the Sentry dashboard. To enable tracing, set the `enableTracing` option to `true` as shown above.
+
+## Instrumenting your own code
+
+You may want to add your own custom spans to your code. To do so, you can use the `Sentry` object
+just as you would in any Node application. For example:
+
+```ts
+import * as Sentry from "@sentry/node";
+
+export class MyService {
+    async myMethod() {
+         Sentry.setContext('My Custom Context,{
+             key: 'value',
+         });
+    }
+}
+```
+
+## Error test mutation
+
+To test whether your Sentry configuration is working correctly, you can set the `includeErrorTestMutation` option to `true`. This will add a mutation to the Admin API
+which will throw an error of the type specified in the `errorType` argument. For example:
+
+```graphql
+mutation CreateTestError {
+    createTestError(errorType: DATABASE_ERROR)
+}
+```
+
+You should then be able to see the error in your Sentry dashboard (it may take a couple of minutes to appear).
+
+```ts title="Signature"
+class SentryPlugin implements NestModule {
+    static options: SentryPluginOptions = {} as any;
+    configure(consumer: MiddlewareConsumer) => any;
+    init(options: SentryPluginOptions) => ;
+}
+```
+* Implements: <code>NestModule</code>
+
+
+
+<div className="members-wrapper">
+
+### options
+
+<MemberInfo kind="property" type={`<a href='/reference/core-plugins/sentry-plugin/sentry-plugin-options#sentrypluginoptions'>SentryPluginOptions</a>`}   />
+
+
+### configure
+
+<MemberInfo kind="method" type={`(consumer: MiddlewareConsumer) => any`}   />
+
+
+### init
+
+<MemberInfo kind="method" type={`(options: <a href='/reference/core-plugins/sentry-plugin/sentry-plugin-options#sentrypluginoptions'>SentryPluginOptions</a>) => `}   />
+
+
+
+
+</div>

+ 48 - 0
docs/docs/reference/core-plugins/sentry-plugin/sentry-plugin-options.md

@@ -0,0 +1,48 @@
+---
+title: "SentryPluginOptions"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## SentryPluginOptions
+
+<GenerationInfo sourceFile="packages/sentry-plugin/src/types.ts" sourceLine="12" packageName="@vendure/sentry-plugin" />
+
+Configuration options for the <a href='/reference/core-plugins/sentry-plugin/#sentryplugin'>SentryPlugin</a>.
+
+```ts title="Signature"
+interface SentryPluginOptions extends NodeOptions {
+    dsn: string;
+    enableTracing?: boolean;
+    includeErrorTestMutation?: boolean;
+}
+```
+* Extends: <code>NodeOptions</code>
+
+
+
+<div className="members-wrapper">
+
+### dsn
+
+<MemberInfo kind="property" type={`string`}   />
+
+The [Data Source Name](https://docs.sentry.io/product/sentry-basics/concepts/dsn-explainer/) for your Sentry instance.
+### enableTracing
+
+<MemberInfo kind="property" type={`boolean`}   />
+
+
+### includeErrorTestMutation
+
+<MemberInfo kind="property" type={`boolean`}   />
+
+
+
+
+</div>

+ 281 - 0
docs/docs/reference/core-plugins/stellate-plugin/index.md

@@ -0,0 +1,281 @@
+---
+title: "StellatePlugin"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## StellatePlugin
+
+<GenerationInfo sourceFile="packages/stellate-plugin/src/stellate-plugin.ts" sourceLine="246" packageName="@vendure/stellate-plugin" since="2.1.5" />
+
+A plugin to integrate the [Stellate](https://stellate.co/) GraphQL caching service with your Vendure server.
+The main purpose of this plugin is to ensure that cached data gets correctly purged in
+response to events inside Vendure. For example, changes to a Product's description should
+purge any associated record for that Product in Stellate's cache.
+
+## Pre-requisites
+
+You will first need to [set up a free Stellate account](https://stellate.co/signup).
+
+You will also need to generate an **API token** for the Stellate Purging API. For instructions on how to generate the token,
+see the [Stellate Purging API docs](https://docs.stellate.co/docs/purging-api#authentication).
+
+## Installation
+
+```
+npm install @vendure/stellate-plugin
+```
+
+## Configuration
+
+The plugin is configured via the `StellatePlugin.init()` method. This method accepts an options object
+which defines the Stellate service name and API token, as well as an array of <a href='/reference/core-plugins/stellate-plugin/purge-rule#purgerule'>PurgeRule</a>s which
+define how the plugin will respond to Vendure events in order to trigger calls to the
+Stellate [Purging API](https://stellate.co/docs/graphql-edge-cache/purging-api).
+
+*Example*
+
+```ts
+import { StellatePlugin, defaultPurgeRules } from '@vendure/stellate-plugin';
+import { VendureConfig } from '@vendure/core';
+
+export const config: VendureConfig = {
+   // ...
+   plugins: [
+       StellatePlugin.init({
+           // The Stellate service name, i.e. `<serviceName>.stellate.sh`
+           serviceName: 'my-service',
+           // The API token for the Stellate Purging API. See the "pre-requisites" section above.
+           apiToken: process.env.STELLATE_PURGE_API_TOKEN,
+           debugMode: !isProd || process.env.STELLATE_DEBUG_MODE ? true : false,
+           debugLogging: process.env.STELLATE_DEBUG_MODE ? true : false,
+           purgeRules: [
+               ...defaultPurgeRules,
+               // custom purge rules can be added here
+           ],
+       }),
+   ],
+};
+```
+
+In your Stellate dashboard, you can use the following configuration example as a sensible default for a
+Vendure application:
+
+*Example*
+
+```ts
+import { Config } from "stellate";
+
+const config: Config = {
+  config: {
+    name: "my-vendure-server",
+    originUrl: "https://my-vendure-server.com/shop-api",
+    ignoreOriginCacheControl: true,
+    passThroughOnly: false,
+    scopes: {
+      SESSION_BOUND: "header:authorization|cookie:session",
+    },
+    headers: {
+      "access-control-expose-headers": "vendure-auth-token",
+    },
+    rootTypeNames: {
+      query: "Query",
+      mutation: "Mutation",
+    },
+    keyFields: {
+      types: {
+        SearchResult: ["productId"],
+        SearchResponseCacheIdentifier: ["collectionSlug"],
+      },
+    },
+    rules: [
+      {
+        types: [
+          "Product",
+          "Collection",
+          "ProductVariant",
+          "SearchResponse",
+        ],
+        maxAge: 900,
+        swr: 900,
+        description: "Cache Products & Collections",
+      },
+      {
+        types: ["Channel"],
+        maxAge: 9000,
+        swr: 9000,
+        description: "Cache active channel",
+      },
+      {
+        types: ["Order", "Customer", "User"],
+        maxAge: 0,
+        swr: 0,
+        description: "Do not cache user data",
+      },
+    ],
+  },
+};
+export default config;
+```
+
+## Storefront setup
+
+In your storefront, you should point your GraphQL client to the Stellate GraphQL API endpoint, which is
+`https://<service-name>.stellate.sh`.
+
+Wherever you are using the `search` query (typically in product listing & search pages), you should also add the
+`cacheIdentifier` field to the query. This will ensure that the Stellate cache is correctly purged when
+a Product or Collection is updated.
+
+*Example*
+
+```ts
+import { graphql } from '../generated/gql';
+
+export const searchProductsDocument = graphql(`
+    query SearchProducts($input: SearchInput!) {
+        search(input: $input) {
+            // highlight-start
+            cacheIdentifier {
+                collectionSlug
+            }
+            // highlight-end
+            items {
+               # ...
+            }
+        }
+    }
+}`);
+```
+
+## Custom PurgeRules
+
+The configuration above only accounts for caching of some of the built-in Vendure entity types. If you have
+custom entity types, you may well want to add them to the Stellate cache. In this case, you'll also need a way to
+purge those entities from the cache when they are updated. This is where the <a href='/reference/core-plugins/stellate-plugin/purge-rule#purgerule'>PurgeRule</a> comes in.
+
+Let's imagine that you have built a simple CMS plugin for Vendure which exposes an `Article` entity in your Shop API, and
+you have added this to your Stellate configuration:
+
+*Example*
+
+```ts
+import { Config } from "stellate";
+
+const config: Config = {
+    config: {
+        // ...
+        rules: [
+            // ...
+            {
+                types: ["Article"],
+                maxAge: 900,
+                swr: 900,
+                description: "Cache Articles",
+            },
+        ],
+    },
+    // ...
+};
+export default config;
+```
+
+You can then add a custom <a href='/reference/core-plugins/stellate-plugin/purge-rule#purgerule'>PurgeRule</a> to the StellatePlugin configuration:
+
+*Example*
+
+```ts
+import { StellatePlugin, defaultPurgeRules } from "@vendure/stellate-plugin";
+import { VendureConfig } from "@vendure/core";
+import { ArticleEvent } from "./plugins/cms/events/article-event";
+
+export const config: VendureConfig = {
+    // ...
+    plugins: [
+        StellatePlugin.init({
+            // ...
+            purgeRules: [
+                ...defaultPurgeRules,
+                new PurgeRule({
+                    eventType: ArticleEvent,
+                    handler: async ({ events, stellateService }) => {
+                        const articleIds = events.map((e) => e.article.id);
+                        stellateService.purge("Article", articleIds);
+                    },
+                }),
+            ],
+        }),
+    ],
+};
+```
+
+## DevMode & Debug Logging
+
+In development, you can set `devMode: true`, which will prevent any calls being made to the Stellate Purging API.
+
+If you want to log the calls that _would_ be made to the Stellate Purge API when in devMode, you can set `debugLogging: true`.
+Note that debugLogging generates a lot of debug-level logging, so it is recommended to only enable this when needed.
+
+*Example*
+
+```ts
+import { StellatePlugin, defaultPurgeRules } from '@vendure/stellate-plugin';
+import { VendureConfig } from '@vendure/core';
+
+export const config: VendureConfig = {
+   // ...
+   plugins: [
+       StellatePlugin.init({
+           // ...
+           devMode: !process.env.PRODUCTION,
+           debugLogging: process.env.STELLATE_DEBUG_MODE ? true : false,
+           purgeRules: [
+               ...defaultPurgeRules,
+           ],
+       }),
+   ],
+};
+```
+
+```ts title="Signature"
+class StellatePlugin implements OnApplicationBootstrap {
+    static options: StellatePluginOptions;
+    init(options: StellatePluginOptions) => ;
+    constructor(options: StellatePluginOptions, eventBus: EventBus, stellateService: StellateService, moduleRef: ModuleRef)
+    onApplicationBootstrap() => ;
+}
+```
+* Implements: <code>OnApplicationBootstrap</code>
+
+
+
+<div className="members-wrapper">
+
+### options
+
+<MemberInfo kind="property" type={`<a href='/reference/core-plugins/stellate-plugin/stellate-plugin-options#stellatepluginoptions'>StellatePluginOptions</a>`}   />
+
+
+### init
+
+<MemberInfo kind="method" type={`(options: <a href='/reference/core-plugins/stellate-plugin/stellate-plugin-options#stellatepluginoptions'>StellatePluginOptions</a>) => `}   />
+
+
+### constructor
+
+<MemberInfo kind="method" type={`(options: <a href='/reference/core-plugins/stellate-plugin/stellate-plugin-options#stellatepluginoptions'>StellatePluginOptions</a>, eventBus: <a href='/reference/typescript-api/events/event-bus#eventbus'>EventBus</a>, stellateService: <a href='/reference/core-plugins/stellate-plugin/stellate-service#stellateservice'>StellateService</a>, moduleRef: ModuleRef) => StellatePlugin`}   />
+
+
+### onApplicationBootstrap
+
+<MemberInfo kind="method" type={`() => `}   />
+
+
+
+
+</div>

+ 95 - 0
docs/docs/reference/core-plugins/stellate-plugin/purge-rule.md

@@ -0,0 +1,95 @@
+---
+title: "PurgeRule"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## PurgeRule
+
+<GenerationInfo sourceFile="packages/stellate-plugin/src/purge-rule.ts" sourceLine="49" packageName="@vendure/stellate-plugin" />
+
+Defines a rule that listens for a particular VendureEvent and uses that to
+make calls to the [Stellate Purging API](https://docs.stellate.co/docs/purging-api) via
+the provided <a href='/reference/core-plugins/stellate-plugin/stellate-service#stellateservice'>StellateService</a> instance.
+
+```ts title="Signature"
+class PurgeRule<Event extends VendureEvent = VendureEvent> {
+    eventType: Type<Event>
+    bufferTimeMs: number | undefined
+    handle(handlerArgs: { events: Event[]; stellateService: StellateService; injector: Injector }) => ;
+    constructor(config: PurgeRuleConfig<Event>)
+}
+```
+
+<div className="members-wrapper">
+
+### eventType
+
+<MemberInfo kind="property" type={`Type&#60;Event&#62;`}   />
+
+
+### bufferTimeMs
+
+<MemberInfo kind="property" type={`number | undefined`}   />
+
+
+### handle
+
+<MemberInfo kind="method" type={`(handlerArgs: { events: Event[]; stellateService: <a href='/reference/core-plugins/stellate-plugin/stellate-service#stellateservice'>StellateService</a>; injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a> }) => `}   />
+
+
+### constructor
+
+<MemberInfo kind="method" type={`(config: <a href='/reference/core-plugins/stellate-plugin/purge-rule#purgeruleconfig'>PurgeRuleConfig</a>&#60;Event&#62;) => PurgeRule`}   />
+
+
+
+
+</div>
+
+
+## PurgeRuleConfig
+
+<GenerationInfo sourceFile="packages/stellate-plugin/src/purge-rule.ts" sourceLine="13" packageName="@vendure/stellate-plugin" />
+
+Configures a <a href='/reference/core-plugins/stellate-plugin/purge-rule#purgerule'>PurgeRule</a>.
+
+```ts title="Signature"
+interface PurgeRuleConfig<Event extends VendureEvent> {
+    eventType: Type<Event>;
+    bufferTime?: number;
+    handler: (handlerArgs: {
+        events: Event[];
+        stellateService: StellateService;
+        injector: Injector;
+    }) => void | Promise<void>;
+}
+```
+
+<div className="members-wrapper">
+
+### eventType
+
+<MemberInfo kind="property" type={`Type&#60;Event&#62;`}   />
+
+Specifies which VendureEvent will trigger this purge rule.
+### bufferTime
+
+<MemberInfo kind="property" type={`number`} default="5000"   />
+
+How long to buffer events for in milliseconds before executing the handler. This allows
+us to efficiently batch calls to the Stellate Purge API.
+### handler
+
+<MemberInfo kind="property" type={`(handlerArgs: {
         events: Event[];
         stellateService: <a href='/reference/core-plugins/stellate-plugin/stellate-service#stellateservice'>StellateService</a>;
         injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>;
     }) =&#62; void | Promise&#60;void&#62;`}   />
+
+The function to invoke when the specified event is published. This function should use the
+<a href='/reference/core-plugins/stellate-plugin/stellate-service#stellateservice'>StellateService</a> instance to call the Stellate Purge API.
+
+
+</div>

+ 69 - 0
docs/docs/reference/core-plugins/stellate-plugin/stellate-plugin-options.md

@@ -0,0 +1,69 @@
+---
+title: "StellatePluginOptions"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## StellatePluginOptions
+
+<GenerationInfo sourceFile="packages/stellate-plugin/src/types.ts" sourceLine="9" packageName="@vendure/stellate-plugin" />
+
+Configuration options for the StellatePlugin.
+
+```ts title="Signature"
+interface StellatePluginOptions {
+    serviceName: string;
+    apiToken: string;
+    purgeRules: PurgeRule[];
+    defaultBufferTimeMs?: number;
+    devMode?: boolean;
+    debugLogging?: boolean;
+}
+```
+
+<div className="members-wrapper">
+
+### serviceName
+
+<MemberInfo kind="property" type={`string`}   />
+
+The Stellate service name, i.e. `<service-name>.stellate.sh`
+### apiToken
+
+<MemberInfo kind="property" type={`string`}   />
+
+The Stellate Purging API token. For instructions on how to generate the token,
+see the [Stellate docs](https://docs.stellate.co/docs/purging-api#authentication)
+### purgeRules
+
+<MemberInfo kind="property" type={`<a href='/reference/core-plugins/stellate-plugin/purge-rule#purgerule'>PurgeRule</a>[]`}   />
+
+An array of <a href='/reference/core-plugins/stellate-plugin/purge-rule#purgerule'>PurgeRule</a> instances which are used to define how the plugin will
+respond to Vendure events in order to trigger calls to the Stellate Purging API.
+### defaultBufferTimeMs
+
+<MemberInfo kind="property" type={`number`} default="2000"   />
+
+When events are published, the PurgeRules will buffer those events in order to efficiently
+batch requests to the Stellate Purging API. You may wish to change the default, e.g. if you are
+running in a serverless environment and cannot introduce pauses after the main request has completed.
+### devMode
+
+<MemberInfo kind="property" type={`boolean`} default="false"   />
+
+When set to `true`, calls will not be made to the Stellate Purge API.
+### debugLogging
+
+<MemberInfo kind="property" type={`boolean`} default="false"   />
+
+If set to true, the plugin will log the calls that would be made
+to the Stellate Purge API. Note, this generates a
+lot of debug-level logging.
+
+
+</div>

+ 75 - 0
docs/docs/reference/core-plugins/stellate-plugin/stellate-service.md

@@ -0,0 +1,75 @@
+---
+title: "StellateService"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## StellateService
+
+<GenerationInfo sourceFile="packages/stellate-plugin/src/service/stellate.service.ts" sourceLine="23" packageName="@vendure/stellate-plugin" />
+
+The StellateService is used to purge the Stellate cache when certain events occur.
+
+```ts title="Signature"
+class StellateService {
+    constructor(options: StellatePluginOptions)
+    purgeProducts(products: Product[]) => ;
+    purgeProductVariants(productVariants: ProductVariant[]) => ;
+    purgeSearchResults(items: Array<ProductVariant | Product>) => ;
+    purgeAllOfType(type: CachedType) => ;
+    purgeCollections(collections: Collection[]) => ;
+    purgeSearchResponseCacheIdentifiers(collections: Collection[]) => ;
+    purge(type: CachedType, keys?: ID[], keyName:  = 'id') => ;
+}
+```
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(options: <a href='/reference/core-plugins/stellate-plugin/stellate-plugin-options#stellatepluginoptions'>StellatePluginOptions</a>) => StellateService`}   />
+
+
+### purgeProducts
+
+<MemberInfo kind="method" type={`(products: <a href='/reference/typescript-api/entities/product#product'>Product</a>[]) => `}   />
+
+Purges the cache for the given Products.
+### purgeProductVariants
+
+<MemberInfo kind="method" type={`(productVariants: <a href='/reference/typescript-api/entities/product-variant#productvariant'>ProductVariant</a>[]) => `}   />
+
+Purges the cache for the given ProductVariants.
+### purgeSearchResults
+
+<MemberInfo kind="method" type={`(items: Array&#60;<a href='/reference/typescript-api/entities/product-variant#productvariant'>ProductVariant</a> | <a href='/reference/typescript-api/entities/product#product'>Product</a>&#62;) => `}   />
+
+Purges the cache for SearchResults which contain the given Products or ProductVariants.
+### purgeAllOfType
+
+<MemberInfo kind="method" type={`(type: CachedType) => `}   />
+
+Purges the entire cache for the given type.
+### purgeCollections
+
+<MemberInfo kind="method" type={`(collections: <a href='/reference/typescript-api/entities/collection#collection'>Collection</a>[]) => `}   />
+
+Purges the cache for the given Collections.
+### purgeSearchResponseCacheIdentifiers
+
+<MemberInfo kind="method" type={`(collections: <a href='/reference/typescript-api/entities/collection#collection'>Collection</a>[]) => `}   />
+
+Purges the cache of SearchResults for the given Collections based on slug.
+### purge
+
+<MemberInfo kind="method" type={`(type: CachedType, keys?: <a href='/reference/typescript-api/common/id#id'>ID</a>[], keyName:  = 'id') => `}   />
+
+Purges the cache for the given type and keys.
+
+
+</div>

+ 1 - 3
docs/docs/reference/graphql-api/admin/enums.md

@@ -1,8 +1,6 @@
 ---
 title: "Enums"
-weight: 5
-date: 2023-07-21T15:33:44.314Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->

+ 13 - 3
docs/docs/reference/graphql-api/admin/input-types.md

@@ -1,8 +1,6 @@
 ---
 title: "Input Objects"
-weight: 4
-date: 2023-07-21T15:33:44.314Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
@@ -1258,6 +1256,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">perCustomerUsageLimit: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
 
+<div class="graphql-code-line ">usageLimit: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
+
 <div class="graphql-code-line ">conditions: [<a href="/reference/graphql-api/admin/input-types#configurableoperationinput">ConfigurableOperationInput</a>!]!</div>
 
 <div class="graphql-code-line ">actions: [<a href="/reference/graphql-api/admin/input-types#configurableoperationinput">ConfigurableOperationInput</a>!]!</div>
@@ -1822,6 +1822,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">languageCode: <a href="/reference/graphql-api/admin/input-types#stringoperators">StringOperators</a></div>
 
+<div class="graphql-code-line ">facetId: <a href="/reference/graphql-api/admin/input-types#idoperators">IDOperators</a></div>
+
 <div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/input-types#stringoperators">StringOperators</a></div>
 
 <div class="graphql-code-line ">code: <a href="/reference/graphql-api/admin/input-types#stringoperators">StringOperators</a></div>
@@ -1877,6 +1879,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">updatedAt: <a href="/reference/graphql-api/admin/enums#sortorder">SortOrder</a></div>
 
+<div class="graphql-code-line ">facetId: <a href="/reference/graphql-api/admin/enums#sortorder">SortOrder</a></div>
+
 <div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/enums#sortorder">SortOrder</a></div>
 
 <div class="graphql-code-line ">code: <a href="/reference/graphql-api/admin/enums#sortorder">SortOrder</a></div>
@@ -2860,6 +2864,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">perCustomerUsageLimit: <a href="/reference/graphql-api/admin/input-types#numberoperators">NumberOperators</a></div>
 
+<div class="graphql-code-line ">usageLimit: <a href="/reference/graphql-api/admin/input-types#numberoperators">NumberOperators</a></div>
+
 <div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/input-types#stringoperators">StringOperators</a></div>
 
 <div class="graphql-code-line ">description: <a href="/reference/graphql-api/admin/input-types#stringoperators">StringOperators</a></div>
@@ -2925,6 +2931,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">perCustomerUsageLimit: <a href="/reference/graphql-api/admin/enums#sortorder">SortOrder</a></div>
 
+<div class="graphql-code-line ">usageLimit: <a href="/reference/graphql-api/admin/enums#sortorder">SortOrder</a></div>
+
 <div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/enums#sortorder">SortOrder</a></div>
 
 <div class="graphql-code-line ">description: <a href="/reference/graphql-api/admin/enums#sortorder">SortOrder</a></div>
@@ -4469,6 +4477,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">perCustomerUsageLimit: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
 
+<div class="graphql-code-line ">usageLimit: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
+
 <div class="graphql-code-line ">conditions: [<a href="/reference/graphql-api/admin/input-types#configurableoperationinput">ConfigurableOperationInput</a>!]</div>
 
 <div class="graphql-code-line ">actions: [<a href="/reference/graphql-api/admin/input-types#configurableoperationinput">ConfigurableOperationInput</a>!]</div>

+ 1 - 3
docs/docs/reference/graphql-api/admin/mutations.md

@@ -1,8 +1,6 @@
 ---
 title: "Mutations"
-weight: 2
-date: 2023-07-21T15:33:44.314Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->

+ 14 - 4
docs/docs/reference/graphql-api/admin/object-types.md

@@ -1,8 +1,6 @@
 ---
 title: "Types"
-weight: 3
-date: 2023-07-21T15:33:44.314Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
@@ -1126,6 +1124,11 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">values: [<a href="/reference/graphql-api/admin/object-types#facetvalue">FacetValue</a>!]!</div>
 
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">Returns a paginated, sortable, filterable list of the Facet's values. Added in v2.1.0.</div>
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line ">valueList(options: <a href="/reference/graphql-api/admin/input-types#facetvaluelistoptions">FacetValueListOptions</a>): <a href="/reference/graphql-api/admin/object-types#facetvaluelist">FacetValueList</a>!</div>
+
 <div class="graphql-code-line ">translations: [<a href="/reference/graphql-api/admin/object-types#facettranslation">FacetTranslation</a>!]!</div>
 
 <div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
@@ -1200,6 +1203,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">facet: <a href="/reference/graphql-api/admin/object-types#facet">Facet</a>!</div>
 
+<div class="graphql-code-line ">facetId: <a href="/reference/graphql-api/admin/object-types#id">ID</a>!</div>
+
 <div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
 
 <div class="graphql-code-line ">code: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
@@ -2265,6 +2270,9 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line comment">"""</div>
 <div class="graphql-code-line ">proratedUnitPriceWithTax: <a href="/reference/graphql-api/admin/object-types#money">Money</a>!</div>
 
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">The quantity of items purchased</div>
+<div class="graphql-code-line comment">"""</div>
 <div class="graphql-code-line ">quantity: <a href="/reference/graphql-api/admin/object-types#int">Int</a>!</div>
 
 <div class="graphql-code-line comment">"""</div>
@@ -2982,7 +2990,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
  &#123;</div>
 <div class="graphql-code-line ">currencyCode: <a href="/reference/graphql-api/admin/enums#currencycode">CurrencyCode</a>!</div>
 
-<div class="graphql-code-line ">price: <a href="/reference/graphql-api/admin/object-types#int">Int</a>!</div>
+<div class="graphql-code-line ">price: <a href="/reference/graphql-api/admin/object-types#money">Money</a>!</div>
 
 
 <div class="graphql-code-line top-level">&#125;</div>
@@ -3026,6 +3034,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">perCustomerUsageLimit: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
 
+<div class="graphql-code-line ">usageLimit: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
+
 <div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
 
 <div class="graphql-code-line ">description: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>

+ 1 - 3
docs/docs/reference/graphql-api/admin/queries.md

@@ -1,8 +1,6 @@
 ---
 title: "Queries"
-weight: 1
-date: 2023-07-21T15:33:44.314Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->

+ 1 - 3
docs/docs/reference/graphql-api/shop/enums.md

@@ -1,8 +1,6 @@
 ---
 title: "Enums"
-weight: 5
-date: 2023-07-21T15:33:42.677Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->

+ 82 - 3
docs/docs/reference/graphql-api/shop/input-types.md

@@ -1,8 +1,6 @@
 ---
 title: "Input Objects"
-weight: 4
-date: 2023-07-21T15:33:42.677Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
@@ -473,6 +471,87 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">or: [<a href="/reference/graphql-api/shop/object-types#id">ID</a>!]</div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+
+</div>
+
+## FacetValueFilterParameter
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">input <span class="graphql-code-identifier">FacetValueFilterParameter</span>
+ &#123;</div>
+<div class="graphql-code-line ">id: <a href="/reference/graphql-api/shop/input-types#idoperators">IDOperators</a></div>
+
+<div class="graphql-code-line ">createdAt: <a href="/reference/graphql-api/shop/input-types#dateoperators">DateOperators</a></div>
+
+<div class="graphql-code-line ">updatedAt: <a href="/reference/graphql-api/shop/input-types#dateoperators">DateOperators</a></div>
+
+<div class="graphql-code-line ">languageCode: <a href="/reference/graphql-api/shop/input-types#stringoperators">StringOperators</a></div>
+
+<div class="graphql-code-line ">facetId: <a href="/reference/graphql-api/shop/input-types#idoperators">IDOperators</a></div>
+
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/input-types#stringoperators">StringOperators</a></div>
+
+<div class="graphql-code-line ">code: <a href="/reference/graphql-api/shop/input-types#stringoperators">StringOperators</a></div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+
+</div>
+
+## FacetValueListOptions
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">input <span class="graphql-code-identifier">FacetValueListOptions</span>
+ &#123;</div>
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">Skips the first n results, for use in pagination</div>
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line ">skip: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
+
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">Takes n results, for use in pagination</div>
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line ">take: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
+
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">Specifies which properties to sort the results by</div>
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line ">sort: <a href="/reference/graphql-api/shop/input-types#facetvaluesortparameter">FacetValueSortParameter</a></div>
+
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">Allows the results to be filtered</div>
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line ">filter: <a href="/reference/graphql-api/shop/input-types#facetvaluefilterparameter">FacetValueFilterParameter</a></div>
+
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND.</div>
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line ">filterOperator: <a href="/reference/graphql-api/shop/enums#logicaloperator">LogicalOperator</a></div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+
+</div>
+
+## FacetValueSortParameter
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">input <span class="graphql-code-identifier">FacetValueSortParameter</span>
+ &#123;</div>
+<div class="graphql-code-line ">id: <a href="/reference/graphql-api/shop/enums#sortorder">SortOrder</a></div>
+
+<div class="graphql-code-line ">createdAt: <a href="/reference/graphql-api/shop/enums#sortorder">SortOrder</a></div>
+
+<div class="graphql-code-line ">updatedAt: <a href="/reference/graphql-api/shop/enums#sortorder">SortOrder</a></div>
+
+<div class="graphql-code-line ">facetId: <a href="/reference/graphql-api/shop/enums#sortorder">SortOrder</a></div>
+
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/enums#sortorder">SortOrder</a></div>
+
+<div class="graphql-code-line ">code: <a href="/reference/graphql-api/shop/enums#sortorder">SortOrder</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 
 </div>

+ 1 - 3
docs/docs/reference/graphql-api/shop/mutations.md

@@ -1,8 +1,6 @@
 ---
 title: "Mutations"
-weight: 2
-date: 2023-07-21T15:33:42.677Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->

+ 26 - 3
docs/docs/reference/graphql-api/shop/object-types.md

@@ -1,8 +1,6 @@
 ---
 title: "Types"
-weight: 3
-date: 2023-07-21T15:33:42.677Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
@@ -795,6 +793,11 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">values: [<a href="/reference/graphql-api/shop/object-types#facetvalue">FacetValue</a>!]!</div>
 
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">Returns a paginated, sortable, filterable list of the Facet's values. Added in v2.1.0.</div>
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line ">valueList(options: <a href="/reference/graphql-api/shop/input-types#facetvaluelistoptions">FacetValueListOptions</a>): <a href="/reference/graphql-api/shop/object-types#facetvaluelist">FacetValueList</a>!</div>
+
 <div class="graphql-code-line ">translations: [<a href="/reference/graphql-api/shop/object-types#facettranslation">FacetTranslation</a>!]!</div>
 
 <div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
@@ -850,6 +853,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">facet: <a href="/reference/graphql-api/shop/object-types#facet">Facet</a>!</div>
 
+<div class="graphql-code-line ">facetId: <a href="/reference/graphql-api/shop/object-types#id">ID</a>!</div>
+
 <div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
 
 <div class="graphql-code-line ">code: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
@@ -859,6 +864,19 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## FacetValueList
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">FacetValueList</span>
+ &#123;</div>
+<div class="graphql-code-line ">items: [<a href="/reference/graphql-api/shop/object-types#facetvalue">FacetValue</a>!]!</div>
+
+<div class="graphql-code-line ">totalItems: <a href="/reference/graphql-api/shop/object-types#int">Int</a>!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -1622,6 +1640,9 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line comment">"""</div>
 <div class="graphql-code-line ">proratedUnitPriceWithTax: <a href="/reference/graphql-api/shop/object-types#money">Money</a>!</div>
 
+<div class="graphql-code-line comment">"""</div>
+<div class="graphql-code-line comment">The quantity of items purchased</div>
+<div class="graphql-code-line comment">"""</div>
 <div class="graphql-code-line ">quantity: <a href="/reference/graphql-api/shop/object-types#int">Int</a>!</div>
 
 <div class="graphql-code-line comment">"""</div>
@@ -2288,6 +2309,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">perCustomerUsageLimit: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
 
+<div class="graphql-code-line ">usageLimit: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
+
 <div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
 
 <div class="graphql-code-line ">description: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>

+ 1 - 3
docs/docs/reference/graphql-api/shop/queries.md

@@ -1,8 +1,6 @@
 ---
 title: "Queries"
-weight: 1
-date: 2023-07-21T15:33:42.677Z
-showtoc: true
+isDefaultIndex: false
 generated: true
 ---
 <!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->

+ 1 - 1
docs/docs/reference/typescript-api/common/permission.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Permission
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="4210" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="4211" packageName="@vendure/common" />
 
 Permissions for administrators and customers. Used to control access to
 GraphQL resolvers via the <a href='/reference/typescript-api/request/allow-decorator#allow'>Allow</a> decorator.

+ 9 - 9
docs/docs/reference/typescript-api/configurable-operation-def/default-form-config-hash.md

@@ -29,15 +29,15 @@ type DefaultFormConfigHash = {
     'product-selector-form-input': Record<string, never>;
     'relation-form-input': Record<string, never>;
     'rich-text-form-input': Record<string, never>;
-    'select-form-input': {
-        options?: Array<{ value: string; label?: Array<Omit<LocalizedString, '__typename'>> }>;
+    'select-form-input': {
+        options?: Array<{ value: string; label?: Array<Omit<LocalizedString, '__typename'>> }>;
     };
     'text-form-input': { prefix?: string; suffix?: string };
-    'textarea-form-input': {
-        spellcheck?: boolean;
+    'textarea-form-input': {
+        spellcheck?: boolean;
     };
-    'product-multi-form-input': {
-        selectionMode?: 'product' | 'variant';
+    'product-multi-form-input': {
+        selectionMode?: 'product' | 'variant';
     };
     'combination-mode-form-input': Record<string, never>;
 }
@@ -107,7 +107,7 @@ type DefaultFormConfigHash = {
 
 ### 'select-form-input'
 
-<MemberInfo kind="property" type={`{
         options?: Array&#60;{ value: string; label?: Array&#60;Omit&#60;LocalizedString, '__typename'&#62;&#62; }&#62;;
     }`}   />
+<MemberInfo kind="property" type={`{         options?: Array&#60;{ value: string; label?: Array&#60;Omit&#60;LocalizedString, '__typename'&#62;&#62; }&#62;;     }`}   />
 
 
 ### 'text-form-input'
@@ -117,12 +117,12 @@ type DefaultFormConfigHash = {
 
 ### 'textarea-form-input'
 
-<MemberInfo kind="property" type={`{
         spellcheck?: boolean;
     }`}   />
+<MemberInfo kind="property" type={`{         spellcheck?: boolean;     }`}   />
 
 
 ### 'product-multi-form-input'
 
-<MemberInfo kind="property" type={`{
         selectionMode?: 'product' | 'variant';
     }`}   />
+<MemberInfo kind="property" type={`{         selectionMode?: 'product' | 'variant';     }`}   />
 
 
 ### 'combination-mode-form-input'

+ 1 - 1
docs/docs/reference/typescript-api/custom-fields/custom-field-type.md

@@ -21,7 +21,7 @@ Type         | DB type                               | GraphQL type
 string       | varchar                               | String
 localeString | varchar                               | String
 text         | longtext(m), text(p,s)                | String
-localText    | longtext(m), text(p,s)                | String
+localeText    | longtext(m), text(p,s)                | String
 int          | int                                   | Int
 float        | double precision                      | Float
 boolean      | tinyint (m), bool (p), boolean (s)    | Boolean

+ 34 - 0
docs/docs/reference/typescript-api/errors/error-result-union.md

@@ -0,0 +1,34 @@
+---
+title: "ErrorResultUnion"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## ErrorResultUnion
+
+<GenerationInfo sourceFile="packages/core/src/common/error/error-result.ts" sourceLine="44" packageName="@vendure/core" />
+
+Used to construct a TypeScript return type for a query or mutation which, in the GraphQL schema,
+returns a union type composed of a success result (e.g. Order) plus one or more ErrorResult
+types.
+
+Since the TypeScript entities do not correspond 1-to-1 with their GraphQL type counterparts,
+we use this type to substitute them.
+
+*Example*
+
+```ts
+type UpdateOrderItemsResult = Order | OrderModificationError | OrderLimitError | NegativeQuantityError;
+type T1 = ErrorResultUnion<UpdateOrderItemsResult, VendureEntityOrder>;
+// T1 = VendureEntityOrder | OrderModificationError | OrderLimitError | NegativeQuantityError;
+```
+
+```ts title="Signature"
+type ErrorResultUnion<T extends GraphQLErrorResult | U, E extends VendureEntity, U = any> = | JustErrorResults<T>
+    | E
+```

+ 44 - 0
docs/docs/reference/typescript-api/errors/is-graph-ql-error-result.md

@@ -0,0 +1,44 @@
+---
+title: "IsGraphQlErrorResult"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## isGraphQlErrorResult
+
+<GenerationInfo sourceFile="packages/core/src/common/error/error-result.ts" sourceLine="71" packageName="@vendure/core" />
+
+Returns true if the <a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a> is actually an ErrorResult type. This is useful when dealing with
+certain internal service method that return an ErrorResultUnion.
+
+*Example*
+
+```ts
+import { isGraphQlErrorResult } from '@vendure/core';
+
+// ...
+
+const transitionResult = await this.orderService.transitionToState(ctx, order.id, newState);
+if (isGraphQlErrorResult(transitionResult)) {
+    // The transition failed with an ErrorResult
+    throw transitionResult;
+} else {
+    // TypeScript will correctly infer the type of `transitionResult` to be `Order`
+    return transitionResult;
+}
+```
+
+```ts title="Signature"
+function isGraphQlErrorResult<T extends GraphQLErrorResult | U, U = any>(input: T): input is JustErrorResults<T>
+```
+Parameters
+
+### input
+
+<MemberInfo kind="parameter" type={`T`} />
+

+ 1 - 2
docs/docs/reference/typescript-api/events/vendure-entity-event.md

@@ -11,10 +11,9 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## VendureEntityEvent
 
-<GenerationInfo sourceFile="packages/core/src/event-bus/vendure-entity-event.ts" sourceLine="13" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/event-bus/vendure-entity-event.ts" sourceLine="12" packageName="@vendure/core" />
 
 The base class for all entity events used by the EventBus system.
-* For event type `'updated'` the entity is the one before applying the patch (if not documented otherwise).
 * For event type `'deleted'` the input will most likely be an `id: ID`
 
 ```ts title="Signature"

+ 1 - 1
docs/docs/reference/typescript-api/orders/default-guest-checkout-strategy.md

@@ -57,7 +57,7 @@ class DefaultGuestCheckoutStrategy implements GuestCheckoutStrategy {
 
 ### setCustomerForOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, order: <a href='/reference/typescript-api/entities/order#order'>Order</a>, input: CreateCustomerInput) => Promise&#60;ErrorResultUnion&#60;SetCustomerForOrderResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, order: <a href='/reference/typescript-api/entities/order#order'>Order</a>, input: CreateCustomerInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;SetCustomerForOrderResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
 
 
 

+ 1 - 1
docs/docs/reference/typescript-api/orders/guest-checkout-strategy.md

@@ -51,7 +51,7 @@ interface GuestCheckoutStrategy extends InjectableStrategy {
 
 ### setCustomerForOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, order: <a href='/reference/typescript-api/entities/order#order'>Order</a>, input: CreateCustomerInput) => | ErrorResultUnion&#60;SetCustomerForOrderResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;
         | Promise&#60;ErrorResultUnion&#60;SetCustomerForOrderResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, order: <a href='/reference/typescript-api/entities/order#order'>Order</a>, input: CreateCustomerInput) => | <a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;SetCustomerForOrderResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;
         | Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;SetCustomerForOrderResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
 
 This method is called when the `setCustomerForOrder` mutation is executed.
 It should return either a Customer object or an ErrorResult.

+ 1 - 1
docs/docs/reference/typescript-api/orders/order-process.md

@@ -196,7 +196,7 @@ Parameters
 
 ## defaultOrderProcess
 
-<GenerationInfo sourceFile="packages/core/src/config/order/default-order-process.ts" sourceLine="474" packageName="@vendure/core" since="2.0.0" />
+<GenerationInfo sourceFile="packages/core/src/config/order/default-order-process.ts" sourceLine="477" packageName="@vendure/core" since="2.0.0" />
 
 This is the built-in <a href='/reference/typescript-api/orders/order-process#orderprocess'>OrderProcess</a> that ships with Vendure. A customized version of this process
 can be created using the <a href='/reference/typescript-api/orders/order-process#configuredefaultorderprocess'>configureDefaultOrderProcess</a> function, which allows you to pass in an object

+ 1 - 1
docs/docs/reference/typescript-api/request/request-context-service.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## RequestContextService
 
-<GenerationInfo sourceFile="packages/core/src/service/helpers/request-context/request-context.service.ts" sourceLine="24" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/helpers/request-context/request-context.service.ts" sourceLine="25" packageName="@vendure/core" />
 
 Creates new <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a> instances.
 

+ 3 - 3
docs/docs/reference/typescript-api/services/channel-service.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ChannelService
 
-<GenerationInfo sourceFile="packages/core/src/service/services/channel.service.ts" sourceLine="53" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/services/channel.service.ts" sourceLine="54" packageName="@vendure/core" />
 
 Contains methods relating to <a href='/reference/typescript-api/entities/channel#channel'>Channel</a> entities.
 
@@ -90,12 +90,12 @@ Returns the default Channel.
 
 ### create
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: CreateChannelInput) => Promise&#60;ErrorResultUnion&#60;CreateChannelResult, <a href='/reference/typescript-api/entities/channel#channel'>Channel</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: CreateChannelInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;CreateChannelResult, <a href='/reference/typescript-api/entities/channel#channel'>Channel</a>&#62;&#62;`}   />
 
 
 ### update
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: UpdateChannelInput) => Promise&#60;ErrorResultUnion&#60;UpdateChannelResult, <a href='/reference/typescript-api/entities/channel#channel'>Channel</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: UpdateChannelInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;UpdateChannelResult, <a href='/reference/typescript-api/entities/channel#channel'>Channel</a>&#62;&#62;`}   />
 
 
 ### delete

+ 1 - 1
docs/docs/reference/typescript-api/services/collection-service.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CollectionService
 
-<GenerationInfo sourceFile="packages/core/src/service/services/collection.service.ts" sourceLine="65" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/services/collection.service.ts" sourceLine="66" packageName="@vendure/core" />
 
 Contains methods relating to <a href='/reference/typescript-api/entities/collection#collection'>Collection</a> entities.
 

+ 29 - 29
docs/docs/reference/typescript-api/services/customer-service.md

@@ -31,8 +31,8 @@ class CustomerService {
     refreshVerificationToken(ctx: RequestContext, emailAddress: string) => Promise<void>;
     verifyCustomerEmailAddress(ctx: RequestContext, verificationToken: string, password?: string) => Promise<ErrorResultUnion<VerifyCustomerAccountResult, Customer>>;
     requestPasswordReset(ctx: RequestContext, emailAddress: string) => Promise<void>;
-    resetPassword(ctx: RequestContext, passwordResetToken: string, password: string) => Promise<
-        User | PasswordResetTokenExpiredError | PasswordResetTokenInvalidError | PasswordValidationError
+    resetPassword(ctx: RequestContext, passwordResetToken: string, password: string) => Promise<
+        User | PasswordResetTokenExpiredError | PasswordResetTokenInvalidError | PasswordValidationError
     >;
     requestUpdateEmailAddress(ctx: RequestContext, userId: ID, newEmailAddress: string) => Promise<boolean | EmailAddressConflictError>;
     updateEmailAddress(ctx: RequestContext, token: string) => Promise<boolean | IdentifierChangeTokenInvalidError | IdentifierChangeTokenExpiredError>;
@@ -69,8 +69,8 @@ class CustomerService {
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, userId: <a href='/reference/typescript-api/common/id#id'>ID</a>, filterOnChannel:  = true) => Promise&#60;<a href='/reference/typescript-api/entities/customer#customer'>Customer</a> | undefined&#62;`}   />
 
-Returns the Customer entity associated with the given userId, if one exists.
-Setting `filterOnChannel` to `true` will limit the results to Customers which are assigned
+Returns the Customer entity associated with the given userId, if one exists.
+Setting `filterOnChannel` to `true` will limit the results to Customers which are assigned
 to the current active Channel only.
 ### findAddressesByCustomerId
 
@@ -84,15 +84,15 @@ Returns all <a href='/reference/typescript-api/entities/address#address'>Address
 Returns a list of all <a href='/reference/typescript-api/entities/customer-group#customergroup'>CustomerGroup</a> entities.
 ### create
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: CreateCustomerInput, password?: string) => Promise&#60;ErrorResultUnion&#60;CreateCustomerResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
-
-Creates a new Customer, including creation of a new User with the special `customer` Role.
-
-If the `password` argument is specified, the Customer will be immediately verified. If not,
-then an <a href='/reference/typescript-api/events/event-types#accountregistrationevent'>AccountRegistrationEvent</a> is published, so that the customer can have their
-email address verified and set their password in a later step using the `verifyCustomerEmailAddress()`
-method.
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: CreateCustomerInput, password?: string) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;CreateCustomerResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
 
+Creates a new Customer, including creation of a new User with the special `customer` Role.
+
+If the `password` argument is specified, the Customer will be immediately verified. If not,
+then an <a href='/reference/typescript-api/events/event-types#accountregistrationevent'>AccountRegistrationEvent</a> is published, so that the customer can have their
+email address verified and set their password in a later step using the `verifyCustomerEmailAddress()`
+method.
+
 This method is intended to be used in admin-created Customer flows.
 ### update
 
@@ -101,59 +101,59 @@ This method is intended to be used in admin-created Customer flows.
 
 ### update
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: UpdateCustomerInput) => Promise&#60;ErrorResultUnion&#60;UpdateCustomerResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: UpdateCustomerInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;UpdateCustomerResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
 
 
 ### update
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: UpdateCustomerInput | (UpdateCustomerShopInput &#38; { id: <a href='/reference/typescript-api/common/id#id'>ID</a> })) => Promise&#60;ErrorResultUnion&#60;UpdateCustomerResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: UpdateCustomerInput | (UpdateCustomerShopInput &#38; { id: <a href='/reference/typescript-api/common/id#id'>ID</a> })) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;UpdateCustomerResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
 
 
 ### registerCustomerAccount
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: RegisterCustomerInput) => Promise&#60;RegisterCustomerAccountResult | EmailAddressConflictError | PasswordValidationError&#62;`}   />
 
-Registers a new Customer account with the <a href='/reference/typescript-api/auth/native-authentication-strategy#nativeauthenticationstrategy'>NativeAuthenticationStrategy</a> and starts
-the email verification flow (unless <a href='/reference/typescript-api/auth/auth-options#authoptions'>AuthOptions</a> `requireVerification` is set to `false`)
-by publishing an <a href='/reference/typescript-api/events/event-types#accountregistrationevent'>AccountRegistrationEvent</a>.
-
+Registers a new Customer account with the <a href='/reference/typescript-api/auth/native-authentication-strategy#nativeauthenticationstrategy'>NativeAuthenticationStrategy</a> and starts
+the email verification flow (unless <a href='/reference/typescript-api/auth/auth-options#authoptions'>AuthOptions</a> `requireVerification` is set to `false`)
+by publishing an <a href='/reference/typescript-api/events/event-types#accountregistrationevent'>AccountRegistrationEvent</a>.
+
 This method is intended to be used in storefront Customer-creation flows.
 ### refreshVerificationToken
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, emailAddress: string) => Promise&#60;void&#62;`}   />
 
-Refreshes a stale email address verification token by generating a new one and
+Refreshes a stale email address verification token by generating a new one and
 publishing a <a href='/reference/typescript-api/events/event-types#accountregistrationevent'>AccountRegistrationEvent</a>.
 ### verifyCustomerEmailAddress
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, verificationToken: string, password?: string) => Promise&#60;ErrorResultUnion&#60;VerifyCustomerAccountResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, verificationToken: string, password?: string) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;VerifyCustomerAccountResult, <a href='/reference/typescript-api/entities/customer#customer'>Customer</a>&#62;&#62;`}   />
 
-Given a valid verification token which has been published in an <a href='/reference/typescript-api/events/event-types#accountregistrationevent'>AccountRegistrationEvent</a>, this
+Given a valid verification token which has been published in an <a href='/reference/typescript-api/events/event-types#accountregistrationevent'>AccountRegistrationEvent</a>, this
 method is used to set the Customer as `verified` as part of the account registration flow.
 ### requestPasswordReset
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, emailAddress: string) => Promise&#60;void&#62;`}   />
 
-Publishes a new <a href='/reference/typescript-api/events/event-types#passwordresetevent'>PasswordResetEvent</a> for the given email address. This event creates
+Publishes a new <a href='/reference/typescript-api/events/event-types#passwordresetevent'>PasswordResetEvent</a> for the given email address. This event creates
 a token which can be used in the `resetPassword()` method.
 ### resetPassword
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, passwordResetToken: string, password: string) => Promise&#60;         <a href='/reference/typescript-api/entities/user#user'>User</a> | PasswordResetTokenExpiredError | PasswordResetTokenInvalidError | PasswordValidationError     &#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, passwordResetToken: string, password: string) => Promise&#60;
         <a href='/reference/typescript-api/entities/user#user'>User</a> | PasswordResetTokenExpiredError | PasswordResetTokenInvalidError | PasswordValidationError
     &#62;`}   />
 
-Given a valid password reset token created by a call to the `requestPasswordReset()` method,
+Given a valid password reset token created by a call to the `requestPasswordReset()` method,
 this method will change the Customer's password to that given as the `password` argument.
 ### requestUpdateEmailAddress
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, userId: <a href='/reference/typescript-api/common/id#id'>ID</a>, newEmailAddress: string) => Promise&#60;boolean | EmailAddressConflictError&#62;`}   />
 
-Publishes a <a href='/reference/typescript-api/events/event-types#identifierchangerequestevent'>IdentifierChangeRequestEvent</a> for the given User. This event contains a token
-which is then used in the `updateEmailAddress()` method to change the email address of the User &
+Publishes a <a href='/reference/typescript-api/events/event-types#identifierchangerequestevent'>IdentifierChangeRequestEvent</a> for the given User. This event contains a token
+which is then used in the `updateEmailAddress()` method to change the email address of the User &
 Customer.
 ### updateEmailAddress
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, token: string) => Promise&#60;boolean | IdentifierChangeTokenInvalidError | IdentifierChangeTokenExpiredError&#62;`}   />
 
-Given a valid email update token published in a <a href='/reference/typescript-api/events/event-types#identifierchangerequestevent'>IdentifierChangeRequestEvent</a>, this method
+Given a valid email update token published in a <a href='/reference/typescript-api/events/event-types#identifierchangerequestevent'>IdentifierChangeRequestEvent</a>, this method
 will update the Customer & User email address.
 ### createOrUpdate
 
@@ -184,8 +184,8 @@ Creates a new <a href='/reference/typescript-api/entities/address#address'>Addre
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, order: <a href='/reference/typescript-api/entities/order#order'>Order</a>) => `}   />
 
-If the Customer associated with the given Order does not yet have any Addresses,
-this method will create new Address(es) based on the Order's shipping & billing
+If the Customer associated with the given Order does not yet have any Addresses,
+this method will create new Address(es) based on the Order's shipping & billing
 addresses.
 ### addNoteToCustomer
 

+ 1 - 1
docs/docs/reference/typescript-api/services/facet-service.md

@@ -91,7 +91,7 @@ Returns the Facet which contains the given FacetValue id.
 Assigns Facets to the specified Channel
 ### removeFacetsFromChannel
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: RemoveFacetsFromChannelInput) => Promise&#60;Array&#60;ErrorResultUnion&#60;RemoveFacetFromChannelResult, <a href='/reference/typescript-api/entities/facet#facet'>Facet</a>&#62;&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: RemoveFacetsFromChannelInput) => Promise&#60;Array&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;RemoveFacetFromChannelResult, <a href='/reference/typescript-api/entities/facet#facet'>Facet</a>&#62;&#62;&#62;`}   />
 
 Remove Facets from the specified Channel
 

+ 15 - 15
docs/docs/reference/typescript-api/services/order-service.md

@@ -166,23 +166,23 @@ User's Customer account.
 Updates the custom fields of an Order.
 ### addItemToOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, productVariantId: <a href='/reference/typescript-api/common/id#id'>ID</a>, quantity: number, customFields?: { [key: string]: any }) => Promise&#60;ErrorResultUnion&#60;UpdateOrderItemsResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, productVariantId: <a href='/reference/typescript-api/common/id#id'>ID</a>, quantity: number, customFields?: { [key: string]: any }) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;UpdateOrderItemsResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Adds an item to the Order, either creating a new OrderLine or
 incrementing an existing one.
 ### adjustOrderLine
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, orderLineId: <a href='/reference/typescript-api/common/id#id'>ID</a>, quantity: number, customFields?: { [key: string]: any }) => Promise&#60;ErrorResultUnion&#60;UpdateOrderItemsResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, orderLineId: <a href='/reference/typescript-api/common/id#id'>ID</a>, quantity: number, customFields?: { [key: string]: any }) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;UpdateOrderItemsResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Adjusts the quantity and/or custom field values of an existing OrderLine.
 ### removeItemFromOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, orderLineId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;ErrorResultUnion&#60;RemoveOrderItemsResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, orderLineId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;RemoveOrderItemsResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Removes the specified OrderLine from the Order.
 ### removeAllItemsFromOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;ErrorResultUnion&#60;RemoveOrderItemsResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;RemoveOrderItemsResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Removes all OrderLines from the Order.
 ### addSurchargeToOrder
@@ -197,7 +197,7 @@ Adds a <a href='/reference/typescript-api/entities/surcharge#surcharge'>Surcharg
 Removes a <a href='/reference/typescript-api/entities/surcharge#surcharge'>Surcharge</a> from the Order.
 ### applyCouponCode
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, couponCode: string) => Promise&#60;ErrorResultUnion&#60;ApplyCouponCodeResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, couponCode: string) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;ApplyCouponCodeResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Applies a coupon code to the Order, which should be a valid coupon code as specified in the configuration
 of an active <a href='/reference/typescript-api/entities/promotion#promotion'>Promotion</a>.
@@ -242,7 +242,7 @@ The quote also includes a price for each method, as determined by the configured
 Returns an array of quotes stating which <a href='/reference/typescript-api/entities/payment-method#paymentmethod'>PaymentMethod</a>s may be used on this Order.
 ### setShippingMethod
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, shippingMethodIds: <a href='/reference/typescript-api/common/id#id'>ID</a>[]) => Promise&#60;ErrorResultUnion&#60;SetOrderShippingMethodResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, shippingMethodIds: <a href='/reference/typescript-api/common/id#id'>ID</a>[]) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;SetOrderShippingMethodResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Sets the ShippingMethod to be used on this Order.
 ### transitionToState
@@ -258,7 +258,7 @@ Transitions a Fulfillment to the given state and then transitions the Order stat
 whether all Fulfillments of the Order are shipped or delivered.
 ### modifyOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: ModifyOrderInput) => Promise&#60;ErrorResultUnion&#60;ModifyOrderResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: ModifyOrderInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;ModifyOrderResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Allows the Order to be modified, which allows several aspects of the Order to be changed:
 
@@ -273,20 +273,20 @@ Order, except history entry and additional payment actions.
 __Using dryRun option, you must wrap function call in transaction manually.__
 ### transitionPaymentToState
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, paymentId: <a href='/reference/typescript-api/common/id#id'>ID</a>, state: <a href='/reference/typescript-api/payment/payment-state#paymentstate'>PaymentState</a>) => Promise&#60;ErrorResultUnion&#60;TransitionPaymentToStateResult, <a href='/reference/typescript-api/entities/payment#payment'>Payment</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, paymentId: <a href='/reference/typescript-api/common/id#id'>ID</a>, state: <a href='/reference/typescript-api/payment/payment-state#paymentstate'>PaymentState</a>) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;TransitionPaymentToStateResult, <a href='/reference/typescript-api/entities/payment#payment'>Payment</a>&#62;&#62;`}   />
 
 Transitions the given <a href='/reference/typescript-api/entities/payment#payment'>Payment</a> to a new state. If the order totalWithTax price is then
 covered by Payments, the Order state will be automatically transitioned to `PaymentSettled`
 or `PaymentAuthorized`.
 ### addPaymentToOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, input: PaymentInput) => Promise&#60;ErrorResultUnion&#60;AddPaymentToOrderResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, orderId: <a href='/reference/typescript-api/common/id#id'>ID</a>, input: PaymentInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;AddPaymentToOrderResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Adds a new Payment to the Order. If the Order totalWithTax is covered by Payments, then the Order
 state will get automatically transitioned to the `PaymentSettled` or `PaymentAuthorized` state.
 ### addManualPaymentToOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: ManualPaymentInput) => Promise&#60;ErrorResultUnion&#60;AddManualPaymentToOrderResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: ManualPaymentInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;AddManualPaymentToOrderResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 This method is used after modifying an existing completed order using the `modifyOrder()` method. If the modifications
 cause the order total to increase (such as when adding a new OrderLine), then there will be an outstanding charge to
@@ -296,19 +296,19 @@ This method allows you to add a new Payment and assumes the actual processing ha
 dashboard of your payment provider.
 ### settlePayment
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, paymentId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;ErrorResultUnion&#60;<a href='/reference/typescript-api/payment/payment-method-types#settlepaymentresult'>SettlePaymentResult</a>, <a href='/reference/typescript-api/entities/payment#payment'>Payment</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, paymentId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;<a href='/reference/typescript-api/payment/payment-method-types#settlepaymentresult'>SettlePaymentResult</a>, <a href='/reference/typescript-api/entities/payment#payment'>Payment</a>&#62;&#62;`}   />
 
 Settles a payment by invoking the <a href='/reference/typescript-api/payment/payment-method-handler#paymentmethodhandler'>PaymentMethodHandler</a>'s `settlePayment()` method. Automatically
 transitions the Order state if all Payments are settled.
 ### cancelPayment
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, paymentId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;ErrorResultUnion&#60;<a href='/reference/typescript-api/payment/payment-method-types#cancelpaymentresult'>CancelPaymentResult</a>, <a href='/reference/typescript-api/entities/payment#payment'>Payment</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, paymentId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;<a href='/reference/typescript-api/payment/payment-method-types#cancelpaymentresult'>CancelPaymentResult</a>, <a href='/reference/typescript-api/entities/payment#payment'>Payment</a>&#62;&#62;`}   />
 
 Cancels a payment by invoking the <a href='/reference/typescript-api/payment/payment-method-handler#paymentmethodhandler'>PaymentMethodHandler</a>'s `cancelPayment()` method (if defined), and transitions the Payment to
 the `Cancelled` state.
 ### createFulfillment
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: FulfillOrderInput) => Promise&#60;ErrorResultUnion&#60;AddFulfillmentToOrderResult, <a href='/reference/typescript-api/entities/fulfillment#fulfillment'>Fulfillment</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: FulfillOrderInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;AddFulfillmentToOrderResult, <a href='/reference/typescript-api/entities/fulfillment#fulfillment'>Fulfillment</a>&#62;&#62;`}   />
 
 Creates a new Fulfillment associated with the given Order and OrderItems.
 ### getOrderFulfillments
@@ -323,13 +323,13 @@ Returns an array of all Fulfillments associated with the Order.
 Returns an array of all Surcharges associated with the Order.
 ### cancelOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: CancelOrderInput) => Promise&#60;ErrorResultUnion&#60;CancelOrderResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: CancelOrderInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;CancelOrderResult, <a href='/reference/typescript-api/entities/order#order'>Order</a>&#62;&#62;`}   />
 
 Cancels an Order by transitioning it to the `Cancelled` state. If stock is being tracked for the ProductVariants
 in the Order, then new <a href='/reference/typescript-api/entities/stock-movement#stockmovement'>StockMovement</a>s will be created to correct the stock levels.
 ### refundOrder
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: RefundOrderInput) => Promise&#60;ErrorResultUnion&#60;RefundOrderResult, Refund&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: RefundOrderInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;RefundOrderResult, Refund&#62;&#62;`}   />
 
 Creates a {@link Refund} against the order and in doing so invokes the `createRefund()` method of the
 <a href='/reference/typescript-api/payment/payment-method-handler#paymentmethodhandler'>PaymentMethodHandler</a>.

+ 4 - 4
docs/docs/reference/typescript-api/services/product-service.md

@@ -11,13 +11,13 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ProductService
 
-<GenerationInfo sourceFile="packages/core/src/service/services/product.service.ts" sourceLine="57" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/services/product.service.ts" sourceLine="53" packageName="@vendure/core" />
 
 Contains methods relating to <a href='/reference/typescript-api/entities/product#product'>Product</a> entities.
 
 ```ts title="Signature"
 class ProductService {
-    constructor(connection: TransactionalConnection, channelService: ChannelService, roleService: RoleService, assetService: AssetService, productVariantService: ProductVariantService, facetValueService: FacetValueService, taxRateService: TaxRateService, collectionService: CollectionService, listQueryBuilder: ListQueryBuilder, translatableSaver: TranslatableSaver, eventBus: EventBus, slugValidator: SlugValidator, customFieldRelationService: CustomFieldRelationService, translator: TranslatorService, productOptionGroupService: ProductOptionGroupService)
+    constructor(connection: TransactionalConnection, channelService: ChannelService, assetService: AssetService, productVariantService: ProductVariantService, facetValueService: FacetValueService, listQueryBuilder: ListQueryBuilder, translatableSaver: TranslatableSaver, eventBus: EventBus, slugValidator: SlugValidator, customFieldRelationService: CustomFieldRelationService, translator: TranslatorService, productOptionGroupService: ProductOptionGroupService)
     findAll(ctx: RequestContext, options?: ListQueryOptions<Product>, relations?: RelationPaths<Product>) => Promise<PaginatedList<Translated<Product>>>;
     findOne(ctx: RequestContext, productId: ID, relations?: RelationPaths<Product>) => Promise<Translated<Product> | undefined>;
     findByIds(ctx: RequestContext, productIds: ID[], relations?: RelationPaths<Product>) => Promise<Array<Translated<Product>>>;
@@ -38,7 +38,7 @@ class ProductService {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, channelService: <a href='/reference/typescript-api/services/channel-service#channelservice'>ChannelService</a>, roleService: <a href='/reference/typescript-api/services/role-service#roleservice'>RoleService</a>, assetService: <a href='/reference/typescript-api/services/asset-service#assetservice'>AssetService</a>, productVariantService: <a href='/reference/typescript-api/services/product-variant-service#productvariantservice'>ProductVariantService</a>, facetValueService: <a href='/reference/typescript-api/services/facet-value-service#facetvalueservice'>FacetValueService</a>, taxRateService: <a href='/reference/typescript-api/services/tax-rate-service#taxrateservice'>TaxRateService</a>, collectionService: <a href='/reference/typescript-api/services/collection-service#collectionservice'>CollectionService</a>, listQueryBuilder: <a href='/reference/typescript-api/data-access/list-query-builder#listquerybuilder'>ListQueryBuilder</a>, translatableSaver: <a href='/reference/typescript-api/service-helpers/translatable-saver#translatablesaver'>TranslatableSaver</a>, eventBus: <a href='/reference/typescript-api/events/event-bus#eventbus'>EventBus</a>, slugValidator: <a href='/reference/typescript-api/service-helpers/slug-validator#slugvalidator'>SlugValidator</a>, customFieldRelationService: CustomFieldRelationService, translator: <a href='/reference/typescript-api/service-helpers/translator-service#translatorservice'>TranslatorService</a>, productOptionGroupService: <a href='/reference/typescript-api/services/product-option-group-service#productoptiongroupservice'>ProductOptionGroupService</a>) => ProductService`}   />
+<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, channelService: <a href='/reference/typescript-api/services/channel-service#channelservice'>ChannelService</a>, assetService: <a href='/reference/typescript-api/services/asset-service#assetservice'>AssetService</a>, productVariantService: <a href='/reference/typescript-api/services/product-variant-service#productvariantservice'>ProductVariantService</a>, facetValueService: <a href='/reference/typescript-api/services/facet-value-service#facetvalueservice'>FacetValueService</a>, listQueryBuilder: <a href='/reference/typescript-api/data-access/list-query-builder#listquerybuilder'>ListQueryBuilder</a>, translatableSaver: <a href='/reference/typescript-api/service-helpers/translatable-saver#translatablesaver'>TranslatableSaver</a>, eventBus: <a href='/reference/typescript-api/events/event-bus#eventbus'>EventBus</a>, slugValidator: <a href='/reference/typescript-api/service-helpers/slug-validator#slugvalidator'>SlugValidator</a>, customFieldRelationService: CustomFieldRelationService, translator: <a href='/reference/typescript-api/service-helpers/translator-service#translatorservice'>TranslatorService</a>, productOptionGroupService: <a href='/reference/typescript-api/services/product-option-group-service#productoptiongroupservice'>ProductOptionGroupService</a>) => ProductService`}   />
 
 
 ### findAll
@@ -107,7 +107,7 @@ each of the Product's variants, and will assign the Product's Assets to the Chan
 
 ### removeOptionGroupFromProduct
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, productId: <a href='/reference/typescript-api/common/id#id'>ID</a>, optionGroupId: <a href='/reference/typescript-api/common/id#id'>ID</a>, force?: boolean) => Promise&#60;ErrorResultUnion&#60;RemoveOptionGroupFromProductResult, Translated&#60;<a href='/reference/typescript-api/entities/product#product'>Product</a>&#62;&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, productId: <a href='/reference/typescript-api/common/id#id'>ID</a>, optionGroupId: <a href='/reference/typescript-api/common/id#id'>ID</a>, force?: boolean) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;RemoveOptionGroupFromProductResult, Translated&#60;<a href='/reference/typescript-api/entities/product#product'>Product</a>&#62;&#62;&#62;`}   />
 
 
 

+ 2 - 2
docs/docs/reference/typescript-api/services/promotion-service.md

@@ -76,12 +76,12 @@ class PromotionService {
 
 ### createPromotion
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: CreatePromotionInput) => Promise&#60;ErrorResultUnion&#60;CreatePromotionResult, <a href='/reference/typescript-api/entities/promotion#promotion'>Promotion</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: CreatePromotionInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;CreatePromotionResult, <a href='/reference/typescript-api/entities/promotion#promotion'>Promotion</a>&#62;&#62;`}   />
 
 
 ### updatePromotion
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: UpdatePromotionInput) => Promise&#60;ErrorResultUnion&#60;UpdatePromotionResult, <a href='/reference/typescript-api/entities/promotion#promotion'>Promotion</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: UpdatePromotionInput) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;UpdatePromotionResult, <a href='/reference/typescript-api/entities/promotion#promotion'>Promotion</a>&#62;&#62;`}   />
 
 
 ### softDeletePromotion

+ 1 - 1
docs/docs/reference/typescript-api/services/user-service.md

@@ -88,7 +88,7 @@ Sets the <a href='/reference/typescript-api/entities/authentication-method#nativ
 flow.
 ### verifyUserByToken
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, verificationToken: string, password?: string) => Promise&#60;ErrorResultUnion&#60;VerifyCustomerAccountResult, <a href='/reference/typescript-api/entities/user#user'>User</a>&#62;&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, verificationToken: string, password?: string) => Promise&#60;<a href='/reference/typescript-api/errors/error-result-union#errorresultunion'>ErrorResultUnion</a>&#60;VerifyCustomerAccountResult, <a href='/reference/typescript-api/entities/user#user'>User</a>&#62;&#62;`}   />
 
 Verifies a verificationToken by looking for a User which has previously had it set using the
 `setVerificationToken()` method, and checks that the token is valid and has not expired.

+ 12 - 0
docs/sidebars.js

@@ -267,6 +267,18 @@ const sidebars = {
                     link: { type: 'doc', id: 'reference/core-plugins/payments-plugin/index' },
                     items: [{ type: 'autogenerated', dirName: 'reference/core-plugins/payments-plugin' }],
                 },
+                {
+                    type: 'category',
+                    label: 'SentryPlugin',
+                    link: { type: 'doc', id: 'reference/core-plugins/sentry-plugin/index' },
+                    items: [{ type: 'autogenerated', dirName: 'reference/core-plugins/sentry-plugin' }],
+                },
+                {
+                    type: 'category',
+                    label: 'StellatePlugin',
+                    link: { type: 'doc', id: 'reference/core-plugins/stellate-plugin/index' },
+                    items: [{ type: 'autogenerated', dirName: 'reference/core-plugins/stellate-plugin' }],
+                },
             ],
         },
         {

+ 1 - 1
lerna.json

@@ -1,6 +1,6 @@
 {
     "packages": ["packages/*"],
-    "version": "2.1.4",
+    "version": "2.1.5",
     "npmClient": "yarn",
     "command": {
         "version": {

+ 1 - 2
package.json

@@ -12,8 +12,7 @@
     "format": "prettier --write --html-whitespace-sensitivity ignore",
     "docs:generate-typescript-docs": "ts-node scripts/docs/generate-typescript-docs.ts",
     "docs:generate-graphql-docs": "ts-node scripts/docs/generate-graphql-docs.ts --api=shop && ts-node scripts/docs/generate-graphql-docs.ts --api=admin",
-    "docs:update-build-info": "ts-node scripts/docs/update-build-info.ts",
-    "docs:build": "yarn docs:generate-graphql-docs && yarn docs:generate-typescript-docs && yarn docs:update-build-info",
+    "docs:build": "yarn docs:generate-graphql-docs && yarn docs:generate-typescript-docs",
     "codegen": "tsc -p scripts/codegen/plugins && ts-node scripts/codegen/generate-graphql-types.ts",
     "version": "yarn check-imports && yarn check-angular-versions && yarn build && yarn check-core-type-defs && yarn generate-changelog && git add CHANGELOG* && git add */version.ts",
     "dev-server:start": "cd packages/dev-server && yarn start",

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

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/admin-ui-plugin",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "main": "lib/index.js",
     "types": "lib/index.d.ts",
     "files": [
@@ -21,8 +21,8 @@
     "devDependencies": {
         "@types/express": "^4.17.8",
         "@types/fs-extra": "^9.0.1",
-        "@vendure/common": "^2.1.4",
-        "@vendure/core": "^2.1.4",
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5",
         "express": "^4.17.1",
         "rimraf": "^3.0.2",
         "typescript": "4.9.5"

+ 1 - 1
packages/admin-ui/package-lock.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/admin-ui",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {

+ 2 - 2
packages/admin-ui/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/admin-ui",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "license": "MIT",
     "scripts": {
         "ng": "ng",
@@ -49,7 +49,7 @@
         "@ng-select/ng-select": "^11.1.1",
         "@ngx-translate/core": "^15.0.0",
         "@ngx-translate/http-loader": "^8.0.0",
-        "@vendure/common": "^2.1.4",
+        "@vendure/common": "^2.1.5",
         "@webcomponents/custom-elements": "^1.6.0",
         "apollo-angular": "^5.0.0",
         "apollo-upload-client": "^17.0.0",

+ 1 - 1
packages/admin-ui/src/lib/core/src/common/version.ts

@@ -1,2 +1,2 @@
 // Auto-generated by the set-version.js script.
-export const ADMIN_UI_VERSION = '2.1.4';
+export const ADMIN_UI_VERSION = '2.1.5';

+ 36 - 34
packages/admin-ui/src/lib/core/src/shared/components/asset-preview/asset-preview.component.html

@@ -107,42 +107,44 @@
         </vdr-card>
     </vdr-page-detail-sidebar>
     <div class="carousel-container">
-    <button *ngIf="showSlideButtons" (click)="previousImage()" [class.disabled]="disablePreviousButton"><clr-icon shape="caret left" class="color-weight-800"></clr-icon
-        ></button>
-    <div class="preview-image" #previewDiv [class.centered]="centered">
-        <div class="image-wrapper">
-            <vdr-focal-point-control
-                [width]="width"
-                [height]="height"
-                [fpx]="fpx"
-                [fpy]="fpy"
-                [editable]="settingFocalPoint"
-                (focalPointChange)="onFocalPointChange($event)"
-            >
-                <img
-                    class="asset-image"
-                    [src]="asset | assetPreview : size"
-                    [ngClass]="size"
-                    #imageElement
-                    (load)="onImageLoad()"
-                />
-            </vdr-focal-point-control>
-            <div class="focal-point-info" *ngIf="settingFocalPoint">
-                <button class="icon-button" (click)="setFocalPointCancel()">
-                    <clr-icon shape="times"></clr-icon>
-                </button>
-                <button
-                    class="btn btn-primary btn-sm"
-                    (click)="setFocalPointEnd()"
-                    [disabled]="!lastFocalPoint"
+        <button *ngIf="showSlideButtons" (click)="previousImage()" class="carousel-button" [class.disabled]="disablePreviousButton">
+            <clr-icon shape="caret left" class="color-weight-800"></clr-icon>
+        </button>
+        <div class="preview-image" #previewDiv [class.centered]="centered">
+            <div class="image-wrapper">
+                <vdr-focal-point-control
+                    [width]="width"
+                    [height]="height"
+                    [fpx]="fpx"
+                    [fpy]="fpy"
+                    [editable]="settingFocalPoint"
+                    (focalPointChange)="onFocalPointChange($event)"
                 >
-                    <clr-icon shape="crosshairs"></clr-icon>
-                    {{ 'asset.set-focal-point' | translate }}
-                </button>
+                    <img
+                        class="asset-image"
+                        [src]="asset | assetPreview : size"
+                        [ngClass]="size"
+                        #imageElement
+                        (load)="onImageLoad()"
+                    />
+                </vdr-focal-point-control>
+                <div class="focal-point-info" *ngIf="settingFocalPoint">
+                    <button class="icon-button" (click)="setFocalPointCancel()">
+                        <clr-icon shape="times"></clr-icon>
+                    </button>
+                    <button
+                        class="btn btn-primary btn-sm"
+                        (click)="setFocalPointEnd()"
+                        [disabled]="!lastFocalPoint"
+                    >
+                        <clr-icon shape="crosshairs"></clr-icon>
+                        {{ 'asset.set-focal-point' | translate }}
+                    </button>
+                </div>
             </div>
         </div>
-    </div>
-    <button  *ngIf="showSlideButtons" (click)="nextImage()" [class.disabled]="disableNextButton"><clr-icon shape="caret right" class="color-weight-800"></clr-icon
-        ></button>
+        <button *ngIf="showSlideButtons" (click)="nextImage()" class="carousel-button" [class.disabled]="disableNextButton">
+            <clr-icon shape="caret right" class="color-weight-800"></clr-icon>
+        </button>
     </div>
 </vdr-page-detail-layout>

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/asset-preview/asset-preview.component.scss

@@ -96,7 +96,7 @@
     justify-content: space-between;
     align-items: center;
 
-    button{
+    button.carousel-button {
         cursor: pointer;
         width: 30px;
         height: 30px;

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

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/asset-server-plugin",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "main": "lib/index.js",
     "types": "lib/index.d.ts",
     "files": [
@@ -27,8 +27,8 @@
         "@types/fs-extra": "^11.0.1",
         "@types/node-fetch": "^2.5.8",
         "@types/sharp": "^0.30.4",
-        "@vendure/common": "^2.1.4",
-        "@vendure/core": "^2.1.4",
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5",
         "express": "^4.17.1",
         "node-fetch": "^2.6.7",
         "rimraf": "^3.0.2",

+ 2 - 2
packages/cli/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/cli",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "description": "A modern, headless ecommerce framework",
     "repository": {
         "type": "git",
@@ -34,7 +34,7 @@
     ],
     "dependencies": {
         "@clack/prompts": "^0.7.0",
-        "@vendure/common": "^2.1.4",
+        "@vendure/common": "^2.1.5",
         "change-case": "^4.1.2",
         "commander": "^11.0.0",
         "fs-extra": "^11.1.1",

+ 1 - 1
packages/common/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/common",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "main": "index.js",
     "license": "MIT",
     "scripts": {

+ 1 - 1
packages/common/src/shared-types.ts

@@ -88,7 +88,7 @@ export type ID = string | number;
  * string       | varchar                               | String
  * localeString | varchar                               | String
  * text         | longtext(m), text(p,s)                | String
- * localText    | longtext(m), text(p,s)                | String
+ * localeText    | longtext(m), text(p,s)                | String
  * int          | int                                   | Int
  * float        | double precision                      | Float
  * boolean      | tinyint (m), bool (p), boolean (s)    | Boolean

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

@@ -39,9 +39,25 @@ export class CustomFieldRelationTestEntity extends VendureEntity {
     parent: Relation<TestEntity>;
 }
 
+@Entity()
+export class CustomFieldOtherRelationTestEntity extends VendureEntity {
+    constructor(input: Partial<CustomFieldOtherRelationTestEntity>) {
+        super(input);
+    }
+
+    @Column()
+    data: string;
+
+    @ManyToOne(() => TestEntity)
+    parent: Relation<TestEntity>;
+}
+
 class TestEntityCustomFields {
     @OneToMany(() => CustomFieldRelationTestEntity, child => child.parent)
     relation: Relation<CustomFieldRelationTestEntity[]>;
+
+    @OneToMany(() => CustomFieldOtherRelationTestEntity, child => child.parent)
+    otherRelation: Relation<CustomFieldOtherRelationTestEntity[]>;
 }
 
 @Entity()
@@ -143,7 +159,7 @@ export class TestEntityTranslation extends VendureEntity implements Translation<
     @ManyToOne(type => TestEntity, base => base.translations)
     base: TestEntity;
 
-    customFields: {};
+    customFields: never;
 }
 
 @Entity()
@@ -171,7 +187,12 @@ export class ListQueryResolver {
         return this.listQueryBuilder
             .build(TestEntity, args.options, {
                 ctx,
-                relations: ['orderRelation', 'orderRelation.customer', 'customFields.relation'],
+                relations: [
+                    'orderRelation',
+                    'orderRelation.customer',
+                    'customFields.relation',
+                    'customFields.otherRelation',
+                ],
                 customPropertyMap: {
                     customerLastName: 'orderRelation.customer.lastName',
                 },
@@ -222,8 +243,14 @@ const apiExtensions = gql`
         data: String!
     }
 
+    type CustomFieldOtherRelationTestEntity implements Node {
+        id: ID!
+        data: String!
+    }
+
     type TestEntityCustomFields {
         relation: [CustomFieldRelationTestEntity!]!
+        otherRelation: [CustomFieldOtherRelationTestEntity!]!
     }
 
     type TestEntity implements Node {
@@ -272,7 +299,13 @@ const apiExtensions = gql`
 
 @VendurePlugin({
     imports: [PluginCommonModule],
-    entities: [TestEntity, TestEntityPrice, TestEntityTranslation, CustomFieldRelationTestEntity],
+    entities: [
+        TestEntity,
+        TestEntityPrice,
+        TestEntityTranslation,
+        CustomFieldRelationTestEntity,
+        CustomFieldOtherRelationTestEntity,
+    ],
     adminApiExtensions: {
         schema: apiExtensions,
         resolvers: [ListQueryResolver],
@@ -409,6 +442,12 @@ export class ListQueryPlugin implements OnApplicationBootstrap {
                                 data: nestedContent.data,
                             }),
                         );
+                        await this.connection.getRepository(CustomFieldOtherRelationTestEntity).save(
+                            new CustomFieldOtherRelationTestEntity({
+                                parent: testEntity,
+                                data: nestedContent.data,
+                            }),
+                        );
                     }
                 }
             }

+ 42 - 0
packages/core/e2e/list-query-builder.e2e-spec.ts

@@ -1268,6 +1268,27 @@ describe('ListQueryBuilder', () => {
                 },
             ]);
         });
+
+        it('should resolve multiple relations in customFields successfully', async () => {
+            const { testEntities } = await shopClient.query(GET_LIST_WITH_MULTIPLE_CUSTOM_FIELD_RELATION, {
+                options: {
+                    filter: {
+                        label: { eq: 'A' },
+                    },
+                },
+            });
+
+            expect(testEntities.items).toEqual([
+                {
+                    id: 'T_1',
+                    label: 'A',
+                    customFields: {
+                        relation: [{ id: 'T_1', data: 'A' }],
+                        otherRelation: [{ id: 'T_1', data: 'A' }],
+                    },
+                },
+            ]);
+        });
     });
 });
 
@@ -1351,3 +1372,24 @@ const GET_LIST_WITH_CUSTOM_FIELD_RELATION = gql`
         }
     }
 `;
+
+const GET_LIST_WITH_MULTIPLE_CUSTOM_FIELD_RELATION = gql`
+    query GetTestWithMultipleCustomFieldRelation($options: TestEntityListOptions) {
+        testEntities(options: $options) {
+            items {
+                id
+                label
+                customFields {
+                    relation {
+                        id
+                        data
+                    }
+                    otherRelation {
+                        id
+                        data
+                    }
+                }
+            }
+        }
+    }
+`;

+ 2 - 2
packages/core/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/core",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "description": "A modern, headless ecommerce framework",
     "repository": {
         "type": "git",
@@ -50,7 +50,7 @@
         "@nestjs/testing": "10.2.1",
         "@nestjs/typeorm": "10.0.0",
         "@types/fs-extra": "^9.0.1",
-        "@vendure/common": "^2.1.4",
+        "@vendure/common": "^2.1.5",
         "bcrypt": "^5.1.1",
         "body-parser": "^1.20.2",
         "chalk": "^4.1.2",

+ 23 - 1
packages/core/src/common/error/error-result.ts

@@ -37,6 +37,9 @@ export type JustErrorResults<T extends GraphQLErrorResult | U, U = any> = Exclud
  * type UpdateOrderItemsResult = Order | OrderModificationError | OrderLimitError | NegativeQuantityError;
  * type T1 = ErrorResultUnion<UpdateOrderItemsResult, VendureEntityOrder>;
  * // T1 = VendureEntityOrder | OrderModificationError | OrderLimitError | NegativeQuantityError;
+ * ```
+ *
+ * @docsCategory errors
  */
 export type ErrorResultUnion<T extends GraphQLErrorResult | U, E extends VendureEntity, U = any> =
     | JustErrorResults<T>
@@ -44,7 +47,26 @@ export type ErrorResultUnion<T extends GraphQLErrorResult | U, E extends Vendure
 
 /**
  * @description
- * Returns true if the ErrorResultUnion is actually an ErrorResult type.
+ * Returns true if the {@link ErrorResultUnion} is actually an ErrorResult type. This is useful when dealing with
+ * certain internal service method that return an ErrorResultUnion.
+ *
+ * @example
+ * ```ts
+ * import { isGraphQlErrorResult } from '\@vendure/core';
+ *
+ * // ...
+ *
+ * const transitionResult = await this.orderService.transitionToState(ctx, order.id, newState);
+ * if (isGraphQlErrorResult(transitionResult)) {
+ *     // The transition failed with an ErrorResult
+ *     throw transitionResult;
+ * } else {
+ *     // TypeScript will correctly infer the type of `transitionResult` to be `Order`
+ *     return transitionResult;
+ * }
+ * ```
+ *
+ * @docsCategory errors
  */
 export function isGraphQlErrorResult<T extends GraphQLErrorResult | U, U = any>(
     input: T,

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

@@ -413,10 +413,13 @@ export function configureDefaultOrderProcess(options: DefaultOrderProcessOptions
                     order.active = false;
                     order.orderPlacedAt = new Date();
                     await Promise.all(
-                        order.lines.map(line =>
+                        order.lines.map(line => {
+                            line.orderPlacedQuantity = line.quantity
                             connection
                                 .getRepository(ctx, OrderLine)
-                                .update(line.id, { orderPlacedQuantity: line.quantity }),
+                                .update(line.id, { orderPlacedQuantity: line.quantity })
+                            return line
+                        }
                         ),
                     );
                     eventBus.publish(new OrderPlacedEvent(fromState, toState, ctx, order));

+ 69 - 0
packages/core/src/entity/base/base.entity.spec.ts

@@ -0,0 +1,69 @@
+import { DeepPartial } from '@vendure/common/lib/shared-types';
+import { describe, expect, it } from 'vitest';
+
+import { Calculated } from '../../common/index';
+import { CalculatedPropertySubscriber } from '../subscribers';
+
+import { VendureEntity } from './base.entity';
+
+class ChildEntity extends VendureEntity {
+    constructor(input?: DeepPartial<ChildEntity>) {
+        super(input);
+    }
+
+    name: string;
+
+    get nameLoud(): string {
+        return this.name.toUpperCase();
+    }
+}
+
+class ChildEntityWithCalculated extends VendureEntity {
+    constructor(input?: DeepPartial<ChildEntity>) {
+        super(input);
+    }
+
+    name: string;
+
+    @Calculated()
+    get nameLoudCalculated(): string {
+        return this.name.toUpperCase();
+    }
+}
+
+describe('VendureEntity', () => {
+    it('instantiating a child entity', () => {
+        const child = new ChildEntity({
+            name: 'foo',
+        });
+
+        expect(child.name).toBe('foo');
+        expect(child.nameLoud).toBe('FOO');
+    });
+
+    it('instantiating from existing entity with getter', () => {
+        const child1 = new ChildEntity({
+            name: 'foo',
+        });
+
+        const child2 = new ChildEntity(child1);
+
+        expect(child2.name).toBe('foo');
+        expect(child2.nameLoud).toBe('FOO');
+    });
+
+    it('instantiating from existing entity with calculated getter', () => {
+        const calculatedPropertySubscriber = new CalculatedPropertySubscriber();
+        const child1 = new ChildEntityWithCalculated({
+            name: 'foo',
+        });
+
+        // This is what happens to entities after being loaded from the DB
+        calculatedPropertySubscriber.afterLoad(child1);
+
+        const child2 = new ChildEntityWithCalculated(child1);
+
+        expect(child2.name).toBe('foo');
+        expect(child2.nameLoudCalculated).toBe('FOO');
+    });
+});

+ 8 - 2
packages/core/src/entity/base/base.entity.ts

@@ -13,8 +13,14 @@ import { PrimaryGeneratedId } from '../entity-id.decorator';
 export abstract class VendureEntity {
     protected constructor(input?: DeepPartial<VendureEntity>) {
         if (input) {
-            for (const [key, value] of Object.entries(input)) {
-                (this as any)[key] = value;
+            for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(input))) {
+                if (descriptor.get && !descriptor.set) {
+                    // A getter has been moved to the entity instance
+                    // by the CalculatedPropertySubscriber
+                    // and cannot be copied over to the new instance.
+                    continue;
+                }
+                (this as any)[key] = descriptor.value;
             }
         }
     }

+ 1 - 0
packages/core/src/event-bus/index.ts

@@ -1,6 +1,7 @@
 export * from './event-bus';
 export * from './event-bus.module';
 export * from './vendure-event';
+export * from './vendure-entity-event';
 
 export * from './events/account-registration-event';
 export * from './events/account-verified-event';

+ 0 - 1
packages/core/src/i18n/messages/en.json

@@ -6,7 +6,6 @@
     "cannot-delete-sole-superadmin": "The sole SuperAdmin cannot be deleted",
     "cannot-locate-customer-for-user": "Cannot locate a Customer for the user",
     "cannot-modify-role": "The role \"{ roleCode }\" cannot be modified",
-    "cannot-create-sales-for-active-order": "Cannot create a Sale for an Order which is still active",
     "cannot-move-collection-into-self": "Cannot move a Collection into itself",
     "cannot-transition-payment-from-to": "Cannot transition Payment from \"{ fromState }\" to \"{ toState }\"",
     "cannot-transition-refund-from-to": "Cannot transition Refund from \"{ fromState }\" to \"{ toState }\"",

+ 10 - 3
packages/core/src/service/helpers/list-query-builder/list-query-builder.ts

@@ -493,14 +493,21 @@ export class ListQueryBuilder implements OnApplicationBootstrap {
                         loadEagerRelations: true,
                     } as FindManyOptions<T>)
                     .then(results =>
-                        results.map(r => ({ relation: relationPaths[0] as keyof T, entity: r })),
+                        results.map(r => ({
+                            relations: relationPaths[0].startsWith('customFields.')
+                                ? relationPaths
+                                : [relationPaths[0]],
+                            entity: r,
+                        })),
                     );
             }),
         ).then(all => all.flat());
         for (const entry of entitiesIdsWithRelations) {
             const finalEntity = entityMap.get(entry.entity.id);
-            if (finalEntity) {
-                this.assignDeep(entry.relation, entry.entity, finalEntity);
+            for (const relation of entry.relations) {
+                if (finalEntity) {
+                    this.assignDeep(relation, entry.entity, finalEntity);
+                }
             }
         }
         return Array.from(entityMap.values());

+ 4 - 2
packages/core/src/service/services/order.service.ts

@@ -588,8 +588,9 @@ export class OrderService {
         let updatedOrderLines = [orderLine];
         if (correctedQuantity === 0) {
             order.lines = order.lines.filter(l => !idsAreEqual(l.id, orderLine.id));
+            const deletedOrderLine = new OrderLine(orderLine);
             await this.connection.getRepository(ctx, OrderLine).remove(orderLine);
-            this.eventBus.publish(new OrderLineEvent(ctx, order, orderLine, 'deleted'));
+            this.eventBus.publish(new OrderLineEvent(ctx, order, deletedOrderLine, 'deleted'));
             updatedOrderLines = [];
         } else {
             await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
@@ -620,8 +621,9 @@ export class OrderService {
         const orderLine = this.getOrderLineOrThrow(order, orderLineId);
         order.lines = order.lines.filter(line => !idsAreEqual(line.id, orderLineId));
         const updatedOrder = await this.applyPriceAdjustments(ctx, order);
+        const deletedOrderLine = new OrderLine(orderLine);
         await this.connection.getRepository(ctx, OrderLine).remove(orderLine);
-        this.eventBus.publish(new OrderLineEvent(ctx, order, orderLine, 'deleted'));
+        this.eventBus.publish(new OrderLineEvent(ctx, order, deletedOrderLine, 'deleted'));
         return updatedOrder;
     }
 

+ 0 - 3
packages/core/src/service/services/stock-movement.service.ts

@@ -137,9 +137,6 @@ export class StockMovementService {
      * increased, indicating that this quantity of stock is allocated and cannot be sold.
      */
     async createAllocationsForOrder(ctx: RequestContext, order: Order): Promise<Allocation[]> {
-        if (order.active !== false) {
-            throw new InternalServerError('error.cannot-create-allocations-for-active-order');
-        }
         const lines = order.lines.map(orderLine => ({
             orderLineId: orderLine.id,
             quantity: orderLine.quantity,

+ 3 - 3
packages/create/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/create",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "license": "MIT",
     "bin": {
         "create": "./index.js"
@@ -28,14 +28,14 @@
         "@types/fs-extra": "^9.0.1",
         "@types/handlebars": "^4.1.0",
         "@types/semver": "^6.2.2",
-        "@vendure/core": "^2.1.4",
+        "@vendure/core": "^2.1.5",
         "rimraf": "^3.0.2",
         "ts-node": "^10.9.1",
         "typescript": "4.9.5"
     },
     "dependencies": {
         "@clack/prompts": "^0.7.0",
-        "@vendure/common": "^2.1.4",
+        "@vendure/common": "^2.1.5",
         "commander": "^11.0.0",
         "cross-spawn": "^7.0.3",
         "detect-port": "^1.5.1",

+ 9 - 9
packages/dev-server/package.json

@@ -1,6 +1,6 @@
 {
     "name": "dev-server",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "main": "index.js",
     "license": "MIT",
     "private": true,
@@ -15,18 +15,18 @@
     },
     "dependencies": {
         "@nestjs/axios": "^3.0.0",
-        "@vendure/admin-ui-plugin": "^2.1.4",
-        "@vendure/asset-server-plugin": "^2.1.4",
-        "@vendure/common": "^2.1.4",
-        "@vendure/core": "^2.1.4",
-        "@vendure/elasticsearch-plugin": "^2.1.4",
-        "@vendure/email-plugin": "^2.1.4",
+        "@vendure/admin-ui-plugin": "^2.1.5",
+        "@vendure/asset-server-plugin": "^2.1.5",
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5",
+        "@vendure/elasticsearch-plugin": "^2.1.5",
+        "@vendure/email-plugin": "^2.1.5",
         "typescript": "4.9.5"
     },
     "devDependencies": {
         "@types/csv-stringify": "^3.1.0",
-        "@vendure/testing": "^2.1.4",
-        "@vendure/ui-devkit": "^2.1.4",
+        "@vendure/testing": "^2.1.5",
+        "@vendure/ui-devkit": "^2.1.5",
         "commander": "^7.1.0",
         "concurrently": "^8.2.1",
         "csv-stringify": "^5.3.3",

+ 3 - 3
packages/elasticsearch-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/elasticsearch-plugin",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "license": "MIT",
     "main": "lib/index.js",
     "types": "lib/index.d.ts",
@@ -26,8 +26,8 @@
         "fast-deep-equal": "^3.1.3"
     },
     "devDependencies": {
-        "@vendure/common": "^2.1.4",
-        "@vendure/core": "^2.1.4",
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5",
         "rimraf": "^3.0.2",
         "typescript": "4.9.5"
     }

+ 1 - 1
packages/elasticsearch-plugin/src/types.ts

@@ -271,7 +271,7 @@ type GraphQlPermittedReturnType = PrimitiveTypeVariations<GraphQlPrimitive>;
 type CustomMappingDefinition<Args extends any[], T extends GraphQlPermittedReturnType, R> = {
     graphQlType: T;
     public?: boolean;
-    valueFn: (...args: Args) => R;
+    valueFn: (...args: Args) => Promise<R> | R;
 };
 
 type TypeVariationMap<GqlType extends GraphQlPrimitive, TsType> = {

+ 3 - 3
packages/email-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/email-plugin",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "license": "MIT",
     "main": "lib/index.js",
     "types": "lib/index.d.ts",
@@ -35,8 +35,8 @@
         "@types/fs-extra": "^9.0.1",
         "@types/handlebars": "^4.1.0",
         "@types/mjml": "^4.0.4",
-        "@vendure/common": "^2.1.4",
-        "@vendure/core": "^2.1.4",
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5",
         "rimraf": "^3.0.2",
         "typescript": "4.9.5"
     }

+ 3 - 3
packages/harden-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/harden-plugin",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "license": "MIT",
     "main": "lib/index.js",
     "types": "lib/index.d.ts",
@@ -21,7 +21,7 @@
         "graphql-query-complexity": "^0.12.0"
     },
     "devDependencies": {
-        "@vendure/common": "^2.1.4",
-        "@vendure/core": "^2.1.4"
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5"
     }
 }

+ 3 - 3
packages/job-queue-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/job-queue-plugin",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "license": "MIT",
     "main": "package/index.js",
     "types": "package/index.d.ts",
@@ -23,8 +23,8 @@
     },
     "devDependencies": {
         "@google-cloud/pubsub": "^2.8.0",
-        "@vendure/common": "^2.1.4",
-        "@vendure/core": "^2.1.4",
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5",
         "bullmq": "^3.15.5",
         "ioredis": "^5.3.0",
         "rimraf": "^3.0.2",

+ 3 - 3
packages/job-queue-plugin/src/bullmq/plugin.ts

@@ -28,13 +28,13 @@ import { BullMQPluginOptions } from './types';
  *
  * ## Installation
  *
- * `yarn add \@vendure/job-queue-plugin bullmq@1`
+ * `yarn add \@vendure/job-queue-plugin bullmq`
  *
  * or
  *
- * `npm install \@vendure/job-queue-plugin bullmq@1`
+ * `npm install \@vendure/job-queue-plugin bullmq`
  *
- * **Note:** The v1.x version of this plugin is designed to work with bullmq v1.x.
+ * **Note:** The v1.x version of this plugin is designed to work with bullmq v1.x, etc.
  *
  * @example
  * ```ts

+ 4 - 4
packages/payments-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/payments-plugin",
-    "version": "2.1.4",
+    "version": "2.1.5",
     "license": "MIT",
     "main": "package/index.js",
     "types": "package/index.d.ts",
@@ -46,9 +46,9 @@
         "@mollie/api-client": "^3.7.0",
         "@types/braintree": "^2.22.15",
         "@types/localtunnel": "2.0.1",
-        "@vendure/common": "^2.1.4",
-        "@vendure/core": "^2.1.4",
-        "@vendure/testing": "^2.1.4",
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5",
+        "@vendure/testing": "^2.1.5",
         "braintree": "^3.16.0",
         "localtunnel": "2.0.2",
         "nock": "^13.1.4",

+ 16 - 4
packages/payments-plugin/src/mollie/mollie.controller.ts

@@ -1,17 +1,16 @@
 import { Body, Controller, Param, Post } from '@nestjs/common';
-import { Ctx, Logger, RequestContext, Transaction } from '@vendure/core';
+import { Ctx, Logger, RequestContext, Transaction, ChannelService, LanguageCode } from '@vendure/core';
 
 import { loggerCtx } from './constants';
 import { MollieService } from './mollie.service';
 
 @Controller('payments')
 export class MollieController {
-    constructor(private mollieService: MollieService) {}
+    constructor(private mollieService: MollieService, private channelService: ChannelService) {}
 
     @Post('mollie/:channelToken/:paymentMethodId')
     @Transaction()
     async webhook(
-        @Ctx() ctx: RequestContext,
         @Param('channelToken') channelToken: string,
         @Param('paymentMethodId') paymentMethodId: string,
         @Body() body: any,
@@ -20,8 +19,10 @@ export class MollieController {
             return Logger.warn(' Ignoring incoming webhook, because it has no body.id.', loggerCtx);
         }
         try {
+            // We need to construct a RequestContext based on the channelToken,
+            // because this is an incoming webhook, not a graphql request with a valid Ctx
+            const ctx = await this.createContext(channelToken);
             await this.mollieService.handleMollieStatusUpdate(ctx, {
-                channelToken,
                 paymentMethodId,
                 orderId: body.id,
             });
@@ -34,4 +35,15 @@ export class MollieController {
             throw error;
         }
     }
+
+    private async createContext(channelToken: string): Promise<RequestContext> {
+        const channel = await this.channelService.getChannelFromToken(channelToken);
+        return new RequestContext({
+            apiType: 'admin',
+            isAuthorized: true,
+            authorizedAsOwnerOnly: false,
+            channel,
+            languageCode: LanguageCode.en,
+        });
+    }
 }

+ 10 - 6
packages/payments-plugin/src/mollie/mollie.service.ts

@@ -37,7 +37,6 @@ import { amountToCents, getLocale, toAmount, toMollieAddress, toMollieOrderLines
 import { MolliePluginOptions } from './mollie.plugin';
 
 interface OrderStatusInput {
-    channelToken: string;
     paymentMethodId: string;
     orderId: string;
 }
@@ -189,7 +188,7 @@ export class MollieService {
             orderInput.method = molliePaymentMethodCode as MollieClientMethod;
         }
         const mollieOrder = await mollieClient.orders.create(orderInput);
-        Logger.info(`Created Mollie order ${mollieOrder.id} for order ${order.code}`);
+        Logger.info(`Created Mollie order ${mollieOrder.id} for order ${order.code}`, loggerCtx);
         const url = mollieOrder.getCheckoutUrl();
         if (!url) {
             throw Error('Unable to getCheckoutUrl() from Mollie order');
@@ -204,10 +203,10 @@ export class MollieService {
      */
     async handleMollieStatusUpdate(
         ctx: RequestContext,
-        { channelToken, paymentMethodId, orderId }: OrderStatusInput,
+        { paymentMethodId, orderId }: OrderStatusInput,
     ): Promise<void> {
         Logger.info(
-            `Received status update for channel ${channelToken} for Mollie order ${orderId}`,
+            `Received status update for channel ${ctx.channel.token} for Mollie order ${orderId}`,
             loggerCtx,
         );
         const paymentMethod = await this.paymentMethodService.findOne(ctx, paymentMethodId);
@@ -218,12 +217,12 @@ export class MollieService {
         const apiKey = paymentMethod.handler.args.find(a => a.name === 'apiKey')?.value;
         const autoCapture = paymentMethod.handler.args.find(a => a.name === 'autoCapture')?.value === 'true';
         if (!apiKey) {
-            throw Error(`No apiKey found for payment ${paymentMethod.id} for channel ${channelToken}`);
+            throw Error(`No apiKey found for payment ${paymentMethod.id} for channel ${ctx.channel.token}`);
         }
         const client = createMollieClient({ apiKey });
         const mollieOrder = await client.orders.get(orderId);
         Logger.info(
-            `Processing status '${mollieOrder.status}' for order ${mollieOrder.orderNumber} for channel ${channelToken} for Mollie order ${orderId}`,
+            `Processing status '${mollieOrder.status}' for order ${mollieOrder.orderNumber} for channel ${ctx.channel.token} for Mollie order ${orderId}`,
             loggerCtx,
         );
         let order = await this.orderService.findOneByCode(ctx, mollieOrder.orderNumber, ['payments']);
@@ -260,6 +259,11 @@ export class MollieService {
         if (order.state === 'PaymentAuthorized' && mollieOrder.status === OrderStatus.completed) {
             return this.settleExistingPayment(ctx, order, mollieOrder.id);
         }
+        if (autoCapture && mollieOrder.status === OrderStatus.completed) {
+            // When autocapture is enabled, we should not handle the completed status from Mollie,
+            // because the order will be transitioned to PaymentSettled during auto capture
+            return;
+        }
         // Any other combination of Mollie status and Vendure status indicates something is wrong.
         throw Error(
             `Unhandled incoming Mollie status '${mollieOrder.status}' for order ${order.code} with status '${order.state}'`,

+ 3 - 0
packages/sentry-plugin/.gitignore

@@ -0,0 +1,3 @@
+yarn-error.log
+lib
+e2e/__data__/*.sqlite

+ 7 - 0
packages/sentry-plugin/README.md

@@ -0,0 +1,7 @@
+# Vendure Sentry Plugin
+
+Integrates your Vendure server with the [Sentry](https://sentry.io/) application monitoring service.
+
+`npm install @vendure/sentry-plugin`
+
+For documentation, see [docs.vendure.io/reference/core-plugins/sentry-plugin/](https://docs.vendure.io/reference/core-plugins/sentry-plugin/)

+ 4 - 0
packages/sentry-plugin/index.ts

@@ -0,0 +1,4 @@
+export * from './src/sentry-plugin';
+export * from './src/sentry.service';
+export * from './src/types';
+export * from './src/constants';

+ 28 - 0
packages/sentry-plugin/package.json

@@ -0,0 +1,28 @@
+{
+    "name": "@vendure/sentry-plugin",
+    "version": "2.1.5",
+    "license": "MIT",
+    "main": "lib/index.js",
+    "types": "lib/index.d.ts",
+    "files": [
+        "lib/**/*"
+    ],
+    "scripts": {
+        "watch": "tsc -p ./tsconfig.build.json --watch",
+        "build": "rimraf lib && tsc -p ./tsconfig.build.json",
+        "lint": "eslint --fix ."
+    },
+    "homepage": "https://www.vendure.io",
+    "funding": "https://github.com/sponsors/michaelbromley",
+    "publishConfig": {
+        "access": "public"
+    },
+    "peerDependencies": {
+        "@sentry/node": "^7.85.0"
+    },
+    "devDependencies": {
+        "@sentry/node": "^7.85.0",
+        "@vendure/common": "^2.1.5",
+        "@vendure/core": "^2.1.5"
+    }
+}

+ 32 - 0
packages/sentry-plugin/src/api/admin-test.resolver.ts

@@ -0,0 +1,32 @@
+import { Args, Mutation, Resolver } from '@nestjs/graphql';
+import { Allow, Permission, UserInputError } from '@vendure/core';
+
+import { SentryService } from '../sentry.service';
+import { ErrorTestService } from './error-test.service';
+
+declare const a: number;
+
+@Resolver()
+export class SentryAdminTestResolver {
+    constructor(private sentryService: SentryService, private errorTestService: ErrorTestService) {}
+
+    @Allow(Permission.SuperAdmin)
+    @Mutation()
+    async createTestError(@Args() args: { errorType: string }) {
+        switch (args.errorType) {
+            case 'UNCAUGHT_ERROR':
+                return a / 10;
+            case 'THROWN_ERROR':
+                throw new UserInputError('SentryPlugin Test Error');
+            case 'CAPTURED_ERROR':
+                this.sentryService.captureException(new Error('SentryPlugin Direct error'));
+                return true;
+            case 'CAPTURED_MESSAGE':
+                this.sentryService.captureMessage('Captured message');
+                return true;
+            case 'DATABASE_ERROR':
+                await this.errorTestService.createDatabaseError();
+                return true;
+        }
+    }
+}

+ 14 - 0
packages/sentry-plugin/src/api/api-extensions.ts

@@ -0,0 +1,14 @@
+import gql from 'graphql-tag';
+
+export const testApiExtensions = gql`
+    enum TestErrorType {
+        UNCAUGHT_ERROR
+        THROWN_ERROR
+        CAPTURED_ERROR
+        CAPTURED_MESSAGE
+        DATABASE_ERROR
+    }
+    extend type Mutation {
+        createTestError(errorType: TestErrorType!): Boolean
+    }
+`;

+ 11 - 0
packages/sentry-plugin/src/api/error-test.service.ts

@@ -0,0 +1,11 @@
+import { Injectable } from '@nestjs/common';
+import { TransactionalConnection } from '@vendure/core';
+
+@Injectable()
+export class ErrorTestService {
+    constructor(private connection: TransactionalConnection) {}
+
+    createDatabaseError() {
+        return this.connection.rawConnection.query('SELECT * FROM non_existent_table');
+    }
+}

+ 3 - 0
packages/sentry-plugin/src/constants.ts

@@ -0,0 +1,3 @@
+export const SENTRY_PLUGIN_OPTIONS = 'SENTRY_PLUGIN_OPTIONS';
+export const SENTRY_TRANSACTION_KEY = 'SENTRY_PLUGIN_TRANSACTION';
+export const loggerCtx = 'SentryPlugin';

+ 58 - 0
packages/sentry-plugin/src/sentry-apollo-plugin.ts

@@ -0,0 +1,58 @@
+/* eslint-disable @typescript-eslint/require-await */
+import {
+    ApolloServerPlugin,
+    GraphQLRequestListener,
+    GraphQLRequestContext,
+    GraphQLRequestContextDidEncounterErrors,
+} from '@apollo/server';
+import { Transaction, setContext } from '@sentry/node';
+
+import { SENTRY_TRANSACTION_KEY } from './constants';
+
+/**
+ * Based on https://github.com/ntegral/nestjs-sentry/issues/97#issuecomment-1252446807
+ */
+export class SentryApolloPlugin implements ApolloServerPlugin {
+    constructor(private options: { enableTracing: boolean }) {}
+
+    async requestDidStart({
+        request,
+        contextValue,
+    }: GraphQLRequestContext<any>): Promise<GraphQLRequestListener<any>> {
+        const { enableTracing } = this.options;
+        const transaction: Transaction | undefined = contextValue.req[SENTRY_TRANSACTION_KEY];
+        if (request.operationName) {
+            if (enableTracing) {
+                // set the transaction Name if we have named queries
+                transaction?.setName(request.operationName);
+            }
+            setContext('Graphql Request', {
+                operation_name: request.operationName,
+                variables: request.variables,
+            });
+        }
+
+        return {
+            // hook for transaction finished
+            async willSendResponse(context) {
+                transaction?.finish();
+            },
+            async executionDidStart() {
+                return {
+                    // hook for each new resolver
+                    willResolveField({ info }) {
+                        if (enableTracing) {
+                            const span = transaction?.startChild({
+                                op: 'resolver',
+                                description: `${info.parentType.name}.${info.fieldName}`,
+                            });
+                            return () => {
+                                span?.finish();
+                            };
+                        }
+                    },
+                };
+            },
+        };
+    }
+}

+ 25 - 0
packages/sentry-plugin/src/sentry-context.middleware.ts

@@ -0,0 +1,25 @@
+import { Inject, Injectable, NestMiddleware } from '@nestjs/common';
+import { Request, Response, NextFunction } from 'express';
+
+import { SENTRY_PLUGIN_OPTIONS, SENTRY_TRANSACTION_KEY } from './constants';
+import { SentryService } from './sentry.service';
+import { SentryPluginOptions } from './types';
+
+@Injectable()
+export class SentryContextMiddleware implements NestMiddleware {
+    constructor(
+        @Inject(SENTRY_PLUGIN_OPTIONS) private options: SentryPluginOptions,
+        private sentryService: SentryService,
+    ) {}
+
+    use(req: Request, res: Response, next: NextFunction) {
+        if (this.options.enableTracing) {
+            const transaction = this.sentryService.startTransaction({
+                op: 'resolver',
+                name: `GraphQLTransaction`,
+            });
+            req[SENTRY_TRANSACTION_KEY] = transaction;
+        }
+        next();
+    }
+}

+ 146 - 0
packages/sentry-plugin/src/sentry-plugin.ts

@@ -0,0 +1,146 @@
+import { MiddlewareConsumer, NestModule } from '@nestjs/common';
+import { APP_FILTER } from '@nestjs/core';
+import { PluginCommonModule, VendurePlugin } from '@vendure/core';
+
+import { SentryAdminTestResolver } from './api/admin-test.resolver';
+import { testApiExtensions } from './api/api-extensions';
+import { ErrorTestService } from './api/error-test.service';
+import { SENTRY_PLUGIN_OPTIONS } from './constants';
+import { SentryApolloPlugin } from './sentry-apollo-plugin';
+import { SentryContextMiddleware } from './sentry-context.middleware';
+import { SentryExceptionsFilter } from './sentry.filter';
+import { SentryService } from './sentry.service';
+import { SentryPluginOptions } from './types';
+
+const SentryOptionsProvider = {
+    provide: SENTRY_PLUGIN_OPTIONS,
+    useFactory: () => SentryPlugin.options,
+};
+
+/**
+ * @description
+ * This plugin integrates the [Sentry](https://sentry.io) error tracking & performance monitoring
+ * service with your Vendure server. In addition to capturing errors, it also provides built-in
+ * support for [tracing](https://docs.sentry.io/product/sentry-basics/concepts/tracing/) as well as
+ * enriching your Sentry events with additional context about the request.
+ *
+ * ## Pre-requisites
+ *
+ * This plugin depends on access to Sentry, which can be self-hosted or used as a cloud service.
+ *
+ * If using the hosted SaaS option, you must have a Sentry account and a project set up ([sign up here](https://sentry.io/signup/)). When setting up your project,
+ * select the "Node.js" platform and no framework.
+ *
+ * Once set up, you will be given a [Data Source Name (DSN)](https://docs.sentry.io/product/sentry-basics/concepts/dsn-explainer/)
+ * which you will need to provide to the plugin.
+ *
+ * ## Installation
+ *
+ * Install this plugin as well as the `@sentry/node` package:
+ *
+ * ```sh
+ * npm install --save \@vendure/sentry-plugin \@sentry/node
+ * ```
+ *
+ * ## Configuration
+ *
+ * Before using the plugin, you must configure it with the DSN provided by Sentry:
+ *
+ * ```ts
+ * import { VendureConfig } from '\@vendure/core';
+ * import { SentryPlugin } from '\@vendure/sentry-plugin';
+ *
+ * export const config: VendureConfig = {
+ *     // ...
+ *     plugins: [
+ *         // ...
+ *         // highlight-start
+ *         SentryPlugin.init({
+ *             dsn: process.env.SENTRY_DSN,
+ *             // Optional configuration
+ *             includeErrorTestMutation: true,
+ *             enableTracing: true,
+ *             // you can also pass in any of the options from \@sentry/node
+ *             // for instance:
+ *             tracesSampleRate: 1.0,
+ *         }),
+ *         // highlight-end
+ *     ],
+ * };
+ *```
+ *
+ * ## Tracing
+ *
+ * This plugin includes built-in support for [tracing](https://docs.sentry.io/product/sentry-basics/concepts/tracing/), which allows you to see the performance of your
+ * GraphQL resolvers in the Sentry dashboard. To enable tracing, set the `enableTracing` option to `true` as shown above.
+ *
+ * ## Instrumenting your own code
+ *
+ * You may want to add your own custom spans to your code. To do so, you can use the `Sentry` object
+ * just as you would in any Node application. For example:
+ *
+ * ```ts
+ * import * as Sentry from "\@sentry/node";
+ *
+ * export class MyService {
+ *     async myMethod() {
+ *          Sentry.setContext('My Custom Context,{
+ *              key: 'value',
+ *          });
+ *     }
+ * }
+ * ```
+ *
+ * ## Error test mutation
+ *
+ * To test whether your Sentry configuration is working correctly, you can set the `includeErrorTestMutation` option to `true`. This will add a mutation to the Admin API
+ * which will throw an error of the type specified in the `errorType` argument. For example:
+ *
+ * ```graphql
+ * mutation CreateTestError {
+ *     createTestError(errorType: DATABASE_ERROR)
+ * }
+ * ```
+ *
+ * You should then be able to see the error in your Sentry dashboard (it may take a couple of minutes to appear).
+ *
+ * @docsCategory core plugins/SentryPlugin
+ */
+@VendurePlugin({
+    imports: [PluginCommonModule],
+    providers: [
+        SentryOptionsProvider,
+        SentryService,
+        ErrorTestService,
+        {
+            provide: APP_FILTER,
+            useClass: SentryExceptionsFilter,
+        },
+    ],
+    configuration: config => {
+        config.apiOptions.apolloServerPlugins.push(
+            new SentryApolloPlugin({
+                enableTracing: !!SentryPlugin.options.enableTracing,
+            }),
+        );
+        return config;
+    },
+    adminApiExtensions: {
+        schema: () => (SentryPlugin.options.includeErrorTestMutation ? testApiExtensions : undefined),
+        resolvers: () => (SentryPlugin.options.includeErrorTestMutation ? [SentryAdminTestResolver] : []),
+    },
+    exports: [SentryService],
+    compatibility: '^2.0.0',
+})
+export class SentryPlugin implements NestModule {
+    static options: SentryPluginOptions = {} as any;
+
+    configure(consumer: MiddlewareConsumer): any {
+        consumer.apply(SentryContextMiddleware).forRoutes('*');
+    }
+
+    static init(options: SentryPluginOptions) {
+        this.options = options;
+        return this;
+    }
+}

+ 27 - 0
packages/sentry-plugin/src/sentry.filter.ts

@@ -0,0 +1,27 @@
+import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
+import { Catch, ExecutionContext } from '@nestjs/common';
+import { GqlContextType, GqlExecutionContext } from '@nestjs/graphql';
+import { setContext } from '@sentry/node';
+
+import { SentryService } from './sentry.service';
+
+@Catch()
+export class SentryExceptionsFilter implements ExceptionFilter {
+    constructor(private readonly sentryService: SentryService) {}
+
+    catch(exception: Error, host: ArgumentsHost): void {
+        if (host.getType<GqlContextType>() === 'graphql') {
+            const gqlContext = GqlExecutionContext.create(host as ExecutionContext);
+            const info = gqlContext.getInfo();
+            setContext('GraphQL Error Context', {
+                fieldName: info.fieldName,
+                path: info.path,
+            });
+        }
+        const variables = (exception as any).variables;
+        if (variables) {
+            setContext('GraphQL Error Variables', variables);
+        }
+        this.sentryService.captureException(exception);
+    }
+}

+ 40 - 0
packages/sentry-plugin/src/sentry.service.ts

@@ -0,0 +1,40 @@
+import { Inject, Injectable, OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';
+import * as Sentry from '@sentry/node';
+import { CaptureContext, TransactionContext } from '@sentry/types';
+
+import { SENTRY_PLUGIN_OPTIONS } from './constants';
+import { SentryPluginOptions } from './types';
+
+@Injectable()
+export class SentryService implements OnApplicationBootstrap, OnApplicationShutdown {
+    constructor(@Inject(SENTRY_PLUGIN_OPTIONS) private options: SentryPluginOptions) {}
+
+    onApplicationBootstrap(): any {
+        const integrations = this.options.integrations ?? [
+            new Sentry.Integrations.Http({ tracing: true }),
+            ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(),
+        ];
+        Sentry.init({
+            ...this.options,
+            tracesSampleRate: this.options.tracesSampleRate ?? 1.0,
+            integrations,
+            dsn: this.options.dsn,
+        });
+    }
+
+    onApplicationShutdown() {
+        return Sentry.close();
+    }
+
+    captureException(exception: Error) {
+        Sentry.captureException(exception);
+    }
+
+    captureMessage(message: string, captureContext?: CaptureContext) {
+        Sentry.captureMessage(message, captureContext);
+    }
+
+    startTransaction(context: TransactionContext) {
+        return Sentry.startTransaction(context);
+    }
+}

+ 26 - 0
packages/sentry-plugin/src/types.ts

@@ -0,0 +1,26 @@
+import { Transaction } from '@sentry/node';
+import { NodeOptions } from '@sentry/node/types/types';
+
+import { SENTRY_TRANSACTION_KEY } from './constants';
+
+/**
+ * @description
+ * Configuration options for the {@link SentryPlugin}.
+ *
+ * @docsCategory core plugins/SentryPlugin
+ */
+export interface SentryPluginOptions extends NodeOptions {
+    /**
+     * @description
+     * The [Data Source Name](https://docs.sentry.io/product/sentry-basics/concepts/dsn-explainer/) for your Sentry instance.
+     */
+    dsn: string;
+    enableTracing?: boolean;
+    includeErrorTestMutation?: boolean;
+}
+
+declare module 'express' {
+    interface Request {
+        [SENTRY_TRANSACTION_KEY]: Transaction | undefined;
+    }
+}

+ 9 - 0
packages/sentry-plugin/tsconfig.build.json

@@ -0,0 +1,9 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./lib"
+  },
+  "files": [
+    "./index.ts"
+  ]
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов