Skip to content

Commit

Permalink
Merge pull request #266 from renejfc/fix/typesafe-constructor
Browse files Browse the repository at this point in the history
fix(types): improve type-safety for cron callback functions
  • Loading branch information
Hexagon authored Nov 16, 2024
2 parents 59967c2 + 8b62d3f commit e92ec11
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 11 deletions.
4 changes: 2 additions & 2 deletions docs/src/usage/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ console.log('Will run first time at', job.nextRun().toLocaleString());
### Interval
```ts
// Trigger on specific interval combined with cron expression
Cron('* * 7-16 * * MON-FRI', { interval: 90 }, function () {
new Cron('* * 7-16 * * MON-FRI', { interval: 90 }, function () {
console.log('This will trigger every 90th second at 7-16 on mondays to fridays.');
});
```
Expand Down Expand Up @@ -214,7 +214,7 @@ const blockForAWhile = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const protectCallback = (job) => console.log(`Call at ${new Date().toISOString()} were blocked by call started at ${job.currentRun().toISOString()}`);

// protect: can be set to ether true or a callback function, to enable over-run protection
Cron("* * * * * *", { protect: protectCallback }, async (job) => {
new Cron("* * * * * *", { protect: protectCallback }, async (job) => {
console.log(`Call started at ${job.currentRun().toISOString()} started`);
await blockForAWhile(4000);
console.log(`Call started at ${job.currentRun().toISOString()} finished ${new Date().toISOString()}`);
Expand Down
27 changes: 20 additions & 7 deletions src/croner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import { CronDate } from "./date.ts";
import { CronPattern } from "./pattern.ts";
import { type CronOptions, CronOptionsHandler } from "./options.ts";
import { isFunction, unrefTimer } from "./utils.ts";
import { isCronCallback, isFunction, unrefTimer } from "./utils.ts";

/**
* Many JS engines stores the delay as a 32-bit signed integer internally.
Expand Down Expand Up @@ -84,6 +84,19 @@ type CronState = {
pattern: CronPattern;
};

/**
* Callback function type
*
* @param self - Reference to the Cron instance that triggered the callback
* @param context - Optional context value passed through options.context
*
* @returns void or Promise<void> for async callbacks
*/
export type CronCallback = (
self: InstanceType<typeof Cron>,
context: unknown,
) => void | Promise<void>;

/**
* Cron entrypoint
*
Expand All @@ -96,11 +109,11 @@ class Cron {
name: string | undefined;
options: CronOptions;
private _states: CronState;
private fn?: Function;
private fn?: CronCallback;
constructor(
pattern: string | Date,
fnOrOptions1?: CronOptions | Function,
fnOrOptions2?: CronOptions | Function,
fnOrOptions1?: CronOptions | CronCallback,
fnOrOptions2?: CronOptions | CronCallback,
) {
// Make options and func optional and interchangable
let options, func;
Expand Down Expand Up @@ -164,8 +177,8 @@ class Cron {
}

// Allow shorthand scheduling
if (func !== void 0 && isFunction(func)) {
this.fn = func as Function;
if (func !== void 0 && isCronCallback(func)) {
this.fn = func;
this.schedule();
}

Expand Down Expand Up @@ -339,7 +352,7 @@ class Cron {
*
* @param func - Function to be run each iteration of pattern
*/
public schedule(func?: Function): Cron {
public schedule(func?: CronCallback): Cron {
// If a function is already scheduled, bail out
if (func && this.fn) {
throw new Error(
Expand Down
13 changes: 13 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { CronCallback } from "./croner.ts";

/**
* Helper function to check if a variable is a function.
*
Expand All @@ -13,6 +15,17 @@ function isFunction(v: unknown) {
);
}

/**
* Type guard to check if a variable is a valid CronCallback function.
*
* @param v The variable to check.
* @returns Type predicate indicating if the variable is a CronCallback.
* @private
*/
export function isCronCallback(v: unknown): v is CronCallback {
return isFunction(v);
}

/**
* Helper function to unref a timer in both Deno and Node.js.
*
Expand Down
4 changes: 2 additions & 2 deletions test/croner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ test(
//@ts-ignore
timeout(2000, (resolve, reject) => {
const c = { a: "b" };
new Cron("* * * * * *", { context: c }, (self: Cron, context: { a: string }) => {
new Cron("* * * * * *", { context: c }, (self, context) => {
self.stop();
if (!context || (context && context.a && context.a !== "b")) {
if (typeof context !== "object" || !context || !("a" in context) || context.a !== "b") {
reject(new Error("Failure"));
} else {
resolve();
Expand Down

0 comments on commit e92ec11

Please sign in to comment.