Skip to content

Feat/compt 75 typed job definitions ischeduler interface#3

Merged
saadmoumou merged 4 commits intodevelopfrom
feat/COMPT-75-typed-job-definitions-ischeduler-interface
Apr 8, 2026
Merged

Feat/compt 75 typed job definitions ischeduler interface#3
saadmoumou merged 4 commits intodevelopfrom
feat/COMPT-75-typed-job-definitions-ischeduler-interface

Conversation

@saadmoumou
Copy link
Copy Markdown
Contributor

Summary

  • What does this PR change?

Why

  • Why is this change needed?

Checklist

  • Added/updated tests (if behavior changed)
  • npm run lint passes
  • npm run typecheck passes
  • npm test passes
  • npm run build passes
  • Added a changeset (npx changeset) if this affects consumers

Notes

  • Anything reviewers should pay attention to?

- Add ScheduledJob type: name, handler, and one of cron | interval | timeout
- Add IScheduler interface: schedule, unschedule, reschedule, list
- Add ScheduledJobStatus type: name, cron, lastRun, nextRun, isRunning
- Add DuplicateJobError thrown when registering a duplicate job name
- Enforce cron/interval/timeout mutual exclusivity via discriminated union
- Add CronExpression named constants for human-readable schedules
- Add cron fluent builder: dailyAt, weeklyOn, monthlyOn, every().minutes()
- Update tsconfig.build.json: CommonJS output, Task 1 files only
- Update package.json: name @ciscode/scheduler-kit, version 0.0.0
…hods

- Add ScheduledJobStatus type: name, cron, lastRun, nextRun, isRunning
- Add status(name) and listStatus() to IScheduler interface and SchedulerService
- Track lastRun timestamp after each execution
- Store cronJob reference for nextRun via cronJob.nextDate()
- Export ScheduledJobStatus from public API
- Fix lint: no-misused-promises, no-floating-promises, prettier formatting
@saadmoumou saadmoumou requested a review from a team as a code owner April 8, 2026 12:54
Copilot AI review requested due to automatic review settings April 8, 2026 12:54
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR evolves the package into a typed, dynamically controllable scheduler kit for NestJS, adding a scheduler service/module, decorators, and cron helpers while updating build/lint tooling to support the new surface area.

Changes:

  • Introduces SchedulerService + SchedulerModule with a typed IScheduler contract, runtime registry, and concurrency guard.
  • Adds @Cron / @Interval / @Timeout decorators plus cron expression constants and a fluent cron builder.
  • Updates TypeScript build config and ESLint/lint-staged/Husky configuration, and adjusts package dependencies.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tsconfig.eslint.json Includes *.mjs in the ESLint TS project to support the new ESLint config format.
tsconfig.build.json Switches build output to CJS and narrows build inputs to scheduler-related entrypoints.
src/services/scheduler.service.ts Implements scheduler registry, scheduling/rescheduling APIs, status reporting, and concurrency guard.
src/services/scheduler.service.spec.ts Adds unit tests for scheduling, rescheduling, timer behavior, concurrency guard, and error handling.
src/scheduler.module.ts Adds a dynamic module that scans providers for scheduler decorators and auto-registers jobs.
src/scheduler.module.spec.ts Tests module auto-registration behavior and dynamic module factories.
src/interfaces/scheduler.interface.ts Adds discriminated union timing types and the public IScheduler contract.
src/index.ts Updates public exports to the scheduler kit API (module/service/decorators/types/errors/helpers).
src/errors/duplicate-job.error.ts Adds a dedicated error for duplicate job registration.
src/errors/duplicate-job.error.spec.ts Adds tests for the new error type.
src/decorators/scheduler.decorators.ts Adds decorators that attach scheduler metadata via reflect-metadata.
src/decorators/scheduler.decorators.spec.ts Adds tests validating decorator metadata shapes.
src/cron-expression.ts Adds human-readable cron expression constants.
src/cron-expression.spec.ts Adds tests for exported cron expression constants.
src/cron-builder.ts Adds a fluent builder for cron strings with human-readable time parsing.
src/cron-builder.spec.ts Adds tests for cron builder outputs and time parsing behavior.
package.json Updates metadata and dependencies/peerDependencies to match the scheduler kit direction.
package-lock.json Updates lockfile to reflect dependency changes (Nest schedule/cron/prettier tooling, etc.).
lint-staged.config.js Switches lint-staged config to CommonJS export style.
eslint.config.mjs Updates ESLint flat config, ignores, and TypeScript project wiring; adds Prettier integration.
eslint.config.js Removes the previous ESLint configuration file.
.husky/pre-push Modifies the pre-push hook commands (currently removes Husky bootstrap/shebang).
Comments suppressed due to low confidence (1)

package.json:81

  • class-validator/class-transformer were removed from dependencies, but the repo still contains TypeScript sources that import class-validator (e.g. src/dto/create-example.dto.ts). Since npm run typecheck uses tsconfig.json which includes src/**/*.ts, this will fail compilation unless those example files are removed/excluded or the dependencies are restored.
  "peerDependencies": {
    "@nestjs/common": "^10 || ^11",
    "@nestjs/core": "^10 || ^11",
    "@nestjs/schedule": "^4 || ^5",
    "reflect-metadata": "^0.2.2",
    "rxjs": "^7"
  },
  "devDependencies": {
    "@changesets/cli": "^2.27.7",
    "@eslint/js": "^9.18.0",
    "@nestjs/common": "^10.4.0",
    "@nestjs/core": "^10.4.0",
    "@nestjs/mapped-types": "^2.0.0",
    "@nestjs/platform-express": "^10.4.0",
    "@nestjs/schedule": "^6.1.1",
    "@nestjs/testing": "^10.4.0",
    "@types/jest": "^29.5.14",
    "@types/node": "^22.10.7",
    "cron": "^4.4.0",
    "eslint": "^9.18.0",
    "eslint-config-prettier": "^10.1.8",
    "eslint-plugin-import": "^2.32.0",
    "eslint-plugin-prettier": "^5.5.5",
    "globals": "^16.5.0",
    "husky": "^9.1.7",
    "jest": "^29.7.0",
    "lint-staged": "^16.2.7",
    "prettier": "^3.4.2",
    "reflect-metadata": "^0.2.2",
    "rxjs": "^7.8.1",
    "ts-jest": "^29.2.5",
    "ts-node": "^10.9.2",
    "tsc-alias": "^1.8.10",
    "tsconfig-paths": "^4.2.0",
    "typescript": "^5.7.3",
    "typescript-eslint": "^8.50.1"
  }

Comment on lines +58 to +65
reschedule(name: string, newTiming: ScheduleTiming): void {
const entry = this.registry.get(name);
if (!entry) return;
const newJob: ScheduledJob = { ...entry.job, ...newTiming } as ScheduledJob;
entry.stop();
this.registry.delete(name);
this.schedule(newJob);
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reschedule() builds newJob by spreading the previous job (...entry.job) into the new timing. Because the old timing keys (e.g. cron) stay on the object at runtime, rescheduling from cron→interval (or similar) can leave multiple timing fields set, causing _createEntry() to pick the wrong schedule type (e.g. still treated as cron). Build the new job from { name, handler, ...newTiming } (or explicitly omit timing keys from the old job) to ensure only one timing field is present at runtime.

Copilot uses AI. Check for mistakes.
constructor(options: SchedulerModuleOptions = {}) {
this.onJobError =
options.onJobError ??
((name, error) => this.logger.error(`Job '${name}' threw an error`, error));
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default onJobError handler passes the caught error object as the second argument to Logger.error(), but Nest's Logger.error(message, trace?, context?) expects a stack/trace string there. Convert the error to a string/stack (e.g. error instanceof Error ? error.stack : String(error)) or log via a structured logger to avoid losing the actual stack trace / producing [object Object] output.

Suggested change
((name, error) => this.logger.error(`Job '${name}' threw an error`, error));
((name, error) =>
this.logger.error(
`Job '${name}' threw an error`,
error instanceof Error ? error.stack ?? error.message : String(error),
));

Copilot uses AI. Check for mistakes.
Comment thread package.json
Comment on lines 9 to 12
"repository": {
"type": "git",
"url": "git+https://github.com/CISCODE-MA/SchedulerKit.git"
"url": "git+https://github.com/CISCODE-MA/"
},
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repository.url value (git+https://github.com/CISCODE-MA/) does not point to an actual git repository (it’s missing the repo path). This will break npm metadata links; update it to the full repo URL (e.g. .../SchedulerKit.git) or remove the field if not known yet.

Copilot uses AI. Check for mistakes.
Comment thread package.json
"@nestjs/common": "^10 || ^11",
"@nestjs/core": "^10 || ^11",
"@nestjs/platform-express": "^10 || ^11",
"@nestjs/schedule": "^4 || ^5",
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nestjs/schedule is declared as a peer dependency, but there are no imports/usages of @nestjs/schedule in the source (the implementation uses CronJob from the cron package directly). Keeping an unused peer dependency increases install/compat burden for consumers; either remove it or refactor to actually use Nest Schedule APIs.

Suggested change
"@nestjs/schedule": "^4 || ^5",

Copilot uses AI. Check for mistakes.
Comment thread package.json
Comment on lines 45 to 60
"peerDependencies": {
"@nestjs/common": "^10 || ^11",
"@nestjs/core": "^10 || ^11",
"@nestjs/platform-express": "^10 || ^11",
"@nestjs/schedule": "^4 || ^5",
"reflect-metadata": "^0.2.2",
"rxjs": "^7"
},
"dependencies": {
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1"
},
"devDependencies": {
"@changesets/cli": "^2.27.7",
"@eslint/js": "^9.18.0",
"@nestjs/common": "^10.4.0",
"@nestjs/core": "^10.4.0",
"@nestjs/mapped-types": "^2.0.0",
"@nestjs/platform-express": "^10.4.0",
"@nestjs/schedule": "^6.1.1",
"@nestjs/testing": "^10.4.0",
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peer dependency range for @nestjs/schedule is ^4 || ^5, but this repo installs @nestjs/schedule@6.1.1 in devDependencies. This will produce peer dependency warnings/errors during install. Align the peer range with the version you test against (or downgrade the devDependency).

Copilot uses AI. Check for mistakes.
Comment thread package.json
Comment on lines 52 to 66
"devDependencies": {
"@changesets/cli": "^2.27.7",
"@eslint/js": "^9.18.0",
"@nestjs/common": "^10.4.0",
"@nestjs/core": "^10.4.0",
"@nestjs/mapped-types": "^2.0.0",
"@nestjs/platform-express": "^10.4.0",
"@nestjs/schedule": "^6.1.1",
"@nestjs/testing": "^10.4.0",
"@types/jest": "^29.5.14",
"@types/node": "^22.10.7",
"cron": "^4.4.0",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SchedulerService imports CronJob from the cron package, but cron is only listed in devDependencies. Consumers installing this package in production will not get cron, causing runtime failures (Cannot find module 'cron'). Move cron to dependencies (or to peerDependencies with a clear install requirement).

Copilot uses AI. Check for mistakes.
Comment thread .husky/pre-push
Comment on lines 1 to 2
npm run typecheck
npm run test:cov No newline at end of file
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Husky hook no longer has a shebang or Husky bootstrap (.husky.sh). Git executes hooks as standalone executables; without a shebang this will typically fail with an "exec format error" and the pre-push checks won't run. Restore the shebang (e.g. #!/usr/bin/env sh) and the Husky init line, or ensure the hook is otherwise invoked via a shell script.

Copilot uses AI. Check for mistakes.
@saadmoumou saadmoumou merged commit db76b7a into develop Apr 8, 2026
2 checks passed
Zaiidmo added a commit that referenced this pull request Apr 10, 2026
* ops: updated sonar variable

* Feat/compt 75 typed job definitions ischeduler interface (#3)

* feat(COMPT-75): define typed scheduled job contracts and IScheduler port

- Add ScheduledJob type: name, handler, and one of cron | interval | timeout
- Add IScheduler interface: schedule, unschedule, reschedule, list
- Add ScheduledJobStatus type: name, cron, lastRun, nextRun, isRunning
- Add DuplicateJobError thrown when registering a duplicate job name
- Enforce cron/interval/timeout mutual exclusivity via discriminated union
- Add CronExpression named constants for human-readable schedules
- Add cron fluent builder: dailyAt, weeklyOn, monthlyOn, every().minutes()
- Update tsconfig.build.json: CommonJS output, Task 1 files only
- Update package.json: name @ciscode/scheduler-kit, version 0.0.0

* test(COMPT-75): add unit tests for CronExpression and cron builder

* feat(COMPT-75): add ScheduledJobStatus type and status/listStatus methods

- Add ScheduledJobStatus type: name, cron, lastRun, nextRun, isRunning
- Add status(name) and listStatus() to IScheduler interface and SchedulerService
- Track lastRun timestamp after each execution
- Store cronJob reference for nextRun via cronJob.nextDate()
- Export ScheduledJobStatus from public API
- Fix lint: no-misused-promises, no-floating-promises, prettier formatting

* style(COMPT-75): format all files with prettier

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* Feat/compt 76 nestjs scheduler module and service (#4)

* feat(COMPT-76): NestJS SchedulerModule and SchedulerService integration

- SchedulerModule.register() and registerAsync() dynamic module factories
- SchedulerService: schedule(), reschedule(), unschedule(), list(), status(), listStatus()
- @Cron, @interval, @timeout decorators with optional job name
- MetadataScanner auto-discovers decorated provider methods on onModuleInit
- DuplicateJobError guard against double registration
- IScheduler interface and ScheduledJobStatus type
- CronExpression constants and cron fluent builder
- Full unit test coverage across module, service and decorators

* test(COMPT-76): add unit tests for CronExpression and cron builder

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* Feat/compt 77 dynamic scheduling error handling (#5)

* chore(COMPT-77): scope index.ts and tsconfig.build.json to COMPT-77 files only

* test(COMPT-77): add tests for status, listStatus, cron jobs and default onJobError

* chore(COMPT-77): restore index.ts and tsconfig.build.json to develop baseline

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* test(COMPT-78): full coverage with Jest fake timers for all schedulin… (#7)

* test(COMPT-78): full coverage with Jest fake timers for all scheduling behaviors

- jest.useFakeTimers() for all time-based tests — no real waiting
- @interval: fires every N ms, stops after unschedule, does not fire before interval
- @timeout: fires exactly once, removes itself from registry, does not fire early
- schedule(): job added and fires on tick
- unschedule(): timer cleared, handler not called after removal
- reschedule(): new schedule applied atomically, old timer cancelled
- Job error: caught via onJobError, scheduler continues — other jobs unaffected
- Concurrent guard: second execution skipped when first still running (isRunning lock)
- status() / listStatus(): reflect isRunning and lastRun correctly
- @Cron: registered and unscheduled without throwing
- Default onJobError: delegates to Logger.error
- Coverage: 98.63% statements, 98.11% branches, 95.34% functions (threshold 85%)

* fix(COMPT-78): resolve SonarCloud S1186 and S4524 issues

- cron-builder.ts: parseInt -> Number.parseInt (Sonar S4524 x3)
- scheduler.decorators.spec.ts: add comments to empty stub methods (Sonar S1186 x6)
- scheduler.module.spec.ts: add comments to empty stub methods and class (Sonar S1186 x6)

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* release(COMPT-79): bump version to 0.1.0, rewrite README, add changeset (#8)

- package.json: 0.0.0 -> 0.1.0
- README: full documentation (decorators, dynamic API, concurrency guard,
  error handling, async config, cron helpers, API reference)
- .changeset/scheduler-kit-v0-1-0.md: minor bump for initial feature release

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* Feat/compt 79 readme changeset publish v0.1.0 (#10)

* release(COMPT-79): bump version to 0.1.0, rewrite README, add changeset

- package.json: 0.0.0 -> 0.1.0
- README: full documentation (decorators, dynamic API, concurrency guard,
  error handling, async config, cron helpers, API reference)
- .changeset/scheduler-kit-v0-1-0.md: minor bump for initial feature release

* fix(COMPT-79): add sonar-project.properties to fix SonarCloud quality gate

- sonar.exclusions: exclude spec files from source analysis
- sonar.tests + sonar.test.inclusions: declare spec files as test code
- fixes 31.9% coverage on new code and 4.8% duplication failures

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

---------

Co-authored-by: Zaiidmo <zaiidmoumnii@gmail.com>
Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>
Zaiidmo added a commit that referenced this pull request Apr 10, 2026
* ops: updated sonar variable

* Feat/compt 75 typed job definitions ischeduler interface (#3)

* feat(COMPT-75): define typed scheduled job contracts and IScheduler port

- Add ScheduledJob type: name, handler, and one of cron | interval | timeout
- Add IScheduler interface: schedule, unschedule, reschedule, list
- Add ScheduledJobStatus type: name, cron, lastRun, nextRun, isRunning
- Add DuplicateJobError thrown when registering a duplicate job name
- Enforce cron/interval/timeout mutual exclusivity via discriminated union
- Add CronExpression named constants for human-readable schedules
- Add cron fluent builder: dailyAt, weeklyOn, monthlyOn, every().minutes()
- Update tsconfig.build.json: CommonJS output, Task 1 files only
- Update package.json: name @ciscode/scheduler-kit, version 0.0.0

* test(COMPT-75): add unit tests for CronExpression and cron builder

* feat(COMPT-75): add ScheduledJobStatus type and status/listStatus methods

- Add ScheduledJobStatus type: name, cron, lastRun, nextRun, isRunning
- Add status(name) and listStatus() to IScheduler interface and SchedulerService
- Track lastRun timestamp after each execution
- Store cronJob reference for nextRun via cronJob.nextDate()
- Export ScheduledJobStatus from public API
- Fix lint: no-misused-promises, no-floating-promises, prettier formatting

* style(COMPT-75): format all files with prettier

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* Feat/compt 76 nestjs scheduler module and service (#4)

* feat(COMPT-76): NestJS SchedulerModule and SchedulerService integration

- SchedulerModule.register() and registerAsync() dynamic module factories
- SchedulerService: schedule(), reschedule(), unschedule(), list(), status(), listStatus()
- @Cron, @interval, @timeout decorators with optional job name
- MetadataScanner auto-discovers decorated provider methods on onModuleInit
- DuplicateJobError guard against double registration
- IScheduler interface and ScheduledJobStatus type
- CronExpression constants and cron fluent builder
- Full unit test coverage across module, service and decorators

* test(COMPT-76): add unit tests for CronExpression and cron builder

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* Feat/compt 77 dynamic scheduling error handling (#5)

* chore(COMPT-77): scope index.ts and tsconfig.build.json to COMPT-77 files only

* test(COMPT-77): add tests for status, listStatus, cron jobs and default onJobError

* chore(COMPT-77): restore index.ts and tsconfig.build.json to develop baseline

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* test(COMPT-78): full coverage with Jest fake timers for all schedulin… (#7)

* test(COMPT-78): full coverage with Jest fake timers for all scheduling behaviors

- jest.useFakeTimers() for all time-based tests — no real waiting
- @interval: fires every N ms, stops after unschedule, does not fire before interval
- @timeout: fires exactly once, removes itself from registry, does not fire early
- schedule(): job added and fires on tick
- unschedule(): timer cleared, handler not called after removal
- reschedule(): new schedule applied atomically, old timer cancelled
- Job error: caught via onJobError, scheduler continues — other jobs unaffected
- Concurrent guard: second execution skipped when first still running (isRunning lock)
- status() / listStatus(): reflect isRunning and lastRun correctly
- @Cron: registered and unscheduled without throwing
- Default onJobError: delegates to Logger.error
- Coverage: 98.63% statements, 98.11% branches, 95.34% functions (threshold 85%)

* fix(COMPT-78): resolve SonarCloud S1186 and S4524 issues

- cron-builder.ts: parseInt -> Number.parseInt (Sonar S4524 x3)
- scheduler.decorators.spec.ts: add comments to empty stub methods (Sonar S1186 x6)
- scheduler.module.spec.ts: add comments to empty stub methods and class (Sonar S1186 x6)

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* release(COMPT-79): bump version to 0.1.0, rewrite README, add changeset (#8)

- package.json: 0.0.0 -> 0.1.0
- README: full documentation (decorators, dynamic API, concurrency guard,
  error handling, async config, cron helpers, API reference)
- .changeset/scheduler-kit-v0-1-0.md: minor bump for initial feature release

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* Feat/compt 79 readme changeset publish v0.1.0 (#10)

* release(COMPT-79): bump version to 0.1.0, rewrite README, add changeset

- package.json: 0.0.0 -> 0.1.0
- README: full documentation (decorators, dynamic API, concurrency guard,
  error handling, async config, cron helpers, API reference)
- .changeset/scheduler-kit-v0-1-0.md: minor bump for initial feature release

* fix(COMPT-79): add sonar-project.properties to fix SonarCloud quality gate

- sonar.exclusions: exclude spec files from source analysis
- sonar.tests + sonar.test.inclusions: declare spec files as test code
- fixes 31.9% coverage on new code and 4.8% duplication failures

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>

* patch lock file deps

* chore(tsconfig): silence TS6 deprecation warnings

---------

Co-authored-by: saadmoumou <s.moumou@ciscod.com>
Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants