@catlabtech/mycal-core 0.1.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/dist/index.d.ts +803 -0
- package/dist/index.js +517 -0
- package/package.json +49 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
interface LocalizedString {
|
|
4
|
+
readonly ms: string;
|
|
5
|
+
readonly en: string;
|
|
6
|
+
readonly zh?: string;
|
|
7
|
+
}
|
|
8
|
+
type HolidayType = "federal" | "state" | "islamic" | "islamic_state" | "replacement" | "adhoc";
|
|
9
|
+
type HolidayStatus = "confirmed" | "tentative" | "announced" | "cancelled";
|
|
10
|
+
type GazetteLevel = "P" | "N";
|
|
11
|
+
type HolidaySource = "jpm" | "jakim" | "state-gov" | "community" | "admin";
|
|
12
|
+
interface Holiday {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly date: string;
|
|
15
|
+
readonly endDate?: string;
|
|
16
|
+
readonly name: LocalizedString;
|
|
17
|
+
readonly type: HolidayType;
|
|
18
|
+
readonly status: HolidayStatus;
|
|
19
|
+
readonly states: readonly string[];
|
|
20
|
+
readonly isPublicHoliday: boolean;
|
|
21
|
+
readonly gazetteLevel: GazetteLevel;
|
|
22
|
+
readonly isReplacementFor?: string;
|
|
23
|
+
readonly hijriDate?: string;
|
|
24
|
+
readonly gazetteRef?: string;
|
|
25
|
+
readonly source: HolidaySource;
|
|
26
|
+
readonly confirmedAt?: string;
|
|
27
|
+
readonly createdAt: string;
|
|
28
|
+
readonly updatedAt: string;
|
|
29
|
+
}
|
|
30
|
+
type StateGroup = "A" | "B";
|
|
31
|
+
interface WeekendConfig {
|
|
32
|
+
readonly from: string;
|
|
33
|
+
readonly to: string | null;
|
|
34
|
+
readonly weekendDays: readonly number[];
|
|
35
|
+
readonly group: StateGroup;
|
|
36
|
+
}
|
|
37
|
+
type StateType = "state" | "federal_territory";
|
|
38
|
+
interface State {
|
|
39
|
+
readonly code: string;
|
|
40
|
+
readonly aliases: readonly string[];
|
|
41
|
+
readonly name: LocalizedString;
|
|
42
|
+
readonly type: StateType;
|
|
43
|
+
readonly weekendHistory: readonly WeekendConfig[];
|
|
44
|
+
readonly group: StateGroup;
|
|
45
|
+
}
|
|
46
|
+
interface SchoolTermSegment {
|
|
47
|
+
readonly startDate: string;
|
|
48
|
+
readonly endDate: string;
|
|
49
|
+
readonly schoolDays: number;
|
|
50
|
+
}
|
|
51
|
+
interface SchoolTerm {
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly year: number;
|
|
54
|
+
readonly term: 1 | 2;
|
|
55
|
+
readonly group: StateGroup;
|
|
56
|
+
readonly segments: readonly SchoolTermSegment[];
|
|
57
|
+
readonly totalSchoolDays: number;
|
|
58
|
+
readonly startDate: string;
|
|
59
|
+
readonly endDate: string;
|
|
60
|
+
}
|
|
61
|
+
type SchoolHolidayType = "cuti_penggal_1" | "cuti_pertengahan" | "cuti_penggal_2" | "cuti_akhir" | "cuti_perayaan_kpm";
|
|
62
|
+
interface SchoolHoliday {
|
|
63
|
+
readonly id: string;
|
|
64
|
+
readonly year: number;
|
|
65
|
+
readonly group: StateGroup;
|
|
66
|
+
readonly type: SchoolHolidayType;
|
|
67
|
+
readonly name: LocalizedString;
|
|
68
|
+
readonly startDate: string;
|
|
69
|
+
readonly endDate: string;
|
|
70
|
+
readonly days: number;
|
|
71
|
+
readonly states?: readonly string[];
|
|
72
|
+
readonly excludeStates?: readonly string[];
|
|
73
|
+
readonly remarks?: string;
|
|
74
|
+
}
|
|
75
|
+
type ExamType = "spm" | "stpm" | "muet" | "pt3" | "stam" | "other";
|
|
76
|
+
type ExamSource = "kpm" | "mpm";
|
|
77
|
+
interface Exam {
|
|
78
|
+
readonly id: string;
|
|
79
|
+
readonly year: number;
|
|
80
|
+
readonly name: string;
|
|
81
|
+
readonly fullName: LocalizedString;
|
|
82
|
+
readonly type: ExamType;
|
|
83
|
+
readonly startDate: string;
|
|
84
|
+
readonly endDate?: string;
|
|
85
|
+
readonly status: "confirmed" | "tentative";
|
|
86
|
+
readonly resultsDate?: string;
|
|
87
|
+
readonly source: ExamSource;
|
|
88
|
+
}
|
|
89
|
+
interface CheckDateResult {
|
|
90
|
+
readonly date: string;
|
|
91
|
+
readonly dayOfWeek: string;
|
|
92
|
+
readonly isHoliday: boolean;
|
|
93
|
+
readonly isWeekend: boolean;
|
|
94
|
+
readonly isWorkingDay: boolean;
|
|
95
|
+
readonly isSchoolDay: boolean;
|
|
96
|
+
readonly holidays: readonly Holiday[];
|
|
97
|
+
readonly school: {
|
|
98
|
+
readonly group: StateGroup;
|
|
99
|
+
readonly term: SchoolTerm | null;
|
|
100
|
+
readonly holiday: SchoolHoliday | null;
|
|
101
|
+
} | null;
|
|
102
|
+
readonly state: {
|
|
103
|
+
readonly code: string;
|
|
104
|
+
readonly weekendDays: readonly string[];
|
|
105
|
+
readonly group: StateGroup;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
interface BusinessDaysResult {
|
|
109
|
+
readonly totalDays: number;
|
|
110
|
+
readonly businessDays: number;
|
|
111
|
+
readonly holidays: number;
|
|
112
|
+
readonly weekendDays: number;
|
|
113
|
+
readonly holidayList: readonly Holiday[];
|
|
114
|
+
}
|
|
115
|
+
interface LongWeekend {
|
|
116
|
+
readonly startDate: string;
|
|
117
|
+
readonly endDate: string;
|
|
118
|
+
readonly totalDays: number;
|
|
119
|
+
readonly holidays: readonly Holiday[];
|
|
120
|
+
readonly weekendDays: number;
|
|
121
|
+
readonly bridgeDaysNeeded: number;
|
|
122
|
+
}
|
|
123
|
+
interface ChangelogEntry {
|
|
124
|
+
readonly timestamp: string;
|
|
125
|
+
readonly event: string;
|
|
126
|
+
readonly holidayId?: string;
|
|
127
|
+
readonly examId?: string;
|
|
128
|
+
readonly description: string;
|
|
129
|
+
readonly changes?: Record<string, {
|
|
130
|
+
from: unknown;
|
|
131
|
+
to: unknown;
|
|
132
|
+
}>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
declare const holidaySchema: z.ZodObject<{
|
|
136
|
+
id: z.ZodString;
|
|
137
|
+
date: z.ZodString;
|
|
138
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
139
|
+
name: z.ZodObject<{
|
|
140
|
+
ms: z.ZodString;
|
|
141
|
+
en: z.ZodString;
|
|
142
|
+
zh: z.ZodOptional<z.ZodString>;
|
|
143
|
+
}, "strip", z.ZodTypeAny, {
|
|
144
|
+
ms: string;
|
|
145
|
+
en: string;
|
|
146
|
+
zh?: string | undefined;
|
|
147
|
+
}, {
|
|
148
|
+
ms: string;
|
|
149
|
+
en: string;
|
|
150
|
+
zh?: string | undefined;
|
|
151
|
+
}>;
|
|
152
|
+
type: z.ZodEnum<["federal", "state", "islamic", "islamic_state", "replacement", "adhoc"]>;
|
|
153
|
+
status: z.ZodEnum<["confirmed", "tentative", "announced", "cancelled"]>;
|
|
154
|
+
states: z.ZodArray<z.ZodString, "many">;
|
|
155
|
+
isPublicHoliday: z.ZodBoolean;
|
|
156
|
+
gazetteLevel: z.ZodEnum<["P", "N"]>;
|
|
157
|
+
isReplacementFor: z.ZodOptional<z.ZodString>;
|
|
158
|
+
hijriDate: z.ZodOptional<z.ZodString>;
|
|
159
|
+
gazetteRef: z.ZodOptional<z.ZodString>;
|
|
160
|
+
source: z.ZodEnum<["jpm", "jakim", "state-gov", "community", "admin"]>;
|
|
161
|
+
confirmedAt: z.ZodOptional<z.ZodString>;
|
|
162
|
+
createdAt: z.ZodString;
|
|
163
|
+
updatedAt: z.ZodString;
|
|
164
|
+
}, "strip", z.ZodTypeAny, {
|
|
165
|
+
type: "federal" | "state" | "islamic" | "islamic_state" | "replacement" | "adhoc";
|
|
166
|
+
status: "confirmed" | "tentative" | "announced" | "cancelled";
|
|
167
|
+
id: string;
|
|
168
|
+
date: string;
|
|
169
|
+
name: {
|
|
170
|
+
ms: string;
|
|
171
|
+
en: string;
|
|
172
|
+
zh?: string | undefined;
|
|
173
|
+
};
|
|
174
|
+
states: string[];
|
|
175
|
+
isPublicHoliday: boolean;
|
|
176
|
+
gazetteLevel: "P" | "N";
|
|
177
|
+
source: "jpm" | "jakim" | "state-gov" | "community" | "admin";
|
|
178
|
+
createdAt: string;
|
|
179
|
+
updatedAt: string;
|
|
180
|
+
endDate?: string | undefined;
|
|
181
|
+
isReplacementFor?: string | undefined;
|
|
182
|
+
hijriDate?: string | undefined;
|
|
183
|
+
gazetteRef?: string | undefined;
|
|
184
|
+
confirmedAt?: string | undefined;
|
|
185
|
+
}, {
|
|
186
|
+
type: "federal" | "state" | "islamic" | "islamic_state" | "replacement" | "adhoc";
|
|
187
|
+
status: "confirmed" | "tentative" | "announced" | "cancelled";
|
|
188
|
+
id: string;
|
|
189
|
+
date: string;
|
|
190
|
+
name: {
|
|
191
|
+
ms: string;
|
|
192
|
+
en: string;
|
|
193
|
+
zh?: string | undefined;
|
|
194
|
+
};
|
|
195
|
+
states: string[];
|
|
196
|
+
isPublicHoliday: boolean;
|
|
197
|
+
gazetteLevel: "P" | "N";
|
|
198
|
+
source: "jpm" | "jakim" | "state-gov" | "community" | "admin";
|
|
199
|
+
createdAt: string;
|
|
200
|
+
updatedAt: string;
|
|
201
|
+
endDate?: string | undefined;
|
|
202
|
+
isReplacementFor?: string | undefined;
|
|
203
|
+
hijriDate?: string | undefined;
|
|
204
|
+
gazetteRef?: string | undefined;
|
|
205
|
+
confirmedAt?: string | undefined;
|
|
206
|
+
}>;
|
|
207
|
+
declare const holidayFileSchema: z.ZodArray<z.ZodObject<{
|
|
208
|
+
id: z.ZodString;
|
|
209
|
+
date: z.ZodString;
|
|
210
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
211
|
+
name: z.ZodObject<{
|
|
212
|
+
ms: z.ZodString;
|
|
213
|
+
en: z.ZodString;
|
|
214
|
+
zh: z.ZodOptional<z.ZodString>;
|
|
215
|
+
}, "strip", z.ZodTypeAny, {
|
|
216
|
+
ms: string;
|
|
217
|
+
en: string;
|
|
218
|
+
zh?: string | undefined;
|
|
219
|
+
}, {
|
|
220
|
+
ms: string;
|
|
221
|
+
en: string;
|
|
222
|
+
zh?: string | undefined;
|
|
223
|
+
}>;
|
|
224
|
+
type: z.ZodEnum<["federal", "state", "islamic", "islamic_state", "replacement", "adhoc"]>;
|
|
225
|
+
status: z.ZodEnum<["confirmed", "tentative", "announced", "cancelled"]>;
|
|
226
|
+
states: z.ZodArray<z.ZodString, "many">;
|
|
227
|
+
isPublicHoliday: z.ZodBoolean;
|
|
228
|
+
gazetteLevel: z.ZodEnum<["P", "N"]>;
|
|
229
|
+
isReplacementFor: z.ZodOptional<z.ZodString>;
|
|
230
|
+
hijriDate: z.ZodOptional<z.ZodString>;
|
|
231
|
+
gazetteRef: z.ZodOptional<z.ZodString>;
|
|
232
|
+
source: z.ZodEnum<["jpm", "jakim", "state-gov", "community", "admin"]>;
|
|
233
|
+
confirmedAt: z.ZodOptional<z.ZodString>;
|
|
234
|
+
createdAt: z.ZodString;
|
|
235
|
+
updatedAt: z.ZodString;
|
|
236
|
+
}, "strip", z.ZodTypeAny, {
|
|
237
|
+
type: "federal" | "state" | "islamic" | "islamic_state" | "replacement" | "adhoc";
|
|
238
|
+
status: "confirmed" | "tentative" | "announced" | "cancelled";
|
|
239
|
+
id: string;
|
|
240
|
+
date: string;
|
|
241
|
+
name: {
|
|
242
|
+
ms: string;
|
|
243
|
+
en: string;
|
|
244
|
+
zh?: string | undefined;
|
|
245
|
+
};
|
|
246
|
+
states: string[];
|
|
247
|
+
isPublicHoliday: boolean;
|
|
248
|
+
gazetteLevel: "P" | "N";
|
|
249
|
+
source: "jpm" | "jakim" | "state-gov" | "community" | "admin";
|
|
250
|
+
createdAt: string;
|
|
251
|
+
updatedAt: string;
|
|
252
|
+
endDate?: string | undefined;
|
|
253
|
+
isReplacementFor?: string | undefined;
|
|
254
|
+
hijriDate?: string | undefined;
|
|
255
|
+
gazetteRef?: string | undefined;
|
|
256
|
+
confirmedAt?: string | undefined;
|
|
257
|
+
}, {
|
|
258
|
+
type: "federal" | "state" | "islamic" | "islamic_state" | "replacement" | "adhoc";
|
|
259
|
+
status: "confirmed" | "tentative" | "announced" | "cancelled";
|
|
260
|
+
id: string;
|
|
261
|
+
date: string;
|
|
262
|
+
name: {
|
|
263
|
+
ms: string;
|
|
264
|
+
en: string;
|
|
265
|
+
zh?: string | undefined;
|
|
266
|
+
};
|
|
267
|
+
states: string[];
|
|
268
|
+
isPublicHoliday: boolean;
|
|
269
|
+
gazetteLevel: "P" | "N";
|
|
270
|
+
source: "jpm" | "jakim" | "state-gov" | "community" | "admin";
|
|
271
|
+
createdAt: string;
|
|
272
|
+
updatedAt: string;
|
|
273
|
+
endDate?: string | undefined;
|
|
274
|
+
isReplacementFor?: string | undefined;
|
|
275
|
+
hijriDate?: string | undefined;
|
|
276
|
+
gazetteRef?: string | undefined;
|
|
277
|
+
confirmedAt?: string | undefined;
|
|
278
|
+
}>, "many">;
|
|
279
|
+
declare const stateSchema: z.ZodObject<{
|
|
280
|
+
code: z.ZodString;
|
|
281
|
+
aliases: z.ZodArray<z.ZodString, "many">;
|
|
282
|
+
name: z.ZodObject<{
|
|
283
|
+
ms: z.ZodString;
|
|
284
|
+
en: z.ZodString;
|
|
285
|
+
zh: z.ZodOptional<z.ZodString>;
|
|
286
|
+
}, "strip", z.ZodTypeAny, {
|
|
287
|
+
ms: string;
|
|
288
|
+
en: string;
|
|
289
|
+
zh?: string | undefined;
|
|
290
|
+
}, {
|
|
291
|
+
ms: string;
|
|
292
|
+
en: string;
|
|
293
|
+
zh?: string | undefined;
|
|
294
|
+
}>;
|
|
295
|
+
type: z.ZodEnum<["state", "federal_territory"]>;
|
|
296
|
+
weekendHistory: z.ZodArray<z.ZodObject<{
|
|
297
|
+
from: z.ZodString;
|
|
298
|
+
to: z.ZodNullable<z.ZodString>;
|
|
299
|
+
weekendDays: z.ZodArray<z.ZodNumber, "many">;
|
|
300
|
+
group: z.ZodEnum<["A", "B"]>;
|
|
301
|
+
}, "strip", z.ZodTypeAny, {
|
|
302
|
+
from: string;
|
|
303
|
+
to: string | null;
|
|
304
|
+
weekendDays: number[];
|
|
305
|
+
group: "A" | "B";
|
|
306
|
+
}, {
|
|
307
|
+
from: string;
|
|
308
|
+
to: string | null;
|
|
309
|
+
weekendDays: number[];
|
|
310
|
+
group: "A" | "B";
|
|
311
|
+
}>, "many">;
|
|
312
|
+
group: z.ZodEnum<["A", "B"]>;
|
|
313
|
+
}, "strip", z.ZodTypeAny, {
|
|
314
|
+
code: string;
|
|
315
|
+
type: "state" | "federal_territory";
|
|
316
|
+
name: {
|
|
317
|
+
ms: string;
|
|
318
|
+
en: string;
|
|
319
|
+
zh?: string | undefined;
|
|
320
|
+
};
|
|
321
|
+
group: "A" | "B";
|
|
322
|
+
aliases: string[];
|
|
323
|
+
weekendHistory: {
|
|
324
|
+
from: string;
|
|
325
|
+
to: string | null;
|
|
326
|
+
weekendDays: number[];
|
|
327
|
+
group: "A" | "B";
|
|
328
|
+
}[];
|
|
329
|
+
}, {
|
|
330
|
+
code: string;
|
|
331
|
+
type: "state" | "federal_territory";
|
|
332
|
+
name: {
|
|
333
|
+
ms: string;
|
|
334
|
+
en: string;
|
|
335
|
+
zh?: string | undefined;
|
|
336
|
+
};
|
|
337
|
+
group: "A" | "B";
|
|
338
|
+
aliases: string[];
|
|
339
|
+
weekendHistory: {
|
|
340
|
+
from: string;
|
|
341
|
+
to: string | null;
|
|
342
|
+
weekendDays: number[];
|
|
343
|
+
group: "A" | "B";
|
|
344
|
+
}[];
|
|
345
|
+
}>;
|
|
346
|
+
declare const statesFileSchema: z.ZodArray<z.ZodObject<{
|
|
347
|
+
code: z.ZodString;
|
|
348
|
+
aliases: z.ZodArray<z.ZodString, "many">;
|
|
349
|
+
name: z.ZodObject<{
|
|
350
|
+
ms: z.ZodString;
|
|
351
|
+
en: z.ZodString;
|
|
352
|
+
zh: z.ZodOptional<z.ZodString>;
|
|
353
|
+
}, "strip", z.ZodTypeAny, {
|
|
354
|
+
ms: string;
|
|
355
|
+
en: string;
|
|
356
|
+
zh?: string | undefined;
|
|
357
|
+
}, {
|
|
358
|
+
ms: string;
|
|
359
|
+
en: string;
|
|
360
|
+
zh?: string | undefined;
|
|
361
|
+
}>;
|
|
362
|
+
type: z.ZodEnum<["state", "federal_territory"]>;
|
|
363
|
+
weekendHistory: z.ZodArray<z.ZodObject<{
|
|
364
|
+
from: z.ZodString;
|
|
365
|
+
to: z.ZodNullable<z.ZodString>;
|
|
366
|
+
weekendDays: z.ZodArray<z.ZodNumber, "many">;
|
|
367
|
+
group: z.ZodEnum<["A", "B"]>;
|
|
368
|
+
}, "strip", z.ZodTypeAny, {
|
|
369
|
+
from: string;
|
|
370
|
+
to: string | null;
|
|
371
|
+
weekendDays: number[];
|
|
372
|
+
group: "A" | "B";
|
|
373
|
+
}, {
|
|
374
|
+
from: string;
|
|
375
|
+
to: string | null;
|
|
376
|
+
weekendDays: number[];
|
|
377
|
+
group: "A" | "B";
|
|
378
|
+
}>, "many">;
|
|
379
|
+
group: z.ZodEnum<["A", "B"]>;
|
|
380
|
+
}, "strip", z.ZodTypeAny, {
|
|
381
|
+
code: string;
|
|
382
|
+
type: "state" | "federal_territory";
|
|
383
|
+
name: {
|
|
384
|
+
ms: string;
|
|
385
|
+
en: string;
|
|
386
|
+
zh?: string | undefined;
|
|
387
|
+
};
|
|
388
|
+
group: "A" | "B";
|
|
389
|
+
aliases: string[];
|
|
390
|
+
weekendHistory: {
|
|
391
|
+
from: string;
|
|
392
|
+
to: string | null;
|
|
393
|
+
weekendDays: number[];
|
|
394
|
+
group: "A" | "B";
|
|
395
|
+
}[];
|
|
396
|
+
}, {
|
|
397
|
+
code: string;
|
|
398
|
+
type: "state" | "federal_territory";
|
|
399
|
+
name: {
|
|
400
|
+
ms: string;
|
|
401
|
+
en: string;
|
|
402
|
+
zh?: string | undefined;
|
|
403
|
+
};
|
|
404
|
+
group: "A" | "B";
|
|
405
|
+
aliases: string[];
|
|
406
|
+
weekendHistory: {
|
|
407
|
+
from: string;
|
|
408
|
+
to: string | null;
|
|
409
|
+
weekendDays: number[];
|
|
410
|
+
group: "A" | "B";
|
|
411
|
+
}[];
|
|
412
|
+
}>, "many">;
|
|
413
|
+
declare const schoolTermSchema: z.ZodObject<{
|
|
414
|
+
id: z.ZodString;
|
|
415
|
+
year: z.ZodNumber;
|
|
416
|
+
term: z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>]>;
|
|
417
|
+
group: z.ZodEnum<["A", "B"]>;
|
|
418
|
+
segments: z.ZodArray<z.ZodObject<{
|
|
419
|
+
startDate: z.ZodString;
|
|
420
|
+
endDate: z.ZodString;
|
|
421
|
+
schoolDays: z.ZodNumber;
|
|
422
|
+
}, "strip", z.ZodTypeAny, {
|
|
423
|
+
endDate: string;
|
|
424
|
+
startDate: string;
|
|
425
|
+
schoolDays: number;
|
|
426
|
+
}, {
|
|
427
|
+
endDate: string;
|
|
428
|
+
startDate: string;
|
|
429
|
+
schoolDays: number;
|
|
430
|
+
}>, "many">;
|
|
431
|
+
totalSchoolDays: z.ZodNumber;
|
|
432
|
+
startDate: z.ZodString;
|
|
433
|
+
endDate: z.ZodString;
|
|
434
|
+
}, "strip", z.ZodTypeAny, {
|
|
435
|
+
id: string;
|
|
436
|
+
endDate: string;
|
|
437
|
+
group: "A" | "B";
|
|
438
|
+
startDate: string;
|
|
439
|
+
year: number;
|
|
440
|
+
term: 1 | 2;
|
|
441
|
+
segments: {
|
|
442
|
+
endDate: string;
|
|
443
|
+
startDate: string;
|
|
444
|
+
schoolDays: number;
|
|
445
|
+
}[];
|
|
446
|
+
totalSchoolDays: number;
|
|
447
|
+
}, {
|
|
448
|
+
id: string;
|
|
449
|
+
endDate: string;
|
|
450
|
+
group: "A" | "B";
|
|
451
|
+
startDate: string;
|
|
452
|
+
year: number;
|
|
453
|
+
term: 1 | 2;
|
|
454
|
+
segments: {
|
|
455
|
+
endDate: string;
|
|
456
|
+
startDate: string;
|
|
457
|
+
schoolDays: number;
|
|
458
|
+
}[];
|
|
459
|
+
totalSchoolDays: number;
|
|
460
|
+
}>;
|
|
461
|
+
declare const schoolHolidaySchema: z.ZodObject<{
|
|
462
|
+
id: z.ZodString;
|
|
463
|
+
year: z.ZodNumber;
|
|
464
|
+
group: z.ZodEnum<["A", "B"]>;
|
|
465
|
+
type: z.ZodEnum<["cuti_penggal_1", "cuti_pertengahan", "cuti_penggal_2", "cuti_akhir", "cuti_perayaan_kpm"]>;
|
|
466
|
+
name: z.ZodObject<{
|
|
467
|
+
ms: z.ZodString;
|
|
468
|
+
en: z.ZodString;
|
|
469
|
+
zh: z.ZodOptional<z.ZodString>;
|
|
470
|
+
}, "strip", z.ZodTypeAny, {
|
|
471
|
+
ms: string;
|
|
472
|
+
en: string;
|
|
473
|
+
zh?: string | undefined;
|
|
474
|
+
}, {
|
|
475
|
+
ms: string;
|
|
476
|
+
en: string;
|
|
477
|
+
zh?: string | undefined;
|
|
478
|
+
}>;
|
|
479
|
+
startDate: z.ZodString;
|
|
480
|
+
endDate: z.ZodString;
|
|
481
|
+
days: z.ZodNumber;
|
|
482
|
+
states: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
483
|
+
excludeStates: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
484
|
+
remarks: z.ZodOptional<z.ZodString>;
|
|
485
|
+
}, "strip", z.ZodTypeAny, {
|
|
486
|
+
type: "cuti_penggal_1" | "cuti_pertengahan" | "cuti_penggal_2" | "cuti_akhir" | "cuti_perayaan_kpm";
|
|
487
|
+
id: string;
|
|
488
|
+
endDate: string;
|
|
489
|
+
name: {
|
|
490
|
+
ms: string;
|
|
491
|
+
en: string;
|
|
492
|
+
zh?: string | undefined;
|
|
493
|
+
};
|
|
494
|
+
group: "A" | "B";
|
|
495
|
+
startDate: string;
|
|
496
|
+
year: number;
|
|
497
|
+
days: number;
|
|
498
|
+
states?: string[] | undefined;
|
|
499
|
+
excludeStates?: string[] | undefined;
|
|
500
|
+
remarks?: string | undefined;
|
|
501
|
+
}, {
|
|
502
|
+
type: "cuti_penggal_1" | "cuti_pertengahan" | "cuti_penggal_2" | "cuti_akhir" | "cuti_perayaan_kpm";
|
|
503
|
+
id: string;
|
|
504
|
+
endDate: string;
|
|
505
|
+
name: {
|
|
506
|
+
ms: string;
|
|
507
|
+
en: string;
|
|
508
|
+
zh?: string | undefined;
|
|
509
|
+
};
|
|
510
|
+
group: "A" | "B";
|
|
511
|
+
startDate: string;
|
|
512
|
+
year: number;
|
|
513
|
+
days: number;
|
|
514
|
+
states?: string[] | undefined;
|
|
515
|
+
excludeStates?: string[] | undefined;
|
|
516
|
+
remarks?: string | undefined;
|
|
517
|
+
}>;
|
|
518
|
+
declare const schoolTermsFileSchema: z.ZodArray<z.ZodObject<{
|
|
519
|
+
id: z.ZodString;
|
|
520
|
+
year: z.ZodNumber;
|
|
521
|
+
term: z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>]>;
|
|
522
|
+
group: z.ZodEnum<["A", "B"]>;
|
|
523
|
+
segments: z.ZodArray<z.ZodObject<{
|
|
524
|
+
startDate: z.ZodString;
|
|
525
|
+
endDate: z.ZodString;
|
|
526
|
+
schoolDays: z.ZodNumber;
|
|
527
|
+
}, "strip", z.ZodTypeAny, {
|
|
528
|
+
endDate: string;
|
|
529
|
+
startDate: string;
|
|
530
|
+
schoolDays: number;
|
|
531
|
+
}, {
|
|
532
|
+
endDate: string;
|
|
533
|
+
startDate: string;
|
|
534
|
+
schoolDays: number;
|
|
535
|
+
}>, "many">;
|
|
536
|
+
totalSchoolDays: z.ZodNumber;
|
|
537
|
+
startDate: z.ZodString;
|
|
538
|
+
endDate: z.ZodString;
|
|
539
|
+
}, "strip", z.ZodTypeAny, {
|
|
540
|
+
id: string;
|
|
541
|
+
endDate: string;
|
|
542
|
+
group: "A" | "B";
|
|
543
|
+
startDate: string;
|
|
544
|
+
year: number;
|
|
545
|
+
term: 1 | 2;
|
|
546
|
+
segments: {
|
|
547
|
+
endDate: string;
|
|
548
|
+
startDate: string;
|
|
549
|
+
schoolDays: number;
|
|
550
|
+
}[];
|
|
551
|
+
totalSchoolDays: number;
|
|
552
|
+
}, {
|
|
553
|
+
id: string;
|
|
554
|
+
endDate: string;
|
|
555
|
+
group: "A" | "B";
|
|
556
|
+
startDate: string;
|
|
557
|
+
year: number;
|
|
558
|
+
term: 1 | 2;
|
|
559
|
+
segments: {
|
|
560
|
+
endDate: string;
|
|
561
|
+
startDate: string;
|
|
562
|
+
schoolDays: number;
|
|
563
|
+
}[];
|
|
564
|
+
totalSchoolDays: number;
|
|
565
|
+
}>, "many">;
|
|
566
|
+
declare const schoolHolidaysFileSchema: z.ZodArray<z.ZodObject<{
|
|
567
|
+
id: z.ZodString;
|
|
568
|
+
year: z.ZodNumber;
|
|
569
|
+
group: z.ZodEnum<["A", "B"]>;
|
|
570
|
+
type: z.ZodEnum<["cuti_penggal_1", "cuti_pertengahan", "cuti_penggal_2", "cuti_akhir", "cuti_perayaan_kpm"]>;
|
|
571
|
+
name: z.ZodObject<{
|
|
572
|
+
ms: z.ZodString;
|
|
573
|
+
en: z.ZodString;
|
|
574
|
+
zh: z.ZodOptional<z.ZodString>;
|
|
575
|
+
}, "strip", z.ZodTypeAny, {
|
|
576
|
+
ms: string;
|
|
577
|
+
en: string;
|
|
578
|
+
zh?: string | undefined;
|
|
579
|
+
}, {
|
|
580
|
+
ms: string;
|
|
581
|
+
en: string;
|
|
582
|
+
zh?: string | undefined;
|
|
583
|
+
}>;
|
|
584
|
+
startDate: z.ZodString;
|
|
585
|
+
endDate: z.ZodString;
|
|
586
|
+
days: z.ZodNumber;
|
|
587
|
+
states: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
588
|
+
excludeStates: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
589
|
+
remarks: z.ZodOptional<z.ZodString>;
|
|
590
|
+
}, "strip", z.ZodTypeAny, {
|
|
591
|
+
type: "cuti_penggal_1" | "cuti_pertengahan" | "cuti_penggal_2" | "cuti_akhir" | "cuti_perayaan_kpm";
|
|
592
|
+
id: string;
|
|
593
|
+
endDate: string;
|
|
594
|
+
name: {
|
|
595
|
+
ms: string;
|
|
596
|
+
en: string;
|
|
597
|
+
zh?: string | undefined;
|
|
598
|
+
};
|
|
599
|
+
group: "A" | "B";
|
|
600
|
+
startDate: string;
|
|
601
|
+
year: number;
|
|
602
|
+
days: number;
|
|
603
|
+
states?: string[] | undefined;
|
|
604
|
+
excludeStates?: string[] | undefined;
|
|
605
|
+
remarks?: string | undefined;
|
|
606
|
+
}, {
|
|
607
|
+
type: "cuti_penggal_1" | "cuti_pertengahan" | "cuti_penggal_2" | "cuti_akhir" | "cuti_perayaan_kpm";
|
|
608
|
+
id: string;
|
|
609
|
+
endDate: string;
|
|
610
|
+
name: {
|
|
611
|
+
ms: string;
|
|
612
|
+
en: string;
|
|
613
|
+
zh?: string | undefined;
|
|
614
|
+
};
|
|
615
|
+
group: "A" | "B";
|
|
616
|
+
startDate: string;
|
|
617
|
+
year: number;
|
|
618
|
+
days: number;
|
|
619
|
+
states?: string[] | undefined;
|
|
620
|
+
excludeStates?: string[] | undefined;
|
|
621
|
+
remarks?: string | undefined;
|
|
622
|
+
}>, "many">;
|
|
623
|
+
declare const examSchema: z.ZodObject<{
|
|
624
|
+
id: z.ZodString;
|
|
625
|
+
year: z.ZodNumber;
|
|
626
|
+
name: z.ZodString;
|
|
627
|
+
fullName: z.ZodObject<{
|
|
628
|
+
ms: z.ZodString;
|
|
629
|
+
en: z.ZodString;
|
|
630
|
+
zh: z.ZodOptional<z.ZodString>;
|
|
631
|
+
}, "strip", z.ZodTypeAny, {
|
|
632
|
+
ms: string;
|
|
633
|
+
en: string;
|
|
634
|
+
zh?: string | undefined;
|
|
635
|
+
}, {
|
|
636
|
+
ms: string;
|
|
637
|
+
en: string;
|
|
638
|
+
zh?: string | undefined;
|
|
639
|
+
}>;
|
|
640
|
+
type: z.ZodEnum<["spm", "stpm", "muet", "pt3", "stam", "other"]>;
|
|
641
|
+
startDate: z.ZodString;
|
|
642
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
643
|
+
status: z.ZodEnum<["confirmed", "tentative"]>;
|
|
644
|
+
resultsDate: z.ZodOptional<z.ZodString>;
|
|
645
|
+
source: z.ZodEnum<["kpm", "mpm"]>;
|
|
646
|
+
}, "strip", z.ZodTypeAny, {
|
|
647
|
+
type: "spm" | "stpm" | "muet" | "pt3" | "stam" | "other";
|
|
648
|
+
status: "confirmed" | "tentative";
|
|
649
|
+
id: string;
|
|
650
|
+
name: string;
|
|
651
|
+
source: "kpm" | "mpm";
|
|
652
|
+
startDate: string;
|
|
653
|
+
year: number;
|
|
654
|
+
fullName: {
|
|
655
|
+
ms: string;
|
|
656
|
+
en: string;
|
|
657
|
+
zh?: string | undefined;
|
|
658
|
+
};
|
|
659
|
+
endDate?: string | undefined;
|
|
660
|
+
resultsDate?: string | undefined;
|
|
661
|
+
}, {
|
|
662
|
+
type: "spm" | "stpm" | "muet" | "pt3" | "stam" | "other";
|
|
663
|
+
status: "confirmed" | "tentative";
|
|
664
|
+
id: string;
|
|
665
|
+
name: string;
|
|
666
|
+
source: "kpm" | "mpm";
|
|
667
|
+
startDate: string;
|
|
668
|
+
year: number;
|
|
669
|
+
fullName: {
|
|
670
|
+
ms: string;
|
|
671
|
+
en: string;
|
|
672
|
+
zh?: string | undefined;
|
|
673
|
+
};
|
|
674
|
+
endDate?: string | undefined;
|
|
675
|
+
resultsDate?: string | undefined;
|
|
676
|
+
}>;
|
|
677
|
+
declare const examsFileSchema: z.ZodArray<z.ZodObject<{
|
|
678
|
+
id: z.ZodString;
|
|
679
|
+
year: z.ZodNumber;
|
|
680
|
+
name: z.ZodString;
|
|
681
|
+
fullName: z.ZodObject<{
|
|
682
|
+
ms: z.ZodString;
|
|
683
|
+
en: z.ZodString;
|
|
684
|
+
zh: z.ZodOptional<z.ZodString>;
|
|
685
|
+
}, "strip", z.ZodTypeAny, {
|
|
686
|
+
ms: string;
|
|
687
|
+
en: string;
|
|
688
|
+
zh?: string | undefined;
|
|
689
|
+
}, {
|
|
690
|
+
ms: string;
|
|
691
|
+
en: string;
|
|
692
|
+
zh?: string | undefined;
|
|
693
|
+
}>;
|
|
694
|
+
type: z.ZodEnum<["spm", "stpm", "muet", "pt3", "stam", "other"]>;
|
|
695
|
+
startDate: z.ZodString;
|
|
696
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
697
|
+
status: z.ZodEnum<["confirmed", "tentative"]>;
|
|
698
|
+
resultsDate: z.ZodOptional<z.ZodString>;
|
|
699
|
+
source: z.ZodEnum<["kpm", "mpm"]>;
|
|
700
|
+
}, "strip", z.ZodTypeAny, {
|
|
701
|
+
type: "spm" | "stpm" | "muet" | "pt3" | "stam" | "other";
|
|
702
|
+
status: "confirmed" | "tentative";
|
|
703
|
+
id: string;
|
|
704
|
+
name: string;
|
|
705
|
+
source: "kpm" | "mpm";
|
|
706
|
+
startDate: string;
|
|
707
|
+
year: number;
|
|
708
|
+
fullName: {
|
|
709
|
+
ms: string;
|
|
710
|
+
en: string;
|
|
711
|
+
zh?: string | undefined;
|
|
712
|
+
};
|
|
713
|
+
endDate?: string | undefined;
|
|
714
|
+
resultsDate?: string | undefined;
|
|
715
|
+
}, {
|
|
716
|
+
type: "spm" | "stpm" | "muet" | "pt3" | "stam" | "other";
|
|
717
|
+
status: "confirmed" | "tentative";
|
|
718
|
+
id: string;
|
|
719
|
+
name: string;
|
|
720
|
+
source: "kpm" | "mpm";
|
|
721
|
+
startDate: string;
|
|
722
|
+
year: number;
|
|
723
|
+
fullName: {
|
|
724
|
+
ms: string;
|
|
725
|
+
en: string;
|
|
726
|
+
zh?: string | undefined;
|
|
727
|
+
};
|
|
728
|
+
endDate?: string | undefined;
|
|
729
|
+
resultsDate?: string | undefined;
|
|
730
|
+
}>, "many">;
|
|
731
|
+
|
|
732
|
+
declare function resolveStateCode(query: string, states: readonly State[]): State | null;
|
|
733
|
+
declare function getStateByCode(code: string, states: readonly State[]): State | null;
|
|
734
|
+
declare function getStatesByGroup(group: "A" | "B", states: readonly State[]): readonly State[];
|
|
735
|
+
|
|
736
|
+
declare function getWeekendConfig(state: State, date: string): WeekendConfig;
|
|
737
|
+
declare function isWeekend(date: string, state: State): boolean;
|
|
738
|
+
declare function getDayOfWeekName(date: string): string;
|
|
739
|
+
declare function getWeekendDayNames(state: State, date: string): readonly string[];
|
|
740
|
+
declare function addDays(date: string, days: number): string;
|
|
741
|
+
declare function nextWorkingDay(date: string, state: State, holidayDates: ReadonlySet<string>): string;
|
|
742
|
+
declare function diffDays(start: string, end: string): number;
|
|
743
|
+
|
|
744
|
+
interface HolidayFilter {
|
|
745
|
+
readonly year?: number;
|
|
746
|
+
readonly month?: number;
|
|
747
|
+
readonly state?: string;
|
|
748
|
+
readonly type?: HolidayType;
|
|
749
|
+
readonly status?: HolidayStatus;
|
|
750
|
+
}
|
|
751
|
+
declare function filterHolidays(holidays: readonly Holiday[], filter: HolidayFilter): readonly Holiday[];
|
|
752
|
+
declare function findHolidaysByDate(date: string, holidays: readonly Holiday[], stateCode?: string): readonly Holiday[];
|
|
753
|
+
declare function findNextHoliday(afterDate: string, holidays: readonly Holiday[], stateCode?: string, type?: HolidayType, limit?: number): readonly Holiday[];
|
|
754
|
+
|
|
755
|
+
declare function calculateReplacementHolidays(holidays: readonly Holiday[], state: State): readonly Holiday[];
|
|
756
|
+
|
|
757
|
+
declare function countBusinessDays(start: string, end: string, state: State, holidays: readonly Holiday[]): BusinessDaysResult;
|
|
758
|
+
declare function addBusinessDays(startDate: string, daysToAdd: number, state: State, holidays: readonly Holiday[]): string;
|
|
759
|
+
|
|
760
|
+
declare function findSchoolTermByDate(date: string, terms: readonly SchoolTerm[], group: StateGroup): SchoolTerm | null;
|
|
761
|
+
declare function findSchoolHolidayByDate(date: string, holidays: readonly SchoolHoliday[], group: StateGroup, stateCode?: string): SchoolHoliday | null;
|
|
762
|
+
declare function isSchoolDay(date: string, terms: readonly SchoolTerm[], schoolHolidays: readonly SchoolHoliday[], group: StateGroup, isPublicHoliday: boolean, isWeekendDay: boolean, stateCode?: string): boolean;
|
|
763
|
+
|
|
764
|
+
declare function generateIcal(holidays: readonly Holiday[], schoolHolidays: readonly SchoolHoliday[], calendarName: string): string;
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Basic Hijri date utilities.
|
|
768
|
+
*
|
|
769
|
+
* Note: Hijri dates for Islamic holidays are approximate until confirmed
|
|
770
|
+
* by JAKIM rukyah (moon sighting). This module provides reference mapping
|
|
771
|
+
* only — actual dates should come from the gazette/JAKIM confirmation.
|
|
772
|
+
*/
|
|
773
|
+
interface HijriMonth {
|
|
774
|
+
readonly number: number;
|
|
775
|
+
readonly nameMs: string;
|
|
776
|
+
readonly nameEn: string;
|
|
777
|
+
readonly nameAr: string;
|
|
778
|
+
}
|
|
779
|
+
declare const HIJRI_MONTHS: readonly HijriMonth[];
|
|
780
|
+
/**
|
|
781
|
+
* Parse a hijri date string like "1 Syawal 1447" → { day, month, year }
|
|
782
|
+
*/
|
|
783
|
+
declare function parseHijriDate(hijriStr: string): {
|
|
784
|
+
day: number;
|
|
785
|
+
month: HijriMonth;
|
|
786
|
+
year: number;
|
|
787
|
+
} | null;
|
|
788
|
+
/**
|
|
789
|
+
* Format a hijri date for display: "1 Syawal 1447"
|
|
790
|
+
*/
|
|
791
|
+
declare function formatHijriDate(day: number, monthNumber: number, year: number): string;
|
|
792
|
+
/**
|
|
793
|
+
* Key Islamic holidays and their Hijri dates (fixed in the Islamic calendar).
|
|
794
|
+
* Gregorian dates shift ~11 days earlier each year.
|
|
795
|
+
*/
|
|
796
|
+
declare const ISLAMIC_HOLIDAY_DATES: readonly {
|
|
797
|
+
readonly nameMs: string;
|
|
798
|
+
readonly nameEn: string;
|
|
799
|
+
readonly hijriMonth: number;
|
|
800
|
+
readonly hijriDay: number;
|
|
801
|
+
}[];
|
|
802
|
+
|
|
803
|
+
export { type BusinessDaysResult, type ChangelogEntry, type CheckDateResult, type Exam, type ExamSource, type ExamType, type GazetteLevel, HIJRI_MONTHS, type HijriMonth, type Holiday, type HolidayFilter, type HolidaySource, type HolidayStatus, type HolidayType, ISLAMIC_HOLIDAY_DATES, type LocalizedString, type LongWeekend, type SchoolHoliday, type SchoolHolidayType, type SchoolTerm, type SchoolTermSegment, type State, type StateGroup, type StateType, type WeekendConfig, addBusinessDays, addDays, calculateReplacementHolidays, countBusinessDays, diffDays, examSchema, examsFileSchema, filterHolidays, findHolidaysByDate, findNextHoliday, findSchoolHolidayByDate, findSchoolTermByDate, formatHijriDate, generateIcal, getDayOfWeekName, getStateByCode, getStatesByGroup, getWeekendConfig, getWeekendDayNames, holidayFileSchema, holidaySchema, isSchoolDay, isWeekend, nextWorkingDay, parseHijriDate, resolveStateCode, schoolHolidaySchema, schoolHolidaysFileSchema, schoolTermSchema, schoolTermsFileSchema, stateSchema, statesFileSchema };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
// src/schemas.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var isoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Must be ISO 8601 date (YYYY-MM-DD)");
|
|
4
|
+
var localizedString = z.object({
|
|
5
|
+
ms: z.string().min(1),
|
|
6
|
+
en: z.string().min(1),
|
|
7
|
+
zh: z.string().optional()
|
|
8
|
+
});
|
|
9
|
+
var holidayTypeSchema = z.enum([
|
|
10
|
+
"federal",
|
|
11
|
+
"state",
|
|
12
|
+
"islamic",
|
|
13
|
+
"islamic_state",
|
|
14
|
+
"replacement",
|
|
15
|
+
"adhoc"
|
|
16
|
+
]);
|
|
17
|
+
var holidayStatusSchema = z.enum([
|
|
18
|
+
"confirmed",
|
|
19
|
+
"tentative",
|
|
20
|
+
"announced",
|
|
21
|
+
"cancelled"
|
|
22
|
+
]);
|
|
23
|
+
var gazetteLevelSchema = z.enum(["P", "N"]);
|
|
24
|
+
var holidaySourceSchema = z.enum([
|
|
25
|
+
"jpm",
|
|
26
|
+
"jakim",
|
|
27
|
+
"state-gov",
|
|
28
|
+
"community",
|
|
29
|
+
"admin"
|
|
30
|
+
]);
|
|
31
|
+
var holidaySchema = z.object({
|
|
32
|
+
id: z.string().min(1),
|
|
33
|
+
date: isoDate,
|
|
34
|
+
endDate: isoDate.optional(),
|
|
35
|
+
name: localizedString,
|
|
36
|
+
type: holidayTypeSchema,
|
|
37
|
+
status: holidayStatusSchema,
|
|
38
|
+
states: z.array(z.string().min(1)).min(1),
|
|
39
|
+
isPublicHoliday: z.boolean(),
|
|
40
|
+
gazetteLevel: gazetteLevelSchema,
|
|
41
|
+
isReplacementFor: z.string().optional(),
|
|
42
|
+
hijriDate: z.string().optional(),
|
|
43
|
+
gazetteRef: z.string().optional(),
|
|
44
|
+
source: holidaySourceSchema,
|
|
45
|
+
confirmedAt: z.string().optional(),
|
|
46
|
+
createdAt: z.string(),
|
|
47
|
+
updatedAt: z.string()
|
|
48
|
+
});
|
|
49
|
+
var holidayFileSchema = z.array(holidaySchema);
|
|
50
|
+
var stateGroupSchema = z.enum(["A", "B"]);
|
|
51
|
+
var weekendConfigSchema = z.object({
|
|
52
|
+
from: isoDate,
|
|
53
|
+
to: isoDate.nullable(),
|
|
54
|
+
weekendDays: z.array(z.number().int().min(0).max(6)),
|
|
55
|
+
group: stateGroupSchema
|
|
56
|
+
});
|
|
57
|
+
var stateSchema = z.object({
|
|
58
|
+
code: z.string().min(1),
|
|
59
|
+
aliases: z.array(z.string()),
|
|
60
|
+
name: localizedString,
|
|
61
|
+
type: z.enum(["state", "federal_territory"]),
|
|
62
|
+
weekendHistory: z.array(weekendConfigSchema).min(1),
|
|
63
|
+
group: stateGroupSchema
|
|
64
|
+
});
|
|
65
|
+
var statesFileSchema = z.array(stateSchema);
|
|
66
|
+
var schoolTermSegmentSchema = z.object({
|
|
67
|
+
startDate: isoDate,
|
|
68
|
+
endDate: isoDate,
|
|
69
|
+
schoolDays: z.number().int().min(0)
|
|
70
|
+
});
|
|
71
|
+
var schoolTermSchema = z.object({
|
|
72
|
+
id: z.string().min(1),
|
|
73
|
+
year: z.number().int().min(2020).max(2100),
|
|
74
|
+
term: z.union([z.literal(1), z.literal(2)]),
|
|
75
|
+
group: stateGroupSchema,
|
|
76
|
+
segments: z.array(schoolTermSegmentSchema).min(1),
|
|
77
|
+
totalSchoolDays: z.number().int().min(0),
|
|
78
|
+
startDate: isoDate,
|
|
79
|
+
endDate: isoDate
|
|
80
|
+
});
|
|
81
|
+
var schoolHolidayTypeSchema = z.enum([
|
|
82
|
+
"cuti_penggal_1",
|
|
83
|
+
"cuti_pertengahan",
|
|
84
|
+
"cuti_penggal_2",
|
|
85
|
+
"cuti_akhir",
|
|
86
|
+
"cuti_perayaan_kpm"
|
|
87
|
+
]);
|
|
88
|
+
var schoolHolidaySchema = z.object({
|
|
89
|
+
id: z.string().min(1),
|
|
90
|
+
year: z.number().int().min(2020).max(2100),
|
|
91
|
+
group: stateGroupSchema,
|
|
92
|
+
type: schoolHolidayTypeSchema,
|
|
93
|
+
name: localizedString,
|
|
94
|
+
startDate: isoDate,
|
|
95
|
+
endDate: isoDate,
|
|
96
|
+
days: z.number().int().min(1),
|
|
97
|
+
states: z.array(z.string()).optional(),
|
|
98
|
+
excludeStates: z.array(z.string()).optional(),
|
|
99
|
+
remarks: z.string().optional()
|
|
100
|
+
});
|
|
101
|
+
var schoolTermsFileSchema = z.array(schoolTermSchema);
|
|
102
|
+
var schoolHolidaysFileSchema = z.array(schoolHolidaySchema);
|
|
103
|
+
var examTypeSchema = z.enum([
|
|
104
|
+
"spm",
|
|
105
|
+
"stpm",
|
|
106
|
+
"muet",
|
|
107
|
+
"pt3",
|
|
108
|
+
"stam",
|
|
109
|
+
"other"
|
|
110
|
+
]);
|
|
111
|
+
var examSchema = z.object({
|
|
112
|
+
id: z.string().min(1),
|
|
113
|
+
year: z.number().int().min(2020).max(2100),
|
|
114
|
+
name: z.string().min(1),
|
|
115
|
+
fullName: localizedString,
|
|
116
|
+
type: examTypeSchema,
|
|
117
|
+
startDate: isoDate,
|
|
118
|
+
endDate: isoDate.optional(),
|
|
119
|
+
status: z.enum(["confirmed", "tentative"]),
|
|
120
|
+
resultsDate: isoDate.optional(),
|
|
121
|
+
source: z.enum(["kpm", "mpm"])
|
|
122
|
+
});
|
|
123
|
+
var examsFileSchema = z.array(examSchema);
|
|
124
|
+
|
|
125
|
+
// src/state-resolver.ts
|
|
126
|
+
function resolveStateCode(query, states) {
|
|
127
|
+
const normalized = query.toLowerCase().trim();
|
|
128
|
+
for (const state of states) {
|
|
129
|
+
if (state.code === normalized) return state;
|
|
130
|
+
for (const alias of state.aliases) {
|
|
131
|
+
if (alias.toLowerCase() === normalized) return state;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function getStateByCode(code, states) {
|
|
137
|
+
return states.find((s) => s.code === code) ?? null;
|
|
138
|
+
}
|
|
139
|
+
function getStatesByGroup(group, states) {
|
|
140
|
+
return states.filter((s) => s.group === group);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/weekend.ts
|
|
144
|
+
var DAY_NAMES = [
|
|
145
|
+
"Sunday",
|
|
146
|
+
"Monday",
|
|
147
|
+
"Tuesday",
|
|
148
|
+
"Wednesday",
|
|
149
|
+
"Thursday",
|
|
150
|
+
"Friday",
|
|
151
|
+
"Saturday"
|
|
152
|
+
];
|
|
153
|
+
function getWeekendConfig(state, date) {
|
|
154
|
+
for (const config of state.weekendHistory) {
|
|
155
|
+
const from = config.from;
|
|
156
|
+
const to = config.to;
|
|
157
|
+
if (date >= from && (to === null || date <= to)) {
|
|
158
|
+
return config;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return state.weekendHistory[0];
|
|
162
|
+
}
|
|
163
|
+
function isWeekend(date, state) {
|
|
164
|
+
const config = getWeekendConfig(state, date);
|
|
165
|
+
const dayOfWeek = (/* @__PURE__ */ new Date(date + "T12:00:00Z")).getUTCDay();
|
|
166
|
+
return config.weekendDays.includes(dayOfWeek);
|
|
167
|
+
}
|
|
168
|
+
function getDayOfWeekName(date) {
|
|
169
|
+
const dayIndex = (/* @__PURE__ */ new Date(date + "T12:00:00Z")).getUTCDay();
|
|
170
|
+
return DAY_NAMES[dayIndex];
|
|
171
|
+
}
|
|
172
|
+
function getWeekendDayNames(state, date) {
|
|
173
|
+
const config = getWeekendConfig(state, date);
|
|
174
|
+
return config.weekendDays.map((d) => DAY_NAMES[d]);
|
|
175
|
+
}
|
|
176
|
+
function addDays(date, days) {
|
|
177
|
+
const d = /* @__PURE__ */ new Date(date + "T12:00:00Z");
|
|
178
|
+
d.setUTCDate(d.getUTCDate() + days);
|
|
179
|
+
return d.toISOString().slice(0, 10);
|
|
180
|
+
}
|
|
181
|
+
function nextWorkingDay(date, state, holidayDates) {
|
|
182
|
+
let candidate = addDays(date, 1);
|
|
183
|
+
while (isWeekend(candidate, state) || holidayDates.has(candidate)) {
|
|
184
|
+
candidate = addDays(candidate, 1);
|
|
185
|
+
}
|
|
186
|
+
return candidate;
|
|
187
|
+
}
|
|
188
|
+
function diffDays(start, end) {
|
|
189
|
+
const s = (/* @__PURE__ */ new Date(start + "T00:00:00")).getTime();
|
|
190
|
+
const e = (/* @__PURE__ */ new Date(end + "T00:00:00")).getTime();
|
|
191
|
+
return Math.round((e - s) / (1e3 * 60 * 60 * 24));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/filter.ts
|
|
195
|
+
function filterHolidays(holidays, filter) {
|
|
196
|
+
return holidays.filter((h) => {
|
|
197
|
+
if (filter.year !== void 0) {
|
|
198
|
+
const year = parseInt(h.date.slice(0, 4), 10);
|
|
199
|
+
if (year !== filter.year) return false;
|
|
200
|
+
}
|
|
201
|
+
if (filter.month !== void 0) {
|
|
202
|
+
const month = parseInt(h.date.slice(5, 7), 10);
|
|
203
|
+
if (month !== filter.month) return false;
|
|
204
|
+
}
|
|
205
|
+
if (filter.state !== void 0) {
|
|
206
|
+
if (!h.states.includes("*") && !h.states.includes(filter.state)) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (filter.type !== void 0 && h.type !== filter.type) return false;
|
|
211
|
+
if (filter.status !== void 0 && h.status !== filter.status) return false;
|
|
212
|
+
return true;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function findHolidaysByDate(date, holidays, stateCode) {
|
|
216
|
+
return holidays.filter((h) => {
|
|
217
|
+
if (h.date !== date) return false;
|
|
218
|
+
if (h.status === "cancelled") return false;
|
|
219
|
+
if (stateCode && !h.states.includes("*") && !h.states.includes(stateCode)) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function findNextHoliday(afterDate, holidays, stateCode, type, limit = 1) {
|
|
226
|
+
const filtered = holidays.filter((h) => {
|
|
227
|
+
if (h.date <= afterDate) return false;
|
|
228
|
+
if (h.status === "cancelled") return false;
|
|
229
|
+
if (stateCode && !h.states.includes("*") && !h.states.includes(stateCode)) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
if (type !== void 0 && h.type !== type) return false;
|
|
233
|
+
return true;
|
|
234
|
+
}).sort((a, b) => a.date.localeCompare(b.date));
|
|
235
|
+
return filtered.slice(0, limit);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/replacement.ts
|
|
239
|
+
function calculateReplacementHolidays(holidays, state) {
|
|
240
|
+
const stateHolidays = holidays.filter(
|
|
241
|
+
(h) => h.status !== "cancelled" && (h.states.includes("*") || h.states.includes(state.code))
|
|
242
|
+
);
|
|
243
|
+
const holidayDates = new Set(stateHolidays.map((h) => h.date));
|
|
244
|
+
const replacementDates = /* @__PURE__ */ new Set();
|
|
245
|
+
const replacements = [];
|
|
246
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
247
|
+
for (const holiday of stateHolidays) {
|
|
248
|
+
if (holiday.type === "replacement") continue;
|
|
249
|
+
if (isWeekend(holiday.date, state)) {
|
|
250
|
+
const allOccupied = /* @__PURE__ */ new Set([...holidayDates, ...replacementDates]);
|
|
251
|
+
const replacementDate = nextWorkingDay(holiday.date, state, allOccupied);
|
|
252
|
+
replacementDates.add(replacementDate);
|
|
253
|
+
replacements.push({
|
|
254
|
+
id: `${holiday.id}-replacement`,
|
|
255
|
+
date: replacementDate,
|
|
256
|
+
name: {
|
|
257
|
+
ms: `Cuti Ganti ${holiday.name.ms}`,
|
|
258
|
+
en: `Replacement for ${holiday.name.en}`,
|
|
259
|
+
zh: holiday.name.zh ? `\u8865\u5047 ${holiday.name.zh}` : void 0
|
|
260
|
+
},
|
|
261
|
+
type: "replacement",
|
|
262
|
+
status: holiday.status,
|
|
263
|
+
states: [state.code],
|
|
264
|
+
isPublicHoliday: true,
|
|
265
|
+
gazetteLevel: holiday.gazetteLevel,
|
|
266
|
+
isReplacementFor: holiday.id,
|
|
267
|
+
source: holiday.source,
|
|
268
|
+
createdAt: now,
|
|
269
|
+
updatedAt: now
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const dateGroups = /* @__PURE__ */ new Map();
|
|
274
|
+
for (const h of stateHolidays) {
|
|
275
|
+
if (h.type === "replacement") continue;
|
|
276
|
+
const existing = dateGroups.get(h.date) ?? [];
|
|
277
|
+
dateGroups.set(h.date, [...existing, h]);
|
|
278
|
+
}
|
|
279
|
+
for (const [, group] of dateGroups) {
|
|
280
|
+
if (group.length <= 1) continue;
|
|
281
|
+
const stateHoliday = group.find((h) => h.gazetteLevel === "N");
|
|
282
|
+
if (!stateHoliday) continue;
|
|
283
|
+
if (replacements.some((r) => r.isReplacementFor === stateHoliday.id)) continue;
|
|
284
|
+
const allOccupied = /* @__PURE__ */ new Set([...holidayDates, ...replacementDates]);
|
|
285
|
+
const replacementDate = nextWorkingDay(stateHoliday.date, state, allOccupied);
|
|
286
|
+
replacementDates.add(replacementDate);
|
|
287
|
+
replacements.push({
|
|
288
|
+
id: `${stateHoliday.id}-overlap-replacement`,
|
|
289
|
+
date: replacementDate,
|
|
290
|
+
name: {
|
|
291
|
+
ms: `Cuti Ganti ${stateHoliday.name.ms}`,
|
|
292
|
+
en: `Replacement for ${stateHoliday.name.en}`,
|
|
293
|
+
zh: stateHoliday.name.zh ? `\u8865\u5047 ${stateHoliday.name.zh}` : void 0
|
|
294
|
+
},
|
|
295
|
+
type: "replacement",
|
|
296
|
+
status: stateHoliday.status,
|
|
297
|
+
states: [state.code],
|
|
298
|
+
isPublicHoliday: true,
|
|
299
|
+
gazetteLevel: stateHoliday.gazetteLevel,
|
|
300
|
+
isReplacementFor: stateHoliday.id,
|
|
301
|
+
source: stateHoliday.source,
|
|
302
|
+
createdAt: now,
|
|
303
|
+
updatedAt: now
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
return replacements;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/business-days.ts
|
|
310
|
+
function countBusinessDays(start, end, state, holidays) {
|
|
311
|
+
const totalDays = diffDays(start, end) + 1;
|
|
312
|
+
let businessDays = 0;
|
|
313
|
+
let weekendCount = 0;
|
|
314
|
+
let holidayCount = 0;
|
|
315
|
+
const holidayList = [];
|
|
316
|
+
let current = start;
|
|
317
|
+
while (current <= end) {
|
|
318
|
+
const weekend = isWeekend(current, state);
|
|
319
|
+
const dayHolidays = findHolidaysByDate(current, holidays, state.code);
|
|
320
|
+
const isHoliday = dayHolidays.length > 0;
|
|
321
|
+
if (weekend) {
|
|
322
|
+
weekendCount++;
|
|
323
|
+
} else if (isHoliday) {
|
|
324
|
+
holidayCount++;
|
|
325
|
+
for (const h of dayHolidays) {
|
|
326
|
+
if (!holidayList.some((existing) => existing.id === h.id)) {
|
|
327
|
+
holidayList.push(h);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
businessDays++;
|
|
332
|
+
}
|
|
333
|
+
current = addDays(current, 1);
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
totalDays,
|
|
337
|
+
businessDays,
|
|
338
|
+
holidays: holidayCount,
|
|
339
|
+
weekendDays: weekendCount,
|
|
340
|
+
holidayList
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function addBusinessDays(startDate, daysToAdd, state, holidays) {
|
|
344
|
+
let remaining = daysToAdd;
|
|
345
|
+
let current = startDate;
|
|
346
|
+
while (remaining > 0) {
|
|
347
|
+
current = addDays(current, 1);
|
|
348
|
+
const weekend = isWeekend(current, state);
|
|
349
|
+
const isHoliday = findHolidaysByDate(current, holidays, state.code).length > 0;
|
|
350
|
+
if (!weekend && !isHoliday) {
|
|
351
|
+
remaining--;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return current;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/school.ts
|
|
358
|
+
function findSchoolTermByDate(date, terms, group) {
|
|
359
|
+
return terms.find(
|
|
360
|
+
(t) => t.group === group && date >= t.startDate && date <= t.endDate
|
|
361
|
+
) ?? null;
|
|
362
|
+
}
|
|
363
|
+
function findSchoolHolidayByDate(date, holidays, group, stateCode) {
|
|
364
|
+
return holidays.find((h) => {
|
|
365
|
+
if (h.group !== group) return false;
|
|
366
|
+
if (date < h.startDate || date > h.endDate) return false;
|
|
367
|
+
if (stateCode && h.excludeStates?.includes(stateCode)) return false;
|
|
368
|
+
if (h.states && !h.states.includes(stateCode ?? "")) return false;
|
|
369
|
+
return true;
|
|
370
|
+
}) ?? null;
|
|
371
|
+
}
|
|
372
|
+
function isSchoolDay(date, terms, schoolHolidays, group, isPublicHoliday, isWeekendDay, stateCode) {
|
|
373
|
+
if (isWeekendDay) return false;
|
|
374
|
+
if (isPublicHoliday) return false;
|
|
375
|
+
const schoolHoliday = findSchoolHolidayByDate(
|
|
376
|
+
date,
|
|
377
|
+
schoolHolidays,
|
|
378
|
+
group,
|
|
379
|
+
stateCode
|
|
380
|
+
);
|
|
381
|
+
if (schoolHoliday) return false;
|
|
382
|
+
const term = findSchoolTermByDate(date, terms, group);
|
|
383
|
+
return term !== null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/ical.ts
|
|
387
|
+
function escapeIcal(text) {
|
|
388
|
+
return text.replace(/[\\;,]/g, (c) => `\\${c}`).replace(/\n/g, "\\n");
|
|
389
|
+
}
|
|
390
|
+
function formatIcalDate(date) {
|
|
391
|
+
return date.replace(/-/g, "");
|
|
392
|
+
}
|
|
393
|
+
function holidayToVevent(h) {
|
|
394
|
+
const dtstart = formatIcalDate(h.date);
|
|
395
|
+
const dtend = h.endDate ? formatIcalDate(h.endDate) : formatIcalDate(h.date);
|
|
396
|
+
return [
|
|
397
|
+
"BEGIN:VEVENT",
|
|
398
|
+
`DTSTART;VALUE=DATE:${dtstart}`,
|
|
399
|
+
`DTEND;VALUE=DATE:${dtend}`,
|
|
400
|
+
`SUMMARY:${escapeIcal(h.name.en)}`,
|
|
401
|
+
`DESCRIPTION:${escapeIcal(h.name.ms)}${h.hijriDate ? ` (${h.hijriDate})` : ""}`,
|
|
402
|
+
`UID:${h.id}@mycal.my`,
|
|
403
|
+
`STATUS:${h.status === "tentative" ? "TENTATIVE" : "CONFIRMED"}`,
|
|
404
|
+
"END:VEVENT"
|
|
405
|
+
].join("\r\n");
|
|
406
|
+
}
|
|
407
|
+
function schoolHolidayToVevent(h) {
|
|
408
|
+
return [
|
|
409
|
+
"BEGIN:VEVENT",
|
|
410
|
+
`DTSTART;VALUE=DATE:${formatIcalDate(h.startDate)}`,
|
|
411
|
+
`DTEND;VALUE=DATE:${formatIcalDate(h.endDate)}`,
|
|
412
|
+
`SUMMARY:[School] ${escapeIcal(h.name.en)}`,
|
|
413
|
+
`DESCRIPTION:${escapeIcal(h.name.ms)}`,
|
|
414
|
+
`UID:${h.id}@mycal.my`,
|
|
415
|
+
"STATUS:CONFIRMED",
|
|
416
|
+
"END:VEVENT"
|
|
417
|
+
].join("\r\n");
|
|
418
|
+
}
|
|
419
|
+
function generateIcal(holidays, schoolHolidays, calendarName) {
|
|
420
|
+
const header = [
|
|
421
|
+
"BEGIN:VCALENDAR",
|
|
422
|
+
"VERSION:2.0",
|
|
423
|
+
"PRODID:-//MyCal//Malaysia Calendar API//EN",
|
|
424
|
+
`X-WR-CALNAME:${escapeIcal(calendarName)}`,
|
|
425
|
+
"X-WR-TIMEZONE:Asia/Kuala_Lumpur",
|
|
426
|
+
"CALSCALE:GREGORIAN",
|
|
427
|
+
"METHOD:PUBLISH",
|
|
428
|
+
"REFRESH-INTERVAL;VALUE=DURATION:PT6H",
|
|
429
|
+
"X-PUBLISHED-TTL:PT6H"
|
|
430
|
+
].join("\r\n");
|
|
431
|
+
const events = [
|
|
432
|
+
...holidays.filter((h) => h.status !== "cancelled").map(holidayToVevent),
|
|
433
|
+
...schoolHolidays.map(schoolHolidayToVevent)
|
|
434
|
+
].join("\r\n");
|
|
435
|
+
return `${header}\r
|
|
436
|
+
${events}\r
|
|
437
|
+
END:VCALENDAR\r
|
|
438
|
+
`;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/hijri.ts
|
|
442
|
+
var HIJRI_MONTHS = [
|
|
443
|
+
{ number: 1, nameMs: "Muharam", nameEn: "Muharram", nameAr: "\u0645\u062D\u0631\u0651\u0645" },
|
|
444
|
+
{ number: 2, nameMs: "Safar", nameEn: "Safar", nameAr: "\u0635\u0641\u0631" },
|
|
445
|
+
{ number: 3, nameMs: "Rabiulawal", nameEn: "Rabi al-Awwal", nameAr: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0651\u0644" },
|
|
446
|
+
{ number: 4, nameMs: "Rabiulakhir", nameEn: "Rabi al-Thani", nameAr: "\u0631\u0628\u064A\u0639 \u0627\u0644\u062B\u0627\u0646\u064A" },
|
|
447
|
+
{ number: 5, nameMs: "Jamadilawal", nameEn: "Jumada al-Ula", nameAr: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649" },
|
|
448
|
+
{ number: 6, nameMs: "Jamadilakhir", nameEn: "Jumada al-Thani", nameAr: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u062B\u0627\u0646\u064A\u0629" },
|
|
449
|
+
{ number: 7, nameMs: "Rejab", nameEn: "Rajab", nameAr: "\u0631\u062C\u0628" },
|
|
450
|
+
{ number: 8, nameMs: "Syaaban", nameEn: "Sha'ban", nameAr: "\u0634\u0639\u0628\u0627\u0646" },
|
|
451
|
+
{ number: 9, nameMs: "Ramadan", nameEn: "Ramadan", nameAr: "\u0631\u0645\u0636\u0627\u0646" },
|
|
452
|
+
{ number: 10, nameMs: "Syawal", nameEn: "Shawwal", nameAr: "\u0634\u0648\u0651\u0627\u0644" },
|
|
453
|
+
{ number: 11, nameMs: "Zulkaedah", nameEn: "Dhu al-Qi'dah", nameAr: "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629" },
|
|
454
|
+
{ number: 12, nameMs: "Zulhijjah", nameEn: "Dhu al-Hijjah", nameAr: "\u0630\u0648 \u0627\u0644\u062D\u062C\u0651\u0629" }
|
|
455
|
+
];
|
|
456
|
+
function parseHijriDate(hijriStr) {
|
|
457
|
+
const match = hijriStr.match(/^(\d+)\s+(.+?)\s+(\d+)$/);
|
|
458
|
+
if (!match) return null;
|
|
459
|
+
const day = parseInt(match[1], 10);
|
|
460
|
+
const monthName = match[2].toLowerCase();
|
|
461
|
+
const year = parseInt(match[3], 10);
|
|
462
|
+
const month = HIJRI_MONTHS.find(
|
|
463
|
+
(m) => m.nameMs.toLowerCase() === monthName || m.nameEn.toLowerCase() === monthName
|
|
464
|
+
);
|
|
465
|
+
if (!month) return null;
|
|
466
|
+
return { day, month, year };
|
|
467
|
+
}
|
|
468
|
+
function formatHijriDate(day, monthNumber, year) {
|
|
469
|
+
const month = HIJRI_MONTHS.find((m) => m.number === monthNumber);
|
|
470
|
+
if (!month) return `${day} ? ${year}`;
|
|
471
|
+
return `${day} ${month.nameMs} ${year}`;
|
|
472
|
+
}
|
|
473
|
+
var ISLAMIC_HOLIDAY_DATES = [
|
|
474
|
+
{ nameMs: "Awal Muharam", nameEn: "Islamic New Year", hijriMonth: 1, hijriDay: 1 },
|
|
475
|
+
{ nameMs: "Maulidur Rasul", nameEn: "Prophet Muhammad's Birthday", hijriMonth: 3, hijriDay: 12 },
|
|
476
|
+
{ nameMs: "Israk dan Mikraj", nameEn: "Isra' Mi'raj", hijriMonth: 7, hijriDay: 27 },
|
|
477
|
+
{ nameMs: "Nuzul Al-Quran", nameEn: "Revelation of the Quran", hijriMonth: 9, hijriDay: 17 },
|
|
478
|
+
{ nameMs: "Hari Raya Aidilfitri", nameEn: "Eid al-Fitr", hijriMonth: 10, hijriDay: 1 },
|
|
479
|
+
{ nameMs: "Hari Raya Haji", nameEn: "Eid al-Adha", hijriMonth: 12, hijriDay: 10 },
|
|
480
|
+
{ nameMs: "Hari Arafah", nameEn: "Day of Arafah", hijriMonth: 12, hijriDay: 9 }
|
|
481
|
+
];
|
|
482
|
+
export {
|
|
483
|
+
HIJRI_MONTHS,
|
|
484
|
+
ISLAMIC_HOLIDAY_DATES,
|
|
485
|
+
addBusinessDays,
|
|
486
|
+
addDays,
|
|
487
|
+
calculateReplacementHolidays,
|
|
488
|
+
countBusinessDays,
|
|
489
|
+
diffDays,
|
|
490
|
+
examSchema,
|
|
491
|
+
examsFileSchema,
|
|
492
|
+
filterHolidays,
|
|
493
|
+
findHolidaysByDate,
|
|
494
|
+
findNextHoliday,
|
|
495
|
+
findSchoolHolidayByDate,
|
|
496
|
+
findSchoolTermByDate,
|
|
497
|
+
formatHijriDate,
|
|
498
|
+
generateIcal,
|
|
499
|
+
getDayOfWeekName,
|
|
500
|
+
getStateByCode,
|
|
501
|
+
getStatesByGroup,
|
|
502
|
+
getWeekendConfig,
|
|
503
|
+
getWeekendDayNames,
|
|
504
|
+
holidayFileSchema,
|
|
505
|
+
holidaySchema,
|
|
506
|
+
isSchoolDay,
|
|
507
|
+
isWeekend,
|
|
508
|
+
nextWorkingDay,
|
|
509
|
+
parseHijriDate,
|
|
510
|
+
resolveStateCode,
|
|
511
|
+
schoolHolidaySchema,
|
|
512
|
+
schoolHolidaysFileSchema,
|
|
513
|
+
schoolTermSchema,
|
|
514
|
+
schoolTermsFileSchema,
|
|
515
|
+
stateSchema,
|
|
516
|
+
statesFileSchema
|
|
517
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@catlabtech/mycal-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core types, schemas, and business logic for the Malaysia Calendar API",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"malaysia",
|
|
7
|
+
"calendar",
|
|
8
|
+
"holidays",
|
|
9
|
+
"cuti",
|
|
10
|
+
"typescript"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/Junhui20/malaysia-calendar-api.git",
|
|
16
|
+
"directory": "packages/core"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://mycal.pages.dev",
|
|
19
|
+
"bugs": "https://github.com/Junhui20/malaysia-calendar-api/issues",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"tsup": "^8.0.0",
|
|
38
|
+
"typescript": "^5.7.0",
|
|
39
|
+
"vitest": "^3.0.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"zod": "^3.24.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
46
|
+
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
47
|
+
"test": "node __tests__/run-tests.cjs"
|
|
48
|
+
}
|
|
49
|
+
}
|