소스 검색

docs(core): Add docs on caching

Relates to #3043.
Michael Bromley 1 년 전
부모
커밋
7b0a26ee50
84개의 변경된 파일1261개의 추가작업 그리고 268개의 파일을 삭제
  1. 4 1
      docs/docs/guides/deployment/horizontal-scaling.md
  2. 206 0
      docs/docs/guides/developer-guide/cache/index.mdx
  3. 14 14
      docs/docs/reference/admin-ui-api/alerts/alert-config.md
  4. 1 1
      docs/docs/reference/admin-ui-api/alerts/alert-context.md
  5. 1 1
      docs/docs/reference/admin-ui-api/components/asset-picker-dialog-component.md
  6. 14 8
      docs/docs/reference/admin-ui-api/components/data-table2component.md
  7. 15 1
      docs/docs/reference/admin-ui-api/list-detail-views/base-list-component.md
  8. 13 1
      docs/docs/reference/admin-ui-api/list-detail-views/typed-base-list-component.md
  9. 20 13
      docs/docs/reference/admin-ui-api/services/data-service.md
  10. 1 1
      docs/docs/reference/core-plugins/admin-ui-plugin/admin-ui-plugin-options.md
  11. 1 1
      docs/docs/reference/core-plugins/admin-ui-plugin/index.md
  12. 1 1
      docs/docs/reference/core-plugins/email-plugin/email-event-handler-with-async-data.md
  13. 27 1
      docs/docs/reference/core-plugins/email-plugin/email-event-handler.md
  14. 19 19
      docs/docs/reference/core-plugins/email-plugin/email-plugin-options.md
  15. 28 6
      docs/docs/reference/core-plugins/email-plugin/email-plugin-types.md
  16. 2 2
      docs/docs/reference/core-plugins/email-plugin/email-send-event.md
  17. 2 2
      docs/docs/reference/core-plugins/payments-plugin/mollie-plugin.md
  18. 1 1
      docs/docs/reference/typescript-api/assets/asset-options.md
  19. 7 8
      docs/docs/reference/typescript-api/auth/auth-options.md
  20. 1 1
      docs/docs/reference/typescript-api/auth/cookie-options.md
  21. 77 0
      docs/docs/reference/typescript-api/auth/default-session-cache-strategy.md
  22. 41 29
      docs/docs/reference/typescript-api/auth/external-authentication-service.md
  23. 14 5
      docs/docs/reference/typescript-api/auth/session-cache-strategy.md
  24. 1 1
      docs/docs/reference/typescript-api/auth/superadmin-credentials.md
  25. 44 0
      docs/docs/reference/typescript-api/cache/default-cache-plugin.md
  26. 83 0
      docs/docs/reference/typescript-api/cache/index.md
  27. 45 0
      docs/docs/reference/typescript-api/cache/redis-cache-plugin.md
  28. 73 0
      docs/docs/reference/typescript-api/cache/redis-cache-strategy.md
  29. 90 0
      docs/docs/reference/typescript-api/cache/sql-cache-strategy.md
  30. 9 9
      docs/docs/reference/typescript-api/configurable-operation-def/default-form-config-hash.md
  31. 1 1
      docs/docs/reference/typescript-api/configuration/api-options.md
  32. 1 1
      docs/docs/reference/typescript-api/configuration/default-config.md
  33. 1 1
      docs/docs/reference/typescript-api/configuration/entity-options.md
  34. 1 1
      docs/docs/reference/typescript-api/configuration/runtime-vendure-config.md
  35. 8 1
      docs/docs/reference/typescript-api/configuration/system-options.md
  36. 1 1
      docs/docs/reference/typescript-api/configuration/vendure-config.md
  37. 54 45
      docs/docs/reference/typescript-api/data-access/transactional-connection.md
  38. 7 0
      docs/docs/reference/typescript-api/entities/order-line.md
  39. 14 0
      docs/docs/reference/typescript-api/entities/product-variant.md
  40. 8 1
      docs/docs/reference/typescript-api/entities/product.md
  41. 2 2
      docs/docs/reference/typescript-api/entities/promotion.md
  42. 17 3
      docs/docs/reference/typescript-api/entities/tax-rate.md
  43. 1 1
      docs/docs/reference/typescript-api/import-export/import-export-options.md
  44. 13 1
      docs/docs/reference/typescript-api/import-export/importer.md
  45. 1 2
      docs/docs/reference/typescript-api/import-export/populator.md
  46. 1 1
      docs/docs/reference/typescript-api/job-queue/job-queue-options.md
  47. 1 1
      docs/docs/reference/typescript-api/money/default-money-strategy.md
  48. 1 1
      docs/docs/reference/typescript-api/money/money-strategy.md
  49. 1 1
      docs/docs/reference/typescript-api/orders/active-order-service.md
  50. 1 1
      docs/docs/reference/typescript-api/orders/order-options.md
  51. 1 1
      docs/docs/reference/typescript-api/payment/payment-options.md
  52. 1 1
      docs/docs/reference/typescript-api/products-stock/catalog-options.md
  53. 14 5
      docs/docs/reference/typescript-api/promotions/facet-value-checker.md
  54. 101 12
      docs/docs/reference/typescript-api/promotions/promotion-action.md
  55. 1 1
      docs/docs/reference/typescript-api/promotions/promotion-options.md
  56. 1 1
      docs/docs/reference/typescript-api/request/ctx-decorator.md
  57. 32 1
      docs/docs/reference/typescript-api/request/request-context.md
  58. 2 2
      docs/docs/reference/typescript-api/service-helpers/order-calculator.md
  59. 1 1
      docs/docs/reference/typescript-api/service-helpers/order-modifier.md
  60. 1 1
      docs/docs/reference/typescript-api/service-helpers/product-price-applicator.md
  61. 1 1
      docs/docs/reference/typescript-api/services/global-settings-service.md
  62. 11 4
      docs/docs/reference/typescript-api/services/order-service.md
  63. 0 1
      docs/docs/reference/typescript-api/services/product-variant-service.md
  64. 1 1
      docs/docs/reference/typescript-api/services/role-service.md
  65. 2 2
      docs/docs/reference/typescript-api/services/tax-rate-service.md
  66. 1 1
      docs/docs/reference/typescript-api/shipping/check-shipping-eligibility-checker-fn.md
  67. 1 1
      docs/docs/reference/typescript-api/shipping/shipping-eligibility-checker-config.md
  68. 7 1
      docs/docs/reference/typescript-api/shipping/shipping-eligibility-checker.md
  69. 1 1
      docs/docs/reference/typescript-api/shipping/shipping-options.md
  70. 1 1
      docs/docs/reference/typescript-api/shipping/should-run-check-fn.md
  71. 1 1
      docs/docs/reference/typescript-api/tax/tax-options.md
  72. 1 0
      docs/sidebars.js
  73. 1 1
      packages/core/src/api/common/request-context.ts
  74. 1 21
      packages/core/src/cache/cache.service.ts
  75. 27 1
      packages/core/src/cache/cache.ts
  76. 14 0
      packages/core/src/cache/request-context-cache.service.ts
  77. 13 4
      packages/core/src/config/session-cache/session-cache-strategy.ts
  78. 1 0
      packages/core/src/config/system/cache-strategy.ts
  79. 1 1
      packages/core/src/config/vendure-config.ts
  80. 10 0
      packages/core/src/plugin/default-cache-plugin/default-cache-plugin.ts
  81. 2 5
      packages/core/src/plugin/default-cache-plugin/sql-cache-strategy.ts
  82. 9 0
      packages/core/src/plugin/redis-cache-plugin/redis-cache-plugin.ts
  83. 8 0
      packages/core/src/plugin/redis-cache-plugin/redis-cache-strategy.ts
  84. 1 1
      packages/core/src/service/helpers/external-authentication/external-authentication.service.ts

+ 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 = {

+ 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/cache/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.

+ 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>

+ 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.
 

+ 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;) => `}   />

+ 19 - 19
docs/docs/reference/core-plugins/email-plugin/email-plugin-options.md

@@ -19,11 +19,11 @@ Configuration for the EmailPlugin.
 interface EmailPluginOptions {
     templatePath?: string;
     templateLoader?: TemplateLoader;
-    transport:
-        | EmailTransportOptions
-        | ((
-              injector?: Injector,
-              ctx?: RequestContext,
+    transport:
+        | EmailTransportOptions
+        | ((
+              injector?: Injector,
+              ctx?: RequestContext,
           ) => EmailTransportOptions | Promise<EmailTransportOptions>);
     handlers: Array<EmailEventHandler<string, any>>;
     globalTemplateVars?: { [key: string]: any } | GlobalTemplateVarsFn;
@@ -38,44 +38,44 @@ interface EmailPluginOptions {
 
 <MemberInfo kind="property" type={`string`}   />
 
-The path to the location of the email templates. In a default Vendure installation,
+The path to the location of the email templates. In a default Vendure installation,
 the templates are installed to `<project root>/vendure/email/templates`.
 ### templateLoader
 
 <MemberInfo kind="property" type={`<a href='/reference/core-plugins/email-plugin/template-loader#templateloader'>TemplateLoader</a>`}  since="2.0.0"  />
 
-An optional TemplateLoader which can be used to load templates from a custom location or async service.
+An optional TemplateLoader which can be used to load templates from a custom location or async service.
 The default uses the FileBasedTemplateLoader which loads templates from `<project root>/vendure/email/templates`
 ### transport
 
-<MemberInfo kind="property" type={`| <a href='/reference/core-plugins/email-plugin/transport-options#emailtransportoptions'>EmailTransportOptions</a>
         | ((
               injector?: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>,
               ctx?: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>,
           ) =&#62; <a href='/reference/core-plugins/email-plugin/transport-options#emailtransportoptions'>EmailTransportOptions</a> | Promise&#60;<a href='/reference/core-plugins/email-plugin/transport-options#emailtransportoptions'>EmailTransportOptions</a>&#62;)`}   />
+<MemberInfo kind="property" type={`| <a href='/reference/core-plugins/email-plugin/transport-options#emailtransportoptions'>EmailTransportOptions</a>         | ((               injector?: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>,               ctx?: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>,           ) =&#62; <a href='/reference/core-plugins/email-plugin/transport-options#emailtransportoptions'>EmailTransportOptions</a> | Promise&#60;<a href='/reference/core-plugins/email-plugin/transport-options#emailtransportoptions'>EmailTransportOptions</a>&#62;)`}   />
 
 Configures how the emails are sent.
 ### handlers
 
 <MemberInfo kind="property" type={`Array&#60;<a href='/reference/core-plugins/email-plugin/email-event-handler#emaileventhandler'>EmailEventHandler</a>&#60;string, any&#62;&#62;`}   />
 
-An array of <a href='/reference/core-plugins/email-plugin/email-event-handler#emaileventhandler'>EmailEventHandler</a>s which define which Vendure events will trigger
+An array of <a href='/reference/core-plugins/email-plugin/email-event-handler#emaileventhandler'>EmailEventHandler</a>s which define which Vendure events will trigger
 emails, and how those emails are generated.
 ### globalTemplateVars
 
 <MemberInfo kind="property" type={`{ [key: string]: any } | <a href='/reference/core-plugins/email-plugin/email-plugin-options#globaltemplatevarsfn'>GlobalTemplateVarsFn</a>`}   />
 
-An object containing variables which are made available to all templates. For example,
-the storefront URL could be defined here and then used in the "email address verification"
-email. Use the GlobalTemplateVarsFn if you need to retrieve variables from Vendure or
+An object containing variables which are made available to all templates. For example,
+the storefront URL could be defined here and then used in the "email address verification"
+email. Use the GlobalTemplateVarsFn if you need to retrieve variables from Vendure or
 plugin services.
 ### emailSender
 
 <MemberInfo kind="property" type={`<a href='/reference/core-plugins/email-plugin/email-sender#emailsender'>EmailSender</a>`} default={`<a href='/reference/core-plugins/email-plugin/email-sender#nodemaileremailsender'>NodemailerEmailSender</a>`}   />
 
-An optional allowed EmailSender, used to allow custom implementations of the send functionality
+An optional allowed EmailSender, used to allow custom implementations of the send functionality
 while still utilizing the existing emailPlugin functionality.
 ### emailGenerator
 
 <MemberInfo kind="property" type={`<a href='/reference/core-plugins/email-plugin/email-generator#emailgenerator'>EmailGenerator</a>`} default={`<a href='/reference/core-plugins/email-plugin/email-generator#handlebarsmjmlgenerator'>HandlebarsMjmlGenerator</a>`}   />
 
-An optional allowed EmailGenerator, used to allow custom email generation functionality to
+An optional allowed EmailGenerator, used to allow custom email generation functionality to
 better match with custom email sending functionality.
 
 
@@ -86,8 +86,8 @@ better match with custom email sending functionality.
 
 <GenerationInfo sourceFile="packages/email-plugin/src/types.ts" sourceLine="64" packageName="@vendure/email-plugin" since="2.3.0" />
 
-Allows you to dynamically load the "globalTemplateVars" key async and access Vendure services
-to create the object. This is not a requirement. You can also specify a simple static object if your
+Allows you to dynamically load the "globalTemplateVars" key async and access Vendure services
+to create the object. This is not a requirement. You can also specify a simple static object if your
 projects doesn't need to access async or dynamic values.
 
 *Example*
@@ -112,9 +112,9 @@ EmailPlugin.init({
 ```
 
 ```ts title="Signature"
-type GlobalTemplateVarsFn = (
-    ctx: RequestContext,
-    injector: Injector,
+type GlobalTemplateVarsFn = (
+    ctx: RequestContext,
+    injector: Injector,
 ) => Promise<{ [key: string]: any }>
 ```
 

+ 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.
 

+ 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="629" packageName="@vendure/core" />
 
 The AssetOptions define how assets (images and other files) are named and stored, and how preview images are generated.
 

+ 7 - 8
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="331" 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;
@@ -82,16 +82,15 @@ If passed as a number should represent milliseconds and if passed as a string de
 [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={`string | 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 that a cache entry 
+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

+ 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="226" 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>

+ 41 - 29
docs/docs/reference/typescript-api/auth/external-authentication-service.md

@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <GenerationInfo sourceFile="packages/core/src/service/helpers/external-authentication/external-authentication.service.ts" sourceLine="24" packageName="@vendure/core" />
 
-This is a helper service which exposes methods related to looking up and creating Users based on an
+This is a helper service which exposes methods related to looking up and creating Users based on an
 external <a href='/reference/typescript-api/auth/authentication-strategy#authenticationstrategy'>AuthenticationStrategy</a>.
 
 ```ts title="Signature"
@@ -21,24 +21,28 @@ class ExternalAuthenticationService {
     constructor(connection: TransactionalConnection, roleService: RoleService, historyService: HistoryService, customerService: CustomerService, administratorService: AdministratorService, channelService: ChannelService)
     findCustomerUser(ctx: RequestContext, strategy: string, externalIdentifier: string, checkCurrentChannelOnly:  = true) => Promise<User | undefined>;
     findAdministratorUser(ctx: RequestContext, strategy: string, externalIdentifier: string) => Promise<User | undefined>;
-    createCustomerAndUser(ctx: RequestContext, config: {
-            strategy: string;
-            externalIdentifier: string;
-            emailAddress: string;
-            firstName: string;
-            lastName: string;
-            verified?: boolean;
+    createCustomerAndUser(ctx: RequestContext, config: {
+            strategy: string;
+            externalIdentifier: string;
+            emailAddress: string;
+            firstName: string;
+            lastName: string;
+            verified?: boolean;
         }) => Promise<User>;
-    createAdministratorAndUser(ctx: RequestContext, config: {
-            strategy: string;
-            externalIdentifier: string;
-            identifier: string;
-            emailAddress?: string;
-            firstName?: string;
-            lastName?: string;
-            roles: Role[];
+    createAdministratorAndUser(ctx: RequestContext, config: {
+            strategy: string;
+            externalIdentifier: string;
+            identifier: string;
+            emailAddress?: string;
+            firstName?: string;
+            lastName?: string;
+            roles: Role[];
         }) => ;
     findUser(ctx: RequestContext, strategy: string, externalIdentifier: string) => Promise<User | undefined>;
+    createUser(ctx: RequestContext, config: {
+            strategy: string;
+            externalIdentifier: string;
+        }) => Promise<User>;
 }
 ```
 
@@ -53,38 +57,46 @@ class ExternalAuthenticationService {
 
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, strategy: string, externalIdentifier: string, checkCurrentChannelOnly:  = true) => Promise&#60;<a href='/reference/typescript-api/entities/user#user'>User</a> | undefined&#62;`}   />
 
-Looks up a User based on their identifier from an external authentication
-provider, ensuring this User is associated with a Customer account.
-
-By default, only customers in the currently-active Channel will be checked.
-By passing `false` as the `checkCurrentChannelOnly` argument, _all_ channels
+Looks up a User based on their identifier from an external authentication
+provider, ensuring this User is associated with a Customer account.
+
+By default, only customers in the currently-active Channel will be checked.
+By passing `false` as the `checkCurrentChannelOnly` argument, _all_ channels
 will be checked.
 ### findAdministratorUser
 
 <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;`}   />
 
-Looks up a User based on their identifier from an external authentication
+Looks up a User based on their identifier from an external authentication
 provider, ensuring this User is associated with an Administrator account.
 ### createCustomerAndUser
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, config: {
             strategy: string;
             externalIdentifier: string;
             emailAddress: string;
             firstName: string;
             lastName: string;
             verified?: boolean;
         }) => Promise&#60;<a href='/reference/typescript-api/entities/user#user'>User</a>&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, config: {             strategy: string;             externalIdentifier: string;             emailAddress: string;             firstName: string;             lastName: string;             verified?: boolean;         }) => Promise&#60;<a href='/reference/typescript-api/entities/user#user'>User</a>&#62;`}   />
 
-If a customer has been successfully authenticated by an external authentication provider, yet cannot
-be found using `findCustomerUser`, then we need to create a new User and
-Customer record in Vendure for that user. This method encapsulates that logic as well as additional
+If a customer has been successfully authenticated by an external authentication provider, yet cannot
+be found using `findCustomerUser`, then we need to create a new User and
+Customer record in Vendure for that user. This method encapsulates that logic as well as additional
 housekeeping such as adding a record to the Customer's history.
 ### createAdministratorAndUser
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, config: {
             strategy: string;
             externalIdentifier: string;
             identifier: string;
             emailAddress?: string;
             firstName?: string;
             lastName?: string;
             roles: <a href='/reference/typescript-api/entities/role#role'>Role</a>[];
         }) => `}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, config: {             strategy: string;             externalIdentifier: string;             identifier: string;             emailAddress?: string;             firstName?: string;             lastName?: string;             roles: <a href='/reference/typescript-api/entities/role#role'>Role</a>[];         }) => `}   />
 
-If an administrator has been successfully authenticated by an external authentication provider, yet cannot
-be found using `findAdministratorUser`, then we need to create a new User and
+If an administrator has been successfully authenticated by an external authentication provider, yet cannot
+be found using `findAdministratorUser`, then we need to create a new User and
 Administrator record in Vendure for that user.
 ### findUser
 
 <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="805" packageName="@vendure/core" />
 
 These credentials will be used to create the Superadmin user & administrator
 when Vendure first bootstraps.

+ 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>

+ 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>

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

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

+ 1 - 1
docs/docs/reference/typescript-api/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="69" 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="955" 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="1211" 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="1044" 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="1081" packageName="@vendure/core" />
 
 All possible configuration options are defined by the
 [`VendureConfig`](https://github.com/vendure-ecommerce/vendure/blob/master/server/src/config/vendure-config.ts) interface.

+ 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.
 
 

+ 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>`}   />

+ 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 {

+ 17 - 3
docs/docs/reference/typescript-api/entities/tax-rate.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## TaxRate
 
-<GenerationInfo sourceFile="packages/core/src/entity/tax-rate/tax-rate.entity.ts" sourceLine="25" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/entity/tax-rate/tax-rate.entity.ts" sourceLine="26" packageName="@vendure/core" />
 
 A TaxRate defines the rate of tax to apply to a <a href='/reference/typescript-api/entities/product-variant#productvariant'>ProductVariant</a> based on three factors:
 
@@ -28,9 +28,13 @@ class TaxRate extends VendureEntity implements HasCustomFields {
     @Index()
     @ManyToOne(type => TaxCategory, taxCategory => taxCategory.taxRates)
     category: TaxCategory;
+    @EntityId({ nullable: true })
+    categoryId: ID;
     @Index()
     @ManyToOne(type => Zone, zone => zone.taxRates)
     zone: Zone;
+    @EntityId({ nullable: true })
+    zoneId: ID;
     @Index()
     @ManyToOne(type => CustomerGroup, customerGroup => customerGroup.taxRates, { nullable: true })
     customerGroup?: CustomerGroup;
@@ -41,7 +45,7 @@ class TaxRate extends VendureEntity implements HasCustomFields {
     taxPayableOn(netPrice: number) => number;
     grossPriceOf(netPrice: number) => number;
     apply(price: number) => TaxLine;
-    test(zone: Zone, taxCategory: TaxCategory) => boolean;
+    test(zone: Zone | ID, taxCategory: TaxCategory | ID) => boolean;
 }
 ```
 * Extends: <code><a href='/reference/typescript-api/entities/vendure-entity#vendureentity'>VendureEntity</a></code>
@@ -78,11 +82,21 @@ class TaxRate extends VendureEntity implements HasCustomFields {
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/tax-category#taxcategory'>TaxCategory</a>`}   />
 
 
+### categoryId
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/common/id#id'>ID</a>`}   />
+
+
 ### zone
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/zone#zone'>Zone</a>`}   />
 
 
+### zoneId
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/common/id#id'>ID</a>`}   />
+
+
 ### customerGroup
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/entities/customer-group#customergroup'>CustomerGroup</a>`}   />
@@ -120,7 +134,7 @@ class TaxRate extends VendureEntity implements HasCustomFields {
 
 ### test
 
-<MemberInfo kind="method" type={`(zone: <a href='/reference/typescript-api/entities/zone#zone'>Zone</a>, taxCategory: <a href='/reference/typescript-api/entities/tax-category#taxcategory'>TaxCategory</a>) => boolean`}   />
+<MemberInfo kind="method" type={`(zone: <a href='/reference/typescript-api/entities/zone#zone'>Zone</a> | <a href='/reference/typescript-api/common/id#id'>ID</a>, taxCategory: <a href='/reference/typescript-api/entities/tax-category#taxcategory'>TaxCategory</a> | <a href='/reference/typescript-api/common/id#id'>ID</a>) => boolean`}   />
 
 
 

+ 1 - 1
docs/docs/reference/typescript-api/import-export/import-export-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ImportExportOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="888" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="890" packageName="@vendure/core" />
 
 Options related to importing & exporting data.
 

+ 13 - 1
docs/docs/reference/typescript-api/import-export/importer.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Importer
 
-<GenerationInfo sourceFile="packages/core/src/data-import/providers/importer/importer.ts" sourceLine="41" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/data-import/providers/importer/importer.ts" sourceLine="42" packageName="@vendure/core" />
 
 Parses and imports Products using the CSV import format.
 
@@ -23,6 +23,8 @@ entities in the Vendure database.
 class Importer {
     parseAndImport(input: string | Stream, ctxOrLanguageCode: RequestContext | LanguageCode, reportProgress: boolean = false) => Observable<ImportProgress>;
     importProducts(ctx: RequestContext, rows: ParsedProductWithVariants[], onProgress: OnProgressFn) => Promise<string[]>;
+    getFacetValueIds(ctx: RequestContext, facets: ParsedFacet[], languageCode: LanguageCode) => Promise<ID[]>;
+    processCustomFieldValues(customFields: { [field: string]: string }, config: CustomFieldConfig[]) => ;
 }
 ```
 
@@ -41,6 +43,16 @@ The `ctxOrLanguageCode` argument is used to specify the languageCode to be used
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, rows: <a href='/reference/typescript-api/import-export/import-parser#parsedproductwithvariants'>ParsedProductWithVariants</a>[], onProgress: OnProgressFn) => Promise&#60;string[]&#62;`}   />
 
 Imports the products specified in the rows object. Return an array of error messages.
+### getFacetValueIds
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, facets: <a href='/reference/typescript-api/import-export/import-parser#parsedfacet'>ParsedFacet</a>[], languageCode: <a href='/reference/typescript-api/common/language-code#languagecode'>LanguageCode</a>) => Promise&#60;<a href='/reference/typescript-api/common/id#id'>ID</a>[]&#62;`}   />
+
+
+### processCustomFieldValues
+
+<MemberInfo kind="method" type={`(customFields: { [field: string]: string }, config: <a href='/reference/typescript-api/custom-fields/custom-field-config#customfieldconfig'>CustomFieldConfig</a>[]) => `}   />
+
+
 
 
 </div>

+ 1 - 2
docs/docs/reference/typescript-api/import-export/populator.md

@@ -30,8 +30,7 @@ class Populator {
 <MemberInfo kind="method" type={`(data: <a href='/reference/typescript-api/import-export/initial-data#initialdata'>InitialData</a>, channel?: <a href='/reference/typescript-api/entities/channel#channel'>Channel</a>) => `}   />
 
 Should be run *before* populating the products, so that there are TaxRates by which
-product prices can be set. If the `channel` argument is set, then any <a href='/reference/typescript-api/entities/interfaces#channelaware'>ChannelAware</a>
-entities will be assigned to that Channel.
+product prices can be set. If the `channel` argument is set, then any <a href='/reference/typescript-api/entities/interfaces#channelaware'>ChannelAware</a>entities will be assigned to that Channel.
 ### populateCollections
 
 <MemberInfo kind="method" type={`(data: <a href='/reference/typescript-api/import-export/initial-data#initialdata'>InitialData</a>, channel?: <a href='/reference/typescript-api/entities/channel#channel'>Channel</a>) => `}   />

+ 1 - 1
docs/docs/reference/typescript-api/job-queue/job-queue-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## JobQueueOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="912" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="914" packageName="@vendure/core" />
 
 Options related to the built-in job queue.
 

+ 1 - 1
docs/docs/reference/typescript-api/money/default-money-strategy.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## DefaultMoneyStrategy
 
-<GenerationInfo sourceFile="packages/core/src/config/entity/default-money-strategy.ts" sourceLine="15" packageName="@vendure/core" since="2.0.0" />
+<GenerationInfo sourceFile="packages/core/src/config/entity/default-money-strategy.ts" sourceLine="14" packageName="@vendure/core" since="2.0.0" />
 
 A <a href='/reference/typescript-api/money/money-strategy#moneystrategy'>MoneyStrategy</a> that stores monetary values as a `int` type in the database.
 The storage configuration and rounding logic replicates the behaviour of Vendure pre-2.0.

+ 1 - 1
docs/docs/reference/typescript-api/money/money-strategy.md

@@ -106,7 +106,7 @@ Defines the logic used to round monetary values. For instance, the default behav
 in the <a href='/reference/typescript-api/money/default-money-strategy#defaultmoneystrategy'>DefaultMoneyStrategy</a> is to round the value, then multiply.
 
 ```ts
-return Math.round(value) * quantity;
+return Math.round(value * quantity);
 ```
 
 However, it may be desirable to instead round only _after_ the unit amount has been

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ActiveOrderService
 
-<GenerationInfo sourceFile="packages/core/src/service/helpers/active-order/active-order.service.ts" sourceLine="17" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/helpers/active-order/active-order.service.ts" sourceLine="18" packageName="@vendure/core" />
 
 This helper class is used to get a reference to the active Order from the current RequestContext.
 

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## OrderOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="482" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="484" packageName="@vendure/core" />
 
 
 

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## PaymentOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="825" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="827" packageName="@vendure/core" />
 
 Defines payment-related options in the <a href='/reference/typescript-api/configuration/vendure-config#vendureconfig'>VendureConfig</a>.
 

+ 1 - 1
docs/docs/reference/typescript-api/products-stock/catalog-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CatalogOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="674" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="676" packageName="@vendure/core" />
 
 Options related to products and collections.
 

+ 14 - 5
docs/docs/reference/typescript-api/promotions/facet-value-checker.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## FacetValueChecker
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/utils/facet-value-checker.ts" sourceLine="48" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/helpers/facet-value-checker/facet-value-checker.ts" sourceLine="53" packageName="@vendure/core" />
 
 The FacetValueChecker is a helper class used to determine whether a given OrderLine consists
 of ProductVariants containing the given FacetValues.
@@ -33,7 +33,7 @@ export const hasFacetValues = new PromotionCondition({
     facets: { type: 'ID', list: true, ui: { component: 'facet-value-form-input' } },
   },
   init(injector) {
-    facetValueChecker = new FacetValueChecker(injector.get(TransactionalConnection));
+    facetValueChecker = injector.get(FacetValueChecker);
   },
   async check(ctx, order, args) {
     let matches = 0;
@@ -48,17 +48,26 @@ export const hasFacetValues = new PromotionCondition({
 ```
 
 ```ts title="Signature"
-class FacetValueChecker {
-    constructor(connection: TransactionalConnection)
+class FacetValueChecker implements OnModuleInit {
+    constructor(connection: TransactionalConnection, cacheService: CacheService, eventBus?: EventBus)
+    onModuleInit() => any;
     hasFacetValues(orderLine: OrderLine, facetValueIds: ID[], ctx?: RequestContext) => Promise<boolean>;
 }
 ```
+* Implements: <code>OnModuleInit</code>
+
+
 
 <div className="members-wrapper">
 
 ### constructor
 
-<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>) => FacetValueChecker`}   />
+<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, cacheService: <a href='/reference/typescript-api/cache/cache-service#cacheservice'>CacheService</a>, eventBus?: <a href='/reference/typescript-api/events/event-bus#eventbus'>EventBus</a>) => FacetValueChecker`}   />
+
+
+### onModuleInit
+
+<MemberInfo kind="method" type={`() => any`}   />
 
 
 ### hasFacetValues

+ 101 - 12
docs/docs/reference/typescript-api/promotions/promotion-action.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## PromotionAction
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="247" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="281" packageName="@vendure/core" />
 
 An abstract class which is extended by <a href='/reference/typescript-api/promotions/promotion-action#promotionitemaction'>PromotionItemAction</a>, <a href='/reference/typescript-api/promotions/promotion-action#promotionorderaction'>PromotionOrderAction</a>,
 and <a href='/reference/typescript-api/promotions/promotion-action#promotionshippingaction'>PromotionShippingAction</a>.
@@ -47,7 +47,7 @@ more information.
 
 ## PromotionItemAction
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="320" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="354" packageName="@vendure/core" />
 
 Represents a PromotionAction which applies to individual <a href='/reference/typescript-api/entities/order-line#orderline'>OrderLine</a>s.
 
@@ -88,7 +88,7 @@ class PromotionItemAction<T extends ConfigArgs = ConfigArgs, U extends Array<Pro
 
 ## PromotionOrderAction
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="375" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="464" packageName="@vendure/core" />
 
 Represents a PromotionAction which applies to the <a href='/reference/typescript-api/entities/order#order'>Order</a> as a whole.
 
@@ -129,7 +129,7 @@ class PromotionOrderAction<T extends ConfigArgs = ConfigArgs, U extends Promotio
 
 ## PromotionShippingAction
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="417" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="506" packageName="@vendure/core" />
 
 Represents a PromotionAction which applies to the shipping cost of an Order.
 
@@ -159,7 +159,7 @@ class PromotionShippingAction<T extends ConfigArgs = ConfigArgs, U extends Promo
 <GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="72" packageName="@vendure/core" />
 
 The function which is used by a PromotionItemAction to calculate the
-discount on the OrderLine.
+discount on the OrderLine for each item.
 
 ```ts title="Signature"
 type ExecutePromotionItemActionFn<T extends ConfigArgs, U extends Array<PromotionCondition<any>>> = (
@@ -172,10 +172,28 @@ type ExecutePromotionItemActionFn<T extends ConfigArgs, U extends Array<Promotio
 ```
 
 
-## ExecutePromotionOrderActionFn
+## ExecutePromotionLineActionFn
 
 <GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="88" packageName="@vendure/core" />
 
+The function which is used by a PromotionLineAction to calculate the
+discount on the OrderLine.
+
+```ts title="Signature"
+type ExecutePromotionLineActionFn<T extends ConfigArgs, U extends Array<PromotionCondition<any>>> = (
+    ctx: RequestContext,
+    orderLine: OrderLine,
+    args: ConfigArgValues<T>,
+    state: ConditionState<U>,
+    promotion: Promotion,
+) => number | Promise<number>
+```
+
+
+## ExecutePromotionOrderActionFn
+
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="104" packageName="@vendure/core" />
+
 The function which is used by a PromotionOrderAction to calculate the
 discount on the Order.
 
@@ -192,7 +210,7 @@ type ExecutePromotionOrderActionFn<T extends ConfigArgs, U extends Array<Promoti
 
 ## ExecutePromotionShippingActionFn
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="104" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="120" packageName="@vendure/core" />
 
 The function which is used by a PromotionOrderAction to calculate the
 discount on the Order.
@@ -211,7 +229,7 @@ type ExecutePromotionShippingActionFn<T extends ConfigArgs, U extends Array<Prom
 
 ## PromotionActionSideEffectFn
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="125" packageName="@vendure/core" since="1.8.0" experimental="true" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="141" packageName="@vendure/core" since="1.8.0" experimental="true" />
 
 The signature of a PromotionAction's side-effect functions `onActivate` and `onDeactivate`.
 
@@ -227,7 +245,7 @@ type PromotionActionSideEffectFn<T extends ConfigArgs> = (
 
 ## PromotionActionConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="139" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="155" packageName="@vendure/core" />
 
 Configuration for all types of <a href='/reference/typescript-api/promotions/promotion-action#promotionaction'>PromotionAction</a>.
 
@@ -284,7 +302,7 @@ Used to reverse or clean up any side effects executed as part of the `onActivate
 
 ## PromotionItemActionConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="193" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="209" packageName="@vendure/core" />
 
 Configuration for a <a href='/reference/typescript-api/promotions/promotion-action#promotionitemaction'>PromotionItemAction</a>
 
@@ -311,9 +329,38 @@ the OrderLine, i.e. the number should be negative.
 </div>
 
 
+## PromotionLineActionConfig
+
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="227" packageName="@vendure/core" />
+
+Configuration for a <a href='/reference/typescript-api/promotions/promotion-action#promotionlineaction'>PromotionLineAction</a>
+
+```ts title="Signature"
+interface PromotionLineActionConfig<T extends ConfigArgs, U extends PromotionCondition[]> extends PromotionActionConfig<T, U> {
+    execute: ExecutePromotionLineActionFn<T, U>;
+}
+```
+* Extends: <code><a href='/reference/typescript-api/promotions/promotion-action#promotionactionconfig'>PromotionActionConfig</a>&#60;T, U&#62;</code>
+
+
+
+<div className="members-wrapper">
+
+### execute
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/promotions/promotion-action#executepromotionlineactionfn'>ExecutePromotionLineActionFn</a>&#60;T, U&#62;`}   />
+
+The function which contains the promotion calculation logic.
+Should resolve to a number which represents the amount by which to discount
+the OrderLine, i.e. the number should be negative.
+
+
+</div>
+
+
 ## PromotionOrderActionConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="210" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="244" packageName="@vendure/core" />
 
 
 
@@ -342,7 +389,7 @@ the Order, i.e. the number should be negative.
 
 ## PromotionShippingActionConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="227" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="261" packageName="@vendure/core" />
 
 
 
@@ -366,4 +413,46 @@ Should resolve to a number which represents the amount by which to discount
 the Shipping, i.e. the number should be negative.
 
 
+</div>
+
+
+## PromotionLineAction
+
+<GenerationInfo sourceFile="packages/core/src/config/promotion/promotion-action.ts" sourceLine="409" packageName="@vendure/core" />
+
+Represents a PromotionAction which applies to individual <a href='/reference/typescript-api/entities/order-line#orderline'>OrderLine</a>s.
+The difference from PromotionItemAction is that it applies regardless of the Quantity of the OrderLine.
+
+*Example*
+
+```ts
+// Applies a percentage discount to each OrderLine
+const linePercentageDiscount = new PromotionLineAction({
+    code: 'line_percentage_discount',
+    args: { discount: 'percentage' },
+    execute(ctx, orderLine, args) {
+        return -orderLine.linePrice * (args.discount / 100);
+    },
+    description: 'Discount every line by { discount }%',
+});
+```
+
+```ts title="Signature"
+class PromotionLineAction<T extends ConfigArgs = ConfigArgs, U extends Array<PromotionCondition<any>> = []> extends PromotionAction<T, U> {
+    constructor(config: PromotionLineActionConfig<T, U>)
+}
+```
+* Extends: <code><a href='/reference/typescript-api/promotions/promotion-action#promotionaction'>PromotionAction</a>&#60;T, U&#62;</code>
+
+
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(config: <a href='/reference/typescript-api/promotions/promotion-action#promotionlineactionconfig'>PromotionLineActionConfig</a>&#60;T, U&#62;) => PromotionLineAction`}   />
+
+
+
+
 </div>

+ 1 - 1
docs/docs/reference/typescript-api/promotions/promotion-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## PromotionOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="736" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="738" packageName="@vendure/core" />
 
 
 

+ 1 - 1
docs/docs/reference/typescript-api/request/ctx-decorator.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Ctx
 
-<GenerationInfo sourceFile="packages/core/src/api/decorators/request-context.decorator.ts" sourceLine="21" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/api/decorators/request-context.decorator.ts" sourceLine="23" packageName="@vendure/core" />
 
 Resolver param decorator which extracts the <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a> from the incoming
 request object.

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## RequestContext
 
-<GenerationInfo sourceFile="packages/core/src/api/common/request-context.ts" sourceLine="44" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/api/common/request-context.ts" sourceLine="179" packageName="@vendure/core" />
 
 The RequestContext holds information relevant to the current request, which may be
 required at various points of the stack.
@@ -23,6 +23,11 @@ This allows the service layer to access information about the current user, the
 the active Channel, and so on. In addition, the <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a> relies on the
 presence of the RequestContext object in order to correctly handle per-request database transactions.
 
+The RequestContext also provides mechanisms for managing the database replication mode via the
+`setReplicationMode` method and the `replicationMode` getter. This allows for finer control
+over whether database queries within the context should be executed against the master or a replica
+database, which can be particularly useful in distributed database environments.
+
 *Example*
 
 ```ts
@@ -32,6 +37,16 @@ myQuery(@Ctx() ctx: RequestContext) {
 }
 ```
 
+*Example*
+
+```ts
+@Query()
+myMutation(@Ctx() ctx: RequestContext) {
+  ctx.setReplicationMode('master');
+  return this.myService.getData(ctx);
+}
+```
+
 ```ts title="Signature"
 class RequestContext {
     empty() => RequestContext;
@@ -50,6 +65,8 @@ class RequestContext {
     isAuthorized: boolean
     authorizedAsOwnerOnly: boolean
     translate(key: string, variables?: { [k: string]: any }) => string;
+    setReplicationMode(mode: ReplicationMode) => void;
+    replicationMode: ReplicationMode | undefined
 }
 ```
 
@@ -146,6 +163,20 @@ are owned by the current session.
 <MemberInfo kind="method" type={`(key: string, variables?: { [k: string]: any }) => string`}   />
 
 Translate the given i18n key
+### setReplicationMode
+
+<MemberInfo kind="method" type={`(mode: ReplicationMode) => void`}   />
+
+Sets the replication mode for the current RequestContext. This mode determines whether the operations
+within this context should interact with the master database or a replica. Use this method to explicitly
+define the replication mode for the context.
+### replicationMode
+
+<MemberInfo kind="property" type={`ReplicationMode | undefined`}   />
+
+Gets the current replication mode of the RequestContext. If no replication mode has been set,
+it returns `undefined`. This property indicates whether the context is configured to interact with
+the master database or a replica.
 
 
 </div>

+ 2 - 2
docs/docs/reference/typescript-api/service-helpers/order-calculator.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## OrderCalculator
 
-<GenerationInfo sourceFile="packages/core/src/service/helpers/order-calculator/order-calculator.ts" sourceLine="33" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/helpers/order-calculator/order-calculator.ts" sourceLine="34" packageName="@vendure/core" />
 
 This helper is used when making changes to an Order, to apply all applicable price adjustments to that Order,
 including:
@@ -32,7 +32,7 @@ class OrderCalculator {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(configService: ConfigService, zoneService: <a href='/reference/typescript-api/services/zone-service#zoneservice'>ZoneService</a>, taxRateService: <a href='/reference/typescript-api/services/tax-rate-service#taxrateservice'>TaxRateService</a>, shippingMethodService: <a href='/reference/typescript-api/services/shipping-method-service#shippingmethodservice'>ShippingMethodService</a>, shippingCalculator: <a href='/reference/typescript-api/shipping/shipping-calculator#shippingcalculator'>ShippingCalculator</a>, requestContextCache: RequestContextCacheService) => OrderCalculator`}   />
+<MemberInfo kind="method" type={`(configService: ConfigService, zoneService: <a href='/reference/typescript-api/services/zone-service#zoneservice'>ZoneService</a>, taxRateService: <a href='/reference/typescript-api/services/tax-rate-service#taxrateservice'>TaxRateService</a>, shippingMethodService: <a href='/reference/typescript-api/services/shipping-method-service#shippingmethodservice'>ShippingMethodService</a>, shippingCalculator: <a href='/reference/typescript-api/shipping/shipping-calculator#shippingcalculator'>ShippingCalculator</a>, requestContextCache: <a href='/reference/typescript-api/cache/request-context-cache-service#requestcontextcacheservice'>RequestContextCacheService</a>) => OrderCalculator`}   />
 
 
 ### applyPriceAdjustments

+ 1 - 1
docs/docs/reference/typescript-api/service-helpers/order-modifier.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## OrderModifier
 
-<GenerationInfo sourceFile="packages/core/src/service/helpers/order-modifier/order-modifier.ts" sourceLine="81" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/helpers/order-modifier/order-modifier.ts" sourceLine="82" packageName="@vendure/core" />
 
 This helper is responsible for modifying the contents of an Order.
 

+ 1 - 1
docs/docs/reference/typescript-api/service-helpers/product-price-applicator.md

@@ -49,7 +49,7 @@ class ProductPriceApplicator {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(configService: ConfigService, taxRateService: <a href='/reference/typescript-api/services/tax-rate-service#taxrateservice'>TaxRateService</a>, zoneService: <a href='/reference/typescript-api/services/zone-service#zoneservice'>ZoneService</a>, requestCache: RequestContextCacheService) => ProductPriceApplicator`}   />
+<MemberInfo kind="method" type={`(configService: ConfigService, taxRateService: <a href='/reference/typescript-api/services/tax-rate-service#taxrateservice'>TaxRateService</a>, zoneService: <a href='/reference/typescript-api/services/zone-service#zoneservice'>ZoneService</a>, requestCache: <a href='/reference/typescript-api/cache/request-context-cache-service#requestcontextcacheservice'>RequestContextCacheService</a>) => ProductPriceApplicator`}   />
 
 
 ### applyChannelPriceAndTax

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

@@ -27,7 +27,7 @@ class GlobalSettingsService {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, configService: ConfigService, customFieldRelationService: CustomFieldRelationService, eventBus: <a href='/reference/typescript-api/events/event-bus#eventbus'>EventBus</a>, requestCache: RequestContextCacheService) => GlobalSettingsService`}   />
+<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, configService: ConfigService, customFieldRelationService: CustomFieldRelationService, eventBus: <a href='/reference/typescript-api/events/event-bus#eventbus'>EventBus</a>, requestCache: <a href='/reference/typescript-api/cache/request-context-cache-service#requestcontextcacheservice'>RequestContextCacheService</a>) => GlobalSettingsService`}   />
 
 
 ### getSettings

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 11 - 4
docs/docs/reference/typescript-api/services/order-service.md


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 1
docs/docs/reference/typescript-api/services/product-variant-service.md


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

@@ -39,7 +39,7 @@ class RoleService {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, channelService: <a href='/reference/typescript-api/services/channel-service#channelservice'>ChannelService</a>, listQueryBuilder: <a href='/reference/typescript-api/data-access/list-query-builder#listquerybuilder'>ListQueryBuilder</a>, configService: ConfigService, eventBus: <a href='/reference/typescript-api/events/event-bus#eventbus'>EventBus</a>, requestContextCache: RequestContextCacheService) => RoleService`}   />
+<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, channelService: <a href='/reference/typescript-api/services/channel-service#channelservice'>ChannelService</a>, listQueryBuilder: <a href='/reference/typescript-api/data-access/list-query-builder#listquerybuilder'>ListQueryBuilder</a>, configService: ConfigService, eventBus: <a href='/reference/typescript-api/events/event-bus#eventbus'>EventBus</a>, requestContextCache: <a href='/reference/typescript-api/cache/request-context-cache-service#requestcontextcacheservice'>RequestContextCacheService</a>) => RoleService`}   />
 
 
 ### initRoles

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

@@ -23,7 +23,7 @@ class TaxRateService {
     create(ctx: RequestContext, input: CreateTaxRateInput) => Promise<TaxRate>;
     update(ctx: RequestContext, input: UpdateTaxRateInput) => Promise<TaxRate>;
     delete(ctx: RequestContext, id: ID) => Promise<DeletionResponse>;
-    getApplicableTaxRate(ctx: RequestContext, zone: Zone, taxCategory: TaxCategory) => Promise<TaxRate>;
+    getApplicableTaxRate(ctx: RequestContext, zone: Zone | ID, taxCategory: TaxCategory | ID) => Promise<TaxRate>;
 }
 ```
 
@@ -61,7 +61,7 @@ class TaxRateService {
 
 ### getApplicableTaxRate
 
-<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, zone: <a href='/reference/typescript-api/entities/zone#zone'>Zone</a>, taxCategory: <a href='/reference/typescript-api/entities/tax-category#taxcategory'>TaxCategory</a>) => Promise&#60;<a href='/reference/typescript-api/entities/tax-rate#taxrate'>TaxRate</a>&#62;`}   />
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, zone: <a href='/reference/typescript-api/entities/zone#zone'>Zone</a> | <a href='/reference/typescript-api/common/id#id'>ID</a>, taxCategory: <a href='/reference/typescript-api/entities/tax-category#taxcategory'>TaxCategory</a> | <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;<a href='/reference/typescript-api/entities/tax-rate#taxrate'>TaxRate</a>&#62;`}   />
 
 Returns the applicable TaxRate based on the specified Zone and TaxCategory. Used when calculating Order
 prices.

+ 1 - 1
docs/docs/reference/typescript-api/shipping/check-shipping-eligibility-checker-fn.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CheckShippingEligibilityCheckerFn
 
-<GenerationInfo sourceFile="packages/core/src/config/shipping-method/shipping-eligibility-checker.ts" sourceLine="123" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/shipping-method/shipping-eligibility-checker.ts" sourceLine="130" packageName="@vendure/core" />
 
 A function which implements logic to determine whether a given <a href='/reference/typescript-api/entities/order#order'>Order</a> is eligible for
 a particular shipping method. Once a ShippingMethod has been assigned to an Order, this

+ 1 - 1
docs/docs/reference/typescript-api/shipping/shipping-eligibility-checker-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ShippingEligibilityCheckerConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/shipping-method/shipping-eligibility-checker.ts" sourceLine="22" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/shipping-method/shipping-eligibility-checker.ts" sourceLine="23" packageName="@vendure/core" />
 
 Configuration passed into the constructor of a <a href='/reference/typescript-api/shipping/shipping-eligibility-checker#shippingeligibilitychecker'>ShippingEligibilityChecker</a> to
 configure its behavior.

+ 7 - 1
docs/docs/reference/typescript-api/shipping/shipping-eligibility-checker.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ShippingEligibilityChecker
 
-<GenerationInfo sourceFile="packages/core/src/config/shipping-method/shipping-eligibility-checker.ts" sourceLine="49" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/shipping-method/shipping-eligibility-checker.ts" sourceLine="50" packageName="@vendure/core" />
 
 The ShippingEligibilityChecker class is used to check whether an order qualifies for a
 given <a href='/reference/typescript-api/entities/shipping-method#shippingmethod'>ShippingMethod</a>.
@@ -34,6 +34,7 @@ const minOrderTotalEligibilityChecker = new ShippingEligibilityChecker({
 ```ts title="Signature"
 class ShippingEligibilityChecker<T extends ConfigArgs = ConfigArgs> extends ConfigurableOperationDef<T> {
     constructor(config: ShippingEligibilityCheckerConfig<T>)
+    init(injector: Injector) => ;
 }
 ```
 * Extends: <code><a href='/reference/typescript-api/configurable-operation-def/#configurableoperationdef'>ConfigurableOperationDef</a>&#60;T&#62;</code>
@@ -47,6 +48,11 @@ class ShippingEligibilityChecker<T extends ConfigArgs = ConfigArgs> extends Conf
 <MemberInfo kind="method" type={`(config: <a href='/reference/typescript-api/shipping/shipping-eligibility-checker-config#shippingeligibilitycheckerconfig'>ShippingEligibilityCheckerConfig</a>&#60;T&#62;) => ShippingEligibilityChecker`}   />
 
 
+### init
+
+<MemberInfo kind="method" type={`(injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>) => `}   />
+
+
 
 
 </div>

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ShippingOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="752" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="754" packageName="@vendure/core" />
 
 
 

+ 1 - 1
docs/docs/reference/typescript-api/shipping/should-run-check-fn.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ShouldRunCheckFn
 
-<GenerationInfo sourceFile="packages/core/src/config/shipping-method/shipping-eligibility-checker.ts" sourceLine="158" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/shipping-method/shipping-eligibility-checker.ts" sourceLine="165" packageName="@vendure/core" />
 
 An optional method which is used to decide whether to run the `check()` function.
 Returns a JSON-compatible object which is cached and compared between calls.

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## TaxOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="865" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="867" packageName="@vendure/core" />
 
 
 

+ 1 - 0
docs/sidebars.js

@@ -91,6 +91,7 @@ const sidebars = {
                     value: 'Advanced Topics',
                     className: 'sidebar-section-header',
                 },
+                'guides/developer-guide/cache/index',
                 'guides/developer-guide/db-subscribers/index',
                 'guides/developer-guide/importing-data/index',
                 'guides/developer-guide/logging/index',

+ 1 - 1
packages/core/src/api/common/request-context.ts

@@ -154,7 +154,7 @@ export function internal_getRequestContext(
  * presence of the RequestContext object in order to correctly handle per-request database transactions.
  *
  * The RequestContext also provides mechanisms for managing the database replication mode via the
- * {@link setReplicationMode} method and the {@link replicationMode} getter. This allows for finer control
+ * `setReplicationMode` method and the `replicationMode` getter. This allows for finer control
  * over whether database queries within the context should be executed against the master or a replica
  * database, which can be particularly useful in distributed database environments.
  *

+ 1 - 21
packages/core/src/cache/cache.service.ts

@@ -15,6 +15,7 @@ import { Cache, CacheConfig } from './cache';
  * the cache into a key-value store.
  *
  * @since 3.1.0
+ * @docsCategory cache
  */
 @Injectable()
 export class CacheService {
@@ -29,27 +30,6 @@ export class CacheService {
      *
      * The `Cache` instance provides a convenience wrapper around the `CacheService`
      * methods.
-     *
-     * @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);
-     * });
-     * ```
      */
     createCache(config: CacheConfig): Cache {
         return new Cache(config, this);

+ 27 - 1
packages/core/src/cache/cache.ts

@@ -7,6 +7,9 @@ import { CacheService } from './cache.service';
 /**
  * @description
  * Configuration for a new {@link Cache} instance.
+ *
+ * @docsCategory cache
+ * @since 3.1.0
  */
 export interface CacheConfig {
     /**
@@ -41,7 +44,30 @@ export interface CacheConfig {
  *
  * In most cases, using the `Cache` class will result in simpler and more readable code.
  *
- * This class is normally created via the {@link CacheService.createCache} method.
+ * This class is normally created via the {@link CacheService}.`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);
+ * });
+ * ```
+ *
+ * @docsCategory cache
+ * @since 3.1.0
  */
 export class Cache {
     constructor(

+ 14 - 0
packages/core/src/cache/request-context-cache.service.ts

@@ -6,14 +6,28 @@ import { RequestContext } from '../api';
  * 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.
+ *
+ * @docsCategory cache
  */
 export class RequestContextCacheService {
     private caches = new WeakMap<RequestContext, Map<any, any>>();
 
+    /**
+     * @description
+     * Set a value in the RequestContext cache.
+     */
     set<T = any>(ctx: RequestContext, key: any, val: T): void {
         this.getContextCache(ctx).set(key, val);
     }
 
+    /**
+     * @description
+     * 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<T = any>(ctx: RequestContext, key: any): T | undefined;
     get<T>(ctx: RequestContext, key: any, getDefault?: () => T): T;
     get<T>(ctx: RequestContext, key: any, getDefault?: () => T): T | Promise<T> | undefined {

+ 13 - 4
packages/core/src/config/session-cache/session-cache-strategy.ts

@@ -50,10 +50,19 @@ export type CachedSession = {
  * 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 {@link InMemorySessionCacheStrategy}, 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 {@link DefaultSessionCacheStrategy}, which delegates
+ * to the configured {@link CacheStrategy} 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 {@link DefaultSessionCacheStrategy}.
+ *
+ * :::
+ *
+ * Prior to v3.1, the default was to use the {@link InMemorySessionCacheStrategy}, which is fast but suitable for
+ * single-instance deployments.
  *
  * :::info
  *

+ 1 - 0
packages/core/src/config/system/cache-strategy.ts

@@ -31,6 +31,7 @@ export interface SetCacheKeyOptions {
  * from the cache.
  *
  * @since 3.1.0
+ * @docsCategory cache
  */
 export interface CacheStrategy extends InjectableStrategy {
     /**

+ 1 - 1
packages/core/src/config/vendure-config.ts

@@ -383,7 +383,7 @@ export interface AuthOptions {
     /**
      * @description
      * 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 {@link SystemOptions.cacheStrategy}.
+     * the underlying cache strategy defined in the {@link SystemOptions}`.cacheStrategy`.
      *
      * @default DefaultSessionCacheStrategy
      */

+ 10 - 0
packages/core/src/plugin/default-cache-plugin/default-cache-plugin.ts

@@ -21,6 +21,16 @@ export interface DefaultCachePluginInitOptions {
     cacheTtlProvider?: CacheTtlProvider;
 }
 
+/**
+ * @description
+ * This plugin provides a simple SQL-based cache strategy {@link SqlCacheStrategy} 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 {@link RedisCachePlugin}.
+ *
+ * @docsCategory cache
+ * @since 3.1.0
+ */
 @VendurePlugin({
     imports: [PluginCommonModule],
     entities: [CacheItem, CacheTag],

+ 2 - 5
packages/core/src/plugin/default-cache-plugin/sql-cache-strategy.ts

@@ -10,13 +10,10 @@ import { CacheItem } from './cache-item.entity';
 import { CacheTag } from './cache-tag.entity';
 
 /**
- * A {@link CacheStrategy} that stores the cache in memory using a simple
- * JavaScript Map.
- *
- * **Caution** do not use this in a multi-instance deployment because
- * cache invalidation will not propagate to other instances.
+ * A {@link CacheStrategy} that stores cached items in the database.
  *
  * @since 3.1.0
+ * @docsCategory cache
  */
 export class SqlCacheStrategy implements CacheStrategy {
     protected cacheSize = 10_000;

+ 9 - 0
packages/core/src/plugin/redis-cache-plugin/redis-cache-plugin.ts

@@ -5,6 +5,15 @@ import { PLUGIN_INIT_OPTIONS } from './constants';
 import { RedisCacheStrategy } from './redis-cache-strategy';
 import { RedisCachePluginInitOptions } from './types';
 
+/**
+ * @description
+ * This plugin provides a Redis-based {@link RedisCacheStrategy} 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 {@link DefaultCachePlugin}.
+ *
+ * @docsCategory cache
+ * @since 3.1.0
+ */
 @VendurePlugin({
     imports: [PluginCommonModule],
     providers: [{ provide: PLUGIN_INIT_OPTIONS, useFactory: () => RedisCachePlugin.options }],

+ 8 - 0
packages/core/src/plugin/redis-cache-plugin/redis-cache-strategy.ts

@@ -6,6 +6,14 @@ import { CacheStrategy, SetCacheKeyOptions } from '../../config/system/cache-str
 import { DEFAULT_NAMESPACE, DEFAULT_TTL, loggerCtx } from './constants';
 import { RedisCachePluginInitOptions } from './types';
 
+/**
+ * @description
+ * A {@link CacheStrategy} which stores cached items in a Redis instance.
+ * This is a high-performance cache strategy which is suitable for production use.
+ *
+ * @docsCategory cache
+ * @since 3.1.0
+ */
 export class RedisCacheStrategy implements CacheStrategy {
     private client: import('ioredis').Redis;
 

+ 1 - 1
packages/core/src/service/helpers/external-authentication/external-authentication.service.ts

@@ -243,7 +243,7 @@ export class ExternalAuthenticationService {
     /**
      * @description
      * Looks up a User based on their identifier from an external authentication
-     * provider. Creates the user if does not exist. Unlike {@link findCustomerUser} and {@link findAdministratorUser},
+     * 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.
      *

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.