Browse Source

docs(core): Add docs on scheduled tasks

Michael Bromley 9 months ago
parent
commit
31c6eba1e3
39 changed files with 1072 additions and 106 deletions
  1. 233 80
      docs/docs/guides/developer-guide/scheduled-tasks/index.md
  2. 12 0
      docs/docs/reference/graphql-api/admin/input-types.md
  3. 9 0
      docs/docs/reference/graphql-api/admin/mutations.md
  4. 26 0
      docs/docs/reference/graphql-api/admin/object-types.md
  5. 9 0
      docs/docs/reference/graphql-api/admin/queries.md
  6. 36 0
      docs/docs/reference/graphql-api/shop/object-types.md
  7. 24 0
      docs/docs/reference/graphql-api/shop/queries.md
  8. 1 1
      docs/docs/reference/typescript-api/assets/asset-options.md
  9. 1 1
      docs/docs/reference/typescript-api/auth/auth-options.md
  10. 1 1
      docs/docs/reference/typescript-api/auth/cookie-options.md
  11. 1 1
      docs/docs/reference/typescript-api/auth/superadmin-credentials.md
  12. 1 1
      docs/docs/reference/typescript-api/common/permission.md
  13. 1 1
      docs/docs/reference/typescript-api/configuration/api-options.md
  14. 1 1
      docs/docs/reference/typescript-api/configuration/entity-options.md
  15. 7 1
      docs/docs/reference/typescript-api/configuration/runtime-vendure-config.md
  16. 1 1
      docs/docs/reference/typescript-api/configuration/system-options.md
  17. 7 1
      docs/docs/reference/typescript-api/configuration/vendure-config.md
  18. 1 1
      docs/docs/reference/typescript-api/import-export/import-export-options.md
  19. 1 1
      docs/docs/reference/typescript-api/job-queue/job-queue-options.md
  20. 1 1
      docs/docs/reference/typescript-api/orders/order-options.md
  21. 1 1
      docs/docs/reference/typescript-api/payment/payment-options.md
  22. 1 1
      docs/docs/reference/typescript-api/products-stock/catalog-options.md
  23. 1 1
      docs/docs/reference/typescript-api/promotions/promotion-options.md
  24. 44 0
      docs/docs/reference/typescript-api/scheduled-tasks/clean-sessions-task.md
  25. 83 0
      docs/docs/reference/typescript-api/scheduled-tasks/default-scheduler-plugin.md
  26. 68 0
      docs/docs/reference/typescript-api/scheduled-tasks/default-scheduler-strategy.md
  27. 14 0
      docs/docs/reference/typescript-api/scheduled-tasks/index.md
  28. 164 0
      docs/docs/reference/typescript-api/scheduled-tasks/scheduled-task.md
  29. 41 0
      docs/docs/reference/typescript-api/scheduled-tasks/scheduler-options.md
  30. 54 0
      docs/docs/reference/typescript-api/scheduled-tasks/scheduler-service.md
  31. 119 0
      docs/docs/reference/typescript-api/scheduled-tasks/scheduler-strategy.md
  32. 23 5
      docs/docs/reference/typescript-api/services/session-service.md
  33. 1 1
      docs/docs/reference/typescript-api/shipping/shipping-options.md
  34. 1 1
      docs/docs/reference/typescript-api/tax/tax-options.md
  35. 4 2
      packages/core/src/plugin/default-scheduler-plugin/default-scheduler.plugin.ts
  36. 1 0
      packages/core/src/plugin/default-scheduler-plugin/types.ts
  37. 18 1
      packages/core/src/scheduler/scheduled-task.ts
  38. 34 0
      packages/core/src/scheduler/scheduler-strategy.ts
  39. 26 0
      packages/core/src/scheduler/tasks/clean-sessions-task.ts

+ 233 - 80
docs/docs/guides/developer-guide/scheduled-tasks/index.md

@@ -1,9 +1,9 @@
 ---
-title: "Scheduled Tasks"
+title: 'Scheduled Tasks'
 showtoc: true
 ---
 
-Scheduled tasks are a way of executing some code at pre-defined intervals. There are many examples of work that can be done using scheduled tasks, 
+Scheduled tasks are a way of executing some code at pre-defined intervals. There are many examples of work that can be done using scheduled tasks,
 such as:
 
 - Generating a sitemap
@@ -11,131 +11,284 @@ such as:
 - Sending abandoned cart emails
 - Cleaning up old data
 
-In Vendure you can create scheduled tasks by defining a [standalone script](/guides/developer-guide/stand-alone-scripts/) which can then 
-be executed via any scheduling mechanism you like, such as a cron job or similar mechanism provided by your hosting provider.
+Since Vendure v3.3, there is a built-in mechanism which allows you to define scheduled tasks in a convenient and powerful way.
 
-## Creating a Scheduled Task
+:::info
+All the information on page applies to Vendure v3.3+
+
+For older versions, there is no built-in support for scheduled tasks, but you can
+instead use a [stand-alone script](/guides/developer-guide/stand-alone-scripts/) triggered by a cron job.
+:::
+
+## Setting up the DefaultSchedulerPlugin
 
-Let's imagine that you have created a plugin that exposes a `SitemapService` which generates a sitemap for your store. You want to run this
-task every night at midnight. 
+In your Vendure config, import and add the [DefaultSchedulerPlugin](/reference/typescript-api/scheduled-tasks/default-scheduler-plugin) to your
+plugins array. If you created your project with a version newer than v3.3, this should already be configured.
 
-First we need to create a standalone script which will run the task. This script will look something like this:
+```ts title="vendure-config.ts"
+import { DefaultSchedulerPlugin, VendureConfig } from '@vendure/core';
 
-```ts title="scheduled-tasks.ts"
-import { bootstrapWorker, Logger, RequestContextService } from '@vendure/core';
-import { SitemapService } from './plugins/sitemap';
+export const config: VendureConfig = {
+    // ...
+    plugins: [DefaultSchedulerPlugin.init()],
+};
+```
+
+When you first add this plugin to your config, you'll need to [generate a migration](/guides/developer-guide/migrations/) because the
+plugin will make use of a new database table in order to guarantee only-once execution of tasks.
 
-import { config } from './vendure-config';
+You can then start adding tasks. Vendure ships with a task that will clean up old sessions from the database.
+
+:::note
+The `cleanSessionsTask` task is actually configured by default from v3.3+, so normally you won't have to specify this
+manually unless you wish to change any of the default configuration using the `.configure()` method.
+:::
 
-if (require.main === module) {
-    generateSitemap()
-        .then(() => process.exit(0))
-        .catch(err => {
-            Logger.error(err);
-            process.exit(1);
+```ts title="vendure-config.ts"
+import { cleanSessionsTask, DefaultSchedulerPlugin, VendureConfig } from '@vendure/core';
+
+export const config: VendureConfig = {
+    // ...
+    schedulerOptions: {
+        tasks: [
+            // Use the task as is
+            cleanSessionsTask,
+            // or further configure the task
+            cleanSessionsTask.configure({
+                // Run the task every day at 3:00am
+                // The default schedule is every day at 00:00am
+                schedule: cron => cron.everyDayAt(3, 0),
+                params: {
+                    // How many sessions to process in each batch
+                    // Default: 10_000
+                    batchSize: 5_000,
+                },
+            }),
+        ],
+    },
+    plugins: [DefaultSchedulerPlugin.init()],
+};
+```
+
+## Creating a Scheduled Task
+
+Let's imagine that you have created a `SitemapPlugin` that exposes a `SitemapService` which generates a sitemap for your store. You want to run this
+task every night at midnight.
+
+Inside the plugin, you would first define a new [ScheduledTask](/reference/typescript-api/scheduled-tasks/scheduled-task) instance:
+
+```ts title="/plugins/sitemap/config/generate-sitemap-task.ts"
+import { ScheduledTask, RequestContextService } from '@vendure/core';
+
+import { SitemapService } from '../services/sitemap.service';
+
+export const generateSitemapTask = new ScheduledTask({
+    // Give your task a unique ID
+    id: 'generate-sitemap',
+    // A human-readable description of the task
+    description: 'Generates a sitemap file',
+    // Params can be used to further configure aspects of the
+    // task. They get passed in to the `execute` function as the
+    // second argument.
+    // They can be later modified using the `.configure()` method on the instance
+    params: {
+        shopBaseUrl: 'https://www.myshop.com',
+    },
+    // Define a default schedule. This can be modified using the
+    // `.configure()` method on the instance later.
+    schedule: cron => cron.everyDayAt(0, 0),
+    // This is the function that will be executed per the schedule.
+    async execute(injector, params) {
+        // Using `app.get()` we can grab an instance of _any_ provider defined in the
+        // Vendure core as well as by our plugins.
+        const sitemapService = app.get(SitemapService);
+
+        // For most service methods, we'll need to pass a RequestContext object.
+        // We can use the RequestContextService to create one.
+        const ctx = await app.get(RequestContextService).create({
+            apiType: 'admin',
         });
-}
 
-async function generateSitemap() {
-    // This will bootstrap an instance of the Vendure Worker, providing
-    // us access to all of the services defined in the Vendure core.
-    // (but without the unnecessary overhead of the API layer).
-    const { app } = await bootstrapWorker(config);
-
-    // Using `app.get()` we can grab an instance of _any_ provider defined in the
-    // Vendure core as well as by our plugins.
-    const sitemapService = app.get(SitemapService);
-
-    // For most service methods, we'll need to pass a RequestContext object.
-    // We can use the RequestContextService to create one.
-    const ctx = await app.get(RequestContextService).create({
-        apiType: 'admin',
-    });
-    
-    await sitemapService.generateSitemap(ctx);
-
-    Logger.info(`Completed sitemap generation`);
-}
+        // Here's the actual work we want to perform.
+        const result = await sitemapService.generateSitemap(ctx);
+
+        // The return value from the `execute` function will be available
+        // as the `lastResult` property when viewing tasks.
+        return { result };
+    },
+});
 ```
 
-### Schedule the task
+## Using a task
+
+Now that the task has been defined, we need to tell Vendure to use it.
+
+To do so we need to add it to the [schedulerOptions.tasks](/reference/typescript-api/scheduled-tasks/scheduler-options#tasks) array.
+
+### Adding directly in Vendure config
+
+This can be done directly in your Vendure config file:
+
+```ts title="vendure-config.ts"
+import { cleanSessionsTask, DefaultSchedulerPlugin, VendureConfig } from '@vendure/core';
+
+// highlight-next-line
+import { SitemapPlugin, generateSitemapTask } from './plugins/sitemap';
+
+export const config: VendureConfig = {
+    // ...
+    schedulerOptions: {
+        tasks: [
+            cleanSessionsTask,
+            // highlight-start
+            // Here's an example of overriding the
+            // default params using the `configure()` method.
+            generateSitemapTask.configure({
+                params: {
+                    shopBaseUrl: 'https://www.shoes.com'
+                }
+            }),
+            // highlight-end
+        ],
+    },
+    plugins: [
+        // highlight-next-line
+        SitemapPlugin,
+        DefaultSchedulerPlugin.init()
+    ],
+};
+```
 
-Each hosting provider has its own way of scheduling tasks. A common way is to use a cron job. 
-For example, to run the above script every night at midnight, you could add the following line to your crontab:
+### Adding in plugin configuration function
+
+An alternative is that a plugin can automatically add the task to the config using the
+plugin's [configuration function](/reference/typescript-api/plugin/vendure-plugin-metadata#configuration), which allows plugins to alter the Vendure config.
+
+This allows a plugin to encapsulate any scheduled tasks so that the plugin consumer only needs to add the plugin, and not worry about
+separately adding the task to the tasks array.
+
+```ts title="src/plugins/sitemap/sitemap.plugin.ts"
+import { VendurePlugin, PluginCommonModule, Type, ScheduledTask, VendureConfig } from '@vendure/core';
+
+import { PLUGIN_OPTIONS } from './constants';
+import { SitemapPluginOptions } from './types';
+import { SitemapService } from './services/sitemap.service';
+import { generateSitemapTask } from './config/generate-sitemap-task';
+
+@VendurePlugin({
+    imports: [PluginCommonModule],
+    providers: [SitemapService],
+    configuration: (config: VendureConfig) => {
+        // highlight-start
+        // Add the task to the schedulerOptions.tasks array
+        config.schedulerOptions.tasks.push(
+            generateSitemapTask.configure({
+                params: {
+                    shopBaseUrl: SitemapPlugin.options.shopBaseUrl,
+                }
+            })
+        );
+        // highlight-end
+        return config;
+    },
+})
+export class SitemapPlugin {
+    static options: SitemapPluginOptions;
+
+    static init(options?: SitemapPluginOptions) {
+        this.options = {
+            shopBaseUrl: '',
+            ...(options ?? {}),
+        }
+    }
+}
+```
 
-```bash
-0 0 * * * node /path/to/scheduled-tasks.js
+This plugin can now be consumed like this:
+
+```ts title="vendure-config.ts"
+import { DefaultSchedulerPlugin, VendureConfig } from '@vendure/core';
+
+// highlight-next-line
+import { SitemapPlugin } from './plugins/sitemap';
+
+export const config: VendureConfig = {
+    // ...
+    plugins: [
+        // highlight-start
+        SitemapPlugin.init({
+            shopBaseUrl: 'https://www.shoes.com'
+        }),
+        // highlight-end
+        DefaultSchedulerPlugin.init()
+    ],
+};
 ```
 
-This will run the script `/path/to/scheduled-tasks.js` every night at midnight.
+## How scheduled tasks work
 
-### Long-running tasks
+The key problems solved by Vendure's task scheduler are:
 
-What if the scheduled task does a significant amount of work that would take many minutes to complete? In this case
-you should consider using the [job queue](/guides/developer-guide/worker-job-queue/#using-job-queues-in-a-plugin) to
-execute the work on the worker.
+- Ensuring that a task is only run a single time per scheduled execution, even when you have multiple instances of servers and workers running.
+- Keeping scheduled task work away from the server instances, so that it does not affect API responsiveness.
 
-Taking the above example, let's now imagine that the `SitemapService` exposes a `triggerGenerate()` method which
-adds a new job to the job queue. The job queue will then execute the task in the background, allowing the scheduled
-task to complete quickly.
+The first problem is handled by the [SchedulerStrategy](/reference/typescript-api/scheduled-tasks/scheduler-strategy), which implements a locking
+mechanism to ensure that the task is executed only once.
 
-```ts title="scheduled-tasks.ts"
-import { bootstrapWorker, Logger, RequestContextService } from '@vendure/core';
-import { SitemapService } from './plugins/sitemap';
+The second problem is handled by having tasks only executed on worker processes.
 
-import { config } from './vendure-config';
+## Scheduled tasks vs job queue
 
-if (require.main === module) {
-    generateSitemap()
-        .then(() => process.exit(0))
-        .catch(err => {
-            Logger.error(err);
-            process.exit(1);
-        });
-}
+There is some overlap between the use of a scheduled task and a [job queue job](/guides/developer-guide/worker-job-queue/). They both perform some
+task on the worker, independent of requests coming in to the server.
 
-async function generateSitemap() {
-    const { app } = await bootstrapWorker(config);
-    const sitemapService = app.get(SitemapService);
-    const ctx = await app.get(RequestContextService).create({
-        apiType: 'admin',
-    });
-    
-    await sitemapService.triggerGenerate(ctx);
+The first difference is that jobs must be triggered explicitly, whereas scheduled tasks are triggered automatically according to the schedule.
 
-    Logger.info(`Sitemap generation triggered`);
-}
-```
+Secondly, jobs are put in a _queue_ and executed once any prior pending jobs have been processed. On the other hand, scheduled tasks are executed
+as soon as the schedule dictates.
+
+It is possible to combine the two: namely, you can define a scheduled task which adds a job to the job queue. This is, for instance, how the
+built-in [cleanSessionsTask](/reference/typescript-api/scheduled-tasks/clean-sessions-task) works. This pattern is something you should
+consider if the scheduled task may take a significant amount of time or resources and you want to let the job queue manage that.
+
+It also has the advantage of giving you a record of results for that work that has been put on the job queue, whereas scheduled tasks
+only record that result of the last execution.
 
-## Using @nestjs/schedule
 
-NestJS provides a [dedicated package for scheduling tasks](https://docs.nestjs.com/techniques/task-scheduling), called `@nestjs/schedule`. 
+## A note on @nestjs/schedule
+
+NestJS provides a [dedicated package for scheduling tasks](https://docs.nestjs.com/techniques/task-scheduling), called `@nestjs/schedule`.
 
 You can also use this approach to schedule tasks, but you need to aware of a very important caveat:
 
 :::warning
 When using `@nestjs/schedule`, any method decorated with the `@Cron()` decorator will run
-on _all_ instances of the application. This means it will run on the server _and_ on the 
+on _all_ instances of the application. This means it will run on the server _and_ on the
 worker. If you are running multiple instances, then it will run on all instances.
+
+This is the specific issue solved by the built-in ScheduledTask system described above.
+Therefore it is not recommended to use the `@nestjs/schedule` package under normal
+circumstances.
 :::
 
-You can, for instance, inject the ProcessContext into the service and check if the current instance is the worker or the server.
+You can, for instance, inject the [ProcessContext](/reference/typescript-api/common/process-context) into the service and check if the current instance is the worker or the server.
 
 ```ts
 import { Injectable } from '@nestjs/common';
 import { Cron } from '@nestjs/schedule';
 
-
 @Injectable()
 export class SitemapService {
     constructor(private processContext: ProcessContext) {}
 
     @Cron('0 0 * * *')
     async generateSitemap() {
+        // highlight-start
         if (this.processContext.isWorker) {
             // Only run on the worker
             await this.triggerGenerate();
         }
+        // highlight-end
     }
 }
 ```

+ 12 - 0
docs/docs/reference/graphql-api/admin/input-types.md

@@ -4321,6 +4321,18 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">channelIds: [<a href="/reference/graphql-api/admin/object-types#id">ID</a>!]</div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## UpdateScheduledTaskInput
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">input <span class="graphql-code-identifier">UpdateScheduledTaskInput</span> &#123;</div>
+<div class="graphql-code-line ">id: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">enabled: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a></div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

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

@@ -1838,6 +1838,15 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">updateRole(input: <a href="/reference/graphql-api/admin/input-types#updateroleinput">UpdateRoleInput</a>!): <a href="/reference/graphql-api/admin/object-types#role">Role</a>!</div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## updateScheduledTask
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
+<div class="graphql-code-line ">updateScheduledTask(input: <a href="/reference/graphql-api/admin/input-types#updatescheduledtaskinput">UpdateScheduledTaskInput</a>!): <a href="/reference/graphql-api/admin/object-types#scheduledtask">ScheduledTask</a>!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

+ 26 - 0
docs/docs/reference/graphql-api/admin/object-types.md

@@ -3519,6 +3519,32 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">customFields: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## ScheduledTask
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">ScheduledTask</span> &#123;</div>
+<div class="graphql-code-line ">id: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">description: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">schedule: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">scheduleDescription: <a href="/reference/graphql-api/admin/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">lastExecutedAt: <a href="/reference/graphql-api/admin/object-types#datetime">DateTime</a></div>
+
+<div class="graphql-code-line ">nextExecutionAt: <a href="/reference/graphql-api/admin/object-types#datetime">DateTime</a></div>
+
+<div class="graphql-code-line ">isRunning: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+<div class="graphql-code-line ">lastResult: <a href="/reference/graphql-api/admin/object-types#json">JSON</a></div>
+
+<div class="graphql-code-line ">enabled: <a href="/reference/graphql-api/admin/object-types#boolean">Boolean</a>!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

+ 9 - 0
docs/docs/reference/graphql-api/admin/queries.md

@@ -517,6 +517,15 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">roles(options: <a href="/reference/graphql-api/admin/input-types#rolelistoptions">RoleListOptions</a>): <a href="/reference/graphql-api/admin/object-types#rolelist">RoleList</a>!</div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## scheduledTasks
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Query</span> &#123;</div>
+<div class="graphql-code-line ">scheduledTasks: [<a href="/reference/graphql-api/admin/object-types#scheduledtask">ScheduledTask</a>!]!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

+ 36 - 0
docs/docs/reference/graphql-api/shop/object-types.md

@@ -2432,6 +2432,42 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">totalItems: <a href="/reference/graphql-api/shop/object-types#int">Int</a>!</div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## PublicPaymentMethod
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">PublicPaymentMethod</span> &#123;</div>
+<div class="graphql-code-line ">id: <a href="/reference/graphql-api/shop/object-types#id">ID</a>!</div>
+
+<div class="graphql-code-line ">code: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">description: <a href="/reference/graphql-api/shop/object-types#string">String</a></div>
+
+<div class="graphql-code-line ">translations: [<a href="/reference/graphql-api/shop/object-types#paymentmethodtranslation">PaymentMethodTranslation</a>!]!</div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## PublicShippingMethod
+
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">PublicShippingMethod</span> &#123;</div>
+<div class="graphql-code-line ">id: <a href="/reference/graphql-api/shop/object-types#id">ID</a>!</div>
+
+<div class="graphql-code-line ">code: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">name: <a href="/reference/graphql-api/shop/object-types#string">String</a>!</div>
+
+<div class="graphql-code-line ">description: <a href="/reference/graphql-api/shop/object-types#string">String</a></div>
+
+<div class="graphql-code-line ">translations: [<a href="/reference/graphql-api/shop/object-types#shippingmethodtranslation">ShippingMethodTranslation</a>!]!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

+ 24 - 0
docs/docs/reference/graphql-api/shop/queries.md

@@ -47,6 +47,30 @@ import MemberDescription from '@site/src/components/MemberDescription';
 <div class="graphql-code-line ">activeOrder: <a href="/reference/graphql-api/shop/object-types#order">Order</a></div>
 
 
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## activePaymentMethods
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Get active payment methods</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Query</span> &#123;</div>
+<div class="graphql-code-line ">activePaymentMethods: [<a href="/reference/graphql-api/shop/object-types#publicpaymentmethod">PublicPaymentMethod</a>]!</div>
+
+
+<div class="graphql-code-line top-level">&#125;</div>
+</div>
+
+## activeShippingMethods
+<div class="graphql-code-block">
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level comment">Get active shipping methods</div>
+<div class="graphql-code-line top-level comment">"""</div>
+<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Query</span> &#123;</div>
+<div class="graphql-code-line ">activeShippingMethods: [<a href="/reference/graphql-api/shop/object-types#publicshippingmethod">PublicShippingMethod</a>]!</div>
+
+
 <div class="graphql-code-line top-level">&#125;</div>
 </div>
 

+ 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="648" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="650" packageName="@vendure/core" />
 
 The AssetOptions define how assets (images and other files) are named and stored, and how preview images are generated.
 

+ 1 - 1
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="333" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="335" packageName="@vendure/core" />
 
 The AuthOptions define how authentication and authorization is managed.
 

+ 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="228" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="230" 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

+ 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="824" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="826" packageName="@vendure/core" />
 
 These credentials will be used to create the Superadmin user & administrator
 when Vendure first bootstraps.

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

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

+ 1 - 1
docs/docs/reference/typescript-api/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="71" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="73" 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/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="974" packageName="@vendure/core" since="1.3.0" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="999" packageName="@vendure/core" since="1.3.0" />
 
 Options relating to the internal handling of entities.
 

+ 7 - 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="1230" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1262" 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.
@@ -26,6 +26,7 @@ interface RuntimeVendureConfig extends Required<VendureConfig> {
     entityOptions: Required<Omit<EntityOptions, 'entityIdStrategy'>> & EntityOptions;
     importExportOptions: Required<ImportExportOptions>;
     jobQueueOptions: Required<JobQueueOptions>;
+    schedulerOptions: Required<SchedulerOptions>;
     orderOptions: Required<OrderOptions>;
     promotionOptions: Required<PromotionOptions>;
     shippingOptions: Required<ShippingOptions>;
@@ -79,6 +80,11 @@ interface RuntimeVendureConfig extends Required<VendureConfig> {
 <MemberInfo kind="property" type={`Required&#60;<a href='/reference/typescript-api/job-queue/job-queue-options#jobqueueoptions'>JobQueueOptions</a>&#62;`}   />
 
 
+### schedulerOptions
+
+<MemberInfo kind="property" type={`Required&#60;<a href='/reference/typescript-api/scheduled-tasks/scheduler-options#scheduleroptions'>SchedulerOptions</a>&#62;`}   />
+
+
 ### orderOptions
 
 <MemberInfo kind="property" type={`Required&#60;<a href='/reference/typescript-api/orders/order-options#orderoptions'>OrderOptions</a>&#62;`}   />

+ 1 - 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="1063" packageName="@vendure/core" since="1.6.0" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1088" packageName="@vendure/core" since="1.6.0" />
 
 Options relating to system functions.
 

+ 7 - 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="1100" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1125" packageName="@vendure/core" />
 
 All possible configuration options are defined by the
 [`VendureConfig`](https://github.com/vendure-ecommerce/vendure/blob/master/packages/core/src/config/vendure-config.ts) interface.
@@ -37,6 +37,7 @@ interface VendureConfig {
     logger?: VendureLogger;
     taxOptions?: TaxOptions;
     jobQueueOptions?: JobQueueOptions;
+    schedulerOptions?: SchedulerOptions;
     systemOptions?: SystemOptions;
 }
 ```
@@ -147,6 +148,11 @@ Configures how taxes are calculated on products.
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/job-queue/job-queue-options#jobqueueoptions'>JobQueueOptions</a>`}   />
 
 Configures how the job queue is persisted and processed.
+### schedulerOptions
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/scheduled-tasks/scheduler-options#scheduleroptions'>SchedulerOptions</a>`}  since="3.3.0"  />
+
+Configures the scheduler mechanism and tasks.
 ### systemOptions
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/configuration/system-options#systemoptions'>SystemOptions</a>`}  since="1.6.0"  />

+ 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="909" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="911" packageName="@vendure/core" />
 
 Options related to importing & exporting data.
 

+ 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="933" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="935" packageName="@vendure/core" />
 
 Options related to the built-in job queue.
 

+ 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="494" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="496" 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="846" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="848" 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="695" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="697" packageName="@vendure/core" />
 
 Options related to products and collections.
 

+ 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="757" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="759" packageName="@vendure/core" />
 
 
 

+ 44 - 0
docs/docs/reference/typescript-api/scheduled-tasks/clean-sessions-task.md

@@ -0,0 +1,44 @@
+---
+title: "CleanSessionsTask"
+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';
+
+
+## cleanSessionsTask
+
+<GenerationInfo sourceFile="packages/core/src/scheduler/tasks/clean-sessions-task.ts" sourceLine="37" packageName="@vendure/core" since="3.3.0" />
+
+A scheduled task that cleans expired & inactive sessions from the database.
+
+*Example*
+
+```ts
+import { cleanSessionsTask, VendureConfig } from '@vendure/core';
+
+export const config: VendureConfig = {
+  // ...
+  schedulerOptions: {
+    tasks: [
+      // Use the task as is
+      cleanSessionsTask,
+      // or configure the task
+      cleanSessionsTask.configure({
+        // Run the task every day at 3:00am
+        // The default schedule is every day at 00:00am
+        schedule: cron => cron.everyDayAt(3, 0),
+        params: {
+          // How many sessions to process in each batch
+          // Default: 10_000
+          batchSize: 5_000,
+        },
+      }),
+    ],
+  },
+};
+```
+

+ 83 - 0
docs/docs/reference/typescript-api/scheduled-tasks/default-scheduler-plugin.md

@@ -0,0 +1,83 @@
+---
+title: "DefaultSchedulerPlugin"
+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';
+
+
+## DefaultSchedulerPlugin
+
+<GenerationInfo sourceFile="packages/core/src/plugin/default-scheduler-plugin/default-scheduler.plugin.ts" sourceLine="35" packageName="@vendure/core" since="3.3.0" />
+
+This plugin configures a default scheduling strategy that executes scheduled
+tasks using the database to ensure that each task is executed exactly once
+at the scheduled time, even if there are multiple instances of the worker
+running.
+
+*Example*
+
+```ts
+import { DefaultSchedulerPlugin, VendureConfig } from '@vendure/core';
+
+export const config: VendureConfig = {
+  plugins: [
+    DefaultSchedulerPlugin.init({
+      // The default is 60s, but you can override it here
+      defaultTimeout: '10s',
+    }),
+  ],
+};
+```
+
+```ts title="Signature"
+class DefaultSchedulerPlugin {
+    static options: DefaultSchedulerPluginOptions = {
+        defaultTimeout: DEFAULT_TIMEOUT,
+    };
+    init(config: DefaultSchedulerPluginOptions) => ;
+}
+```
+
+<div className="members-wrapper">
+
+### options
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/scheduled-tasks/default-scheduler-plugin#defaultschedulerpluginoptions'>DefaultSchedulerPluginOptions</a>`}   />
+
+
+### init
+
+<MemberInfo kind="method" type={`(config: <a href='/reference/typescript-api/scheduled-tasks/default-scheduler-plugin#defaultschedulerpluginoptions'>DefaultSchedulerPluginOptions</a>) => `}   />
+
+
+
+
+</div>
+
+
+## DefaultSchedulerPluginOptions
+
+<GenerationInfo sourceFile="packages/core/src/plugin/default-scheduler-plugin/types.ts" sourceLine="9" packageName="@vendure/core" since="3.3.0" />
+
+The options for the <a href='/reference/typescript-api/scheduled-tasks/default-scheduler-plugin#defaultschedulerplugin'>DefaultSchedulerPlugin</a>.
+
+```ts title="Signature"
+interface DefaultSchedulerPluginOptions {
+    defaultTimeout?: string | number;
+}
+```
+
+<div className="members-wrapper">
+
+### defaultTimeout
+
+<MemberInfo kind="property" type={`string | number`} default={`60_000ms`}   />
+
+The default timeout for scheduled tasks.
+
+
+</div>

+ 68 - 0
docs/docs/reference/typescript-api/scheduled-tasks/default-scheduler-strategy.md

@@ -0,0 +1,68 @@
+---
+title: "DefaultSchedulerStrategy"
+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';
+
+
+## DefaultSchedulerStrategy
+
+<GenerationInfo sourceFile="packages/core/src/plugin/default-scheduler-plugin/default-scheduler-strategy.ts" sourceLine="25" packageName="@vendure/core" since="3.3.0" />
+
+The default <a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#schedulerstrategy'>SchedulerStrategy</a> implementation that uses the database to
+execute scheduled tasks. This strategy is configured when you use the
+<a href='/reference/typescript-api/scheduled-tasks/default-scheduler-plugin#defaultschedulerplugin'>DefaultSchedulerPlugin</a>.
+
+```ts title="Signature"
+class DefaultSchedulerStrategy implements SchedulerStrategy {
+    init(injector: Injector) => ;
+    destroy() => ;
+    executeTask(task: ScheduledTask) => ;
+    getTasks() => Promise<TaskReport[]>;
+    getTask(id: string) => Promise<TaskReport | undefined>;
+    updateTask(input: UpdateScheduledTaskInput) => Promise<TaskReport>;
+}
+```
+* Implements: <code><a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#schedulerstrategy'>SchedulerStrategy</a></code>
+
+
+
+<div className="members-wrapper">
+
+### init
+
+<MemberInfo kind="method" type={`(injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>) => `}   />
+
+
+### destroy
+
+<MemberInfo kind="method" type={`() => `}   />
+
+
+### executeTask
+
+<MemberInfo kind="method" type={`(task: <a href='/reference/typescript-api/scheduled-tasks/scheduled-task#scheduledtask'>ScheduledTask</a>) => `}   />
+
+
+### getTasks
+
+<MemberInfo kind="method" type={`() => Promise&#60;<a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#taskreport'>TaskReport</a>[]&#62;`}   />
+
+
+### getTask
+
+<MemberInfo kind="method" type={`(id: string) => Promise&#60;<a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#taskreport'>TaskReport</a> | undefined&#62;`}   />
+
+
+### updateTask
+
+<MemberInfo kind="method" type={`(input: UpdateScheduledTaskInput) => Promise&#60;<a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#taskreport'>TaskReport</a>&#62;`}   />
+
+
+
+
+</div>

+ 14 - 0
docs/docs/reference/typescript-api/scheduled-tasks/index.md

@@ -0,0 +1,14 @@
+---
+title: "Scheduled Tasks"
+isDefaultIndex: true
+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';
+
+
+import DocCardList from '@theme/DocCardList';
+
+<DocCardList />

+ 164 - 0
docs/docs/reference/typescript-api/scheduled-tasks/scheduled-task.md

@@ -0,0 +1,164 @@
+---
+title: "ScheduledTask"
+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';
+
+
+## ScheduledTask
+
+<GenerationInfo sourceFile="packages/core/src/scheduler/scheduled-task.ts" sourceLine="90" packageName="@vendure/core" since="3.3.0" />
+
+Use this class to define a scheduled task that will be executed at a given cron schedule.
+
+*Example*
+
+```ts
+import { ScheduledTask } from '@vendure/core';
+
+const task = new ScheduledTask({
+    id: 'test-job',
+    schedule: cron => cron.every(2).minutes(),
+    execute: async (injector, params) => {
+        // some logic here
+    },
+});
+```
+
+```ts title="Signature"
+class ScheduledTask<C extends Record<string, any> = Record<string, any>> {
+    constructor(config: ScheduledTaskConfig<C>)
+    id: void
+    options: void
+    execute(injector: Injector) => ;
+    configure(additionalConfig: Partial<Pick<ScheduledTaskConfig<C>, 'schedule' | 'timeout' | 'params'>>) => ;
+}
+```
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(config: <a href='/reference/typescript-api/scheduled-tasks/scheduled-task#scheduledtaskconfig'>ScheduledTaskConfig</a>&#60;C&#62;) => ScheduledTask`}   />
+
+
+### id
+
+<MemberInfo kind="property" type={``}   />
+
+
+### options
+
+<MemberInfo kind="property" type={``}   />
+
+
+### execute
+
+<MemberInfo kind="method" type={`(injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>) => `}   />
+
+
+### configure
+
+<MemberInfo kind="method" type={`(additionalConfig: Partial&#60;Pick&#60;<a href='/reference/typescript-api/scheduled-tasks/scheduled-task#scheduledtaskconfig'>ScheduledTaskConfig</a>&#60;C&#62;, 'schedule' | 'timeout' | 'params'&#62;&#62;) => `}   />
+
+This method allows you to further configure existing scheduled tasks. For example, you may
+wish to change the schedule or timeout of a task, without having to define a new task.
+
+*Example*
+
+```ts
+import { ScheduledTask } from '@vendure/core';
+
+const task = new ScheduledTask({
+    id: 'test-job',
+    schedule: cron => cron.every(2).minutes(),
+    execute: async (injector, params) => {
+        // some logic here
+    },
+});
+
+// later, you can configure the task
+task.configure({ schedule: cron => cron.every(5).minutes() });
+```
+
+
+</div>
+
+
+## ScheduledTaskConfig
+
+<GenerationInfo sourceFile="packages/core/src/scheduler/scheduled-task.ts" sourceLine="12" packageName="@vendure/core" since="3.3.0" />
+
+The configuration for a scheduled task.
+
+```ts title="Signature"
+interface ScheduledTaskConfig<C extends Record<string, any> = Record<string, any>> {
+    id: string;
+    description?: string;
+    params?: C;
+    schedule: string | ((cronTime: typeof CronTime) => string);
+    timeout?: number | string;
+    preventOverlap?: boolean;
+    execute(injector: Injector, config: C): Promise<any>;
+}
+```
+
+<div className="members-wrapper">
+
+### id
+
+<MemberInfo kind="property" type={`string`}   />
+
+The unique identifier for the scheduled task.
+### description
+
+<MemberInfo kind="property" type={`string`}   />
+
+The description for the scheduled task.
+### params
+
+<MemberInfo kind="property" type={`C`}   />
+
+Optional parameters that will be passed to the `execute` function.
+### schedule
+
+<MemberInfo kind="property" type={`string | ((cronTime: typeof CronTime) =&#62; string)`}   />
+
+The cron schedule for the scheduled task. This can be a standard cron expression or
+a function that returns a [cron-time-generator](https://www.npmjs.com/package/cron-time-generator)
+expression.
+
+*Example*
+
+```ts
+// Standard cron expression
+{ schedule: '0 0-23/5 * * *', } // every 5 hours
+{ schedule: '0 22 * * *', } // every day at 10:00 PM
+
+// Cron-time-generator expression
+{ schedule: cronTime => cronTime.every(2).minutes(), }
+{ schedule: cronTime => cronTime.every(5).hours(), }
+```
+### timeout
+
+<MemberInfo kind="property" type={`number | string`} default={`60_000ms`}   />
+
+The timeout for the scheduled task. If the task takes longer than the timeout, the task
+will be considered to have failed with a timeout error.
+### preventOverlap
+
+<MemberInfo kind="property" type={`boolean`} default={`true`}   />
+
+Whether the scheduled task should be prevented from running if it is already running.
+### execute
+
+<MemberInfo kind="method" type={`(injector: <a href='/reference/typescript-api/common/injector#injector'>Injector</a>, config: C) => Promise&#60;any&#62;`}   />
+
+The function that will be executed when the scheduled task is run.
+
+
+</div>

+ 41 - 0
docs/docs/reference/typescript-api/scheduled-tasks/scheduler-options.md

@@ -0,0 +1,41 @@
+---
+title: "SchedulerOptions"
+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';
+
+
+## SchedulerOptions
+
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="974" packageName="@vendure/core" since="3.3.0" />
+
+Options related to scheduled tasks..
+
+```ts title="Signature"
+interface SchedulerOptions {
+    schedulerStrategy?: SchedulerStrategy;
+    tasks?: ScheduledTask[];
+}
+```
+
+<div className="members-wrapper">
+
+### schedulerStrategy
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#schedulerstrategy'>SchedulerStrategy</a>`}   />
+
+The strategy used to execute scheduled tasks. If you are using the
+<a href='/reference/typescript-api/scheduled-tasks/default-scheduler-plugin#defaultschedulerplugin'>DefaultSchedulerPlugin</a> (which is recommended) then this will be set to the
+<a href='/reference/typescript-api/scheduled-tasks/default-scheduler-strategy#defaultschedulerstrategy'>DefaultSchedulerStrategy</a>.
+### tasks
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/scheduled-tasks/scheduled-task#scheduledtask'>ScheduledTask</a>[]`}   />
+
+The tasks to be executed.
+
+
+</div>

+ 54 - 0
docs/docs/reference/typescript-api/scheduled-tasks/scheduler-service.md

@@ -0,0 +1,54 @@
+---
+title: "SchedulerService"
+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';
+
+
+## SchedulerService
+
+<GenerationInfo sourceFile="packages/core/src/scheduler/scheduler.service.ts" sourceLine="33" packageName="@vendure/core" since="3.3.0" />
+
+The service that is responsible for setting up and querying the scheduled tasks.
+
+```ts title="Signature"
+class SchedulerService implements OnApplicationBootstrap {
+    constructor(configService: ConfigService)
+    onApplicationBootstrap() => ;
+    getTaskList() => Promise<TaskInfo[]>;
+    updateTask(input: UpdateScheduledTaskInput) => Promise<TaskInfo>;
+}
+```
+* Implements: <code>OnApplicationBootstrap</code>
+
+
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(configService: ConfigService) => SchedulerService`}   />
+
+
+### onApplicationBootstrap
+
+<MemberInfo kind="method" type={`() => `}   />
+
+
+### getTaskList
+
+<MemberInfo kind="method" type={`() => Promise&#60;TaskInfo[]&#62;`}   />
+
+Returns a list of all the scheduled tasks and their current status.
+### updateTask
+
+<MemberInfo kind="method" type={`(input: UpdateScheduledTaskInput) => Promise&#60;TaskInfo&#62;`}   />
+
+
+
+
+</div>

+ 119 - 0
docs/docs/reference/typescript-api/scheduled-tasks/scheduler-strategy.md

@@ -0,0 +1,119 @@
+---
+title: "SchedulerStrategy"
+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';
+
+
+## SchedulerStrategy
+
+<GenerationInfo sourceFile="packages/core/src/scheduler/scheduler-strategy.ts" sourceLine="42" packageName="@vendure/core" since="3.3.0" />
+
+This strategy is used to define the mechanism by which scheduled tasks are executed
+and how they are reported on. The main purpose of this strategy is to ensure
+that a given task is executed exactly once at the scheduled time, even if there
+are multiple instances of the worker running.
+
+To do this, the strategy must use some form of shared storage and a method of
+locking so that only a single worker is allowed to execute the task.
+
+By default, the <a href='/reference/typescript-api/scheduled-tasks/default-scheduler-strategy#defaultschedulerstrategy'>DefaultSchedulerStrategy</a> will use the database to enable
+this functionality.
+
+```ts title="Signature"
+interface SchedulerStrategy extends InjectableStrategy {
+    executeTask(task: ScheduledTask): (job: Cron) => Promise<any> | any;
+    getTasks(): Promise<TaskReport[]>;
+    getTask(id: string): Promise<TaskReport | undefined>;
+    updateTask(input: UpdateScheduledTaskInput): Promise<TaskReport>;
+}
+```
+* Extends: <code><a href='/reference/typescript-api/common/injectable-strategy#injectablestrategy'>InjectableStrategy</a></code>
+
+
+
+<div className="members-wrapper">
+
+### executeTask
+
+<MemberInfo kind="method" type={`(task: <a href='/reference/typescript-api/scheduled-tasks/scheduled-task#scheduledtask'>ScheduledTask</a>) => (job: Cron) =&#62; Promise&#60;any&#62; | any`}   />
+
+Execute a scheduled task. This method must also take care of
+ensuring that the task is executed exactly once at the scheduled time,
+even if there are multiple instances of the worker running.
+
+For instance, in the <a href='/reference/typescript-api/scheduled-tasks/default-scheduler-strategy#defaultschedulerstrategy'>DefaultSchedulerStrategy</a> we make use of a
+dedicated database table and a locking mechansim. If you implement a custom
+SchedulerStrategy, you must use some other form of shared locking mechanism
+that could make use of something like Redis etc. to ensure that the task
+is executed exactly once at the scheduled time.
+### getTasks
+
+<MemberInfo kind="method" type={`() => Promise&#60;<a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#taskreport'>TaskReport</a>[]&#62;`}   />
+
+Get all scheduled tasks.
+### getTask
+
+<MemberInfo kind="method" type={`(id: string) => Promise&#60;<a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#taskreport'>TaskReport</a> | undefined&#62;`}   />
+
+Get a single scheduled task by its id.
+### updateTask
+
+<MemberInfo kind="method" type={`(input: UpdateScheduledTaskInput) => Promise&#60;<a href='/reference/typescript-api/scheduled-tasks/scheduler-strategy#taskreport'>TaskReport</a>&#62;`}   />
+
+Update a scheduled task.
+
+
+</div>
+
+
+## TaskReport
+
+<GenerationInfo sourceFile="packages/core/src/scheduler/scheduler-strategy.ts" sourceLine="16" packageName="@vendure/core" since="3.3.0" />
+
+A report on the status of a scheduled task.
+
+```ts title="Signature"
+interface TaskReport {
+    id: string;
+    lastExecutedAt: Date | null;
+    isRunning: boolean;
+    lastResult: any;
+    enabled: boolean;
+}
+```
+
+<div className="members-wrapper">
+
+### id
+
+<MemberInfo kind="property" type={`string`}   />
+
+
+### lastExecutedAt
+
+<MemberInfo kind="property" type={`Date | null`}   />
+
+
+### isRunning
+
+<MemberInfo kind="property" type={`boolean`}   />
+
+
+### lastResult
+
+<MemberInfo kind="property" type={`any`}   />
+
+
+### enabled
+
+<MemberInfo kind="property" type={`boolean`}   />
+
+
+
+
+</div>

+ 23 - 5
docs/docs/reference/typescript-api/services/session-service.md

@@ -11,13 +11,14 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## SessionService
 
-<GenerationInfo sourceFile="packages/core/src/service/services/session.service.ts" sourceLine="28" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/service/services/session.service.ts" sourceLine="31" packageName="@vendure/core" />
 
 Contains methods relating to <a href='/reference/typescript-api/entities/session#session'>Session</a> entities.
 
 ```ts title="Signature"
-class SessionService implements EntitySubscriberInterface {
-    constructor(connection: TransactionalConnection, configService: ConfigService, orderService: OrderService)
+class SessionService implements EntitySubscriberInterface, OnApplicationBootstrap {
+    constructor(connection: TransactionalConnection, configService: ConfigService, orderService: OrderService, jobQueueService: JobQueueService, requestContextService: RequestContextService)
+    onApplicationBootstrap() => ;
     createNewAuthenticatedSession(ctx: RequestContext, user: User, authenticationStrategyName: string) => Promise<AuthenticatedSession>;
     createAnonymousSession() => Promise<CachedSession>;
     getSessionFromToken(sessionToken: string) => Promise<CachedSession | undefined>;
@@ -27,9 +28,11 @@ class SessionService implements EntitySubscriberInterface {
     setActiveChannel(serializedSession: CachedSession, channel: Channel) => Promise<CachedSession>;
     deleteSessionsByUser(ctx: RequestContext, user: User) => Promise<void>;
     deleteSessionsByActiveOrderId(ctx: RequestContext, activeOrderId: ID) => Promise<void>;
+    triggerCleanSessionsJob(batchSize: number) => ;
+    cleanExpiredSessions(ctx: RequestContext, batchSize: number) => ;
 }
 ```
-* Implements: <code>EntitySubscriberInterface</code>
+* Implements: <code>EntitySubscriberInterface</code>, <code>OnApplicationBootstrap</code>
 
 
 
@@ -37,7 +40,12 @@ class SessionService implements EntitySubscriberInterface {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, configService: ConfigService, orderService: <a href='/reference/typescript-api/services/order-service#orderservice'>OrderService</a>) => SessionService`}   />
+<MemberInfo kind="method" type={`(connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>, configService: ConfigService, orderService: <a href='/reference/typescript-api/services/order-service#orderservice'>OrderService</a>, jobQueueService: <a href='/reference/typescript-api/job-queue/job-queue-service#jobqueueservice'>JobQueueService</a>, requestContextService: <a href='/reference/typescript-api/request/request-context-service#requestcontextservice'>RequestContextService</a>) => SessionService`}   />
+
+
+### onApplicationBootstrap
+
+<MemberInfo kind="method" type={`() => `}   />
 
 
 ### createNewAuthenticatedSession
@@ -86,6 +94,16 @@ Deletes all existing sessions for the given user.
 <MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, activeOrderId: <a href='/reference/typescript-api/common/id#id'>ID</a>) => Promise&#60;void&#62;`}   />
 
 Deletes all existing sessions with the given activeOrder.
+### triggerCleanSessionsJob
+
+<MemberInfo kind="method" type={`(batchSize: number) => `}   />
+
+Triggers the clean sessions job.
+### cleanExpiredSessions
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, batchSize: number) => `}   />
+
+Cleans expired sessions from the database & the session cache.
 
 
 </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="773" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="775" packageName="@vendure/core" />
 
 
 

+ 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="886" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="888" packageName="@vendure/core" />
 
 
 

+ 4 - 2
packages/core/src/plugin/default-scheduler-plugin/default-scheduler.plugin.ts

@@ -29,6 +29,8 @@ import { DefaultSchedulerPluginOptions } from './types';
  *
  * @since 3.3.0
  * @docsCategory scheduled-tasks
+ * @docsPage DefaultSchedulerPlugin
+ * @docsWeight 0
  */
 @VendurePlugin({
     imports: [PluginCommonModule],
@@ -50,8 +52,8 @@ export class DefaultSchedulerPlugin {
         defaultTimeout: DEFAULT_TIMEOUT,
     };
 
-    static init(config: DefaultSchedulerPluginOptions) {
-        this.options = { ...this.options, ...config };
+    static init(config?: DefaultSchedulerPluginOptions) {
+        this.options = { ...this.options, ...(config || {}) };
         return this;
     }
 }

+ 1 - 0
packages/core/src/plugin/default-scheduler-plugin/types.ts

@@ -4,6 +4,7 @@
  *
  * @since 3.3.0
  * @docsCategory scheduled-tasks
+ * @docsPage DefaultSchedulerPlugin
  */
 export interface DefaultSchedulerPluginOptions {
     /**

+ 18 - 1
packages/core/src/scheduler/scheduled-task.ts

@@ -6,6 +6,8 @@ import { Injector } from '../common/index';
  * The configuration for a scheduled task.
  *
  * @since 3.3.0
+ * @docsCategory scheduled-tasks
+ * @docsPage ScheduledTask
  */
 export interface ScheduledTaskConfig<C extends Record<string, any> = Record<string, any>> {
     /**
@@ -65,10 +67,25 @@ export interface ScheduledTaskConfig<C extends Record<string, any> = Record<stri
 
 /**
  * @description
- * A scheduled task that will be executed at a given cron schedule.
+ * Use this class to define a scheduled task that will be executed at a given cron schedule.
+ *
+ * @example
+ * ```ts
+ * import { ScheduledTask } from '\@vendure/core';
+ *
+ * const task = new ScheduledTask({
+ *     id: 'test-job',
+ *     schedule: cron => cron.every(2).minutes(),
+ *     execute: async (injector, params) => {
+ *         // some logic here
+ *     },
+ * });
+ * ```
  *
  * @since 3.3.0
  * @docsCategory scheduled-tasks
+ * @docsPage ScheduledTask
+ * @docsWeight 0
  */
 export class ScheduledTask<C extends Record<string, any> = Record<string, any>> {
     constructor(private readonly config: ScheduledTaskConfig<C>) {}

+ 34 - 0
packages/core/src/scheduler/scheduler-strategy.ts

@@ -5,6 +5,14 @@ import { InjectableStrategy } from '../common';
 
 import { ScheduledTask } from './scheduled-task';
 
+/**
+ * @description
+ * A report on the status of a scheduled task.
+ *
+ * @since 3.3.0
+ * @docsCategory scheduled-tasks
+ * @docsPage SchedulerStrategy
+ */
 export interface TaskReport {
     id: string;
     lastExecutedAt: Date | null;
@@ -28,10 +36,36 @@ export interface TaskReport {
  *
  * @since 3.3.0
  * @docsCategory scheduled-tasks
+ * @docsPage SchedulerStrategy
+ * @docsWeight 0
  */
 export interface SchedulerStrategy extends InjectableStrategy {
+    /**
+     * @description
+     * Execute a scheduled task. This method must also take care of
+     * ensuring that the task is executed exactly once at the scheduled time,
+     * even if there are multiple instances of the worker running.
+     *
+     * For instance, in the {@link DefaultSchedulerStrategy} we make use of a
+     * dedicated database table and a locking mechansim. If you implement a custom
+     * SchedulerStrategy, you must use some other form of shared locking mechanism
+     * that could make use of something like Redis etc. to ensure that the task
+     * is executed exactly once at the scheduled time.
+     */
     executeTask(task: ScheduledTask): (job: Cron) => Promise<any> | any;
+    /**
+     * @description
+     * Get all scheduled tasks.
+     */
     getTasks(): Promise<TaskReport[]>;
+    /**
+     * @description
+     * Get a single scheduled task by its id.
+     */
     getTask(id: string): Promise<TaskReport | undefined>;
+    /**
+     * @description
+     * Update a scheduled task.
+     */
     updateTask(input: UpdateScheduledTaskInput): Promise<TaskReport>;
 }

+ 26 - 0
packages/core/src/scheduler/tasks/clean-sessions-task.ts

@@ -5,6 +5,32 @@ import { ScheduledTask } from '../scheduled-task';
  * @description
  * A scheduled task that cleans expired & inactive sessions from the database.
  *
+ * @example
+ * ```ts
+ * import { cleanSessionsTask, VendureConfig } from '\@vendure/core';
+ *
+ * export const config: VendureConfig = {
+ *   // ...
+ *   schedulerOptions: {
+ *     tasks: [
+ *       // Use the task as is
+ *       cleanSessionsTask,
+ *       // or configure the task
+ *       cleanSessionsTask.configure({
+ *         // Run the task every day at 3:00am
+ *         // The default schedule is every day at 00:00am
+ *         schedule: cron => cron.everyDayAt(3, 0),
+ *         params: {
+ *           // How many sessions to process in each batch
+ *           // Default: 10_000
+ *           batchSize: 5_000,
+ *         },
+ *       }),
+ *     ],
+ *   },
+ * };
+ * ```
+ *
  * @since 3.3.0
  * @docsCategory scheduled-tasks
  */