Michael Bromley 1 год назад
Родитель
Сommit
b39bf21ac1
100 измененных файлов с 2677 добавлено и 224 удалено
  1. 7 0
      .github/workflows/build_and_test.yml
  2. 0 3
      .graphqlconfig
  3. 21 0
      .vscode/launch.json
  4. 67 0
      CHANGELOG.md
  5. 71 0
      CHANGELOG_NEXT.md
  6. 5 1
      README.md
  7. 12 0
      docs/docs/guides/core-concepts/orders/index.md
  8. 4 1
      docs/docs/guides/deployment/horizontal-scaling.md
  9. 4 0
      docs/docs/guides/deployment/production-configuration/index.md
  10. BIN
      docs/docs/guides/developer-guide/cache/cache-service.webp
  11. 206 0
      docs/docs/guides/developer-guide/cache/index.mdx
  12. 106 0
      docs/docs/guides/developer-guide/custom-fields/index.md
  13. 29 0
      docs/docs/guides/developer-guide/security/index.md
  14. 14 14
      docs/docs/reference/admin-ui-api/alerts/alert-config.md
  15. 1 1
      docs/docs/reference/admin-ui-api/alerts/alert-context.md
  16. 1 1
      docs/docs/reference/admin-ui-api/components/asset-picker-dialog-component.md
  17. 14 8
      docs/docs/reference/admin-ui-api/components/data-table2component.md
  18. 99 15
      docs/docs/reference/admin-ui-api/custom-input-components/default-inputs.md
  19. 15 1
      docs/docs/reference/admin-ui-api/list-detail-views/base-list-component.md
  20. 13 1
      docs/docs/reference/admin-ui-api/list-detail-views/typed-base-list-component.md
  21. 20 13
      docs/docs/reference/admin-ui-api/services/data-service.md
  22. 1 1
      docs/docs/reference/core-plugins/admin-ui-plugin/admin-ui-plugin-options.md
  23. 1 1
      docs/docs/reference/core-plugins/admin-ui-plugin/index.md
  24. 13 1
      docs/docs/reference/core-plugins/asset-server-plugin/asset-server-options.md
  25. 1 1
      docs/docs/reference/core-plugins/asset-server-plugin/cache-config.md
  26. 1 1
      docs/docs/reference/core-plugins/asset-server-plugin/hashed-asset-naming-strategy.md
  27. 1 1
      docs/docs/reference/core-plugins/asset-server-plugin/image-transform-mode.md
  28. 1 1
      docs/docs/reference/core-plugins/asset-server-plugin/image-transform-preset.md
  29. 148 0
      docs/docs/reference/core-plugins/asset-server-plugin/image-transform-strategy.md
  30. 32 5
      docs/docs/reference/core-plugins/asset-server-plugin/index.md
  31. 1 1
      docs/docs/reference/core-plugins/asset-server-plugin/local-asset-storage-strategy.md
  32. 122 0
      docs/docs/reference/core-plugins/asset-server-plugin/preset-only-strategy.md
  33. 3 3
      docs/docs/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy.md
  34. 2 2
      docs/docs/reference/core-plugins/asset-server-plugin/sharp-asset-preview-strategy.md
  35. 1 1
      docs/docs/reference/core-plugins/elasticsearch-plugin/index.md
  36. 1 1
      docs/docs/reference/core-plugins/email-plugin/email-event-handler-with-async-data.md
  37. 27 1
      docs/docs/reference/core-plugins/email-plugin/email-event-handler.md
  38. 28 6
      docs/docs/reference/core-plugins/email-plugin/email-plugin-types.md
  39. 2 2
      docs/docs/reference/core-plugins/email-plugin/email-send-event.md
  40. 2 2
      docs/docs/reference/core-plugins/payments-plugin/mollie-plugin.md
  41. 34 1
      docs/docs/reference/core-plugins/payments-plugin/stripe-plugin.md
  42. 1 0
      docs/docs/reference/graphql-api/admin/enums.md
  43. 5 9
      docs/docs/reference/graphql-api/admin/input-types.md
  44. 24 0
      docs/docs/reference/graphql-api/admin/mutations.md
  45. 243 4
      docs/docs/reference/graphql-api/admin/object-types.md
  46. 1 0
      docs/docs/reference/graphql-api/shop/enums.md
  47. 28 4
      docs/docs/reference/graphql-api/shop/mutations.md
  48. 214 3
      docs/docs/reference/graphql-api/shop/object-types.md
  49. 1 1
      docs/docs/reference/typescript-api/assets/asset-options.md
  50. 13 12
      docs/docs/reference/typescript-api/auth/auth-options.md
  51. 1 1
      docs/docs/reference/typescript-api/auth/cookie-options.md
  52. 77 0
      docs/docs/reference/typescript-api/auth/default-session-cache-strategy.md
  53. 12 0
      docs/docs/reference/typescript-api/auth/external-authentication-service.md
  54. 14 5
      docs/docs/reference/typescript-api/auth/session-cache-strategy.md
  55. 1 1
      docs/docs/reference/typescript-api/auth/superadmin-credentials.md
  56. 48 0
      docs/docs/reference/typescript-api/cache/cache-config.md
  57. 80 0
      docs/docs/reference/typescript-api/cache/cache-service.md
  58. 62 0
      docs/docs/reference/typescript-api/cache/cache-strategy.md
  59. 44 0
      docs/docs/reference/typescript-api/cache/default-cache-plugin.md
  60. 83 0
      docs/docs/reference/typescript-api/cache/index.md
  61. 45 0
      docs/docs/reference/typescript-api/cache/redis-cache-plugin.md
  62. 73 0
      docs/docs/reference/typescript-api/cache/redis-cache-strategy.md
  63. 58 0
      docs/docs/reference/typescript-api/cache/request-context-cache-service.md
  64. 90 0
      docs/docs/reference/typescript-api/cache/sql-cache-strategy.md
  65. 1 1
      docs/docs/reference/typescript-api/common/admin-ui/admin-ui-app-config.md
  66. 1 1
      docs/docs/reference/typescript-api/common/admin-ui/admin-ui-app-dev-mode-config.md
  67. 1 1
      docs/docs/reference/typescript-api/common/admin-ui/admin-ui-config.md
  68. 46 2
      docs/docs/reference/typescript-api/common/bootstrap.md
  69. 1 1
      docs/docs/reference/typescript-api/common/currency-code.md
  70. 1 1
      docs/docs/reference/typescript-api/common/entity-relation-paths.md
  71. 1 1
      docs/docs/reference/typescript-api/common/job-state.md
  72. 1 1
      docs/docs/reference/typescript-api/common/language-code.md
  73. 1 1
      docs/docs/reference/typescript-api/common/permission.md
  74. 1 1
      docs/docs/reference/typescript-api/configurable-operation-def/config-arg-type.md
  75. 2 1
      docs/docs/reference/typescript-api/configurable-operation-def/default-form-component-id.md
  76. 16 10
      docs/docs/reference/typescript-api/configurable-operation-def/default-form-config-hash.md
  77. 1 1
      docs/docs/reference/typescript-api/configuration/api-options.md
  78. 1 1
      docs/docs/reference/typescript-api/configuration/default-config.md
  79. 1 1
      docs/docs/reference/typescript-api/configuration/entity-options.md
  80. 1 1
      docs/docs/reference/typescript-api/configuration/runtime-vendure-config.md
  81. 8 1
      docs/docs/reference/typescript-api/configuration/system-options.md
  82. 1 1
      docs/docs/reference/typescript-api/configuration/vendure-config.md
  83. 2 1
      docs/docs/reference/typescript-api/custom-fields/custom-field-config.md
  84. 4 2
      docs/docs/reference/typescript-api/custom-fields/custom-field-type.md
  85. 8 1
      docs/docs/reference/typescript-api/custom-fields/index.md
  86. 25 0
      docs/docs/reference/typescript-api/custom-fields/struct-custom-field-config.md
  87. 45 0
      docs/docs/reference/typescript-api/custom-fields/struct-field-config.md
  88. 1 1
      docs/docs/reference/typescript-api/custom-fields/typed-custom-single-field-config.md
  89. 54 45
      docs/docs/reference/typescript-api/data-access/transactional-connection.md
  90. 1 1
      docs/docs/reference/typescript-api/default-search-plugin/index.md
  91. 12 2
      docs/docs/reference/typescript-api/entities/history-entry.md
  92. 7 0
      docs/docs/reference/typescript-api/entities/order-line.md
  93. 12 2
      docs/docs/reference/typescript-api/entities/payment.md
  94. 14 0
      docs/docs/reference/typescript-api/entities/product-variant.md
  95. 8 1
      docs/docs/reference/typescript-api/entities/product.md
  96. 2 2
      docs/docs/reference/typescript-api/entities/promotion.md
  97. 12 2
      docs/docs/reference/typescript-api/entities/refund.md
  98. 11 1
      docs/docs/reference/typescript-api/entities/session.md
  99. 12 2
      docs/docs/reference/typescript-api/entities/shipping-line.md
  100. 12 2
      docs/docs/reference/typescript-api/entities/stock-level.md

+ 7 - 0
.github/workflows/build_and_test.yml

@@ -98,6 +98,12 @@ jobs:
         ports:
           - 9200
         options: --health-cmd="curl --silent --fail localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=3
+      redis:
+        image: bitnami/redis:7.4.1
+        env:
+          ALLOW_EMPTY_PASSWORD: yes
+        ports:
+          - 6379
     strategy:
       fail-fast: false
       matrix:
@@ -121,5 +127,6 @@ jobs:
           E2E_MARIADB_PORT: ${{ job.services.mariadb.ports['3306'] }}
           E2E_POSTGRES_PORT: ${{ job.services.postgres.ports['5432'] }}
           E2E_ELASTIC_PORT: ${{ job.services.elastic.ports['9200'] }}
+          E2E_REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
           DB: ${{ matrix.db }}
         run: npm run e2e

+ 0 - 3
.graphqlconfig

@@ -1,3 +0,0 @@
-{
-    "schema": "./schema-admin.json"
-}

+ 21 - 0
.vscode/launch.json

@@ -0,0 +1,21 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "node",
+            "request": "launch",
+            "name": "Debug Dev Server",
+            "program": "${workspaceFolder}/packages/dev-server/index.ts",
+            "outFiles": ["${workspaceFolder}/**/*.js"],
+            "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/ts-node",
+            "runtimeArgs": ["--transpile-only"],
+            "skipFiles": ["<node_internals>/**", "node_modules/**"],
+            "cwd": "${workspaceRoot}",
+            "resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"],
+            "console": "integratedTerminal"
+        }
+    ]
+}

+ 67 - 0
CHANGELOG.md

@@ -1,3 +1,70 @@
+## 3.1.0-next.4 (2024-12-03)
+
+
+#### Features
+
+* **admin-ui** Improve facet selector with the code (#3175) ([35892a5](https://github.com/vendure-ecommerce/vendure/commit/35892a5)), closes [#3175](https://github.com/vendure-ecommerce/vendure/issues/3175)
+* **admin-ui** Integrate Vendure Assets Picker with ProseMirror and add single image selection (#3033) ([18e5ab9](https://github.com/vendure-ecommerce/vendure/commit/18e5ab9)), closes [#3033](https://github.com/vendure-ecommerce/vendure/issues/3033)
+* **admin-ui** Set default shipping and billing address for draft orders (#3196) ([dec72e7](https://github.com/vendure-ecommerce/vendure/commit/dec72e7)), closes [#3196](https://github.com/vendure-ecommerce/vendure/issues/3196) [#2342](https://github.com/vendure-ecommerce/vendure/issues/2342)
+* **asset-server-plugin** Implement ImageTransformStrategy for improved control over image transformations (#3240) ([dde738d](https://github.com/vendure-ecommerce/vendure/commit/dde738d)), closes [#3240](https://github.com/vendure-ecommerce/vendure/issues/3240) [#3040](https://github.com/vendure-ecommerce/vendure/issues/3040)
+* **core** Add custom field support for Payment, Refund, ShippingLine, StockLevel, StockMovement, Session, HistoryEntry ([1167102](https://github.com/vendure-ecommerce/vendure/commit/1167102)), closes [#3044](https://github.com/vendure-ecommerce/vendure/issues/3044)
+* **core** Add means to selectively ignore plugin compatibility errors ([e362475](https://github.com/vendure-ecommerce/vendure/commit/e362475)), closes [#2958](https://github.com/vendure-ecommerce/vendure/issues/2958)
+* **core** Add OrderInterceptor API (#3233) ([7706e35](https://github.com/vendure-ecommerce/vendure/commit/7706e35)), closes [#3233](https://github.com/vendure-ecommerce/vendure/issues/3233) [#2123](https://github.com/vendure-ecommerce/vendure/issues/2123)
+* **core** Add replicationMode for ctx and getRepository (#2746) ([60cdae3](https://github.com/vendure-ecommerce/vendure/commit/60cdae3)), closes [#2746](https://github.com/vendure-ecommerce/vendure/issues/2746)
+* **core** Add StockLocationEvent ([5cff832](https://github.com/vendure-ecommerce/vendure/commit/5cff832))
+* **core** Allow unsetting of shipping and billing addresses (#3185) ([e0f2118](https://github.com/vendure-ecommerce/vendure/commit/e0f2118)), closes [#3185](https://github.com/vendure-ecommerce/vendure/issues/3185)
+* **core** Create a user from external authentication (#3005) ([bb28d70](https://github.com/vendure-ecommerce/vendure/commit/bb28d70)), closes [#3005](https://github.com/vendure-ecommerce/vendure/issues/3005)
+* **core** Create Cache class for a more convenient caching API ([a7ceb74](https://github.com/vendure-ecommerce/vendure/commit/a7ceb74)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+* **core** Create PromotionLineAction (#2971) ([0ff8288](https://github.com/vendure-ecommerce/vendure/commit/0ff8288)), closes [#2971](https://github.com/vendure-ecommerce/vendure/issues/2971) [#2956](https://github.com/vendure-ecommerce/vendure/issues/2956)
+* **core** Enable hydration of translations relation ([84710d5](https://github.com/vendure-ecommerce/vendure/commit/84710d5))
+* **core** Implement bulk versions of order operations ([8d65219](https://github.com/vendure-ecommerce/vendure/commit/8d65219))
+* **core** Implement cache invalidation by tags ([382e314](https://github.com/vendure-ecommerce/vendure/commit/382e314)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+* **core** Implement CacheStrategy and CacheService ([489c9c0](https://github.com/vendure-ecommerce/vendure/commit/489c9c0)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+* **core** Implement caching for FacetValueChecker ([3603b11](https://github.com/vendure-ecommerce/vendure/commit/3603b11)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+* **core** Implement redis cache plugin ([9d99593](https://github.com/vendure-ecommerce/vendure/commit/9d99593)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+* **core** Implement support for struct custom field type (#3178) ([dffd123](https://github.com/vendure-ecommerce/vendure/commit/dffd123)), closes [#3178](https://github.com/vendure-ecommerce/vendure/issues/3178)
+* **core** Include address-based tax zone strategy (#3198) ([5547128](https://github.com/vendure-ecommerce/vendure/commit/5547128)), closes [#3198](https://github.com/vendure-ecommerce/vendure/issues/3198)
+* **core** Initial DefaultCachePlugin implementation ([9c2433f](https://github.com/vendure-ecommerce/vendure/commit/9c2433f)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+* **core** Introduce new default MultiChannelStockLocationStrategy ([62090c9](https://github.com/vendure-ecommerce/vendure/commit/62090c9)), closes [#2356](https://github.com/vendure-ecommerce/vendure/issues/2356)
+* **email-plugin** Allow specifying metadata for EmailSendEvent (#2963) ([ac0baf9](https://github.com/vendure-ecommerce/vendure/commit/ac0baf9)), closes [#2963](https://github.com/vendure-ecommerce/vendure/issues/2963)
+* **payments-plugin** Add option to StripePlugin to handle payment intent that doesn't have Vendure metadata (#3250) ([ec934dd](https://github.com/vendure-ecommerce/vendure/commit/ec934dd)), closes [#3250](https://github.com/vendure-ecommerce/vendure/issues/3250)
+* **payments-plugin** Allow additional options on Stripe payment intent creation (#3194) ([3f66216](https://github.com/vendure-ecommerce/vendure/commit/3f66216)), closes [#3194](https://github.com/vendure-ecommerce/vendure/issues/3194)
+
+#### Fixes
+
+* **admin-ui** Only update facetValueIds if changed ([8f22ef8](https://github.com/vendure-ecommerce/vendure/commit/8f22ef8))
+* **core** Fix error in FacetValueChecker on nullish input ([44aad49](https://github.com/vendure-ecommerce/vendure/commit/44aad49))
+* **core** Fix error with new session cache when seeding data ([508f797](https://github.com/vendure-ecommerce/vendure/commit/508f797))
+* **core** Update DefaultMoneyStrategy.round() Logic (#3023) ([f43c204](https://github.com/vendure-ecommerce/vendure/commit/f43c204)), closes [#3023](https://github.com/vendure-ecommerce/vendure/issues/3023)
+
+#### Perf
+
+* **core** Improve efficiency of order merge ([0a60ee9](https://github.com/vendure-ecommerce/vendure/commit/0a60ee9))
+
+
+### BREAKING CHANGE
+
+* A technically breaking change in this release is that we have corrected the default rounding logic:
+
+```ts
+// v3.0
+return Math.round(value) * quantity;
+
+// v3.1
+return Math.round(value * quantity);
+```
+
+This makes order totals calculations much more "correct" as per most people's expectations, but it pointed out as a technically breaking change in the unlikely event that you rely on the old, less correct method of rounding.
+* If you are using the `FacetValueChecker` utility class, you should
+update your code to get it via the `Injector` rather than directly instantiating it.
+
+Existing code _will_ still work without changes, but by updating you will see improved
+performance due to new caching techniques.
+
+```diff
+- facetValueChecker = new FacetValueChecker(injector.get(TransactionalConnection));
++ facetValueChecker = injector.get(FacetValueChecker);
+```
 ## <small>3.0.8 (2024-12-03)</small>
 
 

+ 71 - 0
CHANGELOG_NEXT.md

@@ -1,3 +1,74 @@
+## 3.1.0-next.3 (2024-10-04)
+
+This release includes all fixes from v3.0.4, as well as the following new features:
+
+#### Features
+
+* **core** Enhance AuthOptions.sessionCacheTTL by allowing string expressions (zeit/ms) (#3088)
+
+## 3.1.0-next.2 (2024-09-11)
+
+#### Fixes
+
+* **core** Fix regression in correctly setting OrderLine.featuredAsset ([7d070f2](https://github.com/vendure-ecommerce/vendure/commit/7d070f2))
+* **core** Fix error in FacetValueChecker on nullish input ([44aad49](https://github.com/vendure-ecommerce/vendure/commit/44aad49))
+
+
+## 3.1.0-next.1 (2024-09-10)
+
+
+#### Features
+
+* **core** Implement CacheStrategy and CacheService ([489c9c0](https://github.com/vendure-ecommerce/vendure/commit/489c9c0)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+* **core** Implement caching for FacetValueChecker ([3603b11](https://github.com/vendure-ecommerce/vendure/commit/3603b11)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+* **core** Initial DefaultCachePlugin implementation ([9c2433f](https://github.com/vendure-ecommerce/vendure/commit/9c2433f)), closes [#3043](https://github.com/vendure-ecommerce/vendure/issues/3043)
+
+#### Fixes
+
+* **admin-ui** Only update facetValueIds if changed ([8f22ef8](https://github.com/vendure-ecommerce/vendure/commit/8f22ef8))
+
+
+### BREAKING CHANGE
+* If you are using the `FacetValueChecker` utility class, you should
+  update your code to get it via the `Injector` rather than directly instantiating it.
+
+Existing code _will_ still work without changes, but by updating you will see improved
+performance due to new caching techniques.
+
+```diff
+- facetValueChecker = new FacetValueChecker(injector.get(TransactionalConnection));
++ facetValueChecker = injector.get(FacetValueChecker);
+```
+
+## 3.1.0-next.0 (2024-08-21)
+
+
+#### Fixes
+
+* **core** Update DefaultMoneyStrategy.round() Logic (#3023) ([f43c204](https://github.com/vendure-ecommerce/vendure/commit/f43c204)), closes [#3023](https://github.com/vendure-ecommerce/vendure/issues/3023)
+
+#### Features
+
+* **core** Add replicationMode for ctx and getRepository (#2746) ([60cdae3](https://github.com/vendure-ecommerce/vendure/commit/60cdae3)), closes [#2746](https://github.com/vendure-ecommerce/vendure/issues/2746)
+* **core** Create a user from external authentication (#3005) ([bb28d70](https://github.com/vendure-ecommerce/vendure/commit/bb28d70)), closes [#3005](https://github.com/vendure-ecommerce/vendure/issues/3005)
+* **core** Create PromotionLineAction (#2971) ([0ff8288](https://github.com/vendure-ecommerce/vendure/commit/0ff8288)), closes [#2971](https://github.com/vendure-ecommerce/vendure/issues/2971) [#2956](https://github.com/vendure-ecommerce/vendure/issues/2956)
+* **email-plugin** Allow specifying metadata for EmailSendEvent (#2963) ([ac0baf9](https://github.com/vendure-ecommerce/vendure/commit/ac0baf9)), closes [#2963](https://github.com/vendure-ecommerce/vendure/issues/2963)
+
+
+### BREAKING CHANGE
+
+* A technically breaking change in this release is that we have corrected the default rounding logic:
+
+```ts
+// v3.0
+return Math.round(value) * quantity;
+
+// v3.1
+return Math.round(value * quantity);
+```
+
+This makes order totals calculations much more "correct" as per most people's expectations, but it pointed out as a technically breaking change in the unlikely event that you rely on the old, less correct method of rounding.
+
 ## 2.2.0-next.8 (2024-04-04)
 
 

+ 5 - 1
README.md

@@ -106,7 +106,7 @@ cd packages/dev-server
 If you are making changes to the admin ui, you need to start the admin ui independent from the dev-server:
 
 1. `cd packages/admin-ui`
-2. `npm run dev`
+2. `npm run start`
 3. Go to http://localhost:4200 and log in with "superadmin", "superadmin"
 
 This will auto restart when you make changes to the admin ui. You don't need this step when you just use the admin ui just
@@ -142,6 +142,10 @@ DB=sqlite npm run dev
 
 3. The dev-server will now have your local changes from the changed package.
 
+### Interactive debugging
+
+To debug the dev server with VS Code use the include [launch.json](/.vscode/launch.json) configuration.
+
 ### Code generation
 
 [graphql-code-generator](https://github.com/dotansimha/graphql-code-generator) is used to automatically create TypeScript interfaces for all GraphQL server operations and admin ui queries. These generated interfaces are used in both the admin ui and the server.

+ 12 - 0
docs/docs/guides/core-concepts/orders/index.md

@@ -352,3 +352,15 @@ If you have defined custom order states, the Admin UI will allow you to manually
 order from one state to another:
 
 ![./custom-order-ui.webp](./custom-order-ui.webp)
+
+## Order Interceptors
+
+Vendure v3.1 introduces the concept of [Order Interceptors](/reference/typescript-api/orders/order-interceptor/). 
+These are a way to intercept operations that add, modify or remove order lines. Examples use-cases include:
+
+* Preventing certain products from being added to the order based on some criteria, e.g. if the  product is already in another active order.
+* Enforcing a minimum or maximum quantity of a given product in the order
+* Using a CAPTCHA to prevent automated order creation
+
+Check the [Order Interceptor](/reference/typescript-api/orders/order-interceptor/) docs for more information as well as a complete
+example of how to implement an interceptor.

+ 4 - 1
docs/docs/guides/deployment/horizontal-scaling.md

@@ -22,7 +22,10 @@ In Vendure, both the server and the worker can be scaled horizontally. Scaling t
 In order to run Vendure in a multi-instance configuration, there are some important configuration changes you'll need to make. The key consideration in configuring Vendure for this scenario is to ensure that any persistent state is managed externally from the Node process, and is shared by all instances. Namely:
 
 * The JobQueue should be stored externally using the [DefaultJobQueuePlugin](/reference/typescript-api/job-queue/default-job-queue-plugin/) (which stores jobs in the database) or the [BullMQJobQueuePlugin](/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-plugin) (which stores jobs in Redis), or some other custom JobQueueStrategy. **Note:** the BullMQJobQueuePlugin is much more efficient than the DefaultJobQueuePlugin, and is recommended for production applications.
-* A custom [SessionCacheStrategy](/reference/typescript-api/auth/session-cache-strategy/) must be used which stores the session cache externally (such as in the database or Redis), since the default strategy stores the cache in-memory and will cause inconsistencies in multi-instance setups. [Example Redis-based SessionCacheStrategy](/reference/typescript-api/auth/session-cache-strategy/)
+* An appropriate [CacheStrategy](/reference/typescript-api/cache/cache-strategy/) must be used which stores the cache externally. Both the [DefaultCachePlugin](/reference/typescript-api/cache/default-cache-plugin/) and the [RedisCachePlugin](/reference/typescript-api/cache/redis-cache-plugin/) are suitable 
+  for multi-instance setups. The DefaultCachePlugin uses the database to store the cache data, which is simple and effective, while the RedisCachePlugin uses a Redis server to store the cache data and can have better performance characteristics.
+* If you are on a version prior to v3.1, a custom [SessionCacheStrategy](/reference/typescript-api/auth/session-cache-strategy/) must be used which stores the session cache externally (such as in the database or Redis), since the default strategy stores the cache in-memory and will cause inconsistencies in multi-instance setups. [Example Redis-based SessionCacheStrategy](/reference/typescript-api/auth/session-cache-strategy/).
+  From v3.1 the session cache is handled by the underlying cache strategy, so you normally don't need to define a custom SessionCacheStrategy.
 * When using cookies to manage sessions, make sure all instances are using the _same_ cookie secret:
     ```ts title="src/vendure-config.ts"
     const config: VendureConfig = {

+ 4 - 0
docs/docs/guides/deployment/production-configuration/index.md

@@ -116,3 +116,7 @@ In **Postgres**, you can execute:
 show timezone;
 ```
 and you should expect to see `UTC` or `Etc/UTC`.
+
+## Security Considerations
+
+Please read over the [Security](/guides/developer-guide/security) section of the Developer Guide for more information on how to secure your Vendure application.

BIN
docs/docs/guides/developer-guide/cache/cache-service.webp


+ 206 - 0
docs/docs/guides/developer-guide/cache/index.mdx

@@ -0,0 +1,206 @@
+---
+title: "Cache"
+---
+
+Caching is a technique to improve performance of a system by saving the results of expensive
+operations and reusing them when the same operation is requested again.
+
+Vendure uses caching in a number of places to improve performance, and the same caching
+mechanism is available for use in custom plugins.
+
+## CacheService
+
+The [`CacheService`](/reference/typescript-api/cache/cache-service) is the general-purpose API for interacting with the cache.
+It provides methods for setting, getting and deleting cache entries.
+
+![CacheService](./cache-service.webp)
+
+Internally, the `CacheService` uses a [CacheStrategy](/reference/typescript-api/cache/cache-strategy) to store the data. The cache strategy is responsible for
+the actual storage and retrieval of the data. The `CacheService` provides a consistent API which can be used
+regardless of the underlying cache strategy.
+
+:::info
+From Vendure v3.1, new projects are created with the [DefaultCachePlugin](/reference/typescript-api/cache/default-cache-plugin) enabled by default. This plugin
+uses the database to store the cache data. This is a simple and effective cache strategy which is suitable
+for most use-cases.
+
+For more advanced use-cases, you can use the [RedisCachePlugin](/reference/typescript-api/cache/redis-cache-plugin) which uses a Redis
+server to store the cache data and can have better performance characteristics.
+:::
+
+### Multi-instance use
+
+It is common to run Vendure in a multi-instance setup, where multiple instances of the server and worker are
+running in parallel.
+
+The `CacheService` is designed to work in this environment. Both the [DefaultCachePlugin](/reference/typescript-api/cache/default-cache-plugin)
+and the [RedisCachePlugin](/reference/typescript-api/cache/redis-cache-plugin) use a single shared cache across all
+instances.
+
+This means that if one instance sets a cache entry, it will be available to all other instances. Likewise,
+if one instance deletes a cache entry, it will be deleted for all other instances.
+
+### Usage
+
+The `CacheService` can be injected into any service, resolver, strategy or configurable operation.
+
+```ts
+import { Injectable } from '@nestjs/common';
+import { CacheService } from '@vendure/core';
+
+@Injectable()
+export class MyService {
+    constructor(private cacheService: CacheService) {}
+
+    async myMethod() {
+        const cacheKey = 'MyService.myMethod';
+        const cachedValue = await this.cacheService.get(cacheKey);
+        if (cachedValue) {
+            return cachedValue;
+        }
+        const newValue = await this.expensiveOperation();
+        // Cache the result for 1 minute (60 * 1000 milliseconds)
+        await this.cacheService.set(cacheKey, newValue, { ttl: 60 * 1000 });
+        return newValue;
+    }
+
+    private async expensiveOperation() {
+        // Do something expensive
+    }
+}
+```
+
+:::info
+
+The data stored in the cache must be serializable. This means you cannot store instances of classes,
+functions, or other non-serializable data types.
+
+:::
+
+### Cache key naming
+
+When setting a cache entry, it is important to choose a unique key which will not conflict
+with other cache entries. The key should be namespaced to avoid conflicts. For example,
+you can use the name of the class & method as part of the key. If there is an identifier
+which is unique to the operation, that can be used as well.
+
+```ts
+getVariantIds(productId: ID): Promise<ID[]> {
+    const cacheKey = `ProductService.getVariantIds:${productId}`;
+    const cachedValue = await this.cacheService.get(cacheKey);
+    if (cachedValue) {
+        return cachedValue;
+    }
+    const newValue = await this.expensiveOperation(productId);
+    await this.cacheService.set(cacheKey, newValue, { ttl: 60 * 1000 });
+    return newValue;
+}
+```
+
+### Cache eviction
+
+The cache is not infinite, and entries will be evicted after a certain time. The time-to-live (TTL)
+of a cache entry can be set when calling `set()`. If no TTL is set, the cache entry will remain
+in the cache indefinitely.
+
+Cache entries can also be manually deleted using the `delete()` method:
+
+```ts
+await this.cacheService.delete(cacheKey);
+```
+
+### Cache tags
+
+When setting a cache entry, you can also specify a list of tags. This allows you to invalidate
+all cache entries which share a tag. For example, if you have a cache entry which is related to
+a Product, you can tag it with the Product's ID. When the Product is updated, you can invalidate
+all cache entries which are tagged with that Product ID.
+
+```ts
+const cacheKey = `ProductService.getVariantIds:${productId}`;
+
+await this.cacheService.set(cacheKey, newValue, {
+    tags: [`Product:${productId}`]
+});
+
+// later
+
+await this.cacheService.invalidateTags([`Product:${productId}`]);
+```
+
+### createCache Helper
+
+The `createCache` helper function can be used to create a [Cache](/reference/typescript-api/cache) instance
+which is a convenience wrapper around the `CacheService` APIs:
+
+```ts
+import { Injectable } from '@nestjs/common';
+import { CacheService, ID, EventBus, ProductEvent,RequestContext } from '@vendure/core';
+
+@Injectable()
+export class FacetValueChecker {
+    // Create a Cache instance with a 1-day TTL
+    private facetValueCache = this.cacheService.createCache({
+        getKey: (productId: ID) => `FacetValueChecker.${productId}`,
+        options: { ttl: 1000 * 60 * 60 * 24 },
+    });
+
+    constructor(private cacheService: CacheService, private eventBus: EventBus) {
+        this.eventBus.ofType(ProductEvent).subscribe(event => {
+            if (event.type !== 'created') {
+                // Invalidate the cache entry when a Product is updated or deleted
+                this.facetValueCache.delete(event.entity.id);
+            }
+        });
+    }
+
+    async getFacetValueIdsForProduct(ctx: RequestContext, productId: ID): Promise<ID[]> {
+        return this.facetValueCache.get(productId, () =>
+            // This function will only be called if the cache entry does not exist
+            // or has expired. It will set the result in the cache automatically.
+            this.calculateFacetValueIdsForProduct(ctx, productId));
+    }
+
+    async calculateFacetValueIdsForProduct(ctx: RequestContext, productId: ID): Promise<ID[]> {
+        // Do something expensive
+    }
+}
+```
+
+## RequestContextCache
+
+The [RequestContextCacheService](/reference/typescript-api/cache/request-context-cache-service) is a specialized
+cache service which is scoped to the current request. This is useful when you want to cache data
+for the duration of a single request, but not across multiple requests.
+
+This can be especially useful in resolvers, where you may want to cache the result of a specific resolved
+field which may be requested multiple times within the same request.
+
+For example, in Vendure core, when dealing with product lists, there's a particular very hot
+code path that is used to calculate the correct prices to return for each product. As part of this
+calculation, we need to know the active tax zone, which can be expensive to calculate newly
+for each product. We use the `RequestContextCacheService` to cache the active tax zone for the
+duration of the request.
+
+```ts
+const activeTaxZone = await this.requestContextCache.get(
+    ctx,
+    'activeTaxZone',
+    () => taxZoneStrategy
+        .determineTaxZone(ctx, zones, ctx.channel, order),
+);
+```
+
+Internally, the `RequestContextCacheService` makes used of the WeakMap data structure which means the cached
+data will be automatically garbage-collected when the request is finished. It is also able to store
+any kind of data, not just serializable data.
+
+## Session Cache
+
+There is an additional cache which is specifically used to cache session data, since this data is commonly
+accessed on almost all requests. Since v3.1, the default is to use the [DefaultSessionCacheStrategy](/reference/typescript-api/auth/default-session-cache-strategy)
+which internally just uses whatever the current `CacheStrategy` is to store the data.
+
+This means that in most cases you don't need to worry about the session cache, but if you have specific
+requirements, you can create a custom session cache strategy and set it via the `authOptions.sessionCacheStrategy`
+config property.

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

@@ -146,6 +146,7 @@ The following types are available for custom fields:
 | `float`        | Floating point number        | product review rating                                    |
 | `boolean`      | Boolean                      | isDownloadable flag on product                           |
 | `datetime`     | A datetime                   | date that variant is back in stock                       |
+| `struct`       | Structured json-like data    | Key-value attributes with additional data for products   |
 | `relation`     | A relation to another entity | Asset used as a customer avatar, related Products        |
 
 To see the underlying DB data type and GraphQL type used for each, see the [CustomFieldType doc](/reference/typescript-api/custom-fields/custom-field-type).
@@ -882,6 +883,111 @@ const config = {
 
 The step value. See [the MDN datetime-local docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#step) to understand how this is used.
 
+### Properties for `struct` fields
+
+:::info
+The `struct` custom field type is available from Vendure v3.1.0.
+:::
+
+In addition to the common properties, the `struct` custom fields have some type-specific properties:
+
+- [`fields`](#fields)
+
+#### fields
+
+<CustomFieldProperty required={true} type="StructFieldConfig[]" typeLink="/reference/typescript-api/custom-fields/struct-field-config" />
+
+A `struct` is a data structure comprising a set of named fields, each with its own type. The `fields` property is an array of `StructFieldConfig` objects, each of which defines a field within the struct.
+
+```ts title="src/vendure-config.ts"
+const config = {
+    // ...
+    customFields: {
+        Product: [
+            {
+                name: 'dimensions',
+                type: 'struct',
+                // highlight-start
+                fields: [
+                    { name: 'length', type: 'int' },
+                    { name: 'width', type: 'int' },
+                    { name: 'height', type: 'int' },
+                ],
+                // highlight-end
+            },
+        ]
+    }
+};
+```
+
+When querying the `Product` entity, the `dimensions` field will be an object with the fields `length`, `width` and `height`:
+
+```graphql
+query {
+    product(id: 1) {
+        customFields {
+            dimensions {
+                length
+                width
+                height
+            }
+        }
+    }
+}
+```
+
+Struct fields support many of the same properties as other custom fields, such as `list`, `label`, `description`, `validate`, `ui` and 
+type-specific properties such as `options` and `pattern` for string types.
+
+:::note
+The following properties are **not** supported for `struct` fields: `public`, `readonly`, `internal`, `defaultValue`, `nullable`, `unique`, `requiresPermission`.
+:::
+
+```ts title="src/vendure-config.ts"
+import { LanguageCode } from '@vendure/core';
+
+const config = {
+    // ...
+    customFields: {
+        OrderLine: [
+            {
+                name: 'customizationOptions',
+                type: 'struct',
+                fields: [
+                    {
+                        name: 'color',
+                        type: 'string',
+                        // highlight-start
+                        options: [
+                            {value: 'red', label: [{languageCode: LanguageCode.en, value: 'Red'}]},
+                            {value: 'blue', label: [{languageCode: LanguageCode.en, value: 'Blue'}]},
+                        ],
+                        // highlight-end
+                    },
+                    {
+                        name: 'engraving',
+                        type: 'string',
+                        // highlight-start
+                        validate: (value: any) => {
+                            if (value.length > 20) {
+                                return 'Engraving text must be 20 characters or fewer';
+                            }
+                        },
+                    },
+                    {
+                        name: 'notifyEmailAddresses',
+                        type: 'string',
+                        // highlight-start
+                        list: true,
+                        // highlight-end
+                    }
+                ],
+            },
+        ]
+    }
+};
+```
+
 ### Properties for `relation` fields
 
 In addition to the common properties, the `relation` custom fields have some type-specific properties:

+ 29 - 0
docs/docs/guides/developer-guide/security/index.md

@@ -72,6 +72,35 @@ export const config: VendureConfig = {
 For a detailed explanation of how to best configure this plugin, see the [HardenPlugin docs](/reference/core-plugins/harden-plugin/).
 :::
 
+### Harden the AssetServerPlugin
+
+If you are using the [AssetServerPlugin](/reference/core-plugins/asset-server-plugin/), it is possible by default to use the dynamic
+image transform feature to overload the server with requests for new image sizes & formats. To prevent this, you can
+configure the plugin to only allow transformations for the preset sizes, and limited quality levels and formats.
+Since v3.1 we ship the [PresetOnlyStrategy](/reference/core-plugins/asset-server-plugin/preset-only-strategy/) for this purpose, and
+you can also create your own strategies.
+
+```ts
+import { VendureConfig } from '@vendure/core';
+import { AssetServerPlugin, PresetOnlyStrategy } from '@vendure/asset-server-plugin';
+
+export const config: VendureConfig = {
+  // ...
+  plugins: [
+    AssetServerPlugin.init({
+      // ...
+      // highlight-start  
+      imageTransformStrategy: new PresetOnlyStrategy({
+        defaultPreset: 'large',
+        permittedQuality: [0, 50, 75, 85, 95],
+        permittedFormats: ['jpg', 'webp', 'avif'],
+        allowFocalPoint: false,
+      }),
+      // highlight-end
+    }),
+  ]
+};
+```
 
 ## OWASP Top Ten Security Assessment
 

+ 14 - 14
docs/docs/reference/admin-ui-api/alerts/alert-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AlertConfig
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/alerts/alerts.service.ts" sourceLine="62" packageName="@vendure/admin-ui" since="2.2.0" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/alerts/alerts.service.ts" sourceLine="63" packageName="@vendure/admin-ui" since="2.2.0" />
 
 A configuration object for an Admin UI alert.
 
@@ -22,9 +22,9 @@ interface AlertConfig<T = any> {
     recheck?: (context: AlertContext) => Observable<any>;
     isAlert: (data: T, context: AlertContext) => boolean;
     action: (data: T, context: AlertContext) => void;
-    label: (
-        data: T,
-        context: AlertContext,
+    label: (
+        data: T,
+        context: AlertContext,
     ) => { text: string; translationVars?: { [key: string]: string | number } };
     requiredPermissions?: Permission[];
 }
@@ -41,18 +41,18 @@ A unique identifier for the alert.
 
 <MemberInfo kind="property" type={`(context: <a href='/reference/admin-ui-api/alerts/alert-context#alertcontext'>AlertContext</a>) =&#62; T | Promise&#60;T&#62; | Observable&#60;T&#62;`}   />
 
-A function which is gets the data used to determine whether the alert should be shown.
-Typically, this function will query the server or some other remote data source.
-
-This function will be called once when the Admin UI app bootstraps, and can be also
+A function which is gets the data used to determine whether the alert should be shown.
+Typically, this function will query the server or some other remote data source.
+
+This function will be called once when the Admin UI app bootstraps, and can be also
 set to run at regular intervals by setting the `recheckIntervalMs` property.
 ### recheck
 
 <MemberInfo kind="property" type={`(context: <a href='/reference/admin-ui-api/alerts/alert-context#alertcontext'>AlertContext</a>) =&#62; Observable&#60;any&#62;`} default={`undefined`}   />
 
-A function which returns an Observable which is used to determine when to re-run the `check`
-function. Whenever the observable emits, the `check` function will be called again.
-
+A function which returns an Observable which is used to determine when to re-run the `check`
+function. Whenever the observable emits, the `check` function will be called again.
+
 A basic time-interval-based recheck can be achieved by using the `interval` function from RxJS.
 
 *Example*
@@ -69,7 +69,7 @@ If this is not set, the `check` function will only be called once when the Admin
 
 <MemberInfo kind="property" type={`(data: T, context: <a href='/reference/admin-ui-api/alerts/alert-context#alertcontext'>AlertContext</a>) =&#62; boolean`}   />
 
-A function which determines whether the alert should be shown based on the data returned by the `check`
+A function which determines whether the alert should be shown based on the data returned by the `check`
 function.
 ### action
 
@@ -78,14 +78,14 @@ function.
 A function which is called when the alert is clicked in the Admin UI.
 ### label
 
-<MemberInfo kind="property" type={`(
         data: T,
         context: <a href='/reference/admin-ui-api/alerts/alert-context#alertcontext'>AlertContext</a>,
     ) =&#62; { text: string; translationVars?: { [key: string]: string | number } }`}   />
+<MemberInfo kind="property" type={`(         data: T,         context: <a href='/reference/admin-ui-api/alerts/alert-context#alertcontext'>AlertContext</a>,     ) =&#62; { text: string; translationVars?: { [key: string]: string | number } }`}   />
 
 A function which returns the text used in the UI to describe the alert.
 ### requiredPermissions
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/common/permission#permission'>Permission</a>[]`}   />
 
-A list of permissions which the current Administrator must have in order. If the current
+A list of permissions which the current Administrator must have in order. If the current
 Administrator does not have these permissions, none of the other alert functions will be called.
 
 

+ 1 - 1
docs/docs/reference/admin-ui-api/alerts/alert-context.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AlertContext
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/alerts/alerts.service.ts" sourceLine="28" packageName="@vendure/admin-ui" since="2.2.0" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/alerts/alerts.service.ts" sourceLine="29" packageName="@vendure/admin-ui" since="2.2.0" />
 
 The context object which is passed to the `check`, `isAlert`, `label` and `action` functions of an
 <a href='/reference/admin-ui-api/alerts/alert-config#alertconfig'>AlertConfig</a> object.

+ 1 - 1
docs/docs/reference/admin-ui-api/components/asset-picker-dialog-component.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AssetPickerDialogComponent
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/components/asset-picker-dialog/asset-picker-dialog.component.ts" sourceLine="52" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/components/asset-picker-dialog/asset-picker-dialog.component.ts" sourceLine="51" packageName="@vendure/admin-ui" />
 
 A dialog which allows the creation and selection of assets.
 

+ 14 - 8
docs/docs/reference/admin-ui-api/components/data-table2component.md

@@ -84,6 +84,7 @@ class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
     @Input() activeIndex = -1;
     @Output() pageChange = new EventEmitter<number>();
     @Output() itemsPerPageChange = new EventEmitter<number>();
+    @Output() visibleColumnsChange = new EventEmitter<Array<DataTable2ColumnComponent<T>>>();
     @ContentChildren(DataTable2ColumnComponent) columns: QueryList<DataTable2ColumnComponent<T>>;
     @ContentChildren(DataTableCustomFieldColumnComponent)
     customFieldColumns: QueryList<DataTableCustomFieldColumnComponent<T>>;
@@ -95,6 +96,7 @@ class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
     route = inject(ActivatedRoute);
     filterPresetService = inject(FilterPresetService);
     dataTableCustomComponentService = inject(DataTableCustomComponentService);
+    dataTableConfigService = inject(DataTableConfigService);
     protected customComponents = new Map<string, { config: DataTableComponentConfig; injector: Injector }>();
     rowTemplate: TemplateRef<any>;
     currentStart: number;
@@ -103,7 +105,7 @@ class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
     showSearchFilterRow = false;
     protected uiLanguage$: Observable<LanguageCode>;
     protected destroy$ = new Subject<void>();
-    constructor(changeDetectorRef: ChangeDetectorRef, localStorageService: LocalStorageService, dataService: DataService)
+    constructor(changeDetectorRef: ChangeDetectorRef, dataService: DataService)
     selectionManager: void
     allColumns: void
     visibleSortedColumns: void
@@ -117,7 +119,6 @@ class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
     trackByFn(index: number, item: any) => ;
     onToggleAllClick() => ;
     onRowClick(item: T, event: MouseEvent) => ;
-    getDataTableConfig() => DataTableConfig;
 }
 ```
 * Implements: <code>AfterContentInit</code>, <code>OnChanges</code>, <code>OnDestroy</code>
@@ -176,6 +177,11 @@ class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
 <MemberInfo kind="property" type={``}   />
 
 
+### visibleColumnsChange
+
+<MemberInfo kind="property" type={``}   />
+
+
 ### columns
 
 <MemberInfo kind="property" type={`QueryList&#60;DataTable2ColumnComponent&#60;T&#62;&#62;`}   />
@@ -226,6 +232,11 @@ class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
 <MemberInfo kind="property" type={``}   />
 
 
+### dataTableConfigService
+
+<MemberInfo kind="property" type={``}   />
+
+
 ### customComponents
 
 <MemberInfo kind="property" type={``}   />
@@ -268,7 +279,7 @@ class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(changeDetectorRef: ChangeDetectorRef, localStorageService: LocalStorageService, dataService: <a href='/reference/admin-ui-api/services/data-service#dataservice'>DataService</a>) => DataTable2Component`}   />
+<MemberInfo kind="method" type={`(changeDetectorRef: ChangeDetectorRef, dataService: <a href='/reference/admin-ui-api/services/data-service#dataservice'>DataService</a>) => DataTable2Component`}   />
 
 
 ### selectionManager
@@ -336,11 +347,6 @@ class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
 <MemberInfo kind="method" type={`(item: T, event: MouseEvent) => `}   />
 
 
-### getDataTableConfig
-
-<MemberInfo kind="method" type={`() => DataTableConfig`}   />
-
-
 
 
 </div>

+ 99 - 15
docs/docs/reference/admin-ui-api/custom-input-components/default-inputs.md

@@ -58,7 +58,7 @@ class BooleanFormInputComponent implements FormInputComponent {
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/code-editor-form-input/html-editor-form-input.component.ts" sourceLine="23" packageName="@vendure/admin-ui" />
 
-A JSON editor input with syntax highlighting and error detection. Works well
+A JSON editor input with syntax highlighting and error detection. Works well
 with `text` type fields.
 
 ```ts title="Signature"
@@ -101,7 +101,7 @@ class HtmlEditorFormInputComponent extends BaseCodeEditorFormInputComponent impl
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/code-editor-form-input/json-editor-form-input.component.ts" sourceLine="33" packageName="@vendure/admin-ui" />
 
-A JSON editor input with syntax highlighting and error detection. Works well
+A JSON editor input with syntax highlighting and error detection. Works well
 with `text` type fields.
 
 ```ts title="Signature"
@@ -276,7 +276,7 @@ class CurrencyFormInputComponent implements FormInputComponent {
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/customer-group-form-input/customer-group-form-input.component.ts" sourceLine="20" packageName="@vendure/admin-ui" />
 
-Allows the selection of a Customer via an autocomplete select input.
+Allows the selection of a Customer via an autocomplete select input.
 Should be used with `ID` type fields which represent Customer IDs.
 
 ```ts title="Signature"
@@ -415,7 +415,7 @@ class DateFormInputComponent implements FormInputComponent {
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/facet-value-form-input/facet-value-form-input.component.ts" sourceLine="16" packageName="@vendure/admin-ui" />
 
-Allows the selection of multiple FacetValues via an autocomplete select input.
+Allows the selection of multiple FacetValues via an autocomplete select input.
 Should be used with `ID` type **list** fields which represent FacetValue IDs.
 
 ```ts title="Signature"
@@ -425,13 +425,13 @@ class FacetValueFormInputComponent implements FormInputComponent {
     readonly: boolean;
     formControl: UntypedFormControl;
     config: InputComponentConfig;
-    valueTransformFn = (values: FacetValueFragment[]) => {
-        const isUsedInConfigArg = this.config.__typename === 'ConfigArgDefinition';
-        if (isUsedInConfigArg) {
-            return JSON.stringify(values.map(s => s.id));
-        } else {
-            return values;
-        }
+    valueTransformFn = (values: FacetValueFragment[]) => {
+        const isUsedInConfigArg = this.config.__typename === 'ConfigArgDefinition';
+        if (isUsedInConfigArg) {
+            return JSON.stringify(values.map(s => s.id));
+        } else {
+            return values;
+        }
     };
 }
 ```
@@ -600,7 +600,7 @@ class PasswordFormInputComponent implements FormInputComponent {
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/product-selector-form-input/product-selector-form-input.component.ts" sourceLine="20" packageName="@vendure/admin-ui" />
 
-Allows the selection of multiple ProductVariants via an autocomplete select input.
+Allows the selection of multiple ProductVariants via an autocomplete select input.
 Should be used with `ID` type **list** fields which represent ProductVariant IDs.
 
 ```ts title="Signature"
@@ -682,8 +682,8 @@ class ProductSelectorFormInputComponent implements FormInputComponent, OnInit {
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/relation-form-input.component.ts" sourceLine="17" packageName="@vendure/admin-ui" />
 
-The default input component for `relation` type custom fields. Allows the selection
-of a ProductVariant, Product, Customer or Asset. For other entity types, a custom
+The default input component for `relation` type custom fields. Allows the selection
+of a ProductVariant, Product, Customer or Asset. For other entity types, a custom
 implementation will need to be defined. See <a href='/reference/admin-ui-api/custom-input-components/register-form-input-component#registerforminputcomponent'>registerFormInputComponent</a>.
 
 ```ts title="Signature"
@@ -774,7 +774,7 @@ class RichTextFormInputComponent implements FormInputComponent {
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/select-form-input/select-form-input.component.ts" sourceLine="18" packageName="@vendure/admin-ui" />
 
-Uses a select input to allow the selection of a string value. Should be used with
+Uses a select input to allow the selection of a string value. Should be used with
 `string` type fields with options.
 
 ```ts title="Signature"
@@ -843,6 +843,90 @@ class SelectFormInputComponent implements FormInputComponent, OnInit {
 
 
 
+</div>
+
+
+## StructFormInputComponent
+
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/struct-form-input/struct-form-input.component.ts" sourceLine="18" packageName="@vendure/admin-ui" />
+
+A checkbox input. The default input component for `boolean` fields.
+
+```ts title="Signature"
+class StructFormInputComponent implements FormInputComponent, OnInit, OnDestroy {
+    static readonly id: DefaultFormComponentId = 'struct-form-input';
+    readonly: boolean;
+    formControl: UntypedFormControl;
+    config: DefaultFormComponentConfig<'struct-form-input'>;
+    uiLanguage$: Observable<LanguageCode>;
+    protected structFormGroup = new FormGroup({});
+    protected fields: Array<{
+        def: StructCustomFieldFragment['fields'][number];
+        formControl: FormControl;
+    }>;
+    constructor(dataService: DataService)
+    ngOnInit() => ;
+    ngOnDestroy() => ;
+}
+```
+* Implements: <code><a href='/reference/admin-ui-api/custom-input-components/form-input-component#forminputcomponent'>FormInputComponent</a></code>, <code>OnInit</code>, <code>OnDestroy</code>
+
+
+
+<div className="members-wrapper">
+
+### id
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/configurable-operation-def/default-form-component-id#defaultformcomponentid'>DefaultFormComponentId</a>`}   />
+
+
+### readonly
+
+<MemberInfo kind="property" type={`boolean`}   />
+
+
+### formControl
+
+<MemberInfo kind="property" type={`UntypedFormControl`}   />
+
+
+### config
+
+<MemberInfo kind="property" type={`DefaultFormComponentConfig&#60;'struct-form-input'&#62;`}   />
+
+
+### uiLanguage$
+
+<MemberInfo kind="property" type={`Observable&#60;<a href='/reference/typescript-api/common/language-code#languagecode'>LanguageCode</a>&#62;`}   />
+
+
+### structFormGroup
+
+<MemberInfo kind="property" type={``}   />
+
+
+### fields
+
+<MemberInfo kind="property" type={`Array&#60;{
         def: StructCustomFieldFragment['fields'][number];
         formControl: FormControl;
     }&#62;`}   />
+
+
+### constructor
+
+<MemberInfo kind="method" type={`(dataService: <a href='/reference/admin-ui-api/services/data-service#dataservice'>DataService</a>) => StructFormInputComponent`}   />
+
+
+### ngOnInit
+
+<MemberInfo kind="method" type={`() => `}   />
+
+
+### ngOnDestroy
+
+<MemberInfo kind="method" type={`() => `}   />
+
+
+
+
 </div>
 
 

+ 15 - 1
docs/docs/reference/admin-ui-api/list-detail-views/base-list-component.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## BaseListComponent
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-list.component.ts" sourceLine="40" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-list.component.ts" sourceLine="43" packageName="@vendure/admin-ui" />
 
 This is a base class which implements the logic required to fetch and manipulate
 a list of data from a query which returns a PaginatedList type.
@@ -33,11 +33,15 @@ class BaseListComponent<ResultType, ItemType, VariableType extends Record<string
     currentPage$: Observable<number>;
     protected destroy$ = new Subject<void>();
     protected refresh$ = new BehaviorSubject<undefined>(undefined);
+    protected visibleCustomFieldColumnChange$ = new Subject<
+        Array<DataTableCustomFieldColumnComponent<any>>
+    >();
     constructor(router: Router, route: ActivatedRoute)
     setQueryFn(listQueryFn: ListQueryFn<ResultType>, mappingFn: MappingFn<ItemType, ResultType>, onPageChangeFn?: OnPageChangeFn<VariableType>, defaults?: { take: number; skip: number }) => ;
     refreshListOnChanges(streams: Array<Observable<any>>) => ;
     setPageNumber(page: number) => ;
     setItemsPerPage(perPage: number) => ;
+    setVisibleColumns(columns: Array<DataTable2ColumnComponent<any>>) => ;
     refresh() => ;
     setQueryParam(hash: { [key: string]: any }, options?: { replaceUrl?: boolean; queryParamsHandling?: QueryParamsHandling }) => ;
     setQueryParam(key: string, value: any, options?: { replaceUrl?: boolean; queryParamsHandling?: QueryParamsHandling }) => ;
@@ -95,6 +99,11 @@ class BaseListComponent<ResultType, ItemType, VariableType extends Record<string
 <MemberInfo kind="property" type={``}   />
 
 
+### visibleCustomFieldColumnChange$
+
+<MemberInfo kind="property" type={``}   />
+
+
 ### constructor
 
 <MemberInfo kind="method" type={`(router: Router, route: ActivatedRoute) => BaseListComponent`}   />
@@ -120,6 +129,11 @@ Sets the current page number in the url.
 <MemberInfo kind="method" type={`(perPage: number) => `}   />
 
 Sets the number of items per page in the url.
+### setVisibleColumns
+
+<MemberInfo kind="method" type={`(columns: Array&#60;DataTable2ColumnComponent&#60;any&#62;&#62;) => `}   />
+
+
 ### refresh
 
 <MemberInfo kind="method" type={`() => `}   />

+ 13 - 1
docs/docs/reference/admin-ui-api/list-detail-views/typed-base-list-component.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## TypedBaseListComponent
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-list.component.ts" sourceLine="199" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-list.component.ts" sourceLine="217" packageName="@vendure/admin-ui" />
 
 A version of the <a href='/reference/admin-ui-api/list-detail-views/base-list-component#baselistcomponent'>BaseListComponent</a> which is designed to be used with a
 [TypedDocumentNode](https://the-guild.dev/graphql/codegen/plugins/typescript/typed-document-node).
@@ -24,6 +24,8 @@ class TypedBaseListComponent<T extends TypedDocumentNode<any, Vars>, Field exten
     protected router = inject(Router);
     protected serverConfigService = inject(ServerConfigService);
     protected permissionsService = inject(PermissionsService);
+    protected dataTableConfigService = inject(DataTableConfigService);
+    protected dataTableListId: string | undefined;
     constructor()
     configure(config: {
         document: T;
@@ -77,6 +79,16 @@ class TypedBaseListComponent<T extends TypedDocumentNode<any, Vars>, Field exten
 <MemberInfo kind="property" type={``}   />
 
 
+### dataTableConfigService
+
+<MemberInfo kind="property" type={``}   />
+
+
+### dataTableListId
+
+<MemberInfo kind="property" type={`string | undefined`}   />
+
+
 ### constructor
 
 <MemberInfo kind="method" type={`() => TypedBaseListComponent`}   />

+ 20 - 13
docs/docs/reference/admin-ui-api/services/data-service.md

@@ -20,8 +20,8 @@ to be effectively cached.
 
 ```ts title="Signature"
 class DataService {
-    query(query: DocumentNode | TypedDocumentNode<T, V>, variables?: V, fetchPolicy: WatchQueryFetchPolicy = 'cache-and-network') => QueryResult<T, V>;
-    mutate(mutation: DocumentNode | TypedDocumentNode<T, V>, variables?: V, update?: MutationUpdaterFn<T>) => Observable<T>;
+    query(query: DocumentNode | TypedDocumentNode<T, V>, variables?: V, fetchPolicy: WatchQueryFetchPolicy = 'cache-and-network', options: ExtendedQueryOptions = {}) => QueryResult<T, V>;
+    mutate(mutation: DocumentNode | TypedDocumentNode<T, V>, variables?: V, update?: MutationUpdaterFn<T>, options: ExtendedQueryOptions = {}) => Observable<T>;
 }
 ```
 
@@ -29,7 +29,7 @@ class DataService {
 
 ### query
 
-<MemberInfo kind="method" type={`(query: DocumentNode | TypedDocumentNode&#60;T, V&#62;, variables?: V, fetchPolicy: WatchQueryFetchPolicy = 'cache-and-network') => <a href='/reference/admin-ui-api/services/data-service#queryresult'>QueryResult</a>&#60;T, V&#62;`}   />
+<MemberInfo kind="method" type={`(query: DocumentNode | TypedDocumentNode&#60;T, V&#62;, variables?: V, fetchPolicy: WatchQueryFetchPolicy = 'cache-and-network', options: ExtendedQueryOptions = {}) => <a href='/reference/admin-ui-api/services/data-service#queryresult'>QueryResult</a>&#60;T, V&#62;`}   />
 
 Perform a GraphQL query. Returns a <a href='/reference/admin-ui-api/services/data-service#queryresult'>QueryResult</a> which allows further control over
 they type of result returned, e.g. stream of values, single value etc.
@@ -50,7 +50,7 @@ const result$ = this.dataService.query(gql`
 ```
 ### mutate
 
-<MemberInfo kind="method" type={`(mutation: DocumentNode | TypedDocumentNode&#60;T, V&#62;, variables?: V, update?: MutationUpdaterFn&#60;T&#62;) => Observable&#60;T&#62;`}   />
+<MemberInfo kind="method" type={`(mutation: DocumentNode | TypedDocumentNode&#60;T, V&#62;, variables?: V, update?: MutationUpdaterFn&#60;T&#62;, options: ExtendedQueryOptions = {}) => Observable&#60;T&#62;`}   />
 
 Perform a GraphQL mutation.
 
@@ -74,21 +74,22 @@ const result$ = this.dataService.mutate(gql`
 
 ## QueryResult
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/data/query-result.ts" sourceLine="19" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/data/query-result.ts" sourceLine="31" packageName="@vendure/admin-ui" />
 
 This class wraps the Apollo Angular QueryRef object and exposes some getters
 for convenience.
 
 ```ts title="Signature"
 class QueryResult<T, V extends Record<string, any> = Record<string, any>> {
-    constructor(queryRef: QueryRef<T, V>, apollo: Apollo)
-    completed$ = new Subject<void>();
+    constructor(queryRef: QueryRef<T, V>, apollo: Apollo, customFieldMap: Map<string, CustomFieldConfig[]>)
     refetchOnChannelChange() => QueryResult<T, V>;
+    refetchOnCustomFieldsChange(customFieldsToInclude$: Observable<string[]>) => QueryResult<T, V>;
     single$: Observable<T>
     stream$: Observable<T>
     ref: QueryRef<T, V>
     mapSingle(mapFn: (item: T) => R) => Observable<R>;
     mapStream(mapFn: (item: T) => R) => Observable<R>;
+    destroy() => ;
 }
 ```
 
@@ -96,12 +97,7 @@ class QueryResult<T, V extends Record<string, any> = Record<string, any>> {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(queryRef: QueryRef&#60;T, V&#62;, apollo: Apollo) => QueryResult`}   />
-
-
-### completed$
-
-<MemberInfo kind="property" type={``}   />
+<MemberInfo kind="method" type={`(queryRef: QueryRef&#60;T, V&#62;, apollo: Apollo, customFieldMap: Map&#60;string, <a href='/reference/typescript-api/custom-fields/custom-field-config#customfieldconfig'>CustomFieldConfig</a>[]&#62;) => QueryResult`}   />
 
 
 ### refetchOnChannelChange
@@ -109,6 +105,12 @@ class QueryResult<T, V extends Record<string, any> = Record<string, any>> {
 <MemberInfo kind="method" type={`() => <a href='/reference/admin-ui-api/services/data-service#queryresult'>QueryResult</a>&#60;T, V&#62;`}   />
 
 Re-fetch this query whenever the active Channel changes.
+### refetchOnCustomFieldsChange
+
+<MemberInfo kind="method" type={`(customFieldsToInclude$: Observable&#60;string[]&#62;) => <a href='/reference/admin-ui-api/services/data-service#queryresult'>QueryResult</a>&#60;T, V&#62;`}  since="3.0.4"  />
+
+Re-fetch this query whenever the custom fields change, updating the query to include the
+specified custom fields.
 ### single$
 
 <MemberInfo kind="property" type={`Observable&#60;T&#62;`}   />
@@ -134,6 +136,11 @@ Returns a single-result Observable after applying the map function.
 <MemberInfo kind="method" type={`(mapFn: (item: T) =&#62; R) => Observable&#60;R&#62;`}   />
 
 Returns a multiple-result Observable after applying the map function.
+### destroy
+
+<MemberInfo kind="method" type={`() => `}   />
+
+Signals to the internal Observable subscriptions that they should complete.
 
 
 </div>

+ 1 - 1
docs/docs/reference/core-plugins/admin-ui-plugin/admin-ui-plugin-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AdminUiPluginOptions
 
-<GenerationInfo sourceFile="packages/admin-ui-plugin/src/plugin.ts" sourceLine="43" packageName="@vendure/admin-ui-plugin" />
+<GenerationInfo sourceFile="packages/admin-ui-plugin/src/plugin.ts" sourceLine="44" packageName="@vendure/admin-ui-plugin" />
 
 Configuration options for the <a href='/reference/core-plugins/admin-ui-plugin/#adminuiplugin'>AdminUiPlugin</a>.
 

+ 1 - 1
docs/docs/reference/core-plugins/admin-ui-plugin/index.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AdminUiPlugin
 
-<GenerationInfo sourceFile="packages/admin-ui-plugin/src/plugin.ts" sourceLine="129" packageName="@vendure/admin-ui-plugin" />
+<GenerationInfo sourceFile="packages/admin-ui-plugin/src/plugin.ts" sourceLine="130" packageName="@vendure/admin-ui-plugin" />
 
 This plugin starts a static server for the Admin UI app, and proxies it via the `/admin/` path of the main Vendure server.
 

+ 13 - 1
docs/docs/reference/core-plugins/asset-server-plugin/asset-server-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AssetServerOptions
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/types.ts" sourceLine="72" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/types.ts" sourceLine="74" packageName="@vendure/asset-server-plugin" />
 
 The configuration options for the AssetServerPlugin.
 
@@ -23,6 +23,7 @@ interface AssetServerOptions {
     previewMaxWidth?: number;
     previewMaxHeight?: number;
     presets?: ImageTransformPreset[];
+    imageTransformStrategy?: ImageTransformStrategy | ImageTransformStrategy[];
     namingStrategy?: AssetNamingStrategy;
     previewStrategy?: AssetPreviewStrategy;
     storageStrategyFactory?: (
@@ -70,6 +71,17 @@ The max height in pixels of a generated preview image.
 <MemberInfo kind="property" type={`<a href='/reference/core-plugins/asset-server-plugin/image-transform-preset#imagetransformpreset'>ImageTransformPreset</a>[]`}   />
 
 An array of additional <a href='/reference/core-plugins/asset-server-plugin/image-transform-preset#imagetransformpreset'>ImageTransformPreset</a> objects.
+### imageTransformStrategy
+
+<MemberInfo kind="property" type={`<a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformstrategy'>ImageTransformStrategy</a> | <a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformstrategy'>ImageTransformStrategy</a>[]`} default={`[]`}  since="3.1.0"  />
+
+The strategy or strategies to use to determine the parameters for transforming an image.
+This can be used to implement custom image transformation logic, for example to
+limit transform parameters to a known set of presets.
+
+If multiple strategies are provided, they will be executed in the order in which they are defined.
+If a strategy throws an error, the image transformation will be aborted and the error
+will be logged, with an HTTP 400 response sent to the client.
 ### namingStrategy
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/assets/asset-naming-strategy#assetnamingstrategy'>AssetNamingStrategy</a>`} default={`<a href='/reference/core-plugins/asset-server-plugin/hashed-asset-naming-strategy#hashedassetnamingstrategy'>HashedAssetNamingStrategy</a>`}   />

+ 1 - 1
docs/docs/reference/core-plugins/asset-server-plugin/cache-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CacheConfig
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/types.ts" sourceLine="52" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/types.ts" sourceLine="54" packageName="@vendure/asset-server-plugin" />
 
 A configuration option for the Cache-Control header in the AssetServerPlugin asset response.
 

+ 1 - 1
docs/docs/reference/core-plugins/asset-server-plugin/hashed-asset-naming-strategy.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## HashedAssetNamingStrategy
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/hashed-asset-naming-strategy.ts" sourceLine="20" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/hashed-asset-naming-strategy.ts" sourceLine="20" packageName="@vendure/asset-server-plugin" />
 
 An extension of the <a href='/reference/typescript-api/assets/default-asset-naming-strategy#defaultassetnamingstrategy'>DefaultAssetNamingStrategy</a> which prefixes file names with
 the type (`'source'` or `'preview'`) as well as a 2-character sub-directory based on

+ 1 - 1
docs/docs/reference/core-plugins/asset-server-plugin/image-transform-mode.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ImageTransformMode
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/types.ts" sourceLine="21" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/types.ts" sourceLine="23" packageName="@vendure/asset-server-plugin" />
 
 Specifies the way in which an asset preview image will be resized to fit in the
 proscribed dimensions:

+ 1 - 1
docs/docs/reference/core-plugins/asset-server-plugin/image-transform-preset.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ImageTransformPreset
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/types.ts" sourceLine="39" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/types.ts" sourceLine="41" packageName="@vendure/asset-server-plugin" />
 
 A configuration option for an image size preset for the AssetServerPlugin.
 

+ 148 - 0
docs/docs/reference/core-plugins/asset-server-plugin/image-transform-strategy.md

@@ -0,0 +1,148 @@
+---
+title: "ImageTransformStrategy"
+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';
+
+
+## ImageTransformStrategy
+
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/image-transform-strategy.ts" sourceLine="56" packageName="@vendure/asset-server-plugin" since="3.1.0" />
+
+An injectable strategy which is used to determine the parameters for transforming an image.
+This can be used to implement custom image transformation logic, for example to
+limit transform parameters to a known set of presets.
+
+This is set via the `imageTransformStrategy` option in the AssetServerOptions. Multiple
+strategies can be defined and will be executed in the order in which they are defined.
+
+If a strategy throws an error, the image transformation will be aborted and the error
+will be logged, with an HTTP 400 response sent to the client.
+
+```ts title="Signature"
+interface ImageTransformStrategy extends InjectableStrategy {
+    getImageTransformParameters(
+        args: GetImageTransformParametersArgs,
+    ): Promise<ImageTransformParameters> | ImageTransformParameters;
+}
+```
+* Extends: <code><a href='/reference/typescript-api/common/injectable-strategy#injectablestrategy'>InjectableStrategy</a></code>
+
+
+
+<div className="members-wrapper">
+
+### getImageTransformParameters
+
+<MemberInfo kind="method" type={`(args: <a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#getimagetransformparametersargs'>GetImageTransformParametersArgs</a>) => Promise&#60;<a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformparameters'>ImageTransformParameters</a>&#62; | <a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformparameters'>ImageTransformParameters</a>`}   />
+
+Given the input parameters, return the parameters which should be used to transform the image.
+
+
+</div>
+
+
+## ImageTransformParameters
+
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/image-transform-strategy.ts" sourceLine="14" packageName="@vendure/asset-server-plugin" since="3.1.0" />
+
+Parameters which are used to transform the image.
+
+```ts title="Signature"
+interface ImageTransformParameters {
+    width: number | undefined;
+    height: number | undefined;
+    mode: ImageTransformMode | undefined;
+    quality: number | undefined;
+    format: ImageTransformFormat | undefined;
+    fpx: number | undefined;
+    fpy: number | undefined;
+    preset: string | undefined;
+}
+```
+
+<div className="members-wrapper">
+
+### width
+
+<MemberInfo kind="property" type={`number | undefined`}   />
+
+
+### height
+
+<MemberInfo kind="property" type={`number | undefined`}   />
+
+
+### mode
+
+<MemberInfo kind="property" type={`<a href='/reference/core-plugins/asset-server-plugin/image-transform-mode#imagetransformmode'>ImageTransformMode</a> | undefined`}   />
+
+
+### quality
+
+<MemberInfo kind="property" type={`number | undefined`}   />
+
+
+### format
+
+<MemberInfo kind="property" type={`ImageTransformFormat | undefined`}   />
+
+
+### fpx
+
+<MemberInfo kind="property" type={`number | undefined`}   />
+
+
+### fpy
+
+<MemberInfo kind="property" type={`number | undefined`}   />
+
+
+### preset
+
+<MemberInfo kind="property" type={`string | undefined`}   />
+
+
+
+
+</div>
+
+
+## GetImageTransformParametersArgs
+
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/image-transform-strategy.ts" sourceLine="33" packageName="@vendure/asset-server-plugin" since="3.1.0" />
+
+The arguments passed to the `getImageTransformParameters` method of an ImageTransformStrategy.
+
+```ts title="Signature"
+interface GetImageTransformParametersArgs {
+    req: Request;
+    availablePresets: ImageTransformPreset[];
+    input: ImageTransformParameters;
+}
+```
+
+<div className="members-wrapper">
+
+### req
+
+<MemberInfo kind="property" type={`Request`}   />
+
+
+### availablePresets
+
+<MemberInfo kind="property" type={`<a href='/reference/core-plugins/asset-server-plugin/image-transform-preset#imagetransformpreset'>ImageTransformPreset</a>[]`}   />
+
+
+### input
+
+<MemberInfo kind="property" type={`<a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformparameters'>ImageTransformParameters</a>`}   />
+
+
+
+
+</div>

+ 32 - 5
docs/docs/reference/core-plugins/asset-server-plugin/index.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AssetServerPlugin
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/plugin.ts" sourceLine="153" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/plugin.ts" sourceLine="176" packageName="@vendure/asset-server-plugin" />
 
 The `AssetServerPlugin` serves assets (images and other files) from the local file system, and can also be configured to use
 other storage strategies (e.g. <a href='/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy#s3assetstoragestrategy'>S3AssetStorageStrategy</a>. It can also perform on-the-fly image transformations
@@ -133,14 +133,41 @@ large | 800px | 800px | resize
 By default, the AssetServerPlugin will cache every transformed image, so that the transformation only needs to be performed a single time for
 a given configuration. Caching can be disabled per-request by setting the `?cache=false` query parameter.
 
+### Limiting transformations
+
+By default, the AssetServerPlugin will allow any transformation to be performed on an image. However, it is possible to restrict the transformations
+which can be performed by using an <a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformstrategy'>ImageTransformStrategy</a>. This can be used to limit the transformations to a known set of presets, for example.
+
+This is advisable in order to prevent abuse of the image transformation feature, as it can be computationally expensive.
+
+Since v3.1.0 we ship with a <a href='/reference/core-plugins/asset-server-plugin/preset-only-strategy#presetonlystrategy'>PresetOnlyStrategy</a> which allows only transformations using a known set of presets.
+
+*Example*
+
+```ts
+import { AssetServerPlugin, PresetOnlyStrategy } from '@vendure/core';
+
+// ...
+
+AssetServerPlugin.init({
+  //...
+  imageTransformStrategy: new PresetOnlyStrategy({
+    defaultPreset: 'thumbnail',
+    permittedQuality: [0, 50, 75, 85, 95],
+    permittedFormats: ['jpg', 'webp', 'avif'],
+    allowFocalPoint: false,
+  }),
+});
+```
+
 ```ts title="Signature"
-class AssetServerPlugin implements NestModule, OnApplicationBootstrap {
+class AssetServerPlugin implements NestModule, OnApplicationBootstrap, OnApplicationShutdown {
     init(options: AssetServerOptions) => Type<AssetServerPlugin>;
-    constructor(processContext: ProcessContext)
+    constructor(options: AssetServerOptions, processContext: ProcessContext, moduleRef: ModuleRef, assetServer: AssetServer)
     configure(consumer: MiddlewareConsumer) => ;
 }
 ```
-* Implements: <code>NestModule</code>, <code>OnApplicationBootstrap</code>
+* Implements: <code>NestModule</code>, <code>OnApplicationBootstrap</code>, <code>OnApplicationShutdown</code>
 
 
 
@@ -153,7 +180,7 @@ class AssetServerPlugin implements NestModule, OnApplicationBootstrap {
 Set the plugin options.
 ### constructor
 
-<MemberInfo kind="method" type={`(processContext: <a href='/reference/typescript-api/common/process-context#processcontext'>ProcessContext</a>) => AssetServerPlugin`}   />
+<MemberInfo kind="method" type={`(options: <a href='/reference/core-plugins/asset-server-plugin/asset-server-options#assetserveroptions'>AssetServerOptions</a>, processContext: <a href='/reference/typescript-api/common/process-context#processcontext'>ProcessContext</a>, moduleRef: ModuleRef, assetServer: AssetServer) => AssetServerPlugin`}   />
 
 
 ### configure

+ 1 - 1
docs/docs/reference/core-plugins/asset-server-plugin/local-asset-storage-strategy.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## LocalAssetStorageStrategy
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/local-asset-storage-strategy.ts" sourceLine="15" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/local-asset-storage-strategy.ts" sourceLine="15" packageName="@vendure/asset-server-plugin" />
 
 A persistence strategy which saves files to the local file system.
 

+ 122 - 0
docs/docs/reference/core-plugins/asset-server-plugin/preset-only-strategy.md

@@ -0,0 +1,122 @@
+---
+title: "PresetOnlyStrategy"
+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';
+
+
+## PresetOnlyStrategy
+
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/preset-only-strategy.ts" sourceLine="85" packageName="@vendure/asset-server-plugin" since="3.1.0" />
+
+An <a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformstrategy'>ImageTransformStrategy</a> which only allows transformations to be made using
+presets which are defined in the available presets.
+
+With this strategy enabled, requests to the asset server must include a `preset` parameter (or use the default preset)
+
+This is valid: `http://localhost:3000/assets/some-asset.jpg?preset=medium`
+
+This is invalid: `http://localhost:3000/assets/some-asset.jpg?w=200&h=200`, and the dimensions will be ignored.
+
+The strategy can be configured to allow only certain quality values and formats, and to
+optionally allow the focal point to be specified in the URL.
+
+If a preset is not found in the available presets, an error will be thrown.
+
+*Example*
+
+```ts
+import { AssetServerPlugin, PresetOnlyStrategy } from '@vendure/core';
+
+// ...
+
+AssetServerPlugin.init({
+  //...
+  imageTransformStrategy: new PresetOnlyStrategy({
+    defaultPreset: 'thumbnail',
+    permittedQuality: [0, 50, 75, 85, 95],
+    permittedFormats: ['jpg', 'webp', 'avif'],
+    allowFocalPoint: true,
+  }),
+});
+```
+
+```ts title="Signature"
+class PresetOnlyStrategy implements ImageTransformStrategy {
+    constructor(options: PresetOnlyStrategyOptions)
+    getImageTransformParameters({
+        input,
+        availablePresets,
+    }: GetImageTransformParametersArgs) => Promise<ImageTransformParameters> | ImageTransformParameters;
+}
+```
+* Implements: <code><a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformstrategy'>ImageTransformStrategy</a></code>
+
+
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(options: <a href='/reference/core-plugins/asset-server-plugin/preset-only-strategy#presetonlystrategyoptions'>PresetOnlyStrategyOptions</a>) => PresetOnlyStrategy`}   />
+
+
+### getImageTransformParameters
+
+<MemberInfo kind="method" type={`({
+        input,
+        availablePresets,
+    }: <a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#getimagetransformparametersargs'>GetImageTransformParametersArgs</a>) => Promise&#60;<a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformparameters'>ImageTransformParameters</a>&#62; | <a href='/reference/core-plugins/asset-server-plugin/image-transform-strategy#imagetransformparameters'>ImageTransformParameters</a>`}   />
+
+
+
+
+</div>
+
+
+## PresetOnlyStrategyOptions
+
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/preset-only-strategy.ts" sourceLine="16" packageName="@vendure/asset-server-plugin" />
+
+Configuration options for the <a href='/reference/core-plugins/asset-server-plugin/preset-only-strategy#presetonlystrategy'>PresetOnlyStrategy</a>.
+
+```ts title="Signature"
+interface PresetOnlyStrategyOptions {
+    defaultPreset: string;
+    permittedQuality?: number[];
+    permittedFormats?: ImageTransformFormat[];
+    allowFocalPoint?: boolean;
+}
+```
+
+<div className="members-wrapper">
+
+### defaultPreset
+
+<MemberInfo kind="property" type={`string`}   />
+
+The name of the default preset to use if no preset is specified in the URL.
+### permittedQuality
+
+<MemberInfo kind="property" type={`number[]`} default={`[0, 50, 75, 85, 95]`}   />
+
+The permitted quality of the transformed images. If set to 'any', then any quality is permitted.
+If set to an array of numbers (0-100), then only those quality values are permitted.
+### permittedFormats
+
+<MemberInfo kind="property" type={`ImageTransformFormat[]`} default={`['jpg', 'webp', 'avif']`}   />
+
+The permitted formats of the transformed images. If set to 'any', then any format is permitted.
+If set to an array of strings e.g. `['jpg', 'webp']`, then only those formats are permitted.
+### allowFocalPoint
+
+<MemberInfo kind="property" type={`boolean`} default={`false`}   />
+
+Whether to allow the focal point to be specified in the URL.
+
+
+</div>

+ 3 - 3
docs/docs/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## S3AssetStorageStrategy
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/s3-asset-storage-strategy.ts" sourceLine="155" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="155" packageName="@vendure/asset-server-plugin" />
 
 An <a href='/reference/typescript-api/assets/asset-storage-strategy#assetstoragestrategy'>AssetStorageStrategy</a> which uses [Amazon S3](https://aws.amazon.com/s3/) object storage service.
 To us this strategy you must first have access to an AWS account.
@@ -100,7 +100,7 @@ class S3AssetStorageStrategy implements AssetStorageStrategy {
 
 ## S3Config
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/s3-asset-storage-strategy.ts" sourceLine="19" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="19" packageName="@vendure/asset-server-plugin" />
 
 Configuration for connecting to AWS S3.
 
@@ -149,7 +149,7 @@ Using type `any` in order to avoid the need to include `aws-sdk` dependency in g
 
 ## configureS3AssetStorage
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/s3-asset-storage-strategy.ts" sourceLine="119" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/s3-asset-storage-strategy.ts" sourceLine="119" packageName="@vendure/asset-server-plugin" />
 
 Returns a configured instance of the <a href='/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy#s3assetstoragestrategy'>S3AssetStorageStrategy</a> which can then be passed to the <a href='/reference/core-plugins/asset-server-plugin/asset-server-options#assetserveroptions'>AssetServerOptions</a>
 `storageStrategyFactory` property.

+ 2 - 2
docs/docs/reference/core-plugins/asset-server-plugin/sharp-asset-preview-strategy.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## SharpAssetPreviewStrategy
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts" sourceLine="95" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/sharp-asset-preview-strategy.ts" sourceLine="95" packageName="@vendure/asset-server-plugin" />
 
 This <a href='/reference/typescript-api/assets/asset-preview-strategy#assetpreviewstrategy'>AssetPreviewStrategy</a> uses the [Sharp library](https://sharp.pixelplumbing.com/) to generate
 preview images of uploaded binary files. For non-image binaries, a generic "file" icon with the mime type
@@ -62,7 +62,7 @@ class SharpAssetPreviewStrategy implements AssetPreviewStrategy {
 
 ## SharpAssetPreviewConfig
 
-<GenerationInfo sourceFile="packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts" sourceLine="17" packageName="@vendure/asset-server-plugin" />
+<GenerationInfo sourceFile="packages/asset-server-plugin/src/config/sharp-asset-preview-strategy.ts" sourceLine="17" packageName="@vendure/asset-server-plugin" />
 
 This <a href='/reference/typescript-api/assets/asset-preview-strategy#assetpreviewstrategy'>AssetPreviewStrategy</a> uses the [Sharp library](https://sharp.pixelplumbing.com/) to generate
 preview images of uploaded binary files. For non-image binaries, a generic "file" icon with the mime type

+ 1 - 1
docs/docs/reference/core-plugins/elasticsearch-plugin/index.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ElasticsearchPlugin
 
-<GenerationInfo sourceFile="packages/elasticsearch-plugin/src/plugin.ts" sourceLine="223" packageName="@vendure/elasticsearch-plugin" />
+<GenerationInfo sourceFile="packages/elasticsearch-plugin/src/plugin.ts" sourceLine="224" packageName="@vendure/elasticsearch-plugin" />
 
 This plugin allows your product search to be powered by [Elasticsearch](https://github.com/elastic/elasticsearch) - a powerful Open Source search
 engine. This is a drop-in replacement for the DefaultSearchPlugin which exposes many powerful configuration options enabling your storefront

+ 1 - 1
docs/docs/reference/core-plugins/email-plugin/email-event-handler-with-async-data.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## EmailEventHandlerWithAsyncData
 
-<GenerationInfo sourceFile="packages/email-plugin/src/handler/event-handler.ts" sourceLine="455" packageName="@vendure/email-plugin" />
+<GenerationInfo sourceFile="packages/email-plugin/src/handler/event-handler.ts" sourceLine="492" packageName="@vendure/email-plugin" />
 
 Identical to the <a href='/reference/core-plugins/email-plugin/email-event-handler#emaileventhandler'>EmailEventHandler</a> but with a `data` property added to the `event` based on the result
 of the `.loadData()` function.

+ 27 - 1
docs/docs/reference/core-plugins/email-plugin/email-event-handler.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## EmailEventHandler
 
-<GenerationInfo sourceFile="packages/email-plugin/src/handler/event-handler.ts" sourceLine="135" packageName="@vendure/email-plugin" />
+<GenerationInfo sourceFile="packages/email-plugin/src/handler/event-handler.ts" sourceLine="136" packageName="@vendure/email-plugin" />
 
 The EmailEventHandler defines how the EmailPlugin will respond to a given event.
 
@@ -135,6 +135,7 @@ class EmailEventHandler<T extends string = string, Event extends EventWithContex
     setSubject(defaultSubject: string | SetSubjectFn<Event>) => EmailEventHandler<T, Event>;
     setFrom(from: string) => EmailEventHandler<T, Event>;
     setOptionalAddressFields(optionalAddressFieldsFn: SetOptionalAddressFieldsFn<Event>) => ;
+    setMetadata(optionalSetMetadataFn: SetMetadataFn<Event>) => ;
     setAttachments(setAttachmentsFn: SetAttachmentsFn<Event>) => ;
     addTemplate(config: EmailTemplateConfig) => EmailEventHandler<T, Event>;
     loadData(loadDataFn: LoadDataFn<Event, R>) => EmailEventHandlerWithAsyncData<R, T, Event, EventWithAsyncData<Event, R>>;
@@ -192,6 +193,31 @@ setTemplateVars() method.
 <MemberInfo kind="method" type={`(optionalAddressFieldsFn: <a href='/reference/core-plugins/email-plugin/email-plugin-types#setoptionaladdressfieldsfn'>SetOptionalAddressFieldsFn</a>&#60;Event&#62;) => `}  since="1.1.0"  />
 
 A function which allows <a href='/reference/core-plugins/email-plugin/email-plugin-types#optionaladdressfields'>OptionalAddressFields</a> to be specified such as "cc" and "bcc".
+### setMetadata
+
+<MemberInfo kind="method" type={`(optionalSetMetadataFn: <a href='/reference/core-plugins/email-plugin/email-plugin-types#setmetadatafn'>SetMetadataFn</a>&#60;Event&#62;) => `}  since="3.1.0"  />
+
+A function which allows <a href='/reference/core-plugins/email-plugin/email-plugin-types#emailmetadata'>EmailMetadata</a> to be specified for the email. This can be used
+to store arbitrary data about the email which can be used for tracking or other purposes.
+
+It will be exposed in the <a href='/reference/core-plugins/email-plugin/email-send-event#emailsendevent'>EmailSendEvent</a> as `event.metadata`. Here's an example of usage:
+
+- An <a href='/reference/typescript-api/events/event-types#orderstatetransitionevent'>OrderStateTransitionEvent</a> occurs, and the EmailEventListener starts processing it.
+- The EmailEventHandler attaches metadata to the email:
+   ```ts
+   new EmailEventListener(EventType.ORDER_CONFIRMATION)
+     .on(OrderStateTransitionEvent)
+     .setMetadata(event => ({
+       type: EventType.ORDER_CONFIRMATION,
+       orderId: event.order.id,
+     }));
+  ```
+- Then, the EmailPlugin tries to send the email and publishes <a href='/reference/core-plugins/email-plugin/email-send-event#emailsendevent'>EmailSendEvent</a>,
+  passing ctx, emailDetails, error or success, and this metadata.
+- In another part of the server, we have an eventBus that subscribes to EmailSendEvent. We can use
+  `metadata.type` and `metadata.orderId` to identify the related order. For example, we can indicate on the
+   order that the email was successfully sent, or in case of an error, send a notification confirming
+   the order in another available way.
 ### setAttachments
 
 <MemberInfo kind="method" type={`(setAttachmentsFn: <a href='/reference/core-plugins/email-plugin/email-plugin-types#setattachmentsfn'>SetAttachmentsFn</a>&#60;Event&#62;) => `}   />

+ 28 - 6
docs/docs/reference/core-plugins/email-plugin/email-plugin-types.md

@@ -130,7 +130,7 @@ type EmailAttachment = Omit<Attachment, 'raw'> & { path?: string }
 
 ## LoadTemplateInput
 
-<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="401" packageName="@vendure/email-plugin" />
+<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="402" packageName="@vendure/email-plugin" />
 
 The object passed to the <a href='/reference/core-plugins/email-plugin/template-loader#templateloader'>TemplateLoader</a> `loadTemplate()` method.
 
@@ -168,7 +168,7 @@ EmailEventHandler's `setTemplateVars()` method.
 
 ## SetTemplateVarsFn
 
-<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="434" packageName="@vendure/email-plugin" />
+<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="435" packageName="@vendure/email-plugin" />
 
 A function used to define template variables available to email templates.
 See <a href='/reference/core-plugins/email-plugin/email-event-handler#emaileventhandler'>EmailEventHandler</a>.setTemplateVars().
@@ -183,7 +183,7 @@ type SetTemplateVarsFn<Event> = (
 
 ## SetAttachmentsFn
 
-<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="448" packageName="@vendure/email-plugin" />
+<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="449" packageName="@vendure/email-plugin" />
 
 A function used to define attachments to be sent with the email.
 See https://nodemailer.com/message/attachments/ for more information about
@@ -196,7 +196,7 @@ type SetAttachmentsFn<Event> = (event: Event) => EmailAttachment[] | Promise<Ema
 
 ## SetSubjectFn
 
-<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="456" packageName="@vendure/email-plugin" />
+<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="457" packageName="@vendure/email-plugin" />
 
 A function used to define the subject to be sent with the email.
 
@@ -211,7 +211,7 @@ type SetSubjectFn<Event> = (
 
 ## OptionalAddressFields
 
-<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="470" packageName="@vendure/email-plugin" since="1.1.0" />
+<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="471" packageName="@vendure/email-plugin" since="1.1.0" />
 
 Optional address-related fields for sending the email.
 
@@ -247,7 +247,7 @@ An email address that will appear on the _Reply-To:_ field
 
 ## SetOptionalAddressFieldsFn
 
-<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="496" packageName="@vendure/email-plugin" since="1.1.0" />
+<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="497" packageName="@vendure/email-plugin" since="1.1.0" />
 
 A function used to set the <a href='/reference/core-plugins/email-plugin/email-plugin-types#optionaladdressfields'>OptionalAddressFields</a>.
 
@@ -256,3 +256,25 @@ type SetOptionalAddressFieldsFn<Event> = (
     event: Event,
 ) => OptionalAddressFields | Promise<OptionalAddressFields>
 ```
+
+
+## SetMetadataFn
+
+<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="509" packageName="@vendure/email-plugin" since="3.1.0" />
+
+A function used to set the <a href='/reference/core-plugins/email-plugin/email-plugin-types#emailmetadata'>EmailMetadata</a>.
+
+```ts title="Signature"
+type SetMetadataFn<Event> = (event: Event) => EmailMetadata | Promise<EmailMetadata>
+```
+
+
+## EmailMetadata
+
+<GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="519" packageName="@vendure/email-plugin" since="3.1.0" />
+
+Metadata that can be attached to an email via the <a href='/reference/core-plugins/email-plugin/email-event-handler#emaileventhandler'>EmailEventHandler</a>`.setMetadata()` method.
+
+```ts title="Signature"
+type EmailMetadata = Record<string, any>
+```

+ 2 - 2
docs/docs/reference/core-plugins/email-plugin/email-send-event.md

@@ -19,7 +19,7 @@ which occurred.
 
 ```ts title="Signature"
 class EmailSendEvent extends VendureEvent {
-    constructor(ctx: RequestContext, details: EmailDetails, success: boolean, error?: Error)
+    constructor(ctx: RequestContext, details: EmailDetails, success: boolean, error?: Error, metadata?: EmailMetadata)
 }
 ```
 * Extends: <code><a href='/reference/typescript-api/events/vendure-event#vendureevent'>VendureEvent</a></code>
@@ -30,7 +30,7 @@ class EmailSendEvent extends VendureEvent {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, details: <a href='/reference/core-plugins/email-plugin/email-plugin-types#emaildetails'>EmailDetails</a>, success: boolean, error?: Error) => EmailSendEvent`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, details: <a href='/reference/core-plugins/email-plugin/email-plugin-types#emaildetails'>EmailDetails</a>, success: boolean, error?: Error, metadata?: <a href='/reference/core-plugins/email-plugin/email-plugin-types#emailmetadata'>EmailMetadata</a>) => EmailSendEvent`}   />
 
 
 

+ 2 - 2
docs/docs/reference/core-plugins/payments-plugin/mollie-plugin.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## MolliePlugin
 
-<GenerationInfo sourceFile="packages/payments-plugin/src/mollie/mollie.plugin.ts" sourceLine="192" packageName="@vendure/payments-plugin" />
+<GenerationInfo sourceFile="packages/payments-plugin/src/mollie/mollie.plugin.ts" sourceLine="191" packageName="@vendure/payments-plugin" />
 
 Plugin to enable payments through the [Mollie platform](https://docs.mollie.com/).
 This plugin uses the Order API from Mollie, not the Payments API.
@@ -145,7 +145,7 @@ Initialize the mollie payment plugin
 
 ## MolliePluginOptions
 
-<GenerationInfo sourceFile="packages/payments-plugin/src/mollie/mollie.plugin.ts" sourceLine="29" packageName="@vendure/payments-plugin" />
+<GenerationInfo sourceFile="packages/payments-plugin/src/mollie/mollie.plugin.ts" sourceLine="28" packageName="@vendure/payments-plugin" />
 
 Configuration options for the Mollie payments plugin.
 

+ 34 - 1
docs/docs/reference/core-plugins/payments-plugin/stripe-plugin.md

@@ -182,7 +182,7 @@ Initialize the Stripe payment plugin
 
 ## StripePluginOptions
 
-<GenerationInfo sourceFile="packages/payments-plugin/src/stripe/types.ts" sourceLine="27" packageName="@vendure/payments-plugin" />
+<GenerationInfo sourceFile="packages/payments-plugin/src/stripe/types.ts" sourceLine="29" packageName="@vendure/payments-plugin" />
 
 Configuration options for the Stripe payments plugin.
 
@@ -199,6 +199,11 @@ interface StripePluginOptions {
         ctx: RequestContext,
         order: Order,
     ) => AdditionalPaymentIntentCreateParams | Promise<AdditionalPaymentIntentCreateParams>;
+    requestOptions?: (
+        injector: Injector,
+        ctx: RequestContext,
+        order: Order,
+    ) => AdditionalRequestOptions | Promise<AdditionalRequestOptions>;
     customerCreateParams?: (
         injector: Injector,
         ctx: RequestContext,
@@ -275,6 +280,34 @@ export const config: VendureConfig = {
   ],
 };
 ```
+### requestOptions
+
+<MemberInfo kind="property" type={`(
         injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>,
         ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>,
         order: <a href='/reference/typescript-api/entities/order#order'>Order</a>,
     ) =&#62; AdditionalRequestOptions | Promise&#60;AdditionalRequestOptions&#62;`}  since="3.1.0"  />
+
+Provide additional options to the Stripe payment intent creation. By default,
+the plugin will already pass the `idempotencyKey` parameter.
+
+For example, if you want to provide a `stripeAccount` for the payment intent, you can do so like this:
+
+*Example*
+
+```ts
+import { VendureConfig } from '@vendure/core';
+import { StripePlugin } from '@vendure/payments-plugin/package/stripe';
+
+export const config: VendureConfig = {
+  // ...
+  plugins: [
+    StripePlugin.init({
+      requestOptions: (injector, ctx, order) => {
+        return {
+          stripeAccount: ctx.channel.seller?.customFields.connectedAccountId
+        },
+      }
+    }),
+  ],
+};
+```
 ### customerCreateParams
 
 <MemberInfo kind="property" type={`(
         injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>,
         ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>,
         order: <a href='/reference/typescript-api/entities/order#order'>Order</a>,
     ) =&#62; AdditionalCustomerCreateParams | Promise&#60;AdditionalCustomerCreateParams&#62;`}  since="2.1.0"  />

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

@@ -423,6 +423,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ORDER_MODIFICATION_ERROR</div>
 <div class="graphql-code-line ">INELIGIBLE_SHIPPING_METHOD_ERROR</div>
 <div class="graphql-code-line ">NO_ACTIVE_ORDER_ERROR</div>
+<div class="graphql-code-line ">ORDER_INTERCEPTOR_ERROR</div>
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

+ 5 - 9
docs/docs/reference/graphql-api/admin/input-types.md

@@ -2917,20 +2917,16 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level">input <span class="graphql-code-identifier">RefundOrderInput</span> &#123;</div>
-<div class="graphql-code-line ">lines: [<a href="/reference/graphql-api/admin/input-types#orderlineinput">OrderLineInput</a>!]!</div>
+<div class="graphql-code-line ">lines: [<a href="/reference/graphql-api/admin/input-types#orderlineinput">OrderLineInput</a>!]</div>
 
-<div class="graphql-code-line ">shipping: <a href="/reference/graphql-api/admin/object-types#money">Money</a>!</div>
+<div class="graphql-code-line ">shipping: <a href="/reference/graphql-api/admin/object-types#money">Money</a></div>
 
-<div class="graphql-code-line ">adjustment: <a href="/reference/graphql-api/admin/object-types#money">Money</a>!</div>
+<div class="graphql-code-line ">adjustment: <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">If an amount is specified, this value will be used to create a Refund rather than calculating the</div>
-
-<div class="graphql-code-line comment">amount automatically. This was added in v2.2 and will be the preferred way to specify the refund</div>
-
-<div class="graphql-code-line comment">amount in the future. The `lines`, <code>shipping</code> and <code>adjustment</code> fields will likely be removed in a future</div>
+<div class="graphql-code-line comment">The amount to be refunded to this particular payment. This was introduced in v2.2.0 as the preferred way to specify the refund amount.</div>
 
-<div class="graphql-code-line comment">version.</div>
+<div class="graphql-code-line comment">Can be as much as the total amount of the payment minus the sum of all previous refunds.</div>
 <div class="graphql-code-line comment">"""</div>
 <div class="graphql-code-line ">amount: <a href="/reference/graphql-api/admin/object-types#money">Money</a></div>
 

+ 24 - 0
docs/docs/reference/graphql-api/admin/mutations.md

@@ -1538,6 +1538,30 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">transitionPaymentToState(id: <a href="/reference/graphql-api/admin/object-types#id">ID</a>!, state: <a href="/reference/graphql-api/admin/object-types#string">String</a>!): <a href="/reference/graphql-api/admin/object-types#transitionpaymenttostateresult">TransitionPaymentToStateResult</a>!</div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## unsetDraftOrderBillingAddress
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Unsets the billing address for a draft Order</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
+<div class="graphql-code-line ">unsetDraftOrderBillingAddress(orderId: <a href="/reference/graphql-api/admin/object-types#id">ID</a>!): <a href="/reference/graphql-api/admin/object-types#order">Order</a>!</div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## unsetDraftOrderShippingAddress
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Unsets the shipping address for a draft Order</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
+<div class="graphql-code-line ">unsetDraftOrderShippingAddress(orderId: <a href="/reference/graphql-api/admin/object-types#id">ID</a>!): <a href="/reference/graphql-api/admin/object-types#order">Order</a>!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

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

@@ -134,6 +134,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">orderLine: <a href="/reference/graphql-api/admin/object-types#orderline">OrderLine</a>!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -267,6 +269,26 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## BooleanStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">BooleanStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -336,6 +358,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">orderLine: <a href="/reference/graphql-api/admin/object-types#orderline">OrderLine</a>!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -793,7 +817,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level">union <span class="graphql-code-identifier">CustomFieldConfig</span> =</div>
-<div class="graphql-code-line "><a href="/reference/graphql-api/admin/object-types#stringcustomfieldconfig">StringCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#localestringcustomfieldconfig">LocaleStringCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#intcustomfieldconfig">IntCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#floatcustomfieldconfig">FloatCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#booleancustomfieldconfig">BooleanCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#datetimecustomfieldconfig">DateTimeCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#relationcustomfieldconfig">RelationCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#textcustomfieldconfig">TextCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#localetextcustomfieldconfig">LocaleTextCustomFieldConfig</a></div>
+<div class="graphql-code-line "><a href="/reference/graphql-api/admin/object-types#stringcustomfieldconfig">StringCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#localestringcustomfieldconfig">LocaleStringCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#intcustomfieldconfig">IntCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#floatcustomfieldconfig">FloatCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#booleancustomfieldconfig">BooleanCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#datetimecustomfieldconfig">DateTimeCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#relationcustomfieldconfig">RelationCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#textcustomfieldconfig">TextCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#localetextcustomfieldconfig">LocaleTextCustomFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#structcustomfieldconfig">StructCustomFieldConfig</a></div>
 </div>
 
 ## CustomFields
@@ -802,7 +826,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line top-level comment">"""</div>
 <div class="graphql-code-line top-level comment">This type is deprecated in v2.2 in favor of the EntityCustomFields type,</div>
 
-<div class="graphql-code-line top-level comment">which allows custom fields to be defined on user-supplies entities.</div>
+<div class="graphql-code-line top-level comment">which allows custom fields to be defined on user-supplied entities.</div>
 <div class="graphql-code-line top-level comment">"""</div>
 <div class="graphql-code-line top-level">type <span class="graphql-code-identifier">CustomFields</span> &#123;</div>
 <div class="graphql-code-line ">Address: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
@@ -827,10 +851,14 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">GlobalSettings: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
+<div class="graphql-code-line ">HistoryEntry: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
+
 <div class="graphql-code-line ">Order: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
 <div class="graphql-code-line ">OrderLine: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
+<div class="graphql-code-line ">Payment: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
+
 <div class="graphql-code-line ">PaymentMethod: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
 <div class="graphql-code-line ">Product: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
@@ -845,14 +873,24 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">Promotion: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
+<div class="graphql-code-line ">Refund: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
+
 <div class="graphql-code-line ">Region: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
 <div class="graphql-code-line ">Seller: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
+<div class="graphql-code-line ">Session: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
+
+<div class="graphql-code-line ">ShippingLine: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
+
 <div class="graphql-code-line ">ShippingMethod: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
+<div class="graphql-code-line ">StockLevel: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
+
 <div class="graphql-code-line ">StockLocation: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
+<div class="graphql-code-line ">StockMovement: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
+
 <div class="graphql-code-line ">TaxCategory: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
 
 <div class="graphql-code-line ">TaxRate: [<a href="/reference/graphql-api/admin/object-types#customfieldconfig">CustomFieldConfig</a>!]!</div>
@@ -990,6 +1028,37 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## DateTimeStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Expects the same validation formats as the <code>&lt;input type="datetime-local"&gt;</code> HTML element.</div>
+
+<div class="graphql-code-line top-level comment">See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">DateTimeStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">min: <a href="/reference/graphql-api/admin/object-types#string">String</a></div>
+
+<div class="graphql-code-line ">max: <a href="/reference/graphql-api/admin/object-types#string">String</a></div>
+
+<div class="graphql-code-line ">step: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -1310,6 +1379,32 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## FloatStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">FloatStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">min: <a href="/reference/graphql-api/admin/object-types#float">Float</a></div>
+
+<div class="graphql-code-line ">max: <a href="/reference/graphql-api/admin/object-types#float">Float</a></div>
+
+<div class="graphql-code-line ">step: <a href="/reference/graphql-api/admin/object-types#float">Float</a></div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -1439,6 +1534,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">data: <a href="/reference/graphql-api/admin/object-types#json">JSON</a>!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -1575,6 +1672,32 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## IntStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">IntStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">min: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">max: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">step: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -2140,6 +2263,23 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## OrderInterceptorError
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Returned when an order operation is rejected by an OrderInterceptor method.</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">OrderInterceptorError</span> &#123;</div>
+<div class="graphql-code-line ">errorCode: <a href="/reference/graphql-api/admin/enums#errorcode">ErrorCode</a>!</div>
+
+<div class="graphql-code-line ">message: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">interceptorError: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -2473,6 +2613,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">metadata: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -3106,6 +3248,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">metadata: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -3273,6 +3417,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">quantity: <a href="/reference/graphql-api/admin/object-types#int">Int</a>!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -3295,7 +3441,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level">union <span class="graphql-code-identifier">RemoveOrderItemsResult</span> =</div>
-<div class="graphql-code-line "><a href="/reference/graphql-api/admin/object-types#order">Order</a> | <a href="/reference/graphql-api/admin/object-types#ordermodificationerror">OrderModificationError</a></div>
+<div class="graphql-code-line "><a href="/reference/graphql-api/admin/object-types#order">Order</a> | <a href="/reference/graphql-api/admin/object-types#ordermodificationerror">OrderModificationError</a> | <a href="/reference/graphql-api/admin/object-types#orderinterceptorerror">OrderInterceptorError</a></div>
 </div>
 
 ## Return
@@ -3314,6 +3460,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">quantity: <a href="/reference/graphql-api/admin/object-types#int">Int</a>!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -3368,6 +3516,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">quantity: <a href="/reference/graphql-api/admin/object-types#int">Int</a>!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -3593,6 +3743,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">discounts: [<a href="/reference/graphql-api/admin/object-types#discount">Discount</a>!]!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -3717,6 +3869,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">quantity: <a href="/reference/graphql-api/admin/object-types#int">Int</a>!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -3739,6 +3893,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">stockLocation: <a href="/reference/graphql-api/admin/object-types#stocklocation">StockLocation</a>!</div>
 
+<div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
 
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
@@ -3849,6 +4005,69 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
+## StringStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">StringStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">length: <a href="/reference/graphql-api/admin/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">pattern: <a href="/reference/graphql-api/admin/object-types#string">String</a></div>
+
+<div class="graphql-code-line ">options: [<a href="/reference/graphql-api/admin/object-types#stringfieldoption">StringFieldOption</a>!]</div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## StructCustomFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">StructCustomFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">fields: [<a href="/reference/graphql-api/admin/object-types#structfieldconfig">StructFieldConfig</a>!]!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">readonly: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a></div>
+
+<div class="graphql-code-line ">internal: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a></div>
+
+<div class="graphql-code-line ">nullable: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a></div>
+
+<div class="graphql-code-line ">requiresPermission: [<a href="/reference/graphql-api/admin/enums#permission">Permission</a>!]</div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## StructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">union <span class="graphql-code-identifier">StructFieldConfig</span> =</div>
+<div class="graphql-code-line "><a href="/reference/graphql-api/admin/object-types#stringstructfieldconfig">StringStructFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#intstructfieldconfig">IntStructFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#floatstructfieldconfig">FloatStructFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#booleanstructfieldconfig">BooleanStructFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#datetimestructfieldconfig">DateTimeStructFieldConfig</a> | <a href="/reference/graphql-api/admin/object-types#textstructfieldconfig">TextStructFieldConfig</a></div>
+</div>
+
 ## Success
 
 <div class="graphql-code-block">
@@ -4051,6 +4270,26 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## TextStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">TextStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/admin/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -4100,7 +4339,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level">union <span class="graphql-code-identifier">UpdateOrderItemsResult</span> =</div>
-<div class="graphql-code-line "><a href="/reference/graphql-api/admin/object-types#order">Order</a> | <a href="/reference/graphql-api/admin/object-types#ordermodificationerror">OrderModificationError</a> | <a href="/reference/graphql-api/admin/object-types#orderlimiterror">OrderLimitError</a> | <a href="/reference/graphql-api/admin/object-types#negativequantityerror">NegativeQuantityError</a> | <a href="/reference/graphql-api/admin/object-types#insufficientstockerror">InsufficientStockError</a></div>
+<div class="graphql-code-line "><a href="/reference/graphql-api/admin/object-types#order">Order</a> | <a href="/reference/graphql-api/admin/object-types#ordermodificationerror">OrderModificationError</a> | <a href="/reference/graphql-api/admin/object-types#orderlimiterror">OrderLimitError</a> | <a href="/reference/graphql-api/admin/object-types#negativequantityerror">NegativeQuantityError</a> | <a href="/reference/graphql-api/admin/object-types#insufficientstockerror">InsufficientStockError</a> | <a href="/reference/graphql-api/admin/object-types#orderinterceptorerror">OrderInterceptorError</a></div>
 </div>
 
 ## UpdatePromotionResult

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

@@ -393,6 +393,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ORDER_MODIFICATION_ERROR</div>
 <div class="graphql-code-line ">INELIGIBLE_SHIPPING_METHOD_ERROR</div>
 <div class="graphql-code-line ">NO_ACTIVE_ORDER_ERROR</div>
+<div class="graphql-code-line ">ORDER_INTERCEPTOR_ERROR</div>
 <div class="graphql-code-line ">ORDER_PAYMENT_STATE_ERROR</div>
 <div class="graphql-code-line ">INELIGIBLE_PAYMENT_METHOD_ERROR</div>
 <div class="graphql-code-line ">PAYMENT_FAILED_ERROR</div>

+ 28 - 4
docs/docs/reference/graphql-api/shop/mutations.md

@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 ## addItemToOrder
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level comment">"""</div>
-<div class="graphql-code-line top-level comment">Adds an item to the order. If custom fields are defined on the OrderLine entity, a third argument 'customFields' will be available.</div>
+<div class="graphql-code-line top-level comment">Adds an item to the Order. If custom fields are defined on the OrderLine entity, a third argument 'customFields' will be available.</div>
 <div class="graphql-code-line top-level comment">"""</div>
 <div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
 <div class="graphql-code-line ">addItemToOrder(productVariantId: <a href="/reference/graphql-api/shop/object-types#id">ID</a>!, quantity: <a href="/reference/graphql-api/shop/object-types#int">Int</a>!): <a href="/reference/graphql-api/shop/object-types#updateorderitemsresult">UpdateOrderItemsResult</a>!</div>
@@ -267,7 +267,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 ## setOrderBillingAddress
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level comment">"""</div>
-<div class="graphql-code-line top-level comment">Sets the billing address for this order</div>
+<div class="graphql-code-line top-level comment">Sets the billing address for the active Order</div>
 <div class="graphql-code-line top-level comment">"""</div>
 <div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
 <div class="graphql-code-line ">setOrderBillingAddress(input: <a href="/reference/graphql-api/shop/input-types#createaddressinput">CreateAddressInput</a>!): <a href="/reference/graphql-api/shop/object-types#activeorderresult">ActiveOrderResult</a>!</div>
@@ -279,7 +279,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 ## setOrderCustomFields
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level comment">"""</div>
-<div class="graphql-code-line top-level comment">Allows any custom fields to be set for the active order</div>
+<div class="graphql-code-line top-level comment">Allows any custom fields to be set for the active Order</div>
 <div class="graphql-code-line top-level comment">"""</div>
 <div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
 <div class="graphql-code-line ">setOrderCustomFields(input: <a href="/reference/graphql-api/shop/input-types#updateorderinput">UpdateOrderInput</a>!): <a href="/reference/graphql-api/shop/object-types#activeorderresult">ActiveOrderResult</a>!</div>
@@ -291,7 +291,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 ## setOrderShippingAddress
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level comment">"""</div>
-<div class="graphql-code-line top-level comment">Sets the shipping address for this order</div>
+<div class="graphql-code-line top-level comment">Sets the shipping address for the active Order</div>
 <div class="graphql-code-line top-level comment">"""</div>
 <div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
 <div class="graphql-code-line ">setOrderShippingAddress(input: <a href="/reference/graphql-api/shop/input-types#createaddressinput">CreateAddressInput</a>!): <a href="/reference/graphql-api/shop/object-types#activeorderresult">ActiveOrderResult</a>!</div>
@@ -327,6 +327,30 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">transitionOrderToState(state: <a href="/reference/graphql-api/shop/object-types#string">String</a>!): <a href="/reference/graphql-api/shop/object-types#transitionordertostateresult">TransitionOrderToStateResult</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## unsetOrderBillingAddress
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Unsets the billing address for the active Order. Available since version 3.1.0</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
+<div class="graphql-code-line ">unsetOrderBillingAddress: <a href="/reference/graphql-api/shop/object-types#activeorderresult">ActiveOrderResult</a>!</div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## unsetOrderShippingAddress
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Unsets the shipping address for the active Order. Available since version 3.1.0</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
+<div class="graphql-code-line ">unsetOrderShippingAddress: <a href="/reference/graphql-api/shop/object-types#activeorderresult">ActiveOrderResult</a>!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

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

@@ -207,6 +207,26 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## BooleanStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">BooleanStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -581,7 +601,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level">union <span class="graphql-code-identifier">CustomFieldConfig</span> =</div>
-<div class="graphql-code-line "><a href="/reference/graphql-api/shop/object-types#stringcustomfieldconfig">StringCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#localestringcustomfieldconfig">LocaleStringCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#intcustomfieldconfig">IntCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#floatcustomfieldconfig">FloatCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#booleancustomfieldconfig">BooleanCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#datetimecustomfieldconfig">DateTimeCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#relationcustomfieldconfig">RelationCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#textcustomfieldconfig">TextCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#localetextcustomfieldconfig">LocaleTextCustomFieldConfig</a></div>
+<div class="graphql-code-line "><a href="/reference/graphql-api/shop/object-types#stringcustomfieldconfig">StringCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#localestringcustomfieldconfig">LocaleStringCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#intcustomfieldconfig">IntCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#floatcustomfieldconfig">FloatCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#booleancustomfieldconfig">BooleanCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#datetimecustomfieldconfig">DateTimeCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#relationcustomfieldconfig">RelationCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#textcustomfieldconfig">TextCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#localetextcustomfieldconfig">LocaleTextCustomFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#structcustomfieldconfig">StructCustomFieldConfig</a></div>
 </div>
 
 ## Customer
@@ -693,6 +713,37 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## DateTimeStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Expects the same validation formats as the <code>&lt;input type="datetime-local"&gt;</code> HTML element.</div>
+
+<div class="graphql-code-line top-level comment">See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">DateTimeStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">min: <a href="/reference/graphql-api/shop/object-types#string">String</a></div>
+
+<div class="graphql-code-line ">max: <a href="/reference/graphql-api/shop/object-types#string">String</a></div>
+
+<div class="graphql-code-line ">step: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -917,6 +968,32 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## FloatStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">FloatStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">min: <a href="/reference/graphql-api/shop/object-types#float">Float</a></div>
+
+<div class="graphql-code-line ">max: <a href="/reference/graphql-api/shop/object-types#float">Float</a></div>
+
+<div class="graphql-code-line ">step: <a href="/reference/graphql-api/shop/object-types#float">Float</a></div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -995,6 +1072,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">data: <a href="/reference/graphql-api/shop/object-types#json">JSON</a>!</div>
 
+<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>
@@ -1145,6 +1224,32 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## IntStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">IntStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">min: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">max: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">step: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -1483,6 +1588,23 @@ 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>
+
+## OrderInterceptorError
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Returned when an order operation is rejected by an OrderInterceptor method.</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">OrderInterceptorError</span> &#123;</div>
+<div class="graphql-code-line ">errorCode: <a href="/reference/graphql-api/shop/enums#errorcode">ErrorCode</a>!</div>
+
+<div class="graphql-code-line ">message: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">interceptorError: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -1822,6 +1944,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">metadata: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
 
+<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>
@@ -2350,6 +2474,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">metadata: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
 
+<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>
@@ -2433,7 +2559,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level">union <span class="graphql-code-identifier">RemoveOrderItemsResult</span> =</div>
-<div class="graphql-code-line "><a href="/reference/graphql-api/shop/object-types#order">Order</a> | <a href="/reference/graphql-api/shop/object-types#ordermodificationerror">OrderModificationError</a></div>
+<div class="graphql-code-line "><a href="/reference/graphql-api/shop/object-types#order">Order</a> | <a href="/reference/graphql-api/shop/object-types#ordermodificationerror">OrderModificationError</a> | <a href="/reference/graphql-api/shop/object-types#orderinterceptorerror">OrderInterceptorError</a></div>
 </div>
 
 ## RequestPasswordResetResult
@@ -2637,6 +2763,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-line ">discounts: [<a href="/reference/graphql-api/shop/object-types#discount">Discount</a>!]!</div>
 
+<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>
@@ -2800,6 +2928,69 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
+## StringStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">StringStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">length: <a href="/reference/graphql-api/shop/object-types#int">Int</a></div>
+
+<div class="graphql-code-line ">pattern: <a href="/reference/graphql-api/shop/object-types#string">String</a></div>
+
+<div class="graphql-code-line ">options: [<a href="/reference/graphql-api/shop/object-types#stringfieldoption">StringFieldOption</a>!]</div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## StructCustomFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">StructCustomFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">fields: [<a href="/reference/graphql-api/shop/object-types#structfieldconfig">StructFieldConfig</a>!]!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">readonly: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a></div>
+
+<div class="graphql-code-line ">internal: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a></div>
+
+<div class="graphql-code-line ">nullable: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a></div>
+
+<div class="graphql-code-line ">requiresPermission: [<a href="/reference/graphql-api/shop/enums#permission">Permission</a>!]</div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## StructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">union <span class="graphql-code-identifier">StructFieldConfig</span> =</div>
+<div class="graphql-code-line "><a href="/reference/graphql-api/shop/object-types#stringstructfieldconfig">StringStructFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#intstructfieldconfig">IntStructFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#floatstructfieldconfig">FloatStructFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#booleanstructfieldconfig">BooleanStructFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#datetimestructfieldconfig">DateTimeStructFieldConfig</a> | <a href="/reference/graphql-api/shop/object-types#textstructfieldconfig">TextStructFieldConfig</a></div>
+</div>
+
 ## Success
 
 <div class="graphql-code-block">
@@ -2964,6 +3155,26 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## TextStructFieldConfig
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">TextStructFieldConfig</span> &#123;</div>
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">type: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">list: <a href="/reference/graphql-api/shop/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">label: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">description: [<a href="/reference/graphql-api/shop/object-types#localizedstring">LocalizedString</a>!]</div>
+
+<div class="graphql-code-line ">ui: <a href="/reference/graphql-api/shop/object-types#json">JSON</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 
@@ -2992,7 +3203,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <div class="graphql-code-block">
 <div class="graphql-code-line top-level">union <span class="graphql-code-identifier">UpdateOrderItemsResult</span> =</div>
-<div class="graphql-code-line "><a href="/reference/graphql-api/shop/object-types#order">Order</a> | <a href="/reference/graphql-api/shop/object-types#ordermodificationerror">OrderModificationError</a> | <a href="/reference/graphql-api/shop/object-types#orderlimiterror">OrderLimitError</a> | <a href="/reference/graphql-api/shop/object-types#negativequantityerror">NegativeQuantityError</a> | <a href="/reference/graphql-api/shop/object-types#insufficientstockerror">InsufficientStockError</a></div>
+<div class="graphql-code-line "><a href="/reference/graphql-api/shop/object-types#order">Order</a> | <a href="/reference/graphql-api/shop/object-types#ordermodificationerror">OrderModificationError</a> | <a href="/reference/graphql-api/shop/object-types#orderlimiterror">OrderLimitError</a> | <a href="/reference/graphql-api/shop/object-types#negativequantityerror">NegativeQuantityError</a> | <a href="/reference/graphql-api/shop/object-types#insufficientstockerror">InsufficientStockError</a> | <a href="/reference/graphql-api/shop/object-types#orderinterceptorerror">OrderInterceptorError</a></div>
 </div>
 
 ## Upload

+ 1 - 1
docs/docs/reference/typescript-api/assets/asset-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AssetOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="627" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="639" packageName="@vendure/core" />
 
 The AssetOptions define how assets (images and other files) are named and stored, and how preview images are generated.
 

+ 13 - 12
docs/docs/reference/typescript-api/auth/auth-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AuthOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="330" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="332" packageName="@vendure/core" />
 
 The AuthOptions define how authentication and authorization is managed.
 
@@ -23,7 +23,7 @@ interface AuthOptions {
     authTokenHeaderKey?: string;
     sessionDuration?: string | number;
     sessionCacheStrategy?: SessionCacheStrategy;
-    sessionCacheTTL?: number;
+    sessionCacheTTL?: string | number;
     requireVerification?: boolean;
     verificationTokenDuration?: string | number;
     superadminCredentials?: SuperadminCredentials;
@@ -78,22 +78,23 @@ Sets the header property which will be used to send the auth token when using th
 Session duration, i.e. the time which must elapse from the last authenticated request
 after which the user must re-authenticate.
 
-Expressed as a string describing a time span per
+If passed as a number should represent milliseconds and if passed as a string describes a time span per
 [zeit/ms](https://github.com/zeit/ms.js).  Eg: `60`, `'2 days'`, `'10h'`, `'7d'`
 ### sessionCacheStrategy
 
-<MemberInfo kind="property" type={`<a href='/reference/typescript-api/auth/session-cache-strategy#sessioncachestrategy'>SessionCacheStrategy</a>`} default={`<a href='/reference/typescript-api/auth/in-memory-session-cache-strategy#inmemorysessioncachestrategy'>InMemorySessionCacheStrategy</a>`}   />
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/auth/session-cache-strategy#sessioncachestrategy'>SessionCacheStrategy</a>`} default={`<a href='/reference/typescript-api/auth/default-session-cache-strategy#defaultsessioncachestrategy'>DefaultSessionCacheStrategy</a>`}   />
 
-This strategy defines how sessions will be cached. By default, sessions are cached using a simple
-in-memory caching strategy which is suitable for development and low-traffic, single-instance
-deployments.
+This strategy defines how sessions will be cached. By default, since v3.1.0, sessions are cached using
+the underlying cache strategy defined in the <a href='/reference/typescript-api/configuration/system-options#systemoptions'>SystemOptions</a>`.cacheStrategy`.
 ### sessionCacheTTL
 
-<MemberInfo kind="property" type={`number`} default={`300`}   />
+<MemberInfo kind="property" type={`string | number`} default={`300`}   />
 
-The "time to live" of a given item in the session cache. This determines the length of time (in seconds)
-that a cache entry is kept before being considered "stale" and being replaced with fresh data
-taken from the database.
+The "time to live" of a given item in the session cache. This determines the length of time that a cache entry
+is kept before being considered "stale" and being replaced with fresh data taken from the database.
+
+If passed as a number should represent seconds and if passed as a string describes a time span per
+[zeit/ms](https://github.com/zeit/ms.js). Eg: `60`, `'2 days'`, `'10h'`, `'7d'`
 ### requireVerification
 
 <MemberInfo kind="property" type={`boolean`} default={`true`}   />
@@ -108,7 +109,7 @@ they receive in their email. See the `registerCustomerAccount` mutation for more
 
 Sets the length of time that a verification token is valid for, after which the verification token must be refreshed.
 
-Expressed as a string describing a time span per
+If passed as a number should represent milliseconds and if passed as a string describes a time span per
 [zeit/ms](https://github.com/zeit/ms.js).  Eg: `60`, `'2 days'`, `'10h'`, `'7d'`
 ### superadminCredentials
 

+ 1 - 1
docs/docs/reference/typescript-api/auth/cookie-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CookieOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="225" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="227" packageName="@vendure/core" />
 
 Options for the handling of the cookies used to track sessions (only applicable if
 `authOptions.tokenMethod` is set to `'cookie'`). These options are passed directly

+ 77 - 0
docs/docs/reference/typescript-api/auth/default-session-cache-strategy.md

@@ -0,0 +1,77 @@
+---
+title: "DefaultSessionCacheStrategy"
+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';
+
+
+## DefaultSessionCacheStrategy
+
+<GenerationInfo sourceFile="packages/core/src/config/session-cache/default-session-cache-strategy.ts" sourceLine="17" packageName="@vendure/core" since="3.1.0" />
+
+The default <a href='/reference/typescript-api/auth/session-cache-strategy#sessioncachestrategy'>SessionCacheStrategy</a> delegates to the configured
+<a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a> to store the session data. This should be suitable
+for most use-cases, assuming you select a suitable <a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a>
+
+```ts title="Signature"
+class DefaultSessionCacheStrategy implements SessionCacheStrategy {
+    protected cacheService: CacheService;
+    constructor(options?: {
+            ttl?: number;
+            cachePrefix?: string;
+        })
+    init(injector: Injector) => ;
+    set(session: CachedSession) => Promise<void>;
+    get(sessionToken: string) => Promise<CachedSession | undefined>;
+    delete(sessionToken: string) => void | Promise<void>;
+    clear() => Promise<void>;
+}
+```
+* Implements: <code><a href='/reference/typescript-api/auth/session-cache-strategy#sessioncachestrategy'>SessionCacheStrategy</a></code>
+
+
+
+<div className="members-wrapper">
+
+### cacheService
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/cache/cache-service#cacheservice'>CacheService</a>`}   />
+
+
+### constructor
+
+<MemberInfo kind="method" type={`(options?: {
             ttl?: number;
             cachePrefix?: string;
         }) => DefaultSessionCacheStrategy`}   />
+
+
+### init
+
+<MemberInfo kind="method" type={`(injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>) => `}   />
+
+
+### set
+
+<MemberInfo kind="method" type={`(session: <a href='/reference/typescript-api/auth/session-cache-strategy#cachedsession'>CachedSession</a>) => Promise&#60;void&#62;`}   />
+
+
+### get
+
+<MemberInfo kind="method" type={`(sessionToken: string) => Promise&#60;<a href='/reference/typescript-api/auth/session-cache-strategy#cachedsession'>CachedSession</a> | undefined&#62;`}   />
+
+
+### delete
+
+<MemberInfo kind="method" type={`(sessionToken: string) => void | Promise&#60;void&#62;`}   />
+
+
+### clear
+
+<MemberInfo kind="method" type={`() => Promise&#60;void&#62;`}   />
+
+
+
+
+</div>

+ 12 - 0
docs/docs/reference/typescript-api/auth/external-authentication-service.md

@@ -39,6 +39,10 @@ class ExternalAuthenticationService {
             roles: Role[];
         }) => ;
     findUser(ctx: RequestContext, strategy: string, externalIdentifier: string) => Promise<User | undefined>;
+    createUser(ctx: RequestContext, config: {
+            strategy: string;
+            externalIdentifier: string;
+        }) => Promise<User>;
 }
 ```
 
@@ -85,6 +89,14 @@ Administrator record in Vendure for that user.
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, strategy: string, externalIdentifier: string) => Promise&#60;<a href='/reference/typescript-api/entities/user#user'>User</a> | undefined&#62;`}   />
 
 
+### createUser
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, config: {
             strategy: string;
             externalIdentifier: string;
         }) => Promise&#60;<a href='/reference/typescript-api/entities/user#user'>User</a>&#62;`}   />
+
+Looks up a User based on their identifier from an external authentication
+provider. Creates the user if does not exist. Unlike `findCustomerUser` and `findAdministratorUser`,
+this method does not enforce that the User is associated with a Customer or
+Administrator account.
 
 
 </div>

+ 14 - 5
docs/docs/reference/typescript-api/auth/session-cache-strategy.md

@@ -11,17 +11,26 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## SessionCacheStrategy
 
-<GenerationInfo sourceFile="packages/core/src/config/session-cache/session-cache-strategy.ts" sourceLine="155" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/session-cache/session-cache-strategy.ts" sourceLine="164" packageName="@vendure/core" />
 
 This strategy defines how sessions get cached. Since most requests will need the Session
 object for permissions data, it can become a bottleneck to go to the database and do a multi-join
 SQL query each time. Therefore, we cache the session data only perform the SQL query once and upon
 invalidation of the cache.
 
-The Vendure default is to use a the <a href='/reference/typescript-api/auth/in-memory-session-cache-strategy#inmemorysessioncachestrategy'>InMemorySessionCacheStrategy</a>, which is fast and suitable for
-single-instance deployments. However, for multi-instance deployments (horizontally scaled, serverless etc.),
-you will need to define a custom strategy that stores the session cache in a shared data store, such as in the
-DB or in Redis.
+The Vendure default from v3.1+ is to use a the <a href='/reference/typescript-api/auth/default-session-cache-strategy#defaultsessioncachestrategy'>DefaultSessionCacheStrategy</a>, which delegates
+to the configured <a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a> to store the session data. This should be suitable
+for most use-cases.
+
+:::note
+
+If you are using v3.1 or later, you should not normally need to implement a custom `SessionCacheStrategy`,
+since this is now handled by the <a href='/reference/typescript-api/auth/default-session-cache-strategy#defaultsessioncachestrategy'>DefaultSessionCacheStrategy</a>.
+
+:::
+
+Prior to v3.1, the default was to use the <a href='/reference/typescript-api/auth/in-memory-session-cache-strategy#inmemorysessioncachestrategy'>InMemorySessionCacheStrategy</a>, which is fast but suitable for
+single-instance deployments.
 
 :::info
 

+ 1 - 1
docs/docs/reference/typescript-api/auth/superadmin-credentials.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## SuperadminCredentials
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="803" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="815" packageName="@vendure/core" />
 
 These credentials will be used to create the Superadmin user & administrator
 when Vendure first bootstraps.

+ 48 - 0
docs/docs/reference/typescript-api/cache/cache-config.md

@@ -0,0 +1,48 @@
+---
+title: "CacheConfig"
+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';
+
+
+## CacheConfig
+
+<GenerationInfo sourceFile="packages/core/src/cache/cache.ts" sourceLine="14" packageName="@vendure/core" since="3.1.0" />
+
+Configuration for a new <a href='/reference/typescript-api/cache/#cache'>Cache</a> instance.
+
+```ts title="Signature"
+interface CacheConfig {
+    getKey: (id: string | number) => string;
+    options?: SetCacheKeyOptions;
+}
+```
+
+<div className="members-wrapper">
+
+### getKey
+
+<MemberInfo kind="property" type={`(id: string | number) =&#62; string`}   />
+
+A function which generates a cache key from the given id.
+This key will be used to store the value in the cache.
+
+By convention, the key should be namespaced to avoid conflicts.
+
+*Example*
+
+```ts
+getKey: id => `MyStrategy:getProductVariantIds:${id}`,
+```
+### options
+
+<MemberInfo kind="property" type={`SetCacheKeyOptions`}   />
+
+Options available when setting the value in the cache.
+
+
+</div>

+ 80 - 0
docs/docs/reference/typescript-api/cache/cache-service.md

@@ -0,0 +1,80 @@
+---
+title: "CacheService"
+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';
+
+
+## CacheService
+
+<GenerationInfo sourceFile="packages/core/src/cache/cache.service.ts" sourceLine="20" packageName="@vendure/core" since="3.1.0" />
+
+The CacheService is used to cache data in order to optimize performance.
+
+Internally it makes use of the configured <a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a> to persist
+the cache into a key-value store.
+
+```ts title="Signature"
+class CacheService {
+    protected cacheStrategy: CacheStrategy;
+    constructor(configService: ConfigService)
+    createCache(config: CacheConfig) => Cache;
+    get(key: string) => Promise<T | undefined>;
+    set(key: string, value: T, options?: SetCacheKeyOptions) => Promise<void>;
+    delete(key: string) => Promise<void>;
+    invalidateTags(tags: string[]) => Promise<void>;
+}
+```
+
+<div className="members-wrapper">
+
+### cacheStrategy
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a>`}   />
+
+
+### constructor
+
+<MemberInfo kind="method" type={`(configService: ConfigService) => CacheService`}   />
+
+
+### createCache
+
+<MemberInfo kind="method" type={`(config: <a href='/reference/typescript-api/cache/cache-config#cacheconfig'>CacheConfig</a>) => <a href='/reference/typescript-api/cache/#cache'>Cache</a>`}   />
+
+Creates a new <a href='/reference/typescript-api/cache/#cache'>Cache</a> instance with the given configuration.
+
+The `Cache` instance provides a convenience wrapper around the `CacheService`
+methods.
+### get
+
+<MemberInfo kind="method" type={`(key: string) => Promise&#60;T | undefined&#62;`}   />
+
+Gets an item from the cache, or returns undefined if the key is not found, or the
+item has expired.
+### set
+
+<MemberInfo kind="method" type={`(key: string, value: T, options?: SetCacheKeyOptions) => Promise&#60;void&#62;`}   />
+
+Sets a key-value pair in the cache. The value must be serializable, so cannot contain
+things like functions, circular data structures, class instances etc.
+
+Optionally a "time to live" (ttl) can be specified, which means that the key will
+be considered stale after that many milliseconds.
+### delete
+
+<MemberInfo kind="method" type={`(key: string) => Promise&#60;void&#62;`}   />
+
+Deletes an item from the cache.
+### invalidateTags
+
+<MemberInfo kind="method" type={`(tags: string[]) => Promise&#60;void&#62;`}   />
+
+Deletes all items from the cache which contain at least one matching tag.
+
+
+</div>

+ 62 - 0
docs/docs/reference/typescript-api/cache/cache-strategy.md

@@ -0,0 +1,62 @@
+---
+title: "CacheStrategy"
+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';
+
+
+## CacheStrategy
+
+<GenerationInfo sourceFile="packages/core/src/config/system/cache-strategy.ts" sourceLine="36" packageName="@vendure/core" since="3.1.0" />
+
+The CacheStrategy defines how the underlying shared cache mechanism is implemented.
+
+It is used by the <a href='/reference/typescript-api/cache/cache-service#cacheservice'>CacheService</a> to take care of storage and retrieval of items
+from the cache.
+
+```ts title="Signature"
+interface CacheStrategy extends InjectableStrategy {
+    get<T extends JsonCompatible<T>>(key: string): Promise<T | undefined>;
+    set<T extends JsonCompatible<T>>(key: string, value: T, options?: SetCacheKeyOptions): Promise<void>;
+    delete(key: string): Promise<void>;
+    invalidateTags(tags: string[]): Promise<void>;
+}
+```
+* Extends: <code><a href='/reference/typescript-api/common/injectable-strategy#injectablestrategy'>InjectableStrategy</a></code>
+
+
+
+<div className="members-wrapper">
+
+### get
+
+<MemberInfo kind="method" type={`(key: string) => Promise&#60;T | undefined&#62;`}   />
+
+Gets an item from the cache, or returns undefined if the key is not found, or the
+item has expired.
+### set
+
+<MemberInfo kind="method" type={`(key: string, value: T, options?: SetCacheKeyOptions) => Promise&#60;void&#62;`}   />
+
+Sets a key-value pair in the cache. The value must be serializable, so cannot contain
+things like functions, circular data structures, class instances etc.
+
+Optionally a "time to live" (ttl) can be specified, which means that the key will
+be considered stale after that many milliseconds.
+### delete
+
+<MemberInfo kind="method" type={`(key: string) => Promise&#60;void&#62;`}   />
+
+Deletes an item from the cache.
+### invalidateTags
+
+<MemberInfo kind="method" type={`(tags: string[]) => Promise&#60;void&#62;`}   />
+
+Deletes all items from the cache which contain at least one matching tag.
+
+
+</div>

+ 44 - 0
docs/docs/reference/typescript-api/cache/default-cache-plugin.md

@@ -0,0 +1,44 @@
+---
+title: "DefaultCachePlugin"
+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';
+
+
+## DefaultCachePlugin
+
+<GenerationInfo sourceFile="packages/core/src/plugin/default-cache-plugin/default-cache-plugin.ts" sourceLine="34" packageName="@vendure/core" since="3.1.0" />
+
+This plugin provides a simple SQL-based cache strategy <a href='/reference/typescript-api/cache/sql-cache-strategy#sqlcachestrategy'>SqlCacheStrategy</a> which stores cached
+items in the database.
+It is suitable for production use (including multi-instance setups). For increased performance
+you can also consider using the <a href='/reference/typescript-api/cache/redis-cache-plugin#rediscacheplugin'>RedisCachePlugin</a>.
+
+```ts title="Signature"
+class DefaultCachePlugin {
+    static options: DefaultCachePluginInitOptions = {
+        cacheSize: 10_000,
+    };
+    init(options: DefaultCachePluginInitOptions) => ;
+}
+```
+
+<div className="members-wrapper">
+
+### options
+
+<MemberInfo kind="property" type={`DefaultCachePluginInitOptions`}   />
+
+
+### init
+
+<MemberInfo kind="method" type={`(options: DefaultCachePluginInitOptions) => `}   />
+
+
+
+
+</div>

+ 83 - 0
docs/docs/reference/typescript-api/cache/index.md

@@ -0,0 +1,83 @@
+---
+title: "Cache"
+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';
+
+
+## Cache
+
+<GenerationInfo sourceFile="packages/core/src/cache/cache.ts" sourceLine="72" packageName="@vendure/core" since="3.1.0" />
+
+A convenience wrapper around the <a href='/reference/typescript-api/cache/cache-service#cacheservice'>CacheService</a> methods which provides a simple
+API for caching and retrieving data.
+
+The advantage of using the `Cache` class rather than directly calling the `CacheService`
+methods is that it allows you to define a consistent way of generating cache keys and
+to set default cache options, and takes care of setting the value in cache if it does not
+already exist.
+
+In most cases, using the `Cache` class will result in simpler and more readable code.
+
+This class is normally created via the <a href='/reference/typescript-api/cache/cache-service#cacheservice'>CacheService</a>.`createCache()` method.
+
+*Example*
+
+```ts
+const cache = cacheService.createCache({
+  getKey: id => `ProductVariantIds:${id}`,
+  options: {
+    ttl: 1000 * 60 * 60,
+    tags: ['products'],
+  },
+});
+
+// This will fetch the value from the cache if it exists, or
+// fetch it from the ProductService if not, and then cache
+// using the key 'ProductVariantIds.${id}'.
+const variantIds = await cache.get(id, async () => {
+  const variants await ProductService.getVariantsByProductId(ctx, id) ;
+  // The cached value must be serializable, so we just return the ids
+  return variants.map(v => v.id);
+});
+```
+
+```ts title="Signature"
+class Cache {
+    constructor(config: CacheConfig, cacheService: CacheService)
+    get(id: string | number, getValueFn: () => T | Promise<T>) => Promise<T>;
+    delete(id: string | number | Array<string | number>) => Promise<void>;
+    invalidateTags(tags: string[]) => Promise<void>;
+}
+```
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(config: <a href='/reference/typescript-api/cache/cache-config#cacheconfig'>CacheConfig</a>, cacheService: <a href='/reference/typescript-api/cache/cache-service#cacheservice'>CacheService</a>) => Cache`}   />
+
+
+### get
+
+<MemberInfo kind="method" type={`(id: string | number, getValueFn: () =&#62; T | Promise&#60;T&#62;) => Promise&#60;T&#62;`}   />
+
+Retrieves the value from the cache if it exists, otherwise calls the `getValueFn` function
+to get the value, sets it in the cache and returns it.
+### delete
+
+<MemberInfo kind="method" type={`(id: string | number | Array&#60;string | number&#62;) => Promise&#60;void&#62;`}   />
+
+Deletes one or more items from the cache.
+### invalidateTags
+
+<MemberInfo kind="method" type={`(tags: string[]) => Promise&#60;void&#62;`}   />
+
+Invalidates one or more tags in the cache.
+
+
+</div>

+ 45 - 0
docs/docs/reference/typescript-api/cache/redis-cache-plugin.md

@@ -0,0 +1,45 @@
+---
+title: "RedisCachePlugin"
+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';
+
+
+## RedisCachePlugin
+
+<GenerationInfo sourceFile="packages/core/src/plugin/redis-cache-plugin/redis-cache-plugin.ts" sourceLine="17" packageName="@vendure/core" since="3.1.0" />
+
+This plugin provides a Redis-based <a href='/reference/typescript-api/cache/redis-cache-strategy#rediscachestrategy'>RedisCacheStrategy</a> which stores cached items in a Redis instance.
+This is a high-performance cache strategy which is suitable for production use, and is a drop-in
+replacement for the <a href='/reference/typescript-api/cache/default-cache-plugin#defaultcacheplugin'>DefaultCachePlugin</a>.
+
+```ts title="Signature"
+class RedisCachePlugin {
+    static options: RedisCachePluginInitOptions = {
+        maxItemSizeInBytes: 128_000,
+        redisOptions: {},
+        namespace: 'vendure-cache',
+    };
+    init(options: RedisCachePluginInitOptions) => ;
+}
+```
+
+<div className="members-wrapper">
+
+### options
+
+<MemberInfo kind="property" type={`RedisCachePluginInitOptions`}   />
+
+
+### init
+
+<MemberInfo kind="method" type={`(options: RedisCachePluginInitOptions) => `}   />
+
+
+
+
+</div>

+ 73 - 0
docs/docs/reference/typescript-api/cache/redis-cache-strategy.md

@@ -0,0 +1,73 @@
+---
+title: "RedisCacheStrategy"
+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';
+
+
+## RedisCacheStrategy
+
+<GenerationInfo sourceFile="packages/core/src/plugin/redis-cache-plugin/redis-cache-strategy.ts" sourceLine="17" packageName="@vendure/core" since="3.1.0" />
+
+A <a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a> which stores cached items in a Redis instance.
+This is a high-performance cache strategy which is suitable for production use.
+
+```ts title="Signature"
+class RedisCacheStrategy implements CacheStrategy {
+    constructor(options: RedisCachePluginInitOptions)
+    init() => ;
+    destroy() => ;
+    get(key: string) => Promise<T | undefined>;
+    set(key: string, value: T, options?: SetCacheKeyOptions) => Promise<void>;
+    delete(key: string) => Promise<void>;
+    invalidateTags(tags: string[]) => Promise<void>;
+}
+```
+* Implements: <code><a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a></code>
+
+
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(options: RedisCachePluginInitOptions) => RedisCacheStrategy`}   />
+
+
+### init
+
+<MemberInfo kind="method" type={`() => `}   />
+
+
+### destroy
+
+<MemberInfo kind="method" type={`() => `}   />
+
+
+### get
+
+<MemberInfo kind="method" type={`(key: string) => Promise&#60;T | undefined&#62;`}   />
+
+
+### set
+
+<MemberInfo kind="method" type={`(key: string, value: T, options?: SetCacheKeyOptions) => Promise&#60;void&#62;`}   />
+
+
+### delete
+
+<MemberInfo kind="method" type={`(key: string) => Promise&#60;void&#62;`}   />
+
+
+### invalidateTags
+
+<MemberInfo kind="method" type={`(tags: string[]) => Promise&#60;void&#62;`}   />
+
+
+
+
+</div>

+ 58 - 0
docs/docs/reference/typescript-api/cache/request-context-cache-service.md

@@ -0,0 +1,58 @@
+---
+title: "RequestContextCacheService"
+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';
+
+
+## RequestContextCacheService
+
+<GenerationInfo sourceFile="packages/core/src/cache/request-context-cache.service.ts" sourceLine="15" packageName="@vendure/core" />
+
+This service is used to cache arbitrary data relative to an ongoing request.
+It does this by using a WeakMap bound to the current RequestContext, so the cached
+data is available for the duration of the request. Once the request completes, the
+cached data will be automatically garbage-collected.
+
+This is useful for caching data which is expensive to compute and which is needed
+multiple times during the handling of a single request.
+
+```ts title="Signature"
+class RequestContextCacheService {
+    set(ctx: RequestContext, key: any, val: T) => void;
+    get(ctx: RequestContext, key: any) => T | undefined;
+    get(ctx: RequestContext, key: any, getDefault?: () => T) => T;
+    get(ctx: RequestContext, key: any, getDefault?: () => T) => T | Promise<T> | undefined;
+}
+```
+
+<div className="members-wrapper">
+
+### set
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, key: any, val: T) => void`}   />
+
+Set a value in the RequestContext cache.
+### get
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, key: any) => T | undefined`}   />
+
+Get a value from the RequestContext cache. If the value is not found, the `getDefault`
+function will be called to get the value, which will then be cached and returned.
+### get
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, key: any, getDefault?: () =&#62; T) => T`}   />
+
+
+### get
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, key: any, getDefault?: () =&#62; T) => T | Promise&#60;T&#62; | undefined`}   />
+
+
+
+
+</div>

+ 90 - 0
docs/docs/reference/typescript-api/cache/sql-cache-strategy.md

@@ -0,0 +1,90 @@
+---
+title: "SqlCacheStrategy"
+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';
+
+
+## SqlCacheStrategy
+
+<GenerationInfo sourceFile="packages/core/src/plugin/default-cache-plugin/sql-cache-strategy.ts" sourceLine="18" packageName="@vendure/core" since="3.1.0" />
+
+
+
+```ts title="Signature"
+class SqlCacheStrategy implements CacheStrategy {
+    protected cacheSize = 10_000;
+    protected ttlProvider: CacheTtlProvider;
+    constructor(config?: { cacheSize?: number; cacheTtlProvider?: CacheTtlProvider })
+    protected connection: TransactionalConnection;
+    protected configService: ConfigService;
+    init(injector: Injector) => ;
+    get(key: string) => Promise<T | undefined>;
+    set(key: string, value: T, options?: SetCacheKeyOptions) => ;
+    delete(key: string) => ;
+    invalidateTags(tags: string[]) => ;
+}
+```
+* Implements: <code><a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a></code>
+
+
+
+<div className="members-wrapper">
+
+### cacheSize
+
+<MemberInfo kind="property" type={``}   />
+
+
+### ttlProvider
+
+<MemberInfo kind="property" type={`CacheTtlProvider`}   />
+
+
+### constructor
+
+<MemberInfo kind="method" type={`(config?: { cacheSize?: number; cacheTtlProvider?: CacheTtlProvider }) => SqlCacheStrategy`}   />
+
+
+### connection
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>`}   />
+
+
+### configService
+
+<MemberInfo kind="property" type={`ConfigService`}   />
+
+
+### init
+
+<MemberInfo kind="method" type={`(injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>) => `}   />
+
+
+### get
+
+<MemberInfo kind="method" type={`(key: string) => Promise&#60;T | undefined&#62;`}   />
+
+
+### set
+
+<MemberInfo kind="method" type={`(key: string, value: T, options?: SetCacheKeyOptions) => `}   />
+
+
+### delete
+
+<MemberInfo kind="method" type={`(key: string) => `}   />
+
+
+### invalidateTags
+
+<MemberInfo kind="method" type={`(tags: string[]) => `}   />
+
+
+
+
+</div>

+ 1 - 1
docs/docs/reference/typescript-api/common/admin-ui/admin-ui-app-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AdminUiAppConfig
 
-<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="349" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="355" packageName="@vendure/common" />
 
 Configures the path to a custom-build of the Admin UI app.
 

+ 1 - 1
docs/docs/reference/typescript-api/common/admin-ui/admin-ui-app-dev-mode-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AdminUiAppDevModeConfig
 
-<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="377" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="383" packageName="@vendure/common" />
 
 Information about the Admin UI app dev server.
 

+ 1 - 1
docs/docs/reference/typescript-api/common/admin-ui/admin-ui-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AdminUiConfig
 
-<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="215" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="221" packageName="@vendure/common" />
 
 This interface describes JSON config file (vendure-ui-config.json) used by the Admin UI.
 The values are loaded at run-time by the Admin UI app, and allow core configuration to be

+ 46 - 2
docs/docs/reference/typescript-api/common/bootstrap.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## bootstrap
 
-<GenerationInfo sourceFile="packages/core/src/bootstrap.ts" sourceLine="106" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/bootstrap.ts" sourceLine="159" packageName="@vendure/core" />
 
 Bootstraps the Vendure server. This is the entry point to the application.
 
@@ -49,6 +49,27 @@ bootstrap(config, {
 });
 ```
 
+### Ignoring compatibility errors for plugins
+
+Since v3.1.0, you can ignore compatibility errors for specific plugins by passing the `ignoreCompatibilityErrorsForPlugins` option.
+
+This should be used with caution, only if you are sure that the plugin will still work as expected with the current version of Vendure.
+
+*Example*
+
+```ts
+import { bootstrap } from '@vendure/core';
+import { config } from './vendure-config';
+import { MyPlugin } from './plugins/my-plugin';
+
+bootstrap(config, {
+  // Let's say that `MyPlugin` is not yet compatible with the current version of Vendure
+  // but we know that it will still work as expected, and we are not able to publish
+  // a new version of the plugin right now.
+  ignoreCompatibilityErrorsForPlugins: [MyPlugin],
+});
+```
+
 ```ts title="Signature"
 function bootstrap(userConfig: Partial<VendureConfig>, options?: BootstrapOptions): Promise<INestApplication>
 ```
@@ -73,7 +94,8 @@ Vendure server.
 
 ```ts title="Signature"
 interface BootstrapOptions {
-    nestApplicationOptions: NestApplicationOptions;
+    nestApplicationOptions?: NestApplicationOptions;
+    ignoreCompatibilityErrorsForPlugins?: Array<DynamicModule | Type<any>>;
 }
 ```
 
@@ -84,6 +106,28 @@ interface BootstrapOptions {
 <MemberInfo kind="property" type={`NestApplicationOptions`}   />
 
 These options get passed directly to the `NestFactory.create()` method.
+### ignoreCompatibilityErrorsForPlugins
+
+<MemberInfo kind="property" type={`Array&#60;DynamicModule | Type&#60;any&#62;&#62;`} default={`[]`}  since="3.1.0"  />
+
+By default, if a plugin specifies a compatibility range which does not include the current
+Vendure version, the bootstrap process will fail. This option allows you to ignore compatibility
+errors for specific plugins.
+
+This setting should be used with caution, only if you are sure that the plugin will still
+work as expected with the current version of Vendure.
+
+*Example*
+
+```ts
+import { bootstrap } from '@vendure/core';
+import { config } from './vendure-config';
+import { MyPlugin } from './plugins/my-plugin';
+
+bootstrap(config, {
+ ignoreCompatibilityErrorsForPlugins: [MyPlugin],
+});
+```
 
 
 </div>

+ 1 - 1
docs/docs/reference/typescript-api/common/currency-code.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CurrencyCode
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="982" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="994" packageName="@vendure/common" />
 
 ISO 4217 currency code
 

+ 1 - 1
docs/docs/reference/typescript-api/common/entity-relation-paths.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## EntityRelationPaths
 
-<GenerationInfo sourceFile="packages/core/src/common/types/entity-relation-paths.ts" sourceLine="23" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/common/types/entity-relation-paths.ts" sourceLine="25" packageName="@vendure/core" />
 
 This type allows type-safe access to entity relations using strings with dot notation.
 It works to 2 levels deep.

+ 1 - 1
docs/docs/reference/typescript-api/common/job-state.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## JobState
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2174" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2238" packageName="@vendure/common" />
 
 The state of a Job in the JobQueue
 

+ 1 - 1
docs/docs/reference/typescript-api/common/language-code.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## LanguageCode
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2192" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2256" packageName="@vendure/common" />
 
 Languages in the form of a ISO 639-1 language code with optional
 region or script modifier (e.g. de_AT). The selection available is based

+ 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="4335" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="4422" 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.

+ 1 - 1
docs/docs/reference/typescript-api/configurable-operation-def/config-arg-type.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ConfigArgType
 
-<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="126" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="130" packageName="@vendure/common" />
 
 Certain entities (those which implement <a href='/reference/typescript-api/configurable-operation-def/#configurableoperationdef'>ConfigurableOperationDef</a>) allow arbitrary
 configuration arguments to be specified which can then be set in the admin-ui and used in

+ 2 - 1
docs/docs/reference/typescript-api/configurable-operation-def/default-form-component-id.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## DefaultFormComponentId
 
-<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="135" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="139" packageName="@vendure/common" />
 
 The ids of the default form input components that ship with the
 Admin UI.
@@ -34,4 +34,5 @@ type DefaultFormComponentId = | 'boolean-form-input'
     | 'textarea-form-input'
     | 'product-multi-form-input'
     | 'combination-mode-form-input'
+    | 'struct-form-input'
 ```

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## DefaultFormConfigHash
 
-<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="160" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="165" packageName="@vendure/common" />
 
 Used to define the expected arguments for a given default form input component.
 
@@ -29,17 +29,18 @@ 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>;
+    'struct-form-input': Record<string, never>;
 }
 ```
 
@@ -107,7 +108,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 +118,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'
@@ -130,6 +131,11 @@ type DefaultFormConfigHash = {
 <MemberInfo kind="property" type={`Record&#60;string, never&#62;`}   />
 
 
+### 'struct-form-input'
+
+<MemberInfo kind="property" type={`Record&#60;string, never&#62;`}   />
+
+
 
 
 </div>

+ 1 - 1
docs/docs/reference/typescript-api/configuration/api-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ApiOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="68" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="70" packageName="@vendure/core" />
 
 The ApiOptions define how the Vendure GraphQL APIs are exposed, as well as allowing the API layer
 to be extended with middleware.

+ 1 - 1
docs/docs/reference/typescript-api/configuration/default-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## defaultConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/default-config.ts" sourceLine="61" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/default-config.ts" sourceLine="63" packageName="@vendure/core" />
 
 The default configuration settings which are used if not explicitly overridden in the bootstrap() call.
 

+ 1 - 1
docs/docs/reference/typescript-api/configuration/entity-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## EntityOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="953" packageName="@vendure/core" since="1.3.0" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="965" packageName="@vendure/core" since="1.3.0" />
 
 Options relating to the internal handling of entities.
 

+ 1 - 1
docs/docs/reference/typescript-api/configuration/runtime-vendure-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## RuntimeVendureConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1200" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1221" packageName="@vendure/core" />
 
 This interface represents the VendureConfig object available at run-time, i.e. the user-supplied
 config values have been merged with the <a href='/reference/typescript-api/configuration/default-config#defaultconfig'>defaultConfig</a> values.

+ 8 - 1
docs/docs/reference/typescript-api/configuration/system-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## SystemOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1042" packageName="@vendure/core" since="1.6.0" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1054" packageName="@vendure/core" since="1.6.0" />
 
 Options relating to system functions.
 
@@ -19,6 +19,7 @@ Options relating to system functions.
 interface SystemOptions {
     healthChecks?: HealthCheckStrategy[];
     errorHandlers?: ErrorHandlerStrategy[];
+    cacheStrategy?: CacheStrategy;
 }
 ```
 
@@ -36,6 +37,12 @@ that any critical systems which the Vendure server depends on are also healthy.
 
 Defines an array of <a href='/reference/typescript-api/errors/error-handler-strategy#errorhandlerstrategy'>ErrorHandlerStrategy</a> instances which are used to define logic to be executed
 when an error occurs, either on the server or the worker.
+### cacheStrategy
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/cache/cache-strategy#cachestrategy'>CacheStrategy</a>`} default={`InMemoryCacheStrategy`}  since="3.1.0"  />
+
+Defines the underlying method used to store cache key-value pairs which powers the
+<a href='/reference/typescript-api/cache/cache-service#cacheservice'>CacheService</a>.
 
 
 </div>

+ 1 - 1
docs/docs/reference/typescript-api/configuration/vendure-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## VendureConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1070" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1091" packageName="@vendure/core" />
 
 All possible configuration options are defined by the
 [`VendureConfig`](https://github.com/vendure-ecommerce/vendure/blob/master/packages/core/src/config/vendure-config.ts) interface.

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CustomFieldConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="124" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="228" packageName="@vendure/core" />
 
 An object used to configure a custom field.
 
@@ -25,4 +25,5 @@ type CustomFieldConfig = | StringCustomFieldConfig
     | BooleanCustomFieldConfig
     | DateTimeCustomFieldConfig
     | RelationCustomFieldConfig
+    | StructCustomFieldConfig
 ```

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CustomFieldType
 
-<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="103" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/shared-types.ts" sourceLine="104" packageName="@vendure/common" />
 
 A data type for a custom field. The CustomFieldType determines the data types used in the generated
 database columns and GraphQL fields as follows (key: m = MySQL, p = Postgres, s = SQLite):
@@ -21,11 +21,12 @@ Type         | DB type                               | GraphQL type
 string       | varchar                               | String
 localeString | varchar                               | String
 text         | longtext(m), text(p,s)                | String
-localeText    | 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
 datetime     | datetime (m,s), timestamp (p)         | DateTime
+struct       | json (m), jsonb (p), text (s)         | JSON
 relation     | many-to-one / many-to-many relation   | As specified in config
 
 Additionally, the CustomFieldType also dictates which [configuration options](/reference/typescript-api/custom-fields/#custom-field-config-properties)
@@ -41,4 +42,5 @@ type CustomFieldType = | 'string'
     | 'relation'
     | 'text'
     | 'localeText'
+    | 'struct'
 ```

+ 8 - 1
docs/docs/reference/typescript-api/custom-fields/index.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CustomFields
 
-<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="159" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="264" packageName="@vendure/core" />
 
 Most entities can have additional fields added to them by defining an array of <a href='/reference/typescript-api/custom-fields/custom-field-config#customfieldconfig'>CustomFieldConfig</a>
 objects on against the corresponding key.
@@ -47,8 +47,10 @@ type CustomFields = {
     FacetValue?: CustomFieldConfig[];
     Fulfillment?: CustomFieldConfig[];
     GlobalSettings?: CustomFieldConfig[];
+    HistoryEntry?: CustomFieldConfig[];
     Order?: CustomFieldConfig[];
     OrderLine?: CustomFieldConfig[];
+    Payment?: CustomFieldConfig[];
     PaymentMethod?: CustomFieldConfig[];
     Product?: CustomFieldConfig[];
     ProductOption?: CustomFieldConfig[];
@@ -56,10 +58,15 @@ type CustomFields = {
     ProductVariant?: CustomFieldConfig[];
     ProductVariantPrice?: CustomFieldConfig[];
     Promotion?: CustomFieldConfig[];
+    Refund?: CustomFieldConfig[];
     Region?: CustomFieldConfig[];
     Seller?: CustomFieldConfig[];
+    Session?: CustomFieldConfig[];
+    ShippingLine?: CustomFieldConfig[];
     ShippingMethod?: CustomFieldConfig[];
+    StockLevel?: CustomFieldConfig[];
     StockLocation?: CustomFieldConfig[];
+    StockMovement?: CustomFieldConfig[];
     TaxCategory?: CustomFieldConfig[];
     TaxRate?: CustomFieldConfig[];
     User?: CustomFieldConfig[];

+ 25 - 0
docs/docs/reference/typescript-api/custom-fields/struct-custom-field-config.md

@@ -0,0 +1,25 @@
+---
+title: "StructCustomFieldConfig"
+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';
+
+
+## StructCustomFieldConfig
+
+<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="215" packageName="@vendure/core" since="3.1.0" />
+
+Configures a "struct" custom field.
+
+```ts title="Signature"
+type StructCustomFieldConfig = TypedCustomFieldConfig<
+    'struct',
+    Omit<GraphQLStructCustomFieldConfig, 'fields'>
+> & {
+    fields: StructFieldConfig[];
+}
+```

+ 45 - 0
docs/docs/reference/typescript-api/custom-fields/struct-field-config.md

@@ -0,0 +1,45 @@
+---
+title: "StructFieldConfig"
+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';
+
+
+## StructFieldConfig
+
+<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="200" packageName="@vendure/core" since="3.1.0" />
+
+Configures an individual field of a "struct" custom field. The individual fields share
+the same API as the top-level custom fields, with the exception that they do not support the
+`readonly`, `internal`, `nullable`, `unique` and `requiresPermission` options.
+
+*Example*
+
+```ts
+const customFields: CustomFields = {
+  Product: [
+    {
+      name: 'specifications',
+      type: 'struct',
+      fields: [
+        { name: 'processor', type: 'string' },
+        { name: 'ram', type: 'string' },
+        { name: 'screenSize', type: 'float' },
+      ],
+    },
+  ],
+};
+```
+
+```ts title="Signature"
+type StructFieldConfig = | StringStructFieldConfig
+    | TextStructFieldConfig
+    | IntStructFieldConfig
+    | FloatStructFieldConfig
+    | BooleanStructFieldConfig
+    | DateTimeStructFieldConfig
+```

+ 1 - 1
docs/docs/reference/typescript-api/custom-fields/typed-custom-single-field-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## TypedCustomSingleFieldConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="66" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="75" packageName="@vendure/core" />
 
 Configures a custom field on an entity in the <a href='/reference/typescript-api/custom-fields/#customfields'>CustomFields</a> config object.
 

+ 54 - 45
docs/docs/reference/typescript-api/data-access/transactional-connection.md

@@ -11,14 +11,14 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## TransactionalConnection
 
-<GenerationInfo sourceFile="packages/core/src/connection/transactional-connection.ts" sourceLine="40" packageName="@vendure/core" />
-
-The TransactionalConnection is a wrapper around the TypeORM `Connection` object which works in conjunction
-with the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator to implement per-request transactions. All services which access the
-database should use this class rather than the raw TypeORM connection, to ensure that db changes can be
-easily wrapped in transactions when required.
-
-The service layer does not need to know about the scope of a transaction, as this is covered at the
+<GenerationInfo sourceFile="packages/core/src/connection/transactional-connection.ts" sourceLine="41" packageName="@vendure/core" />
+
+The TransactionalConnection is a wrapper around the TypeORM `Connection` object which works in conjunction
+with the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator to implement per-request transactions. All services which access the
+database should use this class rather than the raw TypeORM connection, to ensure that db changes can be
+easily wrapped in transactions when required.
+
+The service layer does not need to know about the scope of a transaction, as this is covered at the
 API by the use of the `Transaction` decorator.
 
 ```ts title="Signature"
@@ -26,8 +26,12 @@ class TransactionalConnection {
     constructor(dataSource: DataSource, transactionWrapper: TransactionWrapper)
     rawConnection: DataSource
     getRepository(target: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>;
-    getRepository(ctx: RequestContext | undefined, target: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>;
-    getRepository(ctxOrTarget: RequestContext | ObjectType<Entity> | EntitySchema<Entity> | string | undefined, maybeTarget?: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>;
+    getRepository(ctx: RequestContext | undefined, target: ObjectType<Entity> | EntitySchema<Entity> | string, options?: {
+            replicationMode?: ReplicationMode;
+        }) => Repository<Entity>;
+    getRepository(ctxOrTarget: RequestContext | ObjectType<Entity> | EntitySchema<Entity> | string | undefined, maybeTarget?: ObjectType<Entity> | EntitySchema<Entity> | string, options?: {
+            replicationMode?: ReplicationMode;
+        }) => Repository<Entity>;
     withTransaction(work: (ctx: RequestContext) => Promise<T>) => Promise<T>;
     withTransaction(ctx: RequestContext, work: (ctx: RequestContext) => Promise<T>) => Promise<T>;
     withTransaction(ctxOrWork: RequestContext | ((ctx: RequestContext) => Promise<T>), maybeWork?: (ctx: RequestContext) => Promise<T>) => Promise<T>;
@@ -51,48 +55,53 @@ class TransactionalConnection {
 
 <MemberInfo kind="property" type={`DataSource`}   />
 
-The plain TypeORM Connection object. Should be used carefully as any operations
-performed with this connection will not be performed within any outer
+The plain TypeORM Connection object. Should be used carefully as any operations
+performed with this connection will not be performed within any outer
 transactions.
 ### getRepository
 
 <MemberInfo kind="method" type={`(target: ObjectType&#60;Entity&#62; | EntitySchema&#60;Entity&#62; | string) => Repository&#60;Entity&#62;`}   />
 
-Returns a TypeORM repository. Note that when no RequestContext is supplied, the repository will not
-be aware of any existing transaction. Therefore, calling this method without supplying a RequestContext
+Returns a TypeORM repository. Note that when no RequestContext is supplied, the repository will not
+be aware of any existing transaction. Therefore, calling this method without supplying a RequestContext
 is discouraged without a deliberate reason.
 ### getRepository
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a> | undefined, target: ObjectType&#60;Entity&#62; | EntitySchema&#60;Entity&#62; | string) => Repository&#60;Entity&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a> | undefined, target: ObjectType&#60;Entity&#62; | EntitySchema&#60;Entity&#62; | string, options?: {
             replicationMode?: ReplicationMode;
         }) => Repository&#60;Entity&#62;`}   />
 
-Returns a TypeORM repository which is bound to any existing transactions. It is recommended to _always_ pass
-the RequestContext argument when possible, otherwise the queries will be executed outside of any
-ongoing transactions which have been started by the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator.
+Returns a TypeORM repository which is bound to any existing transactions. It is recommended to _always_ pass
+the RequestContext argument when possible, otherwise the queries will be executed outside of any
+ongoing transactions which have been started by the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator.
+
+The `options` parameter allows specifying additional configurations, such as the `replicationMode`,
+which determines whether the repository should interact with the master or replica database.
 ### getRepository
 
-<MemberInfo kind="method" type={`(ctxOrTarget: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a> | ObjectType&#60;Entity&#62; | EntitySchema&#60;Entity&#62; | string | undefined, maybeTarget?: ObjectType&#60;Entity&#62; | EntitySchema&#60;Entity&#62; | string) => Repository&#60;Entity&#62;`}   />
-
+<MemberInfo kind="method" type={`(ctxOrTarget: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a> | ObjectType&#60;Entity&#62; | EntitySchema&#60;Entity&#62; | string | undefined, maybeTarget?: ObjectType&#60;Entity&#62; | EntitySchema&#60;Entity&#62; | string, options?: {
             replicationMode?: ReplicationMode;
         }) => Repository&#60;Entity&#62;`}   />
 
+Returns a TypeORM repository. Depending on the parameters passed, it will either be transaction-aware
+or not. If `RequestContext` is provided, the repository is bound to any ongoing transactions. The
+`options` parameter allows further customization, such as selecting the replication mode (e.g., 'master').
 ### withTransaction
 
 <MemberInfo kind="method" type={`(work: (ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>) =&#62; Promise&#60;T&#62;) => Promise&#60;T&#62;`}  since="1.3.0"  />
 
-Allows database operations to be wrapped in a transaction, ensuring that in the event of an error being
-thrown at any point, the entire transaction will be rolled back and no changes will be saved.
-
-In the context of API requests, you should instead use the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator on your resolver or
-controller method.
-
-On the other hand, for code that does not run in the context of a GraphQL/REST request, this method
-should be used to protect against non-atomic changes to the data which could leave your data in an
-inconsistent state.
-
-Such situations include function processed by the JobQueue or stand-alone scripts which make use
-of Vendure internal services.
-
-If there is already a <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a> object available, you should pass it in as the first
-argument in order to create transactional context as the copy. If not, omit the first argument and an empty
-RequestContext object will be created, which is then used to propagate the transaction to
+Allows database operations to be wrapped in a transaction, ensuring that in the event of an error being
+thrown at any point, the entire transaction will be rolled back and no changes will be saved.
+
+In the context of API requests, you should instead use the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator on your resolver or
+controller method.
+
+On the other hand, for code that does not run in the context of a GraphQL/REST request, this method
+should be used to protect against non-atomic changes to the data which could leave your data in an
+inconsistent state.
+
+Such situations include function processed by the JobQueue or stand-alone scripts which make use
+of Vendure internal services.
+
+If there is already a <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a> object available, you should pass it in as the first
+argument in order to create transactional context as the copy. If not, omit the first argument and an empty
+RequestContext object will be created, which is then used to propagate the transaction to
 all inner method calls.
 
 *Example*
@@ -128,40 +137,40 @@ private async transferCredit(outerCtx: RequestContext, fromId: ID, toId: ID, amo
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, isolationLevel?: <a href='/reference/typescript-api/request/transaction-decorator#transactionisolationlevel'>TransactionIsolationLevel</a>) => `}   />
 
-Manually start a transaction if one is not already in progress. This method should be used in
+Manually start a transaction if one is not already in progress. This method should be used in
 conjunction with the `'manual'` mode of the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator.
 ### commitOpenTransaction
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>) => `}   />
 
-Manually commits any open transaction. Should be very rarely needed, since the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator
-and the internal TransactionInterceptor take care of this automatically. Use-cases include situations
-in which the worker thread needs to access changes made in the current transaction, or when using the
+Manually commits any open transaction. Should be very rarely needed, since the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator
+and the internal TransactionInterceptor take care of this automatically. Use-cases include situations
+in which the worker thread needs to access changes made in the current transaction, or when using the
 Transaction decorator in manual mode.
 ### rollBackTransaction
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>) => `}   />
 
-Manually rolls back any open transaction. Should be very rarely needed, since the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator
-and the internal TransactionInterceptor take care of this automatically. Use-cases include when using the
+Manually rolls back any open transaction. Should be very rarely needed, since the <a href='/reference/typescript-api/request/transaction-decorator#transaction'>Transaction</a> decorator
+and the internal TransactionInterceptor take care of this automatically. Use-cases include when using the
 Transaction decorator in manual mode.
 ### getEntityOrThrow
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, entityType: Type&#60;T&#62;, id: <a href='/reference/typescript-api/common/id#id'>ID</a>, options: <a href='/reference/typescript-api/data-access/get-entity-or-throw-options#getentityorthrowoptions'>GetEntityOrThrowOptions</a>&#60;T&#62; = {}) => Promise&#60;T&#62;`}   />
 
-Finds an entity of the given type by ID, or throws an `EntityNotFoundError` if none
+Finds an entity of the given type by ID, or throws an `EntityNotFoundError` if none
 is found.
 ### findOneInChannel
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, entity: Type&#60;T&#62;, id: <a href='/reference/typescript-api/common/id#id'>ID</a>, channelId: <a href='/reference/typescript-api/common/id#id'>ID</a>, options: FindOneOptions&#60;T&#62; = {}) => `}   />
 
-Like the TypeOrm `Repository.findOne()` method, but limits the results to
+Like the TypeOrm `Repository.findOne()` method, but limits the results to
 the given Channel.
 ### findByIdsInChannel
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, entity: Type&#60;T&#62;, ids: <a href='/reference/typescript-api/common/id#id'>ID</a>[], channelId: <a href='/reference/typescript-api/common/id#id'>ID</a>, options: FindManyOptions&#60;T&#62;) => `}   />
 
-Like the TypeOrm `Repository.findByIds()` method, but limits the results to
+Like the TypeOrm `Repository.findByIds()` method, but limits the results to
 the given Channel.
 
 

+ 1 - 1
docs/docs/reference/typescript-api/default-search-plugin/index.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## DefaultSearchPlugin
 
-<GenerationInfo sourceFile="packages/core/src/plugin/default-search-plugin/default-search-plugin.ts" sourceLine="69" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/plugin/default-search-plugin/default-search-plugin.ts" sourceLine="70" packageName="@vendure/core" />
 
 The DefaultSearchPlugin provides a full-text Product search based on the full-text searching capabilities of the
 underlying database.

+ 12 - 2
docs/docs/reference/typescript-api/entities/history-entry.md

@@ -11,13 +11,13 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## HistoryEntry
 
-<GenerationInfo sourceFile="packages/core/src/entity/history-entry/history-entry.entity.ts" sourceLine="14" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/entity/history-entry/history-entry.entity.ts" sourceLine="16" packageName="@vendure/core" />
 
 An abstract entity representing an entry in the history of an Order (<a href='/reference/typescript-api/entities/order-history-entry#orderhistoryentry'>OrderHistoryEntry</a>)
 or a Customer (<a href='/reference/typescript-api/entities/customer-history-entry#customerhistoryentry'>CustomerHistoryEntry</a>).
 
 ```ts title="Signature"
-class HistoryEntry extends VendureEntity {
+class HistoryEntry extends VendureEntity implements HasCustomFields {
     @Index()
     @ManyToOne(type => Administrator)
     administrator?: Administrator;
@@ -27,11 +27,16 @@ class HistoryEntry extends VendureEntity {
     isPublic: boolean;
     @Column('simple-json')
     data: any;
+    @Column(type => CustomHistoryEntryFields)
+    customFields: CustomHistoryEntryFields;
 }
 ```
 * Extends: <code><a href='/reference/typescript-api/entities/vendure-entity#vendureentity'>VendureEntity</a></code>
 
 
+* Implements: <code>HasCustomFields</code>
+
+
 
 <div className="members-wrapper">
 
@@ -55,6 +60,11 @@ class HistoryEntry extends VendureEntity {
 <MemberInfo kind="property" type={`any`}   />
 
 
+### customFields
+
+<MemberInfo kind="property" type={`CustomHistoryEntryFields`}   />
+
+
 
 
 </div>

+ 7 - 0
docs/docs/reference/typescript-api/entities/order-line.md

@@ -40,6 +40,8 @@ class OrderLine extends VendureEntity implements HasCustomFields {
     @Index()
     @ManyToOne(type => TaxCategory)
     taxCategory: TaxCategory;
+    @EntityId({ nullable: true })
+    taxCategoryId: ID;
     @Index()
     @ManyToOne(type => Asset, asset => asset.featuredInVariants, { onDelete: 'SET NULL' })
     featuredAsset: Asset;
@@ -144,6 +146,11 @@ The <a href='/reference/typescript-api/entities/product-variant#productvariant'>
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/tax-category#taxcategory'>TaxCategory</a>`}   />
 
 
+### taxCategoryId
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/common/id#id'>ID</a>`}   />
+
+
 ### featuredAsset
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/asset#asset'>Asset</a>`}   />

+ 12 - 2
docs/docs/reference/typescript-api/entities/payment.md

@@ -11,13 +11,13 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Payment
 
-<GenerationInfo sourceFile="packages/core/src/entity/payment/payment.entity.ts" sourceLine="18" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/entity/payment/payment.entity.ts" sourceLine="20" packageName="@vendure/core" />
 
 A Payment represents a single payment transaction and exists in a well-defined state
 defined by the <a href='/reference/typescript-api/payment/payment-state#paymentstate'>PaymentState</a> type.
 
 ```ts title="Signature"
-class Payment extends VendureEntity {
+class Payment extends VendureEntity implements HasCustomFields {
     constructor(input?: DeepPartial<Payment>)
     @Column() method: string;
     @Money() amount: number;
@@ -32,11 +32,16 @@ class Payment extends VendureEntity {
     order: Order;
     @OneToMany(type => Refund, refund => refund.payment)
     refunds: Refund[];
+    @Column(type => CustomPaymentFields)
+    customFields: CustomPaymentFields;
 }
 ```
 * Extends: <code><a href='/reference/typescript-api/entities/vendure-entity#vendureentity'>VendureEntity</a></code>
 
 
+* Implements: <code>HasCustomFields</code>
+
+
 
 <div className="members-wrapper">
 
@@ -85,6 +90,11 @@ class Payment extends VendureEntity {
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/refund#refund'>Refund</a>[]`}   />
 
 
+### customFields
+
+<MemberInfo kind="property" type={`CustomPaymentFields`}   />
+
+
 
 
 </div>

+ 14 - 0
docs/docs/reference/typescript-api/entities/product-variant.md

@@ -37,6 +37,8 @@ class ProductVariant extends VendureEntity implements Translatable, HasCustomFie
     @Index()
     @ManyToOne(type => Asset, asset => asset.featuredInVariants, { onDelete: 'SET NULL' })
     featuredAsset: Asset;
+    @EntityId({ nullable: true })
+    featuredAssetId: ID;
     @OneToMany(type => ProductVariantAsset, productVariantAsset => productVariantAsset.productVariant, {
         onDelete: 'SET NULL',
     })
@@ -44,6 +46,8 @@ class ProductVariant extends VendureEntity implements Translatable, HasCustomFie
     @Index()
     @ManyToOne(type => TaxCategory, taxCategory => taxCategory.productVariants)
     taxCategory: TaxCategory;
+    @EntityId({ nullable: true })
+    taxCategoryId: ID;
     @OneToMany(type => ProductVariantPrice, price => price.variant, { eager: true })
     productVariantPrices: ProductVariantPrice[];
     @OneToMany(type => ProductVariantTranslation, translation => translation.base, { eager: true })
@@ -149,6 +153,11 @@ class ProductVariant extends VendureEntity implements Translatable, HasCustomFie
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/asset#asset'>Asset</a>`}   />
 
 
+### featuredAssetId
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/common/id#id'>ID</a>`}   />
+
+
 ### assets
 
 <MemberInfo kind="property" type={`ProductVariantAsset[]`}   />
@@ -159,6 +168,11 @@ class ProductVariant extends VendureEntity implements Translatable, HasCustomFie
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/tax-category#taxcategory'>TaxCategory</a>`}   />
 
 
+### taxCategoryId
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/common/id#id'>ID</a>`}   />
+
+
 ### productVariantPrices
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/product-variant-price#productvariantprice'>ProductVariantPrice</a>[]`}   />

+ 8 - 1
docs/docs/reference/typescript-api/entities/product.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Product
 
-<GenerationInfo sourceFile="packages/core/src/entity/product/product.entity.ts" sourceLine="25" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/entity/product/product.entity.ts" sourceLine="26" packageName="@vendure/core" />
 
 A Product contains one or more <a href='/reference/typescript-api/entities/product-variant#productvariant'>ProductVariant</a>s and serves as a container for those variants,
 providing an overall name, description etc.
@@ -29,6 +29,8 @@ class Product extends VendureEntity implements Translatable, HasCustomFields, Ch
     @Index()
     @ManyToOne(type => Asset, asset => asset.featuredInProducts, { onDelete: 'SET NULL' })
     featuredAsset: Asset;
+    @EntityId({ nullable: true })
+    featuredAssetId: ID;
     @OneToMany(type => ProductAsset, productAsset => productAsset.product)
     assets: ProductAsset[];
     @OneToMany(type => ProductTranslation, translation => translation.base, { eager: true })
@@ -91,6 +93,11 @@ class Product extends VendureEntity implements Translatable, HasCustomFields, Ch
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/asset#asset'>Asset</a>`}   />
 
 
+### featuredAssetId
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/common/id#id'>ID</a>`}   />
+
+
 ### assets
 
 <MemberInfo kind="property" type={`ProductAsset[]`}   />

+ 2 - 2
docs/docs/reference/typescript-api/entities/promotion.md

@@ -11,13 +11,13 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Promotion
 
-<GenerationInfo sourceFile="packages/core/src/entity/promotion/promotion.entity.ts" sourceLine="56" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/entity/promotion/promotion.entity.ts" sourceLine="61" packageName="@vendure/core" />
 
 A Promotion is used to define a set of conditions under which promotions actions (typically discounts)
 will be applied to an Order.
 
 Each assigned <a href='/reference/typescript-api/promotions/promotion-condition#promotioncondition'>PromotionCondition</a> is checked against the Order, and if they all return `true`,
-then each assign <a href='/reference/typescript-api/promotions/promotion-action#promotionitemaction'>PromotionItemAction</a> / <a href='/reference/typescript-api/promotions/promotion-action#promotionorderaction'>PromotionOrderAction</a> is applied to the Order.
+then each assign <a href='/reference/typescript-api/promotions/promotion-action#promotionitemaction'>PromotionItemAction</a> / <a href='/reference/typescript-api/promotions/promotion-action#promotionlineaction'>PromotionLineAction</a> / <a href='/reference/typescript-api/promotions/promotion-action#promotionorderaction'>PromotionOrderAction</a> / <a href='/reference/typescript-api/promotions/promotion-action#promotionshippingaction'>PromotionShippingAction</a> is applied to the Order.
 
 ```ts title="Signature"
 class Promotion extends AdjustmentSource implements ChannelAware, SoftDeletable, HasCustomFields, Translatable {

+ 12 - 2
docs/docs/reference/typescript-api/entities/refund.md

@@ -11,12 +11,12 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Refund
 
-<GenerationInfo sourceFile="packages/core/src/entity/refund/refund.entity.ts" sourceLine="17" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/entity/refund/refund.entity.ts" sourceLine="19" packageName="@vendure/core" />
 
 A refund the belongs to an order
 
 ```ts title="Signature"
-class Refund extends VendureEntity {
+class Refund extends VendureEntity implements HasCustomFields {
     constructor(input?: DeepPartial<Refund>)
     @Money() items: number;
     @Money() shipping: number;
@@ -36,11 +36,16 @@ class Refund extends VendureEntity {
     @EntityId()
     paymentId: ID;
     @Column('simple-json') metadata: PaymentMetadata;
+    @Column(type => CustomRefundFields)
+    customFields: CustomRefundFields;
 }
 ```
 * Extends: <code><a href='/reference/typescript-api/entities/vendure-entity#vendureentity'>VendureEntity</a></code>
 
 
+* Implements: <code>HasCustomFields</code>
+
+
 
 <div className="members-wrapper">
 
@@ -109,6 +114,11 @@ class Refund extends VendureEntity {
 <MemberInfo kind="property" type={`PaymentMetadata`}   />
 
 
+### customFields
+
+<MemberInfo kind="property" type={`CustomRefundFields`}   />
+
+
 
 
 </div>

+ 11 - 1
docs/docs/reference/typescript-api/entities/session.md

@@ -17,7 +17,7 @@ A Session is created when a user makes a request to restricted API operations. A
 in the case of un-authenticated users, otherwise it is an <a href='/reference/typescript-api/entities/authenticated-session#authenticatedsession'>AuthenticatedSession</a>.
 
 ```ts title="Signature"
-class Session extends VendureEntity {
+class Session extends VendureEntity implements HasCustomFields {
     @Index({ unique: true })
     @Column()
     token: string;
@@ -33,11 +33,16 @@ class Session extends VendureEntity {
     @Index()
     @ManyToOne(type => Channel)
     activeChannel: Channel | null;
+    @Column(type => CustomSessionFields)
+    customFields: CustomSessionFields;
 }
 ```
 * Extends: <code><a href='/reference/typescript-api/entities/vendure-entity#vendureentity'>VendureEntity</a></code>
 
 
+* Implements: <code>HasCustomFields</code>
+
+
 
 <div className="members-wrapper">
 
@@ -76,6 +81,11 @@ class Session extends VendureEntity {
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/channel#channel'>Channel</a> | null`}   />
 
 
+### customFields
+
+<MemberInfo kind="property" type={`CustomSessionFields`}   />
+
+
 
 
 </div>

+ 12 - 2
docs/docs/reference/typescript-api/entities/shipping-line.md

@@ -11,14 +11,14 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ShippingLine
 
-<GenerationInfo sourceFile="packages/core/src/entity/shipping-line/shipping-line.entity.ts" sourceLine="24" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/entity/shipping-line/shipping-line.entity.ts" sourceLine="26" packageName="@vendure/core" />
 
 A ShippingLine is created when a <a href='/reference/typescript-api/entities/shipping-method#shippingmethod'>ShippingMethod</a> is applied to an <a href='/reference/typescript-api/entities/order#order'>Order</a>.
 It contains information about the price of the shipping method, any discounts that were
 applied, and the resulting tax on the shipping method.
 
 ```ts title="Signature"
-class ShippingLine extends VendureEntity {
+class ShippingLine extends VendureEntity implements HasCustomFields {
     constructor(input?: DeepPartial<ShippingLine>)
     @EntityId()
     shippingMethodId: ID | null;
@@ -38,6 +38,8 @@ class ShippingLine extends VendureEntity {
     taxLines: TaxLine[];
     @OneToMany(type => OrderLine, orderLine => orderLine.shippingLine)
     orderLines: OrderLine[];
+    @Column(type => CustomShippingLineFields)
+    customFields: CustomShippingLineFields;
     price: number
     priceWithTax: number
     discountedPrice: number
@@ -51,6 +53,9 @@ class ShippingLine extends VendureEntity {
 * Extends: <code><a href='/reference/typescript-api/entities/vendure-entity#vendureentity'>VendureEntity</a></code>
 
 
+* Implements: <code>HasCustomFields</code>
+
+
 
 <div className="members-wrapper">
 
@@ -99,6 +104,11 @@ class ShippingLine extends VendureEntity {
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/order-line#orderline'>OrderLine</a>[]`}   />
 
 
+### customFields
+
+<MemberInfo kind="property" type={`CustomShippingLineFields`}   />
+
+
 ### price
 
 <MemberInfo kind="property" type={`number`}   />

+ 12 - 2
docs/docs/reference/typescript-api/entities/stock-level.md

@@ -11,13 +11,13 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## StockLevel
 
-<GenerationInfo sourceFile="packages/core/src/entity/stock-level/stock-level.entity.ts" sourceLine="16" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/entity/stock-level/stock-level.entity.ts" sourceLine="18" packageName="@vendure/core" />
 
 A StockLevel represents the number of a particular <a href='/reference/typescript-api/entities/product-variant#productvariant'>ProductVariant</a> which are available
 at a particular <a href='/reference/typescript-api/entities/stock-location#stocklocation'>StockLocation</a>.
 
 ```ts title="Signature"
-class StockLevel extends VendureEntity {
+class StockLevel extends VendureEntity implements HasCustomFields {
     constructor(input: DeepPartial<StockLevel>)
     @Index()
     @ManyToOne(type => ProductVariant, productVariant => productVariant.stockLevels, { onDelete: 'CASCADE' })
@@ -33,11 +33,16 @@ class StockLevel extends VendureEntity {
     stockOnHand: number;
     @Column()
     stockAllocated: number;
+    @Column(type => CustomStockLevelFields)
+    customFields: CustomStockLevelFields;
 }
 ```
 * Extends: <code><a href='/reference/typescript-api/entities/vendure-entity#vendureentity'>VendureEntity</a></code>
 
 
+* Implements: <code>HasCustomFields</code>
+
+
 
 <div className="members-wrapper">
 
@@ -76,6 +81,11 @@ class StockLevel extends VendureEntity {
 <MemberInfo kind="property" type={`number`}   />
 
 
+### customFields
+
+<MemberInfo kind="property" type={`CustomStockLevelFields`}   />
+
+
 
 
 </div>

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