bsuir-iis-api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
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.
package/LICENSE ADDED
@@ -0,0 +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.
package/README.md ADDED
@@ -0,0 +1,98 @@
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
@@ -0,0 +1,250 @@
1
+ type QueryValue = string | number | boolean | null | undefined;
2
+ type QueryParams = Record<string, QueryValue>;
3
+ interface RequestOptions {
4
+ query?: QueryParams | undefined;
5
+ signal?: AbortSignal | undefined;
6
+ }
7
+ interface BsuirClientOptions {
8
+ baseUrl?: string;
9
+ fetch?: typeof globalThis.fetch;
10
+ timeoutMs?: number;
11
+ retries?: number;
12
+ retryDelayMs?: number;
13
+ userAgent?: string;
14
+ defaultRaw?: boolean;
15
+ }
16
+
17
+ interface ReadOptions extends RequestOptions {
18
+ raw?: boolean;
19
+ }
20
+
21
+ declare function createBsuirClient(options?: BsuirClientOptions): {
22
+ schedule: {
23
+ getGroup(groupNumber: string, options?: ReadOptions): Promise<ScheduleResponse | NormalizedScheduleResponse>;
24
+ getEmployee(urlId: string, options?: ReadOptions): Promise<ScheduleResponse | NormalizedScheduleResponse>;
25
+ getCurrentWeek(options?: ReadOptions): Promise<number>;
26
+ getLastUpdateByGroup(params: {
27
+ groupNumber: string;
28
+ } | {
29
+ id: number;
30
+ }, options?: ReadOptions): Promise<ApiDateResponse>;
31
+ getLastUpdateByEmployee(params: {
32
+ urlId: string;
33
+ } | {
34
+ id: number;
35
+ }, options?: ReadOptions): Promise<ApiDateResponse>;
36
+ };
37
+ groups: {
38
+ listAll(options?: ReadOptions): Promise<StudentGroupCatalogItem[]>;
39
+ };
40
+ employees: {
41
+ listAll(options?: ReadOptions): Promise<EmployeeCatalogItem[]>;
42
+ };
43
+ faculties: {
44
+ listAll(options?: ReadOptions): Promise<Faculty[]>;
45
+ };
46
+ departments: {
47
+ listAll(options?: ReadOptions): Promise<Department[]>;
48
+ };
49
+ specialities: {
50
+ listAll(options?: ReadOptions): Promise<Speciality[]>;
51
+ };
52
+ announcements: {
53
+ byEmployee(urlId: string, options?: ReadOptions): Promise<Announcement[]>;
54
+ byDepartment(id: number, options?: ReadOptions): Promise<Announcement[]>;
55
+ };
56
+ auditories: {
57
+ listAll(options?: ReadOptions): Promise<Auditory[]>;
58
+ };
59
+ lastUpdate: {
60
+ byGroup(params: {
61
+ groupNumber: string;
62
+ } | {
63
+ id: number;
64
+ }, options?: ReadOptions): Promise<ApiDateResponse>;
65
+ byEmployee(params: {
66
+ urlId: string;
67
+ } | {
68
+ id: number;
69
+ }, options?: ReadOptions): Promise<ApiDateResponse>;
70
+ };
71
+ currentWeek: {
72
+ get(options?: ReadOptions): Promise<number>;
73
+ };
74
+ };
75
+ type BsuirClient = ReturnType<typeof createBsuirClient>;
76
+
77
+ declare class BsuirApiError extends Error {
78
+ readonly status: number;
79
+ readonly endpoint: string;
80
+ readonly body: unknown;
81
+ constructor(message: string, status: number, endpoint: string, body: unknown);
82
+ }
83
+ declare class BsuirNetworkError extends Error {
84
+ readonly endpoint: string;
85
+ readonly causeError: unknown;
86
+ constructor(message: string, endpoint: string, causeError: unknown);
87
+ }
88
+ declare class BsuirTimeoutError extends Error {
89
+ readonly endpoint: string;
90
+ readonly timeoutMs: number;
91
+ constructor(message: string, endpoint: string, timeoutMs: number);
92
+ }
93
+ declare class BsuirValidationError extends Error {
94
+ constructor(message: string);
95
+ }
96
+
97
+ type Weekday = "Понедельник" | "Вторник" | "Среда" | "Четверг" | "Пятница" | "Суббота";
98
+ interface ApiDateResponse {
99
+ lastUpdateDate: string;
100
+ }
101
+ interface StudentGroupShort {
102
+ id: number;
103
+ name: string;
104
+ }
105
+ type Maybe<T> = T | null;
106
+
107
+ interface Announcement {
108
+ id: number;
109
+ employee: string;
110
+ content: string;
111
+ date: string;
112
+ employeeDepartments: string[];
113
+ studentGroups: StudentGroupShort[];
114
+ }
115
+
116
+ interface Faculty {
117
+ id: number;
118
+ name: string;
119
+ abbrev: string;
120
+ }
121
+ interface Department {
122
+ id: number;
123
+ name: string;
124
+ abbrev: string;
125
+ }
126
+ interface EducationForm {
127
+ id: number;
128
+ name: string;
129
+ }
130
+ interface Speciality {
131
+ id: number;
132
+ name: string;
133
+ abbrev: string;
134
+ educationForm: EducationForm[];
135
+ facultyId: number;
136
+ code: string;
137
+ }
138
+ interface StudentGroupCatalogItem {
139
+ name: string;
140
+ facultyId: number;
141
+ facultyName?: string;
142
+ facultyAbbrev?: string;
143
+ specialityDepartmentEducationFormId: number;
144
+ specialityName: string;
145
+ specialityAbbrev?: string;
146
+ course: number;
147
+ id: number;
148
+ calendarId: string;
149
+ educationDegree?: number;
150
+ }
151
+ interface AuditoryType {
152
+ id: number;
153
+ name: string;
154
+ abbrev: string;
155
+ }
156
+ interface BuildingNumber {
157
+ id: number;
158
+ name: string;
159
+ }
160
+ interface AuditoryDepartment {
161
+ idDepartment: number;
162
+ abbrev: string;
163
+ name: string;
164
+ nameAndAbbrev: string;
165
+ }
166
+ interface Auditory {
167
+ id: number;
168
+ name: string;
169
+ note: string;
170
+ capacity: number | null;
171
+ auditoryType: AuditoryType;
172
+ buildingNumber: BuildingNumber;
173
+ department: AuditoryDepartment;
174
+ }
175
+
176
+ interface Employee {
177
+ firstName: string;
178
+ lastName: string;
179
+ middleName: string;
180
+ degree: string;
181
+ degreeAbbrev?: string;
182
+ email: Maybe<string>;
183
+ rank: Maybe<string>;
184
+ photoLink: string;
185
+ calendarId: string;
186
+ id: number;
187
+ urlId: string;
188
+ jobPositions: Maybe<string[]>;
189
+ }
190
+ interface EmployeeCatalogItem {
191
+ firstName: string;
192
+ lastName: string;
193
+ middleName: string;
194
+ degree: string;
195
+ rank: string;
196
+ photoLink: string;
197
+ calendarId: string;
198
+ academicDepartment?: string[];
199
+ id: number;
200
+ urlId: string;
201
+ fio: string;
202
+ }
203
+
204
+ interface LessonStudentGroup {
205
+ specialityName: string;
206
+ specialityCode: string;
207
+ numberOfStudents: number;
208
+ name: string;
209
+ educationDegree: number;
210
+ }
211
+ interface ScheduleItem {
212
+ weekNumber: number[];
213
+ studentGroups: LessonStudentGroup[];
214
+ numSubgroup: number;
215
+ auditories: string[];
216
+ startLessonTime: string;
217
+ endLessonTime: string;
218
+ subject: string;
219
+ subjectFullName: string;
220
+ note: Maybe<string>;
221
+ lessonTypeAbbrev: string;
222
+ dateLesson: Maybe<string>;
223
+ startLessonDate: Maybe<string>;
224
+ endLessonDate: Maybe<string>;
225
+ announcement: boolean;
226
+ split: boolean;
227
+ employees: Maybe<Employee[]>;
228
+ }
229
+ type WeekScheduleMap = Partial<Record<Weekday, ScheduleItem[]>>;
230
+ interface ScheduleResponse {
231
+ employeeDto: Maybe<Employee>;
232
+ studentGroupDto: Maybe<StudentGroupCatalogItem>;
233
+ schedules: WeekScheduleMap;
234
+ exams: ScheduleItem[];
235
+ startDate: Maybe<string>;
236
+ endDate: Maybe<string>;
237
+ startExamsDate: Maybe<string>;
238
+ endExamsDate: Maybe<string>;
239
+ }
240
+ interface FlattenedScheduleItem extends ScheduleItem {
241
+ day: Weekday | null;
242
+ source: "schedules" | "exams";
243
+ }
244
+ interface NormalizedScheduleResponse extends Omit<ScheduleResponse, "schedules" | "exams"> {
245
+ schedules: WeekScheduleMap;
246
+ exams: ScheduleItem[];
247
+ lessons: FlattenedScheduleItem[];
248
+ }
249
+
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 };
package/dist/index.js ADDED
@@ -0,0 +1,405 @@
1
+ // src/client/errors.ts
2
+ var BsuirApiError = class extends Error {
3
+ status;
4
+ endpoint;
5
+ body;
6
+ constructor(message, status, endpoint, body) {
7
+ super(message);
8
+ this.name = "BsuirApiError";
9
+ this.status = status;
10
+ this.endpoint = endpoint;
11
+ this.body = body;
12
+ }
13
+ };
14
+ var BsuirNetworkError = class extends Error {
15
+ endpoint;
16
+ causeError;
17
+ constructor(message, endpoint, causeError) {
18
+ super(message);
19
+ this.name = "BsuirNetworkError";
20
+ this.endpoint = endpoint;
21
+ this.causeError = causeError;
22
+ }
23
+ };
24
+ var BsuirTimeoutError = class extends Error {
25
+ endpoint;
26
+ timeoutMs;
27
+ constructor(message, endpoint, timeoutMs) {
28
+ super(message);
29
+ this.name = "BsuirTimeoutError";
30
+ this.endpoint = endpoint;
31
+ this.timeoutMs = timeoutMs;
32
+ }
33
+ };
34
+ var BsuirValidationError = class extends Error {
35
+ constructor(message) {
36
+ super(message);
37
+ this.name = "BsuirValidationError";
38
+ }
39
+ };
40
+
41
+ // src/utils/guards.ts
42
+ function assertNonEmptyString(value, fieldName) {
43
+ if (!value || value.trim().length === 0) {
44
+ throw new BsuirValidationError(`'${fieldName}' must be a non-empty string`);
45
+ }
46
+ }
47
+ function assertPositiveInt(value, fieldName) {
48
+ if (!Number.isInteger(value) || value <= 0) {
49
+ throw new BsuirValidationError(`'${fieldName}' must be a positive integer`);
50
+ }
51
+ }
52
+ function isAbortError(error) {
53
+ return error instanceof DOMException && error.name === "AbortError";
54
+ }
55
+
56
+ // src/client/http.ts
57
+ var RETRIABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
58
+ function buildUrl(baseUrl, path, query) {
59
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
60
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
61
+ const url = new URL(`${normalizedBase}${normalizedPath}`);
62
+ if (query) {
63
+ for (const [key, value] of Object.entries(query)) {
64
+ if (value === void 0 || value === null) {
65
+ continue;
66
+ }
67
+ url.searchParams.set(key, String(value));
68
+ }
69
+ }
70
+ return url.toString();
71
+ }
72
+ function mergeSignals(signal, timeoutMs) {
73
+ if (typeof AbortSignal.any === "function") {
74
+ return AbortSignal.any([AbortSignal.timeout(timeoutMs), signal].filter(Boolean));
75
+ }
76
+ if (signal) {
77
+ return signal;
78
+ }
79
+ return AbortSignal.timeout(timeoutMs);
80
+ }
81
+ function sleep(ms) {
82
+ return new Promise((resolve) => setTimeout(resolve, ms));
83
+ }
84
+ async function parseBody(response) {
85
+ const contentType = response.headers.get("content-type") ?? "";
86
+ if (!contentType.includes("application/json")) {
87
+ return response.text();
88
+ }
89
+ try {
90
+ return await response.json();
91
+ } catch {
92
+ throw new BsuirApiError("Invalid JSON response payload", response.status, response.url, null);
93
+ }
94
+ }
95
+ async function requestJson(config, path, options = {}) {
96
+ const endpoint = buildUrl(config.baseUrl, path, options.query);
97
+ const requestSignal = mergeSignals(options.signal, config.timeoutMs);
98
+ const headers = new Headers({
99
+ Accept: "application/json"
100
+ });
101
+ if (config.userAgent) {
102
+ headers.set("User-Agent", config.userAgent);
103
+ }
104
+ for (let attempt = 0; attempt <= config.retries; attempt += 1) {
105
+ try {
106
+ const response = await config.fetchImpl(endpoint, {
107
+ method: "GET",
108
+ headers,
109
+ signal: requestSignal
110
+ });
111
+ if (!response.ok) {
112
+ const errorBody = await parseBody(response);
113
+ if (attempt < config.retries && RETRIABLE_STATUS_CODES.has(response.status)) {
114
+ await sleep(config.retryDelayMs * (attempt + 1));
115
+ continue;
116
+ }
117
+ throw new BsuirApiError(
118
+ `BSUIR API returned HTTP ${response.status} for ${path}`,
119
+ response.status,
120
+ endpoint,
121
+ errorBody
122
+ );
123
+ }
124
+ return await parseBody(response);
125
+ } catch (error) {
126
+ if (error instanceof BsuirApiError) {
127
+ throw error;
128
+ }
129
+ if (isAbortError(error)) {
130
+ if (options.signal?.aborted) {
131
+ throw error;
132
+ }
133
+ throw new BsuirTimeoutError(
134
+ `Request timed out after ${config.timeoutMs}ms: ${path}`,
135
+ endpoint,
136
+ config.timeoutMs
137
+ );
138
+ }
139
+ if (attempt < config.retries) {
140
+ await sleep(config.retryDelayMs * (attempt + 1));
141
+ continue;
142
+ }
143
+ throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, error);
144
+ }
145
+ }
146
+ throw new BsuirNetworkError(`Network error while requesting ${path}`, endpoint, null);
147
+ }
148
+
149
+ // src/modules/announcements.ts
150
+ function createAnnouncementsModule(config) {
151
+ return {
152
+ async byEmployee(urlId, options = {}) {
153
+ assertNonEmptyString(urlId, "urlId");
154
+ return requestJson(config, "/announcements/employees", {
155
+ query: { "url-id": urlId },
156
+ signal: options.signal
157
+ });
158
+ },
159
+ async byDepartment(id, options = {}) {
160
+ assertPositiveInt(id, "id");
161
+ return requestJson(config, "/announcements/departments", {
162
+ query: { id },
163
+ signal: options.signal
164
+ });
165
+ }
166
+ };
167
+ }
168
+
169
+ // src/modules/auditories.ts
170
+ function createAuditoriesModule(config) {
171
+ return {
172
+ async listAll(options = {}) {
173
+ return requestJson(config, "/auditories", {
174
+ signal: options.signal
175
+ });
176
+ }
177
+ };
178
+ }
179
+
180
+ // src/modules/currentWeek.ts
181
+ function createCurrentWeekModule(config) {
182
+ return {
183
+ async get(options = {}) {
184
+ return requestJson(config, "/schedule/current-week", {
185
+ signal: options.signal
186
+ });
187
+ }
188
+ };
189
+ }
190
+
191
+ // src/modules/departments.ts
192
+ function createDepartmentsModule(config) {
193
+ return {
194
+ async listAll(options = {}) {
195
+ return requestJson(config, "/departments", {
196
+ signal: options.signal
197
+ });
198
+ }
199
+ };
200
+ }
201
+
202
+ // src/modules/employees.ts
203
+ function createEmployeesModule(config) {
204
+ return {
205
+ async listAll(options = {}) {
206
+ return requestJson(config, "/employees/all", {
207
+ signal: options.signal
208
+ });
209
+ }
210
+ };
211
+ }
212
+
213
+ // src/modules/faculties.ts
214
+ function createFacultiesModule(config) {
215
+ return {
216
+ async listAll(options = {}) {
217
+ return requestJson(config, "/faculties", {
218
+ signal: options.signal
219
+ });
220
+ }
221
+ };
222
+ }
223
+
224
+ // src/modules/groups.ts
225
+ function createGroupsModule(config) {
226
+ return {
227
+ async listAll(options = {}) {
228
+ return requestJson(config, "/student-groups", {
229
+ signal: options.signal
230
+ });
231
+ }
232
+ };
233
+ }
234
+
235
+ // src/modules/lastUpdate.ts
236
+ function createLastUpdateModule(config) {
237
+ return {
238
+ async byGroup(params, options = {}) {
239
+ let query;
240
+ if ("groupNumber" in params) {
241
+ assertNonEmptyString(params.groupNumber, "groupNumber");
242
+ query = { groupNumber: params.groupNumber };
243
+ } else {
244
+ assertPositiveInt(params.id, "id");
245
+ query = { id: params.id };
246
+ }
247
+ return requestJson(config, "/last-update-date/student-group", {
248
+ query,
249
+ signal: options.signal
250
+ });
251
+ },
252
+ async byEmployee(params, options = {}) {
253
+ let query;
254
+ if ("urlId" in params) {
255
+ assertNonEmptyString(params.urlId, "urlId");
256
+ query = { "url-id": params.urlId };
257
+ } else {
258
+ assertPositiveInt(params.id, "id");
259
+ query = { id: params.id };
260
+ }
261
+ return requestJson(config, "/last-update-date/employee", {
262
+ query,
263
+ signal: options.signal
264
+ });
265
+ }
266
+ };
267
+ }
268
+
269
+ // src/modules/schedule.ts
270
+ var WEEKDAYS = [
271
+ "\u041F\u043E\u043D\u0435\u0434\u0435\u043B\u044C\u043D\u0438\u043A",
272
+ "\u0412\u0442\u043E\u0440\u043D\u0438\u043A",
273
+ "\u0421\u0440\u0435\u0434\u0430",
274
+ "\u0427\u0435\u0442\u0432\u0435\u0440\u0433",
275
+ "\u041F\u044F\u0442\u043D\u0438\u0446\u0430",
276
+ "\u0421\u0443\u0431\u0431\u043E\u0442\u0430"
277
+ ];
278
+ function normalizeSchedule(response) {
279
+ const lessons = [];
280
+ for (const day of WEEKDAYS) {
281
+ const dayItems = response.schedules[day] ?? [];
282
+ for (const item of dayItems) {
283
+ lessons.push({ ...item, day, source: "schedules" });
284
+ }
285
+ }
286
+ for (const exam of response.exams) {
287
+ lessons.push({
288
+ ...exam,
289
+ day: null,
290
+ // Exams are not grouped by weekday in API response.
291
+ source: "exams"
292
+ });
293
+ }
294
+ return {
295
+ ...response,
296
+ lessons
297
+ };
298
+ }
299
+ function createScheduleModule(config) {
300
+ 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);
308
+ },
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);
315
+ },
316
+ async getCurrentWeek(options = {}) {
317
+ return requestJson(config, "/schedule/current-week", { signal: options.signal });
318
+ },
319
+ async getLastUpdateByGroup(params, options = {}) {
320
+ let query;
321
+ if ("groupNumber" in params) {
322
+ assertNonEmptyString(params.groupNumber, "groupNumber");
323
+ query = { groupNumber: params.groupNumber };
324
+ } else {
325
+ assertPositiveInt(params.id, "id");
326
+ query = { id: params.id };
327
+ }
328
+ return requestJson(config, "/last-update-date/student-group", {
329
+ query,
330
+ signal: options.signal
331
+ });
332
+ },
333
+ async getLastUpdateByEmployee(params, options = {}) {
334
+ let query;
335
+ if ("urlId" in params) {
336
+ assertNonEmptyString(params.urlId, "urlId");
337
+ query = { "url-id": params.urlId };
338
+ } else {
339
+ assertPositiveInt(params.id, "id");
340
+ query = { id: params.id };
341
+ }
342
+ return requestJson(config, "/last-update-date/employee", {
343
+ query,
344
+ signal: options.signal
345
+ });
346
+ }
347
+ };
348
+ }
349
+
350
+ // src/modules/specialities.ts
351
+ function createSpecialitiesModule(config) {
352
+ return {
353
+ async listAll(options = {}) {
354
+ return requestJson(config, "/specialities", {
355
+ signal: options.signal
356
+ });
357
+ }
358
+ };
359
+ }
360
+
361
+ // src/client/createClient.ts
362
+ var DEFAULT_BASE_URL = "https://iis.bsuir.by/api/v1";
363
+ function resolveFetch(customFetch) {
364
+ if (customFetch) {
365
+ return customFetch;
366
+ }
367
+ if (typeof globalThis.fetch !== "function") {
368
+ throw new Error("Global fetch is unavailable. Provide 'fetch' in createBsuirClient options.");
369
+ }
370
+ return globalThis.fetch;
371
+ }
372
+ function createInternalConfig(options = {}) {
373
+ return {
374
+ baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
375
+ fetchImpl: resolveFetch(options.fetch),
376
+ timeoutMs: options.timeoutMs ?? 1e4,
377
+ retries: options.retries ?? 1,
378
+ retryDelayMs: options.retryDelayMs ?? 300,
379
+ userAgent: options.userAgent,
380
+ defaultRaw: options.defaultRaw ?? false
381
+ };
382
+ }
383
+ function createBsuirClient(options = {}) {
384
+ const config = createInternalConfig(options);
385
+ return {
386
+ schedule: createScheduleModule(config),
387
+ groups: createGroupsModule(config),
388
+ employees: createEmployeesModule(config),
389
+ faculties: createFacultiesModule(config),
390
+ departments: createDepartmentsModule(config),
391
+ specialities: createSpecialitiesModule(config),
392
+ announcements: createAnnouncementsModule(config),
393
+ auditories: createAuditoriesModule(config),
394
+ lastUpdate: createLastUpdateModule(config),
395
+ currentWeek: createCurrentWeekModule(config)
396
+ };
397
+ }
398
+ export {
399
+ BsuirApiError,
400
+ BsuirNetworkError,
401
+ BsuirTimeoutError,
402
+ BsuirValidationError,
403
+ createBsuirClient
404
+ };
405
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":[]}
package/package.json ADDED
@@ -0,0 +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
+ }