bsuir-iis-api 0.12.0 → 0.13.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,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Browser runtime compatibility, hook coverage, and public type exports.
8
+ - Export `InvalidLessonTimeHook` from the package entry point for typed `onInvalidTime` handlers.
9
+ - Map browser `DOMException` with `name: "TimeoutError"` to abort/timeout handling so `AbortSignal.timeout()` surfaces as `BsuirTimeoutError` in Chromium and other browser runtimes.
10
+ - Invoke `onError` when a per-request `responseValidator` throws (e.g. `validateResponses` array checks on announcements).
11
+ - Reject impossible `dd.mm.yyyy` calendar dates during schedule date parsing.
12
+
3
13
  ## 0.12.0
4
14
 
5
15
  ### Minor Changes
package/README.md CHANGED
@@ -59,10 +59,10 @@ const client = createBsuirClient({
59
59
  - `allowedBaseUrlHosts` controls which hosts are allowed for `baseUrl` (defaults to `["iis.bsuir.by"]`).
60
60
  - `allowInsecureHttp` enables `http://` only for trusted local/test endpoints.
61
61
  - `signal` in `createBsuirClient({ signal })` acts as a global cancellation signal for all requests made by that client.
62
- - `cache` stores successful GET responses in-memory for the configured TTL. A live `AbortSignal` can still use cache; an already-aborted signal skips cache.
62
+ - `cache` stores successful GET responses in-memory for the configured TTL. A live `AbortSignal` can still use cache; an already-aborted signal skips cache. Cache hits return **deep-frozen** JSON (same reference on repeat reads); clone the payload if you need to mutate it. Use a separate client instance per identity in multi-tenant apps.
63
63
  - `dedupeInFlight` reuses the same in-flight GET request for concurrent callers. It is disabled for per-request signals, non-default cache modes, private credential headers, and already-aborted signals.
64
64
  - `maxResponseBytes` limits body size per response to protect against memory spikes.
65
- - `validateResponses` enables opt-in runtime payload-shape checks for key endpoints.
65
+ - `validateResponses` enables runtime payload-shape checks for list, schedule, announcement, and last-update endpoints when set to `true` (default: `false`). Normalized schedule calls still apply a minimal envelope check so normalization cannot crash on non-objects.
66
66
  - `hooks` provides lifecycle callbacks (`onRequest`, `onRetry`, `onResponse`, `onError`) for observability.
67
67
  - `AbortSignal` is supported by all read methods.
68
68
 
@@ -98,13 +98,13 @@ const client = createBsuirClient({
98
98
  - `client.announcements.byEmployee(urlId, options?)`
99
99
  - `client.announcements.byDepartment(id, options?)`
100
100
 
101
- When IIS responds with HTTP `404` or `400` (no list, missing resource, or endpoint quirks), these methods resolve to an empty array `[]` instead of throwing `BsuirApiError`. Client-side validation still runs first (`urlId`, department `id`). If IIS later returns a meaningful `400` for bad parameters, it will also map to `[]`; other HTTP errors are unchanged.
101
+ When IIS responds with HTTP `404` (the employee or department has no announcements), these methods resolve to an empty array `[]` instead of throwing `BsuirApiError`. Pass `treat404AsEmpty: false` to receive the underlying `BsuirApiError` instead. Client-side validation still runs first (`urlId`, department `id`); all other HTTP errors (including `400`) are always thrown so malformed-request bugs are not silently masked.
102
102
 
103
103
  ### Public exports (runtime utilities and types)
104
104
 
105
105
  - Core runtime API: `createBsuirClient`, `BsuirClient`
106
106
  - Client/runtime option types: `BsuirClientOptions`, `CacheOptions`, `ClientHooks`, `RequestOptions`, `ReadOptions`, `RequestHookContext`, `RetryHookContext`, `ResponseHookContext`, `ErrorHookContext`
107
- - Schedule utilities: `normalizeSchedule`, `filterLessons`, `getLessonsForDate`, `getTodayLessons`, `getTomorrowLessons`, `getLessonsForWeek`, `sortLessonsByTime`, `groupLessonsByDay`, `getCurrentLesson`, `getNextLesson`, `buildScheduleDays`, `ScheduleFilterOptions`
107
+ - Schedule utilities: `normalizeSchedule`, `filterLessons`, `getLessonsForDate`, `getTodayLessons`, `getTomorrowLessons`, `getLessonsForWeek`, `sortLessonsByTime`, `groupLessonsByDay`, `getCurrentLesson`, `getNextLesson`, `buildScheduleDays`, `ScheduleFilterOptions`, `InvalidLessonTimeHook`
108
108
  - Error classes: `BsuirApiError`, `BsuirNetworkError`, `BsuirTimeoutError`, `BsuirValidationError`, `BsuirResponseValidationError`, `BsuirResponsePayloadTooLargeError`, `BsuirConfigurationError`
109
109
  - Domain types: `Announcement`, `ApiDateResponse`, `Auditory`, `AuditoryDepartment`, `AuditoryType`, `BuildingNumber`, `Department`, `EducationForm`, `Employee`, `EmployeeCatalogItem`, `Faculty`, `FlattenedLessonsByDay`, `FlattenedScheduleItem`, `LessonStudentGroup`, `Maybe`, `NormalizedScheduleResponse`, `ScheduleItem`, `ScheduleResponse`, `Speciality`, `StudentGroupCatalogItem`, `StudentGroupShort`, `Weekday`, `WeekScheduleMap`
110
110
 
@@ -112,7 +112,7 @@ When IIS responds with HTTP `404` or `400` (no list, missing resource, or endpoi
112
112
 
113
113
  SDK throws typed errors:
114
114
 
115
- - `BsuirApiError` for HTTP errors (contains `status`, `endpoint`, `body`). **Exception:** `client.announcements.byEmployee` / `byDepartment` resolve to `[]` on IIS HTTP `404` or `400` instead of throwing (see Announcements above).
115
+ - `BsuirApiError` for HTTP errors (contains `status`, `endpoint`, `body`). **Exception:** `client.announcements.byEmployee` / `byDepartment` resolve to `[]` on IIS HTTP `404` (unless `treat404AsEmpty: false`) instead of throwing (see Announcements above).
116
116
  - `BsuirResponsePayloadTooLargeError` when response body size exceeds configured `maxResponseBytes`.
117
117
  - `BsuirNetworkError` for transport errors (contains `endpoint` and standard `cause`)
118
118
  - `BsuirResponseValidationError` for invalid payload shapes when `validateResponses: true`
@@ -222,7 +222,8 @@ PowerShell:
222
222
  $env:BSUIR_LIVE_TESTS="1"; npm run test:live
223
223
  ```
224
224
 
225
- CI has a manual `workflow_dispatch` path that also runs live contracts (`live-contract` job).
225
+ GitHub Actions runs live contracts weekly (Mondays 06:00 UTC) and on demand via the
226
+ **Live contract** workflow (`workflow_dispatch`).
226
227
 
227
228
  ## Release checklist
228
229
 
@@ -61,6 +61,8 @@ export declare function assertEmployeeUrlId(value: unknown, fieldName?: string):
61
61
  */
62
62
  export declare function assertGroupNumber(value: unknown, fieldName?: string): asserts value is string;
63
63
 
64
+ export declare type AssertInvalidLessonTimeHookExport = InvalidLessonTimeHook;
65
+
64
66
  /**
65
67
  * Asserts that a value is a non-empty string.
66
68
  */
@@ -501,7 +503,7 @@ export { ClientHooks }
501
503
  export { ClientHooks as ClientHooks_alias_1 }
502
504
 
503
505
  /**
504
- *
506
+ * Creates the announcements module (`byEmployee`, `byDepartment`).
505
507
  */
506
508
  declare function createAnnouncementsModule(config: Readonly<InternalClientConfig>): {
507
509
  byEmployee(urlId: string, options?: AnnouncementReadOptions): Promise<Announcement[]>;
@@ -571,6 +573,11 @@ export { createScheduleModule }
571
573
  export { createScheduleModule as createScheduleModule_alias_1 }
572
574
  export { createScheduleModule as createScheduleModule_alias_2 }
573
575
 
576
+ /**
577
+ * Fetch stub that rejects when the request AbortSignal fires — same contract as browser fetch.
578
+ */
579
+ export declare function createSignalAwareFetch(onAbort?: (signal: AbortSignal) => void): typeof fetch;
580
+
574
581
  /**
575
582
  * Creates API module for `/specialities`.
576
583
  */
@@ -578,10 +585,18 @@ declare function createSpecialitiesModule(config: Readonly<InternalClientConfig>
578
585
  export { createSpecialitiesModule }
579
586
  export { createSpecialitiesModule as createSpecialitiesModule_alias_1 }
580
587
 
588
+ export declare interface DdMmYyyyParts {
589
+ day: number;
590
+ month: number;
591
+ year: number;
592
+ }
593
+
581
594
  export declare const default_alias: Options | Options[] | ((overrideOptions: Options) => Options | Options[] | Promise<Options | Options[]>);
582
595
 
583
596
  export declare const default_alias_1: UserConfig;
584
597
 
598
+ export declare const default_alias_2: UserConfig;
599
+
585
600
  declare interface Department {
586
601
  id: number;
587
602
  name: string;
@@ -972,11 +987,13 @@ export declare interface InternalClientConfig {
972
987
  * });
973
988
  * ```
974
989
  */
975
- export declare type InvalidLessonTimeHook = (info: {
990
+ declare type InvalidLessonTimeHook = (info: {
976
991
  field: "startLessonTime" | "endLessonTime";
977
992
  value: string;
978
993
  lesson: LessonWithTime;
979
994
  }) => void;
995
+ export { InvalidLessonTimeHook }
996
+ export { InvalidLessonTimeHook as InvalidLessonTimeHook_alias_1 }
980
997
 
981
998
  /**
982
999
  * Returns `true` if the error object represents an abort/cancellation.
@@ -1067,10 +1084,17 @@ export declare function parseBody(response: Response, maxResponseBytes: number):
1067
1084
  export declare function parseCurrentWeek(payload: unknown): number;
1068
1085
 
1069
1086
  /**
1070
- *
1087
+ * Parses a BSUIR `dd.mm.yyyy` date string into a UTC midnight `Date`.
1088
+ * Returns `null` for missing, malformed, or impossible calendar dates.
1071
1089
  */
1072
1090
  export declare function parseDdMmYyyy(value: string | null): Date | null;
1073
1091
 
1092
+ /**
1093
+ * Parses a BSUIR `dd.mm.yyyy` date string into numeric parts.
1094
+ * Returns `null` for missing, malformed, or impossible calendar dates.
1095
+ */
1096
+ export declare function parseDdMmYyyyParts(value: string | null): DdMmYyyyParts | null;
1097
+
1074
1098
  declare type QueryParams = Record<string, QueryValue>;
1075
1099
  export { QueryParams }
1076
1100
  export { QueryParams as QueryParams_alias_1 }
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export { ResponseHookContext } from './_tsup-dts-rollup.js';
15
15
  export { RetryHookContext } from './_tsup-dts-rollup.js';
16
16
  export { ReadOptions } from './_tsup-dts-rollup.js';
17
17
  export { ScheduleModule } from './_tsup-dts-rollup.js';
18
+ export { InvalidLessonTimeHook } from './_tsup-dts-rollup.js';
18
19
  export { buildScheduleDays } from './_tsup-dts-rollup.js';
19
20
  export { filterLessons } from './_tsup-dts-rollup.js';
20
21
  export { formatEmployeeShortName } from './_tsup-dts-rollup.js';
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  // src/client/errors.ts
2
- function fixErrorPrototype(instance, prototype) {
3
- Object.setPrototypeOf(instance, prototype);
2
+ function fixErrorPrototype(_instance, _prototype) {
4
3
  }
5
4
  var BsuirApiError = class _BsuirApiError extends Error {
6
5
  status;
@@ -196,7 +195,7 @@ function assertEmployeeUrlId(value, fieldName = "urlId") {
196
195
  }
197
196
  }
198
197
  function isAbortError(error) {
199
- if (error instanceof DOMException && error.name === "AbortError") {
198
+ if (error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) {
200
199
  return true;
201
200
  }
202
201
  if (typeof error === "object" && error !== null) {
@@ -424,11 +423,7 @@ var UNSAFE_QUERY_KEY = /[\u0000-\u0020\u007F&=?#]/;
424
423
  var SCHEME_PREFIX = /^[A-Za-z][A-Za-z\d+.-]*:/;
425
424
  function assertSafeQueryKey(key) {
426
425
  if (key.length === 0) {
427
- throw new BsuirValidationError(
428
- "Query parameter key must not be empty",
429
- "queryKey",
430
- key
431
- );
426
+ throw new BsuirValidationError("Query parameter key must not be empty", "queryKey", key);
432
427
  }
433
428
  if (UNSAFE_QUERY_KEY.test(key)) {
434
429
  throw new BsuirValidationError(
@@ -468,7 +463,11 @@ function assertSafePath(path) {
468
463
  throw new BsuirValidationError("Path contains malformed escape sequence", "path", path);
469
464
  }
470
465
  if (decodedSegment === "." || decodedSegment === "..") {
471
- throw new BsuirValidationError("Path must not contain relative traversal segments", "path", path);
466
+ throw new BsuirValidationError(
467
+ "Path must not contain relative traversal segments",
468
+ "path",
469
+ path
470
+ );
472
471
  }
473
472
  }
474
473
  }
@@ -617,6 +616,7 @@ async function requestJson(config, path, options = {}) {
617
616
  return cached;
618
617
  }
619
618
  }
619
+ let lastSuccessResponse;
620
620
  const performRequest = async () => {
621
621
  for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
622
622
  const attemptNumber = attempt + 1;
@@ -645,7 +645,11 @@ async function requestJson(config, path, options = {}) {
645
645
  if (!response.ok) {
646
646
  const errorBody = await parseBody(response, config.maxResponseBytes);
647
647
  if (attempt < maxRetries && RETRIABLE_STATUS_CODES.has(response.status)) {
648
- const retryDecision = getRetryDecision(config, attempt, response.headers.get("retry-after"));
648
+ const retryDecision = getRetryDecision(
649
+ config,
650
+ attempt,
651
+ response.headers.get("retry-after")
652
+ );
649
653
  if (retryDecision.retryable) {
650
654
  const retryCtx = {
651
655
  ...hookCtx,
@@ -680,13 +684,15 @@ async function requestJson(config, path, options = {}) {
680
684
  throw apiError;
681
685
  }
682
686
  const parsed = await parseBody(response, config.maxResponseBytes);
687
+ const durationMs = Date.now() - startedAt;
683
688
  const responseCtx = {
684
689
  ...hookCtx,
685
690
  status: response.status,
686
- durationMs: Date.now() - startedAt,
691
+ durationMs,
687
692
  fromCache: false
688
693
  };
689
694
  config.hooks.onResponse?.(responseCtx);
695
+ lastSuccessResponse = { hookCtx, durationMs };
690
696
  return parsed;
691
697
  } catch (error) {
692
698
  if (error instanceof BsuirApiError) {
@@ -755,8 +761,28 @@ async function requestJson(config, path, options = {}) {
755
761
  }
756
762
  throw new BsuirNetworkError(`Unexpected retry loop termination for ${path}`, endpoint, null);
757
763
  };
764
+ const runResponseValidator = (payload) => {
765
+ if (!options.responseValidator) {
766
+ return;
767
+ }
768
+ const responseMeta = lastSuccessResponse ?? {
769
+ hookCtx: baseHookContext(method, path, endpoint, maxAttempts, maxAttempts, options.query),
770
+ durationMs: 0
771
+ };
772
+ try {
773
+ options.responseValidator(payload);
774
+ } catch (error) {
775
+ const errorCtx = {
776
+ ...responseMeta.hookCtx,
777
+ durationMs: responseMeta.durationMs,
778
+ error
779
+ };
780
+ config.hooks.onError?.(errorCtx);
781
+ throw error;
782
+ }
783
+ };
758
784
  const requestAndMaybeCache = () => performRequest().then((payload) => {
759
- options.responseValidator?.(payload);
785
+ runResponseValidator(payload);
760
786
  if (canWriteToCache) {
761
787
  const cached = setCache(config, ensureRequestKey(), payload);
762
788
  if (cached !== void 0) {
@@ -860,10 +886,12 @@ function endpointMatchesPath(endpoint, path) {
860
886
  async function requestAnnouncementList(config, path, options) {
861
887
  const treat404AsEmpty = options.treat404AsEmpty ?? true;
862
888
  try {
863
- const payload = await requestJson(config, path, options);
864
- if (config.validateResponses) {
865
- assertArrayResponse(payload, path);
866
- }
889
+ const payload = await requestJson(config, path, {
890
+ ...options,
891
+ responseValidator: config.validateResponses ? (value) => {
892
+ assertArrayResponse(value, path);
893
+ } : void 0
894
+ });
867
895
  return payload;
868
896
  } catch (error) {
869
897
  if (treat404AsEmpty && error instanceof BsuirApiError && error.status === 404 && endpointMatchesPath(error.endpoint, path)) {
@@ -914,9 +942,11 @@ function createListModule(config, endpoint) {
914
942
  async listAll(options = {}) {
915
943
  const payload = await requestJson(config, endpoint, {
916
944
  signal: options.signal,
917
- cache: options.cache
945
+ cache: options.cache,
946
+ responseValidator: config.validateResponses ? (value) => {
947
+ assertArrayResponse(value, endpoint);
948
+ } : void 0
918
949
  });
919
- assertArrayResponse(payload, endpoint);
920
950
  return payload;
921
951
  }
922
952
  };
@@ -1041,6 +1071,9 @@ function filterLessons(response, filter) {
1041
1071
  if (typeof filter.weekNumber === "number") {
1042
1072
  assertPositiveInt(filter.weekNumber, "filter.weekNumber");
1043
1073
  }
1074
+ if (typeof filter.subgroup === "number") {
1075
+ assertPositiveInt(filter.subgroup, "filter.subgroup");
1076
+ }
1044
1077
  return response.lessons.filter((item) => matchesFilter(item, filter));
1045
1078
  }
1046
1079
 
@@ -1342,11 +1375,11 @@ function createScheduleModule(config) {
1342
1375
  const payload = await requestJson(config, "/last-update-date/student-group", {
1343
1376
  query,
1344
1377
  signal: options.signal,
1345
- cache: options.cache
1378
+ cache: options.cache,
1379
+ responseValidator: config.validateResponses ? (value) => {
1380
+ assertApiDateResponse(value, "/last-update-date/student-group");
1381
+ } : void 0
1346
1382
  });
1347
- if (config.validateResponses) {
1348
- assertApiDateResponse(payload, "/last-update-date/student-group");
1349
- }
1350
1383
  return payload;
1351
1384
  },
1352
1385
  /**
@@ -1364,24 +1397,17 @@ function createScheduleModule(config) {
1364
1397
  const payload = await requestJson(config, "/last-update-date/employee", {
1365
1398
  query,
1366
1399
  signal: options.signal,
1367
- cache: options.cache
1400
+ cache: options.cache,
1401
+ responseValidator: config.validateResponses ? (value) => {
1402
+ assertApiDateResponse(value, "/last-update-date/employee");
1403
+ } : void 0
1368
1404
  });
1369
- if (config.validateResponses) {
1370
- assertApiDateResponse(payload, "/last-update-date/employee");
1371
- }
1372
1405
  return payload;
1373
1406
  }
1374
1407
  };
1375
1408
  }
1376
1409
 
1377
- // src/helpers/schedule.ts
1378
- var SUNDAY_LABEL = "\u0412\u043E\u0441\u043A\u0440\u0435\u0441\u0435\u043D\u044C\u0435";
1379
- var MS_PER_DAY = 24 * 60 * 60 * 1e3;
1380
- function createEmptyLessonsByDay() {
1381
- return Object.fromEntries(
1382
- WEEKDAYS.map((day) => [day, []])
1383
- );
1384
- }
1410
+ // src/utils/date.ts
1385
1411
  function parseDdMmYyyyParts(value) {
1386
1412
  if (!value) {
1387
1413
  return null;
@@ -1403,6 +1429,15 @@ function parseDdMmYyyyParts(value) {
1403
1429
  }
1404
1430
  return { day, month, year };
1405
1431
  }
1432
+
1433
+ // src/helpers/schedule.ts
1434
+ var SUNDAY_LABEL = "\u0412\u043E\u0441\u043A\u0440\u0435\u0441\u0435\u043D\u044C\u0435";
1435
+ var MS_PER_DAY = 24 * 60 * 60 * 1e3;
1436
+ function createEmptyLessonsByDay() {
1437
+ return Object.fromEntries(
1438
+ WEEKDAYS.map((day) => [day, []])
1439
+ );
1440
+ }
1406
1441
  function toDayOrdinal(parts) {
1407
1442
  return Math.floor(Date.UTC(parts.year, parts.month - 1, parts.day) / MS_PER_DAY);
1408
1443
  }
@@ -1745,9 +1780,7 @@ function assertSafeHeaderValue(value, optionName) {
1745
1780
  return void 0;
1746
1781
  }
1747
1782
  if (INVALID_HEADER_LINE_BREAK.test(value) || value.includes("\0")) {
1748
- throw new BsuirConfigurationError(
1749
- `'${optionName}' must not contain CR, LF or NUL characters`
1750
- );
1783
+ throw new BsuirConfigurationError(`'${optionName}' must not contain CR, LF or NUL characters`);
1751
1784
  }
1752
1785
  return value;
1753
1786
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/errors.ts","../src/client/mergeSignals.ts","../src/utils/guards.ts","../src/client/http/cache.ts","../src/client/http/response.ts","../src/client/http/retry.ts","../src/client/http/url.ts","../src/client/http/requestJson.ts","../src/client/responseValidators.ts","../src/modules/announcements.ts","../src/modules/createListModule.ts","../src/modules/auditories.ts","../src/modules/departments.ts","../src/modules/employees.ts","../src/modules/faculties.ts","../src/modules/groups.ts","../src/utils/week.ts","../src/modules/scheduleFilter.ts","../src/types/common.ts","../src/modules/scheduleNormalize.ts","../src/modules/scheduleApi.ts","../src/helpers/schedule.ts","../src/helpers/scheduleFormat.ts","../src/modules/specialities.ts","../src/client/createClient.ts"],"sourcesContent":["/**\n * Restores proper prototype chain for Error subclasses in transpiled outputs.\n * TypeScript's ES<2015 transpilation breaks `class Foo extends Error`:\n * the compiled function lacks Error.prototype in its chain. This restores it.\n * See: https://github.com/microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins\n */\nfunction fixErrorPrototype(instance: Error, prototype: object): void {\n Object.setPrototypeOf(instance, prototype);\n}\n\nexport class BsuirApiError extends Error {\n readonly status: number;\n readonly endpoint: string;\n readonly body: unknown;\n\n constructor(message: string, status: number, endpoint: string, body: unknown) {\n super(message);\n fixErrorPrototype(this, BsuirApiError.prototype);\n this.name = \"BsuirApiError\";\n this.status = status;\n this.endpoint = endpoint;\n this.body = body;\n }\n}\n\nexport class BsuirResponsePayloadTooLargeError extends Error {\n readonly status: number;\n readonly endpoint: string;\n readonly maxResponseBytes: number;\n\n constructor(message: string, status: number, endpoint: string, maxResponseBytes: number) {\n super(message);\n fixErrorPrototype(this, BsuirResponsePayloadTooLargeError.prototype);\n this.name = \"BsuirResponsePayloadTooLargeError\";\n this.status = status;\n this.endpoint = endpoint;\n this.maxResponseBytes = maxResponseBytes;\n }\n}\n\nexport class BsuirNetworkError extends Error {\n readonly endpoint: string;\n\n constructor(message: string, endpoint: string, cause: unknown) {\n super(message, { cause });\n fixErrorPrototype(this, BsuirNetworkError.prototype);\n this.name = \"BsuirNetworkError\";\n this.endpoint = endpoint;\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, cause?: unknown) {\n super(message, { cause });\n fixErrorPrototype(this, BsuirTimeoutError.prototype);\n this.name = \"BsuirTimeoutError\";\n this.endpoint = endpoint;\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class BsuirValidationError extends Error {\n readonly field: string | undefined;\n readonly value: unknown;\n\n constructor(message: string, field?: string, value?: unknown) {\n super(message);\n fixErrorPrototype(this, BsuirValidationError.prototype);\n this.name = \"BsuirValidationError\";\n this.field = field;\n this.value = value;\n }\n}\n\nexport class BsuirResponseValidationError extends Error {\n readonly endpoint: string;\n\n constructor(message: string, endpoint: string) {\n super(message);\n fixErrorPrototype(this, BsuirResponseValidationError.prototype);\n this.name = \"BsuirResponseValidationError\";\n this.endpoint = endpoint;\n }\n}\n\nexport class BsuirConfigurationError extends Error {\n constructor(message: string) {\n super(message);\n fixErrorPrototype(this, BsuirConfigurationError.prototype);\n this.name = \"BsuirConfigurationError\";\n }\n}\n","type AbortSignalConstructor = typeof AbortSignal & {\n any?(signals: AbortSignal[]): AbortSignal;\n};\n\nconst AbortSignalCtor = AbortSignal as AbortSignalConstructor;\n\n// Use a WeakMap to associate merged signals with their cleanup callbacks instead\n// of attaching properties to the signal object.\nconst mergedSignalCleanupMap = new WeakMap<AbortSignal, () => void>();\n\nfunction getOnlySignal(signals: readonly AbortSignal[]): AbortSignal {\n const signal = signals[0];\n if (signal === undefined) {\n throw new Error(\"Expected at least one abort signal\");\n }\n return signal;\n}\n\n/**\n * Returns a cleanup callback for manually merged signals, when available.\n */\nexport function getMergedSignalCleanup(signal: AbortSignal): (() => void) | undefined {\n return mergedSignalCleanupMap.get(signal);\n}\n\n/**\n * Combines multiple abort signals and/or a timeout into a single signal.\n * When `AbortSignal.any` exists at runtime, delegates to the platform implementation.\n * Otherwise uses a manual merge so all signals are respected.\n */\nexport function mergeSignals(\n signals: readonly (AbortSignal | undefined)[],\n timeoutMs?: number\n): AbortSignal {\n const parts = signals.filter((s): s is AbortSignal => s !== undefined);\n\n if (parts.length === 0) {\n if (timeoutMs !== undefined) {\n if (typeof AbortSignalCtor.any === \"function\") {\n return AbortSignalCtor.any([AbortSignal.timeout(timeoutMs)]);\n }\n return mergeSignalsManual([], timeoutMs);\n }\n return new AbortController().signal;\n }\n\n if (parts.length === 1 && timeoutMs === undefined) {\n return getOnlySignal(parts);\n }\n\n if (typeof AbortSignalCtor.any === \"function\") {\n const all = timeoutMs === undefined ? parts : [...parts, AbortSignal.timeout(timeoutMs)];\n return AbortSignalCtor.any(all);\n }\n\n return mergeSignalsManual(parts, timeoutMs);\n}\n\n/** Used when `AbortSignal.any` is unavailable; exposed for unit tests. */\nexport function mergeSignalsManual(signals: AbortSignal[], timeoutMs?: number): AbortSignal {\n if (signals.length === 0 && timeoutMs === undefined) {\n return new AbortController().signal;\n }\n if (signals.length === 1 && timeoutMs === undefined) {\n return getOnlySignal(signals);\n }\n\n const combined = new AbortController();\n const listeners: { signal: AbortSignal; handler: () => void }[] = [];\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let cleanedUp = false;\n\n const onAnyAbort = (): void => {\n if (!combined.signal.aborted) {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n combined.abort();\n }\n };\n\n const cleanup = (): void => {\n if (cleanedUp) {\n return;\n }\n cleanedUp = true;\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n for (const listener of listeners) {\n listener.signal.removeEventListener(\"abort\", listener.handler);\n }\n listeners.length = 0;\n combined.signal.removeEventListener(\"abort\", cleanup);\n mergedSignalCleanupMap.delete(combined.signal);\n };\n\n // Register cleanup before possibly synchronous abort path below.\n combined.signal.addEventListener(\"abort\", cleanup, { once: true });\n mergedSignalCleanupMap.set(combined.signal, cleanup);\n\n if (timeoutMs !== undefined) {\n timeoutId = setTimeout(() => {\n if (!combined.signal.aborted) {\n combined.abort();\n }\n }, timeoutMs);\n }\n\n for (const signal of signals) {\n if (signal.aborted) {\n onAnyAbort();\n break;\n }\n const record = { signal, handler: onAnyAbort };\n listeners.push(record);\n signal.addEventListener(\"abort\", onAnyAbort, { once: true });\n }\n\n return combined.signal;\n}\n","import { BsuirValidationError } from \"../client/errors\";\n\n// BSUIR IIS API accepts only numeric group IDs (e.g., \"053503\").\n// Pattern is intentionally strict and covers only current IIS format.\nconst GROUP_NUMBER_PATTERN = /^\\d+$/;\nconst EMPLOYEE_URL_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/i;\n\n/**\n * Asserts that a value is a non-empty string.\n */\nexport function assertNonEmptyString(value: unknown, fieldName: string): asserts value is string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new BsuirValidationError(`'${fieldName}' must be a non-empty string`, fieldName, value);\n }\n}\n\n/**\n * Asserts that a value is a positive integer.\n */\nexport function assertPositiveInt(value: unknown, fieldName: string): asserts value is number {\n if (typeof value !== \"number\" || !Number.isInteger(value) || value <= 0) {\n throw new BsuirValidationError(`'${fieldName}' must be a positive integer`, fieldName, value);\n }\n}\n\n/**\n * Asserts that a group number is a non-empty string containing only digits.\n */\nexport function assertGroupNumber(\n value: unknown,\n fieldName = \"groupNumber\"\n): asserts value is string {\n assertNonEmptyString(value, fieldName);\n if (!GROUP_NUMBER_PATTERN.test(value)) {\n throw new BsuirValidationError(`'${fieldName}' must contain only digits`, fieldName, value);\n }\n}\n\n/**\n * Asserts that an employee urlId is a non-empty slug.\n */\nexport function assertEmployeeUrlId(value: unknown, fieldName = \"urlId\"): asserts value is string {\n assertNonEmptyString(value, fieldName);\n if (!EMPLOYEE_URL_ID_PATTERN.test(value)) {\n throw new BsuirValidationError(\n `'${fieldName}' must be a valid slug (letters, digits, hyphen)`,\n fieldName,\n value\n );\n }\n}\n\n/**\n * Returns `true` if the error object represents an abort/cancellation.\n */\nexport function isAbortError(error: unknown): boolean {\n // Browser: DOMException with name \"AbortError\"\n if (error instanceof DOMException && error.name === \"AbortError\") {\n return true;\n }\n\n // Node.js: Error-like object with code \"ABORT_ERR\"\n if (typeof error === \"object\" && error !== null) {\n const maybeError = error as { name?: unknown; code?: unknown };\n return maybeError.name === \"AbortError\" || maybeError.code === \"ABORT_ERR\";\n }\n\n return false;\n}\n","import { BsuirConfigurationError } from \"../errors\";\nimport type { InternalClientConfig } from \"../types\";\n\nfunction isJsonValue(value: unknown): boolean {\n if (value === null) {\n return true;\n }\n const t = typeof value;\n if (t === \"string\" || t === \"boolean\") {\n return true;\n }\n if (t === \"number\") {\n return Number.isFinite(value);\n }\n if (t !== \"object\") {\n return false;\n }\n // Reject typed arrays, Maps, Sets, Dates, etc. structuredClone would handle some of these\n // but the cache layer is designed to hold parsed JSON only; anything else indicates a\n // misconfigured value reaching the cache.\n const proto: unknown = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== Array.prototype && proto !== null) {\n return false;\n }\n return true;\n}\n\nfunction deepFreezeJson<T>(value: T): T {\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n if (Object.isFrozen(value)) {\n return value;\n }\n Object.freeze(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n deepFreezeJson(item);\n }\n } else {\n for (const key of Object.keys(value)) {\n deepFreezeJson((value as Record<string, unknown>)[key]);\n }\n }\n return value;\n}\n\n/**\n * Reads a cached response value by key, applying TTL validation and LRU touch update.\n *\n * Returns the cached value directly (deep-frozen on write) instead of cloning on every\n * read. Callers that need a mutable copy must clone explicitly. This avoids paying\n * structuredClone() cost on every cache hit, which can dominate latency for large\n * schedule payloads on a hot cache path.\n */\nexport function tryReadCache(config: Readonly<InternalClientConfig>, key: string): unknown {\n const entry = config.responseCache.get(key);\n if (!entry) {\n return undefined;\n }\n if (entry.expiresAt <= Date.now()) {\n config.responseCache.delete(key);\n return undefined;\n }\n // Touch key to move it to the end (most recently used).\n config.responseCache.delete(key);\n config.responseCache.set(key, entry);\n return entry.value;\n}\n\n/**\n * Writes response value to cache and performs expiration/LRU eviction.\n *\n * Throws if the value is not a JSON-compatible structure — the cache is designed to\n * hold parsed JSON only, and storing other shapes (Map/Set/Date/typed arrays/class\n * instances) would either fail later on read or silently lose data on\n * structuredClone-based eviction strategies.\n */\nexport function setCache<T>(\n config: Readonly<InternalClientConfig>,\n key: string,\n value: T\n): T | undefined {\n if (config.cacheTtlMs === undefined) {\n return undefined;\n }\n if (!isJsonValue(value)) {\n throw new BsuirConfigurationError(\n \"Response cache only accepts JSON-compatible values (object/array/string/finite-number/boolean/null)\"\n );\n }\n const now = Date.now();\n\n // Clone once on write to isolate the cache from later mutations of the caller's\n // payload, then deep-freeze so reads can return the same reference safely without\n // paying structuredClone cost on every hit.\n const frozen = deepFreezeJson(structuredClone(value));\n\n // Re-insert to mark as most recently used.\n config.responseCache.delete(key);\n config.responseCache.set(key, {\n value: frozen,\n expiresAt: now + config.cacheTtlMs\n });\n\n if (config.responseCache.size <= config.cacheMaxEntries) {\n return frozen;\n }\n\n // Remove expired entries before capacity-based eviction, but only when oversized.\n for (const [k, v] of config.responseCache) {\n if (v.expiresAt <= now) {\n config.responseCache.delete(k);\n }\n }\n\n if (config.responseCache.size <= config.cacheMaxEntries) {\n return frozen;\n }\n\n // Evict least-recently-used entries using insertion order.\n for (const k of config.responseCache.keys()) {\n if (config.responseCache.size <= config.cacheMaxEntries) {\n break;\n }\n config.responseCache.delete(k);\n }\n\n return frozen;\n}\n","import { BsuirApiError, BsuirResponsePayloadTooLargeError } from \"../errors\";\n\nconst UTF8_ENCODER = new TextEncoder();\n\nasync function readBodyTextWithLimit(\n response: Response,\n maxResponseBytes: number\n): Promise<string> {\n const contentLengthHeader = response.headers.get(\"content-length\");\n if (contentLengthHeader) {\n const parsedLength = Number(contentLengthHeader);\n if (Number.isFinite(parsedLength) && parsedLength > maxResponseBytes) {\n throw new BsuirResponsePayloadTooLargeError(\n `Response body exceeds maxResponseBytes limit (${String(maxResponseBytes)} bytes)`,\n response.status,\n response.url,\n maxResponseBytes\n );\n }\n }\n\n if (!response.body || typeof response.body.getReader !== \"function\") {\n const fallbackText = await response.text();\n if (UTF8_ENCODER.encode(fallbackText).byteLength > maxResponseBytes) {\n throw new BsuirResponsePayloadTooLargeError(\n `Response body exceeds maxResponseBytes limit (${String(maxResponseBytes)} bytes)`,\n response.status,\n response.url,\n maxResponseBytes\n );\n }\n return fallbackText;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let bytesRead = 0;\n let text = \"\";\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n bytesRead += value.byteLength;\n if (bytesRead > maxResponseBytes) {\n await reader.cancel();\n throw new BsuirResponsePayloadTooLargeError(\n `Response body exceeds maxResponseBytes limit (${String(maxResponseBytes)} bytes)`,\n response.status,\n response.url,\n maxResponseBytes\n );\n }\n text += decoder.decode(value, { stream: true });\n }\n text += decoder.decode();\n return text;\n}\n\n/**\n * Parses response body as JSON when possible, otherwise returns text.\n * Returns `null` for empty non-JSON bodies.\n * Throws `BsuirApiError` for declared JSON payloads that are empty/invalid.\n */\nexport async function parseBody(response: Response, maxResponseBytes: number): Promise<unknown> {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n const declaredJson = contentType.includes(\"application/json\");\n const text = await readBodyTextWithLimit(response, maxResponseBytes);\n if (text.length === 0) {\n if (declaredJson) {\n throw new BsuirApiError(\"Invalid JSON response payload\", response.status, response.url, null);\n }\n return null;\n }\n try {\n return JSON.parse(text) as unknown;\n } catch {\n if (declaredJson) {\n throw new BsuirApiError(\"Invalid JSON response payload\", response.status, response.url, null);\n }\n return text;\n }\n}\n","import type { InternalClientConfig } from \"../types\";\n\n/** HTTP status codes retriable by the request pipeline. */\nexport const RETRIABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\nconst MAX_ACCEPTED_RETRY_AFTER_MS = 60_000;\n// Hard internal cap applied to any Retry-After value (numeric or date-based) before\n// the higher-level decision logic compares it against MAX_ACCEPTED_RETRY_AFTER_MS.\n// Prevents arithmetic overflow or unreasonably large delays from hostile/misconfigured\n// upstreams from being propagated further.\nconst RETRY_AFTER_INTERNAL_CAP_MS = 86_400_000;\n\n/** Delays execution for a specified number of milliseconds. */\nexport function 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 || retryAfter.trim().length === 0) {\n return null;\n }\n\n // Try parsing as seconds (RFC 7231: numeric-value).\n const asSeconds = Number(retryAfter);\n if (\n Number.isFinite(asSeconds) &&\n asSeconds >= 0 && // Validate it's actually numeric format, not a date string starting with a digit.\n /^\\d+(\\.\\d+)?$/.test(retryAfter.trim())\n ) {\n const ms = Math.floor(asSeconds * 1000);\n return Math.min(ms, RETRY_AFTER_INTERNAL_CAP_MS);\n }\n\n // Try parsing as HTTP date format (RFC 7231: http-date).\n const dateValue = Date.parse(retryAfter);\n if (Number.isFinite(dateValue)) {\n const delayMs = dateValue - Date.now();\n // Only accept if date is in the future.\n if (delayMs > 0) {\n return Math.min(delayMs, RETRY_AFTER_INTERNAL_CAP_MS);\n }\n }\n\n return null;\n}\n\nfunction getBackoffDelayMs(config: Readonly<InternalClientConfig>, attempt: number): number {\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 const jitterFactor = 0.75 + Math.random() * 0.5;\n return Math.floor(baseDelay * jitterFactor);\n}\n\nexport type RetryDecision =\n | { retryable: true; delayMs: number }\n | { retryable: false; rejectedDelayMs: number };\n\n/**\n * Calculates retry behavior for the current attempt.\n *\n * If server-provided `Retry-After` exceeds a sane safety bound, retry is rejected\n * to avoid long caller stalls caused by hostile or misconfigured upstreams.\n */\nexport function getRetryDecision(\n config: Readonly<InternalClientConfig>,\n attempt: number,\n retryAfterHeader?: string | null\n): RetryDecision {\n const retryAfterDelay = parseRetryAfterMs(retryAfterHeader ?? null);\n if (retryAfterDelay !== null) {\n if (retryAfterDelay > MAX_ACCEPTED_RETRY_AFTER_MS) {\n return { retryable: false, rejectedDelayMs: retryAfterDelay };\n }\n return { retryable: true, delayMs: Math.min(retryAfterDelay, config.retryMaxDelayMs) };\n }\n return { retryable: true, delayMs: getBackoffDelayMs(config, attempt) };\n}\n\n/**\n * Calculates retry delay using `Retry-After` when present, otherwise exponential backoff.\n */\nexport function getRetryDelayMs(\n config: Readonly<InternalClientConfig>,\n attempt: number,\n retryAfterHeader?: string | null\n): number {\n const decision = getRetryDecision(config, attempt, retryAfterHeader);\n if (decision.retryable) {\n return decision.delayMs;\n }\n return getBackoffDelayMs(config, attempt);\n}\n","import type { QueryParams } from \"../types\";\nimport { BsuirValidationError } from \"../errors\";\n\n// Reject control chars (incl. CR/LF/NUL), whitespace, DEL, and URL-structural characters\n// (`?`, `#`, `&`, `=`). Other printable characters are allowed and will be percent-encoded\n// by URLSearchParams as needed.\n// eslint-disable-next-line no-control-regex\nconst UNSAFE_QUERY_KEY = /[\\u0000-\\u0020\\u007F&=?#]/;\nconst SCHEME_PREFIX = /^[A-Za-z][A-Za-z\\d+.-]*:/;\n\nfunction assertSafeQueryKey(key: string): void {\n if (key.length === 0) {\n throw new BsuirValidationError(\n \"Query parameter key must not be empty\",\n \"queryKey\",\n key\n );\n }\n if (UNSAFE_QUERY_KEY.test(key)) {\n throw new BsuirValidationError(\n `Invalid query parameter key '${key}': must not contain control characters, whitespace, or '? # & ='`,\n \"queryKey\",\n key\n );\n }\n}\n\nfunction assertSafePath(path: string): void {\n if (path.trim().length === 0) {\n throw new BsuirValidationError(\"Path must not be empty\", \"path\", path);\n }\n if (path.includes(\"\\\\\")) {\n throw new BsuirValidationError(\n \"Path must use forward slashes and must not contain backslashes\",\n \"path\",\n path\n );\n }\n if (path.startsWith(\"//\")) {\n throw new BsuirValidationError(\"Path must not start with '//'\", \"path\", path);\n }\n if (SCHEME_PREFIX.test(path)) {\n throw new BsuirValidationError(\"Path must not include a URL scheme\", \"path\", path);\n }\n\n const normalizedPath = path.startsWith(\"/\") ? path.slice(1) : path;\n const segments = normalizedPath.split(\"/\");\n for (const segment of segments) {\n if (segment.length === 0) {\n continue;\n }\n let decodedSegment: string;\n try {\n decodedSegment = decodeURIComponent(segment);\n } catch {\n throw new BsuirValidationError(\"Path contains malformed escape sequence\", \"path\", path);\n }\n if (decodedSegment === \".\" || decodedSegment === \"..\") {\n throw new BsuirValidationError(\"Path must not contain relative traversal segments\", \"path\", path);\n }\n }\n}\n\n/**\n * Builds absolute endpoint URL from base URL, path and query params.\n *\n * Query parameters are sorted by key to produce deterministic URLs - important\n * for cache key stability and for reproducible request fingerprints.\n */\nexport function buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n assertSafePath(path);\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 const sortedEntries = Object.entries(query)\n .filter(([, value]) => value !== undefined && value !== null)\n .toSorted((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n for (const [key, value] of sortedEntries) {\n assertSafeQueryKey(key);\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n}\n","import {\n BsuirApiError,\n BsuirNetworkError,\n BsuirResponsePayloadTooLargeError,\n BsuirTimeoutError\n} from \"../errors\";\nimport { getMergedSignalCleanup, mergeSignals } from \"../mergeSignals\";\nimport type {\n ErrorHookContext,\n InternalClientConfig,\n QueryParams,\n RequestHookContext,\n RequestMethod,\n RequestOptions,\n ResponseHookContext,\n RetryHookContext\n} from \"../types\";\nimport { isAbortError } from \"../../utils/guards\";\nimport { setCache, tryReadCache } from \"./cache\";\nimport { parseBody } from \"./response\";\nimport { getRetryDecision, getRetryDelayMs, RETRIABLE_STATUS_CODES, sleep } from \"./retry\";\nimport { buildUrl } from \"./url\";\n\nconst CACHE_KEY_HEADER_ALLOWLIST = new Set<string>([\"accept\", \"accept-language\"]);\n\nfunction normalizeHeadersForRequestKey(headers: Headers): string {\n return [...headers.entries()]\n .map(([key, value]) => [key.toLowerCase(), value] as const)\n .filter(([key]) => CACHE_KEY_HEADER_ALLOWLIST.has(key))\n .toSorted((a, b) => {\n if (a[0] !== b[0]) {\n return a[0] < b[0] ? -1 : 1;\n }\n if (a[1] !== b[1]) {\n return a[1] < b[1] ? -1 : 1;\n }\n return 0;\n })\n .map(([key, value]) => `${key}:${value}`)\n .join(\"\\n\");\n}\n\nfunction buildRequestKey(method: RequestMethod, endpoint: string, headers: Headers): string {\n return `${method}\\n${endpoint}\\n${normalizeHeadersForRequestKey(headers)}`;\n}\n\n// Explicit denylist of header names that carry per-identity credentials. Any of these,\n// when present on a request, disables shared response caching and in-flight dedup so\n// that one identity's response cannot be returned to another caller.\nconst PRIVATE_HEADER_DENYLIST = new Set<string>([\n \"authorization\",\n \"proxy-authorization\",\n \"cookie\",\n \"set-cookie\",\n \"x-api-key\",\n \"api-key\",\n \"x-auth-token\",\n \"x-access-token\",\n \"x-csrf-token\",\n \"x-session-id\",\n \"x-session-token\"\n]);\n\nfunction isBodyInit(value: unknown): value is BodyInit {\n if (typeof value === \"string\") {\n return true;\n }\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n if (\n value instanceof URLSearchParams ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value)\n ) {\n return true;\n }\n // FormData, Blob and ReadableStream are not always available in every runtime,\n // so guard with typeof to avoid ReferenceErrors in minimal environments.\n if (typeof FormData !== \"undefined\" && value instanceof FormData) {\n return true;\n }\n if (typeof Blob !== \"undefined\" && value instanceof Blob) {\n return true;\n }\n if (typeof ReadableStream !== \"undefined\" && value instanceof ReadableStream) {\n return true;\n }\n return false;\n}\n\nfunction serializeRequestBody(rawBody: unknown, headers: Headers): BodyInit | undefined {\n if (rawBody === undefined) {\n return undefined;\n }\n if (isBodyInit(rawBody)) {\n // Pass-through for stream/form/binary bodies. Do not set Content-Type; the platform\n // (or the caller's explicit header) is responsible for it — e.g. FormData picks its\n // own multipart boundary.\n return rawBody;\n }\n // Fall through to JSON for plain objects/arrays/numbers/booleans/null.\n const serialized = JSON.stringify(rawBody);\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n return serialized;\n}\n\nfunction isPrivateHeader(name: string): boolean {\n return PRIVATE_HEADER_DENYLIST.has(name.toLowerCase());\n}\n\nfunction hasPrivateHeaders(headers: Headers): boolean {\n for (const [key] of headers.entries()) {\n if (isPrivateHeader(key)) {\n return true;\n }\n }\n return false;\n}\n\nfunction baseHookContext(\n method: RequestMethod,\n path: string,\n endpoint: string,\n attempt: number,\n maxAttempts: number,\n query: QueryParams | undefined\n): RequestHookContext {\n return {\n method,\n path,\n endpoint,\n attempt,\n maxAttempts,\n query\n };\n}\n\n/**\n * Executes JSON HTTP request with timeout, retry/backoff, deduplication and cache support.\n */\nexport async function requestJson<T>(\n config: Readonly<InternalClientConfig>,\n path: string,\n options: RequestOptions = {}\n): Promise<T> {\n const endpoint = buildUrl(config.baseUrl, path, options.query);\n const method = options.method ?? \"GET\";\n const requestCanRetry = method === \"GET\";\n const maxRetries = requestCanRetry ? config.retries : 0;\n const maxAttempts = maxRetries + 1;\n const headers = new Headers({\n Accept: \"application/json\"\n });\n\n if (config.userAgent) {\n headers.set(\"User-Agent\", config.userAgent);\n }\n if (options.headers) {\n for (const [key, value] of new Headers(options.headers)) {\n headers.set(key, value);\n }\n }\n\n const body = serializeRequestBody(options.body, headers);\n\n const cacheMode = options.cache ?? \"default\";\n // Caching is disabled only when a relevant signal is already aborted. A live\n // signal is fine — its job is to cancel the network request, not to signal\n // \"this caller has private data.\" In-flight dedup is still disabled for\n // per-call signals so callers do not inherit each other's cancellation semantics.\n const signalAborted = options.signal?.aborted === true || config.signal?.aborted === true;\n const hasPrivateRequestHeaders = hasPrivateHeaders(headers);\n const canUseCaching =\n config.cacheTtlMs !== undefined &&\n method === \"GET\" &&\n !signalAborted &&\n !hasPrivateRequestHeaders;\n const canReadFromCache = canUseCaching && cacheMode === \"default\";\n const canWriteToCache = canUseCaching && cacheMode !== \"no-store\";\n const perCallSignalProvided = options.signal !== undefined;\n const canUseDedup =\n config.dedupeInFlight &&\n method === \"GET\" &&\n !signalAborted &&\n !hasPrivateRequestHeaders &&\n cacheMode === \"default\" &&\n !perCallSignalProvided;\n let requestKey: string | undefined;\n const ensureRequestKey = (): string => {\n requestKey ??= buildRequestKey(method, endpoint, headers);\n return requestKey;\n };\n\n if (canReadFromCache) {\n const cached = tryReadCache(config, ensureRequestKey());\n if (cached !== undefined) {\n const cacheHitCtx: ResponseHookContext = {\n ...baseHookContext(method, path, endpoint, 1, maxAttempts, options.query),\n status: 200,\n durationMs: 0,\n fromCache: true\n };\n config.hooks.onResponse?.(cacheHitCtx);\n return cached as T;\n }\n }\n\n const performRequest = async (): Promise<T> => {\n for (let attempt = 0; attempt <= maxRetries; attempt += 1) {\n const attemptNumber = attempt + 1;\n const startedAt = Date.now();\n const hookCtx = baseHookContext(\n method,\n path,\n endpoint,\n attemptNumber,\n maxAttempts,\n options.query\n );\n\n config.hooks.onRequest?.(hookCtx);\n\n const requestSignal = mergeSignals([options.signal, config.signal], config.timeoutMs);\n const requestSignalCleanup = getMergedSignalCleanup(requestSignal);\n\n try {\n const requestInit: RequestInit = {\n method,\n headers,\n signal: requestSignal\n };\n if (body !== undefined) {\n requestInit.body = body;\n }\n\n const response = await config.fetchImpl(endpoint, requestInit);\n\n if (!response.ok) {\n const errorBody = await parseBody(response, config.maxResponseBytes);\n if (attempt < maxRetries && RETRIABLE_STATUS_CODES.has(response.status)) {\n const retryDecision = getRetryDecision(config, attempt, response.headers.get(\"retry-after\"));\n if (retryDecision.retryable) {\n const retryCtx: RetryHookContext = {\n ...hookCtx,\n delayMs: retryDecision.delayMs,\n reason: \"http_status\",\n status: response.status\n };\n config.hooks.onRetry?.(retryCtx);\n await sleep(retryDecision.delayMs);\n continue;\n }\n const skipRetryCtx: RetryHookContext = {\n ...hookCtx,\n delayMs: retryDecision.rejectedDelayMs,\n reason: \"retry_after_too_large\",\n status: response.status\n };\n config.hooks.onRetry?.(skipRetryCtx);\n }\n const apiError = new BsuirApiError(\n `BSUIR API returned HTTP ${String(response.status)} for ${method} ${path}`,\n response.status,\n endpoint,\n errorBody\n );\n const errorCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error: apiError\n };\n config.hooks.onError?.(errorCtx);\n throw apiError;\n }\n\n const parsed = (await parseBody(response, config.maxResponseBytes)) as T;\n const responseCtx: ResponseHookContext = {\n ...hookCtx,\n status: response.status,\n durationMs: Date.now() - startedAt,\n fromCache: false\n };\n config.hooks.onResponse?.(responseCtx);\n return parsed;\n } catch (error: unknown) {\n if (error instanceof BsuirApiError) {\n throw error;\n }\n\n if (error instanceof BsuirResponsePayloadTooLargeError) {\n const payloadTooLargeCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error\n };\n config.hooks.onError?.(payloadTooLargeCtx);\n throw error;\n }\n\n if (isAbortError(error)) {\n if (options.signal?.aborted || config.signal?.aborted) {\n const abortCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error\n };\n config.hooks.onError?.(abortCtx);\n throw error;\n }\n const timeoutError = new BsuirTimeoutError(\n `Request timed out after ${String(config.timeoutMs)}ms: ${path}`,\n endpoint,\n config.timeoutMs,\n error\n );\n const timeoutCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error: timeoutError\n };\n config.hooks.onError?.(timeoutCtx);\n throw timeoutError;\n }\n\n if (attempt < maxRetries) {\n const delayMs = getRetryDelayMs(config, attempt);\n const retryCtx: RetryHookContext = {\n ...hookCtx,\n delayMs,\n reason: \"network_error\",\n status: undefined\n };\n config.hooks.onRetry?.(retryCtx);\n await sleep(delayMs);\n continue;\n }\n\n const networkError = new BsuirNetworkError(\n `Network error while requesting ${path}`,\n endpoint,\n error\n );\n const networkErrorCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error: networkError\n };\n config.hooks.onError?.(networkErrorCtx);\n throw networkError;\n } finally {\n requestSignalCleanup?.();\n }\n }\n\n throw new BsuirNetworkError(`Unexpected retry loop termination for ${path}`, endpoint, null);\n };\n\n const requestAndMaybeCache = (): Promise<T> =>\n performRequest().then((payload) => {\n options.responseValidator?.(payload);\n if (canWriteToCache) {\n const cached = setCache(config, ensureRequestKey(), payload);\n if (cached !== undefined) {\n return cached;\n }\n }\n return payload;\n });\n\n if (canUseDedup) {\n const key = ensureRequestKey();\n const inFlight = config.inFlightRequests.get(key);\n if (inFlight) {\n return (await inFlight) as T;\n }\n\n const inFlightPromise: Promise<T> = requestAndMaybeCache()\n .finally(() => {\n config.inFlightRequests.delete(key);\n });\n config.inFlightRequests.set(key, inFlightPromise);\n return await inFlightPromise;\n }\n\n return await requestAndMaybeCache();\n}\n","import { BsuirResponseValidationError } from \"./errors\";\nimport type { ApiDateResponse } from \"../types/common\";\nimport type { ScheduleResponse } from \"../types/schedule\";\n\nfunction asRecord(payload: unknown): Record<string, unknown> | null {\n if (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n return null;\n }\n return payload as Record<string, unknown>;\n}\n\nfunction ensureRecord(\n payload: unknown,\n endpoint: string,\n expected: string\n): Record<string, unknown> {\n const record = asRecord(payload);\n if (!record) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: expected ${expected}`,\n endpoint\n );\n }\n return record;\n}\n\nfunction isNullableObject(value: unknown): boolean {\n return (\n value === null || value === undefined || (typeof value === \"object\" && !Array.isArray(value))\n );\n}\n\n/**\n * Asserts that payload is an array response.\n */\nexport function assertArrayResponse(\n payload: unknown,\n endpoint: string\n): asserts payload is unknown[] {\n if (!Array.isArray(payload)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: expected array, got ${typeof payload}`,\n endpoint\n );\n }\n}\n\n/**\n * Asserts that payload matches `{ lastUpdateDate: string }`.\n */\nexport function assertApiDateResponse(\n payload: unknown,\n endpoint: string\n): asserts payload is ApiDateResponse {\n const record = ensureRecord(payload, endpoint, \"object with lastUpdateDate\");\n if (typeof record.lastUpdateDate !== \"string\" || record.lastUpdateDate.trim().length === 0) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'lastUpdateDate' must be a non-empty string, got ${typeof record.lastUpdateDate}`,\n endpoint\n );\n }\n}\n\n/**\n * Asserts that payload is a schedule response envelope.\n */\nexport function assertScheduleResponse(\n payload: unknown,\n endpoint: string\n): asserts payload is ScheduleResponse {\n const record = ensureRecord(payload, endpoint, \"object\");\n const schedules = record.schedules;\n const exams = record.exams;\n const employeeDto = record.employeeDto;\n const studentGroupDto = record.studentGroupDto;\n\n // undefined treated as absent field — API may omit schedules/exams for exam-only or schedule-only entries\n if (\n schedules !== null &&\n schedules !== undefined &&\n (typeof schedules !== \"object\" || Array.isArray(schedules))\n ) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'schedules' must be object or null, got ${\n Array.isArray(schedules) ? \"array\" : typeof schedules\n }`,\n endpoint\n );\n }\n\n if (exams !== null && exams !== undefined && !Array.isArray(exams)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'exams' must be array or null, got ${typeof exams}`,\n endpoint\n );\n }\n\n if (!isNullableObject(employeeDto)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'employeeDto' must be object or null, got ${typeof employeeDto}`,\n endpoint\n );\n }\n\n if (!isNullableObject(studentGroupDto)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'studentGroupDto' must be object or null, got ${typeof studentGroupDto}`,\n endpoint\n );\n }\n}\n","import { BsuirApiError } from \"../client/errors\";\nimport { requestJson } from \"../client/http\";\nimport { assertArrayResponse } from \"../client/responseValidators\";\nimport type { InternalClientConfig } from \"../client/types\";\nimport type { Announcement } from \"../types/announcement\";\nimport { assertEmployeeUrlId, assertPositiveInt } from \"../utils/guards\";\nimport type { ReadOptions } from \"./types\";\n\n/**\n * Options accepted by `announcements.byEmployee` and `announcements.byDepartment`.\n *\n * The BSUIR IIS API responds with HTTP 404 when no announcements exist for an\n * employee or department. By default this SDK converts that 404 into an empty\n * array. Set `treat404AsEmpty: false` to receive a `BsuirApiError` instead —\n * useful when you want to distinguish \"entity not found\" from \"entity has no\n * announcements\" at the call site.\n */\nexport interface AnnouncementReadOptions extends ReadOptions {\n /**\n * When `true` (the default), HTTP 404 responses from the announcements endpoint\n * are converted to an empty array. When `false`, the underlying `BsuirApiError`\n * is rethrown.\n *\n * @defaultValue true\n */\n treat404AsEmpty?: boolean;\n}\n\nfunction endpointMatchesPath(endpoint: string, path: string): boolean {\n try {\n return new URL(endpoint).pathname.endsWith(path);\n } catch {\n return false;\n }\n}\n\n/**\n * Fetches an announcement list, optionally converting 404 to empty array.\n *\n * The previous implementation inspected response bodies for marker strings like\n * \"announcement\"/\"объяв\" to decide whether to swallow the 404. That heuristic\n * masked genuine errors with similar wording and was fragile across API versions.\n * We now rely on the endpoint-scoped 404 status alone, with an opt-out.\n *\n * @returns The announcement list, or empty array for 404 when `treat404AsEmpty` is `true`\n * @throws {BsuirApiError} For non-404 errors, or any 404 when `treat404AsEmpty` is `false`\n * @throws {BsuirNetworkError} On transport failures\n * @throws {BsuirTimeoutError} When request times out\n */\nasync function requestAnnouncementList(\n config: Readonly<InternalClientConfig>,\n path: string,\n options: AnnouncementReadOptions & { query: Record<string, string | number> }\n): Promise<Announcement[]> {\n const treat404AsEmpty = options.treat404AsEmpty ?? true;\n try {\n const payload = await requestJson<unknown>(config, path, options);\n if (config.validateResponses) {\n assertArrayResponse(payload, path);\n }\n return payload as Announcement[];\n } catch (error) {\n if (\n treat404AsEmpty &&\n error instanceof BsuirApiError &&\n error.status === 404 &&\n endpointMatchesPath(error.endpoint, path)\n ) {\n return [];\n }\n throw error;\n }\n}\n\n/**\n *\n */\nexport function createAnnouncementsModule(config: Readonly<InternalClientConfig>): {\n byEmployee(urlId: string, options?: AnnouncementReadOptions): Promise<Announcement[]>;\n byDepartment(id: number, options?: AnnouncementReadOptions): Promise<Announcement[]>;\n} {\n return {\n /**\n * Lists announcements for an employee.\n *\n * IIS responds with HTTP 404 when the employee has no announcements. By default\n * the SDK maps that to `[]`; pass `treat404AsEmpty: false` to receive the\n * underlying `BsuirApiError` instead.\n */\n async byEmployee(\n urlId: string,\n options: AnnouncementReadOptions = {}\n ): Promise<Announcement[]> {\n assertEmployeeUrlId(urlId, \"urlId\");\n return requestAnnouncementList(config, \"/announcements/employees\", {\n ...options,\n query: { \"url-id\": urlId }\n });\n },\n\n /**\n * Lists announcements for a department.\n *\n * IIS responds with HTTP 404 when the department has no announcements. By default\n * the SDK maps that to `[]`; pass `treat404AsEmpty: false` to receive the\n * underlying `BsuirApiError` instead.\n */\n async byDepartment(\n id: number,\n options: AnnouncementReadOptions = {}\n ): Promise<Announcement[]> {\n assertPositiveInt(id, \"id\");\n return requestAnnouncementList(config, \"/announcements/departments\", {\n ...options,\n query: { id }\n });\n }\n };\n}\n","import { requestJson } from \"../client/http\";\nimport { assertArrayResponse } from \"../client/responseValidators\";\nimport type { InternalClientConfig } from \"../client/types\";\nimport type { ReadOptions } from \"./types\";\n\nexport interface ListModule<T> {\n listAll(options?: ReadOptions): Promise<T[]>;\n}\n\n/**\n * Creates a simple catalog-like module exposing `listAll()` for a fixed endpoint.\n */\nexport function createListModule<T>(\n config: Readonly<InternalClientConfig>,\n endpoint: string\n): ListModule<T> {\n return {\n /**\n * Returns all items from the configured endpoint.\n */\n async listAll(options: ReadOptions = {}): Promise<T[]> {\n const payload = await requestJson<unknown>(config, endpoint, {\n signal: options.signal,\n cache: options.cache\n });\n assertArrayResponse(payload, endpoint);\n return payload as T[];\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { Auditory } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/auditories`.\n */\nexport function createAuditoriesModule(\n config: Readonly<InternalClientConfig>\n): ListModule<Auditory> {\n return createListModule<Auditory>(config, \"/auditories\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { Department } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/departments`.\n */\nexport function createDepartmentsModule(\n config: Readonly<InternalClientConfig>\n): ListModule<Department> {\n return createListModule<Department>(config, \"/departments\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { EmployeeCatalogItem } from \"../types/employee\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/employees/all`.\n */\nexport function createEmployeesModule(\n config: Readonly<InternalClientConfig>\n): ListModule<EmployeeCatalogItem> {\n return createListModule<EmployeeCatalogItem>(config, \"/employees/all\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { Faculty } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/faculties`.\n */\nexport function createFacultiesModule(config: Readonly<InternalClientConfig>): ListModule<Faculty> {\n return createListModule<Faculty>(config, \"/faculties\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { StudentGroupCatalogItem } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/student-groups`.\n */\nexport function createGroupsModule(\n config: Readonly<InternalClientConfig>\n): ListModule<StudentGroupCatalogItem> {\n return createListModule<StudentGroupCatalogItem>(config, \"/student-groups\");\n}\n","import { BsuirValidationError } from \"../client/errors\";\nimport { assertPositiveInt } from \"./guards\";\n\n// API response should never nest deeper than 2 levels; 10 is a generous safety cap to prevent stack overflow\nconst MAX_PARSE_DEPTH = 10;\n\n/**\n * Normalizes current week payload from API to a positive integer.\n * API can return plain text (`\"1\\n\"`) or number.\n */\nexport function parseCurrentWeek(payload: unknown): number {\n return parseCurrentWeekInternal(payload, 0);\n}\n\nfunction parseCurrentWeekInternal(payload: unknown, depth: number): number {\n if (depth > MAX_PARSE_DEPTH) {\n throw new BsuirValidationError(\n \"'currentWeek' response payload has excessive nesting depth\",\n \"currentWeek\",\n payload\n );\n }\n\n if (typeof payload === \"number\") {\n assertPositiveInt(payload, \"currentWeek\");\n return payload;\n }\n\n if (typeof payload === \"string\") {\n const normalized = payload.trim();\n if (normalized.length === 0) {\n throw new BsuirValidationError(\n \"'currentWeek' response payload is an empty string\",\n \"currentWeek\",\n payload\n );\n }\n const parsed = Number(normalized);\n // assertPositiveInt validates both isInteger and > 0 — no need to pre-check isInteger\n assertPositiveInt(parsed, \"currentWeek\");\n return parsed;\n }\n\n if (typeof payload === \"object\" && payload !== null) {\n const record = payload as Record<string, unknown>;\n if (\"weekNumber\" in record) {\n return parseCurrentWeekInternal(record.weekNumber, depth + 1);\n }\n if (\"currentWeek\" in record) {\n return parseCurrentWeekInternal(record.currentWeek, depth + 1);\n }\n }\n\n throw new BsuirValidationError(\n \"'currentWeek' response payload must be a positive integer\",\n \"currentWeek\",\n payload\n );\n}\n","import { assertPositiveInt } from \"../utils/guards\";\nimport type {\n FlattenedScheduleItem,\n NormalizedScheduleResponse,\n ScheduleFilterOptions,\n ScheduleItem\n} from \"../types/schedule\";\n\nfunction lessonAuditories(item: ScheduleItem): string[] {\n const { auditories } = item;\n return Array.isArray(auditories) ? auditories : [];\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 (\n typeof filter.weekNumber === \"number\" &&\n (!Array.isArray(item.weekNumber) || !item.weekNumber.includes(filter.weekNumber))\n ) {\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 (!item.lessonTypeAbbrev || !types.includes(item.lessonTypeAbbrev)) {\n return false;\n }\n }\n\n if (filter.subjectQuery) {\n const query = filter.subjectQuery.toLowerCase();\n const haystack = `${item.subject} ${item.subjectFullName} ${item.note ?? \"\"}`.toLowerCase();\n if (!haystack.includes(query)) {\n return false;\n }\n }\n\n if (filter.employeeUrlId) {\n const employeeMatch = item.employees?.some(\n (employee) => employee.urlId === filter.employeeUrlId\n );\n if (!employeeMatch) {\n return false;\n }\n }\n\n if (filter.auditory && !lessonAuditories(item).includes(filter.auditory)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Filters normalized schedule lessons by criteria.\n */\nexport function filterLessons(\n response: NormalizedScheduleResponse,\n filter: ScheduleFilterOptions\n): FlattenedScheduleItem[] {\n if (typeof filter.weekNumber === \"number\") {\n assertPositiveInt(filter.weekNumber, \"filter.weekNumber\");\n }\n return response.lessons.filter((item) => matchesFilter(item, filter));\n}\n","export const WEEKDAYS = [\n \"Понедельник\",\n \"Вторник\",\n \"Среда\",\n \"Четверг\",\n \"Пятница\",\n \"Суббота\"\n] as const;\n\nexport type Weekday = (typeof WEEKDAYS)[number];\n\n/** Payload from IIS legacy `last-update-date/*` endpoints (`schedule.getLastUpdateByGroup` / `getLastUpdateByEmployee`). */\nexport interface ApiDateResponse {\n lastUpdateDate: string;\n}\n\nexport interface StudentGroupShort {\n id: number;\n name: string;\n}\n\nexport type Maybe<T> = T | null;\n","import { BsuirResponseValidationError } from \"../client/errors\";\nimport { assertScheduleResponse } from \"../client/responseValidators\";\nimport { WEEKDAYS } from \"../types/common\";\nimport type {\n FlattenedLessonsByDay,\n FlattenedScheduleItem,\n LessonStudentGroup,\n NormalizedScheduleResponse,\n ScheduleItem,\n ScheduleResponse\n} from \"../types/schedule\";\n\nfunction lessonAuditories(item: ScheduleItem): string[] {\n const { auditories } = item;\n return Array.isArray(auditories) ? [...auditories] : [];\n}\n\nfunction cloneLessonStudentGroups(groups: ScheduleItem[\"studentGroups\"]): LessonStudentGroup[] {\n return groups.map((group) => ({ ...group }));\n}\n\nfunction cloneEmployees(employees: ScheduleItem[\"employees\"]): ScheduleItem[\"employees\"] {\n if (!Array.isArray(employees)) {\n return employees;\n }\n return employees.map((employee) => ({ ...employee }));\n}\n\nfunction cloneScheduleItem(item: ScheduleItem): ScheduleItem {\n return {\n ...item,\n weekNumber: Array.isArray(item.weekNumber) ? [...item.weekNumber] : item.weekNumber,\n studentGroups: cloneLessonStudentGroups(item.studentGroups),\n auditories: lessonAuditories(item),\n employees: cloneEmployees(item.employees)\n };\n}\n\nfunction deepFreezeJson<T>(value: T): T {\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n if (Object.isFrozen(value)) {\n return value;\n }\n Object.freeze(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n deepFreezeJson(item);\n }\n } else {\n for (const key of Object.keys(value)) {\n deepFreezeJson((value as Record<string, unknown>)[key]);\n }\n }\n return value;\n}\n\n// Minimal envelope check kept here (not in responseValidators) because the normalize\n// path always needs at least this much shape safety to avoid crashing on a non-object\n// payload — even when full validation is disabled. The full validator\n// `assertScheduleResponse` is the single source of truth for the complete check.\nfunction assertMinimalScheduleEnvelope(\n payload: unknown,\n endpoint: string\n): asserts payload is { schedules?: unknown; exams?: unknown } {\n if (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: expected object`,\n endpoint\n );\n }\n}\n\n/**\n * Transforms raw schedule response into normalized structure with flattened lessons.\n */\nexport function normalizeSchedule(\n response: ScheduleResponse,\n options?: { validate?: boolean; endpoint?: string }\n): NormalizedScheduleResponse {\n const endpoint = options?.endpoint ?? \"/schedule\";\n if (options?.validate) {\n // Full envelope validation via the single source of truth.\n assertScheduleResponse(response, endpoint);\n } else {\n // Even without full validation, refuse to normalize a non-object payload —\n // letting it through would only push a less-clear TypeError onto the caller.\n assertMinimalScheduleEnvelope(response, endpoint);\n }\n const scheduleLessons: FlattenedScheduleItem[] = [];\n const examLessons: FlattenedScheduleItem[] = [];\n const lessonsByDay = Object.fromEntries(\n WEEKDAYS.map((day) => [day, [] as FlattenedScheduleItem[]])\n ) as FlattenedLessonsByDay;\n const sourceSchedules = response.schedules ?? {};\n const normalizedSchedules: NonNullable<ScheduleResponse[\"schedules\"]> = {};\n const normalizedExams = Array.isArray(response.exams)\n ? response.exams.map((item) => cloneScheduleItem(item))\n : [];\n\n for (const day of WEEKDAYS) {\n const dayItems = sourceSchedules[day] ?? [];\n const clonedDayItems = dayItems.map((item) => cloneScheduleItem(item));\n if (Object.hasOwn(sourceSchedules, day)) {\n normalizedSchedules[day] = clonedDayItems;\n }\n\n const flattenedDayItems: FlattenedScheduleItem[] = clonedDayItems.map((item) =>\n deepFreezeJson({\n ...item,\n day,\n source: \"schedules\" as const\n })\n );\n lessonsByDay[day] = flattenedDayItems;\n scheduleLessons.push(...flattenedDayItems);\n }\n\n for (const exam of normalizedExams) {\n const flattenedExam: FlattenedScheduleItem = deepFreezeJson({\n ...exam,\n day: null,\n source: \"exams\"\n });\n examLessons.push(flattenedExam);\n }\n\n const lessons = [...scheduleLessons, ...examLessons];\n\n return {\n ...response,\n schedules: normalizedSchedules,\n exams: normalizedExams,\n lessons,\n lessonsByDay,\n scheduleLessons,\n examLessons\n };\n}\n","import { requestJson } from \"../client/http\";\nimport { assertApiDateResponse, assertScheduleResponse } from \"../client/responseValidators\";\nimport type { InternalClientConfig } from \"../client/types\";\nimport type { ApiDateResponse } from \"../types/common\";\nimport type {\n FlattenedScheduleItem,\n NormalizedScheduleResponse,\n ScheduleFilterOptions,\n ScheduleItem,\n ScheduleResponse\n} from \"../types/schedule\";\nimport { assertEmployeeUrlId, assertGroupNumber, assertPositiveInt } from \"../utils/guards\";\nimport { parseCurrentWeek } from \"../utils/week\";\nimport { filterLessons } from \"./scheduleFilter\";\nimport { normalizeSchedule } from \"./scheduleNormalize\";\nimport type { ReadOptions } from \"./types\";\n\nexport interface ScheduleModule {\n getGroup(groupNumber: string, options?: ReadOptions): Promise<NormalizedScheduleResponse>;\n getEmployee(urlId: string, options?: ReadOptions): Promise<NormalizedScheduleResponse>;\n\n getGroupFiltered(\n groupNumber: string,\n filter: ScheduleFilterOptions,\n options?: ReadOptions\n ): Promise<FlattenedScheduleItem[]>;\n\n getEmployeeFiltered(\n urlId: string,\n filter: ScheduleFilterOptions,\n options?: ReadOptions\n ): Promise<FlattenedScheduleItem[]>;\n\n getGroupExams(groupNumber: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;\n getEmployeeExams(urlId: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;\n\n // Explicit raw/envelope helpers (new explicit API)\n getGroupRaw(groupNumber: string, options?: ReadOptions): Promise<ScheduleResponse>;\n getGroupEnvelope(groupNumber: string, subgroup: number, options?: ReadOptions): Promise<ScheduleResponse>;\n\n getEmployeeRaw(urlId: string, options?: ReadOptions): Promise<ScheduleResponse>;\n getEmployeeEnvelope(urlId: string, subgroup: number, options?: ReadOptions): Promise<ScheduleResponse>;\n\n getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { rawEnvelope: true }\n ): Promise<ScheduleResponse>;\n getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { raw: true; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[]>;\n getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { raw: boolean; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;\n getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options?: ReadOptions & { raw?: false | undefined; rawEnvelope?: false | undefined }\n ): Promise<FlattenedScheduleItem[]>;\n\n getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { rawEnvelope: true }\n ): Promise<ScheduleResponse>;\n getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { raw: true; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[]>;\n getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { raw: boolean; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;\n getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options?: ReadOptions & { raw?: false | undefined; rawEnvelope?: false | undefined }\n ): Promise<FlattenedScheduleItem[]>;\n\n getCurrentWeek(options?: ReadOptions): Promise<number>;\n\n getLastUpdateByGroup(\n params: { groupNumber: string } | { id: number },\n options?: ReadOptions\n ): Promise<ApiDateResponse>;\n\n getLastUpdateByEmployee(\n params: { urlId: string } | { id: number },\n options?: ReadOptions\n ): Promise<ApiDateResponse>;\n}\n\nfunction filterRawSubgroupLessons(\n response: ScheduleResponse,\n subgroup: number\n): ScheduleItem[] {\n const items: ScheduleItem[] = [];\n const schedules = response.schedules ?? {};\n for (const dayItems of Object.values(schedules)) {\n for (const lesson of dayItems) {\n if (lesson.numSubgroup === subgroup) {\n items.push(structuredClone(lesson));\n }\n }\n }\n return items;\n}\n\nfunction filterRawSubgroupEnvelope(\n response: ScheduleResponse,\n subgroup: number\n): ScheduleResponse {\n const cloned = structuredClone(response);\n const schedules = cloned.schedules ?? {};\n for (const day of Object.keys(schedules) as (keyof typeof schedules)[]) {\n const items = schedules[day] ?? [];\n schedules[day] = items.filter((lesson) => lesson.numSubgroup === subgroup);\n }\n return cloned;\n}\n\n/**\n * Creates schedule API module with raw/normalized response support.\n */\nexport function createScheduleModule(\n config: Readonly<InternalClientConfig>\n): ScheduleModule {\n /**\n * Returns schedule for a student group.\n * Returns a normalized payload. Use `getGroupRaw` for the raw API envelope.\n */\n async function getGroup(groupNumber: string, options: ReadOptions = {}): Promise<NormalizedScheduleResponse> {\n const resolvedOptions = options;\n assertGroupNumber(groupNumber, \"groupNumber\");\n const payload = await requestJson<unknown>(config, \"/schedule\", {\n query: { studentGroup: groupNumber },\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertScheduleResponse(value, \"/schedule\");\n }\n : undefined\n });\n const response = payload as ScheduleResponse;\n return normalizeSchedule(response, {\n validate: false,\n endpoint: \"/schedule\"\n });\n }\n\n /**\n * Returns schedule for an employee.\n * Returns a normalized payload. Use `getEmployeeRaw` for the raw API envelope.\n */\n async function getEmployee(urlId: string, options: ReadOptions = {}): Promise<NormalizedScheduleResponse> {\n const resolvedOptions = options;\n assertEmployeeUrlId(urlId, \"urlId\");\n const endpoint = `/employees/schedule/${encodeURIComponent(urlId)}`;\n const payload = await requestJson<unknown>(config, endpoint, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertScheduleResponse(value, endpoint);\n }\n : undefined\n });\n const response = payload as ScheduleResponse;\n return normalizeSchedule(response, {\n validate: false,\n endpoint\n });\n }\n\n /**\n * Returns filtered schedule items for a group from normalized schedule payload.\n */\n async function getGroupFiltered(\n groupNumber: string,\n filter: ScheduleFilterOptions,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n const normalized = await getGroup(groupNumber, {\n signal: options.signal,\n cache: options.cache\n });\n return filterLessons(normalized, filter);\n }\n\n /**\n * Returns filtered schedule items for an employee from normalized schedule payload.\n */\n async function getEmployeeFiltered(\n urlId: string,\n filter: ScheduleFilterOptions,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n const normalized = await getEmployee(urlId, {\n signal: options.signal,\n cache: options.cache\n });\n return filterLessons(normalized, filter);\n }\n\n /**\n * Returns current academic week number.\n */\n async function getCurrentWeek(options: ReadOptions = {}): Promise<number> {\n const payload = await requestJson<unknown>(config, \"/schedule/current-week\", {\n signal: options.signal,\n cache: options.cache\n });\n return parseCurrentWeek(payload);\n }\n\n /**\n * Returns regular schedule lessons for a subgroup.\n *\n * Shape selection:\n * - `rawEnvelope: true` → returns the full `ScheduleResponse` with `schedules` arrays\n * filtered to the requested subgroup. Preserves `employeeDto`, `studentGroupDto`,\n * exam fields, and date ranges from the original envelope.\n * - `raw: true` (without `rawEnvelope`) → returns `ScheduleItem[]` only.\n * - default → returns flattened `FlattenedScheduleItem[]` with day/source metadata.\n */\n function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { rawEnvelope: true }\n ): Promise<ScheduleResponse>;\n function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { raw: true; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[]>;\n function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { raw: boolean; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;\n function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options?: ReadOptions & { raw?: false | undefined; rawEnvelope?: false | undefined }\n ): Promise<FlattenedScheduleItem[]>;\n async function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options?: ReadOptions & { raw?: boolean | undefined; rawEnvelope?: boolean | undefined }\n ): Promise<FlattenedScheduleItem[] | ScheduleItem[] | ScheduleResponse> {\n const resolvedOptions = options ?? {};\n assertPositiveInt(subgroup, \"subgroup\");\n if (resolvedOptions.rawEnvelope === true) {\n const raw = await getGroupRaw(groupNumber, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache\n });\n return filterRawSubgroupEnvelope(raw, subgroup);\n }\n if (resolvedOptions.raw === true) {\n const raw = await getGroupRaw(groupNumber, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache\n });\n return filterRawSubgroupLessons(raw, subgroup);\n }\n return getGroupFiltered(groupNumber, { source: \"schedules\", subgroup }, resolvedOptions);\n }\n\n /**\n * Returns regular schedule lessons for an employee subgroup filter.\n *\n * Shape selection:\n * - `rawEnvelope: true` → returns the full `ScheduleResponse` with `schedules` arrays\n * filtered to the requested subgroup. Preserves `employeeDto`, `studentGroupDto`,\n * exam fields, and date ranges from the original envelope.\n * - `raw: true` (without `rawEnvelope`) → returns `ScheduleItem[]` only.\n * - default → returns flattened `FlattenedScheduleItem[]` with day/source metadata.\n */\n function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { rawEnvelope: true }\n ): Promise<ScheduleResponse>;\n function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { raw: true; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[]>;\n function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { raw: boolean; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;\n function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options?: ReadOptions & { raw?: false | undefined; rawEnvelope?: false | undefined }\n ): Promise<FlattenedScheduleItem[]>;\n async function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options?: ReadOptions & { raw?: boolean | undefined; rawEnvelope?: boolean | undefined }\n ): Promise<FlattenedScheduleItem[] | ScheduleItem[] | ScheduleResponse> {\n const resolvedOptions = options ?? {};\n assertPositiveInt(subgroup, \"subgroup\");\n if (resolvedOptions.rawEnvelope === true) {\n const raw = await getEmployeeRaw(urlId, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache\n });\n return filterRawSubgroupEnvelope(raw, subgroup);\n }\n if (resolvedOptions.raw === true) {\n const raw = await getEmployeeRaw(urlId, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache\n });\n return filterRawSubgroupLessons(raw, subgroup);\n }\n return getEmployeeFiltered(urlId, { source: \"schedules\", subgroup }, resolvedOptions);\n }\n\n // Explicit helpers: raw/envelope variants to make API shape explicit.\n async function getGroupRaw(\n groupNumber: string,\n options: ReadOptions = {}\n ): Promise<ScheduleResponse> {\n assertGroupNumber(groupNumber, \"groupNumber\");\n const payload = await requestJson<unknown>(config, \"/schedule\", {\n query: { studentGroup: groupNumber },\n signal: options.signal,\n cache: options.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertScheduleResponse(value, \"/schedule\");\n }\n : undefined\n });\n return payload as ScheduleResponse;\n }\n\n async function getGroupEnvelope(groupNumber: string, subgroup: number, options: ReadOptions = {}) {\n const raw = await getGroupRaw(groupNumber, options);\n return filterRawSubgroupEnvelope(raw, subgroup);\n }\n\n async function getEmployeeRaw(urlId: string, options: ReadOptions = {}) {\n assertEmployeeUrlId(urlId, \"urlId\");\n const endpoint = `/employees/schedule/${encodeURIComponent(urlId)}`;\n const payload = await requestJson<unknown>(config, endpoint, {\n signal: options.signal,\n cache: options.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertScheduleResponse(value, endpoint);\n }\n : undefined\n });\n return payload as ScheduleResponse;\n }\n\n async function getEmployeeEnvelope(urlId: string, subgroup: number, options: ReadOptions = {}) {\n const raw = await getEmployeeRaw(urlId, options);\n return filterRawSubgroupEnvelope(raw, subgroup);\n }\n\n return {\n getGroup,\n getEmployee,\n getGroupRaw,\n getGroupEnvelope,\n getEmployeeRaw,\n getEmployeeEnvelope,\n getGroupFiltered,\n getEmployeeFiltered,\n getGroupBySubgroup,\n getEmployeeBySubgroup,\n getCurrentWeek,\n\n /**\n * Returns exams for a group.\n */\n async getGroupExams(\n groupNumber: string,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n return getGroupFiltered(groupNumber, { source: \"exams\" }, options);\n },\n\n /**\n * Returns exams for an employee.\n */\n async getEmployeeExams(\n urlId: string,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n return getEmployeeFiltered(urlId, { source: \"exams\" }, options);\n },\n\n /**\n * Calls IIS `/last-update-date/student-group`.\n */\n async getLastUpdateByGroup(\n params: { groupNumber: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"groupNumber\" in params) {\n assertGroupNumber(params.groupNumber, \"groupNumber\");\n query = { groupNumber: params.groupNumber };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n const payload = await requestJson<unknown>(config, \"/last-update-date/student-group\", {\n query,\n signal: options.signal,\n cache: options.cache\n });\n if (config.validateResponses) {\n assertApiDateResponse(payload, \"/last-update-date/student-group\");\n }\n return payload as ApiDateResponse;\n },\n\n /**\n * Calls IIS `/last-update-date/employee`.\n */\n async getLastUpdateByEmployee(\n params: { urlId: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"urlId\" in params) {\n assertEmployeeUrlId(params.urlId, \"urlId\");\n query = { \"url-id\": params.urlId };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n const payload = await requestJson<unknown>(config, \"/last-update-date/employee\", {\n query,\n signal: options.signal,\n cache: options.cache\n });\n if (config.validateResponses) {\n assertApiDateResponse(payload, \"/last-update-date/employee\");\n }\n return payload as ApiDateResponse;\n }\n };\n}\n","import { filterLessons } from \"../modules/scheduleFilter\";\nimport { WEEKDAYS, type Weekday } from \"../types/common\";\nimport type {\n BuildScheduleDaysOptions,\n FlattenedLessonsByDay,\n FlattenedScheduleItem,\n LessonWithTime,\n NormalizedScheduleResponse,\n ScheduleDay\n} from \"../types/schedule\";\nimport { assertPositiveInt } from \"../utils/guards\";\n\nconst SUNDAY_LABEL = \"Воскресенье\";\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\ninterface DdMmYyyyParts {\n day: number;\n month: number;\n year: number;\n}\n\nfunction createEmptyLessonsByDay(): FlattenedLessonsByDay {\n return Object.fromEntries(\n WEEKDAYS.map((day) => [day, [] as FlattenedScheduleItem[]])\n ) as FlattenedLessonsByDay;\n}\n\nfunction parseDdMmYyyyParts(value: string | null): DdMmYyyyParts | null {\n if (!value) {\n return null;\n }\n const matched = /^(\\d{2})\\.(\\d{2})\\.(\\d{4})$/.exec(value);\n if (!matched) {\n return null;\n }\n const [, dayPart, monthPart, yearPart] = matched;\n const day = Number(dayPart);\n const month = Number(monthPart);\n const year = Number(yearPart);\n if (!Number.isInteger(day) || !Number.isInteger(month) || !Number.isInteger(year)) {\n return null;\n }\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() !== month - 1 ||\n utcDate.getUTCDate() !== day\n ) {\n return null;\n }\n return { day, month, year };\n}\n\nfunction toDayOrdinal(parts: DdMmYyyyParts): number {\n return Math.floor(Date.UTC(parts.year, parts.month - 1, parts.day) / MS_PER_DAY);\n}\n\nfunction toDateDayOrdinal(date: Date): number {\n return Math.floor(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / MS_PER_DAY);\n}\n\nfunction toDateKey(date: Date): string {\n const year = String(date.getFullYear());\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n\nfunction toLessonDateKey(value: string | null): string | null {\n const parts = parseDdMmYyyyParts(value);\n if (!parts) {\n return null;\n }\n return `${String(parts.year)}-${String(parts.month).padStart(2, \"0\")}-${String(parts.day).padStart(2, \"0\")}`;\n}\n\nfunction toLessonDayOrdinal(value: string | null): number | null {\n const parts = parseDdMmYyyyParts(value);\n return parts ? toDayOrdinal(parts) : null;\n}\n\nfunction toWeekday(date: Date): Weekday | null {\n const dayIndex = date.getDay();\n if (dayIndex < 1 || dayIndex > 6) {\n return null;\n }\n return WEEKDAYS[dayIndex - 1] ?? null;\n}\n\n/**\n * Validates and clones a Date. Only `Date` objects are accepted.\n * ISO strings like `\"2026-05-15\"` are NOT accepted to avoid UTC vs local timezone ambiguity —\n * pass an explicit `new Date(...)` instead.\n */\nfunction toDateOrThrow(value: Date, fieldName: string): Date {\n if (!(value instanceof Date)) {\n throw new TypeError(`'${fieldName}' must be a Date object`);\n }\n const cloned = new Date(value);\n if (Number.isNaN(cloned.getTime())) {\n throw new TypeError(`'${fieldName}' must be a valid Date`);\n }\n return cloned;\n}\n\n/**\n * Optional callback invoked when a lesson's `startLessonTime` or `endLessonTime`\n * cannot be parsed as `HH:MM`. Use this to surface upstream data issues that the\n * default behavior (sorting such lessons to the end, ignoring them for\n * current/next lookups) would otherwise hide.\n *\n * The callback is fired at most once per malformed value per call site. It must\n * not throw — exceptions from a hook are caught and discarded.\n *\n * @example\n * ```ts\n * const sorted = sortLessonsByTime(lessons, {\n * onInvalidTime: (info) => logger.warn(\"malformed lesson time\", info),\n * });\n * ```\n */\nexport type InvalidLessonTimeHook = (info: {\n field: \"startLessonTime\" | \"endLessonTime\";\n value: string;\n lesson: LessonWithTime;\n}) => void;\n\nfunction parseTimeToMinutes(value: string): number | null {\n const matched = /^(\\d{1,2}):(\\d{2})$/.exec(value);\n if (!matched) {\n return null;\n }\n const [, hourPart, minutePart] = matched;\n const hours = Number(hourPart);\n const minutes = Number(minutePart);\n if (\n !Number.isInteger(hours) ||\n !Number.isInteger(minutes) ||\n hours < 0 ||\n hours > 23 ||\n minutes < 0 ||\n minutes > 59\n ) {\n return null;\n }\n return hours * 60 + minutes;\n}\n\nfunction reportInvalidTime(\n lesson: LessonWithTime,\n field: \"startLessonTime\" | \"endLessonTime\",\n value: string,\n hook: InvalidLessonTimeHook | undefined\n): void {\n if (!hook) {\n return;\n }\n try {\n hook({ field, value, lesson });\n } catch {\n // Hook failures must not break schedule sorting/lookup.\n }\n}\n\nfunction parseLessonTime(\n lesson: LessonWithTime,\n field: \"startLessonTime\" | \"endLessonTime\",\n hook: InvalidLessonTimeHook | undefined\n): number | null {\n const value = lesson[field];\n const parsed = parseTimeToMinutes(value);\n if (parsed === null && value.length > 0) {\n reportInvalidTime(lesson, field, value, hook);\n }\n return parsed;\n}\n\nfunction isWithinLessonDateRange(\n targetOrdinal: number,\n startDate: string | null,\n endDate: string | null\n): boolean {\n const startOrdinal = toLessonDayOrdinal(startDate);\n const endOrdinal = toLessonDayOrdinal(endDate);\n if (startOrdinal !== null && targetOrdinal < startOrdinal) {\n return false;\n }\n if (endOrdinal !== null && targetOrdinal > endOrdinal) {\n return false;\n }\n return true;\n}\n\nfunction usesFourWeekCycle(response: NormalizedScheduleResponse): boolean {\n const values = response.scheduleLessons\n .flatMap((lesson) => lesson.weekNumber ?? [])\n .filter((value): value is number => Number.isInteger(value) && value > 0);\n if (values.length === 0) {\n return false;\n }\n return values.every((value) => value >= 1 && value <= 4);\n}\n\nfunction inferWeekNumberForDate(response: NormalizedScheduleResponse, date: Date): number | null {\n const startDateParts = parseDdMmYyyyParts(response.startDate);\n if (!startDateParts) {\n return null;\n }\n const startOrdinal = toDayOrdinal(startDateParts);\n const targetOrdinal = toDateDayOrdinal(date);\n const diffDays = targetOrdinal - startOrdinal;\n if (diffDays < 0) {\n return null;\n }\n const absoluteWeek = Math.floor(diffDays / 7) + 1;\n if (!usesFourWeekCycle(response)) {\n return absoluteWeek;\n }\n return ((absoluteWeek - 1) % 4) + 1;\n}\n\n/**\n * Returns lessons scheduled for a specific calendar date.\n *\n * Date matching uses local calendar date semantics from the provided `date` object.\n * Lessons with `dateLesson` are matched directly by date key.\n * Weekly schedule lessons are matched by weekday and inferred week number.\n * Exams without `dateLesson` but with a date range appear on every day within that range —\n * in practice BSUIR exams have `dateLesson` set, so this branch handles edge cases only.\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param date - Target date. Must be a `Date` object — local calendar date is used.\n * @returns Lessons for that date sorted by start time.\n *\n * @example\n * ```ts\n * const lessons = getLessonsForDate(schedule, new Date(2026, 1, 10));\n * ```\n */\nexport function getLessonsForDate(\n normalizedSchedule: NormalizedScheduleResponse,\n date: Date\n): FlattenedScheduleItem[] {\n const targetDate = toDateOrThrow(date, \"date\");\n const targetDateKey = toDateKey(targetDate);\n const targetOrdinal = toDateDayOrdinal(targetDate);\n const targetWeekday = toWeekday(targetDate);\n const inferredWeekNumber = inferWeekNumberForDate(normalizedSchedule, targetDate);\n\n return sortLessonsByTime(\n normalizedSchedule.lessons.filter((lesson) => {\n const lessonDateKey = toLessonDateKey(lesson.dateLesson);\n if (lessonDateKey) {\n return lessonDateKey === targetDateKey;\n }\n\n if (lesson.source === \"exams\") {\n if (!lesson.startLessonDate && !lesson.endLessonDate) {\n return false;\n }\n return isWithinLessonDateRange(targetOrdinal, lesson.startLessonDate, lesson.endLessonDate);\n }\n\n if (lesson.day !== targetWeekday) {\n return false;\n }\n\n if (\n typeof inferredWeekNumber === \"number\" &&\n Array.isArray(lesson.weekNumber) &&\n lesson.weekNumber.length > 0 &&\n !lesson.weekNumber.includes(inferredWeekNumber)\n ) {\n return false;\n }\n\n return isWithinLessonDateRange(targetOrdinal, lesson.startLessonDate, lesson.endLessonDate);\n })\n );\n}\n\n/**\n * Returns lessons for the local current day.\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param now - Optional current moment override for deterministic usage.\n * @returns Lessons for today sorted by start time.\n *\n * @example\n * ```ts\n * const todayLessons = getTodayLessons(schedule, new Date());\n * ```\n */\nexport function getTodayLessons(\n normalizedSchedule: NormalizedScheduleResponse,\n now: Date = new Date()\n): FlattenedScheduleItem[] {\n const current = toDateOrThrow(now, \"now\");\n return getLessonsForDate(normalizedSchedule, current);\n}\n\n/**\n * Returns lessons for the next local calendar day.\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param now - Optional current moment override for deterministic usage.\n * @returns Lessons for tomorrow sorted by start time.\n *\n * @example\n * ```ts\n * const tomorrowLessons = getTomorrowLessons(schedule, new Date());\n * ```\n */\nexport function getTomorrowLessons(\n normalizedSchedule: NormalizedScheduleResponse,\n now: Date = new Date()\n): FlattenedScheduleItem[] {\n const current = toDateOrThrow(now, \"now\");\n const tomorrow = new Date(current);\n tomorrow.setDate(current.getDate() + 1);\n return getLessonsForDate(normalizedSchedule, tomorrow);\n}\n\n/**\n * Returns regular schedule lessons for a specific week number.\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param weekNumber - Positive week number to match.\n * @returns Matching regular lessons sorted by start time.\n *\n * @example\n * ```ts\n * const secondWeek = getLessonsForWeek(schedule, 2);\n * ```\n */\nexport function getLessonsForWeek(\n normalizedSchedule: NormalizedScheduleResponse,\n weekNumber: number\n): FlattenedScheduleItem[] {\n assertPositiveInt(weekNumber, \"weekNumber\");\n return sortLessonsByTime(\n filterLessons(normalizedSchedule, {\n source: \"schedules\",\n weekNumber\n })\n );\n}\n\n/**\n * Returns a new lessons array sorted by start time, then by end time.\n * Invalid or missing time strings are kept at the end in original order.\n *\n * @param lessons - Lessons to sort.\n * @returns New sorted lessons array.\n *\n * @example\n * ```ts\n * const sorted = sortLessonsByTime(response.lessons);\n * ```\n */\nexport function sortLessonsByTime<T extends LessonWithTime>(\n lessons: readonly T[],\n options?: { onInvalidTime?: InvalidLessonTimeHook | undefined }\n): T[] {\n const hook = options?.onInvalidTime;\n return lessons\n .map((lesson, index) => ({\n lesson,\n index,\n start: parseLessonTime(lesson, \"startLessonTime\", hook),\n end: parseLessonTime(lesson, \"endLessonTime\", hook)\n }))\n .toSorted((a, b) => {\n const startDiff =\n (a.start ?? Number.POSITIVE_INFINITY) - (b.start ?? Number.POSITIVE_INFINITY);\n if (startDiff !== 0) {\n return startDiff;\n }\n const endDiff = (a.end ?? Number.POSITIVE_INFINITY) - (b.end ?? Number.POSITIVE_INFINITY);\n if (endDiff !== 0) {\n return endDiff;\n }\n return a.index - b.index;\n })\n .map((entry) => entry.lesson);\n}\n\n/**\n * Groups lessons by weekday.\n *\n * Lessons with `day === null` (e.g., date-specific exams) are omitted from groups.\n *\n * @param lessons - Lessons to group.\n * @returns Weekday map with arrays sorted by time for each day.\n *\n * @example\n * ```ts\n * const grouped = groupLessonsByDay(response.lessons);\n * ```\n */\nexport function groupLessonsByDay(\n lessons: readonly FlattenedScheduleItem[]\n): FlattenedLessonsByDay {\n const grouped = createEmptyLessonsByDay();\n for (const lesson of lessons) {\n if (!lesson.day) {\n continue;\n }\n grouped[lesson.day].push(lesson);\n }\n for (const weekday of WEEKDAYS) {\n grouped[weekday] = sortLessonsByTime(grouped[weekday]);\n }\n return grouped;\n}\n\n/**\n * Returns the lesson active at the specified moment.\n *\n * Time comparison uses local hours/minutes: `startLessonTime <= now < endLessonTime`.\n * A lesson is not considered active exactly at its end time.\n *\n * @param lessons - Lessons for one day (or any same-day set).\n * @param now - Optional current moment override.\n * @returns Current lesson or `null` when none is active.\n *\n * @example\n * ```ts\n * const current = getCurrentLesson(todayLessons, new Date());\n * ```\n */\nexport function getCurrentLesson<T extends LessonWithTime>(\n lessons: readonly T[],\n now: Date = new Date(),\n options?: { onInvalidTime?: InvalidLessonTimeHook | undefined }\n): T | null {\n const current = toDateOrThrow(now, \"now\");\n const nowMinutes = current.getHours() * 60 + current.getMinutes();\n const hook = options?.onInvalidTime;\n for (const lesson of sortLessonsByTime(lessons, { onInvalidTime: hook })) {\n const start = parseLessonTime(lesson, \"startLessonTime\", hook);\n const end = parseLessonTime(lesson, \"endLessonTime\", hook);\n if (start === null || end === null || end <= start) {\n continue;\n }\n if (nowMinutes >= start && nowMinutes < end) {\n return lesson;\n }\n }\n return null;\n}\n\n/**\n * Returns the nearest upcoming lesson after the specified moment.\n *\n * A lesson starting exactly at `now` is not returned (use {@link getCurrentLesson} instead).\n * A lesson that just ended at `now` is also not returned as current — the next one is.\n *\n * @param lessons - Lessons for one day (or any same-day set).\n * @param now - Optional current moment override.\n * @returns Next lesson or `null` when there is no upcoming lesson.\n *\n * @example\n * ```ts\n * const next = getNextLesson(todayLessons, new Date());\n * ```\n */\nexport function getNextLesson<T extends LessonWithTime>(\n lessons: readonly T[],\n now: Date = new Date(),\n options?: { onInvalidTime?: InvalidLessonTimeHook | undefined }\n): T | null {\n const current = toDateOrThrow(now, \"now\");\n const nowMinutes = current.getHours() * 60 + current.getMinutes();\n const hook = options?.onInvalidTime;\n for (const lesson of sortLessonsByTime(lessons, { onInvalidTime: hook })) {\n const start = parseLessonTime(lesson, \"startLessonTime\", hook);\n if (start === null) {\n continue;\n }\n if (start > nowMinutes) {\n return lesson;\n }\n }\n return null;\n}\n\n/**\n * Builds lightweight day models for schedule screens.\n *\n * Uses local calendar dates. Returns day objects with lessons,\n * \"today\" marker, and optional current/next lesson metadata for the current day.\n * `currentLesson` and `nextLesson` are only computed for today (`isToday === true`).\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param options - Builder options for date range and filtering.\n * @returns Day models ready for direct UI rendering.\n *\n * @example\n * ```ts\n * const days = buildScheduleDays(schedule, { days: 7, includeEmptyDays: false });\n * // Use days?.lessons for in-day progress:\n * const current = getCurrentLesson(days?.lessons ?? []);\n * ```\n */\nexport function buildScheduleDays(\n normalizedSchedule: NormalizedScheduleResponse,\n options: BuildScheduleDaysOptions = {}\n): ScheduleDay[] {\n const now = toDateOrThrow(options.now ?? new Date(), \"options.now\");\n const startDate = toDateOrThrow(options.startDate ?? now, \"options.startDate\");\n const days = options.days ?? 7;\n assertPositiveInt(days, \"options.days\");\n\n const includeEmptyDays = options.includeEmptyDays ?? true;\n const includeCurrentAndNextLessons = options.includeCurrentAndNextLessons ?? true;\n const onInvalidTime = options.onInvalidTime as InvalidLessonTimeHook | undefined;\n const todayKey = toDateKey(now);\n const rangeStart = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());\n\n const scheduleDays: ScheduleDay[] = [];\n for (let index = 0; index < days; index += 1) {\n const dayDate = new Date(rangeStart);\n dayDate.setDate(rangeStart.getDate() + index);\n\n const lessons = getLessonsForDate(normalizedSchedule, dayDate);\n const dateKey = toDateKey(dayDate);\n const isToday = dateKey === todayKey;\n const hasLessons = lessons.length > 0;\n\n if (!includeEmptyDays && !hasLessons) {\n continue;\n }\n\n const weekday = toWeekday(dayDate);\n scheduleDays.push({\n date: dayDate,\n dateKey,\n weekday,\n weekdayLabel: weekday ?? SUNDAY_LABEL,\n lessons,\n isToday,\n hasLessons,\n currentLesson:\n includeCurrentAndNextLessons && isToday\n ? getCurrentLesson(lessons, now, { onInvalidTime })\n : null,\n nextLesson:\n includeCurrentAndNextLessons && isToday\n ? getNextLesson(lessons, now, { onInvalidTime })\n : null\n });\n }\n\n return scheduleDays;\n}\n","import type { Employee } from \"../types/employee\";\nimport type { FlattenedScheduleItem } from \"../types/schedule\";\n\n/**\n * Formats lesson start and end time as a range string.\n *\n * @returns `\"HH:MM–HH:MM\"`, or an empty string when both times are missing.\n *\n * @example\n * ```ts\n * formatLessonTimeRange(lesson); // \"10:00–11:30\"\n * ```\n */\nexport function formatLessonTimeRange(\n lesson: Pick<FlattenedScheduleItem, \"startLessonTime\" | \"endLessonTime\">\n): string {\n const start = lesson.startLessonTime.trim();\n const end = lesson.endLessonTime.trim();\n if (start && end) return `${start}\\u2013${end}`;\n if (start) return start;\n if (end) return end;\n return \"\";\n}\n\n/**\n * Returns the lesson type abbreviation.\n *\n * @returns The abbreviation string (e.g. `\"ЛК\"`, `\"ПЗ\"`, `\"ЛР\"`),\n * or an empty string when the field is absent.\n *\n * @example\n * ```ts\n * formatLessonType(lesson); // \"ЛК\"\n * ```\n */\nexport function formatLessonType(lesson: Pick<FlattenedScheduleItem, \"lessonTypeAbbrev\">): string {\n return lesson.lessonTypeAbbrev?.trim() ?? \"\";\n}\n\n/**\n * Returns a human-readable subgroup label.\n *\n * @returns `\"1 подгруппа\"` / `\"2 подгруппа\"` etc., or `\"\"` when `numSubgroup` is 0.\n *\n * @example\n * ```ts\n * formatLessonSubgroup(lesson); // \"1 подгруппа\"\n * ```\n */\nexport function formatLessonSubgroup(lesson: Pick<FlattenedScheduleItem, \"numSubgroup\">): string {\n if (!lesson.numSubgroup) return \"\";\n return `${String(lesson.numSubgroup)} \\u043F\\u043E\\u0434\\u0433\\u0440\\u0443\\u043F\\u043F\\u0430`;\n}\n\n/**\n * Formats the week numbers list as a compact string.\n *\n * Returns `\"кажд. нед.\"` when `weekNumber` is `null` or empty.\n * Returns `\"1, 3 нед.\"` for a specific set of weeks.\n *\n * @example\n * ```ts\n * formatLessonWeekNumbers({ weekNumber: [1, 3] }); // \"1, 3 нед.\"\n * formatLessonWeekNumbers({ weekNumber: null }); // \"кажд. нед.\"\n * ```\n */\nexport function formatLessonWeekNumbers(lesson: Pick<FlattenedScheduleItem, \"weekNumber\">): string {\n if (!lesson.weekNumber || lesson.weekNumber.length === 0) {\n return \"\\u043A\\u0430\\u0436\\u0434. \\u043D\\u0435\\u0434.\";\n }\n return `${lesson.weekNumber.join(\", \")} \\u043D\\u0435\\u0434.`;\n}\n\n/**\n * Formats a list of auditories as a comma-separated string.\n *\n * @returns `\"101-2, 102-3\"` or `\"\"` when the list is empty.\n *\n * @example\n * ```ts\n * formatLessonAuditories(lesson); // \"101-2, 102-3\"\n * ```\n */\nexport function formatLessonAuditories(lesson: Pick<FlattenedScheduleItem, \"auditories\">): string {\n return lesson.auditories.join(\", \");\n}\n\n/**\n * Formats an employee name as `\"Фамилия И.О.\"`.\n *\n * Falls back gracefully when first or middle name is missing.\n *\n * @example\n * ```ts\n * formatEmployeeShortName(employee); // \"Иванов И.И.\"\n * ```\n */\nexport function formatEmployeeShortName(\n employee: Pick<Employee, \"lastName\" | \"firstName\" | \"middleName\">\n): string {\n const lastName = employee.lastName.trim();\n const first = employee.firstName.trim();\n const middle = employee.middleName.trim();\n\n let initials = \"\";\n if (first.length > 0) initials += `${first.charAt(0)}.`;\n if (middle.length > 0) initials += `${middle.charAt(0)}.`;\n\n return initials.length > 0 ? `${lastName} ${initials}` : lastName;\n}\n\n/**\n * Formats all lesson employees as short names joined by `\", \"`.\n *\n * @returns `\"Иванов И.И., Петров П.П.\"` or `\"\"` when employees list is absent.\n *\n * @example\n * ```ts\n * formatLessonEmployees(lesson); // \"Иванов И.И.\"\n * ```\n */\nexport function formatLessonEmployees(lesson: Pick<FlattenedScheduleItem, \"employees\">): string {\n if (!lesson.employees || lesson.employees.length === 0) return \"\";\n return lesson.employees.map((employee) => formatEmployeeShortName(employee)).join(\", \");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { Speciality } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/specialities`.\n */\nexport function createSpecialitiesModule(\n config: Readonly<InternalClientConfig>\n): ListModule<Speciality> {\n return createListModule<Speciality>(config, \"/specialities\");\n}\n","import { BsuirConfigurationError } from \"./errors\";\nimport type { BsuirClientOptions, InternalClientConfig } from \"./types\";\nimport {\n createAnnouncementsModule,\n createAuditoriesModule,\n createDepartmentsModule,\n createEmployeesModule,\n createFacultiesModule,\n createGroupsModule,\n createScheduleModule,\n createSpecialitiesModule\n} from \"../modules\";\n\nconst DEFAULT_BASE_URL = \"https://iis.bsuir.by/api/v1\";\nconst DEFAULT_ALLOWED_BASE_URL_HOSTS = [\"iis.bsuir.by\"];\nconst DEFAULT_MAX_RESPONSE_BYTES = 5_000_000;\nconst INVALID_HEADER_LINE_BREAK = /[\\r\\n]/;\n\n// Prevents setTimeout() integer overflow (max safe value ~24.8 days).\n// 5 minutes is a generous upper bound for any HTTP request in this context.\nconst MAX_TIMEOUT_MS = 300_000;\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 BsuirConfigurationError(\n \"Global fetch is unavailable. Provide 'fetch' in createBsuirClient options.\"\n );\n }\n\n return globalThis.fetch;\n}\n\nfunction assertIntegerOption(\n value: number | undefined,\n name: string,\n minInclusive: number\n): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < minInclusive) {\n throw new BsuirConfigurationError(\n `'${name}' must be an integer greater than or equal to ${String(minInclusive)}`\n );\n }\n return value;\n}\n\nfunction normalizeHostname(rawHostname: string): string {\n const trimmed = rawHostname.trim().toLowerCase().replace(/\\.+$/, \"\");\n if (trimmed.length === 0) {\n return \"\";\n }\n if (trimmed.startsWith(\"[\") && trimmed.endsWith(\"]\")) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\nfunction isLoopbackHost(hostname: string): boolean {\n return hostname === \"localhost\" || hostname === \"::1\" || hostname.startsWith(\"127.\");\n}\n\n// Per-request `options.headers` go through the platform `Headers` constructor in\n// requestJson, which rejects CR/LF/NUL with a runtime error. We only pre-validate\n// configuration-level header values (e.g. `userAgent`) here so misconfiguration is\n// surfaced as a structured `BsuirConfigurationError` at client construction time\n// rather than at first request.\nfunction assertSafeHeaderValue(value: string | undefined, optionName: string): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (INVALID_HEADER_LINE_BREAK.test(value) || value.includes(\"\\0\")) {\n throw new BsuirConfigurationError(\n `'${optionName}' must not contain CR, LF or NUL characters`\n );\n }\n return value;\n}\n\nfunction normalizeBaseUrl(\n rawBaseUrl: string,\n allowInsecureHttp: boolean,\n allowedHosts: readonly string[]\n): string {\n let parsed: URL;\n try {\n parsed = new URL(rawBaseUrl);\n } catch {\n throw new BsuirConfigurationError(\"'baseUrl' must be a valid absolute URL\");\n }\n\n if (parsed.username || parsed.password) {\n throw new BsuirConfigurationError(\"'baseUrl' must not include credentials\");\n }\n\n if (parsed.search || parsed.hash) {\n throw new BsuirConfigurationError(\"'baseUrl' must not include query string or hash\");\n }\n\n if (parsed.protocol !== \"https:\" && !(allowInsecureHttp && parsed.protocol === \"http:\")) {\n throw new BsuirConfigurationError(\n \"'baseUrl' must use HTTPS. Set 'allowInsecureHttp: true' only for trusted local/test endpoints.\"\n );\n }\n\n if (parsed.port.length > 0) {\n throw new BsuirConfigurationError(\"'baseUrl' must not include an explicit port\");\n }\n\n const normalizedAllowedHosts = new Set(\n allowedHosts.map((host) => normalizeHostname(host)).filter((host) => host.length > 0)\n );\n if (normalizedAllowedHosts.size === 0) {\n throw new BsuirConfigurationError(\"'allowedBaseUrlHosts' must contain at least one hostname\");\n }\n\n // When allowInsecureHttp is enabled, only loopback hosts are permitted in the allowlist —\n // a non-loopback host in the list would create a confusing configuration where the user\n // appears to have opted in to plaintext HTTP for a public host. Plaintext HTTP must stay\n // strictly local/test only.\n if (allowInsecureHttp) {\n const nonLoopback = [...normalizedAllowedHosts].filter((host) => !isLoopbackHost(host));\n if (nonLoopback.length > 0) {\n throw new BsuirConfigurationError(\n `'allowInsecureHttp: true' requires every host in 'allowedBaseUrlHosts' to be loopback (localhost/127.x/[::1]); got non-loopback: ${nonLoopback.join(\", \")}`\n );\n }\n }\n\n const host = normalizeHostname(parsed.hostname);\n if (host.length === 0) {\n throw new BsuirConfigurationError(\"'baseUrl' must include a valid hostname\");\n }\n if (parsed.protocol === \"http:\" && allowInsecureHttp && !isLoopbackHost(host)) {\n throw new BsuirConfigurationError(\n \"'allowInsecureHttp: true' is restricted to localhost/loopback HTTP endpoints only\"\n );\n }\n if (!normalizedAllowedHosts.has(host)) {\n throw new BsuirConfigurationError(\n `'baseUrl' host '${host}' is not allowed. Allowed hosts: ${[...normalizedAllowedHosts].join(\", \")}`\n );\n }\n\n parsed.hostname = host;\n const normalizedPath = parsed.pathname.replace(/\\/+$/, \"\");\n return `${parsed.origin}${normalizedPath}`;\n}\n\nfunction createInternalConfig(options: BsuirClientOptions): InternalClientConfig {\n const timeoutMs = assertIntegerOption(options.timeoutMs, \"timeoutMs\", 1) ?? 10_000;\n if (timeoutMs > MAX_TIMEOUT_MS) {\n throw new BsuirConfigurationError(\n `'timeoutMs' must not exceed ${String(MAX_TIMEOUT_MS)}ms (5 minutes)`\n );\n }\n\n const retries = assertIntegerOption(options.retries, \"retries\", 0) ?? 1;\n const retryDelayMs = assertIntegerOption(options.retryDelayMs, \"retryDelayMs\", 0) ?? 300;\n const retryMaxDelayMs =\n assertIntegerOption(options.retryMaxDelayMs, \"retryMaxDelayMs\", 0) ?? 3000;\n const cacheTtlMs = assertIntegerOption(options.cache?.ttlMs, \"cache.ttlMs\", 1);\n const cacheMaxEntries =\n assertIntegerOption(options.cache?.maxEntries, \"cache.maxEntries\", 1) ?? 200;\n const maxResponseBytes =\n assertIntegerOption(options.maxResponseBytes, \"maxResponseBytes\", 1) ??\n DEFAULT_MAX_RESPONSE_BYTES;\n const allowInsecureHttp = options.allowInsecureHttp ?? false;\n const allowedBaseUrlHosts = options.allowedBaseUrlHosts ?? DEFAULT_ALLOWED_BASE_URL_HOSTS;\n\n if (retryDelayMs > retryMaxDelayMs) {\n throw new BsuirConfigurationError(\n \"'retryDelayMs' must be less than or equal to 'retryMaxDelayMs'\"\n );\n }\n\n return {\n baseUrl: normalizeBaseUrl(\n options.baseUrl ?? DEFAULT_BASE_URL,\n allowInsecureHttp,\n allowedBaseUrlHosts\n ),\n fetchImpl: resolveFetch(options.fetch),\n signal: options.signal,\n timeoutMs,\n retries,\n retryDelayMs,\n retryMaxDelayMs,\n retryJitter: options.retryJitter ?? true,\n userAgent: assertSafeHeaderValue(options.userAgent, \"userAgent\"),\n cacheTtlMs,\n cacheMaxEntries,\n dedupeInFlight: options.dedupeInFlight ?? false,\n maxResponseBytes,\n validateResponses: options.validateResponses ?? false,\n hooks: options.hooks ?? {},\n responseCache: new Map(),\n inFlightRequests: new Map()\n };\n}\n\n/**\n * Fully-typed public shape of the BSUIR API client.\n * All module types are inlined so API Extractor never needs to reach into private helpers.\n *\n * Use explicit raw/envelope helpers on the `schedule` module to obtain the\n * API's raw `ScheduleResponse` when required (e.g. `getGroupRaw`,\n * `getEmployeeRaw`, `getGroupEnvelope`). The default `getGroup`/`getEmployee`\n * return a normalized payload.\n */\nexport interface BsuirClientShape {\n schedule: ReturnType<typeof createScheduleModule>;\n groups: ReturnType<typeof createGroupsModule>;\n employees: ReturnType<typeof createEmployeesModule>;\n faculties: ReturnType<typeof createFacultiesModule>;\n departments: ReturnType<typeof createDepartmentsModule>;\n specialities: ReturnType<typeof createSpecialitiesModule>;\n announcements: ReturnType<typeof createAnnouncementsModule>;\n auditories: ReturnType<typeof createAuditoriesModule>;\n}\n\n/**\n * Creates a configured BSUIR IIS API client.\n *\n * The `schedule` module exposes both normalized and explicit raw/envelope\n * helpers. Use `getGroup` / `getEmployee` for normalized payloads and\n * `getGroupRaw` / `getEmployeeRaw` when you need the original API envelope.\n */\nexport function createBsuirClient(options: BsuirClientOptions = {}): BsuirClientShape {\n const config = createInternalConfig(options);\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 };\n}\n\n/**\n * Public client contract returned by `createBsuirClient`.\n */\nexport type BsuirClient = BsuirClientShape;\n"],"mappings":"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","names":["lessonAuditories","deepFreezeJson","host"]}
1
+ {"version":3,"sources":["../src/client/errors.ts","../src/client/mergeSignals.ts","../src/utils/guards.ts","../src/client/http/cache.ts","../src/client/http/response.ts","../src/client/http/retry.ts","../src/client/http/url.ts","../src/client/http/requestJson.ts","../src/client/responseValidators.ts","../src/modules/announcements.ts","../src/modules/createListModule.ts","../src/modules/auditories.ts","../src/modules/departments.ts","../src/modules/employees.ts","../src/modules/faculties.ts","../src/modules/groups.ts","../src/utils/week.ts","../src/modules/scheduleFilter.ts","../src/types/common.ts","../src/modules/scheduleNormalize.ts","../src/modules/scheduleApi.ts","../src/utils/date.ts","../src/helpers/schedule.ts","../src/helpers/scheduleFormat.ts","../src/modules/specialities.ts","../src/client/createClient.ts"],"sourcesContent":["/**\n * Restores proper prototype chain for Error subclasses in transpiled outputs.\n * TypeScript's ES<2015 transpilation breaks `class Foo extends Error`:\n * the compiled function lacks Error.prototype in its chain. This restores it.\n * See: https://github.com/microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins\n */\nfunction fixErrorPrototype(_instance: Error, _prototype: object): void {\n // No-op: target in this project is ES2015+ so Error subclassing is correct.\n}\n\nexport class BsuirApiError extends Error {\n readonly status: number;\n readonly endpoint: string;\n readonly body: unknown;\n\n constructor(message: string, status: number, endpoint: string, body: unknown) {\n super(message);\n fixErrorPrototype(this, BsuirApiError.prototype);\n this.name = \"BsuirApiError\";\n this.status = status;\n this.endpoint = endpoint;\n this.body = body;\n }\n}\n\nexport class BsuirResponsePayloadTooLargeError extends Error {\n readonly status: number;\n readonly endpoint: string;\n readonly maxResponseBytes: number;\n\n constructor(message: string, status: number, endpoint: string, maxResponseBytes: number) {\n super(message);\n fixErrorPrototype(this, BsuirResponsePayloadTooLargeError.prototype);\n this.name = \"BsuirResponsePayloadTooLargeError\";\n this.status = status;\n this.endpoint = endpoint;\n this.maxResponseBytes = maxResponseBytes;\n }\n}\n\nexport class BsuirNetworkError extends Error {\n readonly endpoint: string;\n\n constructor(message: string, endpoint: string, cause: unknown) {\n super(message, { cause });\n fixErrorPrototype(this, BsuirNetworkError.prototype);\n this.name = \"BsuirNetworkError\";\n this.endpoint = endpoint;\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, cause?: unknown) {\n super(message, { cause });\n fixErrorPrototype(this, BsuirTimeoutError.prototype);\n this.name = \"BsuirTimeoutError\";\n this.endpoint = endpoint;\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class BsuirValidationError extends Error {\n readonly field: string | undefined;\n readonly value: unknown;\n\n constructor(message: string, field?: string, value?: unknown) {\n super(message);\n fixErrorPrototype(this, BsuirValidationError.prototype);\n this.name = \"BsuirValidationError\";\n this.field = field;\n this.value = value;\n }\n}\n\nexport class BsuirResponseValidationError extends Error {\n readonly endpoint: string;\n\n constructor(message: string, endpoint: string) {\n super(message);\n fixErrorPrototype(this, BsuirResponseValidationError.prototype);\n this.name = \"BsuirResponseValidationError\";\n this.endpoint = endpoint;\n }\n}\n\nexport class BsuirConfigurationError extends Error {\n constructor(message: string) {\n super(message);\n fixErrorPrototype(this, BsuirConfigurationError.prototype);\n this.name = \"BsuirConfigurationError\";\n }\n}\n","type AbortSignalConstructor = typeof AbortSignal & {\n any?(signals: AbortSignal[]): AbortSignal;\n};\n\nconst AbortSignalCtor = AbortSignal as AbortSignalConstructor;\n\n// Use a WeakMap to associate merged signals with their cleanup callbacks instead\n// of attaching properties to the signal object.\nconst mergedSignalCleanupMap = new WeakMap<AbortSignal, () => void>();\n\nfunction getOnlySignal(signals: readonly AbortSignal[]): AbortSignal {\n const signal = signals[0];\n if (signal === undefined) {\n throw new Error(\"Expected at least one abort signal\");\n }\n return signal;\n}\n\n/**\n * Returns a cleanup callback for manually merged signals, when available.\n */\nexport function getMergedSignalCleanup(signal: AbortSignal): (() => void) | undefined {\n return mergedSignalCleanupMap.get(signal);\n}\n\n/**\n * Combines multiple abort signals and/or a timeout into a single signal.\n * When `AbortSignal.any` exists at runtime, delegates to the platform implementation.\n * Otherwise uses a manual merge so all signals are respected.\n */\nexport function mergeSignals(\n signals: readonly (AbortSignal | undefined)[],\n timeoutMs?: number\n): AbortSignal {\n const parts = signals.filter((s): s is AbortSignal => s !== undefined);\n\n if (parts.length === 0) {\n if (timeoutMs !== undefined) {\n if (typeof AbortSignalCtor.any === \"function\") {\n return AbortSignalCtor.any([AbortSignal.timeout(timeoutMs)]);\n }\n return mergeSignalsManual([], timeoutMs);\n }\n return new AbortController().signal;\n }\n\n if (parts.length === 1 && timeoutMs === undefined) {\n return getOnlySignal(parts);\n }\n\n if (typeof AbortSignalCtor.any === \"function\") {\n const all = timeoutMs === undefined ? parts : [...parts, AbortSignal.timeout(timeoutMs)];\n return AbortSignalCtor.any(all);\n }\n\n return mergeSignalsManual(parts, timeoutMs);\n}\n\n/** Used when `AbortSignal.any` is unavailable; exposed for unit tests. */\nexport function mergeSignalsManual(signals: AbortSignal[], timeoutMs?: number): AbortSignal {\n if (signals.length === 0 && timeoutMs === undefined) {\n return new AbortController().signal;\n }\n if (signals.length === 1 && timeoutMs === undefined) {\n return getOnlySignal(signals);\n }\n\n const combined = new AbortController();\n const listeners: { signal: AbortSignal; handler: () => void }[] = [];\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let cleanedUp = false;\n\n const onAnyAbort = (): void => {\n if (!combined.signal.aborted) {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n combined.abort();\n }\n };\n\n const cleanup = (): void => {\n if (cleanedUp) {\n return;\n }\n cleanedUp = true;\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n for (const listener of listeners) {\n listener.signal.removeEventListener(\"abort\", listener.handler);\n }\n listeners.length = 0;\n combined.signal.removeEventListener(\"abort\", cleanup);\n mergedSignalCleanupMap.delete(combined.signal);\n };\n\n // Register cleanup before possibly synchronous abort path below.\n combined.signal.addEventListener(\"abort\", cleanup, { once: true });\n mergedSignalCleanupMap.set(combined.signal, cleanup);\n\n if (timeoutMs !== undefined) {\n timeoutId = setTimeout(() => {\n if (!combined.signal.aborted) {\n combined.abort();\n }\n }, timeoutMs);\n }\n\n for (const signal of signals) {\n if (signal.aborted) {\n onAnyAbort();\n break;\n }\n const record = { signal, handler: onAnyAbort };\n listeners.push(record);\n signal.addEventListener(\"abort\", onAnyAbort, { once: true });\n }\n\n return combined.signal;\n}\n","import { BsuirValidationError } from \"../client/errors\";\n\n// BSUIR IIS API accepts only numeric group IDs (e.g., \"053503\").\n// Pattern is intentionally strict and covers only current IIS format.\nconst GROUP_NUMBER_PATTERN = /^\\d+$/;\nconst EMPLOYEE_URL_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/i;\n\n/**\n * Asserts that a value is a non-empty string.\n */\nexport function assertNonEmptyString(value: unknown, fieldName: string): asserts value is string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new BsuirValidationError(`'${fieldName}' must be a non-empty string`, fieldName, value);\n }\n}\n\n/**\n * Asserts that a value is a positive integer.\n */\nexport function assertPositiveInt(value: unknown, fieldName: string): asserts value is number {\n if (typeof value !== \"number\" || !Number.isInteger(value) || value <= 0) {\n throw new BsuirValidationError(`'${fieldName}' must be a positive integer`, fieldName, value);\n }\n}\n\n/**\n * Asserts that a group number is a non-empty string containing only digits.\n */\nexport function assertGroupNumber(\n value: unknown,\n fieldName = \"groupNumber\"\n): asserts value is string {\n assertNonEmptyString(value, fieldName);\n if (!GROUP_NUMBER_PATTERN.test(value)) {\n throw new BsuirValidationError(`'${fieldName}' must contain only digits`, fieldName, value);\n }\n}\n\n/**\n * Asserts that an employee urlId is a non-empty slug.\n */\nexport function assertEmployeeUrlId(value: unknown, fieldName = \"urlId\"): asserts value is string {\n assertNonEmptyString(value, fieldName);\n if (!EMPLOYEE_URL_ID_PATTERN.test(value)) {\n throw new BsuirValidationError(\n `'${fieldName}' must be a valid slug (letters, digits, hyphen)`,\n fieldName,\n value\n );\n }\n}\n\n/**\n * Returns `true` if the error object represents an abort/cancellation.\n */\nexport function isAbortError(error: unknown): boolean {\n // Browser: DOMException with name \"AbortError\" or \"TimeoutError\" (AbortSignal.timeout reason)\n if (\n error instanceof DOMException &&\n (error.name === \"AbortError\" || error.name === \"TimeoutError\")\n ) {\n return true;\n }\n\n // Node.js: Error-like object with code \"ABORT_ERR\"\n if (typeof error === \"object\" && error !== null) {\n const maybeError = error as { name?: unknown; code?: unknown };\n return maybeError.name === \"AbortError\" || maybeError.code === \"ABORT_ERR\";\n }\n\n return false;\n}\n","import { BsuirConfigurationError } from \"../errors\";\nimport type { InternalClientConfig } from \"../types\";\n\nfunction isJsonValue(value: unknown): boolean {\n if (value === null) {\n return true;\n }\n const t = typeof value;\n if (t === \"string\" || t === \"boolean\") {\n return true;\n }\n if (t === \"number\") {\n return Number.isFinite(value);\n }\n if (t !== \"object\") {\n return false;\n }\n // Reject typed arrays, Maps, Sets, Dates, etc. structuredClone would handle some of these\n // but the cache layer is designed to hold parsed JSON only; anything else indicates a\n // misconfigured value reaching the cache.\n const proto: unknown = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== Array.prototype && proto !== null) {\n return false;\n }\n return true;\n}\n\nfunction deepFreezeJson<T>(value: T): T {\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n if (Object.isFrozen(value)) {\n return value;\n }\n Object.freeze(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n deepFreezeJson(item);\n }\n } else {\n for (const key of Object.keys(value)) {\n deepFreezeJson((value as Record<string, unknown>)[key]);\n }\n }\n return value;\n}\n\n/**\n * Reads a cached response value by key, applying TTL validation and LRU touch update.\n *\n * Returns the cached value directly (deep-frozen on write) instead of cloning on every\n * read. Callers that need a mutable copy must clone explicitly. This avoids paying\n * structuredClone() cost on every cache hit, which can dominate latency for large\n * schedule payloads on a hot cache path.\n */\nexport function tryReadCache(config: Readonly<InternalClientConfig>, key: string): unknown {\n const entry = config.responseCache.get(key);\n if (!entry) {\n return undefined;\n }\n if (entry.expiresAt <= Date.now()) {\n config.responseCache.delete(key);\n return undefined;\n }\n // Touch key to move it to the end (most recently used).\n config.responseCache.delete(key);\n config.responseCache.set(key, entry);\n return entry.value;\n}\n\n/**\n * Writes response value to cache and performs expiration/LRU eviction.\n *\n * Throws if the value is not a JSON-compatible structure — the cache is designed to\n * hold parsed JSON only, and storing other shapes (Map/Set/Date/typed arrays/class\n * instances) would either fail later on read or silently lose data on\n * structuredClone-based eviction strategies.\n */\nexport function setCache<T>(\n config: Readonly<InternalClientConfig>,\n key: string,\n value: T\n): T | undefined {\n if (config.cacheTtlMs === undefined) {\n return undefined;\n }\n if (!isJsonValue(value)) {\n throw new BsuirConfigurationError(\n \"Response cache only accepts JSON-compatible values (object/array/string/finite-number/boolean/null)\"\n );\n }\n const now = Date.now();\n\n // Clone once on write to isolate the cache from later mutations of the caller's\n // payload, then deep-freeze so reads can return the same reference safely without\n // paying structuredClone cost on every hit.\n const frozen = deepFreezeJson(structuredClone(value));\n\n // Re-insert to mark as most recently used.\n config.responseCache.delete(key);\n config.responseCache.set(key, {\n value: frozen,\n expiresAt: now + config.cacheTtlMs\n });\n\n if (config.responseCache.size <= config.cacheMaxEntries) {\n return frozen;\n }\n\n // Remove expired entries before capacity-based eviction, but only when oversized.\n for (const [k, v] of config.responseCache) {\n if (v.expiresAt <= now) {\n config.responseCache.delete(k);\n }\n }\n\n if (config.responseCache.size <= config.cacheMaxEntries) {\n return frozen;\n }\n\n // Evict least-recently-used entries using insertion order.\n for (const k of config.responseCache.keys()) {\n if (config.responseCache.size <= config.cacheMaxEntries) {\n break;\n }\n config.responseCache.delete(k);\n }\n\n return frozen;\n}\n","import { BsuirApiError, BsuirResponsePayloadTooLargeError } from \"../errors\";\n\nconst UTF8_ENCODER = new TextEncoder();\n\nasync function readBodyTextWithLimit(\n response: Response,\n maxResponseBytes: number\n): Promise<string> {\n const contentLengthHeader = response.headers.get(\"content-length\");\n if (contentLengthHeader) {\n const parsedLength = Number(contentLengthHeader);\n if (Number.isFinite(parsedLength) && parsedLength > maxResponseBytes) {\n throw new BsuirResponsePayloadTooLargeError(\n `Response body exceeds maxResponseBytes limit (${String(maxResponseBytes)} bytes)`,\n response.status,\n response.url,\n maxResponseBytes\n );\n }\n }\n\n if (!response.body || typeof response.body.getReader !== \"function\") {\n const fallbackText = await response.text();\n if (UTF8_ENCODER.encode(fallbackText).byteLength > maxResponseBytes) {\n throw new BsuirResponsePayloadTooLargeError(\n `Response body exceeds maxResponseBytes limit (${String(maxResponseBytes)} bytes)`,\n response.status,\n response.url,\n maxResponseBytes\n );\n }\n return fallbackText;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let bytesRead = 0;\n let text = \"\";\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n bytesRead += value.byteLength;\n if (bytesRead > maxResponseBytes) {\n await reader.cancel();\n throw new BsuirResponsePayloadTooLargeError(\n `Response body exceeds maxResponseBytes limit (${String(maxResponseBytes)} bytes)`,\n response.status,\n response.url,\n maxResponseBytes\n );\n }\n text += decoder.decode(value, { stream: true });\n }\n text += decoder.decode();\n return text;\n}\n\n/**\n * Parses response body as JSON when possible, otherwise returns text.\n * Returns `null` for empty non-JSON bodies.\n * Throws `BsuirApiError` for declared JSON payloads that are empty/invalid.\n */\nexport async function parseBody(response: Response, maxResponseBytes: number): Promise<unknown> {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n const declaredJson = contentType.includes(\"application/json\");\n const text = await readBodyTextWithLimit(response, maxResponseBytes);\n if (text.length === 0) {\n if (declaredJson) {\n throw new BsuirApiError(\"Invalid JSON response payload\", response.status, response.url, null);\n }\n return null;\n }\n try {\n return JSON.parse(text) as unknown;\n } catch {\n if (declaredJson) {\n throw new BsuirApiError(\"Invalid JSON response payload\", response.status, response.url, null);\n }\n return text;\n }\n}\n","import type { InternalClientConfig } from \"../types\";\n\n/** HTTP status codes retriable by the request pipeline. */\nexport const RETRIABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\nconst MAX_ACCEPTED_RETRY_AFTER_MS = 60_000;\n// Hard internal cap applied to any Retry-After value (numeric or date-based) before\n// the higher-level decision logic compares it against MAX_ACCEPTED_RETRY_AFTER_MS.\n// Prevents arithmetic overflow or unreasonably large delays from hostile/misconfigured\n// upstreams from being propagated further.\nconst RETRY_AFTER_INTERNAL_CAP_MS = 86_400_000;\n\n/** Delays execution for a specified number of milliseconds. */\nexport function 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 || retryAfter.trim().length === 0) {\n return null;\n }\n\n // Try parsing as seconds (RFC 7231: numeric-value).\n const asSeconds = Number(retryAfter);\n if (\n Number.isFinite(asSeconds) &&\n asSeconds >= 0 && // Validate it's actually numeric format, not a date string starting with a digit.\n /^\\d+(\\.\\d+)?$/.test(retryAfter.trim())\n ) {\n const ms = Math.floor(asSeconds * 1000);\n return Math.min(ms, RETRY_AFTER_INTERNAL_CAP_MS);\n }\n\n // Try parsing as HTTP date format (RFC 7231: http-date).\n const dateValue = Date.parse(retryAfter);\n if (Number.isFinite(dateValue)) {\n const delayMs = dateValue - Date.now();\n // Only accept if date is in the future.\n if (delayMs > 0) {\n return Math.min(delayMs, RETRY_AFTER_INTERNAL_CAP_MS);\n }\n }\n\n return null;\n}\n\nfunction getBackoffDelayMs(config: Readonly<InternalClientConfig>, attempt: number): number {\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 const jitterFactor = 0.75 + Math.random() * 0.5;\n return Math.floor(baseDelay * jitterFactor);\n}\n\nexport type RetryDecision =\n | { retryable: true; delayMs: number }\n | { retryable: false; rejectedDelayMs: number };\n\n/**\n * Calculates retry behavior for the current attempt.\n *\n * If server-provided `Retry-After` exceeds a sane safety bound, retry is rejected\n * to avoid long caller stalls caused by hostile or misconfigured upstreams.\n */\nexport function getRetryDecision(\n config: Readonly<InternalClientConfig>,\n attempt: number,\n retryAfterHeader?: string | null\n): RetryDecision {\n const retryAfterDelay = parseRetryAfterMs(retryAfterHeader ?? null);\n if (retryAfterDelay !== null) {\n if (retryAfterDelay > MAX_ACCEPTED_RETRY_AFTER_MS) {\n return { retryable: false, rejectedDelayMs: retryAfterDelay };\n }\n return { retryable: true, delayMs: Math.min(retryAfterDelay, config.retryMaxDelayMs) };\n }\n return { retryable: true, delayMs: getBackoffDelayMs(config, attempt) };\n}\n\n/**\n * Calculates retry delay using `Retry-After` when present, otherwise exponential backoff.\n */\nexport function getRetryDelayMs(\n config: Readonly<InternalClientConfig>,\n attempt: number,\n retryAfterHeader?: string | null\n): number {\n const decision = getRetryDecision(config, attempt, retryAfterHeader);\n if (decision.retryable) {\n return decision.delayMs;\n }\n return getBackoffDelayMs(config, attempt);\n}\n","import type { QueryParams } from \"../types\";\nimport { BsuirValidationError } from \"../errors\";\n\n// Reject control chars (incl. CR/LF/NUL), whitespace, DEL, and URL-structural characters\n// (`?`, `#`, `&`, `=`). Other printable characters are allowed and will be percent-encoded\n// by URLSearchParams as needed.\n// eslint-disable-next-line no-control-regex\nconst UNSAFE_QUERY_KEY = /[\\u0000-\\u0020\\u007F&=?#]/;\nconst SCHEME_PREFIX = /^[A-Za-z][A-Za-z\\d+.-]*:/;\n\nfunction assertSafeQueryKey(key: string): void {\n if (key.length === 0) {\n throw new BsuirValidationError(\"Query parameter key must not be empty\", \"queryKey\", key);\n }\n if (UNSAFE_QUERY_KEY.test(key)) {\n throw new BsuirValidationError(\n `Invalid query parameter key '${key}': must not contain control characters, whitespace, or '? # & ='`,\n \"queryKey\",\n key\n );\n }\n}\n\nfunction assertSafePath(path: string): void {\n if (path.trim().length === 0) {\n throw new BsuirValidationError(\"Path must not be empty\", \"path\", path);\n }\n if (path.includes(\"\\\\\")) {\n throw new BsuirValidationError(\n \"Path must use forward slashes and must not contain backslashes\",\n \"path\",\n path\n );\n }\n if (path.startsWith(\"//\")) {\n throw new BsuirValidationError(\"Path must not start with '//'\", \"path\", path);\n }\n if (SCHEME_PREFIX.test(path)) {\n throw new BsuirValidationError(\"Path must not include a URL scheme\", \"path\", path);\n }\n\n const normalizedPath = path.startsWith(\"/\") ? path.slice(1) : path;\n const segments = normalizedPath.split(\"/\");\n for (const segment of segments) {\n if (segment.length === 0) {\n continue;\n }\n let decodedSegment: string;\n try {\n decodedSegment = decodeURIComponent(segment);\n } catch {\n throw new BsuirValidationError(\"Path contains malformed escape sequence\", \"path\", path);\n }\n if (decodedSegment === \".\" || decodedSegment === \"..\") {\n throw new BsuirValidationError(\n \"Path must not contain relative traversal segments\",\n \"path\",\n path\n );\n }\n }\n}\n\n/**\n * Builds absolute endpoint URL from base URL, path and query params.\n *\n * Query parameters are sorted by key to produce deterministic URLs - important\n * for cache key stability and for reproducible request fingerprints.\n */\nexport function buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n assertSafePath(path);\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 const sortedEntries = Object.entries(query)\n .filter(([, value]) => value !== undefined && value !== null)\n .toSorted((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n for (const [key, value] of sortedEntries) {\n assertSafeQueryKey(key);\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n}\n","import {\n BsuirApiError,\n BsuirNetworkError,\n BsuirResponsePayloadTooLargeError,\n BsuirTimeoutError\n} from \"../errors\";\nimport { getMergedSignalCleanup, mergeSignals } from \"../mergeSignals\";\nimport type {\n ErrorHookContext,\n InternalClientConfig,\n QueryParams,\n RequestHookContext,\n RequestMethod,\n RequestOptions,\n ResponseHookContext,\n RetryHookContext\n} from \"../types\";\nimport { isAbortError } from \"../../utils/guards\";\nimport { setCache, tryReadCache } from \"./cache\";\nimport { parseBody } from \"./response\";\nimport { getRetryDecision, getRetryDelayMs, RETRIABLE_STATUS_CODES, sleep } from \"./retry\";\nimport { buildUrl } from \"./url\";\n\nconst CACHE_KEY_HEADER_ALLOWLIST = new Set<string>([\"accept\", \"accept-language\"]);\n\nfunction normalizeHeadersForRequestKey(headers: Headers): string {\n return [...headers.entries()]\n .map(([key, value]) => [key.toLowerCase(), value] as const)\n .filter(([key]) => CACHE_KEY_HEADER_ALLOWLIST.has(key))\n .toSorted((a, b) => {\n if (a[0] !== b[0]) {\n return a[0] < b[0] ? -1 : 1;\n }\n if (a[1] !== b[1]) {\n return a[1] < b[1] ? -1 : 1;\n }\n return 0;\n })\n .map(([key, value]) => `${key}:${value}`)\n .join(\"\\n\");\n}\n\nfunction buildRequestKey(method: RequestMethod, endpoint: string, headers: Headers): string {\n return `${method}\\n${endpoint}\\n${normalizeHeadersForRequestKey(headers)}`;\n}\n\n// Explicit denylist of header names that carry per-identity credentials. Any of these,\n// when present on a request, disables shared response caching and in-flight dedup so\n// that one identity's response cannot be returned to another caller.\nconst PRIVATE_HEADER_DENYLIST = new Set<string>([\n \"authorization\",\n \"proxy-authorization\",\n \"cookie\",\n \"set-cookie\",\n \"x-api-key\",\n \"api-key\",\n \"x-auth-token\",\n \"x-access-token\",\n \"x-csrf-token\",\n \"x-session-id\",\n \"x-session-token\"\n]);\n\nfunction isBodyInit(value: unknown): value is BodyInit {\n if (typeof value === \"string\") {\n return true;\n }\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n if (\n value instanceof URLSearchParams ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value)\n ) {\n return true;\n }\n // FormData, Blob and ReadableStream are not always available in every runtime,\n // so guard with typeof to avoid ReferenceErrors in minimal environments.\n if (typeof FormData !== \"undefined\" && value instanceof FormData) {\n return true;\n }\n if (typeof Blob !== \"undefined\" && value instanceof Blob) {\n return true;\n }\n if (typeof ReadableStream !== \"undefined\" && value instanceof ReadableStream) {\n return true;\n }\n return false;\n}\n\nfunction serializeRequestBody(rawBody: unknown, headers: Headers): BodyInit | undefined {\n if (rawBody === undefined) {\n return undefined;\n }\n if (isBodyInit(rawBody)) {\n // Pass-through for stream/form/binary bodies. Do not set Content-Type; the platform\n // (or the caller's explicit header) is responsible for it — e.g. FormData picks its\n // own multipart boundary.\n return rawBody;\n }\n // Fall through to JSON for plain objects/arrays/numbers/booleans/null.\n const serialized = JSON.stringify(rawBody);\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n return serialized;\n}\n\nfunction isPrivateHeader(name: string): boolean {\n return PRIVATE_HEADER_DENYLIST.has(name.toLowerCase());\n}\n\nfunction hasPrivateHeaders(headers: Headers): boolean {\n for (const [key] of headers.entries()) {\n if (isPrivateHeader(key)) {\n return true;\n }\n }\n return false;\n}\n\nfunction baseHookContext(\n method: RequestMethod,\n path: string,\n endpoint: string,\n attempt: number,\n maxAttempts: number,\n query: QueryParams | undefined\n): RequestHookContext {\n return {\n method,\n path,\n endpoint,\n attempt,\n maxAttempts,\n query\n };\n}\n\n/**\n * Executes JSON HTTP request with timeout, retry/backoff, deduplication and cache support.\n */\nexport async function requestJson<T>(\n config: Readonly<InternalClientConfig>,\n path: string,\n options: RequestOptions = {}\n): Promise<T> {\n const endpoint = buildUrl(config.baseUrl, path, options.query);\n const method = options.method ?? \"GET\";\n const requestCanRetry = method === \"GET\";\n const maxRetries = requestCanRetry ? config.retries : 0;\n const maxAttempts = maxRetries + 1;\n const headers = new Headers({\n Accept: \"application/json\"\n });\n\n if (config.userAgent) {\n headers.set(\"User-Agent\", config.userAgent);\n }\n if (options.headers) {\n for (const [key, value] of new Headers(options.headers)) {\n headers.set(key, value);\n }\n }\n\n const body = serializeRequestBody(options.body, headers);\n\n const cacheMode = options.cache ?? \"default\";\n // Caching is disabled only when a relevant signal is already aborted. A live\n // signal is fine — its job is to cancel the network request, not to signal\n // \"this caller has private data.\" In-flight dedup is still disabled for\n // per-call signals so callers do not inherit each other's cancellation semantics.\n const signalAborted = options.signal?.aborted === true || config.signal?.aborted === true;\n const hasPrivateRequestHeaders = hasPrivateHeaders(headers);\n const canUseCaching =\n config.cacheTtlMs !== undefined &&\n method === \"GET\" &&\n !signalAborted &&\n !hasPrivateRequestHeaders;\n const canReadFromCache = canUseCaching && cacheMode === \"default\";\n const canWriteToCache = canUseCaching && cacheMode !== \"no-store\";\n const perCallSignalProvided = options.signal !== undefined;\n const canUseDedup =\n config.dedupeInFlight &&\n method === \"GET\" &&\n !signalAborted &&\n !hasPrivateRequestHeaders &&\n cacheMode === \"default\" &&\n !perCallSignalProvided;\n let requestKey: string | undefined;\n const ensureRequestKey = (): string => {\n requestKey ??= buildRequestKey(method, endpoint, headers);\n return requestKey;\n };\n\n if (canReadFromCache) {\n const cached = tryReadCache(config, ensureRequestKey());\n if (cached !== undefined) {\n const cacheHitCtx: ResponseHookContext = {\n ...baseHookContext(method, path, endpoint, 1, maxAttempts, options.query),\n status: 200,\n durationMs: 0,\n fromCache: true\n };\n config.hooks.onResponse?.(cacheHitCtx);\n return cached as T;\n }\n }\n\n let lastSuccessResponse: { hookCtx: RequestHookContext; durationMs: number } | undefined;\n\n const performRequest = async (): Promise<T> => {\n for (let attempt = 0; attempt <= maxRetries; attempt += 1) {\n const attemptNumber = attempt + 1;\n const startedAt = Date.now();\n const hookCtx = baseHookContext(\n method,\n path,\n endpoint,\n attemptNumber,\n maxAttempts,\n options.query\n );\n\n config.hooks.onRequest?.(hookCtx);\n\n const requestSignal = mergeSignals([options.signal, config.signal], config.timeoutMs);\n const requestSignalCleanup = getMergedSignalCleanup(requestSignal);\n\n try {\n const requestInit: RequestInit = {\n method,\n headers,\n signal: requestSignal\n };\n if (body !== undefined) {\n requestInit.body = body;\n }\n\n const response = await config.fetchImpl(endpoint, requestInit);\n\n if (!response.ok) {\n const errorBody = await parseBody(response, config.maxResponseBytes);\n if (attempt < maxRetries && RETRIABLE_STATUS_CODES.has(response.status)) {\n const retryDecision = getRetryDecision(\n config,\n attempt,\n response.headers.get(\"retry-after\")\n );\n if (retryDecision.retryable) {\n const retryCtx: RetryHookContext = {\n ...hookCtx,\n delayMs: retryDecision.delayMs,\n reason: \"http_status\",\n status: response.status\n };\n config.hooks.onRetry?.(retryCtx);\n await sleep(retryDecision.delayMs);\n continue;\n }\n const skipRetryCtx: RetryHookContext = {\n ...hookCtx,\n delayMs: retryDecision.rejectedDelayMs,\n reason: \"retry_after_too_large\",\n status: response.status\n };\n config.hooks.onRetry?.(skipRetryCtx);\n }\n const apiError = new BsuirApiError(\n `BSUIR API returned HTTP ${String(response.status)} for ${method} ${path}`,\n response.status,\n endpoint,\n errorBody\n );\n const errorCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error: apiError\n };\n config.hooks.onError?.(errorCtx);\n throw apiError;\n }\n\n const parsed = (await parseBody(response, config.maxResponseBytes)) as T;\n const durationMs = Date.now() - startedAt;\n const responseCtx: ResponseHookContext = {\n ...hookCtx,\n status: response.status,\n durationMs,\n fromCache: false\n };\n config.hooks.onResponse?.(responseCtx);\n lastSuccessResponse = { hookCtx, durationMs };\n return parsed;\n } catch (error: unknown) {\n if (error instanceof BsuirApiError) {\n throw error;\n }\n\n if (error instanceof BsuirResponsePayloadTooLargeError) {\n const payloadTooLargeCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error\n };\n config.hooks.onError?.(payloadTooLargeCtx);\n throw error;\n }\n\n if (isAbortError(error)) {\n if (options.signal?.aborted || config.signal?.aborted) {\n const abortCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error\n };\n config.hooks.onError?.(abortCtx);\n throw error;\n }\n const timeoutError = new BsuirTimeoutError(\n `Request timed out after ${String(config.timeoutMs)}ms: ${path}`,\n endpoint,\n config.timeoutMs,\n error\n );\n const timeoutCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error: timeoutError\n };\n config.hooks.onError?.(timeoutCtx);\n throw timeoutError;\n }\n\n if (attempt < maxRetries) {\n const delayMs = getRetryDelayMs(config, attempt);\n const retryCtx: RetryHookContext = {\n ...hookCtx,\n delayMs,\n reason: \"network_error\",\n status: undefined\n };\n config.hooks.onRetry?.(retryCtx);\n await sleep(delayMs);\n continue;\n }\n\n const networkError = new BsuirNetworkError(\n `Network error while requesting ${path}`,\n endpoint,\n error\n );\n const networkErrorCtx: ErrorHookContext = {\n ...hookCtx,\n durationMs: Date.now() - startedAt,\n error: networkError\n };\n config.hooks.onError?.(networkErrorCtx);\n throw networkError;\n } finally {\n requestSignalCleanup?.();\n }\n }\n\n throw new BsuirNetworkError(`Unexpected retry loop termination for ${path}`, endpoint, null);\n };\n\n const runResponseValidator = (payload: T): void => {\n if (!options.responseValidator) {\n return;\n }\n const responseMeta = lastSuccessResponse ?? {\n hookCtx: baseHookContext(method, path, endpoint, maxAttempts, maxAttempts, options.query),\n durationMs: 0\n };\n try {\n options.responseValidator(payload);\n } catch (error: unknown) {\n const errorCtx: ErrorHookContext = {\n ...responseMeta.hookCtx,\n durationMs: responseMeta.durationMs,\n error\n };\n config.hooks.onError?.(errorCtx);\n throw error;\n }\n };\n\n const requestAndMaybeCache = (): Promise<T> =>\n performRequest().then((payload) => {\n runResponseValidator(payload);\n if (canWriteToCache) {\n const cached = setCache(config, ensureRequestKey(), payload);\n if (cached !== undefined) {\n return cached;\n }\n }\n return payload;\n });\n\n if (canUseDedup) {\n const key = ensureRequestKey();\n const inFlight = config.inFlightRequests.get(key);\n if (inFlight) {\n return (await inFlight) as T;\n }\n\n const inFlightPromise: Promise<T> = requestAndMaybeCache().finally(() => {\n config.inFlightRequests.delete(key);\n });\n config.inFlightRequests.set(key, inFlightPromise);\n return await inFlightPromise;\n }\n\n return await requestAndMaybeCache();\n}\n","import { BsuirResponseValidationError } from \"./errors\";\nimport type { ApiDateResponse } from \"../types/common\";\nimport type { ScheduleResponse } from \"../types/schedule\";\n\nfunction asRecord(payload: unknown): Record<string, unknown> | null {\n if (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n return null;\n }\n return payload as Record<string, unknown>;\n}\n\nfunction ensureRecord(\n payload: unknown,\n endpoint: string,\n expected: string\n): Record<string, unknown> {\n const record = asRecord(payload);\n if (!record) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: expected ${expected}`,\n endpoint\n );\n }\n return record;\n}\n\nfunction isNullableObject(value: unknown): boolean {\n return (\n value === null || value === undefined || (typeof value === \"object\" && !Array.isArray(value))\n );\n}\n\n/**\n * Asserts that payload is an array response.\n */\nexport function assertArrayResponse(\n payload: unknown,\n endpoint: string\n): asserts payload is unknown[] {\n if (!Array.isArray(payload)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: expected array, got ${typeof payload}`,\n endpoint\n );\n }\n}\n\n/**\n * Asserts that payload matches `{ lastUpdateDate: string }`.\n */\nexport function assertApiDateResponse(\n payload: unknown,\n endpoint: string\n): asserts payload is ApiDateResponse {\n const record = ensureRecord(payload, endpoint, \"object with lastUpdateDate\");\n if (typeof record.lastUpdateDate !== \"string\" || record.lastUpdateDate.trim().length === 0) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'lastUpdateDate' must be a non-empty string, got ${typeof record.lastUpdateDate}`,\n endpoint\n );\n }\n}\n\n/**\n * Asserts that payload is a schedule response envelope.\n */\nexport function assertScheduleResponse(\n payload: unknown,\n endpoint: string\n): asserts payload is ScheduleResponse {\n const record = ensureRecord(payload, endpoint, \"object\");\n const schedules = record.schedules;\n const exams = record.exams;\n const employeeDto = record.employeeDto;\n const studentGroupDto = record.studentGroupDto;\n\n // undefined treated as absent field — API may omit schedules/exams for exam-only or schedule-only entries\n if (\n schedules !== null &&\n schedules !== undefined &&\n (typeof schedules !== \"object\" || Array.isArray(schedules))\n ) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'schedules' must be object or null, got ${\n Array.isArray(schedules) ? \"array\" : typeof schedules\n }`,\n endpoint\n );\n }\n\n if (exams !== null && exams !== undefined && !Array.isArray(exams)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'exams' must be array or null, got ${typeof exams}`,\n endpoint\n );\n }\n\n if (!isNullableObject(employeeDto)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'employeeDto' must be object or null, got ${typeof employeeDto}`,\n endpoint\n );\n }\n\n if (!isNullableObject(studentGroupDto)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: 'studentGroupDto' must be object or null, got ${typeof studentGroupDto}`,\n endpoint\n );\n }\n}\n","import { BsuirApiError } from \"../client/errors\";\nimport { requestJson } from \"../client/http\";\nimport { assertArrayResponse } from \"../client/responseValidators\";\nimport type { InternalClientConfig } from \"../client/types\";\nimport type { Announcement } from \"../types/announcement\";\nimport { assertEmployeeUrlId, assertPositiveInt } from \"../utils/guards\";\nimport type { ReadOptions } from \"./types\";\n\n/**\n * Options accepted by `announcements.byEmployee` and `announcements.byDepartment`.\n *\n * The BSUIR IIS API responds with HTTP 404 when no announcements exist for an\n * employee or department. By default this SDK converts that 404 into an empty\n * array. Set `treat404AsEmpty: false` to receive a `BsuirApiError` instead —\n * useful when you want to distinguish \"entity not found\" from \"entity has no\n * announcements\" at the call site.\n */\nexport interface AnnouncementReadOptions extends ReadOptions {\n /**\n * When `true` (the default), HTTP 404 responses from the announcements endpoint\n * are converted to an empty array. When `false`, the underlying `BsuirApiError`\n * is rethrown.\n *\n * @defaultValue true\n */\n treat404AsEmpty?: boolean;\n}\n\nfunction endpointMatchesPath(endpoint: string, path: string): boolean {\n try {\n return new URL(endpoint).pathname.endsWith(path);\n } catch {\n return false;\n }\n}\n\n/**\n * Fetches an announcement list, optionally converting 404 to empty array.\n *\n * The previous implementation inspected response bodies for marker strings like\n * \"announcement\"/\"объяв\" to decide whether to swallow the 404. That heuristic\n * masked genuine errors with similar wording and was fragile across API versions.\n * We now rely on the endpoint-scoped 404 status alone, with an opt-out.\n *\n * @returns The announcement list, or empty array for 404 when `treat404AsEmpty` is `true`\n * @throws {BsuirApiError} For non-404 errors, or any 404 when `treat404AsEmpty` is `false`\n * @throws {BsuirNetworkError} On transport failures\n * @throws {BsuirTimeoutError} When request times out\n */\nasync function requestAnnouncementList(\n config: Readonly<InternalClientConfig>,\n path: string,\n options: AnnouncementReadOptions & { query: Record<string, string | number> }\n): Promise<Announcement[]> {\n const treat404AsEmpty = options.treat404AsEmpty ?? true;\n try {\n const payload = await requestJson<unknown>(config, path, {\n ...options,\n responseValidator: config.validateResponses\n ? (value) => {\n assertArrayResponse(value, path);\n }\n : undefined\n });\n return payload as Announcement[];\n } catch (error) {\n if (\n treat404AsEmpty &&\n error instanceof BsuirApiError &&\n error.status === 404 &&\n endpointMatchesPath(error.endpoint, path)\n ) {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Creates the announcements module (`byEmployee`, `byDepartment`).\n */\nexport function createAnnouncementsModule(config: Readonly<InternalClientConfig>): {\n byEmployee(urlId: string, options?: AnnouncementReadOptions): Promise<Announcement[]>;\n byDepartment(id: number, options?: AnnouncementReadOptions): Promise<Announcement[]>;\n} {\n return {\n /**\n * Lists announcements for an employee.\n *\n * IIS responds with HTTP 404 when the employee has no announcements. By default\n * the SDK maps that to `[]`; pass `treat404AsEmpty: false` to receive the\n * underlying `BsuirApiError` instead.\n */\n async byEmployee(\n urlId: string,\n options: AnnouncementReadOptions = {}\n ): Promise<Announcement[]> {\n assertEmployeeUrlId(urlId, \"urlId\");\n return requestAnnouncementList(config, \"/announcements/employees\", {\n ...options,\n query: { \"url-id\": urlId }\n });\n },\n\n /**\n * Lists announcements for a department.\n *\n * IIS responds with HTTP 404 when the department has no announcements. By default\n * the SDK maps that to `[]`; pass `treat404AsEmpty: false` to receive the\n * underlying `BsuirApiError` instead.\n */\n async byDepartment(id: number, options: AnnouncementReadOptions = {}): Promise<Announcement[]> {\n assertPositiveInt(id, \"id\");\n return requestAnnouncementList(config, \"/announcements/departments\", {\n ...options,\n query: { id }\n });\n }\n };\n}\n","import { requestJson } from \"../client/http\";\nimport { assertArrayResponse } from \"../client/responseValidators\";\nimport type { InternalClientConfig } from \"../client/types\";\nimport type { ReadOptions } from \"./types\";\n\nexport interface ListModule<T> {\n listAll(options?: ReadOptions): Promise<T[]>;\n}\n\n/**\n * Creates a simple catalog-like module exposing `listAll()` for a fixed endpoint.\n */\nexport function createListModule<T>(\n config: Readonly<InternalClientConfig>,\n endpoint: string\n): ListModule<T> {\n return {\n /**\n * Returns all items from the configured endpoint.\n */\n async listAll(options: ReadOptions = {}): Promise<T[]> {\n const payload = await requestJson<unknown>(config, endpoint, {\n signal: options.signal,\n cache: options.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertArrayResponse(value, endpoint);\n }\n : undefined\n });\n return payload as T[];\n }\n };\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { Auditory } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/auditories`.\n */\nexport function createAuditoriesModule(\n config: Readonly<InternalClientConfig>\n): ListModule<Auditory> {\n return createListModule<Auditory>(config, \"/auditories\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { Department } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/departments`.\n */\nexport function createDepartmentsModule(\n config: Readonly<InternalClientConfig>\n): ListModule<Department> {\n return createListModule<Department>(config, \"/departments\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { EmployeeCatalogItem } from \"../types/employee\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/employees/all`.\n */\nexport function createEmployeesModule(\n config: Readonly<InternalClientConfig>\n): ListModule<EmployeeCatalogItem> {\n return createListModule<EmployeeCatalogItem>(config, \"/employees/all\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { Faculty } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/faculties`.\n */\nexport function createFacultiesModule(config: Readonly<InternalClientConfig>): ListModule<Faculty> {\n return createListModule<Faculty>(config, \"/faculties\");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { StudentGroupCatalogItem } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/student-groups`.\n */\nexport function createGroupsModule(\n config: Readonly<InternalClientConfig>\n): ListModule<StudentGroupCatalogItem> {\n return createListModule<StudentGroupCatalogItem>(config, \"/student-groups\");\n}\n","import { BsuirValidationError } from \"../client/errors\";\nimport { assertPositiveInt } from \"./guards\";\n\n// API response should never nest deeper than 2 levels; 10 is a generous safety cap to prevent stack overflow\nconst MAX_PARSE_DEPTH = 10;\n\n/**\n * Normalizes current week payload from API to a positive integer.\n * API can return plain text (`\"1\\n\"`) or number.\n */\nexport function parseCurrentWeek(payload: unknown): number {\n return parseCurrentWeekInternal(payload, 0);\n}\n\nfunction parseCurrentWeekInternal(payload: unknown, depth: number): number {\n if (depth > MAX_PARSE_DEPTH) {\n throw new BsuirValidationError(\n \"'currentWeek' response payload has excessive nesting depth\",\n \"currentWeek\",\n payload\n );\n }\n\n if (typeof payload === \"number\") {\n assertPositiveInt(payload, \"currentWeek\");\n return payload;\n }\n\n if (typeof payload === \"string\") {\n const normalized = payload.trim();\n if (normalized.length === 0) {\n throw new BsuirValidationError(\n \"'currentWeek' response payload is an empty string\",\n \"currentWeek\",\n payload\n );\n }\n const parsed = Number(normalized);\n // assertPositiveInt validates both isInteger and > 0 — no need to pre-check isInteger\n assertPositiveInt(parsed, \"currentWeek\");\n return parsed;\n }\n\n if (typeof payload === \"object\" && payload !== null) {\n const record = payload as Record<string, unknown>;\n if (\"weekNumber\" in record) {\n return parseCurrentWeekInternal(record.weekNumber, depth + 1);\n }\n if (\"currentWeek\" in record) {\n return parseCurrentWeekInternal(record.currentWeek, depth + 1);\n }\n }\n\n throw new BsuirValidationError(\n \"'currentWeek' response payload must be a positive integer\",\n \"currentWeek\",\n payload\n );\n}\n","import { assertPositiveInt } from \"../utils/guards\";\nimport type {\n FlattenedScheduleItem,\n NormalizedScheduleResponse,\n ScheduleFilterOptions,\n ScheduleItem\n} from \"../types/schedule\";\n\nfunction lessonAuditories(item: ScheduleItem): string[] {\n const { auditories } = item;\n return Array.isArray(auditories) ? auditories : [];\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 (\n typeof filter.weekNumber === \"number\" &&\n (!Array.isArray(item.weekNumber) || !item.weekNumber.includes(filter.weekNumber))\n ) {\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 (!item.lessonTypeAbbrev || !types.includes(item.lessonTypeAbbrev)) {\n return false;\n }\n }\n\n if (filter.subjectQuery) {\n const query = filter.subjectQuery.toLowerCase();\n const haystack = `${item.subject} ${item.subjectFullName} ${item.note ?? \"\"}`.toLowerCase();\n if (!haystack.includes(query)) {\n return false;\n }\n }\n\n if (filter.employeeUrlId) {\n const employeeMatch = item.employees?.some(\n (employee) => employee.urlId === filter.employeeUrlId\n );\n if (!employeeMatch) {\n return false;\n }\n }\n\n if (filter.auditory && !lessonAuditories(item).includes(filter.auditory)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Filters normalized schedule lessons by criteria.\n */\nexport function filterLessons(\n response: NormalizedScheduleResponse,\n filter: ScheduleFilterOptions\n): FlattenedScheduleItem[] {\n if (typeof filter.weekNumber === \"number\") {\n assertPositiveInt(filter.weekNumber, \"filter.weekNumber\");\n }\n if (typeof filter.subgroup === \"number\") {\n assertPositiveInt(filter.subgroup, \"filter.subgroup\");\n }\n return response.lessons.filter((item) => matchesFilter(item, filter));\n}\n","export const WEEKDAYS = [\n \"Понедельник\",\n \"Вторник\",\n \"Среда\",\n \"Четверг\",\n \"Пятница\",\n \"Суббота\"\n] as const;\n\nexport type Weekday = (typeof WEEKDAYS)[number];\n\n/** Payload from IIS legacy `last-update-date/*` endpoints (`schedule.getLastUpdateByGroup` / `getLastUpdateByEmployee`). */\nexport interface ApiDateResponse {\n lastUpdateDate: string;\n}\n\nexport interface StudentGroupShort {\n id: number;\n name: string;\n}\n\nexport type Maybe<T> = T | null;\n","import { BsuirResponseValidationError } from \"../client/errors\";\nimport { assertScheduleResponse } from \"../client/responseValidators\";\nimport { WEEKDAYS } from \"../types/common\";\nimport type {\n FlattenedLessonsByDay,\n FlattenedScheduleItem,\n LessonStudentGroup,\n NormalizedScheduleResponse,\n ScheduleItem,\n ScheduleResponse\n} from \"../types/schedule\";\n\nfunction lessonAuditories(item: ScheduleItem): string[] {\n const { auditories } = item;\n return Array.isArray(auditories) ? [...auditories] : [];\n}\n\nfunction cloneLessonStudentGroups(groups: ScheduleItem[\"studentGroups\"]): LessonStudentGroup[] {\n return groups.map((group) => ({ ...group }));\n}\n\nfunction cloneEmployees(employees: ScheduleItem[\"employees\"]): ScheduleItem[\"employees\"] {\n if (!Array.isArray(employees)) {\n return employees;\n }\n return employees.map((employee) => ({ ...employee }));\n}\n\nfunction cloneScheduleItem(item: ScheduleItem): ScheduleItem {\n return {\n ...item,\n weekNumber: Array.isArray(item.weekNumber) ? [...item.weekNumber] : item.weekNumber,\n studentGroups: cloneLessonStudentGroups(item.studentGroups),\n auditories: lessonAuditories(item),\n employees: cloneEmployees(item.employees)\n };\n}\n\nfunction deepFreezeJson<T>(value: T): T {\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n if (Object.isFrozen(value)) {\n return value;\n }\n Object.freeze(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n deepFreezeJson(item);\n }\n } else {\n for (const key of Object.keys(value)) {\n deepFreezeJson((value as Record<string, unknown>)[key]);\n }\n }\n return value;\n}\n\n// Minimal envelope check kept here (not in responseValidators) because the normalize\n// path always needs at least this much shape safety to avoid crashing on a non-object\n// payload — even when full validation is disabled. The full validator\n// `assertScheduleResponse` is the single source of truth for the complete check.\nfunction assertMinimalScheduleEnvelope(\n payload: unknown,\n endpoint: string\n): asserts payload is { schedules?: unknown; exams?: unknown } {\n if (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n throw new BsuirResponseValidationError(\n `Invalid response payload for ${endpoint}: expected object`,\n endpoint\n );\n }\n}\n\n/**\n * Transforms raw schedule response into normalized structure with flattened lessons.\n */\nexport function normalizeSchedule(\n response: ScheduleResponse,\n options?: { validate?: boolean; endpoint?: string }\n): NormalizedScheduleResponse {\n const endpoint = options?.endpoint ?? \"/schedule\";\n if (options?.validate) {\n // Full envelope validation via the single source of truth.\n assertScheduleResponse(response, endpoint);\n } else {\n // Even without full validation, refuse to normalize a non-object payload —\n // letting it through would only push a less-clear TypeError onto the caller.\n assertMinimalScheduleEnvelope(response, endpoint);\n }\n const scheduleLessons: FlattenedScheduleItem[] = [];\n const examLessons: FlattenedScheduleItem[] = [];\n const lessonsByDay = Object.fromEntries(\n WEEKDAYS.map((day) => [day, [] as FlattenedScheduleItem[]])\n ) as FlattenedLessonsByDay;\n const sourceSchedules = response.schedules ?? {};\n const normalizedSchedules: NonNullable<ScheduleResponse[\"schedules\"]> = {};\n const normalizedExams = Array.isArray(response.exams)\n ? response.exams.map((item) => cloneScheduleItem(item))\n : [];\n\n for (const day of WEEKDAYS) {\n const dayItems = sourceSchedules[day] ?? [];\n const clonedDayItems = dayItems.map((item) => cloneScheduleItem(item));\n if (Object.hasOwn(sourceSchedules, day)) {\n normalizedSchedules[day] = clonedDayItems;\n }\n\n const flattenedDayItems: FlattenedScheduleItem[] = clonedDayItems.map((item) =>\n deepFreezeJson({\n ...item,\n day,\n source: \"schedules\" as const\n })\n );\n lessonsByDay[day] = flattenedDayItems;\n scheduleLessons.push(...flattenedDayItems);\n }\n\n for (const exam of normalizedExams) {\n const flattenedExam: FlattenedScheduleItem = deepFreezeJson({\n ...exam,\n day: null,\n source: \"exams\"\n });\n examLessons.push(flattenedExam);\n }\n\n const lessons = [...scheduleLessons, ...examLessons];\n\n return {\n ...response,\n schedules: normalizedSchedules,\n exams: normalizedExams,\n lessons,\n lessonsByDay,\n scheduleLessons,\n examLessons\n };\n}\n","import { requestJson } from \"../client/http\";\nimport { assertApiDateResponse, assertScheduleResponse } from \"../client/responseValidators\";\nimport type { InternalClientConfig } from \"../client/types\";\nimport type { ApiDateResponse } from \"../types/common\";\nimport type {\n FlattenedScheduleItem,\n NormalizedScheduleResponse,\n ScheduleFilterOptions,\n ScheduleItem,\n ScheduleResponse\n} from \"../types/schedule\";\nimport { assertEmployeeUrlId, assertGroupNumber, assertPositiveInt } from \"../utils/guards\";\nimport { parseCurrentWeek } from \"../utils/week\";\nimport { filterLessons } from \"./scheduleFilter\";\nimport { normalizeSchedule } from \"./scheduleNormalize\";\nimport type { ReadOptions } from \"./types\";\n\nexport interface ScheduleModule {\n getGroup(groupNumber: string, options?: ReadOptions): Promise<NormalizedScheduleResponse>;\n getEmployee(urlId: string, options?: ReadOptions): Promise<NormalizedScheduleResponse>;\n\n getGroupFiltered(\n groupNumber: string,\n filter: ScheduleFilterOptions,\n options?: ReadOptions\n ): Promise<FlattenedScheduleItem[]>;\n\n getEmployeeFiltered(\n urlId: string,\n filter: ScheduleFilterOptions,\n options?: ReadOptions\n ): Promise<FlattenedScheduleItem[]>;\n\n getGroupExams(groupNumber: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;\n getEmployeeExams(urlId: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;\n\n // Explicit raw/envelope helpers (new explicit API)\n getGroupRaw(groupNumber: string, options?: ReadOptions): Promise<ScheduleResponse>;\n getGroupEnvelope(\n groupNumber: string,\n subgroup: number,\n options?: ReadOptions\n ): Promise<ScheduleResponse>;\n\n getEmployeeRaw(urlId: string, options?: ReadOptions): Promise<ScheduleResponse>;\n getEmployeeEnvelope(\n urlId: string,\n subgroup: number,\n options?: ReadOptions\n ): Promise<ScheduleResponse>;\n\n getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { rawEnvelope: true }\n ): Promise<ScheduleResponse>;\n getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { raw: true; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[]>;\n getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { raw: boolean; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;\n getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options?: ReadOptions & { raw?: false | undefined; rawEnvelope?: false | undefined }\n ): Promise<FlattenedScheduleItem[]>;\n\n getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { rawEnvelope: true }\n ): Promise<ScheduleResponse>;\n getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { raw: true; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[]>;\n getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { raw: boolean; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;\n getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options?: ReadOptions & { raw?: false | undefined; rawEnvelope?: false | undefined }\n ): Promise<FlattenedScheduleItem[]>;\n\n getCurrentWeek(options?: ReadOptions): Promise<number>;\n\n getLastUpdateByGroup(\n params: { groupNumber: string } | { id: number },\n options?: ReadOptions\n ): Promise<ApiDateResponse>;\n\n getLastUpdateByEmployee(\n params: { urlId: string } | { id: number },\n options?: ReadOptions\n ): Promise<ApiDateResponse>;\n}\n\nfunction filterRawSubgroupLessons(response: ScheduleResponse, subgroup: number): ScheduleItem[] {\n const items: ScheduleItem[] = [];\n const schedules = response.schedules ?? {};\n for (const dayItems of Object.values(schedules)) {\n for (const lesson of dayItems) {\n if (lesson.numSubgroup === subgroup) {\n items.push(structuredClone(lesson));\n }\n }\n }\n return items;\n}\n\nfunction filterRawSubgroupEnvelope(response: ScheduleResponse, subgroup: number): ScheduleResponse {\n const cloned = structuredClone(response);\n const schedules = cloned.schedules ?? {};\n for (const day of Object.keys(schedules) as (keyof typeof schedules)[]) {\n const items = schedules[day] ?? [];\n schedules[day] = items.filter((lesson) => lesson.numSubgroup === subgroup);\n }\n return cloned;\n}\n\n/**\n * Creates schedule API module with raw/normalized response support.\n */\nexport function createScheduleModule(config: Readonly<InternalClientConfig>): ScheduleModule {\n /**\n * Returns schedule for a student group.\n * Returns a normalized payload. Use `getGroupRaw` for the raw API envelope.\n */\n async function getGroup(\n groupNumber: string,\n options: ReadOptions = {}\n ): Promise<NormalizedScheduleResponse> {\n const resolvedOptions = options;\n assertGroupNumber(groupNumber, \"groupNumber\");\n const payload = await requestJson<unknown>(config, \"/schedule\", {\n query: { studentGroup: groupNumber },\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertScheduleResponse(value, \"/schedule\");\n }\n : undefined\n });\n const response = payload as ScheduleResponse;\n return normalizeSchedule(response, {\n validate: false,\n endpoint: \"/schedule\"\n });\n }\n\n /**\n * Returns schedule for an employee.\n * Returns a normalized payload. Use `getEmployeeRaw` for the raw API envelope.\n */\n async function getEmployee(\n urlId: string,\n options: ReadOptions = {}\n ): Promise<NormalizedScheduleResponse> {\n const resolvedOptions = options;\n assertEmployeeUrlId(urlId, \"urlId\");\n const endpoint = `/employees/schedule/${encodeURIComponent(urlId)}`;\n const payload = await requestJson<unknown>(config, endpoint, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertScheduleResponse(value, endpoint);\n }\n : undefined\n });\n const response = payload as ScheduleResponse;\n return normalizeSchedule(response, {\n validate: false,\n endpoint\n });\n }\n\n /**\n * Returns filtered schedule items for a group from normalized schedule payload.\n */\n async function getGroupFiltered(\n groupNumber: string,\n filter: ScheduleFilterOptions,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n const normalized = await getGroup(groupNumber, {\n signal: options.signal,\n cache: options.cache\n });\n return filterLessons(normalized, filter);\n }\n\n /**\n * Returns filtered schedule items for an employee from normalized schedule payload.\n */\n async function getEmployeeFiltered(\n urlId: string,\n filter: ScheduleFilterOptions,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n const normalized = await getEmployee(urlId, {\n signal: options.signal,\n cache: options.cache\n });\n return filterLessons(normalized, filter);\n }\n\n /**\n * Returns current academic week number.\n */\n async function getCurrentWeek(options: ReadOptions = {}): Promise<number> {\n const payload = await requestJson<unknown>(config, \"/schedule/current-week\", {\n signal: options.signal,\n cache: options.cache\n });\n return parseCurrentWeek(payload);\n }\n\n /**\n * Returns regular schedule lessons for a subgroup.\n *\n * Shape selection:\n * - `rawEnvelope: true` → returns the full `ScheduleResponse` with `schedules` arrays\n * filtered to the requested subgroup. Preserves `employeeDto`, `studentGroupDto`,\n * exam fields, and date ranges from the original envelope.\n * - `raw: true` (without `rawEnvelope`) → returns `ScheduleItem[]` only.\n * - default → returns flattened `FlattenedScheduleItem[]` with day/source metadata.\n */\n function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { rawEnvelope: true }\n ): Promise<ScheduleResponse>;\n function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { raw: true; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[]>;\n function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions & { raw: boolean; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;\n function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options?: ReadOptions & { raw?: false | undefined; rawEnvelope?: false | undefined }\n ): Promise<FlattenedScheduleItem[]>;\n async function getGroupBySubgroup(\n groupNumber: string,\n subgroup: number,\n options?: ReadOptions & { raw?: boolean | undefined; rawEnvelope?: boolean | undefined }\n ): Promise<FlattenedScheduleItem[] | ScheduleItem[] | ScheduleResponse> {\n const resolvedOptions = options ?? {};\n assertPositiveInt(subgroup, \"subgroup\");\n if (resolvedOptions.rawEnvelope === true) {\n const raw = await getGroupRaw(groupNumber, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache\n });\n return filterRawSubgroupEnvelope(raw, subgroup);\n }\n if (resolvedOptions.raw === true) {\n const raw = await getGroupRaw(groupNumber, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache\n });\n return filterRawSubgroupLessons(raw, subgroup);\n }\n return getGroupFiltered(groupNumber, { source: \"schedules\", subgroup }, resolvedOptions);\n }\n\n /**\n * Returns regular schedule lessons for an employee subgroup filter.\n *\n * Shape selection:\n * - `rawEnvelope: true` → returns the full `ScheduleResponse` with `schedules` arrays\n * filtered to the requested subgroup. Preserves `employeeDto`, `studentGroupDto`,\n * exam fields, and date ranges from the original envelope.\n * - `raw: true` (without `rawEnvelope`) → returns `ScheduleItem[]` only.\n * - default → returns flattened `FlattenedScheduleItem[]` with day/source metadata.\n */\n function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { rawEnvelope: true }\n ): Promise<ScheduleResponse>;\n function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { raw: true; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[]>;\n function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options: ReadOptions & { raw: boolean; rawEnvelope?: false | undefined }\n ): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;\n function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options?: ReadOptions & { raw?: false | undefined; rawEnvelope?: false | undefined }\n ): Promise<FlattenedScheduleItem[]>;\n async function getEmployeeBySubgroup(\n urlId: string,\n subgroup: number,\n options?: ReadOptions & { raw?: boolean | undefined; rawEnvelope?: boolean | undefined }\n ): Promise<FlattenedScheduleItem[] | ScheduleItem[] | ScheduleResponse> {\n const resolvedOptions = options ?? {};\n assertPositiveInt(subgroup, \"subgroup\");\n if (resolvedOptions.rawEnvelope === true) {\n const raw = await getEmployeeRaw(urlId, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache\n });\n return filterRawSubgroupEnvelope(raw, subgroup);\n }\n if (resolvedOptions.raw === true) {\n const raw = await getEmployeeRaw(urlId, {\n signal: resolvedOptions.signal,\n cache: resolvedOptions.cache\n });\n return filterRawSubgroupLessons(raw, subgroup);\n }\n return getEmployeeFiltered(urlId, { source: \"schedules\", subgroup }, resolvedOptions);\n }\n\n // Explicit helpers: raw/envelope variants to make API shape explicit.\n async function getGroupRaw(\n groupNumber: string,\n options: ReadOptions = {}\n ): Promise<ScheduleResponse> {\n assertGroupNumber(groupNumber, \"groupNumber\");\n const payload = await requestJson<unknown>(config, \"/schedule\", {\n query: { studentGroup: groupNumber },\n signal: options.signal,\n cache: options.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertScheduleResponse(value, \"/schedule\");\n }\n : undefined\n });\n return payload as ScheduleResponse;\n }\n\n async function getGroupEnvelope(\n groupNumber: string,\n subgroup: number,\n options: ReadOptions = {}\n ) {\n const raw = await getGroupRaw(groupNumber, options);\n return filterRawSubgroupEnvelope(raw, subgroup);\n }\n\n async function getEmployeeRaw(urlId: string, options: ReadOptions = {}) {\n assertEmployeeUrlId(urlId, \"urlId\");\n const endpoint = `/employees/schedule/${encodeURIComponent(urlId)}`;\n const payload = await requestJson<unknown>(config, endpoint, {\n signal: options.signal,\n cache: options.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertScheduleResponse(value, endpoint);\n }\n : undefined\n });\n return payload as ScheduleResponse;\n }\n\n async function getEmployeeEnvelope(urlId: string, subgroup: number, options: ReadOptions = {}) {\n const raw = await getEmployeeRaw(urlId, options);\n return filterRawSubgroupEnvelope(raw, subgroup);\n }\n\n return {\n getGroup,\n getEmployee,\n getGroupRaw,\n getGroupEnvelope,\n getEmployeeRaw,\n getEmployeeEnvelope,\n getGroupFiltered,\n getEmployeeFiltered,\n getGroupBySubgroup,\n getEmployeeBySubgroup,\n getCurrentWeek,\n\n /**\n * Returns exams for a group.\n */\n async getGroupExams(\n groupNumber: string,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n return getGroupFiltered(groupNumber, { source: \"exams\" }, options);\n },\n\n /**\n * Returns exams for an employee.\n */\n async getEmployeeExams(\n urlId: string,\n options: ReadOptions = {}\n ): Promise<FlattenedScheduleItem[]> {\n return getEmployeeFiltered(urlId, { source: \"exams\" }, options);\n },\n\n /**\n * Calls IIS `/last-update-date/student-group`.\n */\n async getLastUpdateByGroup(\n params: { groupNumber: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"groupNumber\" in params) {\n assertGroupNumber(params.groupNumber, \"groupNumber\");\n query = { groupNumber: params.groupNumber };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n const payload = await requestJson<unknown>(config, \"/last-update-date/student-group\", {\n query,\n signal: options.signal,\n cache: options.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertApiDateResponse(value, \"/last-update-date/student-group\");\n }\n : undefined\n });\n return payload as ApiDateResponse;\n },\n\n /**\n * Calls IIS `/last-update-date/employee`.\n */\n async getLastUpdateByEmployee(\n params: { urlId: string } | { id: number },\n options: ReadOptions = {}\n ): Promise<ApiDateResponse> {\n let query: Record<string, string | number>;\n if (\"urlId\" in params) {\n assertEmployeeUrlId(params.urlId, \"urlId\");\n query = { \"url-id\": params.urlId };\n } else {\n assertPositiveInt(params.id, \"id\");\n query = { id: params.id };\n }\n const payload = await requestJson<unknown>(config, \"/last-update-date/employee\", {\n query,\n signal: options.signal,\n cache: options.cache,\n responseValidator: config.validateResponses\n ? (value) => {\n assertApiDateResponse(value, \"/last-update-date/employee\");\n }\n : undefined\n });\n return payload as ApiDateResponse;\n }\n };\n}\n","export interface DdMmYyyyParts {\n day: number;\n month: number;\n year: number;\n}\n\n/**\n * Parses a BSUIR `dd.mm.yyyy` date string into numeric parts.\n * Returns `null` for missing, malformed, or impossible calendar dates.\n */\nexport function parseDdMmYyyyParts(value: string | null): DdMmYyyyParts | null {\n if (!value) {\n return null;\n }\n const matched = /^(\\d{2})\\.(\\d{2})\\.(\\d{4})$/.exec(value);\n if (!matched) {\n return null;\n }\n const [, dayPart, monthPart, yearPart] = matched;\n const day = Number(dayPart);\n const month = Number(monthPart);\n const year = Number(yearPart);\n if (!Number.isInteger(day) || !Number.isInteger(month) || !Number.isInteger(year)) {\n return null;\n }\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() !== month - 1 ||\n utcDate.getUTCDate() !== day\n ) {\n return null;\n }\n return { day, month, year };\n}\n\n/**\n * Parses a BSUIR `dd.mm.yyyy` date string into a UTC midnight `Date`.\n * Returns `null` for missing, malformed, or impossible calendar dates.\n */\nexport function parseDdMmYyyy(value: string | null): Date | null {\n const parts = parseDdMmYyyyParts(value);\n if (!parts) {\n return null;\n }\n return new Date(Date.UTC(parts.year, parts.month - 1, parts.day));\n}\n","import { filterLessons } from \"../modules/scheduleFilter\";\nimport { WEEKDAYS, type Weekday } from \"../types/common\";\nimport type {\n BuildScheduleDaysOptions,\n FlattenedLessonsByDay,\n FlattenedScheduleItem,\n LessonWithTime,\n NormalizedScheduleResponse,\n ScheduleDay\n} from \"../types/schedule\";\nimport { parseDdMmYyyyParts, type DdMmYyyyParts } from \"../utils/date\";\nimport { assertPositiveInt } from \"../utils/guards\";\n\nconst SUNDAY_LABEL = \"Воскресенье\";\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nfunction createEmptyLessonsByDay(): FlattenedLessonsByDay {\n return Object.fromEntries(\n WEEKDAYS.map((day) => [day, [] as FlattenedScheduleItem[]])\n ) as FlattenedLessonsByDay;\n}\n\nfunction toDayOrdinal(parts: DdMmYyyyParts): number {\n return Math.floor(Date.UTC(parts.year, parts.month - 1, parts.day) / MS_PER_DAY);\n}\n\nfunction toDateDayOrdinal(date: Date): number {\n return Math.floor(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / MS_PER_DAY);\n}\n\nfunction toDateKey(date: Date): string {\n const year = String(date.getFullYear());\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n\nfunction toLessonDateKey(value: string | null): string | null {\n const parts = parseDdMmYyyyParts(value);\n if (!parts) {\n return null;\n }\n return `${String(parts.year)}-${String(parts.month).padStart(2, \"0\")}-${String(parts.day).padStart(2, \"0\")}`;\n}\n\nfunction toLessonDayOrdinal(value: string | null): number | null {\n const parts = parseDdMmYyyyParts(value);\n return parts ? toDayOrdinal(parts) : null;\n}\n\nfunction toWeekday(date: Date): Weekday | null {\n const dayIndex = date.getDay();\n if (dayIndex < 1 || dayIndex > 6) {\n return null;\n }\n return WEEKDAYS[dayIndex - 1] ?? null;\n}\n\n/**\n * Validates and clones a Date. Only `Date` objects are accepted.\n * ISO strings like `\"2026-05-15\"` are NOT accepted to avoid UTC vs local timezone ambiguity —\n * pass an explicit `new Date(...)` instead.\n */\nfunction toDateOrThrow(value: Date, fieldName: string): Date {\n if (!(value instanceof Date)) {\n throw new TypeError(`'${fieldName}' must be a Date object`);\n }\n const cloned = new Date(value);\n if (Number.isNaN(cloned.getTime())) {\n throw new TypeError(`'${fieldName}' must be a valid Date`);\n }\n return cloned;\n}\n\n/**\n * Optional callback invoked when a lesson's `startLessonTime` or `endLessonTime`\n * cannot be parsed as `HH:MM`. Use this to surface upstream data issues that the\n * default behavior (sorting such lessons to the end, ignoring them for\n * current/next lookups) would otherwise hide.\n *\n * The callback is fired at most once per malformed value per call site. It must\n * not throw — exceptions from a hook are caught and discarded.\n *\n * @example\n * ```ts\n * const sorted = sortLessonsByTime(lessons, {\n * onInvalidTime: (info) => logger.warn(\"malformed lesson time\", info),\n * });\n * ```\n */\nexport type InvalidLessonTimeHook = (info: {\n field: \"startLessonTime\" | \"endLessonTime\";\n value: string;\n lesson: LessonWithTime;\n}) => void;\n\nfunction parseTimeToMinutes(value: string): number | null {\n const matched = /^(\\d{1,2}):(\\d{2})$/.exec(value);\n if (!matched) {\n return null;\n }\n const [, hourPart, minutePart] = matched;\n const hours = Number(hourPart);\n const minutes = Number(minutePart);\n if (\n !Number.isInteger(hours) ||\n !Number.isInteger(minutes) ||\n hours < 0 ||\n hours > 23 ||\n minutes < 0 ||\n minutes > 59\n ) {\n return null;\n }\n return hours * 60 + minutes;\n}\n\nfunction reportInvalidTime(\n lesson: LessonWithTime,\n field: \"startLessonTime\" | \"endLessonTime\",\n value: string,\n hook: InvalidLessonTimeHook | undefined\n): void {\n if (!hook) {\n return;\n }\n try {\n hook({ field, value, lesson });\n } catch {\n // Hook failures must not break schedule sorting/lookup.\n }\n}\n\nfunction parseLessonTime(\n lesson: LessonWithTime,\n field: \"startLessonTime\" | \"endLessonTime\",\n hook: InvalidLessonTimeHook | undefined\n): number | null {\n const value = lesson[field];\n const parsed = parseTimeToMinutes(value);\n if (parsed === null && value.length > 0) {\n reportInvalidTime(lesson, field, value, hook);\n }\n return parsed;\n}\n\nfunction isWithinLessonDateRange(\n targetOrdinal: number,\n startDate: string | null,\n endDate: string | null\n): boolean {\n const startOrdinal = toLessonDayOrdinal(startDate);\n const endOrdinal = toLessonDayOrdinal(endDate);\n if (startOrdinal !== null && targetOrdinal < startOrdinal) {\n return false;\n }\n if (endOrdinal !== null && targetOrdinal > endOrdinal) {\n return false;\n }\n return true;\n}\n\nfunction usesFourWeekCycle(response: NormalizedScheduleResponse): boolean {\n const values = response.scheduleLessons\n .flatMap((lesson) => lesson.weekNumber ?? [])\n .filter((value): value is number => Number.isInteger(value) && value > 0);\n if (values.length === 0) {\n return false;\n }\n return values.every((value) => value >= 1 && value <= 4);\n}\n\nfunction inferWeekNumberForDate(response: NormalizedScheduleResponse, date: Date): number | null {\n const startDateParts = parseDdMmYyyyParts(response.startDate);\n if (!startDateParts) {\n return null;\n }\n const startOrdinal = toDayOrdinal(startDateParts);\n const targetOrdinal = toDateDayOrdinal(date);\n const diffDays = targetOrdinal - startOrdinal;\n if (diffDays < 0) {\n return null;\n }\n const absoluteWeek = Math.floor(diffDays / 7) + 1;\n if (!usesFourWeekCycle(response)) {\n return absoluteWeek;\n }\n return ((absoluteWeek - 1) % 4) + 1;\n}\n\n/**\n * Returns lessons scheduled for a specific calendar date.\n *\n * Date matching uses local calendar date semantics from the provided `date` object.\n * Lessons with `dateLesson` are matched directly by date key.\n * Weekly schedule lessons are matched by weekday and inferred week number.\n * Exams without `dateLesson` but with a date range appear on every day within that range —\n * in practice BSUIR exams have `dateLesson` set, so this branch handles edge cases only.\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param date - Target date. Must be a `Date` object — local calendar date is used.\n * @returns Lessons for that date sorted by start time.\n *\n * @example\n * ```ts\n * const lessons = getLessonsForDate(schedule, new Date(2026, 1, 10));\n * ```\n */\nexport function getLessonsForDate(\n normalizedSchedule: NormalizedScheduleResponse,\n date: Date\n): FlattenedScheduleItem[] {\n const targetDate = toDateOrThrow(date, \"date\");\n const targetDateKey = toDateKey(targetDate);\n const targetOrdinal = toDateDayOrdinal(targetDate);\n const targetWeekday = toWeekday(targetDate);\n const inferredWeekNumber = inferWeekNumberForDate(normalizedSchedule, targetDate);\n\n return sortLessonsByTime(\n normalizedSchedule.lessons.filter((lesson) => {\n const lessonDateKey = toLessonDateKey(lesson.dateLesson);\n if (lessonDateKey) {\n return lessonDateKey === targetDateKey;\n }\n\n if (lesson.source === \"exams\") {\n if (!lesson.startLessonDate && !lesson.endLessonDate) {\n return false;\n }\n return isWithinLessonDateRange(targetOrdinal, lesson.startLessonDate, lesson.endLessonDate);\n }\n\n if (lesson.day !== targetWeekday) {\n return false;\n }\n\n if (\n typeof inferredWeekNumber === \"number\" &&\n Array.isArray(lesson.weekNumber) &&\n lesson.weekNumber.length > 0 &&\n !lesson.weekNumber.includes(inferredWeekNumber)\n ) {\n return false;\n }\n\n return isWithinLessonDateRange(targetOrdinal, lesson.startLessonDate, lesson.endLessonDate);\n })\n );\n}\n\n/**\n * Returns lessons for the local current day.\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param now - Optional current moment override for deterministic usage.\n * @returns Lessons for today sorted by start time.\n *\n * @example\n * ```ts\n * const todayLessons = getTodayLessons(schedule, new Date());\n * ```\n */\nexport function getTodayLessons(\n normalizedSchedule: NormalizedScheduleResponse,\n now: Date = new Date()\n): FlattenedScheduleItem[] {\n const current = toDateOrThrow(now, \"now\");\n return getLessonsForDate(normalizedSchedule, current);\n}\n\n/**\n * Returns lessons for the next local calendar day.\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param now - Optional current moment override for deterministic usage.\n * @returns Lessons for tomorrow sorted by start time.\n *\n * @example\n * ```ts\n * const tomorrowLessons = getTomorrowLessons(schedule, new Date());\n * ```\n */\nexport function getTomorrowLessons(\n normalizedSchedule: NormalizedScheduleResponse,\n now: Date = new Date()\n): FlattenedScheduleItem[] {\n const current = toDateOrThrow(now, \"now\");\n const tomorrow = new Date(current);\n tomorrow.setDate(current.getDate() + 1);\n return getLessonsForDate(normalizedSchedule, tomorrow);\n}\n\n/**\n * Returns regular schedule lessons for a specific week number.\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param weekNumber - Positive week number to match.\n * @returns Matching regular lessons sorted by start time.\n *\n * @example\n * ```ts\n * const secondWeek = getLessonsForWeek(schedule, 2);\n * ```\n */\nexport function getLessonsForWeek(\n normalizedSchedule: NormalizedScheduleResponse,\n weekNumber: number\n): FlattenedScheduleItem[] {\n assertPositiveInt(weekNumber, \"weekNumber\");\n return sortLessonsByTime(\n filterLessons(normalizedSchedule, {\n source: \"schedules\",\n weekNumber\n })\n );\n}\n\n/**\n * Returns a new lessons array sorted by start time, then by end time.\n * Invalid or missing time strings are kept at the end in original order.\n *\n * @param lessons - Lessons to sort.\n * @returns New sorted lessons array.\n *\n * @example\n * ```ts\n * const sorted = sortLessonsByTime(response.lessons);\n * ```\n */\nexport function sortLessonsByTime<T extends LessonWithTime>(\n lessons: readonly T[],\n options?: { onInvalidTime?: InvalidLessonTimeHook | undefined }\n): T[] {\n const hook = options?.onInvalidTime;\n return lessons\n .map((lesson, index) => ({\n lesson,\n index,\n start: parseLessonTime(lesson, \"startLessonTime\", hook),\n end: parseLessonTime(lesson, \"endLessonTime\", hook)\n }))\n .toSorted((a, b) => {\n const startDiff =\n (a.start ?? Number.POSITIVE_INFINITY) - (b.start ?? Number.POSITIVE_INFINITY);\n if (startDiff !== 0) {\n return startDiff;\n }\n const endDiff = (a.end ?? Number.POSITIVE_INFINITY) - (b.end ?? Number.POSITIVE_INFINITY);\n if (endDiff !== 0) {\n return endDiff;\n }\n return a.index - b.index;\n })\n .map((entry) => entry.lesson);\n}\n\n/**\n * Groups lessons by weekday.\n *\n * Lessons with `day === null` (e.g., date-specific exams) are omitted from groups.\n *\n * @param lessons - Lessons to group.\n * @returns Weekday map with arrays sorted by time for each day.\n *\n * @example\n * ```ts\n * const grouped = groupLessonsByDay(response.lessons);\n * ```\n */\nexport function groupLessonsByDay(\n lessons: readonly FlattenedScheduleItem[]\n): FlattenedLessonsByDay {\n const grouped = createEmptyLessonsByDay();\n for (const lesson of lessons) {\n if (!lesson.day) {\n continue;\n }\n grouped[lesson.day].push(lesson);\n }\n for (const weekday of WEEKDAYS) {\n grouped[weekday] = sortLessonsByTime(grouped[weekday]);\n }\n return grouped;\n}\n\n/**\n * Returns the lesson active at the specified moment.\n *\n * Time comparison uses local hours/minutes: `startLessonTime <= now < endLessonTime`.\n * A lesson is not considered active exactly at its end time.\n *\n * @param lessons - Lessons for one day (or any same-day set).\n * @param now - Optional current moment override.\n * @returns Current lesson or `null` when none is active.\n *\n * @example\n * ```ts\n * const current = getCurrentLesson(todayLessons, new Date());\n * ```\n */\nexport function getCurrentLesson<T extends LessonWithTime>(\n lessons: readonly T[],\n now: Date = new Date(),\n options?: { onInvalidTime?: InvalidLessonTimeHook | undefined }\n): T | null {\n const current = toDateOrThrow(now, \"now\");\n const nowMinutes = current.getHours() * 60 + current.getMinutes();\n const hook = options?.onInvalidTime;\n for (const lesson of sortLessonsByTime(lessons, { onInvalidTime: hook })) {\n const start = parseLessonTime(lesson, \"startLessonTime\", hook);\n const end = parseLessonTime(lesson, \"endLessonTime\", hook);\n if (start === null || end === null || end <= start) {\n continue;\n }\n if (nowMinutes >= start && nowMinutes < end) {\n return lesson;\n }\n }\n return null;\n}\n\n/**\n * Returns the nearest upcoming lesson after the specified moment.\n *\n * A lesson starting exactly at `now` is not returned (use {@link getCurrentLesson} instead).\n * A lesson that just ended at `now` is also not returned as current — the next one is.\n *\n * @param lessons - Lessons for one day (or any same-day set).\n * @param now - Optional current moment override.\n * @returns Next lesson or `null` when there is no upcoming lesson.\n *\n * @example\n * ```ts\n * const next = getNextLesson(todayLessons, new Date());\n * ```\n */\nexport function getNextLesson<T extends LessonWithTime>(\n lessons: readonly T[],\n now: Date = new Date(),\n options?: { onInvalidTime?: InvalidLessonTimeHook | undefined }\n): T | null {\n const current = toDateOrThrow(now, \"now\");\n const nowMinutes = current.getHours() * 60 + current.getMinutes();\n const hook = options?.onInvalidTime;\n for (const lesson of sortLessonsByTime(lessons, { onInvalidTime: hook })) {\n const start = parseLessonTime(lesson, \"startLessonTime\", hook);\n if (start === null) {\n continue;\n }\n if (start > nowMinutes) {\n return lesson;\n }\n }\n return null;\n}\n\n/**\n * Builds lightweight day models for schedule screens.\n *\n * Uses local calendar dates. Returns day objects with lessons,\n * \"today\" marker, and optional current/next lesson metadata for the current day.\n * `currentLesson` and `nextLesson` are only computed for today (`isToday === true`).\n *\n * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.\n * @param options - Builder options for date range and filtering.\n * @returns Day models ready for direct UI rendering.\n *\n * @example\n * ```ts\n * const days = buildScheduleDays(schedule, { days: 7, includeEmptyDays: false });\n * // Use days?.lessons for in-day progress:\n * const current = getCurrentLesson(days?.lessons ?? []);\n * ```\n */\nexport function buildScheduleDays(\n normalizedSchedule: NormalizedScheduleResponse,\n options: BuildScheduleDaysOptions = {}\n): ScheduleDay[] {\n const now = toDateOrThrow(options.now ?? new Date(), \"options.now\");\n const startDate = toDateOrThrow(options.startDate ?? now, \"options.startDate\");\n const days = options.days ?? 7;\n assertPositiveInt(days, \"options.days\");\n\n const includeEmptyDays = options.includeEmptyDays ?? true;\n const includeCurrentAndNextLessons = options.includeCurrentAndNextLessons ?? true;\n const onInvalidTime = options.onInvalidTime as InvalidLessonTimeHook | undefined;\n const todayKey = toDateKey(now);\n const rangeStart = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());\n\n const scheduleDays: ScheduleDay[] = [];\n for (let index = 0; index < days; index += 1) {\n const dayDate = new Date(rangeStart);\n dayDate.setDate(rangeStart.getDate() + index);\n\n const lessons = getLessonsForDate(normalizedSchedule, dayDate);\n const dateKey = toDateKey(dayDate);\n const isToday = dateKey === todayKey;\n const hasLessons = lessons.length > 0;\n\n if (!includeEmptyDays && !hasLessons) {\n continue;\n }\n\n const weekday = toWeekday(dayDate);\n scheduleDays.push({\n date: dayDate,\n dateKey,\n weekday,\n weekdayLabel: weekday ?? SUNDAY_LABEL,\n lessons,\n isToday,\n hasLessons,\n currentLesson:\n includeCurrentAndNextLessons && isToday\n ? getCurrentLesson(lessons, now, { onInvalidTime })\n : null,\n nextLesson:\n includeCurrentAndNextLessons && isToday\n ? getNextLesson(lessons, now, { onInvalidTime })\n : null\n });\n }\n\n return scheduleDays;\n}\n","import type { Employee } from \"../types/employee\";\nimport type { FlattenedScheduleItem } from \"../types/schedule\";\n\n/**\n * Formats lesson start and end time as a range string.\n *\n * @returns `\"HH:MM–HH:MM\"`, or an empty string when both times are missing.\n *\n * @example\n * ```ts\n * formatLessonTimeRange(lesson); // \"10:00–11:30\"\n * ```\n */\nexport function formatLessonTimeRange(\n lesson: Pick<FlattenedScheduleItem, \"startLessonTime\" | \"endLessonTime\">\n): string {\n const start = lesson.startLessonTime.trim();\n const end = lesson.endLessonTime.trim();\n if (start && end) return `${start}\\u2013${end}`;\n if (start) return start;\n if (end) return end;\n return \"\";\n}\n\n/**\n * Returns the lesson type abbreviation.\n *\n * @returns The abbreviation string (e.g. `\"ЛК\"`, `\"ПЗ\"`, `\"ЛР\"`),\n * or an empty string when the field is absent.\n *\n * @example\n * ```ts\n * formatLessonType(lesson); // \"ЛК\"\n * ```\n */\nexport function formatLessonType(lesson: Pick<FlattenedScheduleItem, \"lessonTypeAbbrev\">): string {\n return lesson.lessonTypeAbbrev?.trim() ?? \"\";\n}\n\n/**\n * Returns a human-readable subgroup label.\n *\n * @returns `\"1 подгруппа\"` / `\"2 подгруппа\"` etc., or `\"\"` when `numSubgroup` is 0.\n *\n * @example\n * ```ts\n * formatLessonSubgroup(lesson); // \"1 подгруппа\"\n * ```\n */\nexport function formatLessonSubgroup(lesson: Pick<FlattenedScheduleItem, \"numSubgroup\">): string {\n if (!lesson.numSubgroup) return \"\";\n return `${String(lesson.numSubgroup)} \\u043F\\u043E\\u0434\\u0433\\u0440\\u0443\\u043F\\u043F\\u0430`;\n}\n\n/**\n * Formats the week numbers list as a compact string.\n *\n * Returns `\"кажд. нед.\"` when `weekNumber` is `null` or empty.\n * Returns `\"1, 3 нед.\"` for a specific set of weeks.\n *\n * @example\n * ```ts\n * formatLessonWeekNumbers({ weekNumber: [1, 3] }); // \"1, 3 нед.\"\n * formatLessonWeekNumbers({ weekNumber: null }); // \"кажд. нед.\"\n * ```\n */\nexport function formatLessonWeekNumbers(lesson: Pick<FlattenedScheduleItem, \"weekNumber\">): string {\n if (!lesson.weekNumber || lesson.weekNumber.length === 0) {\n return \"\\u043A\\u0430\\u0436\\u0434. \\u043D\\u0435\\u0434.\";\n }\n return `${lesson.weekNumber.join(\", \")} \\u043D\\u0435\\u0434.`;\n}\n\n/**\n * Formats a list of auditories as a comma-separated string.\n *\n * @returns `\"101-2, 102-3\"` or `\"\"` when the list is empty.\n *\n * @example\n * ```ts\n * formatLessonAuditories(lesson); // \"101-2, 102-3\"\n * ```\n */\nexport function formatLessonAuditories(lesson: Pick<FlattenedScheduleItem, \"auditories\">): string {\n return lesson.auditories.join(\", \");\n}\n\n/**\n * Formats an employee name as `\"Фамилия И.О.\"`.\n *\n * Falls back gracefully when first or middle name is missing.\n *\n * @example\n * ```ts\n * formatEmployeeShortName(employee); // \"Иванов И.И.\"\n * ```\n */\nexport function formatEmployeeShortName(\n employee: Pick<Employee, \"lastName\" | \"firstName\" | \"middleName\">\n): string {\n const lastName = employee.lastName.trim();\n const first = employee.firstName.trim();\n const middle = employee.middleName.trim();\n\n let initials = \"\";\n if (first.length > 0) initials += `${first.charAt(0)}.`;\n if (middle.length > 0) initials += `${middle.charAt(0)}.`;\n\n return initials.length > 0 ? `${lastName} ${initials}` : lastName;\n}\n\n/**\n * Formats all lesson employees as short names joined by `\", \"`.\n *\n * @returns `\"Иванов И.И., Петров П.П.\"` or `\"\"` when employees list is absent.\n *\n * @example\n * ```ts\n * formatLessonEmployees(lesson); // \"Иванов И.И.\"\n * ```\n */\nexport function formatLessonEmployees(lesson: Pick<FlattenedScheduleItem, \"employees\">): string {\n if (!lesson.employees || lesson.employees.length === 0) return \"\";\n return lesson.employees.map((employee) => formatEmployeeShortName(employee)).join(\", \");\n}\n","import type { InternalClientConfig } from \"../client/types\";\nimport type { Speciality } from \"../types/catalog\";\nimport { createListModule, type ListModule } from \"./createListModule\";\n\n/**\n * Creates API module for `/specialities`.\n */\nexport function createSpecialitiesModule(\n config: Readonly<InternalClientConfig>\n): ListModule<Speciality> {\n return createListModule<Speciality>(config, \"/specialities\");\n}\n","import { BsuirConfigurationError } from \"./errors\";\nimport type { BsuirClientOptions, InternalClientConfig } from \"./types\";\nimport {\n createAnnouncementsModule,\n createAuditoriesModule,\n createDepartmentsModule,\n createEmployeesModule,\n createFacultiesModule,\n createGroupsModule,\n createScheduleModule,\n createSpecialitiesModule\n} from \"../modules\";\n\nconst DEFAULT_BASE_URL = \"https://iis.bsuir.by/api/v1\";\nconst DEFAULT_ALLOWED_BASE_URL_HOSTS = [\"iis.bsuir.by\"];\nconst DEFAULT_MAX_RESPONSE_BYTES = 5_000_000;\nconst INVALID_HEADER_LINE_BREAK = /[\\r\\n]/;\n\n// Prevents setTimeout() integer overflow (max safe value ~24.8 days).\n// 5 minutes is a generous upper bound for any HTTP request in this context.\nconst MAX_TIMEOUT_MS = 300_000;\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 BsuirConfigurationError(\n \"Global fetch is unavailable. Provide 'fetch' in createBsuirClient options.\"\n );\n }\n\n return globalThis.fetch;\n}\n\nfunction assertIntegerOption(\n value: number | undefined,\n name: string,\n minInclusive: number\n): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < minInclusive) {\n throw new BsuirConfigurationError(\n `'${name}' must be an integer greater than or equal to ${String(minInclusive)}`\n );\n }\n return value;\n}\n\nfunction normalizeHostname(rawHostname: string): string {\n const trimmed = rawHostname.trim().toLowerCase().replace(/\\.+$/, \"\");\n if (trimmed.length === 0) {\n return \"\";\n }\n if (trimmed.startsWith(\"[\") && trimmed.endsWith(\"]\")) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\nfunction isLoopbackHost(hostname: string): boolean {\n return hostname === \"localhost\" || hostname === \"::1\" || hostname.startsWith(\"127.\");\n}\n\n// Per-request `options.headers` go through the platform `Headers` constructor in\n// requestJson, which rejects CR/LF/NUL with a runtime error. We only pre-validate\n// configuration-level header values (e.g. `userAgent`) here so misconfiguration is\n// surfaced as a structured `BsuirConfigurationError` at client construction time\n// rather than at first request.\nfunction assertSafeHeaderValue(value: string | undefined, optionName: string): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (INVALID_HEADER_LINE_BREAK.test(value) || value.includes(\"\\0\")) {\n throw new BsuirConfigurationError(`'${optionName}' must not contain CR, LF or NUL characters`);\n }\n return value;\n}\n\nfunction normalizeBaseUrl(\n rawBaseUrl: string,\n allowInsecureHttp: boolean,\n allowedHosts: readonly string[]\n): string {\n let parsed: URL;\n try {\n parsed = new URL(rawBaseUrl);\n } catch {\n throw new BsuirConfigurationError(\"'baseUrl' must be a valid absolute URL\");\n }\n\n if (parsed.username || parsed.password) {\n throw new BsuirConfigurationError(\"'baseUrl' must not include credentials\");\n }\n\n if (parsed.search || parsed.hash) {\n throw new BsuirConfigurationError(\"'baseUrl' must not include query string or hash\");\n }\n\n if (parsed.protocol !== \"https:\" && !(allowInsecureHttp && parsed.protocol === \"http:\")) {\n throw new BsuirConfigurationError(\n \"'baseUrl' must use HTTPS. Set 'allowInsecureHttp: true' only for trusted local/test endpoints.\"\n );\n }\n\n if (parsed.port.length > 0) {\n throw new BsuirConfigurationError(\"'baseUrl' must not include an explicit port\");\n }\n\n const normalizedAllowedHosts = new Set(\n allowedHosts.map((host) => normalizeHostname(host)).filter((host) => host.length > 0)\n );\n if (normalizedAllowedHosts.size === 0) {\n throw new BsuirConfigurationError(\"'allowedBaseUrlHosts' must contain at least one hostname\");\n }\n\n // When allowInsecureHttp is enabled, only loopback hosts are permitted in the allowlist —\n // a non-loopback host in the list would create a confusing configuration where the user\n // appears to have opted in to plaintext HTTP for a public host. Plaintext HTTP must stay\n // strictly local/test only.\n if (allowInsecureHttp) {\n const nonLoopback = [...normalizedAllowedHosts].filter((host) => !isLoopbackHost(host));\n if (nonLoopback.length > 0) {\n throw new BsuirConfigurationError(\n `'allowInsecureHttp: true' requires every host in 'allowedBaseUrlHosts' to be loopback (localhost/127.x/[::1]); got non-loopback: ${nonLoopback.join(\", \")}`\n );\n }\n }\n\n const host = normalizeHostname(parsed.hostname);\n if (host.length === 0) {\n throw new BsuirConfigurationError(\"'baseUrl' must include a valid hostname\");\n }\n if (parsed.protocol === \"http:\" && allowInsecureHttp && !isLoopbackHost(host)) {\n throw new BsuirConfigurationError(\n \"'allowInsecureHttp: true' is restricted to localhost/loopback HTTP endpoints only\"\n );\n }\n if (!normalizedAllowedHosts.has(host)) {\n throw new BsuirConfigurationError(\n `'baseUrl' host '${host}' is not allowed. Allowed hosts: ${[...normalizedAllowedHosts].join(\", \")}`\n );\n }\n\n parsed.hostname = host;\n const normalizedPath = parsed.pathname.replace(/\\/+$/, \"\");\n return `${parsed.origin}${normalizedPath}`;\n}\n\nfunction createInternalConfig(options: BsuirClientOptions): InternalClientConfig {\n const timeoutMs = assertIntegerOption(options.timeoutMs, \"timeoutMs\", 1) ?? 10_000;\n if (timeoutMs > MAX_TIMEOUT_MS) {\n throw new BsuirConfigurationError(\n `'timeoutMs' must not exceed ${String(MAX_TIMEOUT_MS)}ms (5 minutes)`\n );\n }\n\n const retries = assertIntegerOption(options.retries, \"retries\", 0) ?? 1;\n const retryDelayMs = assertIntegerOption(options.retryDelayMs, \"retryDelayMs\", 0) ?? 300;\n const retryMaxDelayMs =\n assertIntegerOption(options.retryMaxDelayMs, \"retryMaxDelayMs\", 0) ?? 3000;\n const cacheTtlMs = assertIntegerOption(options.cache?.ttlMs, \"cache.ttlMs\", 1);\n const cacheMaxEntries =\n assertIntegerOption(options.cache?.maxEntries, \"cache.maxEntries\", 1) ?? 200;\n const maxResponseBytes =\n assertIntegerOption(options.maxResponseBytes, \"maxResponseBytes\", 1) ??\n DEFAULT_MAX_RESPONSE_BYTES;\n const allowInsecureHttp = options.allowInsecureHttp ?? false;\n const allowedBaseUrlHosts = options.allowedBaseUrlHosts ?? DEFAULT_ALLOWED_BASE_URL_HOSTS;\n\n if (retryDelayMs > retryMaxDelayMs) {\n throw new BsuirConfigurationError(\n \"'retryDelayMs' must be less than or equal to 'retryMaxDelayMs'\"\n );\n }\n\n return {\n baseUrl: normalizeBaseUrl(\n options.baseUrl ?? DEFAULT_BASE_URL,\n allowInsecureHttp,\n allowedBaseUrlHosts\n ),\n fetchImpl: resolveFetch(options.fetch),\n signal: options.signal,\n timeoutMs,\n retries,\n retryDelayMs,\n retryMaxDelayMs,\n retryJitter: options.retryJitter ?? true,\n userAgent: assertSafeHeaderValue(options.userAgent, \"userAgent\"),\n cacheTtlMs,\n cacheMaxEntries,\n dedupeInFlight: options.dedupeInFlight ?? false,\n maxResponseBytes,\n validateResponses: options.validateResponses ?? false,\n hooks: options.hooks ?? {},\n responseCache: new Map(),\n inFlightRequests: new Map()\n };\n}\n\n/**\n * Fully-typed public shape of the BSUIR API client.\n * All module types are inlined so API Extractor never needs to reach into private helpers.\n *\n * Use explicit raw/envelope helpers on the `schedule` module to obtain the\n * API's raw `ScheduleResponse` when required (e.g. `getGroupRaw`,\n * `getEmployeeRaw`, `getGroupEnvelope`). The default `getGroup`/`getEmployee`\n * return a normalized payload.\n */\nexport interface BsuirClientShape {\n schedule: ReturnType<typeof createScheduleModule>;\n groups: ReturnType<typeof createGroupsModule>;\n employees: ReturnType<typeof createEmployeesModule>;\n faculties: ReturnType<typeof createFacultiesModule>;\n departments: ReturnType<typeof createDepartmentsModule>;\n specialities: ReturnType<typeof createSpecialitiesModule>;\n announcements: ReturnType<typeof createAnnouncementsModule>;\n auditories: ReturnType<typeof createAuditoriesModule>;\n}\n\n/**\n * Creates a configured BSUIR IIS API client.\n *\n * The `schedule` module exposes both normalized and explicit raw/envelope\n * helpers. Use `getGroup` / `getEmployee` for normalized payloads and\n * `getGroupRaw` / `getEmployeeRaw` when you need the original API envelope.\n */\nexport function createBsuirClient(options: BsuirClientOptions = {}): BsuirClientShape {\n const config = createInternalConfig(options);\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 };\n}\n\n/**\n * Public client contract returned by `createBsuirClient`.\n */\nexport type BsuirClient = BsuirClientShape;\n"],"mappings":"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","names":["lessonAuditories","deepFreezeJson","host"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bsuir-iis-api",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Type-safe ESM SDK for BSUIR IIS API",
5
5
  "type": "module",
6
6
  "author": "kotru21",
@@ -44,11 +44,13 @@
44
44
  "typecheck": "tsc --noEmit",
45
45
  "lint": "eslint .",
46
46
  "lint:fix": "eslint . --fix",
47
+ "format:check": "prettier --check .",
47
48
  "test": "vitest run",
49
+ "test:browser": "vitest run --config vitest.browser.config.ts",
48
50
  "test:live": "vitest run test/integration/live-api.contract.test.ts",
49
51
  "test:coverage": "vitest run --coverage",
50
52
  "check": "npm run lint && npm run typecheck && npm run test",
51
- "check:full": "npm run lint && npm run typecheck && npm run test:coverage",
53
+ "check:full": "npm run lint && npm run typecheck && npm run format:check && npm run test:coverage",
52
54
  "api:report": "api-extractor run --local",
53
55
  "api:report:check": "api-extractor run",
54
56
  "prepublishOnly": "npm run build",
@@ -57,20 +59,21 @@
57
59
  "devDependencies": {
58
60
  "@changesets/cli": "2.31.0",
59
61
  "@eslint/js": "10.0.1",
60
- "@microsoft/api-extractor": "7.58.7",
61
- "@types/node": "25.9.1",
62
- "@typescript-eslint/eslint-plugin": "8.59.4",
63
- "@typescript-eslint/parser": "8.59.4",
64
- "@vitest/coverage-v8": "4.1.7",
65
- "eslint": "10.4.0",
62
+ "@microsoft/api-extractor": "7.58.8",
63
+ "@vitest/browser-playwright": "4.1.8",
64
+ "@types/node": "25.9.2",
65
+ "@typescript-eslint/eslint-plugin": "8.61.0",
66
+ "@typescript-eslint/parser": "8.61.0",
67
+ "@vitest/coverage-v8": "4.1.8",
68
+ "eslint": "10.4.1",
66
69
  "eslint-config-prettier": "10.1.8",
67
- "eslint-plugin-jsdoc": "^63.0.0",
68
- "eslint-plugin-unicorn": "^64.0.0",
70
+ "eslint-plugin-jsdoc": "^63.0.2",
71
+ "eslint-plugin-unicorn": "^65.0.1",
69
72
  "globals": "17.6.0",
70
73
  "prettier": "3.8.3",
71
74
  "tsup": "8.5.1",
72
75
  "typescript": "6.0.3",
73
- "typescript-eslint": "8.59.4",
74
- "vitest": "4.1.7"
76
+ "typescript-eslint": "8.61.0",
77
+ "vitest": "4.1.8"
75
78
  }
76
79
  }