@skalfa/skalfa-cron 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - master
8
+
9
+ jobs:
10
+ publish:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout repository
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Setup Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 20
20
+ registry-url: https://registry.npmjs.org/
21
+
22
+ - name: Setup Bun
23
+ uses: oven-sh/setup-bun@v1
24
+ with:
25
+ bun-version: latest
26
+
27
+ - name: Publish if version bumped
28
+ run: |
29
+ LOCAL_VER=$(node -p "require('./package.json').version")
30
+ NPM_VER=$(npm view @skalfa/skalfa-cron version 2>/dev/null || echo "0.0.0")
31
+ if [ "$LOCAL_VER" != "$NPM_VER" ]; then
32
+ echo "Publishing @skalfa/skalfa-cron $LOCAL_VER (Registry version: $NPM_VER)"
33
+ bun install
34
+ npm run build
35
+ npm publish --access public
36
+ else
37
+ echo "@skalfa/skalfa-cron is up to date ($LOCAL_VER)"
38
+ fi
39
+ env:
40
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,45 @@
1
+ # Contributing to @skalfa/skalfa-cron
2
+
3
+ Thank you for your interest in contributing to Skalfa! We welcome contributions from the community to help make Skalfa the premier development framework.
4
+
5
+ ## How to Contribute
6
+
7
+ To contribute to this package, please follow these standard open-source steps:
8
+
9
+ ### 1. Fork the Repository
10
+ Fork the official Skalfa repository on GitHub to your personal account.
11
+
12
+ ### 2. Clone Your Fork
13
+ Clone your personal fork to your local machine:
14
+ ```bash
15
+ git clone https://github.com/your-username/skalfa.git
16
+ cd skalfa
17
+ ```
18
+
19
+ ### 3. Create a Feature Branch
20
+ Create a new branch for your feature or bugfix:
21
+ ```bash
22
+ git checkout -b feature/amazing-new-feature
23
+ ```
24
+ *(Use `bugfix/issue-description` for bugfixes).*
25
+
26
+ ### 4. Implement and Commit Your Changes
27
+ Make your changes in the codebase. Write clean, formatted code and ensure all tests and typechecks pass. Commit your changes using semantic commit messages:
28
+ ```bash
29
+ git commit -m "feat: add amazing new feature"
30
+ ```
31
+ *(Use `fix: description` for bugfixes, and `docs: description` for documentation updates).*
32
+
33
+ ### 5. Push to GitHub
34
+ Push your branch to your personal fork on GitHub:
35
+ ```bash
36
+ git push origin feature/amazing-new-feature
37
+ ```
38
+
39
+ ### 6. Submit a Pull Request (PR)
40
+ Go to the official Skalfa repository on GitHub. You will see a prompt to submit a pull request. Click "New Pull Request", describe your changes in detail, link any related issues, and submit it for review by the maintainers.
41
+
42
+ ## Guidelines
43
+ * **Code Style**: Ensure your code conforms to the project's ESLint rules and formatting guidelines.
44
+ * **Type Safety**: Write strict, type-safe TypeScript. Do not bypass the compiler.
45
+ * **Testing**: Run local tests and verify that the build compiles with 0 errors before submitting a PR.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Skalfa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ <p align="center">
2
+ <img src="https://skalfa.sejedigital.com/images/logo-skalfa.png" alt="Skalfa Logo" width="300" />
3
+ </p>
4
+
5
+ # @skalfa/skalfa-cron
6
+
7
+ > Automated background cron job scheduling and task worker utility extension for Skalfa backend services.
8
+
9
+ ---
10
+
11
+ ## About this Package
12
+
13
+ This package is part of the **Skalfa Framework**, a premium development ecosystem designed to build high-performance, modular web applications and APIs.
14
+
15
+ ### Usage Scope & Standalone Status
16
+ > 🔒 **Skalfa Ecosystem Integration:** This package is designed to run **integrated within the Skalfa ecosystem** (such as Skalfa API or Skalfa App). It relies on the global service registry and core framework abstractions to operate.
17
+
18
+ ---
19
+
20
+ ## Documentation
21
+
22
+ See the usage documentation at [Documentation](https://skalfa.sejedigital.com).
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ You can install this package using your preferred package manager:
29
+
30
+ ```bash
31
+ # Using npm
32
+ npm install @skalfa/skalfa-cron
33
+
34
+ # Using bun
35
+ bun add @skalfa/skalfa-cron
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Pre-installed Dependencies
41
+
42
+ The following key dependencies are packaged and managed within this project:
43
+
44
+ | Dependency | Scope | Version |
45
+ | :--- | :--- | :--- |
46
+ | `@skalfa/skalfa-api-core` | runtime | `file:../skalfa-api-core` |
47
+ | `@types/node` | development | `^26.0.0` |
48
+ | `typescript` | development | `^6.0.3` |
49
+
50
+ ---
51
+
52
+ ## License
53
+
54
+ This package is licensed under the **MIT License**. For full license text, see the [LICENSE](LICENSE) file.
package/dist/add.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { CronJob } from "./state";
2
+ export declare const add: (schedule: string, handler: CronJob["handler"], jobName?: string) => void;
package/dist/add.js ADDED
@@ -0,0 +1,6 @@
1
+ import { jobs } from "./state";
2
+ export const add = (schedule, handler, jobName) => {
3
+ const name = jobName || `Cronjob(${jobs.length + 1})`;
4
+ jobs.push({ name, schedule, handler, last: null });
5
+ };
6
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../src/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAW,MAAM,SAAS,CAAC;AAExC,MAAM,CAAC,MAAM,GAAG,GAAG,CACjB,QAAoB,EACpB,OAAgC,EAChC,OAAoB,EACpB,EAAE;IACF,MAAM,IAAI,GAAG,OAAO,IAAI,WAAW,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAA;IACrD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;AACpD,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,12 +1,9 @@
1
- export type CronJob = {
2
- name: string;
3
- schedule: string;
4
- handler: () => Promise<void> | void;
5
- last?: string | null;
6
- };
1
+ import { parseSchedule } from "./parse-schedule";
2
+ import { isScheduleMatch } from "./is-schedule-match";
3
+ export type { CronJob } from "./state";
7
4
  export declare const cron: {
8
- add: (schedule: string, handler: CronJob["handler"], jobName?: string) => void;
5
+ add: (schedule: string, handler: import("./state").CronJob["handler"], jobName?: string) => void;
9
6
  worker: () => void;
10
- parseSchedule(value: number, pattern: string): boolean;
11
- isScheduleMatch(schedule: string, date: Date): boolean;
7
+ parseSchedule: typeof parseSchedule;
8
+ isScheduleMatch: typeof isScheduleMatch;
12
9
  };
package/dist/index.js CHANGED
@@ -1,69 +1,23 @@
1
- import { logger } from "@skalfa/skalfa-api-core";
2
- const jobs = [];
3
- const interval = Number(process.env.CRON_INTERVAL) || 10000;
1
+ import { add } from "./add";
2
+ import { worker } from "./worker";
3
+ import { parseSchedule } from "./parse-schedule";
4
+ import { isScheduleMatch } from "./is-schedule-match";
4
5
  export const cron = {
5
6
  // ===========================>
6
7
  // ## Cron: add new job
7
8
  // ===========================>
8
- add: (schedule, handler, jobName) => {
9
- const name = jobName || `Cronjob(${jobs.length + 1})`;
10
- jobs.push({ name, schedule, handler, last: null });
11
- },
9
+ add,
12
10
  // ===========================>
13
11
  // ## Cron: job worker
14
12
  // ===========================>
15
- worker: () => {
16
- setInterval(async () => {
17
- const now = new Date();
18
- const minuteKey = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ` + `${now.getHours()}:${now.getMinutes()}`;
19
- for (const job of jobs) {
20
- if (!cron.isScheduleMatch(job.schedule, now))
21
- continue;
22
- if (job.last === minuteKey)
23
- continue;
24
- job.last = minuteKey;
25
- try {
26
- await job.handler();
27
- logger.cron(`${job.name} at (${now}) success!`);
28
- }
29
- catch (err) {
30
- const em = err instanceof Error ? err.message : String(err);
31
- logger.cronError(`${job.name} at (${now}) error : ${em}`, { error: em, reference: job.name, at: minuteKey });
32
- }
33
- }
34
- }, interval);
35
- },
13
+ worker,
36
14
  // ===========================>
37
15
  // ## Cron: job schedule parser
38
16
  // ===========================>
39
- parseSchedule(value, pattern) {
40
- if (pattern === "*")
41
- return true;
42
- if (pattern.includes(",")) {
43
- return pattern.split(",").some((p) => this.parseSchedule(value, p));
44
- }
45
- if (pattern.includes("/")) {
46
- const [base, step] = pattern.split("/");
47
- const stepNum = Number(step);
48
- if (base === "*")
49
- return value % stepNum === 0;
50
- }
51
- if (pattern.includes("-")) {
52
- const [start, end] = pattern.split("-").map(Number);
53
- return value >= start && value <= end;
54
- }
55
- return Number(pattern) === value;
56
- },
17
+ parseSchedule,
57
18
  // ===========================>
58
19
  // ## Cron: check job schedule
59
20
  // ===========================>
60
- isScheduleMatch(schedule, date) {
61
- const [min, hour, day, month, week] = schedule.split(" ");
62
- return (this.parseSchedule(date.getMinutes(), min) &&
63
- this.parseSchedule(date.getHours(), hour) &&
64
- this.parseSchedule(date.getDate(), day) &&
65
- this.parseSchedule(date.getMonth() + 1, month) &&
66
- this.parseSchedule(date.getDay(), week));
67
- },
21
+ isScheduleMatch,
68
22
  };
69
23
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAItD,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,+BAA+B;IAC/B,uBAAuB;IACvB,+BAA+B;IAC/B,GAAG;IAEH,+BAA+B;IAC/B,sBAAsB;IACtB,+BAA+B;IAC/B,MAAM;IAEN,+BAA+B;IAC/B,+BAA+B;IAC/B,+BAA+B;IAC/B,aAAa;IAEb,+BAA+B;IAC/B,8BAA8B;IAC9B,+BAA+B;IAC/B,eAAe;CAChB,CAAA"}
@@ -0,0 +1 @@
1
+ export declare function isScheduleMatch(schedule: string, date: Date): boolean;
@@ -0,0 +1,10 @@
1
+ import { parseSchedule } from "./parse-schedule";
2
+ export function isScheduleMatch(schedule, date) {
3
+ const [min, hour, day, month, week] = schedule.split(" ");
4
+ return (parseSchedule(date.getMinutes(), min) &&
5
+ parseSchedule(date.getHours(), hour) &&
6
+ parseSchedule(date.getDate(), day) &&
7
+ parseSchedule(date.getMonth() + 1, month) &&
8
+ parseSchedule(date.getDay(), week));
9
+ }
10
+ //# sourceMappingURL=is-schedule-match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-schedule-match.js","sourceRoot":"","sources":["../src/is-schedule-match.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,IAAU;IAC1D,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAEzD,OAAO,CACL,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC;QACrC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC;QACpC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC;QAClC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC;QACzC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CACnC,CAAA;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function parseSchedule(value: number, pattern: string): boolean;
@@ -0,0 +1,19 @@
1
+ export function parseSchedule(value, pattern) {
2
+ if (pattern === "*")
3
+ return true;
4
+ if (pattern.includes(",")) {
5
+ return pattern.split(",").some((p) => parseSchedule(value, p));
6
+ }
7
+ if (pattern.includes("/")) {
8
+ const [base, step] = pattern.split("/");
9
+ const stepNum = Number(step);
10
+ if (base === "*")
11
+ return value % stepNum === 0;
12
+ }
13
+ if (pattern.includes("-")) {
14
+ const [start, end] = pattern.split("-").map(Number);
15
+ return value >= start && value <= end;
16
+ }
17
+ return Number(pattern) === value;
18
+ }
19
+ //# sourceMappingURL=parse-schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-schedule.js","sourceRoot":"","sources":["../src/parse-schedule.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,OAAe;IAC1D,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAA;IAEhC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;IAChE,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,IAAI,KAAK,GAAG;YAAE,OAAO,KAAK,GAAG,OAAO,KAAK,CAAC,CAAA;IAChD,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACnD,OAAO,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG,CAAA;IACvC,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,KAAK,CAAA;AAClC,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type CronJob = {
2
+ name: string;
3
+ schedule: string;
4
+ handler: () => Promise<void> | void;
5
+ last?: string | null;
6
+ };
7
+ export declare const jobs: CronJob[];
8
+ export declare const interval: number;
package/dist/state.js ADDED
@@ -0,0 +1,3 @@
1
+ export const jobs = [];
2
+ export const interval = Number(process.env.CRON_INTERVAL) || 10000;
3
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,IAAI,GAAgB,EAAE,CAAA;AACnC,MAAM,CAAC,MAAM,QAAQ,GAAY,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,KAAK,CAAA"}
@@ -0,0 +1 @@
1
+ export declare const worker: () => void;
package/dist/worker.js ADDED
@@ -0,0 +1,25 @@
1
+ import { logger } from "@skalfa/skalfa-api-core";
2
+ import { jobs, interval } from "./state";
3
+ import { isScheduleMatch } from "./is-schedule-match";
4
+ export const worker = () => {
5
+ setInterval(async () => {
6
+ const now = new Date();
7
+ const minuteKey = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ` + `${now.getHours()}:${now.getMinutes()}`;
8
+ for (const job of jobs) {
9
+ if (!isScheduleMatch(job.schedule, now))
10
+ continue;
11
+ if (job.last === minuteKey)
12
+ continue;
13
+ job.last = minuteKey;
14
+ try {
15
+ await job.handler();
16
+ logger.cron(`${job.name} at (${now}) success!`);
17
+ }
18
+ catch (err) {
19
+ const em = err instanceof Error ? err.message : String(err);
20
+ logger.cronError(`${job.name} at (${now}) error : ${em}`, { error: em, reference: job.name, at: minuteKey });
21
+ }
22
+ }
23
+ }, interval);
24
+ };
25
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,EAAE;IACzB,WAAW,CAAC,KAAK,IAAI,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;QAEtB,MAAM,SAAS,GAAG,GAAG,GAAG,CAAC,WAAW,EAAE,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,UAAU,EAAE,EAAE,CAAA;QAE1H,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC;gBAAE,SAAQ;YAEjD,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;gBAAE,SAAQ;YAEpC,GAAG,CAAC,IAAI,GAAG,SAAS,CAAA;YAEpB,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,OAAO,EAAE,CAAA;gBACnB,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,QAAQ,GAAG,YAAY,CAAC,CAAA;YACjD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,EAAE,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;gBAC3D,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,IAAI,QAAQ,GAAG,aAAa,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;YAC/G,CAAC;QACH,CAAC;IACH,CAAC,EAAE,QAAQ,CAAC,CAAA;AACd,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@skalfa/skalfa-cron",
3
- "version": "1.0.0",
4
- "description": "Cron utility package for Skalfa API framework.",
3
+ "version": "1.0.3",
4
+ "description": "Automated background cron job scheduling and task worker utility extension for Skalfa backend services.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
@@ -15,7 +15,7 @@
15
15
  "author": "",
16
16
  "license": "UNLICENSED",
17
17
  "dependencies": {
18
- "@skalfa/skalfa-api-core": "file:../skalfa-api-core"
18
+ "@skalfa/skalfa-api-core": "^1.0.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/node": "^26.0.0",
package/src/add.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { jobs, CronJob } from "./state";
2
+
3
+ export const add = (
4
+ schedule : string,
5
+ handler : CronJob["handler"],
6
+ jobName ?: string
7
+ ) => {
8
+ const name = jobName || `Cronjob(${jobs.length + 1})`
9
+ jobs.push({ name, schedule, handler, last: null })
10
+ };
package/src/index.ts CHANGED
@@ -1,97 +1,28 @@
1
- import { logger } from "@skalfa/skalfa-api-core";
1
+ import { add } from "./add";
2
+ import { worker } from "./worker";
3
+ import { parseSchedule } from "./parse-schedule";
4
+ import { isScheduleMatch } from "./is-schedule-match";
2
5
 
3
- export type CronJob = {
4
- name : string
5
- schedule : string
6
- handler : () => Promise<void> | void
7
- last ?: string | null
8
- }
9
-
10
- const jobs: CronJob[] = []
11
- const interval = Number(process.env.CRON_INTERVAL) || 10000
6
+ export type { CronJob } from "./state";
12
7
 
13
8
  export const cron = {
14
9
  // ===========================>
15
10
  // ## Cron: add new job
16
11
  // ===========================>
17
- add: (
18
- schedule : string,
19
- handler : CronJob["handler"],
20
- jobName ?: string
21
- ) => {
22
- const name = jobName || `Cronjob(${jobs.length + 1})`
23
- jobs.push({ name, schedule, handler, last: null })
24
- },
25
-
26
-
12
+ add,
27
13
 
28
14
  // ===========================>
29
15
  // ## Cron: job worker
30
16
  // ===========================>
31
- worker: () => {
32
- setInterval(async () => {
33
- const now = new Date()
34
-
35
- const minuteKey = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ` + `${now.getHours()}:${now.getMinutes()}`
36
-
37
- for (const job of jobs) {
38
- if (!cron.isScheduleMatch(job.schedule, now)) continue
39
-
40
- if (job.last === minuteKey) continue
41
-
42
- job.last = minuteKey
43
-
44
- try {
45
- await job.handler()
46
- logger.cron(`${job.name} at (${now}) success!`)
47
- } catch (err) {
48
- const em = err instanceof Error ? err.message : String(err)
49
- logger.cronError(`${job.name} at (${now}) error : ${em}`, { error: em, reference: job.name, at: minuteKey });
50
- }
51
- }
52
- }, interval)
53
- },
54
-
55
-
17
+ worker,
56
18
 
57
19
  // ===========================>
58
20
  // ## Cron: job schedule parser
59
21
  // ===========================>
60
- parseSchedule(value: number, pattern: string): boolean {
61
- if (pattern === "*") return true
62
-
63
- if (pattern.includes(",")) {
64
- return pattern.split(",").some((p) => this.parseSchedule(value, p))
65
- }
66
-
67
- if (pattern.includes("/")) {
68
- const [base, step] = pattern.split("/")
69
- const stepNum = Number(step)
70
- if (base === "*") return value % stepNum === 0
71
- }
72
-
73
- if (pattern.includes("-")) {
74
- const [start, end] = pattern.split("-").map(Number)
75
- return value >= start && value <= end
76
- }
77
-
78
- return Number(pattern) === value
79
- },
80
-
81
-
22
+ parseSchedule,
82
23
 
83
24
  // ===========================>
84
25
  // ## Cron: check job schedule
85
26
  // ===========================>
86
- isScheduleMatch(schedule: string, date: Date): boolean {
87
- const [min, hour, day, month, week] = schedule.split(" ")
88
-
89
- return (
90
- this.parseSchedule(date.getMinutes(), min) &&
91
- this.parseSchedule(date.getHours(), hour) &&
92
- this.parseSchedule(date.getDate(), day) &&
93
- this.parseSchedule(date.getMonth() + 1, month) &&
94
- this.parseSchedule(date.getDay(), week)
95
- )
96
- },
27
+ isScheduleMatch,
97
28
  }
@@ -0,0 +1,13 @@
1
+ import { parseSchedule } from "./parse-schedule";
2
+
3
+ export function isScheduleMatch(schedule: string, date: Date): boolean {
4
+ const [min, hour, day, month, week] = schedule.split(" ")
5
+
6
+ return (
7
+ parseSchedule(date.getMinutes(), min) &&
8
+ parseSchedule(date.getHours(), hour) &&
9
+ parseSchedule(date.getDate(), day) &&
10
+ parseSchedule(date.getMonth() + 1, month) &&
11
+ parseSchedule(date.getDay(), week)
12
+ )
13
+ }
@@ -0,0 +1,20 @@
1
+ export function parseSchedule(value: number, pattern: string): boolean {
2
+ if (pattern === "*") return true
3
+
4
+ if (pattern.includes(",")) {
5
+ return pattern.split(",").some((p) => parseSchedule(value, p))
6
+ }
7
+
8
+ if (pattern.includes("/")) {
9
+ const [base, step] = pattern.split("/")
10
+ const stepNum = Number(step)
11
+ if (base === "*") return value % stepNum === 0
12
+ }
13
+
14
+ if (pattern.includes("-")) {
15
+ const [start, end] = pattern.split("-").map(Number)
16
+ return value >= start && value <= end
17
+ }
18
+
19
+ return Number(pattern) === value
20
+ }
package/src/state.ts ADDED
@@ -0,0 +1,9 @@
1
+ export type CronJob = {
2
+ name : string
3
+ schedule : string
4
+ handler : () => Promise<void> | void
5
+ last ?: string | null
6
+ }
7
+
8
+ export const jobs: CronJob[] = []
9
+ export const interval = Number(process.env.CRON_INTERVAL) || 10000
package/src/worker.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { logger } from "@skalfa/skalfa-api-core";
2
+ import { jobs, interval } from "./state";
3
+ import { isScheduleMatch } from "./is-schedule-match";
4
+
5
+ export const worker = () => {
6
+ setInterval(async () => {
7
+ const now = new Date()
8
+
9
+ const minuteKey = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ` + `${now.getHours()}:${now.getMinutes()}`
10
+
11
+ for (const job of jobs) {
12
+ if (!isScheduleMatch(job.schedule, now)) continue
13
+
14
+ if (job.last === minuteKey) continue
15
+
16
+ job.last = minuteKey
17
+
18
+ try {
19
+ await job.handler()
20
+ logger.cron(`${job.name} at (${now}) success!`)
21
+ } catch (err) {
22
+ const em = err instanceof Error ? err.message : String(err)
23
+ logger.cronError(`${job.name} at (${now}) error : ${em}`, { error: em, reference: job.name, at: minuteKey });
24
+ }
25
+ }
26
+ }, interval)
27
+ };
package/bun.lock DELETED
@@ -1,82 +0,0 @@
1
- {
2
- "lockfileVersion": 1,
3
- "workspaces": {
4
- "": {
5
- "name": "@skalfa/skalfa-cron",
6
- "dependencies": {
7
- "@skalfa/skalfa-api-core": "file:../skalfa-api-core",
8
- },
9
- "devDependencies": {
10
- "@types/node": "^26.0.0",
11
- "typescript": "^6.0.3",
12
- },
13
- },
14
- },
15
- "packages": {
16
- "@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="],
17
-
18
- "@sinclair/typebox": ["@sinclair/typebox@0.34.49", "", {}, "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A=="],
19
-
20
- "@skalfa/skalfa-api-core": ["@skalfa/skalfa-api-core@file:../skalfa-api-core", { "dependencies": { "@skalfa/skalfa-orm": "file:../skalfa-orm", "bcrypt": "^6.0.0", "commander": "^12.1.0", "dotenv": "^17.2.2", "elysia": "latest", "nodemailer": "^7.0.9", "validator": "^13.15.15" }, "devDependencies": { "@types/bcrypt": "^6.0.0", "@types/node": "^26.0.0", "@types/nodemailer": "^7.0.2", "@types/validator": "^13.15.3", "bun-types": "latest", "typescript": "^6.0.3" } }],
21
-
22
- "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
23
-
24
- "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
25
-
26
- "@types/bcrypt": ["@types/bcrypt@6.0.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ=="],
27
-
28
- "@types/node": ["@types/node@26.0.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA=="],
29
-
30
- "@types/nodemailer": ["@types/nodemailer@7.0.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-80vKwiIsVSyFA1rRovH59jNPLBOuc6dRZIHEu40gXTkBkZnQv8vog1xSGEb9j5q/tdMAs5ivvDR2pLTU0hGHXA=="],
31
-
32
- "@types/validator": ["@types/validator@13.15.10", "", {}, "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA=="],
33
-
34
- "bcrypt": ["bcrypt@6.0.0", "", { "dependencies": { "node-addon-api": "^8.3.0", "node-gyp-build": "^4.8.4" } }, "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg=="],
35
-
36
- "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
37
-
38
- "commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
39
-
40
- "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
41
-
42
- "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
43
-
44
- "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
45
-
46
- "elysia": ["elysia@1.4.29", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-GwMRGGwSdjfPt+w3LA0fqTuYJtS8uVRJicvoar98/HrO5qdFKDc9CwjIb6Kja+v39lkY+58hr2JvdR9jQzlUuA=="],
47
-
48
- "exact-mirror": ["exact-mirror@0.2.7", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg=="],
49
-
50
- "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
51
-
52
- "file-type": ["file-type@22.0.1", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.5", "token-types": "^6.1.2", "uint8array-extras": "^1.5.0" } }, "sha512-ww5Mhre0EE+jmBvOXTmXAbEMuZE7uX4a3+oRCQFNj8w++g3ev913N6tXQz0XTXbueQ5TWQfm6BdaViEHHn8bhA=="],
53
-
54
- "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
55
-
56
- "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
57
-
58
- "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
59
-
60
- "node-addon-api": ["node-addon-api@8.8.0", "", {}, "sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA=="],
61
-
62
- "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
63
-
64
- "nodemailer": ["nodemailer@7.0.13", "", {}, "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw=="],
65
-
66
- "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
67
-
68
- "strtok3": ["strtok3@10.3.5", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA=="],
69
-
70
- "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
71
-
72
- "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
73
-
74
- "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
75
-
76
- "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
77
-
78
- "validator": ["validator@13.15.35", "", {}, "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw=="],
79
-
80
- "@skalfa/skalfa-api-core/@skalfa/skalfa-orm": ["@skalfa/skalfa-orm@file:..\\skalfa-orm", {}],
81
- }
82
- }