From 7d82c0e4ac5a25760df612e670cacb0367b1be32 Mon Sep 17 00:00:00 2001 From: anyprod Date: Tue, 22 Sep 2026 22:55:15 +0300 Subject: [PATCH] docs(pulse): deprecate @tsed/pulse and guide users to @tsed/agenda + Agenda v6 cloes #3376 --- docs/.vitepress/config.mts | 2 +- docs/index.md | 3 - docs/tutorials/agenda.md | 4 + docs/tutorials/pulse.md | 212 +++++++++++++++--- .../complete-pulse-deprecation-docs/design.md | 29 +++ .../proposal.md | 25 +++ .../specs/pulse-deprecation-guidance/spec.md | 25 +++ .../complete-pulse-deprecation-docs/tasks.md | 21 ++ packages/third-parties/agenda/readme.md | 4 + packages/third-parties/pulse/package.json | 2 +- packages/third-parties/pulse/readme.md | 154 +++++++++++-- .../pulse/src/decorators/define.ts | 6 + .../pulse/src/decorators/every.ts | 6 + .../pulse/src/decorators/pulse.ts | 9 +- .../pulse/src/interfaces/interfaces.ts | 8 + .../pulse/src/services/PulseService.ts | 14 +- 16 files changed, 461 insertions(+), 63 deletions(-) create mode 100644 openspec/changes/complete-pulse-deprecation-docs/design.md create mode 100644 openspec/changes/complete-pulse-deprecation-docs/proposal.md create mode 100644 openspec/changes/complete-pulse-deprecation-docs/specs/pulse-deprecation-guidance/spec.md create mode 100644 openspec/changes/complete-pulse-deprecation-docs/tasks.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 78fe54c9bca..74c46dd5f3f 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -328,7 +328,7 @@ const Tutorials = [ link: `/tutorials/agenda` }, { - text: "Pulse", + text: "Pulse (deprecated)", link: `/tutorials/pulse` }, { diff --git a/docs/index.md b/docs/index.md index 4c36a20dd83..96c5c242698 100644 --- a/docs/index.md +++ b/docs/index.md @@ -181,9 +181,6 @@ frameworks: - title: Vike href: /tutorials/vike.html src: /vike.svg - - title: Pulse - href: /tutorials/pulse.html - src: /pulse.png - title: Vault href: /plugins/premium/config-source/vault.html src: /vault.png diff --git a/docs/tutorials/agenda.md b/docs/tutorials/agenda.md index 80c73503d76..d87e23c73a3 100644 --- a/docs/tutorials/agenda.md +++ b/docs/tutorials/agenda.md @@ -95,6 +95,10 @@ Migrate to @tsed/agenda v8.30.0: - `AgendaModule` → `Agenda` from `agenda` ``` +::: tip Migrating from `@tsed/pulse`? +`@tsed/pulse` is deprecated. See the [Pulse migration guide](/tutorials/pulse#migrate-to-tsed-agenda) for step-by-step rewrites from `@tsed/pulse` to `@tsed/agenda`. +::: + ## Configure your server Import `@tsed/agenda` in your Server: diff --git a/docs/tutorials/pulse.md b/docs/tutorials/pulse.md index 1231ebbee28..ad0efce8f10 100644 --- a/docs/tutorials/pulse.md +++ b/docs/tutorials/pulse.md @@ -1,40 +1,65 @@ --- -description: "Use Pulse cron with Express/Koa, TypeScript and Ts.ED." +description: "@tsed/pulse is deprecated. Migrate from Pulse to @tsed/agenda with Agenda v6 in your Express/Koa TypeScript Ts.ED application." head: - - meta - name: description - content: Use Pulse cron with Express/Koa, TypeScript and Ts.ED. Pulse is a maintained for of Agenda, a light-weight job scheduling library for Node.js + content: "@tsed/pulse is deprecated. Migrate from Pulse to @tsed/agenda with Agenda v6 in your Express/Koa TypeScript Ts.ED application. Pulse is a fork of Agenda, a light-weight job scheduling library for Node.js" - - meta - name: keywords - content: ts.ed express typescript agenda node.js javascript decorators pulse pulse-cron pulsecron job-scheduling cron background-jobs + content: ts.ed express typescript agenda node.js javascript decorators pulse pulse-cron pulsecron job-scheduling cron background-jobs deprecated migration --- # Pulse -::: warning +::: danger Deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. This package won't receive any future updates. +**Do not start new projects on `@tsed/pulse`.** -For new projects, use `@tsed/agenda` with Agenda v6. -Existing consumers should migrate to `@tsed/agenda` + `agenda` + `@agendajs/mongo-backend`. +For new projects, use [`@tsed/agenda`](/tutorials/agenda) with Agenda v6. +Existing `@tsed/pulse` users should plan a migration to `@tsed/agenda` + `agenda` + `@agendajs/mongo-backend` +by following the [migration guide](#migrate-to-tsed-agenda) below. -The legacy Pulse examples below are kept only to help existing consumers migrate old code. +The legacy Pulse examples at the end of this page are kept only to help existing consumers maintain or migrate old code. ::: ## Feature -`@pulsecron/pulse` is maintained fork of the Agenda. - -Currently, `@tsed/pulse` allows you to decorate classes with `@Pulse` and -corresponding methods to have them picked up by the @pulsecron/pulse library to be +`@pulsecron/pulse` is a fork of Agenda. `@tsed/pulse` allowed you to decorate classes with `@Pulse` and +corresponding methods to have them picked up by the `@pulsecron/pulse` library to be scheduled automatically (`@Every`) or programmatically (`@Define`) via the PulseService. -For more information about Pulse look at the documentation [here](https://github.com/pulsecron/pulse); +The same features are available in `@tsed/agenda` on top of Agenda v6, which is the recommended and maintained +scheduling integration for Ts.ED. See the [Agenda documentation](/tutorials/agenda). ## Installation -To begin, install the Pulse module for Ts.ED: +Do not install `@tsed/pulse` for new projects. Install the recommended Agenda v6 stack instead: + +::: code-group + +```sh [npm] +npm install --save @tsed/agenda agenda @agendajs/mongo-backend +``` + +```sh [yarn] +yarn add @tsed/agenda agenda @agendajs/mongo-backend +``` + +```sh [pnpm] +pnpm add @tsed/agenda agenda @agendajs/mongo-backend +``` + +```sh [bun] +bun add @tsed/agenda agenda @agendajs/mongo-backend +``` + +::: + +Then follow the [Agenda documentation](/tutorials/agenda) to configure your server. + +:::: details Legacy installation (existing projects that cannot migrate yet) ::: code-group @@ -56,12 +81,50 @@ bun add @tsed/pulse @pulsecron/pulse ::: -## Migrate to Agenda +:::: + +## Migrate to `@tsed/agenda` + +The target stack is `@tsed/agenda` + `agenda` (v6) + `@agendajs/mongo-backend`. + +### 1. Replace dependencies + +| Remove | Add | +| ------------------ | ------------------------- | +| `@tsed/pulse` | `@tsed/agenda` | +| `@pulsecron/pulse` | `agenda` | +| | `@agendajs/mongo-backend` | -Migrate from `@tsed/pulse` to `@tsed/agenda` on top of Agenda v6. +::: code-group + +```sh [npm] +npm uninstall @tsed/pulse @pulsecron/pulse +npm install --save @tsed/agenda agenda @agendajs/mongo-backend +``` + +```sh [yarn] +yarn remove @tsed/pulse @pulsecron/pulse +yarn add @tsed/agenda agenda @agendajs/mongo-backend +``` + +```sh [pnpm] +pnpm remove @tsed/pulse @pulsecron/pulse +pnpm add @tsed/agenda agenda @agendajs/mongo-backend +``` + +```sh [bun] +bun remove @tsed/pulse @pulsecron/pulse +bun add @tsed/agenda agenda @agendajs/mongo-backend +``` + +::: + +### 2. Update the server configuration + +The `pulse` configuration key becomes `agenda`, and the legacy `db` options move into a `MongoBackend` instance. ```diff -import {Configuration} from "@tsed/di"; + import {Configuration} from "@tsed/di"; -import "@tsed/pulse"; +import "@tsed/agenda"; +import {MongoBackend} from "@agendajs/mongo-backend"; @@ -81,28 +144,110 @@ import {Configuration} from "@tsed/di"; export class Server {} ``` +The Ts.ED specific flags (`enabled`, `disableJobProcessing`, `drainJobsBeforeClose`) keep the same names and +behavior in `@tsed/agenda`. + +### 3. Update decorators, injection and lifecycle hooks + +`@tsed/agenda` exposes the same `@Every` and `@Define` decorators. The class decorator is `@JobsController` +and the scheduler instance is the `Agenda` class from `agenda` (there is no `AgendaModule`). + +```diff +-import {Pulse, Every, Define, PulseModule} from "@tsed/pulse"; +-import {Job} from "@pulsecron/pulse"; ++import {JobsController, Every, Define} from "@tsed/agenda"; ++import {Agenda, Job} from "agenda"; + +-@Pulse({namespace: "email"}) ++@JobsController({namespace: "email"}) + export class EmailJobService { + @Inject() +- pulse: PulseModule; ++ agenda: Agenda; + + @Every("60 minutes", {name: "maintenanceJob"}) + async sendAdminStatistics(job: Job) {} + + @Define({name: "sendWelcomeEmail"}) + async sendWelcomeEmail(job: Job) {} + +- async $beforePulseStart() {} +- async $afterPulseStart() {} ++ async $beforeAgendaStart() {} ++ async $afterAgendaStart() {} + } +``` + +Scheduling APIs keep the same shape (`now()`, `schedule()`, `every()`, `create()`), so call sites only need the +injected instance to be renamed: + +```diff + @Service() + export class UsersService { + @Inject() +- private pulse: PulseModule; ++ private agenda: Agenda; + + async create(user: User) { +- await this.pulse.now("email.sendWelcomeEmail", {user}); +- await this.pulse.schedule("in 2 hours", "email.sendFollowUpEmail", {user}); ++ await this.agenda.now("email.sendWelcomeEmail", {user}); ++ await this.agenda.schedule("in 2 hours", "email.sendFollowUpEmail", {user}); + } + } +``` + ### AI migration note -Apply these deterministic rewrites: +Apply these deterministic rewrites when migrating a codebase with a coding agent: ```text -Migrate from @tsed/pulse to @tsed/agenda: +Migrate from @tsed/pulse to @tsed/agenda (Agenda v6): + +Dependencies +- remove `@tsed/pulse` → add `@tsed/agenda` +- remove `@pulsecron/pulse` → add `agenda` + `@agendajs/mongo-backend` +Imports - `import "@tsed/pulse"` → `import "@tsed/agenda"` -- add `import {MongoBackend} from "@agendajs/mongo-backend"` -- `@Pulse(...)` → `@JobsController(...)` -- `PulseModule` → `Agenda` from `agenda` -- `pulse` config key → `agenda` -- `pulse.db.address` → `agenda.backend: new MongoBackend({address: ...})` +- `import {Pulse, JobsController, Every, Define, PulseModule, PulseService} from "@tsed/pulse"` → `import {JobsController, Every, Define} from "@tsed/agenda"` +- `import {Job} from "@pulsecron/pulse"` → `import {Job} from "agenda"` +- add `import {Agenda} from "agenda"` where `PulseModule` / `PulseService` was injected +- add `import {MongoBackend} from "@agendajs/mongo-backend"` in the server configuration file + +Configuration (@Configuration / TsED.Configuration) +- `pulse: {...}` config key → `agenda: {...}` +- `pulse.db.address` → `agenda.backend: new MongoBackend({address})` - `pulse.db.collection` / `pulse.db.options` → `agenda.backend: new MongoBackend({collection, options})` -- `pulse.mongo` / `pulse.repository` → `agenda.backend` -- `pulse.ensureIndex` / `pulse.sort` → move into `new MongoBackend(...)` -- `@pulsecron/pulse` dependency → `agenda` + `@agendajs/mongo-backend` +- `pulse.mongo` → `agenda.backend: new MongoBackend({mongo})` +- `pulse.ensureIndex` / `pulse.sort` → move inside `new MongoBackend({ensureIndex, sort})`, sort directions use `"asc"` / `"desc"` +- `pulse.enabled`, `pulse.disableJobProcessing`, `pulse.drainJobsBeforeClose` → keep the same names under `agenda` +- `PulseSettings` type → `AgendaSettings` -For more details about @tsed/agenda, see [Agenda for Ts.ED](https://tsed.dev/ai/tutorials/agenda.md). +Decorators and injection +- `@Pulse(...)` → `@JobsController(...)` +- `@JobsController(...)` (from `@tsed/pulse`) → `@JobsController(...)` (from `@tsed/agenda`) +- `@Every(...)` / `@Define(...)` → unchanged (import them from `@tsed/agenda`) +- `PulseModule` / `PulseService` injected type → `Agenda` (from `agenda`) +- `pulse.now(...)`, `pulse.schedule(...)`, `pulse.every(...)`, `pulse.create(...)`, `pulse.define(...)` → same methods on the injected `Agenda` +- `pulse.jobs(...)` → `agenda.queryJobs(...)` + +Lifecycle hooks +- `$beforePulseStart()` → `$beforeAgendaStart()` +- `$afterPulseStart()` → `$afterAgendaStart()` ``` -## Configure your server +For more details about `@tsed/agenda`, see [Agenda for Ts.ED](/tutorials/agenda) +(LLM-friendly version: [tsed.dev/tutorials/agenda.md](https://tsed.dev/tutorials/agenda.md)). + +## Legacy usage (existing projects only) + +::: warning +The following sections document the deprecated `@tsed/pulse` API. They are kept for reference only. +Do not use them for new code. +::: + +### Configure your server Import `@tsed/pulse` in your Server: @@ -115,7 +260,7 @@ const mongoConnectionString = "mongodb://127.0.0.1/pulse"; @Configuration({ pulse: { enabled: true, // Enable Pulse jobs for this instance. - // drainJobsBeforeStop: true, // Wait for jobs to finish before stopping the pulse process. + // drainJobsBeforeClose: true, // Wait for jobs to finish before stopping the pulse process. // disableJobProcessing: true, // Prevents jobs from being processed. // pass any options that you would normally pass to new Pulse(), e.g. db: { @@ -126,7 +271,7 @@ const mongoConnectionString = "mongodb://127.0.0.1/pulse"; export class Server {} ``` -## Create a new Service +### Create a new Service Decorate the class with `@Pulse`. The `namespace` option is optional and will prefix the job name with `namespace.` @@ -167,7 +312,7 @@ export class EmailJobService { } ``` -## Define a job processor manually +### Define a job processor manually PulseModule exposes methods to manually define a job processor. It can be useful to define a job processor when you need to fetch data beforehand and dynamically build job name / options. @@ -213,14 +358,13 @@ export class EmailJobService { } ``` -## Inject Pulse +### Inject Pulse Inject the PulseService instance to interact with it directly, e.g. to schedule a job manually. ```typescript -import {Service} from "@tsed/di"; -import {AfterRoutesInit} from "@tsed/platform-params"; +import {Service, Inject} from "@tsed/di"; import {PulseModule} from "@tsed/pulse"; @Service() diff --git a/openspec/changes/complete-pulse-deprecation-docs/design.md b/openspec/changes/complete-pulse-deprecation-docs/design.md new file mode 100644 index 00000000000..68068893317 --- /dev/null +++ b/openspec/changes/complete-pulse-deprecation-docs/design.md @@ -0,0 +1,29 @@ +## Context + +`@tsed/pulse` is superseded by `@tsed/agenda` on Agenda v6. The first deprecation pass only covered part of the docs, so users still receive install commands for Pulse and coding agents lack a complete rewrite table. `AgendaModule` no longer exists in `@tsed/agenda` (consumers inject `Agenda` from `agenda`), so the migration examples suggested in the issue must be adapted to the current API. + +## Goals / Non-Goals + +**Goals:** + +- Make both Pulse docs entry points self-sufficient deprecation + migration guides. +- Give IDEs and npm a deprecation signal without changing runtime behavior. +- Stop promoting Pulse from discovery surfaces (home page, sidebar label). + +**Non-Goals:** + +- Removing the `@tsed/pulse` package or its tests. +- Running `npm deprecate` (a publish-time operation handled by maintainers). +- Changing `@tsed/agenda` behavior. + +## Decisions + +- Keep legacy Pulse examples, but move them under a clearly labelled "Legacy usage (existing projects only)" section after the migration guide, so the reading order is deprecation → replacement → migration → legacy reference. +- Document migration targets against the actual `@tsed/agenda` API: `@JobsController`, `Agenda` from `agenda`, `$beforeAgendaStart` / `$afterAgendaStart`, `queryJobs()`. Do not mention `AgendaModule`, which was removed. +- Use `@deprecated` TSDoc on every exported symbol rather than a runtime warning, to avoid noisy logs for existing consumers. +- Keep the tutorial page reachable from the sidebar (labelled deprecated) so existing users can find the migration guide, but remove the home page card that promotes new adoption. + +## Risks / Trade-offs + +- [Docs drift between README and tutorial] → Both files share the same migration content, adapted only for GitHub alerts vs VitePress containers. +- [Broken anchors] → GitHub slug `#migrate-to-tsedagenda` and VitePress slug `#migrate-to-tsed-agenda` are used respectively. diff --git a/openspec/changes/complete-pulse-deprecation-docs/proposal.md b/openspec/changes/complete-pulse-deprecation-docs/proposal.md new file mode 100644 index 00000000000..5f659b26dd0 --- /dev/null +++ b/openspec/changes/complete-pulse-deprecation-docs/proposal.md @@ -0,0 +1,25 @@ +## Why + +Issue [#3376](https://github.com/tsedio/tsed/issues/3376) asks to deprecate `@tsed/pulse` and redirect users to `@tsed/agenda` + Agenda v6. The archived `migrate-tsed-agenda-to-v6` change added a first banner and a config diff, but both Pulse docs entry points still promote `npm install @tsed/pulse @pulsecron/pulse`, the package README lacks the AI-oriented rewrite section, the tutorial lacks the decorator/injection/lifecycle-hook diffs, the README banner is malformed, and the package itself carries no deprecation signal for IDEs or npm. + +## What Changes + +- Turn `packages/third-parties/pulse/readme.md` and `docs/tutorials/pulse.md` into complete deprecation + migration guides: deprecation banner, Agenda v6 install commands (npm/yarn/pnpm/bun), dependency table, config diff, decorator/injection/lifecycle-hook diffs, and an AI-oriented deterministic rewrite list. Legacy Pulse usage is moved under an explicit "Legacy usage" section. +- Mark the `@tsed/pulse` public API (`JobsController`, `Pulse`, `Every`, `Define`, `PulseSettings`, `PulseService`, `PulseModule`, the `pulse` configuration key) with `@deprecated` TSDoc pointing to `@tsed/agenda`, and prefix the package description with `[DEPRECATED]`. +- Stop promoting Pulse: remove the Pulse card from the docs home page and label the sidebar entry "Pulse (deprecated)". +- Cross-link the Agenda docs (tutorial and README) to the Pulse migration guide. + +## Capabilities + +### New Capabilities + + + +### Modified Capabilities + +- `pulse-deprecation-guidance`: require Agenda v6 install guidance, package-level deprecation signals, and that Pulse is no longer promoted from the docs home page. + +## Impact + +- Affected files: `packages/third-parties/pulse/{readme.md,package.json,src/**}`, `docs/tutorials/pulse.md`, `docs/tutorials/agenda.md`, `packages/third-parties/agenda/readme.md`, `docs/index.md`, `docs/.vitepress/config.mts`. +- No runtime behavior change: only TSDoc comments and package metadata change in `@tsed/pulse`. diff --git a/openspec/changes/complete-pulse-deprecation-docs/specs/pulse-deprecation-guidance/spec.md b/openspec/changes/complete-pulse-deprecation-docs/specs/pulse-deprecation-guidance/spec.md new file mode 100644 index 00000000000..3f78aa12215 --- /dev/null +++ b/openspec/changes/complete-pulse-deprecation-docs/specs/pulse-deprecation-guidance/spec.md @@ -0,0 +1,25 @@ +## MODIFIED Requirements + +### Requirement: Pulse docs deprecation guidance + +Ts.ED Pulse docs MUST direct users toward `@tsed/agenda` + Agenda v6, MUST NOT present `@tsed/pulse` as the primary installation target, and the `@tsed/pulse` package MUST carry a deprecation signal at the package level. + +#### Scenario: Reader opens Pulse package or tutorial docs + +- **WHEN** a consumer reads Pulse documentation +- **THEN** the docs clearly state that `@tsed/pulse` is deprecated and that new projects must not start on it +- **AND** the primary install commands (npm, yarn, pnpm, bun in the tutorial) install `@tsed/agenda`, `agenda` and `@agendajs/mongo-backend` +- **AND** the docs provide migration notes covering dependencies, configuration, decorators, injection and lifecycle hooks with before/after diffs +- **AND** the docs include explicit deterministic rewrites useful for AI-assisted migrations +- **AND** legacy Pulse usage is presented only in a section explicitly marked as legacy + +#### Scenario: Developer imports `@tsed/pulse` in an IDE + +- **WHEN** a developer uses an exported symbol of `@tsed/pulse` or the `pulse` configuration key +- **THEN** the symbol is flagged as deprecated through TSDoc with a pointer to `@tsed/agenda` + +#### Scenario: Reader browses the documentation site + +- **WHEN** a reader opens the docs home page +- **THEN** Pulse is not listed among the promoted integrations +- **AND** the sidebar entry for the Pulse tutorial is labelled as deprecated diff --git a/openspec/changes/complete-pulse-deprecation-docs/tasks.md b/openspec/changes/complete-pulse-deprecation-docs/tasks.md new file mode 100644 index 00000000000..e3e78a06e55 --- /dev/null +++ b/openspec/changes/complete-pulse-deprecation-docs/tasks.md @@ -0,0 +1,21 @@ +## 1. Pulse documentation + +- [x] 1.1 Rewrite `packages/third-parties/pulse/readme.md`: fix the deprecation banner, replace install guidance with the Agenda v6 stack, add dependency/config/decorator/hook diffs and the AI migration note, move legacy usage under a dedicated section. +- [x] 1.2 Rewrite `docs/tutorials/pulse.md` with the same content using VitePress containers and npm/yarn/pnpm/bun code groups; update the page description metadata. +- [x] 1.3 Cross-link the Pulse migration guide from `docs/tutorials/agenda.md` and `packages/third-parties/agenda/readme.md`. + +## 2. Package-level deprecation + +- [x] 2.1 Add `@deprecated` TSDoc to the `@tsed/pulse` public API and the `pulse` configuration key. +- [x] 2.2 Prefix the `@tsed/pulse` package description with `[DEPRECATED]`. + +## 3. Stop promoting Pulse + +- [x] 3.1 Remove the Pulse card from `docs/index.md`. +- [x] 3.2 Label the sidebar entry "Pulse (deprecated)" in `docs/.vitepress/config.mts`. + +## 4. Validation + +- [x] 4.1 Run formatting and lint checks on the touched files (`oxfmt --check`, `oxlint`). +- [x] 4.2 Build the `@tsed/pulse` package to confirm the TSDoc changes compile (`lerna run build --scope @tsed/pulse --include-dependencies`). +- [x] 4.3 Render `docs/tutorials/pulse.md` and `docs/tutorials/agenda.md` through the VitePress markdown renderer to confirm containers, code groups, anchors and cross-links (full `docs:build` requires `api:build` first). diff --git a/packages/third-parties/agenda/readme.md b/packages/third-parties/agenda/readme.md index 300edd4b090..2c758754c16 100644 --- a/packages/third-parties/agenda/readme.md +++ b/packages/third-parties/agenda/readme.md @@ -76,6 +76,10 @@ npm install --save @tsed/agenda agenda @agendajs/mongo-backend export class Server {} ``` +> [!TIP] +> Migrating from `@tsed/pulse`? The package is deprecated. See the +> [Pulse migration guide](https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda) for step-by-step rewrites. + ## Configure your server Import `@tsed/agenda` in your Server: diff --git a/packages/third-parties/pulse/package.json b/packages/third-parties/pulse/package.json index 06db7e63029..8f0838dbba7 100644 --- a/packages/third-parties/pulse/package.json +++ b/packages/third-parties/pulse/package.json @@ -1,7 +1,7 @@ { "name": "@tsed/pulse", "version": "8.17.0", - "description": "@pulsecron/pulse package for Ts.ED framework", + "description": "[DEPRECATED] @pulsecron/pulse package for Ts.ED framework. Use @tsed/agenda with Agenda v6 instead.", "contributors": [ { "name": "Valentin Ivanenco" diff --git a/packages/third-parties/pulse/readme.md b/packages/third-parties/pulse/readme.md index 9904d9f94ac..fa0234f37ca 100644 --- a/packages/third-parties/pulse/readme.md +++ b/packages/third-parties/pulse/readme.md @@ -30,35 +30,59 @@ A package of Ts.ED framework. See website: https://tsed.dev -> [!WARNING] > `@tsed/pulse` is deprecated and will be removed in a future major release. -> This package won't receive any future updates. -> For new projects, use `@tsed/agenda` with Agenda v6. -> Existing consumers should plan a migration to `@tsed/agenda` + `agenda` + `@agendajs/mongo-backend`. +> [!WARNING] +> `@tsed/pulse` is deprecated and will be removed in a future major release. +> This package won't receive any future updates. Do not start new projects on `@tsed/pulse`. > -> The legacy Pulse examples below are kept only to help existing consumers maintain or migrate old code. +> For new projects, use [`@tsed/agenda`](https://tsed.dev/tutorials/agenda.html) with Agenda v6. +> Existing `@tsed/pulse` users should plan a migration to `@tsed/agenda` + `agenda` + `@agendajs/mongo-backend` +> by following the [migration guide](#migrate-to-tsedagenda) below. +> +> The legacy Pulse examples at the end of this page are kept only to help existing consumers maintain or migrate old code. ## Feature -`@pulsecron/pulse` is maintained fork of the Agenda. - -Currently, `@tsed/pulse` allows you to decorate classes with `@Pulse` and -corresponding methods to have them picked up by the @pulsecron/pulse library to be +`@pulsecron/pulse` is a fork of Agenda. `@tsed/pulse` allowed you to decorate classes with `@Pulse` and +corresponding methods to have them picked up by the `@pulsecron/pulse` library to be scheduled automatically (`@Every`) or programmatically (`@Define`) via the PulseService. -For more information about Pulse look at the documentation [here](https://github.com/pulsecron/pulse); +The same features are available in `@tsed/agenda` on top of Agenda v6, which is the recommended and maintained +scheduling integration for Ts.ED. See the [Agenda documentation](https://tsed.dev/tutorials/agenda.html). ## Installation -To begin, install the Pulse module for Ts.ED: +Do not install `@tsed/pulse` for new projects. Install the recommended Agenda v6 stack instead: ```bash -npm install --save @tsed/pulse -npm install --save @pulsecron/pulse +npm install --save @tsed/agenda agenda @agendajs/mongo-backend ``` -## Migration note +Then follow the [Agenda documentation](https://tsed.dev/tutorials/agenda.html) to configure your server. + +> [!NOTE] +> Only if you maintain an existing project that cannot migrate yet: +> `npm install --save @tsed/pulse @pulsecron/pulse`. + +## Migrate to `@tsed/agenda` -Prefer `@tsed/agenda` over `@tsed/pulse`. +The target stack is `@tsed/agenda` + `agenda` (v6) + `@agendajs/mongo-backend`. + +### 1. Replace dependencies + +| Remove | Add | +| ------------------ | ------------------------- | +| `@tsed/pulse` | `@tsed/agenda` | +| `@pulsecron/pulse` | `agenda` | +| | `@agendajs/mongo-backend` | + +```bash +npm uninstall @tsed/pulse @pulsecron/pulse +npm install --save @tsed/agenda agenda @agendajs/mongo-backend +``` + +### 2. Update the server configuration + +The `pulse` configuration key becomes `agenda`, and the legacy `db` options move into a `MongoBackend` instance. ```diff import {Configuration} from "@tsed/di"; @@ -81,10 +105,19 @@ Prefer `@tsed/agenda` over `@tsed/pulse`. export class Server {} ``` +The Ts.ED specific flags (`enabled`, `disableJobProcessing`, `drainJobsBeforeClose`) keep the same names and +behavior in `@tsed/agenda`. + +### 3. Update decorators, injection and lifecycle hooks + +`@tsed/agenda` exposes the same `@Every` and `@Define` decorators. The class decorator is `@JobsController` +and the scheduler instance is the `Agenda` class from `agenda` (there is no `AgendaModule`). + ```diff -import {Pulse, Every, Define, PulseModule} from "@tsed/pulse"; +-import {Job} from "@pulsecron/pulse"; +import {JobsController, Every, Define} from "@tsed/agenda"; -+import {Agenda} from "agenda"; ++import {Agenda, Job} from "agenda"; -@Pulse({namespace: "email"}) +@JobsController({namespace: "email"}) @@ -92,10 +125,89 @@ Prefer `@tsed/agenda` over `@tsed/pulse`. @Inject() - pulse: PulseModule; + agenda: Agenda; + + @Every("60 minutes", {name: "maintenanceJob"}) + async sendAdminStatistics(job: Job) {} + + @Define({name: "sendWelcomeEmail"}) + async sendWelcomeEmail(job: Job) {} + +- async $beforePulseStart() {} +- async $afterPulseStart() {} ++ async $beforeAgendaStart() {} ++ async $afterAgendaStart() {} + } +``` + +Scheduling APIs keep the same shape (`now()`, `schedule()`, `every()`, `create()`), so call sites only need the +injected instance to be renamed: + +```diff + @Service() + export class UsersService { + @Inject() +- private pulse: PulseModule; ++ private agenda: Agenda; + + async create(user: User) { +- await this.pulse.now("email.sendWelcomeEmail", {user}); +- await this.pulse.schedule("in 2 hours", "email.sendFollowUpEmail", {user}); ++ await this.agenda.now("email.sendWelcomeEmail", {user}); ++ await this.agenda.schedule("in 2 hours", "email.sendFollowUpEmail", {user}); + } } ``` -## Configure your server +### AI migration note + +Apply these deterministic rewrites when migrating a codebase with a coding agent: + +```text +Migrate from @tsed/pulse to @tsed/agenda (Agenda v6): + +Dependencies +- remove `@tsed/pulse` → add `@tsed/agenda` +- remove `@pulsecron/pulse` → add `agenda` + `@agendajs/mongo-backend` + +Imports +- `import "@tsed/pulse"` → `import "@tsed/agenda"` +- `import {Pulse, JobsController, Every, Define, PulseModule, PulseService} from "@tsed/pulse"` → `import {JobsController, Every, Define} from "@tsed/agenda"` +- `import {Job} from "@pulsecron/pulse"` → `import {Job} from "agenda"` +- add `import {Agenda} from "agenda"` where `PulseModule` / `PulseService` was injected +- add `import {MongoBackend} from "@agendajs/mongo-backend"` in the server configuration file + +Configuration (@Configuration / TsED.Configuration) +- `pulse: {...}` config key → `agenda: {...}` +- `pulse.db.address` → `agenda.backend: new MongoBackend({address})` +- `pulse.db.collection` / `pulse.db.options` → `agenda.backend: new MongoBackend({collection, options})` +- `pulse.mongo` → `agenda.backend: new MongoBackend({mongo})` +- `pulse.ensureIndex` / `pulse.sort` → move inside `new MongoBackend({ensureIndex, sort})`, sort directions use `"asc"` / `"desc"` +- `pulse.enabled`, `pulse.disableJobProcessing`, `pulse.drainJobsBeforeClose` → keep the same names under `agenda` +- `PulseSettings` type → `AgendaSettings` + +Decorators and injection +- `@Pulse(...)` → `@JobsController(...)` +- `@JobsController(...)` (from `@tsed/pulse`) → `@JobsController(...)` (from `@tsed/agenda`) +- `@Every(...)` / `@Define(...)` → unchanged (import them from `@tsed/agenda`) +- `PulseModule` / `PulseService` injected type → `Agenda` (from `agenda`) +- `pulse.now(...)`, `pulse.schedule(...)`, `pulse.every(...)`, `pulse.create(...)`, `pulse.define(...)` → same methods on the injected `Agenda` +- `pulse.jobs(...)` → `agenda.queryJobs(...)` + +Lifecycle hooks +- `$beforePulseStart()` → `$beforeAgendaStart()` +- `$afterPulseStart()` → `$afterAgendaStart()` +``` + +For more details about `@tsed/agenda`, see [Agenda for Ts.ED](https://tsed.dev/tutorials/agenda.html) +(LLM-friendly version: https://tsed.dev/tutorials/agenda.md). + +## Legacy usage (existing projects only) + +> [!WARNING] +> The following sections document the deprecated `@tsed/pulse` API. They are kept for reference only. +> Do not use them for new code. + +### Configure your server Import `@tsed/pulse` in your Server: @@ -108,7 +220,7 @@ const mongoConnectionString = "mongodb://127.0.0.1/pulse"; @Configuration({ pulse: { enabled: true, // Enable Pulse jobs for this instance. - // drainJobsBeforeStop: true, // Wait for jobs to finish before stopping the pulse process. + // drainJobsBeforeClose: true, // Wait for jobs to finish before stopping the pulse process. // disableJobProcessing: true, // Prevents jobs from being processed. // pass any options that you would normally pass to new Pulse(), e.g. db: { @@ -119,7 +231,7 @@ const mongoConnectionString = "mongodb://127.0.0.1/pulse"; export class Server {} ``` -## Create a new Service +### Create a new Service Decorate the class with `@Pulse`. The `namespace` option is optional and will prefix the job name with `namespace.` @@ -160,7 +272,7 @@ export class EmailJobService { } ``` -## Define a job processor manually +### Define a job processor manually PulseModule exposes methods to manually define a job processor. It can be useful to define a job processor when you need to fetch data beforehand and dynamically build job name / options. @@ -206,7 +318,7 @@ export class EmailJobService { } ``` -## Inject Pulse +### Inject Pulse Inject the PulseService instance to interact with it directly, e.g. to schedule a job manually. diff --git a/packages/third-parties/pulse/src/decorators/define.ts b/packages/third-parties/pulse/src/decorators/define.ts index 721bd89cabd..e3489d3fc0a 100644 --- a/packages/third-parties/pulse/src/decorators/define.ts +++ b/packages/third-parties/pulse/src/decorators/define.ts @@ -1,6 +1,12 @@ import {DefineOptions, PulseStore} from "../interfaces/PulseStore.js"; import {Store} from "@tsed/core"; +/** + * Register the decorated method as a Pulse job processor. + * + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Use `Define` from `@tsed/agenda` instead. + */ export function Define(options: DefineOptions = {}): MethodDecorator { return (target: Object, propertyKey: string | symbol) => { const store: PulseStore = { diff --git a/packages/third-parties/pulse/src/decorators/every.ts b/packages/third-parties/pulse/src/decorators/every.ts index d5c3d1015d7..1bf6a9c50a0 100644 --- a/packages/third-parties/pulse/src/decorators/every.ts +++ b/packages/third-parties/pulse/src/decorators/every.ts @@ -2,6 +2,12 @@ import {EveryOptions, PulseStore} from "../interfaces/PulseStore.js"; import {Store, useDecorators} from "@tsed/core"; import {Define} from "./define.js"; +/** + * Schedule the decorated method as a recurring Pulse job. + * + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Use `Every` from `@tsed/agenda` instead. + */ export function Every(interval: string, options: EveryOptions = {}): MethodDecorator { return useDecorators(Define(options), (target: Object, propertyKey: string) => { const store: PulseStore = { diff --git a/packages/third-parties/pulse/src/decorators/pulse.ts b/packages/third-parties/pulse/src/decorators/pulse.ts index a66058ba07c..fa282cc47bc 100644 --- a/packages/third-parties/pulse/src/decorators/pulse.ts +++ b/packages/third-parties/pulse/src/decorators/pulse.ts @@ -6,6 +6,12 @@ interface PulseOptions { namespace?: string; } +/** + * Declare a class as a Pulse jobs controller. + * + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Use `JobsController` from `@tsed/agenda` instead. + */ export function JobsController(options?: PulseOptions): ClassDecorator { return useDecorators( options?.namespace && StoreMerge("pulse", options), @@ -16,6 +22,7 @@ export function JobsController(options?: PulseOptions): ClassDecorator { } /** - * @deprecated Use `JobsController` instead. + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Use `JobsController` from `@tsed/agenda` instead. */ export const Pulse: typeof JobsController = JobsController; diff --git a/packages/third-parties/pulse/src/interfaces/interfaces.ts b/packages/third-parties/pulse/src/interfaces/interfaces.ts index e121aff1235..d8c331621d1 100644 --- a/packages/third-parties/pulse/src/interfaces/interfaces.ts +++ b/packages/third-parties/pulse/src/interfaces/interfaces.ts @@ -1,5 +1,9 @@ import {PulseConfig} from "@pulsecron/pulse"; +/** + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Use `AgendaSettings` from `@tsed/agenda` with `agenda.backend: new MongoBackend(...)` instead. + */ export type PulseSettings = PulseConfig & { enabled?: boolean; disableJobProcessing?: boolean; @@ -9,6 +13,10 @@ export type PulseSettings = PulseConfig & { declare global { namespace TsED { interface Configuration { + /** + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Use the `agenda` configuration key from `@tsed/agenda` instead. + */ pulse?: PulseSettings; } } diff --git a/packages/third-parties/pulse/src/services/PulseService.ts b/packages/third-parties/pulse/src/services/PulseService.ts index 1addb7ec849..01b82e47597 100644 --- a/packages/third-parties/pulse/src/services/PulseService.ts +++ b/packages/third-parties/pulse/src/services/PulseService.ts @@ -105,6 +105,10 @@ async function onDestroy(pulse: Pulse) { } } +/** + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Inject `Agenda` from `agenda` instead. + */ export const PulseService = injectable(Pulse) .factory(() => { const opts = getOpts(); @@ -158,11 +162,17 @@ export const PulseService = injectable(Pulse) .token(); /** - * @deprecated Use `Pulse` from `pulse` instead. + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Inject `Agenda` from `agenda` instead. */ export type PulseService = Pulse; /** - * @deprecated Use `Pulse` from `pulse` instead. + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Inject `Agenda` from `agenda` instead. */ export const PulseModule = Pulse; +/** + * @deprecated `@tsed/pulse` is deprecated and will be removed in a future major release. Migrate to `@tsed/agenda` (Agenda v6). See https://tsed.dev/tutorials/pulse.html#migrate-to-tsed-agenda + * Inject `Agenda` from `agenda` instead. + */ export type PulseModule = Pulse;