@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 +82 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/package.json +24 -0
- package/src/index.ts +97 -0
- package/tsconfig.json +19 -0
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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|