@skalfa/skalfa-cron 1.0.0

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.
package/bun.lock ADDED
@@ -0,0 +1,82 @@
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
+ }
@@ -0,0 +1,12 @@
1
+ export type CronJob = {
2
+ name: string;
3
+ schedule: string;
4
+ handler: () => Promise<void> | void;
5
+ last?: string | null;
6
+ };
7
+ export declare const cron: {
8
+ add: (schedule: string, handler: CronJob["handler"], jobName?: string) => void;
9
+ worker: () => void;
10
+ parseSchedule(value: number, pattern: string): boolean;
11
+ isScheduleMatch(schedule: string, date: Date): boolean;
12
+ };
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ import { logger } from "@skalfa/skalfa-api-core";
2
+ const jobs = [];
3
+ const interval = Number(process.env.CRON_INTERVAL) || 10000;
4
+ export const cron = {
5
+ // ===========================>
6
+ // ## Cron: add new job
7
+ // ===========================>
8
+ add: (schedule, handler, jobName) => {
9
+ const name = jobName || `Cronjob(${jobs.length + 1})`;
10
+ jobs.push({ name, schedule, handler, last: null });
11
+ },
12
+ // ===========================>
13
+ // ## Cron: job worker
14
+ // ===========================>
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
+ },
36
+ // ===========================>
37
+ // ## Cron: job schedule parser
38
+ // ===========================>
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
+ },
57
+ // ===========================>
58
+ // ## Cron: check job schedule
59
+ // ===========================>
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
+ },
68
+ };
69
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@skalfa/skalfa-cron",
3
+ "version": "1.0.0",
4
+ "description": "Cron utility package for Skalfa API framework.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc --ignoreDeprecations 6.0"
9
+ },
10
+ "keywords": [
11
+ "aluna",
12
+ "cron",
13
+ "skalfa"
14
+ ],
15
+ "author": "",
16
+ "license": "UNLICENSED",
17
+ "dependencies": {
18
+ "@skalfa/skalfa-api-core": "file:../skalfa-api-core"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^26.0.0",
22
+ "typescript": "^6.0.3"
23
+ }
24
+ }
package/src/index.ts ADDED
@@ -0,0 +1,97 @@
1
+ import { logger } from "@skalfa/skalfa-api-core";
2
+
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
12
+
13
+ export const cron = {
14
+ // ===========================>
15
+ // ## Cron: add new job
16
+ // ===========================>
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
+
27
+
28
+ // ===========================>
29
+ // ## Cron: job worker
30
+ // ===========================>
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
+
56
+
57
+ // ===========================>
58
+ // ## Cron: job schedule parser
59
+ // ===========================>
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
+
82
+
83
+ // ===========================>
84
+ // ## Cron: check job schedule
85
+ // ===========================>
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
+ },
97
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "ignoreDeprecations": "5.0",
4
+ "target": "ES2021",
5
+ "module": "ES2022",
6
+ "moduleResolution": "node",
7
+ "declaration": true,
8
+ "sourceMap": true,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "strict": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "baseUrl": ".",
16
+ "types": ["node"]
17
+ },
18
+ "include": ["src/**/*"]
19
+ }