bsuir-iis-api 0.1.0 → 0.2.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 CHANGED
@@ -1,16 +1,33 @@
1
- # Changelog
2
-
3
- All notable changes to this project are documented in this file.
4
-
5
- The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
-
8
- ## [0.1.0] - 2026-03-15
9
-
10
- ### Added
11
-
12
- - Initial ESM-only TypeScript SDK implementation.
13
- - Typed modules for schedules, catalogs, announcements, current week and last update.
14
- - Retry/timeout-enabled HTTP client with typed errors.
15
- - Unit and smoke tests with Vitest.
16
- - CI, release and npm publishing scaffolding.
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-03-15
9
+
10
+ ### Added (0.2.0)
11
+
12
+ - Schedule filtering helpers: `getGroupFiltered()` and `getEmployeeFiltered()`.
13
+ - Additional schedule DX helpers: exams-only and subgroup shortcuts.
14
+ - Expanded normalized schedule payload with `lessonsByDay`, `scheduleLessons`, and `examLessons`.
15
+ - Retry enhancements with exponential backoff, jitter, and `Retry-After` support.
16
+
17
+ ## [0.1.1] - 2026-03-15
18
+
19
+ ### Changed (0.1.1)
20
+
21
+ - Hardened timeout test logic to avoid CI race conditions with aborted signals.
22
+ - Updated release workflow to support manual dispatch and OIDC trusted publishing.
23
+ - Normalized `repository.url` in `package.json` for npm metadata compliance.
24
+
25
+ ## [0.1.0] - 2026-03-15
26
+
27
+ ### Added (0.1.0)
28
+
29
+ - Initial ESM-only TypeScript SDK implementation.
30
+ - Typed modules for schedules, catalogs, announcements, current week and last update.
31
+ - Retry/timeout-enabled HTTP client with typed errors.
32
+ - Unit and smoke tests with Vitest.
33
+ - CI, release and npm publishing scaffolding.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 kotru21
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kotru21
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,98 +1,114 @@
1
- # bsuir-iis-api
2
-
3
- Type-safe ESM SDK for [BSUIR IIS API](https://iis.bsuir.by/api/v1) with support for Node.js and browser runtimes.
4
-
5
- ## Install
6
-
7
- ```bash
8
- npm install bsuir-iis-api
9
- ```
10
-
11
- ## Quick start
12
-
13
- ```ts
14
- import { createBsuirClient } from "bsuir-iis-api";
15
-
16
- const client = createBsuirClient();
17
-
18
- const schedule = await client.schedule.getGroup("053503");
19
- if ("lessons" in schedule) {
20
- console.log(schedule.lessons.length);
21
- }
22
- ```
23
-
24
- ## Client options
25
-
26
- ```ts
27
- const client = createBsuirClient({
28
- baseUrl: "https://iis.bsuir.by/api/v1",
29
- timeoutMs: 10000,
30
- retries: 2,
31
- retryDelayMs: 300,
32
- defaultRaw: false
33
- });
34
- ```
35
-
36
- - `fetch` can be passed for custom runtime/testing.
37
- - `AbortSignal` is supported by all read methods.
38
-
39
- ## API
40
-
41
- ### Schedule
42
-
43
- - `client.schedule.getGroup(groupNumber, options?)`
44
- - `client.schedule.getEmployee(urlId, options?)`
45
- - `client.schedule.getCurrentWeek(options?)`
46
- - `client.schedule.getLastUpdateByGroup({ groupNumber } | { id }, options?)`
47
- - `client.schedule.getLastUpdateByEmployee({ urlId } | { id }, options?)`
48
-
49
- ### Catalogs
50
-
51
- - `client.groups.listAll(options?)`
52
- - `client.employees.listAll(options?)`
53
- - `client.faculties.listAll(options?)`
54
- - `client.departments.listAll(options?)`
55
- - `client.specialities.listAll(options?)`
56
- - `client.auditories.listAll(options?)`
57
-
58
- ### Announcements
59
-
60
- - `client.announcements.byEmployee(urlId, options?)`
61
- - `client.announcements.byDepartment(id, options?)`
62
-
63
- ### Meta
64
-
65
- - `client.currentWeek.get(options?)`
66
- - `client.lastUpdate.byGroup({ groupNumber } | { id }, options?)`
67
- - `client.lastUpdate.byEmployee({ urlId } | { id }, options?)`
68
-
69
- ## Errors
70
-
71
- SDK throws typed errors:
72
-
73
- - `BsuirApiError` for HTTP errors (contains `status`, `endpoint`, `body`)
74
- - `BsuirNetworkError` for transport errors
75
- - `BsuirTimeoutError` for timeouts
76
- - `BsuirValidationError` for invalid input parameters
77
-
78
- ## Raw vs normalized schedule response
79
-
80
- By default, schedule methods return normalized response with `lessons` array.
81
-
82
- ```ts
83
- const raw = await client.schedule.getGroup("053503", { raw: true });
84
- ```
85
-
86
- Use `defaultRaw: true` in `createBsuirClient` to change global behavior.
87
-
88
- ## Development
89
-
90
- ```bash
91
- npm install
92
- npm run check
93
- npm run build
94
- ```
95
-
96
- ## License
97
-
98
- MIT
1
+ # bsuir-iis-api
2
+
3
+ Type-safe ESM SDK for [BSUIR IIS API](https://iis.bsuir.by/api/v1) with support for Node.js and browser runtimes.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install bsuir-iis-api
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { createBsuirClient } from "bsuir-iis-api";
15
+
16
+ const client = createBsuirClient();
17
+
18
+ const schedule = await client.schedule.getGroup("053503");
19
+ console.log(schedule.lessons.length);
20
+ ```
21
+
22
+ ## Client options
23
+
24
+ ```ts
25
+ const client = createBsuirClient({
26
+ baseUrl: "https://iis.bsuir.by/api/v1",
27
+ timeoutMs: 10000,
28
+ retries: 2,
29
+ retryDelayMs: 300,
30
+ retryMaxDelayMs: 3000,
31
+ retryJitter: true,
32
+ defaultRaw: false
33
+ });
34
+ ```
35
+
36
+ - `fetch` can be passed for custom runtime/testing.
37
+ - `AbortSignal` is supported by all read methods.
38
+
39
+ ## API
40
+
41
+ ### Schedule
42
+
43
+ - `client.schedule.getGroup(groupNumber, options?)`
44
+ - `client.schedule.getEmployee(urlId, options?)`
45
+ - `client.schedule.getGroupFiltered(groupNumber, filter, options?)`
46
+ - `client.schedule.getEmployeeFiltered(urlId, filter, options?)`
47
+ - `client.schedule.getGroupExams(groupNumber, options?)`
48
+ - `client.schedule.getEmployeeExams(urlId, options?)`
49
+ - `client.schedule.getGroupBySubgroup(groupNumber, subgroup, options?)`
50
+ - `client.schedule.getEmployeeBySubgroup(urlId, subgroup, options?)`
51
+ - `client.schedule.getCurrentWeek(options?)`
52
+ - `client.schedule.getLastUpdateByGroup({ groupNumber } | { id }, options?)`
53
+ - `client.schedule.getLastUpdateByEmployee({ urlId } | { id }, options?)`
54
+
55
+ ### Catalogs
56
+
57
+ - `client.groups.listAll(options?)`
58
+ - `client.employees.listAll(options?)`
59
+ - `client.faculties.listAll(options?)`
60
+ - `client.departments.listAll(options?)`
61
+ - `client.specialities.listAll(options?)`
62
+ - `client.auditories.listAll(options?)`
63
+
64
+ ### Announcements
65
+
66
+ - `client.announcements.byEmployee(urlId, options?)`
67
+ - `client.announcements.byDepartment(id, options?)`
68
+
69
+ ### Meta
70
+
71
+ - `client.currentWeek.get(options?)`
72
+ - `client.lastUpdate.byGroup({ groupNumber } | { id }, options?)`
73
+ - `client.lastUpdate.byEmployee({ urlId } | { id }, options?)`
74
+
75
+ ## Errors
76
+
77
+ SDK throws typed errors:
78
+
79
+ - `BsuirApiError` for HTTP errors (contains `status`, `endpoint`, `body`)
80
+ - `BsuirNetworkError` for transport errors
81
+ - `BsuirTimeoutError` for timeouts
82
+ - `BsuirValidationError` for invalid input parameters
83
+
84
+ ## Raw vs normalized schedule response
85
+
86
+ By default, schedule methods return normalized response with `lessons`, `lessonsByDay`,
87
+ `scheduleLessons`, and `examLessons`.
88
+
89
+ ```ts
90
+ const raw = await client.schedule.getGroup("053503", { raw: true });
91
+ ```
92
+
93
+ Use `defaultRaw: true` in `createBsuirClient` to change global behavior.
94
+
95
+ Filtering example:
96
+
97
+ ```ts
98
+ const exams = await client.schedule.getGroupFiltered("053503", {
99
+ source: "exams",
100
+ lessonTypeAbbrev: ["Консультация", "Экзамен"]
101
+ });
102
+ ```
103
+
104
+ ## Development
105
+
106
+ ```bash
107
+ npm install
108
+ npm run check
109
+ npm run build
110
+ ```
111
+
112
+ ## License
113
+
114
+ MIT
package/dist/index.d.ts CHANGED
@@ -7,10 +7,18 @@ interface RequestOptions {
7
7
  interface BsuirClientOptions {
8
8
  baseUrl?: string;
9
9
  fetch?: typeof globalThis.fetch;
10
+ /** Request timeout per attempt, in milliseconds. */
10
11
  timeoutMs?: number;
12
+ /** Number of retry attempts for retriable GET failures. */
11
13
  retries?: number;
14
+ /** Base retry delay before backoff, in milliseconds. */
12
15
  retryDelayMs?: number;
16
+ /** Upper bound for retry delay, in milliseconds. */
17
+ retryMaxDelayMs?: number;
18
+ /** Enable jitter for retry backoff to avoid synchronized retries. */
19
+ retryJitter?: boolean;
13
20
  userAgent?: string;
21
+ /** Force raw API payload for schedule endpoints by default. */
14
22
  defaultRaw?: boolean;
15
23
  }
16
24
 
@@ -20,8 +28,18 @@ interface ReadOptions extends RequestOptions {
20
28
 
21
29
  declare function createBsuirClient(options?: BsuirClientOptions): {
22
30
  schedule: {
23
- getGroup(groupNumber: string, options?: ReadOptions): Promise<ScheduleResponse | NormalizedScheduleResponse>;
24
- getEmployee(urlId: string, options?: ReadOptions): Promise<ScheduleResponse | NormalizedScheduleResponse>;
31
+ getGroup: <TRaw extends boolean | undefined = undefined>(groupNumber: string, options?: ReadOptions & {
32
+ raw?: TRaw;
33
+ }) => Promise<TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse>;
34
+ getEmployee: <TRaw extends boolean | undefined = undefined>(urlId: string, options?: ReadOptions & {
35
+ raw?: TRaw;
36
+ }) => Promise<TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse>;
37
+ getGroupFiltered: (groupNumber: string, filter: ScheduleFilterOptions, options?: ReadOptions) => Promise<FlattenedScheduleItem[]>;
38
+ getEmployeeFiltered: (urlId: string, filter: ScheduleFilterOptions, options?: ReadOptions) => Promise<FlattenedScheduleItem[]>;
39
+ getGroupExams(groupNumber: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
40
+ getEmployeeExams(urlId: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
41
+ getGroupBySubgroup(groupNumber: string, subgroup: number, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
42
+ getEmployeeBySubgroup(urlId: string, subgroup: number, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
25
43
  getCurrentWeek(options?: ReadOptions): Promise<number>;
26
44
  getLastUpdateByGroup(params: {
27
45
  groupNumber: string;
@@ -241,10 +259,24 @@ interface FlattenedScheduleItem extends ScheduleItem {
241
259
  day: Weekday | null;
242
260
  source: "schedules" | "exams";
243
261
  }
262
+ type FlattenedLessonsByDay = Partial<Record<Weekday, FlattenedScheduleItem[]>>;
263
+ interface ScheduleFilterOptions {
264
+ source?: "schedules" | "exams";
265
+ weekday?: Weekday;
266
+ weekNumber?: number;
267
+ subgroup?: number;
268
+ lessonTypeAbbrev?: string | string[];
269
+ subjectQuery?: string;
270
+ employeeUrlId?: string;
271
+ auditory?: string;
272
+ }
244
273
  interface NormalizedScheduleResponse extends Omit<ScheduleResponse, "schedules" | "exams"> {
245
274
  schedules: WeekScheduleMap;
246
275
  exams: ScheduleItem[];
247
276
  lessons: FlattenedScheduleItem[];
277
+ lessonsByDay: FlattenedLessonsByDay;
278
+ scheduleLessons: FlattenedScheduleItem[];
279
+ examLessons: FlattenedScheduleItem[];
248
280
  }
249
281
 
250
- 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 FlattenedScheduleItem, type LessonStudentGroup, type Maybe, type NormalizedScheduleResponse, type ScheduleItem, type ScheduleResponse, type Speciality, type StudentGroupCatalogItem, type StudentGroupShort, type WeekScheduleMap, type Weekday, createBsuirClient };
282
+ 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 ScheduleFilterOptions, type ScheduleItem, type ScheduleResponse, type Speciality, type StudentGroupCatalogItem, type StudentGroupShort, type WeekScheduleMap, type Weekday, createBsuirClient };
package/dist/index.js CHANGED
@@ -81,6 +81,33 @@ function mergeSignals(signal, timeoutMs) {
81
81
  function sleep(ms) {
82
82
  return new Promise((resolve) => setTimeout(resolve, ms));
83
83
  }
84
+ function parseRetryAfterMs(retryAfter) {
85
+ if (!retryAfter) {
86
+ return null;
87
+ }
88
+ const asSeconds = Number(retryAfter);
89
+ if (!Number.isNaN(asSeconds)) {
90
+ return Math.max(0, Math.floor(asSeconds * 1e3));
91
+ }
92
+ const dateValue = Date.parse(retryAfter);
93
+ if (!Number.isNaN(dateValue)) {
94
+ return Math.max(0, dateValue - Date.now());
95
+ }
96
+ return null;
97
+ }
98
+ function getRetryDelayMs(config, attempt, retryAfterHeader) {
99
+ const retryAfterDelay = parseRetryAfterMs(retryAfterHeader ?? null);
100
+ if (retryAfterDelay !== null) {
101
+ return Math.min(retryAfterDelay, config.retryMaxDelayMs);
102
+ }
103
+ const exponent = Math.max(0, attempt);
104
+ const baseDelay = Math.min(config.retryDelayMs * 2 ** exponent, config.retryMaxDelayMs);
105
+ if (!config.retryJitter) {
106
+ return baseDelay;
107
+ }
108
+ const jitterFactor = 0.75 + Math.random() * 0.5;
109
+ return Math.floor(baseDelay * jitterFactor);
110
+ }
84
111
  async function parseBody(response) {
85
112
  const contentType = response.headers.get("content-type") ?? "";
86
113
  if (!contentType.includes("application/json")) {
@@ -94,7 +121,6 @@ async function parseBody(response) {
94
121
  }
95
122
  async function requestJson(config, path, options = {}) {
96
123
  const endpoint = buildUrl(config.baseUrl, path, options.query);
97
- const requestSignal = mergeSignals(options.signal, config.timeoutMs);
98
124
  const headers = new Headers({
99
125
  Accept: "application/json"
100
126
  });
@@ -102,6 +128,7 @@ async function requestJson(config, path, options = {}) {
102
128
  headers.set("User-Agent", config.userAgent);
103
129
  }
104
130
  for (let attempt = 0; attempt <= config.retries; attempt += 1) {
131
+ const requestSignal = mergeSignals(options.signal, config.timeoutMs);
105
132
  try {
106
133
  const response = await config.fetchImpl(endpoint, {
107
134
  method: "GET",
@@ -111,7 +138,8 @@ async function requestJson(config, path, options = {}) {
111
138
  if (!response.ok) {
112
139
  const errorBody = await parseBody(response);
113
140
  if (attempt < config.retries && RETRIABLE_STATUS_CODES.has(response.status)) {
114
- await sleep(config.retryDelayMs * (attempt + 1));
141
+ const delayMs = getRetryDelayMs(config, attempt, response.headers.get("retry-after"));
142
+ await sleep(delayMs);
115
143
  continue;
116
144
  }
117
145
  throw new BsuirApiError(
@@ -137,7 +165,8 @@ async function requestJson(config, path, options = {}) {
137
165
  );
138
166
  }
139
167
  if (attempt < config.retries) {
140
- await sleep(config.retryDelayMs * (attempt + 1));
168
+ const delayMs = getRetryDelayMs(config, attempt);
169
+ await sleep(delayMs);
141
170
  continue;
142
171
  }
143
172
  throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, error);
@@ -277,8 +306,11 @@ var WEEKDAYS = [
277
306
  ];
278
307
  function normalizeSchedule(response) {
279
308
  const lessons = [];
309
+ const lessonsByDay = {};
280
310
  for (const day of WEEKDAYS) {
281
311
  const dayItems = response.schedules[day] ?? [];
312
+ const flattenedDayItems = dayItems.map((item) => ({ ...item, day, source: "schedules" }));
313
+ lessonsByDay[day] = flattenedDayItems;
282
314
  for (const item of dayItems) {
283
315
  lessons.push({ ...item, day, source: "schedules" });
284
316
  }
@@ -291,27 +323,100 @@ function normalizeSchedule(response) {
291
323
  source: "exams"
292
324
  });
293
325
  }
326
+ const scheduleLessons = lessons.filter((lesson) => lesson.source === "schedules");
327
+ const examLessons = lessons.filter((lesson) => lesson.source === "exams");
294
328
  return {
295
329
  ...response,
296
- lessons
330
+ lessons,
331
+ lessonsByDay,
332
+ scheduleLessons,
333
+ examLessons
297
334
  };
298
335
  }
336
+ function matchesFilter(item, filter) {
337
+ if (filter.source && item.source !== filter.source) {
338
+ return false;
339
+ }
340
+ if (filter.weekday && item.day !== filter.weekday) {
341
+ return false;
342
+ }
343
+ if (typeof filter.weekNumber === "number" && !item.weekNumber.includes(filter.weekNumber)) {
344
+ return false;
345
+ }
346
+ if (typeof filter.subgroup === "number" && item.numSubgroup !== filter.subgroup) {
347
+ return false;
348
+ }
349
+ if (filter.lessonTypeAbbrev) {
350
+ const types = Array.isArray(filter.lessonTypeAbbrev) ? filter.lessonTypeAbbrev : [filter.lessonTypeAbbrev];
351
+ if (!types.includes(item.lessonTypeAbbrev)) {
352
+ return false;
353
+ }
354
+ }
355
+ if (filter.subjectQuery) {
356
+ const query = filter.subjectQuery.toLowerCase();
357
+ const haystack = `${item.subject} ${item.subjectFullName} ${item.note ?? ""}`.toLowerCase();
358
+ if (!haystack.includes(query)) {
359
+ return false;
360
+ }
361
+ }
362
+ if (filter.employeeUrlId) {
363
+ const employeeMatch = item.employees?.some((employee) => employee.urlId === filter.employeeUrlId);
364
+ if (!employeeMatch) {
365
+ return false;
366
+ }
367
+ }
368
+ if (filter.auditory && !item.auditories.includes(filter.auditory)) {
369
+ return false;
370
+ }
371
+ return true;
372
+ }
373
+ function filterLessons(response, filter) {
374
+ return response.lessons.filter((item) => matchesFilter(item, filter));
375
+ }
299
376
  function createScheduleModule(config) {
377
+ async function getGroup(groupNumber, options = {}) {
378
+ assertNonEmptyString(groupNumber, "groupNumber");
379
+ const response = await requestJson(config, "/schedule", {
380
+ query: { studentGroup: groupNumber },
381
+ signal: options.signal
382
+ });
383
+ const result = options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);
384
+ return result;
385
+ }
386
+ async function getEmployee(urlId, options = {}) {
387
+ assertNonEmptyString(urlId, "urlId");
388
+ const response = await requestJson(config, `/employees/schedule/${urlId}`, {
389
+ signal: options.signal
390
+ });
391
+ const result = options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);
392
+ return result;
393
+ }
394
+ async function getGroupFiltered(groupNumber, filter, options = {}) {
395
+ const normalized = await getGroup(groupNumber, { ...options, raw: false });
396
+ return filterLessons(normalized, filter);
397
+ }
398
+ async function getEmployeeFiltered(urlId, filter, options = {}) {
399
+ const normalized = await getEmployee(urlId, { ...options, raw: false });
400
+ return filterLessons(normalized, filter);
401
+ }
300
402
  return {
301
- async getGroup(groupNumber, options = {}) {
302
- assertNonEmptyString(groupNumber, "groupNumber");
303
- const response = await requestJson(config, "/schedule", {
304
- query: { studentGroup: groupNumber },
305
- signal: options.signal
306
- });
307
- return options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);
403
+ getGroup,
404
+ getEmployee,
405
+ getGroupFiltered,
406
+ getEmployeeFiltered,
407
+ async getGroupExams(groupNumber, options = {}) {
408
+ return getGroupFiltered(groupNumber, { source: "exams" }, options);
308
409
  },
309
- async getEmployee(urlId, options = {}) {
310
- assertNonEmptyString(urlId, "urlId");
311
- const response = await requestJson(config, `/employees/schedule/${urlId}`, {
312
- signal: options.signal
313
- });
314
- return options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);
410
+ async getEmployeeExams(urlId, options = {}) {
411
+ return getEmployeeFiltered(urlId, { source: "exams" }, options);
412
+ },
413
+ async getGroupBySubgroup(groupNumber, subgroup, options = {}) {
414
+ assertPositiveInt(subgroup, "subgroup");
415
+ return getGroupFiltered(groupNumber, { source: "schedules", subgroup }, options);
416
+ },
417
+ async getEmployeeBySubgroup(urlId, subgroup, options = {}) {
418
+ assertPositiveInt(subgroup, "subgroup");
419
+ return getEmployeeFiltered(urlId, { source: "schedules", subgroup }, options);
315
420
  },
316
421
  async getCurrentWeek(options = {}) {
317
422
  return requestJson(config, "/schedule/current-week", { signal: options.signal });
@@ -376,6 +481,8 @@ function createInternalConfig(options = {}) {
376
481
  timeoutMs: options.timeoutMs ?? 1e4,
377
482
  retries: options.retries ?? 1,
378
483
  retryDelayMs: options.retryDelayMs ?? 300,
484
+ retryMaxDelayMs: options.retryMaxDelayMs ?? 3e3,
485
+ retryJitter: options.retryJitter ?? true,
379
486
  userAgent: options.userAgent,
380
487
  defaultRaw: options.defaultRaw ?? false
381
488
  };
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/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 {\r\n readonly status: number;\r\n readonly endpoint: string;\r\n readonly body: unknown;\r\n\r\n constructor(message: string, status: number, endpoint: string, body: unknown) {\r\n super(message);\r\n this.name = \"BsuirApiError\";\r\n this.status = status;\r\n this.endpoint = endpoint;\r\n this.body = body;\r\n }\r\n}\r\n\r\nexport class BsuirNetworkError extends Error {\r\n readonly endpoint: string;\r\n readonly causeError: unknown;\r\n\r\n constructor(message: string, endpoint: string, causeError: unknown) {\r\n super(message);\r\n this.name = \"BsuirNetworkError\";\r\n this.endpoint = endpoint;\r\n this.causeError = causeError;\r\n }\r\n}\r\n\r\nexport class BsuirTimeoutError extends Error {\r\n readonly endpoint: string;\r\n readonly timeoutMs: number;\r\n\r\n constructor(message: string, endpoint: string, timeoutMs: number) {\r\n super(message);\r\n this.name = \"BsuirTimeoutError\";\r\n this.endpoint = endpoint;\r\n this.timeoutMs = timeoutMs;\r\n }\r\n}\r\n\r\nexport class BsuirValidationError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"BsuirValidationError\";\r\n }\r\n}\r\n","import { BsuirValidationError } from \"../client/errors\";\r\n\r\nexport function assertNonEmptyString(value: string, fieldName: string): void {\r\n if (!value || value.trim().length === 0) {\r\n throw new BsuirValidationError(`'${fieldName}' must be a non-empty string`);\r\n }\r\n}\r\n\r\nexport function assertPositiveInt(value: number, fieldName: string): void {\r\n if (!Number.isInteger(value) || value <= 0) {\r\n throw new BsuirValidationError(`'${fieldName}' must be a positive integer`);\r\n }\r\n}\r\n\r\nexport function isAbortError(error: unknown): boolean {\r\n return error instanceof DOMException && error.name === \"AbortError\";\r\n}\r\n","import { BsuirApiError, BsuirNetworkError, BsuirTimeoutError } from \"./errors\";\r\nimport type { InternalClientConfig, QueryParams, RequestOptions } from \"./types\";\r\nimport { isAbortError } from \"../utils/guards\";\r\n\r\nconst RETRIABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\r\n\r\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\r\n const normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\r\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\r\n const url = new URL(`${normalizedBase}${normalizedPath}`);\r\n\r\n if (query) {\r\n for (const [key, value] of Object.entries(query)) {\r\n if (value === undefined || value === null) {\r\n continue;\r\n }\r\n url.searchParams.set(key, String(value));\r\n }\r\n }\r\n\r\n return url.toString();\r\n}\r\n\r\nfunction mergeSignals(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal {\r\n if (typeof AbortSignal.any === \"function\") {\r\n return AbortSignal.any([AbortSignal.timeout(timeoutMs), signal].filter(Boolean) as AbortSignal[]);\r\n }\r\n\r\n if (signal) {\r\n return signal;\r\n }\r\n\r\n return AbortSignal.timeout(timeoutMs);\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\nasync function parseBody(response: Response): Promise<unknown> {\r\n const contentType = response.headers.get(\"content-type\") ?? \"\";\r\n if (!contentType.includes(\"application/json\")) {\r\n return response.text();\r\n }\r\n\r\n try {\r\n return await response.json();\r\n } catch {\r\n throw new BsuirApiError(\"Invalid JSON response payload\", response.status, response.url, null);\r\n }\r\n}\r\n\r\nexport async function requestJson<T>(\r\n config: InternalClientConfig,\r\n path: string,\r\n options: RequestOptions = {}\r\n): Promise<T> {\r\n const endpoint = buildUrl(config.baseUrl, path, options.query);\r\n const requestSignal = mergeSignals(options.signal, config.timeoutMs);\r\n const headers = new Headers({\r\n Accept: \"application/json\"\r\n });\r\n\r\n if (config.userAgent) {\r\n headers.set(\"User-Agent\", config.userAgent);\r\n }\r\n\r\n for (let attempt = 0; attempt <= config.retries; attempt += 1) {\r\n try {\r\n const response = await config.fetchImpl(endpoint, {\r\n method: \"GET\",\r\n headers,\r\n signal: requestSignal\r\n });\r\n\r\n if (!response.ok) {\r\n const errorBody = await parseBody(response);\r\n if (attempt < config.retries && RETRIABLE_STATUS_CODES.has(response.status)) {\r\n await sleep(config.retryDelayMs * (attempt + 1));\r\n continue;\r\n }\r\n throw new BsuirApiError(\r\n `BSUIR API returned HTTP ${response.status} for ${path}`,\r\n response.status,\r\n endpoint,\r\n errorBody\r\n );\r\n }\r\n\r\n return (await parseBody(response)) as T;\r\n } catch (error) {\r\n if (error instanceof BsuirApiError) {\r\n throw error;\r\n }\r\n\r\n if (isAbortError(error)) {\r\n if (options.signal?.aborted) {\r\n throw error;\r\n }\r\n throw new BsuirTimeoutError(\r\n `Request timed out after ${config.timeoutMs}ms: ${path}`,\r\n endpoint,\r\n config.timeoutMs\r\n );\r\n }\r\n\r\n if (attempt < config.retries) {\r\n await sleep(config.retryDelayMs * (attempt + 1));\r\n continue;\r\n }\r\n\r\n throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, error);\r\n }\r\n }\r\n\r\n throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, null);\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport { assertNonEmptyString, assertPositiveInt } from \"../utils/guards\";\r\nimport type { Announcement } from \"../types/announcement\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createAnnouncementsModule(config: InternalClientConfig) {\r\n return {\r\n async byEmployee(urlId: string, options: ReadOptions = {}): Promise<Announcement[]> {\r\n assertNonEmptyString(urlId, \"urlId\");\r\n return requestJson<Announcement[]>(config, \"/announcements/employees\", {\r\n query: { \"url-id\": urlId },\r\n signal: options.signal\r\n });\r\n },\r\n\r\n async byDepartment(id: number, options: ReadOptions = {}): Promise<Announcement[]> {\r\n assertPositiveInt(id, \"id\");\r\n return requestJson<Announcement[]>(config, \"/announcements/departments\", {\r\n query: { id },\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport type { Auditory } from \"../types/catalog\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createAuditoriesModule(config: InternalClientConfig) {\r\n return {\r\n async listAll(options: ReadOptions = {}): Promise<Auditory[]> {\r\n return requestJson<Auditory[]>(config, \"/auditories\", {\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createCurrentWeekModule(config: InternalClientConfig) {\r\n return {\r\n async get(options: ReadOptions = {}): Promise<number> {\r\n return requestJson<number>(config, \"/schedule/current-week\", {\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport type { Department } from \"../types/catalog\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createDepartmentsModule(config: InternalClientConfig) {\r\n return {\r\n async listAll(options: ReadOptions = {}): Promise<Department[]> {\r\n return requestJson<Department[]>(config, \"/departments\", {\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport type { EmployeeCatalogItem } from \"../types/employee\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createEmployeesModule(config: InternalClientConfig) {\r\n return {\r\n async listAll(options: ReadOptions = {}): Promise<EmployeeCatalogItem[]> {\r\n return requestJson<EmployeeCatalogItem[]>(config, \"/employees/all\", {\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport type { Faculty } from \"../types/catalog\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createFacultiesModule(config: InternalClientConfig) {\r\n return {\r\n async listAll(options: ReadOptions = {}): Promise<Faculty[]> {\r\n return requestJson<Faculty[]>(config, \"/faculties\", {\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport type { StudentGroupCatalogItem } from \"../types/catalog\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createGroupsModule(config: InternalClientConfig) {\r\n return {\r\n async listAll(options: ReadOptions = {}): Promise<StudentGroupCatalogItem[]> {\r\n return requestJson<StudentGroupCatalogItem[]>(config, \"/student-groups\", {\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport { assertNonEmptyString, assertPositiveInt } from \"../utils/guards\";\r\nimport type { ApiDateResponse } from \"../types/common\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createLastUpdateModule(config: InternalClientConfig) {\r\n return {\r\n async byGroup(\r\n params: { groupNumber: string } | { id: number },\r\n options: ReadOptions = {}\r\n ): Promise<ApiDateResponse> {\r\n let query: Record<string, string | number>;\r\n if (\"groupNumber\" in params) {\r\n assertNonEmptyString(params.groupNumber, \"groupNumber\");\r\n query = { groupNumber: params.groupNumber };\r\n } else {\r\n assertPositiveInt(params.id, \"id\");\r\n query = { id: params.id };\r\n }\r\n\r\n return requestJson<ApiDateResponse>(config, \"/last-update-date/student-group\", {\r\n query,\r\n signal: options.signal\r\n });\r\n },\r\n\r\n async byEmployee(\r\n params: { urlId: string } | { id: number },\r\n options: ReadOptions = {}\r\n ): Promise<ApiDateResponse> {\r\n let query: Record<string, string | number>;\r\n if (\"urlId\" in params) {\r\n assertNonEmptyString(params.urlId, \"urlId\");\r\n query = { \"url-id\": params.urlId };\r\n } else {\r\n assertPositiveInt(params.id, \"id\");\r\n query = { id: params.id };\r\n }\r\n\r\n return requestJson<ApiDateResponse>(config, \"/last-update-date/employee\", {\r\n query,\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport { assertNonEmptyString, assertPositiveInt } from \"../utils/guards\";\r\nimport type {\r\n FlattenedScheduleItem,\r\n NormalizedScheduleResponse,\r\n ScheduleResponse\r\n} from \"../types/schedule\";\r\nimport type { ApiDateResponse } from \"../types/common\";\r\nimport type { Weekday } from \"../types/common\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nconst WEEKDAYS: Weekday[] = [\r\n \"Понедельник\",\r\n \"Вторник\",\r\n \"Среда\",\r\n \"Четверг\",\r\n \"Пятница\",\r\n \"Суббота\"\r\n];\r\n\r\nfunction normalizeSchedule(response: ScheduleResponse): NormalizedScheduleResponse {\r\n const lessons: FlattenedScheduleItem[] = [];\r\n for (const day of WEEKDAYS) {\r\n const dayItems = response.schedules[day] ?? [];\r\n for (const item of dayItems) {\r\n lessons.push({ ...item, day, source: \"schedules\" });\r\n }\r\n }\r\n\r\n for (const exam of response.exams) {\r\n lessons.push({\r\n ...exam,\r\n day: null,\r\n // Exams are not grouped by weekday in API response.\r\n source: \"exams\"\r\n });\r\n }\r\n\r\n return {\r\n ...response,\r\n lessons\r\n };\r\n}\r\n\r\nexport function createScheduleModule(config: InternalClientConfig) {\r\n return {\r\n async getGroup(\r\n groupNumber: string,\r\n options: ReadOptions = {}\r\n ): Promise<ScheduleResponse | NormalizedScheduleResponse> {\r\n assertNonEmptyString(groupNumber, \"groupNumber\");\r\n const response = await requestJson<ScheduleResponse>(config, \"/schedule\", {\r\n query: { studentGroup: groupNumber },\r\n signal: options.signal\r\n });\r\n return options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);\r\n },\r\n\r\n async getEmployee(\r\n urlId: string,\r\n options: ReadOptions = {}\r\n ): Promise<ScheduleResponse | NormalizedScheduleResponse> {\r\n assertNonEmptyString(urlId, \"urlId\");\r\n const response = await requestJson<ScheduleResponse>(config, `/employees/schedule/${urlId}`, {\r\n signal: options.signal\r\n });\r\n return options.raw ?? config.defaultRaw ? response : normalizeSchedule(response);\r\n },\r\n\r\n async getCurrentWeek(options: ReadOptions = {}): Promise<number> {\r\n return requestJson<number>(config, \"/schedule/current-week\", { signal: options.signal });\r\n },\r\n\r\n async getLastUpdateByGroup(\r\n params: { groupNumber: string } | { id: number },\r\n options: ReadOptions = {}\r\n ): Promise<ApiDateResponse> {\r\n let query: Record<string, string | number>;\r\n if (\"groupNumber\" in params) {\r\n assertNonEmptyString(params.groupNumber, \"groupNumber\");\r\n query = { groupNumber: params.groupNumber };\r\n } else {\r\n assertPositiveInt(params.id, \"id\");\r\n query = { id: params.id };\r\n }\r\n return requestJson<ApiDateResponse>(config, \"/last-update-date/student-group\", {\r\n query,\r\n signal: options.signal\r\n });\r\n },\r\n\r\n async getLastUpdateByEmployee(\r\n params: { urlId: string } | { id: number },\r\n options: ReadOptions = {}\r\n ): Promise<ApiDateResponse> {\r\n let query: Record<string, string | number>;\r\n if (\"urlId\" in params) {\r\n assertNonEmptyString(params.urlId, \"urlId\");\r\n query = { \"url-id\": params.urlId };\r\n } else {\r\n assertPositiveInt(params.id, \"id\");\r\n query = { id: params.id };\r\n }\r\n return requestJson<ApiDateResponse>(config, \"/last-update-date/employee\", {\r\n query,\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { InternalClientConfig } from \"../client/types\";\r\nimport { requestJson } from \"../client/http\";\r\nimport type { Speciality } from \"../types/catalog\";\r\nimport type { ReadOptions } from \"./types\";\r\n\r\nexport function createSpecialitiesModule(config: InternalClientConfig) {\r\n return {\r\n async listAll(options: ReadOptions = {}): Promise<Speciality[]> {\r\n return requestJson<Speciality[]>(config, \"/specialities\", {\r\n signal: options.signal\r\n });\r\n }\r\n };\r\n}\r\n","import type { BsuirClientOptions, InternalClientConfig } from \"./types\";\r\nimport { createAnnouncementsModule } from \"../modules/announcements\";\r\nimport { createAuditoriesModule } from \"../modules/auditories\";\r\nimport { createCurrentWeekModule } from \"../modules/currentWeek\";\r\nimport { createDepartmentsModule } from \"../modules/departments\";\r\nimport { createEmployeesModule } from \"../modules/employees\";\r\nimport { createFacultiesModule } from \"../modules/faculties\";\r\nimport { createGroupsModule } from \"../modules/groups\";\r\nimport { createLastUpdateModule } from \"../modules/lastUpdate\";\r\nimport { createScheduleModule } from \"../modules/schedule\";\r\nimport { createSpecialitiesModule } from \"../modules/specialities\";\r\n\r\nconst DEFAULT_BASE_URL = \"https://iis.bsuir.by/api/v1\";\r\n\r\nfunction resolveFetch(customFetch?: typeof globalThis.fetch): typeof globalThis.fetch {\r\n if (customFetch) {\r\n return customFetch;\r\n }\r\n\r\n if (typeof globalThis.fetch !== \"function\") {\r\n throw new Error(\"Global fetch is unavailable. Provide 'fetch' in createBsuirClient options.\");\r\n }\r\n\r\n return globalThis.fetch;\r\n}\r\n\r\nfunction createInternalConfig(options: BsuirClientOptions = {}): InternalClientConfig {\r\n return {\r\n baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,\r\n fetchImpl: resolveFetch(options.fetch),\r\n timeoutMs: options.timeoutMs ?? 10_000,\r\n retries: options.retries ?? 1,\r\n retryDelayMs: options.retryDelayMs ?? 300,\r\n userAgent: options.userAgent,\r\n defaultRaw: options.defaultRaw ?? false\r\n };\r\n}\r\n\r\nexport function createBsuirClient(options: BsuirClientOptions = {}) {\r\n const config = createInternalConfig(options);\r\n\r\n return {\r\n schedule: createScheduleModule(config),\r\n groups: createGroupsModule(config),\r\n employees: createEmployeesModule(config),\r\n faculties: createFacultiesModule(config),\r\n departments: createDepartmentsModule(config),\r\n specialities: createSpecialitiesModule(config),\r\n announcements: createAnnouncementsModule(config),\r\n auditories: createAuditoriesModule(config),\r\n lastUpdate: createLastUpdateModule(config),\r\n currentWeek: createCurrentWeekModule(config)\r\n };\r\n}\r\n\r\nexport type BsuirClient = ReturnType<typeof createBsuirClient>;\r\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/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\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 isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\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 ${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 ${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 { assertNonEmptyString, 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 assertNonEmptyString(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 { ReadOptions } from \"./types\";\n\nexport function createCurrentWeekModule(config: InternalClientConfig) {\n return {\n async get(options: ReadOptions = {}): Promise<number> {\n return requestJson<number>(config, \"/schedule/current-week\", {\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 { assertNonEmptyString, 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 assertNonEmptyString(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 assertNonEmptyString(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 { assertNonEmptyString, 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\";\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 for (const day of WEEKDAYS) {\n const dayItems = response.schedules[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 response.exams) {\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 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\" && !item.weekNumber.includes(filter.weekNumber)) {\n return false;\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 (!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 async function getGroup<TRaw extends boolean | undefined = undefined>(\n groupNumber: string,\n options: ReadOptions & { raw?: TRaw } = {}\n ): Promise<TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse> {\n assertNonEmptyString(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 async function getEmployee<TRaw extends boolean | undefined = undefined>(\n urlId: string,\n options: ReadOptions & { raw?: TRaw } = {}\n ): Promise<TRaw extends true ? ScheduleResponse : NormalizedScheduleResponse> {\n assertNonEmptyString(urlId, \"urlId\");\n const response = await requestJson<ScheduleResponse>(config, `/employees/schedule/${urlId}`, {\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 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 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 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 async getCurrentWeek(options: ReadOptions = {}): Promise<number> {\n return requestJson<number>(config, \"/schedule/current-week\", { signal: options.signal });\n },\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 assertNonEmptyString(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 assertNonEmptyString(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\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\nexport type BsuirClient = ReturnType<typeof createBsuirClient>;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,66 +1,66 @@
1
- {
2
- "name": "bsuir-iis-api",
3
- "version": "0.1.0",
4
- "description": "Type-safe ESM SDK for BSUIR IIS API",
5
- "type": "module",
6
- "author": "kotru21",
7
- "license": "MIT",
8
- "repository": {
9
- "type": "git",
10
- "url": "https://github.com/kotru21/bsuir-iis-api.git"
11
- },
12
- "homepage": "https://github.com/kotru21/bsuir-iis-api#readme",
13
- "bugs": {
14
- "url": "https://github.com/kotru21/bsuir-iis-api/issues"
15
- },
16
- "keywords": [
17
- "bsuir",
18
- "iis",
19
- "schedule",
20
- "api",
21
- "sdk",
22
- "typescript"
23
- ],
24
- "engines": {
25
- "node": ">=18"
26
- },
27
- "exports": {
28
- ".": {
29
- "types": "./dist/index.d.ts",
30
- "import": "./dist/index.js"
31
- }
32
- },
33
- "main": "./dist/index.js",
34
- "types": "./dist/index.d.ts",
35
- "files": [
36
- "dist",
37
- "README.md",
38
- "CHANGELOG.md",
39
- "LICENSE"
40
- ],
41
- "scripts": {
42
- "build": "tsup",
43
- "dev": "tsup --watch",
44
- "typecheck": "tsc --noEmit",
45
- "lint": "eslint .",
46
- "test": "vitest run",
47
- "test:coverage": "vitest run --coverage",
48
- "check": "npm run lint && npm run typecheck && npm run test",
49
- "release:dry": "npm pack --dry-run"
50
- },
51
- "devDependencies": {
52
- "@changesets/cli": "^2.29.7",
53
- "@eslint/js": "^9.38.0",
54
- "@types/node": "^24.7.2",
55
- "@typescript-eslint/eslint-plugin": "^8.46.1",
56
- "@typescript-eslint/parser": "^8.46.1",
57
- "@vitest/coverage-v8": "^3.2.4",
58
- "eslint": "^9.38.0",
59
- "eslint-config-prettier": "^10.1.8",
60
- "globals": "^16.4.0",
61
- "prettier": "^3.6.2",
62
- "tsup": "^8.5.0",
63
- "typescript": "^5.9.3",
64
- "vitest": "^3.2.4"
65
- }
66
- }
1
+ {
2
+ "name": "bsuir-iis-api",
3
+ "version": "0.2.0",
4
+ "description": "Type-safe ESM SDK for BSUIR IIS API",
5
+ "type": "module",
6
+ "author": "kotru21",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/kotru21/bsuir-iis-api.git"
11
+ },
12
+ "homepage": "https://github.com/kotru21/bsuir-iis-api#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/kotru21/bsuir-iis-api/issues"
15
+ },
16
+ "keywords": [
17
+ "bsuir",
18
+ "iis",
19
+ "schedule",
20
+ "api",
21
+ "sdk",
22
+ "typescript"
23
+ ],
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ }
32
+ },
33
+ "main": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "CHANGELOG.md",
39
+ "LICENSE"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "dev": "tsup --watch",
44
+ "typecheck": "tsc --noEmit",
45
+ "lint": "eslint .",
46
+ "test": "vitest run",
47
+ "test:coverage": "vitest run --coverage",
48
+ "check": "npm run lint && npm run typecheck && npm run test",
49
+ "release:dry": "npm pack --dry-run"
50
+ },
51
+ "devDependencies": {
52
+ "@changesets/cli": "^2.29.7",
53
+ "@eslint/js": "^9.38.0",
54
+ "@types/node": "^24.7.2",
55
+ "@typescript-eslint/eslint-plugin": "^8.46.1",
56
+ "@typescript-eslint/parser": "^8.46.1",
57
+ "@vitest/coverage-v8": "^3.2.4",
58
+ "eslint": "^9.38.0",
59
+ "eslint-config-prettier": "^10.1.8",
60
+ "globals": "^16.4.0",
61
+ "prettier": "^3.6.2",
62
+ "tsup": "^8.5.0",
63
+ "typescript": "^5.9.3",
64
+ "vitest": "^3.2.4"
65
+ }
66
+ }