bsuir-iis-api 0.2.7 → 0.3.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/CHANGELOG.md +19 -0
- package/README.md +18 -16
- package/dist/index.d.ts +2 -14
- package/dist/index.js +51 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,25 @@ This changelog is maintained manually and updated in release commits.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.3.0] - 2026-03-19
|
|
12
|
+
|
|
13
|
+
### Changed (0.3.0)
|
|
14
|
+
|
|
15
|
+
- Simplified current week behavior to use only IIS API `/schedule/current-week` value.
|
|
16
|
+
- Removed cycle-week abstractions and conversions: `schedule.getCurrentCycleWeek()`,
|
|
17
|
+
`currentWeek.getCycle()`, and public `toCycleWeek()` export.
|
|
18
|
+
- Replaced standalone `currentWeek` module implementation with `currentWeek.get` alias
|
|
19
|
+
to `schedule.getCurrentWeek`.
|
|
20
|
+
- Updated tests and README to reflect single-source current week semantics.
|
|
21
|
+
|
|
22
|
+
## [0.2.8] - 2026-03-16
|
|
23
|
+
|
|
24
|
+
### Changed (0.2.8)
|
|
25
|
+
|
|
26
|
+
- Hardened error handling for malformed inputs and partial schedule payloads.
|
|
27
|
+
- Extended error-focused tests with payload assertions for API/network/timeout error fields.
|
|
28
|
+
- Expanded README error handling docs with retry/abort semantics and validation constraints.
|
|
29
|
+
|
|
11
30
|
## [0.2.7] - 2026-03-16
|
|
12
31
|
|
|
13
32
|
### Added (0.2.7)
|
package/README.md
CHANGED
|
@@ -49,7 +49,6 @@ const client = createBsuirClient({
|
|
|
49
49
|
- `client.schedule.getGroupBySubgroup(groupNumber, subgroup, options?)`
|
|
50
50
|
- `client.schedule.getEmployeeBySubgroup(urlId, subgroup, options?)`
|
|
51
51
|
- `client.schedule.getCurrentWeek(options?)`
|
|
52
|
-
- `client.schedule.getCurrentCycleWeek(options?)`
|
|
53
52
|
- `client.schedule.getLastUpdateByGroup({ groupNumber } | { id }, options?)`
|
|
54
53
|
- `client.schedule.getLastUpdateByEmployee({ urlId } | { id }, options?)`
|
|
55
54
|
|
|
@@ -70,7 +69,6 @@ const client = createBsuirClient({
|
|
|
70
69
|
### Meta
|
|
71
70
|
|
|
72
71
|
- `client.currentWeek.get(options?)` (alias to `client.schedule.getCurrentWeek`)
|
|
73
|
-
- `client.currentWeek.getCycle(options?)` (alias to `client.schedule.getCurrentCycleWeek`)
|
|
74
72
|
- `client.lastUpdate.byGroup({ groupNumber } | { id }, options?)`
|
|
75
73
|
- `client.lastUpdate.byEmployee({ urlId } | { id }, options?)`
|
|
76
74
|
|
|
@@ -79,11 +77,23 @@ const client = createBsuirClient({
|
|
|
79
77
|
SDK throws typed errors:
|
|
80
78
|
|
|
81
79
|
- `BsuirApiError` for HTTP errors (contains `status`, `endpoint`, `body`)
|
|
82
|
-
- `BsuirNetworkError` for transport errors
|
|
83
|
-
- `BsuirTimeoutError` for timeouts
|
|
80
|
+
- `BsuirNetworkError` for transport errors (contains `endpoint`, `causeError`, and standard `cause`)
|
|
81
|
+
- `BsuirTimeoutError` for timeouts (contains `endpoint`, `timeoutMs`)
|
|
84
82
|
- `BsuirValidationError` for invalid input parameters
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
|
|
84
|
+
Validation rules:
|
|
85
|
+
|
|
86
|
+
- `groupNumber` must contain digits only
|
|
87
|
+
- `urlId` must be a slug with letters/digits/hyphens (for example `s-nesterenkov`)
|
|
88
|
+
- `id` and `subgroup` parameters must be positive integers
|
|
89
|
+
Retry and abort behavior:
|
|
90
|
+
|
|
91
|
+
- Retries are applied to `429`, `500`, `502`, `503`, `504`
|
|
92
|
+
- `Retry-After` is respected for retriable responses
|
|
93
|
+
- Caller-provided aborted `AbortSignal` is re-thrown as native `AbortError`
|
|
94
|
+
- Internal timeout is mapped to `BsuirTimeoutError`
|
|
95
|
+
|
|
96
|
+
`createBsuirClient()` throws regular `Error` if no `fetch` implementation is available.
|
|
87
97
|
|
|
88
98
|
## Raw vs normalized schedule response
|
|
89
99
|
|
|
@@ -99,19 +109,11 @@ When `raw` is omitted, `getGroup()` and `getEmployee()` return normalized payloa
|
|
|
99
109
|
In raw mode API may return `schedules: null`; normalized mode always converts it to `{}`.
|
|
100
110
|
In raw mode some lesson fields may also be nullable (`weekNumber`, `lessonTypeAbbrev`), so keep null checks if you consume raw payload directly.
|
|
101
111
|
|
|
102
|
-
##
|
|
112
|
+
## Current week
|
|
103
113
|
|
|
104
|
-
`getCurrentWeek()` returns the current
|
|
105
|
-
For cycle week (1..4) use `getCurrentCycleWeek()` or helper `toCycleWeek()`.
|
|
114
|
+
`getCurrentWeek()` returns the current week value directly from IIS API.
|
|
106
115
|
The SDK normalizes `current-week` payloads, including plain-text responses like `1\n`.
|
|
107
116
|
|
|
108
|
-
```ts
|
|
109
|
-
import { toCycleWeek } from "bsuir-iis-api";
|
|
110
|
-
|
|
111
|
-
const semesterWeek = await client.schedule.getCurrentWeek();
|
|
112
|
-
const cycleWeek = toCycleWeek(semesterWeek); // default: 2 semester weeks per cycle week
|
|
113
|
-
```
|
|
114
|
-
|
|
115
117
|
Filtering example:
|
|
116
118
|
|
|
117
119
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -53,9 +53,6 @@ declare function createBsuirClient(options?: BsuirClientOptions): {
|
|
|
53
53
|
getGroupBySubgroup(groupNumber: string, subgroup: number, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
|
|
54
54
|
getEmployeeBySubgroup(urlId: string, subgroup: number, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
|
|
55
55
|
getCurrentWeek: (options?: ReadOptions) => Promise<number>;
|
|
56
|
-
getCurrentCycleWeek: (options?: ReadOptions & {
|
|
57
|
-
weeksPerCycle?: number;
|
|
58
|
-
}) => Promise<number>;
|
|
59
56
|
getLastUpdateByGroup(params: {
|
|
60
57
|
groupNumber: string;
|
|
61
58
|
} | {
|
|
@@ -103,9 +100,6 @@ declare function createBsuirClient(options?: BsuirClientOptions): {
|
|
|
103
100
|
};
|
|
104
101
|
currentWeek: {
|
|
105
102
|
get: (options?: ReadOptions) => Promise<number>;
|
|
106
|
-
getCycle: (options?: ReadOptions & {
|
|
107
|
-
weeksPerCycle?: number;
|
|
108
|
-
}) => Promise<number>;
|
|
109
103
|
};
|
|
110
104
|
};
|
|
111
105
|
/**
|
|
@@ -113,12 +107,6 @@ declare function createBsuirClient(options?: BsuirClientOptions): {
|
|
|
113
107
|
*/
|
|
114
108
|
type BsuirClient = ReturnType<typeof createBsuirClient>;
|
|
115
109
|
|
|
116
|
-
/**
|
|
117
|
-
* Converts semester week number to BSUIR cycle week (1..4).
|
|
118
|
-
* For default configuration weeks 1-2 => 1, 3-4 => 2, 5-6 => 3, 7-8 => 4.
|
|
119
|
-
*/
|
|
120
|
-
declare function toCycleWeek(semesterWeek: number, weeksPerCycle?: number): number;
|
|
121
|
-
|
|
122
110
|
/**
|
|
123
111
|
* Read options used by all module methods.
|
|
124
112
|
*/
|
|
@@ -286,7 +274,7 @@ interface ScheduleResponse {
|
|
|
286
274
|
employeeDto: Maybe<Employee>;
|
|
287
275
|
studentGroupDto: Maybe<StudentGroupCatalogItem>;
|
|
288
276
|
schedules: WeekScheduleMap | null;
|
|
289
|
-
exams: ScheduleItem[];
|
|
277
|
+
exams: ScheduleItem[] | null;
|
|
290
278
|
startDate: Maybe<string>;
|
|
291
279
|
endDate: Maybe<string>;
|
|
292
280
|
startExamsDate: Maybe<string>;
|
|
@@ -316,4 +304,4 @@ interface NormalizedScheduleResponse extends Omit<ScheduleResponse, "schedules"
|
|
|
316
304
|
examLessons: FlattenedScheduleItem[];
|
|
317
305
|
}
|
|
318
306
|
|
|
319
|
-
export { type Announcement, type ApiDateResponse, type Auditory, type AuditoryDepartment, type AuditoryType, BsuirApiError, type BsuirClient, type BsuirClientOptions, BsuirNetworkError, BsuirTimeoutError, BsuirValidationError, type BuildingNumber, type Department, type EducationForm, type Employee, type EmployeeCatalogItem, type Faculty, type FlattenedLessonsByDay, type FlattenedScheduleItem, type LessonStudentGroup, type Maybe, type NormalizedScheduleResponse, type ReadOptions, type RequestOptions, type ScheduleFilterOptions, type ScheduleItem, type ScheduleResponse, type Speciality, type StudentGroupCatalogItem, type StudentGroupShort, type WeekScheduleMap, type Weekday, createBsuirClient
|
|
307
|
+
export { type Announcement, type ApiDateResponse, type Auditory, type AuditoryDepartment, type AuditoryType, BsuirApiError, type BsuirClient, type BsuirClientOptions, BsuirNetworkError, BsuirTimeoutError, BsuirValidationError, type BuildingNumber, type Department, type EducationForm, type Employee, type EmployeeCatalogItem, type Faculty, type FlattenedLessonsByDay, type FlattenedScheduleItem, type LessonStudentGroup, type Maybe, type NormalizedScheduleResponse, type ReadOptions, type RequestOptions, type ScheduleFilterOptions, type ScheduleItem, type ScheduleResponse, type Speciality, type StudentGroupCatalogItem, type StudentGroupShort, type WeekScheduleMap, type Weekday, createBsuirClient };
|
package/dist/index.js
CHANGED
|
@@ -1,39 +1,46 @@
|
|
|
1
1
|
// src/client/errors.ts
|
|
2
|
-
|
|
2
|
+
function fixErrorPrototype(instance, prototype) {
|
|
3
|
+
Object.setPrototypeOf(instance, prototype);
|
|
4
|
+
}
|
|
5
|
+
var BsuirApiError = class _BsuirApiError extends Error {
|
|
3
6
|
status;
|
|
4
7
|
endpoint;
|
|
5
8
|
body;
|
|
6
9
|
constructor(message, status, endpoint, body) {
|
|
7
10
|
super(message);
|
|
11
|
+
fixErrorPrototype(this, _BsuirApiError.prototype);
|
|
8
12
|
this.name = "BsuirApiError";
|
|
9
13
|
this.status = status;
|
|
10
14
|
this.endpoint = endpoint;
|
|
11
15
|
this.body = body;
|
|
12
16
|
}
|
|
13
17
|
};
|
|
14
|
-
var BsuirNetworkError = class extends Error {
|
|
18
|
+
var BsuirNetworkError = class _BsuirNetworkError extends Error {
|
|
15
19
|
endpoint;
|
|
16
20
|
causeError;
|
|
17
21
|
constructor(message, endpoint, causeError) {
|
|
18
|
-
super(message);
|
|
22
|
+
super(message, { cause: causeError });
|
|
23
|
+
fixErrorPrototype(this, _BsuirNetworkError.prototype);
|
|
19
24
|
this.name = "BsuirNetworkError";
|
|
20
25
|
this.endpoint = endpoint;
|
|
21
26
|
this.causeError = causeError;
|
|
22
27
|
}
|
|
23
28
|
};
|
|
24
|
-
var BsuirTimeoutError = class extends Error {
|
|
29
|
+
var BsuirTimeoutError = class _BsuirTimeoutError extends Error {
|
|
25
30
|
endpoint;
|
|
26
31
|
timeoutMs;
|
|
27
32
|
constructor(message, endpoint, timeoutMs) {
|
|
28
33
|
super(message);
|
|
34
|
+
fixErrorPrototype(this, _BsuirTimeoutError.prototype);
|
|
29
35
|
this.name = "BsuirTimeoutError";
|
|
30
36
|
this.endpoint = endpoint;
|
|
31
37
|
this.timeoutMs = timeoutMs;
|
|
32
38
|
}
|
|
33
39
|
};
|
|
34
|
-
var BsuirValidationError = class extends Error {
|
|
40
|
+
var BsuirValidationError = class _BsuirValidationError extends Error {
|
|
35
41
|
constructor(message) {
|
|
36
42
|
super(message);
|
|
43
|
+
fixErrorPrototype(this, _BsuirValidationError.prototype);
|
|
37
44
|
this.name = "BsuirValidationError";
|
|
38
45
|
}
|
|
39
46
|
};
|
|
@@ -42,12 +49,12 @@ var BsuirValidationError = class extends Error {
|
|
|
42
49
|
var GROUP_NUMBER_PATTERN = /^\d+$/;
|
|
43
50
|
var EMPLOYEE_URL_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/i;
|
|
44
51
|
function assertNonEmptyString(value, fieldName) {
|
|
45
|
-
if (
|
|
52
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
46
53
|
throw new BsuirValidationError(`'${fieldName}' must be a non-empty string`);
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
function assertPositiveInt(value, fieldName) {
|
|
50
|
-
if (!Number.isInteger(value) || value <= 0) {
|
|
57
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
51
58
|
throw new BsuirValidationError(`'${fieldName}' must be a positive integer`);
|
|
52
59
|
}
|
|
53
60
|
}
|
|
@@ -195,7 +202,7 @@ async function requestJson(config, path, options = {}) {
|
|
|
195
202
|
throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, error);
|
|
196
203
|
}
|
|
197
204
|
}
|
|
198
|
-
throw new BsuirNetworkError(`
|
|
205
|
+
throw new BsuirNetworkError(`Unexpected retry loop termination for ${path}`, endpoint, null);
|
|
199
206
|
}
|
|
200
207
|
|
|
201
208
|
// src/modules/announcements.ts
|
|
@@ -229,55 +236,6 @@ function createAuditoriesModule(config) {
|
|
|
229
236
|
};
|
|
230
237
|
}
|
|
231
238
|
|
|
232
|
-
// src/utils/week.ts
|
|
233
|
-
var CYCLE_LENGTH = 4;
|
|
234
|
-
function toCycleWeek(semesterWeek, weeksPerCycle = 2) {
|
|
235
|
-
assertPositiveInt(semesterWeek, "semesterWeek");
|
|
236
|
-
assertPositiveInt(weeksPerCycle, "weeksPerCycle");
|
|
237
|
-
return Math.floor((semesterWeek - 1) / weeksPerCycle) % CYCLE_LENGTH + 1;
|
|
238
|
-
}
|
|
239
|
-
function parseSemesterWeek(payload) {
|
|
240
|
-
if (typeof payload === "number") {
|
|
241
|
-
assertPositiveInt(payload, "semesterWeek");
|
|
242
|
-
return payload;
|
|
243
|
-
}
|
|
244
|
-
if (typeof payload === "string") {
|
|
245
|
-
const normalized = payload.trim();
|
|
246
|
-
if (normalized.length > 0) {
|
|
247
|
-
const parsed = Number(normalized);
|
|
248
|
-
if (Number.isInteger(parsed)) {
|
|
249
|
-
assertPositiveInt(parsed, "semesterWeek");
|
|
250
|
-
return parsed;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (typeof payload === "object" && payload !== null) {
|
|
255
|
-
const record = payload;
|
|
256
|
-
if ("weekNumber" in record) {
|
|
257
|
-
return parseSemesterWeek(record.weekNumber);
|
|
258
|
-
}
|
|
259
|
-
if ("currentWeek" in record) {
|
|
260
|
-
return parseSemesterWeek(record.currentWeek);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
throw new BsuirValidationError("'semesterWeek' response payload must be a positive integer");
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// src/modules/currentWeek.ts
|
|
267
|
-
function createCurrentWeekModule(config) {
|
|
268
|
-
async function get(options = {}) {
|
|
269
|
-
const payload = await requestJson(config, "/schedule/current-week", {
|
|
270
|
-
signal: options.signal
|
|
271
|
-
});
|
|
272
|
-
return parseSemesterWeek(payload);
|
|
273
|
-
}
|
|
274
|
-
async function getCycle(options = {}) {
|
|
275
|
-
const semesterWeek = await get(options);
|
|
276
|
-
return toCycleWeek(semesterWeek, options.weeksPerCycle);
|
|
277
|
-
}
|
|
278
|
-
return { get, getCycle };
|
|
279
|
-
}
|
|
280
|
-
|
|
281
239
|
// src/modules/departments.ts
|
|
282
240
|
function createDepartmentsModule(config) {
|
|
283
241
|
return {
|
|
@@ -356,6 +314,34 @@ function createLastUpdateModule(config) {
|
|
|
356
314
|
};
|
|
357
315
|
}
|
|
358
316
|
|
|
317
|
+
// src/utils/week.ts
|
|
318
|
+
function parseCurrentWeek(payload) {
|
|
319
|
+
if (typeof payload === "number") {
|
|
320
|
+
assertPositiveInt(payload, "currentWeek");
|
|
321
|
+
return payload;
|
|
322
|
+
}
|
|
323
|
+
if (typeof payload === "string") {
|
|
324
|
+
const normalized = payload.trim();
|
|
325
|
+
if (normalized.length > 0) {
|
|
326
|
+
const parsed = Number(normalized);
|
|
327
|
+
if (Number.isInteger(parsed)) {
|
|
328
|
+
assertPositiveInt(parsed, "currentWeek");
|
|
329
|
+
return parsed;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (typeof payload === "object" && payload !== null) {
|
|
334
|
+
const record = payload;
|
|
335
|
+
if ("weekNumber" in record) {
|
|
336
|
+
return parseCurrentWeek(record.weekNumber);
|
|
337
|
+
}
|
|
338
|
+
if ("currentWeek" in record) {
|
|
339
|
+
return parseCurrentWeek(record.currentWeek);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
throw new BsuirValidationError("'currentWeek' response payload must be a positive integer");
|
|
343
|
+
}
|
|
344
|
+
|
|
359
345
|
// src/modules/schedule.ts
|
|
360
346
|
var WEEKDAYS = [
|
|
361
347
|
"\u041F\u043E\u043D\u0435\u0434\u0435\u043B\u044C\u043D\u0438\u043A",
|
|
@@ -369,7 +355,7 @@ function normalizeSchedule(response) {
|
|
|
369
355
|
const lessons = [];
|
|
370
356
|
const lessonsByDay = {};
|
|
371
357
|
const safeSchedules = response.schedules ?? {};
|
|
372
|
-
const safeExams = response.exams;
|
|
358
|
+
const safeExams = response.exams ?? [];
|
|
373
359
|
for (const day of WEEKDAYS) {
|
|
374
360
|
const dayItems = safeSchedules[day] ?? [];
|
|
375
361
|
const flattenedDayItems = dayItems.map((item) => ({ ...item, day, source: "schedules" }));
|
|
@@ -474,11 +460,7 @@ function createScheduleModule(config) {
|
|
|
474
460
|
const payload = await requestJson(config, "/schedule/current-week", {
|
|
475
461
|
signal: options.signal
|
|
476
462
|
});
|
|
477
|
-
return
|
|
478
|
-
}
|
|
479
|
-
async function getCurrentCycleWeek(options = {}) {
|
|
480
|
-
const semesterWeek = await getCurrentWeek(options);
|
|
481
|
-
return toCycleWeek(semesterWeek, options.weeksPerCycle);
|
|
463
|
+
return parseCurrentWeek(payload);
|
|
482
464
|
}
|
|
483
465
|
return {
|
|
484
466
|
getGroup,
|
|
@@ -500,7 +482,6 @@ function createScheduleModule(config) {
|
|
|
500
482
|
return getEmployeeFiltered(urlId, { source: "schedules", subgroup }, options);
|
|
501
483
|
},
|
|
502
484
|
getCurrentWeek,
|
|
503
|
-
getCurrentCycleWeek,
|
|
504
485
|
async getLastUpdateByGroup(params, options = {}) {
|
|
505
486
|
let query;
|
|
506
487
|
if ("groupNumber" in params) {
|
|
@@ -569,8 +550,9 @@ function createInternalConfig(options = {}) {
|
|
|
569
550
|
}
|
|
570
551
|
function createBsuirClient(options = {}) {
|
|
571
552
|
const config = createInternalConfig(options);
|
|
553
|
+
const schedule = createScheduleModule(config);
|
|
572
554
|
return {
|
|
573
|
-
schedule
|
|
555
|
+
schedule,
|
|
574
556
|
groups: createGroupsModule(config),
|
|
575
557
|
employees: createEmployeesModule(config),
|
|
576
558
|
faculties: createFacultiesModule(config),
|
|
@@ -579,7 +561,9 @@ function createBsuirClient(options = {}) {
|
|
|
579
561
|
announcements: createAnnouncementsModule(config),
|
|
580
562
|
auditories: createAuditoriesModule(config),
|
|
581
563
|
lastUpdate: createLastUpdateModule(config),
|
|
582
|
-
currentWeek:
|
|
564
|
+
currentWeek: {
|
|
565
|
+
get: schedule.getCurrentWeek
|
|
566
|
+
}
|
|
583
567
|
};
|
|
584
568
|
}
|
|
585
569
|
export {
|
|
@@ -587,7 +571,6 @@ export {
|
|
|
587
571
|
BsuirNetworkError,
|
|
588
572
|
BsuirTimeoutError,
|
|
589
573
|
BsuirValidationError,
|
|
590
|
-
createBsuirClient
|
|
591
|
-
toCycleWeek
|
|
574
|
+
createBsuirClient
|
|
592
575
|
};
|
|
593
576
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/errors.ts","../src/utils/guards.ts","../src/client/http.ts","../src/modules/announcements.ts","../src/modules/auditories.ts","../src/utils/week.ts","../src/modules/currentWeek.ts","../src/modules/departments.ts","../src/modules/employees.ts","../src/modules/faculties.ts","../src/modules/groups.ts","../src/modules/lastUpdate.ts","../src/modules/schedule.ts","../src/modules/specialities.ts","../src/client/createClient.ts"],"sourcesContent":["export class BsuirApiError extends Error {\n readonly status: number;\n readonly endpoint: string;\n readonly body: unknown;\n\n constructor(message: string, status: number, endpoint: string, body: unknown) {\n super(message);\n this.name = \"BsuirApiError\";\n this.status = status;\n this.endpoint = endpoint;\n this.body = body;\n }\n}\n\nexport class BsuirNetworkError extends Error {\n readonly endpoint: string;\n readonly causeError: unknown;\n\n constructor(message: string, endpoint: string, causeError: unknown) {\n super(message);\n this.name = \"BsuirNetworkError\";\n this.endpoint = endpoint;\n this.causeError = causeError;\n }\n}\n\nexport class BsuirTimeoutError extends Error {\n readonly endpoint: string;\n readonly timeoutMs: number;\n\n constructor(message: string, endpoint: string, timeoutMs: number) {\n super(message);\n this.name = \"BsuirTimeoutError\";\n this.endpoint = endpoint;\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class BsuirValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"BsuirValidationError\";\n }\n}\n","import { BsuirValidationError } from \"../client/errors\";\n\nconst GROUP_NUMBER_PATTERN = /^\\d+$/;\nconst EMPLOYEE_URL_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/i;\n\nexport function assertNonEmptyString(value: string, fieldName: string): void {\n if (!value || value.trim().length === 0) {\n throw new BsuirValidationError(`'${fieldName}' must be a non-empty string`);\n }\n}\n\nexport function assertPositiveInt(value: number, fieldName: string): void {\n if (!Number.isInteger(value) || value <= 0) {\n throw new BsuirValidationError(`'${fieldName}' must be a positive integer`);\n }\n}\n\nexport function assertGroupNumber(value: string, fieldName = \"groupNumber\"): void {\n assertNonEmptyString(value, fieldName);\n if (!GROUP_NUMBER_PATTERN.test(value)) {\n throw new BsuirValidationError(`'${fieldName}' must contain only digits`);\n }\n}\n\nexport function assertEmployeeUrlId(value: string, fieldName = \"urlId\"): void {\n assertNonEmptyString(value, fieldName);\n if (!EMPLOYEE_URL_ID_PATTERN.test(value)) {\n throw new BsuirValidationError(\n `'${fieldName}' must be a valid slug (letters, digits, hyphen)`\n );\n }\n}\n\nexport function isAbortError(error: unknown): boolean {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n return true;\n }\n\n if (typeof error === \"object\" && error !== null) {\n const maybeError = error as { name?: unknown; code?: unknown };\n return maybeError.name === \"AbortError\" || maybeError.code === \"ABORT_ERR\";\n }\n\n return false;\n}\n","import { BsuirApiError, BsuirNetworkError, BsuirTimeoutError } from \"./errors\";\nimport type { InternalClientConfig, QueryParams, RequestOptions } from \"./types\";\nimport { isAbortError } from \"../utils/guards\";\n\nconst RETRIABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\n\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n const normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n const url = new URL(`${normalizedBase}${normalizedPath}`);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) {\n continue;\n }\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n}\n\nfunction mergeSignals(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal {\n if (typeof AbortSignal.any === \"function\") {\n return AbortSignal.any([AbortSignal.timeout(timeoutMs), signal].filter(Boolean) as AbortSignal[]);\n }\n\n if (signal) {\n return signal;\n }\n\n return AbortSignal.timeout(timeoutMs);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseRetryAfterMs(retryAfter: string | null): number | null {\n if (!retryAfter) {\n return null;\n }\n\n const asSeconds = Number(retryAfter);\n if (!Number.isNaN(asSeconds)) {\n return Math.max(0, Math.floor(asSeconds * 1000));\n }\n\n const dateValue = Date.parse(retryAfter);\n if (!Number.isNaN(dateValue)) {\n return Math.max(0, dateValue - Date.now());\n }\n\n return null;\n}\n\nfunction getRetryDelayMs(\n config: InternalClientConfig,\n attempt: number,\n retryAfterHeader?: string | null\n): number {\n const retryAfterDelay = parseRetryAfterMs(retryAfterHeader ?? null);\n if (retryAfterDelay !== null) {\n return Math.min(retryAfterDelay, config.retryMaxDelayMs);\n }\n\n const exponent = Math.max(0, attempt);\n const baseDelay = Math.min(config.retryDelayMs * 2 ** exponent, config.retryMaxDelayMs);\n if (!config.retryJitter) {\n return baseDelay;\n }\n\n const jitterFactor = 0.75 + Math.random() * 0.5;\n return Math.floor(baseDelay * jitterFactor);\n}\n\nasync function parseBody(response: Response): Promise<unknown> {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"application/json\")) {\n return response.text();\n }\n\n try {\n return await response.json();\n } catch {\n throw new BsuirApiError(\"Invalid JSON response payload\", response.status, response.url, null);\n }\n}\n\nexport async function requestJson<T>(\n config: InternalClientConfig,\n path: string,\n options: RequestOptions = {}\n): Promise<T> {\n const endpoint = buildUrl(config.baseUrl, path, options.query);\n const headers = new Headers({\n Accept: \"application/json\"\n });\n\n if (config.userAgent) {\n headers.set(\"User-Agent\", config.userAgent);\n }\n\n for (let attempt = 0; attempt <= config.retries; attempt += 1) {\n const requestSignal = mergeSignals(options.signal, config.timeoutMs);\n try {\n const response = await config.fetchImpl(endpoint, {\n method: \"GET\",\n headers,\n signal: requestSignal\n });\n\n if (!response.ok) {\n const errorBody = await parseBody(response);\n if (attempt < config.retries && RETRIABLE_STATUS_CODES.has(response.status)) {\n const delayMs = getRetryDelayMs(config, attempt, response.headers.get(\"retry-after\"));\n await sleep(delayMs);\n continue;\n }\n throw new BsuirApiError(\n `BSUIR API returned HTTP ${String(response.status)} for ${path}`,\n response.status,\n endpoint,\n errorBody\n );\n }\n\n return (await parseBody(response)) as T;\n } catch (error) {\n if (error instanceof BsuirApiError) {\n throw error;\n }\n\n if (isAbortError(error)) {\n if (options.signal?.aborted) {\n throw error;\n }\n throw new BsuirTimeoutError(\n `Request timed out after ${String(config.timeoutMs)}ms: ${path}`,\n endpoint,\n config.timeoutMs\n );\n }\n\n if (attempt < config.retries) {\n const delayMs = getRetryDelayMs(config, attempt);\n await sleep(delayMs);\n continue;\n }\n\n throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, error);\n }\n }\n\n throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, null);\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport { assertEmployeeUrlId, assertPositiveInt } from \"../utils/guards\";\nimport type { Announcement } from \"../types/announcement\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createAnnouncementsModule(config: InternalClientConfig) {\n return {\n async byEmployee(urlId: string, options: ReadOptions = {}): Promise<Announcement[]> {\n assertEmployeeUrlId(urlId, \"urlId\");\n return requestJson<Announcement[]>(config, \"/announcements/employees\", {\n query: { \"url-id\": urlId },\n signal: options.signal\n });\n },\n\n async byDepartment(id: number, options: ReadOptions = {}): Promise<Announcement[]> {\n assertPositiveInt(id, \"id\");\n return requestJson<Announcement[]>(config, \"/announcements/departments\", {\n query: { id },\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { Auditory } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createAuditoriesModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<Auditory[]> {\n return requestJson<Auditory[]>(config, \"/auditories\", {\n signal: options.signal\n });\n }\n };\n}\n","import { BsuirValidationError } from \"../client/errors\";\nimport { assertPositiveInt } from \"./guards\";\n\nconst CYCLE_LENGTH = 4;\n\n/**\n * Converts semester week number to BSUIR cycle week (1..4).\n * For default configuration weeks 1-2 => 1, 3-4 => 2, 5-6 => 3, 7-8 => 4.\n */\nexport function toCycleWeek(semesterWeek: number, weeksPerCycle = 2): number {\n assertPositiveInt(semesterWeek, \"semesterWeek\");\n assertPositiveInt(weeksPerCycle, \"weeksPerCycle\");\n\n return (Math.floor((semesterWeek - 1) / weeksPerCycle) % CYCLE_LENGTH) + 1;\n}\n\n/**\n * Normalizes semester week payload from API to a positive integer.\n * API can return plain text (`\"1\\n\"`) or number.\n */\nexport function parseSemesterWeek(payload: unknown): number {\n if (typeof payload === \"number\") {\n assertPositiveInt(payload, \"semesterWeek\");\n return payload;\n }\n\n if (typeof payload === \"string\") {\n const normalized = payload.trim();\n if (normalized.length > 0) {\n const parsed = Number(normalized);\n if (Number.isInteger(parsed)) {\n assertPositiveInt(parsed, \"semesterWeek\");\n return parsed;\n }\n }\n }\n\n if (typeof payload === \"object\" && payload !== null) {\n const record = payload as Record<string, unknown>;\n if (\"weekNumber\" in record) {\n return parseSemesterWeek(record.weekNumber);\n }\n if (\"currentWeek\" in record) {\n return parseSemesterWeek(record.currentWeek);\n }\n }\n\n throw new BsuirValidationError(\"'semesterWeek' response payload must be a positive integer\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { ReadOptions } from \"./types\";\nimport { parseSemesterWeek, toCycleWeek } from \"../utils/week\";\n\nexport function createCurrentWeekModule(config: InternalClientConfig) {\n /**\n * Returns current semester week number.\n */\n async function get(options: ReadOptions = {}): Promise<number> {\n const payload = await requestJson<unknown>(config, \"/schedule/current-week\", {\n signal: options.signal\n });\n return parseSemesterWeek(payload);\n }\n\n /**\n * Returns current cycle week number (1..4).\n */\n async function getCycle(options: ReadOptions & { weeksPerCycle?: number } = {}): Promise<number> {\n const semesterWeek = await get(options);\n return toCycleWeek(semesterWeek, options.weeksPerCycle);\n }\n\n return { get, getCycle };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { Department } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createDepartmentsModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<Department[]> {\n return requestJson<Department[]>(config, \"/departments\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { EmployeeCatalogItem } from \"../types/employee\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createEmployeesModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<EmployeeCatalogItem[]> {\n return requestJson<EmployeeCatalogItem[]>(config, \"/employees/all\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { Faculty } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createFacultiesModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<Faculty[]> {\n return requestJson<Faculty[]>(config, \"/faculties\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { StudentGroupCatalogItem } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createGroupsModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<StudentGroupCatalogItem[]> {\n return requestJson<StudentGroupCatalogItem[]>(config, \"/student-groups\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport { assertEmployeeUrlId, assertGroupNumber, assertPositiveInt } from \"../utils/guards\";\nimport type { ApiDateResponse } from \"../types/common\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createLastUpdateModule(config: InternalClientConfig) {\n return {\n async byGroup(\n params: { groupNumber: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"groupNumber\" in params) {\n assertGroupNumber(params.groupNumber, \"groupNumber\");\n query = { groupNumber: params.groupNumber };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n\n return requestJson<ApiDateResponse>(config, \"/last-update-date/student-group\", {\n query,\n signal: options.signal\n });\n },\n\n async byEmployee(\n params: { urlId: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"urlId\" in params) {\n assertEmployeeUrlId(params.urlId, \"urlId\");\n query = { \"url-id\": params.urlId };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n\n return requestJson<ApiDateResponse>(config, \"/last-update-date/employee\", {\n query,\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport { assertEmployeeUrlId, assertGroupNumber, assertPositiveInt } from \"../utils/guards\";\nimport type {\n FlattenedLessonsByDay,\n FlattenedScheduleItem,\n NormalizedScheduleResponse,\n ScheduleFilterOptions,\n ScheduleResponse\n} from \"../types/schedule\";\nimport type { ApiDateResponse } from \"../types/common\";\nimport type { Weekday } from \"../types/common\";\nimport type { ReadOptions } from \"./types\";\nimport { parseSemesterWeek, toCycleWeek } from \"../utils/week\";\n\nconst WEEKDAYS: Weekday[] = [\n \"Понедельник\",\n \"Вторник\",\n \"Среда\",\n \"Четверг\",\n \"Пятница\",\n \"Суббота\"\n];\n\nfunction normalizeSchedule(response: ScheduleResponse): NormalizedScheduleResponse {\n const lessons: FlattenedScheduleItem[] = [];\n const lessonsByDay: FlattenedLessonsByDay = {};\n const safeSchedules = response.schedules ?? {};\n const safeExams = response.exams;\n for (const day of WEEKDAYS) {\n const dayItems = safeSchedules[day] ?? [];\n const flattenedDayItems = dayItems.map((item) => ({ ...item, day, source: \"schedules\" as const }));\n lessonsByDay[day] = flattenedDayItems;\n for (const item of dayItems) {\n lessons.push({ ...item, day, source: \"schedules\" });\n }\n }\n\n for (const exam of safeExams) {\n lessons.push({\n ...exam,\n day: null,\n // Exams are not grouped by weekday in API response.\n source: \"exams\"\n });\n }\n\n const scheduleLessons = lessons.filter((lesson) => lesson.source === \"schedules\");\n const examLessons = lessons.filter((lesson) => lesson.source === \"exams\");\n\n return {\n ...response,\n schedules: safeSchedules,\n exams: safeExams,\n lessons,\n lessonsByDay,\n scheduleLessons,\n examLessons\n };\n}\n\nfunction matchesFilter(item: FlattenedScheduleItem, filter: ScheduleFilterOptions): boolean {\n if (filter.source && item.source !== filter.source) {\n return false;\n }\n\n if (filter.weekday && item.day !== filter.weekday) {\n return false;\n }\n\n if (typeof filter.weekNumber === \"number\") {\n if (!Array.isArray(item.weekNumber) || !item.weekNumber.includes(filter.weekNumber)) {\n return false;\n }\n }\n\n if (typeof filter.subgroup === \"number\" && item.numSubgroup !== filter.subgroup) {\n return false;\n }\n\n if (filter.lessonTypeAbbrev) {\n const types = Array.isArray(filter.lessonTypeAbbrev)\n ? filter.lessonTypeAbbrev\n : [filter.lessonTypeAbbrev];\n if (!item.lessonTypeAbbrev || !types.includes(item.lessonTypeAbbrev)) {\n return false;\n }\n }\n\n if (filter.subjectQuery) {\n const query = filter.subjectQuery.toLowerCase();\n const haystack = `${item.subject} ${item.subjectFullName} ${item.note ?? \"\"}`.toLowerCase();\n if (!haystack.includes(query)) {\n return false;\n }\n }\n\n if (filter.employeeUrlId) {\n const employeeMatch = item.employees?.some((employee) => employee.urlId === filter.employeeUrlId);\n if (!employeeMatch) {\n return false;\n }\n }\n\n if (filter.auditory && !item.auditories.includes(filter.auditory)) {\n return false;\n }\n\n return true;\n}\n\nfunction filterLessons(\n response: NormalizedScheduleResponse,\n filter: ScheduleFilterOptions\n): FlattenedScheduleItem[] {\n return response.lessons.filter((item) => matchesFilter(item, filter));\n}\n\nexport function createScheduleModule(config: InternalClientConfig) {\n /**\n * Returns schedule for a student group.\n * By default returns normalized payload, unless `raw: true` is passed.\n */\n async function getGroup<TRaw extends boolean | undefined = undefined>(\n groupNumber: string,\n options: ReadOptions & { raw?: TRaw } = {}\n ): Promise<TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse> {\n assertGroupNumber(groupNumber, \"groupNumber\");\n const response = await requestJson<ScheduleResponse>(config, \"/schedule\", {\n query: { studentGroup: groupNumber },\n signal: options.signal\n });\n const result = options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);\n return result as TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse;\n }\n\n /**\n * Returns schedule for an employee.\n * By default returns normalized payload, unless `raw: true` is passed.\n */\n async function getEmployee<TRaw extends boolean | undefined = undefined>(\n urlId: string,\n options: ReadOptions & { raw?: TRaw } = {}\n ): Promise<TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse> {\n assertEmployeeUrlId(urlId, \"urlId\");\n const response = await requestJson<ScheduleResponse>(\n config,\n `/employees/schedule/${encodeURIComponent(urlId)}`,\n {\n signal: options.signal\n }\n );\n const result = options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);\n return result as TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse;\n }\n\n /**\n * Returns filtered schedule items for a group from normalized schedule payload.\n */\n async function getGroupFiltered(\n groupNumber: string,\n filter: ScheduleFilterOptions,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n const normalized = await getGroup(groupNumber, { ...options, raw: false });\n return filterLessons(normalized, filter);\n }\n\n /**\n * Returns filtered schedule items for an employee from normalized schedule payload.\n */\n async function getEmployeeFiltered(\n urlId: string,\n filter: ScheduleFilterOptions,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n const normalized = await getEmployee(urlId, { ...options, raw: false });\n return filterLessons(normalized, filter);\n }\n\n async function getCurrentWeek(options: ReadOptions = {}): Promise<number> {\n const payload = await requestJson<unknown>(config, \"/schedule/current-week\", {\n signal: options.signal\n });\n return parseSemesterWeek(payload);\n }\n\n async function getCurrentCycleWeek(\n options: ReadOptions & { weeksPerCycle?: number } = {}\n ): Promise<number> {\n const semesterWeek = await getCurrentWeek(options);\n return toCycleWeek(semesterWeek, options.weeksPerCycle);\n }\n\n return {\n getGroup,\n getEmployee,\n getGroupFiltered,\n getEmployeeFiltered,\n\n async getGroupExams(groupNumber: string, options: ReadOptions = {}): Promise<FlattenedScheduleItem[]> {\n return getGroupFiltered(groupNumber, { source: \"exams\" }, options);\n },\n\n async getEmployeeExams(urlId: string, options: ReadOptions = {}): Promise<FlattenedScheduleItem[]> {\n return getEmployeeFiltered(urlId, { source: \"exams\" }, options);\n },\n\n async getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n assertPositiveInt(subgroup, \"subgroup\");\n return getGroupFiltered(groupNumber, { source: \"schedules\", subgroup }, options);\n },\n\n async getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n assertPositiveInt(subgroup, \"subgroup\");\n return getEmployeeFiltered(urlId, { source: \"schedules\", subgroup }, options);\n },\n\n getCurrentWeek,\n getCurrentCycleWeek,\n\n async getLastUpdateByGroup(\n params: { groupNumber: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"groupNumber\" in params) {\n assertGroupNumber(params.groupNumber, \"groupNumber\");\n query = { groupNumber: params.groupNumber };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n return requestJson<ApiDateResponse>(config, \"/last-update-date/student-group\", {\n query,\n signal: options.signal\n });\n },\n\n async getLastUpdateByEmployee(\n params: { urlId: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"urlId\" in params) {\n assertEmployeeUrlId(params.urlId, \"urlId\");\n query = { \"url-id\": params.urlId };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n return requestJson<ApiDateResponse>(config, \"/last-update-date/employee\", {\n query,\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { Speciality } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createSpecialitiesModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<Speciality[]> {\n return requestJson<Speciality[]>(config, \"/specialities\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { BsuirClientOptions, InternalClientConfig } from \"./types\";\nimport { createAnnouncementsModule } from \"../modules/announcements\";\nimport { createAuditoriesModule } from \"../modules/auditories\";\nimport { createCurrentWeekModule } from \"../modules/currentWeek\";\nimport { createDepartmentsModule } from \"../modules/departments\";\nimport { createEmployeesModule } from \"../modules/employees\";\nimport { createFacultiesModule } from \"../modules/faculties\";\nimport { createGroupsModule } from \"../modules/groups\";\nimport { createLastUpdateModule } from \"../modules/lastUpdate\";\nimport { createScheduleModule } from \"../modules/schedule\";\nimport { createSpecialitiesModule } from \"../modules/specialities\";\n\nconst DEFAULT_BASE_URL = \"https://iis.bsuir.by/api/v1\";\n\nfunction resolveFetch(customFetch?: typeof globalThis.fetch): typeof globalThis.fetch {\n if (customFetch) {\n return customFetch;\n }\n\n if (typeof globalThis.fetch !== \"function\") {\n throw new Error(\"Global fetch is unavailable. Provide 'fetch' in createBsuirClient options.\");\n }\n\n return globalThis.fetch;\n}\n\nfunction createInternalConfig(options: BsuirClientOptions = {}): InternalClientConfig {\n return {\n baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,\n fetchImpl: resolveFetch(options.fetch),\n timeoutMs: options.timeoutMs ?? 10_000,\n retries: options.retries ?? 1,\n retryDelayMs: options.retryDelayMs ?? 300,\n retryMaxDelayMs: options.retryMaxDelayMs ?? 3_000,\n retryJitter: options.retryJitter ?? true,\n userAgent: options.userAgent,\n defaultRaw: options.defaultRaw ?? false\n };\n}\n\n/**\n * Creates a configured BSUIR IIS API client.\n */\nexport function createBsuirClient(options: BsuirClientOptions = {}) {\n const config = createInternalConfig(options);\n\n return {\n schedule: createScheduleModule(config),\n groups: createGroupsModule(config),\n employees: createEmployeesModule(config),\n faculties: createFacultiesModule(config),\n departments: createDepartmentsModule(config),\n specialities: createSpecialitiesModule(config),\n announcements: createAnnouncementsModule(config),\n auditories: createAuditoriesModule(config),\n lastUpdate: createLastUpdateModule(config),\n currentWeek: createCurrentWeekModule(config)\n };\n}\n\n/**\n * Public client contract returned by {@link createBsuirClient}.\n */\nexport type BsuirClient = ReturnType<typeof createBsuirClient>;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client/errors.ts","../src/utils/guards.ts","../src/client/http.ts","../src/modules/announcements.ts","../src/modules/auditories.ts","../src/modules/departments.ts","../src/modules/employees.ts","../src/modules/faculties.ts","../src/modules/groups.ts","../src/modules/lastUpdate.ts","../src/utils/week.ts","../src/modules/schedule.ts","../src/modules/specialities.ts","../src/client/createClient.ts"],"sourcesContent":["function fixErrorPrototype(instance: Error, prototype: object): void {\n Object.setPrototypeOf(instance, prototype);\n}\n\nexport class BsuirApiError extends Error {\n readonly status: number;\n readonly endpoint: string;\n readonly body: unknown;\n\n constructor(message: string, status: number, endpoint: string, body: unknown) {\n super(message);\n fixErrorPrototype(this, BsuirApiError.prototype);\n this.name = \"BsuirApiError\";\n this.status = status;\n this.endpoint = endpoint;\n this.body = body;\n }\n}\n\nexport class BsuirNetworkError extends Error {\n readonly endpoint: string;\n readonly causeError: unknown;\n\n constructor(message: string, endpoint: string, causeError: unknown) {\n super(message, { cause: causeError });\n fixErrorPrototype(this, BsuirNetworkError.prototype);\n this.name = \"BsuirNetworkError\";\n this.endpoint = endpoint;\n this.causeError = causeError;\n }\n}\n\nexport class BsuirTimeoutError extends Error {\n readonly endpoint: string;\n readonly timeoutMs: number;\n\n constructor(message: string, endpoint: string, timeoutMs: number) {\n super(message);\n fixErrorPrototype(this, BsuirTimeoutError.prototype);\n this.name = \"BsuirTimeoutError\";\n this.endpoint = endpoint;\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class BsuirValidationError extends Error {\n constructor(message: string) {\n super(message);\n fixErrorPrototype(this, BsuirValidationError.prototype);\n this.name = \"BsuirValidationError\";\n }\n}\n","import { BsuirValidationError } from \"../client/errors\";\n\nconst GROUP_NUMBER_PATTERN = /^\\d+$/;\nconst EMPLOYEE_URL_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/i;\n\nexport function assertNonEmptyString(value: unknown, fieldName: string): asserts value is string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new BsuirValidationError(`'${fieldName}' must be a non-empty string`);\n }\n}\n\nexport function assertPositiveInt(value: unknown, fieldName: string): asserts value is number {\n if (typeof value !== \"number\" || !Number.isInteger(value) || value <= 0) {\n throw new BsuirValidationError(`'${fieldName}' must be a positive integer`);\n }\n}\n\nexport function assertGroupNumber(value: unknown, fieldName = \"groupNumber\"): asserts value is string {\n assertNonEmptyString(value, fieldName);\n if (!GROUP_NUMBER_PATTERN.test(value)) {\n throw new BsuirValidationError(`'${fieldName}' must contain only digits`);\n }\n}\n\nexport function assertEmployeeUrlId(value: unknown, fieldName = \"urlId\"): asserts value is string {\n assertNonEmptyString(value, fieldName);\n if (!EMPLOYEE_URL_ID_PATTERN.test(value)) {\n throw new BsuirValidationError(\n `'${fieldName}' must be a valid slug (letters, digits, hyphen)`\n );\n }\n}\n\nexport function isAbortError(error: unknown): boolean {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n return true;\n }\n\n if (typeof error === \"object\" && error !== null) {\n const maybeError = error as { name?: unknown; code?: unknown };\n return maybeError.name === \"AbortError\" || maybeError.code === \"ABORT_ERR\";\n }\n\n return false;\n}\n","import { BsuirApiError, BsuirNetworkError, BsuirTimeoutError } from \"./errors\";\nimport type { InternalClientConfig, QueryParams, RequestOptions } from \"./types\";\nimport { isAbortError } from \"../utils/guards\";\n\nconst RETRIABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\n\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n const normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n const url = new URL(`${normalizedBase}${normalizedPath}`);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) {\n continue;\n }\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n}\n\nfunction mergeSignals(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal {\n if (typeof AbortSignal.any === \"function\") {\n return AbortSignal.any([AbortSignal.timeout(timeoutMs), signal].filter(Boolean) as AbortSignal[]);\n }\n\n if (signal) {\n return signal;\n }\n\n return AbortSignal.timeout(timeoutMs);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseRetryAfterMs(retryAfter: string | null): number | null {\n if (!retryAfter) {\n return null;\n }\n\n const asSeconds = Number(retryAfter);\n if (!Number.isNaN(asSeconds)) {\n return Math.max(0, Math.floor(asSeconds * 1000));\n }\n\n const dateValue = Date.parse(retryAfter);\n if (!Number.isNaN(dateValue)) {\n return Math.max(0, dateValue - Date.now());\n }\n\n return null;\n}\n\nfunction getRetryDelayMs(\n config: InternalClientConfig,\n attempt: number,\n retryAfterHeader?: string | null\n): number {\n const retryAfterDelay = parseRetryAfterMs(retryAfterHeader ?? null);\n if (retryAfterDelay !== null) {\n return Math.min(retryAfterDelay, config.retryMaxDelayMs);\n }\n\n const exponent = Math.max(0, attempt);\n const baseDelay = Math.min(config.retryDelayMs * 2 ** exponent, config.retryMaxDelayMs);\n if (!config.retryJitter) {\n return baseDelay;\n }\n\n const jitterFactor = 0.75 + Math.random() * 0.5;\n return Math.floor(baseDelay * jitterFactor);\n}\n\nasync function parseBody(response: Response): Promise<unknown> {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"application/json\")) {\n return response.text();\n }\n\n try {\n return await response.json();\n } catch {\n throw new BsuirApiError(\"Invalid JSON response payload\", response.status, response.url, null);\n }\n}\n\nexport async function requestJson<T>(\n config: InternalClientConfig,\n path: string,\n options: RequestOptions = {}\n): Promise<T> {\n const endpoint = buildUrl(config.baseUrl, path, options.query);\n const headers = new Headers({\n Accept: \"application/json\"\n });\n\n if (config.userAgent) {\n headers.set(\"User-Agent\", config.userAgent);\n }\n\n for (let attempt = 0; attempt <= config.retries; attempt += 1) {\n const requestSignal = mergeSignals(options.signal, config.timeoutMs);\n try {\n const response = await config.fetchImpl(endpoint, {\n method: \"GET\",\n headers,\n signal: requestSignal\n });\n\n if (!response.ok) {\n const errorBody = await parseBody(response);\n if (attempt < config.retries && RETRIABLE_STATUS_CODES.has(response.status)) {\n const delayMs = getRetryDelayMs(config, attempt, response.headers.get(\"retry-after\"));\n await sleep(delayMs);\n continue;\n }\n throw new BsuirApiError(\n `BSUIR API returned HTTP ${String(response.status)} for ${path}`,\n response.status,\n endpoint,\n errorBody\n );\n }\n\n return (await parseBody(response)) as T;\n } catch (error) {\n if (error instanceof BsuirApiError) {\n throw error;\n }\n\n if (isAbortError(error)) {\n if (options.signal?.aborted) {\n throw error;\n }\n throw new BsuirTimeoutError(\n `Request timed out after ${String(config.timeoutMs)}ms: ${path}`,\n endpoint,\n config.timeoutMs\n );\n }\n\n if (attempt < config.retries) {\n const delayMs = getRetryDelayMs(config, attempt);\n await sleep(delayMs);\n continue;\n }\n\n throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, error);\n }\n }\n\n throw new BsuirNetworkError(`Unexpected retry loop termination for ${path}`, endpoint, null);\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport { assertEmployeeUrlId, assertPositiveInt } from \"../utils/guards\";\nimport type { Announcement } from \"../types/announcement\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createAnnouncementsModule(config: InternalClientConfig) {\n return {\n async byEmployee(urlId: string, options: ReadOptions = {}): Promise<Announcement[]> {\n assertEmployeeUrlId(urlId, \"urlId\");\n return requestJson<Announcement[]>(config, \"/announcements/employees\", {\n query: { \"url-id\": urlId },\n signal: options.signal\n });\n },\n\n async byDepartment(id: number, options: ReadOptions = {}): Promise<Announcement[]> {\n assertPositiveInt(id, \"id\");\n return requestJson<Announcement[]>(config, \"/announcements/departments\", {\n query: { id },\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { Auditory } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createAuditoriesModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<Auditory[]> {\n return requestJson<Auditory[]>(config, \"/auditories\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { Department } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createDepartmentsModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<Department[]> {\n return requestJson<Department[]>(config, \"/departments\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { EmployeeCatalogItem } from \"../types/employee\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createEmployeesModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<EmployeeCatalogItem[]> {\n return requestJson<EmployeeCatalogItem[]>(config, \"/employees/all\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { Faculty } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createFacultiesModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<Faculty[]> {\n return requestJson<Faculty[]>(config, \"/faculties\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { StudentGroupCatalogItem } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createGroupsModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<StudentGroupCatalogItem[]> {\n return requestJson<StudentGroupCatalogItem[]>(config, \"/student-groups\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport { assertEmployeeUrlId, assertGroupNumber, assertPositiveInt } from \"../utils/guards\";\nimport type { ApiDateResponse } from \"../types/common\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createLastUpdateModule(config: InternalClientConfig) {\n return {\n async byGroup(\n params: { groupNumber: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"groupNumber\" in params) {\n assertGroupNumber(params.groupNumber, \"groupNumber\");\n query = { groupNumber: params.groupNumber };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n\n return requestJson<ApiDateResponse>(config, \"/last-update-date/student-group\", {\n query,\n signal: options.signal\n });\n },\n\n async byEmployee(\n params: { urlId: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"urlId\" in params) {\n assertEmployeeUrlId(params.urlId, \"urlId\");\n query = { \"url-id\": params.urlId };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n\n return requestJson<ApiDateResponse>(config, \"/last-update-date/employee\", {\n query,\n signal: options.signal\n });\n }\n };\n}\n","import { BsuirValidationError } from \"../client/errors\";\nimport { assertPositiveInt } from \"./guards\";\n\n/**\n * Normalizes current week payload from API to a positive integer.\n * API can return plain text (`\"1\\n\"`) or number.\n */\nexport function parseCurrentWeek(payload: unknown): number {\n if (typeof payload === \"number\") {\n assertPositiveInt(payload, \"currentWeek\");\n return payload;\n }\n\n if (typeof payload === \"string\") {\n const normalized = payload.trim();\n if (normalized.length > 0) {\n const parsed = Number(normalized);\n if (Number.isInteger(parsed)) {\n assertPositiveInt(parsed, \"currentWeek\");\n return parsed;\n }\n }\n }\n\n if (typeof payload === \"object\" && payload !== null) {\n const record = payload as Record<string, unknown>;\n if (\"weekNumber\" in record) {\n return parseCurrentWeek(record.weekNumber);\n }\n if (\"currentWeek\" in record) {\n return parseCurrentWeek(record.currentWeek);\n }\n }\n\n throw new BsuirValidationError(\"'currentWeek' response payload must be a positive integer\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport { assertEmployeeUrlId, assertGroupNumber, assertPositiveInt } from \"../utils/guards\";\nimport type {\n FlattenedLessonsByDay,\n FlattenedScheduleItem,\n NormalizedScheduleResponse,\n ScheduleFilterOptions,\n ScheduleResponse\n} from \"../types/schedule\";\nimport type { ApiDateResponse } from \"../types/common\";\nimport type { Weekday } from \"../types/common\";\nimport type { ReadOptions } from \"./types\";\nimport { parseCurrentWeek } from \"../utils/week\";\n\nconst WEEKDAYS: Weekday[] = [\n \"Понедельник\",\n \"Вторник\",\n \"Среда\",\n \"Четверг\",\n \"Пятница\",\n \"Суббота\"\n];\n\nfunction normalizeSchedule(response: ScheduleResponse): NormalizedScheduleResponse {\n const lessons: FlattenedScheduleItem[] = [];\n const lessonsByDay: FlattenedLessonsByDay = {};\n const safeSchedules = response.schedules ?? {};\n const safeExams = response.exams ?? [];\n for (const day of WEEKDAYS) {\n const dayItems = safeSchedules[day] ?? [];\n const flattenedDayItems = dayItems.map((item) => ({ ...item, day, source: \"schedules\" as const }));\n lessonsByDay[day] = flattenedDayItems;\n for (const item of dayItems) {\n lessons.push({ ...item, day, source: \"schedules\" });\n }\n }\n\n for (const exam of safeExams) {\n lessons.push({\n ...exam,\n day: null,\n // Exams are not grouped by weekday in API response.\n source: \"exams\"\n });\n }\n\n const scheduleLessons = lessons.filter((lesson) => lesson.source === \"schedules\");\n const examLessons = lessons.filter((lesson) => lesson.source === \"exams\");\n\n return {\n ...response,\n schedules: safeSchedules,\n exams: safeExams,\n lessons,\n lessonsByDay,\n scheduleLessons,\n examLessons\n };\n}\n\nfunction matchesFilter(item: FlattenedScheduleItem, filter: ScheduleFilterOptions): boolean {\n if (filter.source && item.source !== filter.source) {\n return false;\n }\n\n if (filter.weekday && item.day !== filter.weekday) {\n return false;\n }\n\n if (typeof filter.weekNumber === \"number\") {\n if (!Array.isArray(item.weekNumber) || !item.weekNumber.includes(filter.weekNumber)) {\n return false;\n }\n }\n\n if (typeof filter.subgroup === \"number\" && item.numSubgroup !== filter.subgroup) {\n return false;\n }\n\n if (filter.lessonTypeAbbrev) {\n const types = Array.isArray(filter.lessonTypeAbbrev)\n ? filter.lessonTypeAbbrev\n : [filter.lessonTypeAbbrev];\n if (!item.lessonTypeAbbrev || !types.includes(item.lessonTypeAbbrev)) {\n return false;\n }\n }\n\n if (filter.subjectQuery) {\n const query = filter.subjectQuery.toLowerCase();\n const haystack = `${item.subject} ${item.subjectFullName} ${item.note ?? \"\"}`.toLowerCase();\n if (!haystack.includes(query)) {\n return false;\n }\n }\n\n if (filter.employeeUrlId) {\n const employeeMatch = item.employees?.some((employee) => employee.urlId === filter.employeeUrlId);\n if (!employeeMatch) {\n return false;\n }\n }\n\n if (filter.auditory && !item.auditories.includes(filter.auditory)) {\n return false;\n }\n\n return true;\n}\n\nfunction filterLessons(\n response: NormalizedScheduleResponse,\n filter: ScheduleFilterOptions\n): FlattenedScheduleItem[] {\n return response.lessons.filter((item) => matchesFilter(item, filter));\n}\n\nexport function createScheduleModule(config: InternalClientConfig) {\n /**\n * Returns schedule for a student group.\n * By default returns normalized payload, unless `raw: true` is passed.\n */\n async function getGroup<TRaw extends boolean | undefined = undefined>(\n groupNumber: string,\n options: ReadOptions & { raw?: TRaw } = {}\n ): Promise<TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse> {\n assertGroupNumber(groupNumber, \"groupNumber\");\n const response = await requestJson<ScheduleResponse>(config, \"/schedule\", {\n query: { studentGroup: groupNumber },\n signal: options.signal\n });\n const result = options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);\n return result as TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse;\n }\n\n /**\n * Returns schedule for an employee.\n * By default returns normalized payload, unless `raw: true` is passed.\n */\n async function getEmployee<TRaw extends boolean | undefined = undefined>(\n urlId: string,\n options: ReadOptions & { raw?: TRaw } = {}\n ): Promise<TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse> {\n assertEmployeeUrlId(urlId, \"urlId\");\n const response = await requestJson<ScheduleResponse>(\n config,\n `/employees/schedule/${encodeURIComponent(urlId)}`,\n {\n signal: options.signal\n }\n );\n const result = options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);\n return result as TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse;\n }\n\n /**\n * Returns filtered schedule items for a group from normalized schedule payload.\n */\n async function getGroupFiltered(\n groupNumber: string,\n filter: ScheduleFilterOptions,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n const normalized = await getGroup(groupNumber, { ...options, raw: false });\n return filterLessons(normalized, filter);\n }\n\n /**\n * Returns filtered schedule items for an employee from normalized schedule payload.\n */\n async function getEmployeeFiltered(\n urlId: string,\n filter: ScheduleFilterOptions,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n const normalized = await getEmployee(urlId, { ...options, raw: false });\n return filterLessons(normalized, filter);\n }\n\n async function getCurrentWeek(options: ReadOptions = {}): Promise<number> {\n const payload = await requestJson<unknown>(config, \"/schedule/current-week\", {\n signal: options.signal\n });\n return parseCurrentWeek(payload);\n }\n\n return {\n getGroup,\n getEmployee,\n getGroupFiltered,\n getEmployeeFiltered,\n\n async getGroupExams(groupNumber: string, options: ReadOptions = {}): Promise<FlattenedScheduleItem[]> {\n return getGroupFiltered(groupNumber, { source: \"exams\" }, options);\n },\n\n async getEmployeeExams(urlId: string, options: ReadOptions = {}): Promise<FlattenedScheduleItem[]> {\n return getEmployeeFiltered(urlId, { source: \"exams\" }, options);\n },\n\n async getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n assertPositiveInt(subgroup, \"subgroup\");\n return getGroupFiltered(groupNumber, { source: \"schedules\", subgroup }, options);\n },\n\n async getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n assertPositiveInt(subgroup, \"subgroup\");\n return getEmployeeFiltered(urlId, { source: \"schedules\", subgroup }, options);\n },\n\n getCurrentWeek,\n\n async getLastUpdateByGroup(\n params: { groupNumber: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"groupNumber\" in params) {\n assertGroupNumber(params.groupNumber, \"groupNumber\");\n query = { groupNumber: params.groupNumber };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n return requestJson<ApiDateResponse>(config, \"/last-update-date/student-group\", {\n query,\n signal: options.signal\n });\n },\n\n async getLastUpdateByEmployee(\n params: { urlId: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"urlId\" in params) {\n assertEmployeeUrlId(params.urlId, \"urlId\");\n query = { \"url-id\": params.urlId };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n return requestJson<ApiDateResponse>(config, \"/last-update-date/employee\", {\n query,\n signal: options.signal\n });\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport { requestJson } from \"../client/http\";\nimport type { Speciality } from \"../types/catalog\";\nimport type { ReadOptions } from \"./types\";\n\nexport function createSpecialitiesModule(config: InternalClientConfig) {\n return {\n async listAll(options: ReadOptions = {}): Promise<Speciality[]> {\n return requestJson<Speciality[]>(config, \"/specialities\", {\n signal: options.signal\n });\n }\n };\n}\n","import type { BsuirClientOptions, InternalClientConfig } from \"./types\";\nimport { createAnnouncementsModule } from \"../modules/announcements\";\nimport { createAuditoriesModule } from \"../modules/auditories\";\nimport { createDepartmentsModule } from \"../modules/departments\";\nimport { createEmployeesModule } from \"../modules/employees\";\nimport { createFacultiesModule } from \"../modules/faculties\";\nimport { createGroupsModule } from \"../modules/groups\";\nimport { createLastUpdateModule } from \"../modules/lastUpdate\";\nimport { createScheduleModule } from \"../modules/schedule\";\nimport { createSpecialitiesModule } from \"../modules/specialities\";\n\nconst DEFAULT_BASE_URL = \"https://iis.bsuir.by/api/v1\";\n\nfunction resolveFetch(customFetch?: typeof globalThis.fetch): typeof globalThis.fetch {\n if (customFetch) {\n return customFetch;\n }\n\n if (typeof globalThis.fetch !== \"function\") {\n throw new Error(\"Global fetch is unavailable. Provide 'fetch' in createBsuirClient options.\");\n }\n\n return globalThis.fetch;\n}\n\nfunction createInternalConfig(options: BsuirClientOptions = {}): InternalClientConfig {\n return {\n baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,\n fetchImpl: resolveFetch(options.fetch),\n timeoutMs: options.timeoutMs ?? 10_000,\n retries: options.retries ?? 1,\n retryDelayMs: options.retryDelayMs ?? 300,\n retryMaxDelayMs: options.retryMaxDelayMs ?? 3_000,\n retryJitter: options.retryJitter ?? true,\n userAgent: options.userAgent,\n defaultRaw: options.defaultRaw ?? false\n };\n}\n\n/**\n * Creates a configured BSUIR IIS API client.\n */\nexport function createBsuirClient(options: BsuirClientOptions = {}) {\n const config = createInternalConfig(options);\n const schedule = createScheduleModule(config);\n\n return {\n schedule,\n groups: createGroupsModule(config),\n employees: createEmployeesModule(config),\n faculties: createFacultiesModule(config),\n departments: createDepartmentsModule(config),\n specialities: createSpecialitiesModule(config),\n announcements: createAnnouncementsModule(config),\n auditories: createAuditoriesModule(config),\n lastUpdate: createLastUpdateModule(config),\n currentWeek: {\n get: schedule.getCurrentWeek\n }\n };\n}\n\n/**\n * Public client contract returned by {@link createBsuirClient}.\n */\nexport type BsuirClient = ReturnType<typeof createBsuirClient>;\n"],"mappings":"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","names":[]}
|