bsuir-iis-api 0.10.0 → 0.11.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.
@@ -13,6 +13,26 @@ export { Announcement }
13
13
  export { Announcement as Announcement_alias_1 }
14
14
  export { Announcement as Announcement_alias_2 }
15
15
 
16
+ /**
17
+ * Options accepted by `announcements.byEmployee` and `announcements.byDepartment`.
18
+ *
19
+ * The BSUIR IIS API responds with HTTP 404 when no announcements exist for an
20
+ * employee or department. By default this SDK converts that 404 into an empty
21
+ * array. Set `treat404AsEmpty: false` to receive a `BsuirApiError` instead —
22
+ * useful when you want to distinguish "entity not found" from "entity has no
23
+ * announcements" at the call site.
24
+ */
25
+ export declare interface AnnouncementReadOptions extends ReadOptions {
26
+ /**
27
+ * When `true` (the default), HTTP 404 responses from the announcements endpoint
28
+ * are converted to an empty array. When `false`, the underlying `BsuirApiError`
29
+ * is rethrown.
30
+ *
31
+ * @defaultValue true
32
+ */
33
+ treat404AsEmpty?: boolean;
34
+ }
35
+
16
36
  /** Payload from IIS legacy `last-update-date/*` endpoints (`schedule.getLastUpdateByGroup` / `getLastUpdateByEmployee`). */
17
37
  declare interface ApiDateResponse {
18
38
  lastUpdateDate: string;
@@ -21,18 +41,39 @@ export { ApiDateResponse }
21
41
  export { ApiDateResponse as ApiDateResponse_alias_1 }
22
42
  export { ApiDateResponse as ApiDateResponse_alias_2 }
23
43
 
44
+ /**
45
+ * Asserts that payload matches `{ lastUpdateDate: string }`.
46
+ */
24
47
  export declare function assertApiDateResponse(payload: unknown, endpoint: string): asserts payload is ApiDateResponse;
25
48
 
49
+ /**
50
+ * Asserts that payload is an array response.
51
+ */
26
52
  export declare function assertArrayResponse(payload: unknown, endpoint: string): asserts payload is unknown[];
27
53
 
54
+ /**
55
+ * Asserts that an employee urlId is a non-empty slug.
56
+ */
28
57
  export declare function assertEmployeeUrlId(value: unknown, fieldName?: string): asserts value is string;
29
58
 
59
+ /**
60
+ * Asserts that a group number is a non-empty string containing only digits.
61
+ */
30
62
  export declare function assertGroupNumber(value: unknown, fieldName?: string): asserts value is string;
31
63
 
64
+ /**
65
+ * Asserts that a value is a non-empty string.
66
+ */
32
67
  export declare function assertNonEmptyString(value: unknown, fieldName: string): asserts value is string;
33
68
 
69
+ /**
70
+ * Asserts that a value is a positive integer.
71
+ */
34
72
  export declare function assertPositiveInt(value: unknown, fieldName: string): asserts value is number;
35
73
 
74
+ /**
75
+ * Asserts that payload is a schedule response envelope.
76
+ */
36
77
  export declare function assertScheduleResponse(payload: unknown, endpoint: string): asserts payload is ScheduleResponse;
37
78
 
38
79
  declare interface Auditory {
@@ -78,9 +119,8 @@ export { BsuirApiError as BsuirApiError_alias_1 }
78
119
 
79
120
  /**
80
121
  * Public client contract returned by `createBsuirClient`.
81
- * Use `BsuirClientShape<true>` or `BsuirClientShape<false>` for typed overloads.
82
122
  */
83
- declare type BsuirClient = ReturnType<typeof createBsuirClient>;
123
+ declare type BsuirClient = BsuirClientShape;
84
124
  export { BsuirClient }
85
125
  export { BsuirClient as BsuirClient_alias_1 }
86
126
 
@@ -98,6 +138,7 @@ declare interface BsuirClientOptions {
98
138
  * Allows using `http://` for `baseUrl`.
99
139
  *
100
140
  * Keep disabled unless you explicitly need local/non-TLS endpoints in tests.
141
+ * When enabled, `http://` is allowed only for localhost/loopback hosts.
101
142
  *
102
143
  * @defaultValue false
103
144
  */
@@ -127,9 +168,9 @@ declare interface BsuirClientOptions {
127
168
  * Global `AbortSignal` that cancels **all** requests made by this client
128
169
  * instance. Per-call signals are combined with this one.
129
170
  *
130
- * Note: caching and in-flight deduplication are disabled for requests that
131
- * carry a signal (per-call or global) to prevent stale data from being
132
- * returned after cancellation.
171
+ * Note: caching is disabled only when the signal is already aborted at the
172
+ * time the request is made. A live (non-aborted) global signal is fine:
173
+ * caching remains enabled and in-flight deduplication can still be used.
133
174
  */
134
175
  signal?: AbortSignal;
135
176
  /**
@@ -185,11 +226,19 @@ declare interface BsuirClientOptions {
185
226
  *
186
227
  * When configured, responses are stored in a `Map` keyed by the full request
187
228
  * URL. Cache hits skip the network entirely and fire `onResponse` with
188
- * `fromCache: true`. The cache uses a true LRU eviction policy — the
189
- * least-recently-*read* entry is evicted first when `maxEntries` is exceeded.
229
+ * `fromCache: true`. The cache uses `Map` insertion order as LRU:
230
+ * cache reads "touch" entries and eviction removes oldest keys first.
231
+ *
232
+ * Caching is automatically skipped for requests where the relevant
233
+ * `AbortSignal` (per-call or global) is already aborted, to prevent
234
+ * serving stale data after cancellation.
235
+ *
236
+ * Caching is also automatically skipped when request headers include
237
+ * credentials/private identity data such as `Authorization`, `Cookie`,
238
+ * `Proxy-Authorization`, or `X-API-Key`.
190
239
  *
191
- * Caching is automatically skipped for requests that carry an `AbortSignal`
192
- * (per-call or global) to prevent serving stale data after cancellation.
240
+ * For server-side multi-tenant apps, prefer one client instance per identity
241
+ * (per user/session/token) to avoid accidental data sharing.
193
242
  *
194
243
  * @example
195
244
  * ```ts
@@ -201,15 +250,17 @@ declare interface BsuirClientOptions {
201
250
  */
202
251
  cache?: CacheOptions;
203
252
  /**
204
- * Enables in-flight GET request deduplication by full URL.
253
+ * Enables in-flight GET request deduplication by method + URL + headers.
205
254
  *
206
255
  * When two identical GET requests are made concurrently, only the first one
207
256
  * hits the network; the second one awaits the same `Promise`. This prevents
208
257
  * duplicate API calls in scenarios like parallel component rendering.
209
258
  *
210
- * Disabled automatically when the request carries an `AbortSignal`.
259
+ * Disabled automatically when the relevant `AbortSignal` is already aborted.
260
+ * Also disabled for per-call signals, non-default cache modes, and requests
261
+ * with private credential headers.
211
262
  *
212
- * @defaultValue true
263
+ * @defaultValue false
213
264
  */
214
265
  dedupeInFlight?: boolean;
215
266
  /**
@@ -224,14 +275,14 @@ declare interface BsuirClientOptions {
224
275
  * Enables runtime shape validation of API responses.
225
276
  *
226
277
  * When `true`, each response is checked against the expected TypeScript type
227
- * at runtime. An `BsuirApiError` is thrown if the payload does not match,
278
+ * at runtime. A `BsuirResponseValidationError` is thrown if the payload does not match,
228
279
  * which makes integration issues with the upstream API visible immediately
229
280
  * instead of causing silent type-cast bugs later.
230
281
  *
231
282
  * **Recommended to enable during development and in test environments.**
232
283
  * Can be left `false` in production for a small performance gain.
233
284
  *
234
- * @defaultValue true
285
+ * @defaultValue false
235
286
  *
236
287
  * @example
237
288
  * ```ts
@@ -262,34 +313,6 @@ declare interface BsuirClientOptions {
262
313
  * ```
263
314
  */
264
315
  hooks?: ClientHooks;
265
- /**
266
- * Controls the default return type of `schedule.getGroup` and
267
- * `schedule.getEmployee` when the per-call `raw` option is omitted.
268
- *
269
- * - `false` (default) — returns `NormalizedScheduleResponse` with flattened
270
- * `lessons`, `lessonsByDay`, `scheduleLessons`, and `examLessons` arrays.
271
- * - `true` — returns the raw `ScheduleResponse` exactly as received from the
272
- * BSUIR IIS API.
273
- *
274
- * A per-call `raw` option always takes precedence over this default.
275
- *
276
- * @defaultValue false
277
- *
278
- * @example
279
- * ```ts
280
- * // Normalized (default):
281
- * const client = createBsuirClient();
282
- * const norm = await client.schedule.getGroup("053503"); // NormalizedScheduleResponse
283
- *
284
- * // Raw by default:
285
- * const rawClient = createBsuirClient({ defaultRaw: true });
286
- * const raw = await rawClient.schedule.getGroup("053503"); // ScheduleResponse
287
- *
288
- * // Per-call override (always wins):
289
- * const override = await client.schedule.getGroup("053503", { raw: true }); // ScheduleResponse
290
- * ```
291
- */
292
- defaultRaw?: boolean;
293
316
  }
294
317
  export { BsuirClientOptions }
295
318
  export { BsuirClientOptions as BsuirClientOptions_alias_1 }
@@ -298,29 +321,13 @@ export { BsuirClientOptions as BsuirClientOptions_alias_1 }
298
321
  * Fully-typed public shape of the BSUIR API client.
299
322
  * All module types are inlined so API Extractor never needs to reach into private helpers.
300
323
  *
301
- * `TRawDefault` controls the default return type of
302
- * `schedule.getGroup` / `schedule.getEmployee` when the per-call `raw` option is omitted:
303
- * - `false` (default) returns `NormalizedScheduleResponse`
304
- * - `true` → returns `ScheduleResponse` (raw API payload)
305
- *
306
- * Per-call `raw` always takes precedence over this default.
307
- *
308
- * @example
309
- * ```ts
310
- * // Default (normalized):
311
- * const client = createBsuirClient();
312
- * const norm = await client.schedule.getGroup("053503"); // NormalizedScheduleResponse
313
- *
314
- * // Raw by default:
315
- * const rawClient = createBsuirClient({ defaultRaw: true });
316
- * const raw = await rawClient.schedule.getGroup("053503"); // ScheduleResponse
317
- *
318
- * // Per-call override (always wins):
319
- * const override = await client.schedule.getGroup("053503", { raw: true }); // ScheduleResponse
320
- * ```
324
+ * Use explicit raw/envelope helpers on the `schedule` module to obtain the
325
+ * API's raw `ScheduleResponse` when required (e.g. `getGroupRaw`,
326
+ * `getEmployeeRaw`, `getGroupEnvelope`). The default `getGroup`/`getEmployee`
327
+ * return a normalized payload.
321
328
  */
322
- export declare interface BsuirClientShape<TRawDefault extends boolean> {
323
- schedule: ReturnType<typeof createScheduleModule<TRawDefault>>;
329
+ declare interface BsuirClientShape {
330
+ schedule: ReturnType<typeof createScheduleModule>;
324
331
  groups: ReturnType<typeof createGroupsModule>;
325
332
  employees: ReturnType<typeof createEmployeesModule>;
326
333
  faculties: ReturnType<typeof createFacultiesModule>;
@@ -329,6 +336,8 @@ export declare interface BsuirClientShape<TRawDefault extends boolean> {
329
336
  announcements: ReturnType<typeof createAnnouncementsModule>;
330
337
  auditories: ReturnType<typeof createAuditoriesModule>;
331
338
  }
339
+ export { BsuirClientShape }
340
+ export { BsuirClientShape as BsuirClientShape_alias_1 }
332
341
 
333
342
  declare class BsuirConfigurationError extends Error {
334
343
  constructor(message: string);
@@ -343,6 +352,15 @@ declare class BsuirNetworkError extends Error {
343
352
  export { BsuirNetworkError }
344
353
  export { BsuirNetworkError as BsuirNetworkError_alias_1 }
345
354
 
355
+ declare class BsuirResponsePayloadTooLargeError extends Error {
356
+ readonly status: number;
357
+ readonly endpoint: string;
358
+ readonly maxResponseBytes: number;
359
+ constructor(message: string, status: number, endpoint: string, maxResponseBytes: number);
360
+ }
361
+ export { BsuirResponsePayloadTooLargeError }
362
+ export { BsuirResponsePayloadTooLargeError as BsuirResponsePayloadTooLargeError_alias_1 }
363
+
346
364
  declare class BsuirResponseValidationError extends Error {
347
365
  readonly endpoint: string;
348
366
  constructor(message: string, endpoint: string);
@@ -359,7 +377,9 @@ export { BsuirTimeoutError }
359
377
  export { BsuirTimeoutError as BsuirTimeoutError_alias_1 }
360
378
 
361
379
  declare class BsuirValidationError extends Error {
362
- constructor(message: string);
380
+ readonly field: string | undefined;
381
+ readonly value: unknown;
382
+ constructor(message: string, field?: string, value?: unknown);
363
383
  }
364
384
  export { BsuirValidationError }
365
385
  export { BsuirValidationError as BsuirValidationError_alias_1 }
@@ -372,8 +392,75 @@ export { BuildingNumber }
372
392
  export { BuildingNumber as BuildingNumber_alias_1 }
373
393
  export { BuildingNumber as BuildingNumber_alias_2 }
374
394
 
395
+ /**
396
+ * Builds lightweight day models for schedule screens.
397
+ *
398
+ * Uses local calendar dates. Returns day objects with lessons,
399
+ * "today" marker, and optional current/next lesson metadata for the current day.
400
+ * `currentLesson` and `nextLesson` are only computed for today (`isToday === true`).
401
+ *
402
+ * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.
403
+ * @param options - Builder options for date range and filtering.
404
+ * @returns Day models ready for direct UI rendering.
405
+ *
406
+ * @example
407
+ * ```ts
408
+ * const days = buildScheduleDays(schedule, { days: 7, includeEmptyDays: false });
409
+ * // Use days?.lessons for in-day progress:
410
+ * const current = getCurrentLesson(days?.lessons ?? []);
411
+ * ```
412
+ */
413
+ declare function buildScheduleDays(normalizedSchedule: NormalizedScheduleResponse, options?: BuildScheduleDaysOptions): ScheduleDay[];
414
+ export { buildScheduleDays }
415
+ export { buildScheduleDays as buildScheduleDays_alias_1 }
416
+ export { buildScheduleDays as buildScheduleDays_alias_2 }
417
+
418
+ /**
419
+ * Options for {@link buildScheduleDays}.
420
+ */
421
+ declare interface BuildScheduleDaysOptions {
422
+ /** Reference moment for "today" and current/next lesson detection. Defaults to `new Date()`. */
423
+ now?: Date;
424
+ /** First day of the range. Defaults to `now`. */
425
+ startDate?: Date;
426
+ /** Number of days to build. Must be a positive integer. Defaults to `7`. */
427
+ days?: number;
428
+ /**
429
+ * Whether to include days with no lessons.
430
+ * @defaultValue `true`
431
+ */
432
+ includeEmptyDays?: boolean;
433
+ /**
434
+ * Whether to compute `currentLesson` and `nextLesson` for today.
435
+ * @defaultValue `true`
436
+ */
437
+ includeCurrentAndNextLessons?: boolean;
438
+ /**
439
+ * Callback fired once per lesson whose `startLessonTime` or `endLessonTime`
440
+ * cannot be parsed as `HH:MM`. The lesson is otherwise still included in the
441
+ * day's `lessons` array but sorted to the end. Use this to surface upstream
442
+ * data issues that would otherwise be silently ignored.
443
+ *
444
+ * Errors thrown by the hook are caught and discarded.
445
+ */
446
+ onInvalidTime?: (info: {
447
+ field: "startLessonTime" | "endLessonTime";
448
+ value: string;
449
+ lesson: {
450
+ startLessonTime: string;
451
+ endLessonTime: string;
452
+ };
453
+ }) => void;
454
+ }
455
+ export { BuildScheduleDaysOptions }
456
+ export { BuildScheduleDaysOptions as BuildScheduleDaysOptions_alias_1 }
457
+ export { BuildScheduleDaysOptions as BuildScheduleDaysOptions_alias_2 }
458
+
375
459
  /**
376
460
  * Builds absolute endpoint URL from base URL, path and query params.
461
+ *
462
+ * Query parameters are sorted by key to produce deterministic URLs - important
463
+ * for cache key stability and for reproducible request fingerprints.
377
464
  */
378
465
  export declare function buildUrl(baseUrl: string, path: string, query?: QueryParams): string;
379
466
 
@@ -413,15 +500,12 @@ declare interface ClientHooks {
413
500
  export { ClientHooks }
414
501
  export { ClientHooks as ClientHooks_alias_1 }
415
502
 
503
+ /**
504
+ *
505
+ */
416
506
  declare function createAnnouncementsModule(config: Readonly<InternalClientConfig>): {
417
- /**
418
- * Lists announcements for an employee. IIS may return HTTP `404` or `400` (no list / endpoint quirks); the SDK maps those to `[]`.
419
- */
420
- byEmployee(urlId: string, options?: ReadOptions): Promise<Announcement[]>;
421
- /**
422
- * Lists announcements for a department. IIS may return HTTP `404` or `400` (no list / endpoint quirks); the SDK maps those to `[]`.
423
- */
424
- byDepartment(id: number, options?: ReadOptions): Promise<Announcement[]>;
507
+ byEmployee(urlId: string, options?: AnnouncementReadOptions): Promise<Announcement[]>;
508
+ byDepartment(id: number, options?: AnnouncementReadOptions): Promise<Announcement[]>;
425
509
  };
426
510
  export { createAnnouncementsModule }
427
511
  export { createAnnouncementsModule as createAnnouncementsModule_alias_1 }
@@ -429,74 +513,46 @@ export { createAnnouncementsModule as createAnnouncementsModule_alias_1 }
429
513
  /**
430
514
  * Creates API module for `/auditories`.
431
515
  */
432
- declare function createAuditoriesModule(config: Readonly<InternalClientConfig>): {
433
- listAll(options?: ReadOptions): Promise<Auditory[]>;
434
- };
516
+ declare function createAuditoriesModule(config: Readonly<InternalClientConfig>): ListModule<Auditory>;
435
517
  export { createAuditoriesModule }
436
518
  export { createAuditoriesModule as createAuditoriesModule_alias_1 }
437
519
 
438
520
  /**
439
521
  * Creates a configured BSUIR IIS API client.
440
522
  *
441
- * Pass `{ defaultRaw: true }` to switch the default return shape of
442
- * `schedule.getGroup` and `schedule.getEmployee` from `NormalizedScheduleResponse`
443
- * to the raw `ScheduleResponse`. Per-call `raw` option always takes precedence.
444
- *
445
- * @example
446
- * ```ts
447
- * // Normalized (default):
448
- * const client = createBsuirClient();
449
- *
450
- * // Raw by default:
451
- * const rawClient = createBsuirClient({ defaultRaw: true });
452
- *
453
- * // Custom fetch + timeout:
454
- * const client = createBsuirClient({ fetch: myFetch, timeoutMs: 5_000 });
455
- * ```
523
+ * The `schedule` module exposes both normalized and explicit raw/envelope
524
+ * helpers. Use `getGroup` / `getEmployee` for normalized payloads and
525
+ * `getGroupRaw` / `getEmployeeRaw` when you need the original API envelope.
456
526
  */
457
- declare function createBsuirClient(options: BsuirClientOptions & {
458
- defaultRaw: true;
459
- }): BsuirClientShape<true>;
460
-
461
- declare function createBsuirClient(options?: BsuirClientOptions & {
462
- defaultRaw?: false | undefined;
463
- }): BsuirClientShape<false>;
527
+ declare function createBsuirClient(options?: BsuirClientOptions): BsuirClientShape;
464
528
  export { createBsuirClient }
465
529
  export { createBsuirClient as createBsuirClient_alias_1 }
466
530
 
467
531
  /**
468
532
  * Creates API module for `/departments`.
469
533
  */
470
- declare function createDepartmentsModule(config: Readonly<InternalClientConfig>): {
471
- listAll(options?: ReadOptions): Promise<Department[]>;
472
- };
534
+ declare function createDepartmentsModule(config: Readonly<InternalClientConfig>): ListModule<Department>;
473
535
  export { createDepartmentsModule }
474
536
  export { createDepartmentsModule as createDepartmentsModule_alias_1 }
475
537
 
476
538
  /**
477
539
  * Creates API module for `/employees/all`.
478
540
  */
479
- declare function createEmployeesModule(config: Readonly<InternalClientConfig>): {
480
- listAll(options?: ReadOptions): Promise<EmployeeCatalogItem[]>;
481
- };
541
+ declare function createEmployeesModule(config: Readonly<InternalClientConfig>): ListModule<EmployeeCatalogItem>;
482
542
  export { createEmployeesModule }
483
543
  export { createEmployeesModule as createEmployeesModule_alias_1 }
484
544
 
485
545
  /**
486
546
  * Creates API module for `/faculties`.
487
547
  */
488
- declare function createFacultiesModule(config: Readonly<InternalClientConfig>): {
489
- listAll(options?: ReadOptions): Promise<Faculty[]>;
490
- };
548
+ declare function createFacultiesModule(config: Readonly<InternalClientConfig>): ListModule<Faculty>;
491
549
  export { createFacultiesModule }
492
550
  export { createFacultiesModule as createFacultiesModule_alias_1 }
493
551
 
494
552
  /**
495
553
  * Creates API module for `/student-groups`.
496
554
  */
497
- declare function createGroupsModule(config: Readonly<InternalClientConfig>): {
498
- listAll(options?: ReadOptions): Promise<StudentGroupCatalogItem[]>;
499
- };
555
+ declare function createGroupsModule(config: Readonly<InternalClientConfig>): ListModule<StudentGroupCatalogItem>;
500
556
  export { createGroupsModule }
501
557
  export { createGroupsModule as createGroupsModule_alias_1 }
502
558
 
@@ -505,59 +561,12 @@ export declare function createJsonResponse({ status, headers, body }: MockRespon
505
561
  /**
506
562
  * Creates a simple catalog-like module exposing `listAll()` for a fixed endpoint.
507
563
  */
508
- export declare function createListModule<T>(config: Readonly<InternalClientConfig>, endpoint: string): {
509
- /**
510
- * Returns all items from the configured endpoint.
511
- */
512
- listAll(options?: ReadOptions): Promise<T[]>;
513
- };
564
+ export declare function createListModule<T>(config: Readonly<InternalClientConfig>, endpoint: string): ListModule<T>;
514
565
 
515
566
  /**
516
567
  * Creates schedule API module with raw/normalized response support.
517
568
  */
518
- declare function createScheduleModule<TRawDefault extends boolean>(config: Readonly<InternalClientConfig<TRawDefault>>): {
519
- getGroup: <TRaw extends boolean | undefined = undefined>(groupNumber: string, options?: ReadOptions & {
520
- raw?: TRaw;
521
- }) => Promise<ScheduleResponseByRawOption<TRaw, TRawDefault>>;
522
- getEmployee: <TRaw extends boolean | undefined = undefined>(urlId: string, options?: ReadOptions & {
523
- raw?: TRaw;
524
- }) => Promise<ScheduleResponseByRawOption<TRaw, TRawDefault>>;
525
- getGroupFiltered: (groupNumber: string, filter: ScheduleFilterOptions, options?: ReadOptions) => Promise<FlattenedScheduleItem[]>;
526
- getEmployeeFiltered: (urlId: string, filter: ScheduleFilterOptions, options?: ReadOptions) => Promise<FlattenedScheduleItem[]>;
527
- /**
528
- * Returns exams for a group.
529
- */
530
- getGroupExams(groupNumber: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
531
- /**
532
- * Returns exams for an employee.
533
- */
534
- getEmployeeExams(urlId: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
535
- /**
536
- * Returns regular schedule lessons of a specific subgroup for a group.
537
- */
538
- getGroupBySubgroup(groupNumber: string, subgroup: number, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
539
- /**
540
- * Returns regular schedule lessons of a specific subgroup for an employee.
541
- */
542
- getEmployeeBySubgroup(urlId: string, subgroup: number, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
543
- getCurrentWeek: (options?: ReadOptions) => Promise<number>;
544
- /**
545
- * Calls IIS `/last-update-date/student-group`.
546
- */
547
- getLastUpdateByGroup(params: {
548
- groupNumber: string;
549
- } | {
550
- id: number;
551
- }, options?: ReadOptions): Promise<ApiDateResponse>;
552
- /**
553
- * Calls IIS `/last-update-date/employee`.
554
- */
555
- getLastUpdateByEmployee(params: {
556
- urlId: string;
557
- } | {
558
- id: number;
559
- }, options?: ReadOptions): Promise<ApiDateResponse>;
560
- };
569
+ declare function createScheduleModule(config: Readonly<InternalClientConfig>): ScheduleModule;
561
570
  export { createScheduleModule }
562
571
  export { createScheduleModule as createScheduleModule_alias_1 }
563
572
  export { createScheduleModule as createScheduleModule_alias_2 }
@@ -565,9 +574,7 @@ export { createScheduleModule as createScheduleModule_alias_2 }
565
574
  /**
566
575
  * Creates API module for `/specialities`.
567
576
  */
568
- declare function createSpecialitiesModule(config: Readonly<InternalClientConfig>): {
569
- listAll(options?: ReadOptions): Promise<Speciality[]>;
570
- };
577
+ declare function createSpecialitiesModule(config: Readonly<InternalClientConfig>): ListModule<Speciality>;
571
578
  export { createSpecialitiesModule }
572
579
  export { createSpecialitiesModule as createSpecialitiesModule_alias_1 }
573
580
 
@@ -664,12 +671,269 @@ export { FlattenedScheduleItem }
664
671
  export { FlattenedScheduleItem as FlattenedScheduleItem_alias_1 }
665
672
  export { FlattenedScheduleItem as FlattenedScheduleItem_alias_2 }
666
673
 
674
+ /**
675
+ * Formats an employee name as `"Фамилия И.О."`.
676
+ *
677
+ * Falls back gracefully when first or middle name is missing.
678
+ *
679
+ * @example
680
+ * ```ts
681
+ * formatEmployeeShortName(employee); // "Иванов И.И."
682
+ * ```
683
+ */
684
+ declare function formatEmployeeShortName(employee: Pick<Employee, "lastName" | "firstName" | "middleName">): string;
685
+ export { formatEmployeeShortName }
686
+ export { formatEmployeeShortName as formatEmployeeShortName_alias_1 }
687
+ export { formatEmployeeShortName as formatEmployeeShortName_alias_2 }
688
+
689
+ /**
690
+ * Formats a list of auditories as a comma-separated string.
691
+ *
692
+ * @returns `"101-2, 102-3"` or `""` when the list is empty.
693
+ *
694
+ * @example
695
+ * ```ts
696
+ * formatLessonAuditories(lesson); // "101-2, 102-3"
697
+ * ```
698
+ */
699
+ declare function formatLessonAuditories(lesson: Pick<FlattenedScheduleItem, "auditories">): string;
700
+ export { formatLessonAuditories }
701
+ export { formatLessonAuditories as formatLessonAuditories_alias_1 }
702
+ export { formatLessonAuditories as formatLessonAuditories_alias_2 }
703
+
704
+ /**
705
+ * Formats all lesson employees as short names joined by `", "`.
706
+ *
707
+ * @returns `"Иванов И.И., Петров П.П."` or `""` when employees list is absent.
708
+ *
709
+ * @example
710
+ * ```ts
711
+ * formatLessonEmployees(lesson); // "Иванов И.И."
712
+ * ```
713
+ */
714
+ declare function formatLessonEmployees(lesson: Pick<FlattenedScheduleItem, "employees">): string;
715
+ export { formatLessonEmployees }
716
+ export { formatLessonEmployees as formatLessonEmployees_alias_1 }
717
+ export { formatLessonEmployees as formatLessonEmployees_alias_2 }
718
+
719
+ /**
720
+ * Returns a human-readable subgroup label.
721
+ *
722
+ * @returns `"1 подгруппа"` / `"2 подгруппа"` etc., or `""` when `numSubgroup` is 0.
723
+ *
724
+ * @example
725
+ * ```ts
726
+ * formatLessonSubgroup(lesson); // "1 подгруппа"
727
+ * ```
728
+ */
729
+ declare function formatLessonSubgroup(lesson: Pick<FlattenedScheduleItem, "numSubgroup">): string;
730
+ export { formatLessonSubgroup }
731
+ export { formatLessonSubgroup as formatLessonSubgroup_alias_1 }
732
+ export { formatLessonSubgroup as formatLessonSubgroup_alias_2 }
733
+
734
+ /**
735
+ * Formats lesson start and end time as a range string.
736
+ *
737
+ * @returns `"HH:MM–HH:MM"`, or an empty string when both times are missing.
738
+ *
739
+ * @example
740
+ * ```ts
741
+ * formatLessonTimeRange(lesson); // "10:00–11:30"
742
+ * ```
743
+ */
744
+ declare function formatLessonTimeRange(lesson: Pick<FlattenedScheduleItem, "startLessonTime" | "endLessonTime">): string;
745
+ export { formatLessonTimeRange }
746
+ export { formatLessonTimeRange as formatLessonTimeRange_alias_1 }
747
+ export { formatLessonTimeRange as formatLessonTimeRange_alias_2 }
748
+
749
+ /**
750
+ * Returns the lesson type abbreviation.
751
+ *
752
+ * @returns The abbreviation string (e.g. `"ЛК"`, `"ПЗ"`, `"ЛР"`),
753
+ * or an empty string when the field is absent.
754
+ *
755
+ * @example
756
+ * ```ts
757
+ * formatLessonType(lesson); // "ЛК"
758
+ * ```
759
+ */
760
+ declare function formatLessonType(lesson: Pick<FlattenedScheduleItem, "lessonTypeAbbrev">): string;
761
+ export { formatLessonType }
762
+ export { formatLessonType as formatLessonType_alias_1 }
763
+ export { formatLessonType as formatLessonType_alias_2 }
764
+
765
+ /**
766
+ * Formats the week numbers list as a compact string.
767
+ *
768
+ * Returns `"кажд. нед."` when `weekNumber` is `null` or empty.
769
+ * Returns `"1, 3 нед."` for a specific set of weeks.
770
+ *
771
+ * @example
772
+ * ```ts
773
+ * formatLessonWeekNumbers({ weekNumber: [1, 3] }); // "1, 3 нед."
774
+ * formatLessonWeekNumbers({ weekNumber: null }); // "кажд. нед."
775
+ * ```
776
+ */
777
+ declare function formatLessonWeekNumbers(lesson: Pick<FlattenedScheduleItem, "weekNumber">): string;
778
+ export { formatLessonWeekNumbers }
779
+ export { formatLessonWeekNumbers as formatLessonWeekNumbers_alias_1 }
780
+ export { formatLessonWeekNumbers as formatLessonWeekNumbers_alias_2 }
781
+
782
+ /**
783
+ * Returns the lesson active at the specified moment.
784
+ *
785
+ * Time comparison uses local hours/minutes: `startLessonTime <= now < endLessonTime`.
786
+ * A lesson is not considered active exactly at its end time.
787
+ *
788
+ * @param lessons - Lessons for one day (or any same-day set).
789
+ * @param now - Optional current moment override.
790
+ * @returns Current lesson or `null` when none is active.
791
+ *
792
+ * @example
793
+ * ```ts
794
+ * const current = getCurrentLesson(todayLessons, new Date());
795
+ * ```
796
+ */
797
+ declare function getCurrentLesson<T extends LessonWithTime>(lessons: readonly T[], now?: Date, options?: {
798
+ onInvalidTime?: InvalidLessonTimeHook | undefined;
799
+ }): T | null;
800
+ export { getCurrentLesson }
801
+ export { getCurrentLesson as getCurrentLesson_alias_1 }
802
+ export { getCurrentLesson as getCurrentLesson_alias_2 }
803
+
804
+ /**
805
+ * Returns lessons scheduled for a specific calendar date.
806
+ *
807
+ * Date matching uses local calendar date semantics from the provided `date` object.
808
+ * Lessons with `dateLesson` are matched directly by date key.
809
+ * Weekly schedule lessons are matched by weekday and inferred week number.
810
+ * Exams without `dateLesson` but with a date range appear on every day within that range —
811
+ * in practice BSUIR exams have `dateLesson` set, so this branch handles edge cases only.
812
+ *
813
+ * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.
814
+ * @param date - Target date. Must be a `Date` object — local calendar date is used.
815
+ * @returns Lessons for that date sorted by start time.
816
+ *
817
+ * @example
818
+ * ```ts
819
+ * const lessons = getLessonsForDate(schedule, new Date(2026, 1, 10));
820
+ * ```
821
+ */
822
+ declare function getLessonsForDate(normalizedSchedule: NormalizedScheduleResponse, date: Date): FlattenedScheduleItem[];
823
+ export { getLessonsForDate }
824
+ export { getLessonsForDate as getLessonsForDate_alias_1 }
825
+ export { getLessonsForDate as getLessonsForDate_alias_2 }
826
+
827
+ /**
828
+ * Returns regular schedule lessons for a specific week number.
829
+ *
830
+ * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.
831
+ * @param weekNumber - Positive week number to match.
832
+ * @returns Matching regular lessons sorted by start time.
833
+ *
834
+ * @example
835
+ * ```ts
836
+ * const secondWeek = getLessonsForWeek(schedule, 2);
837
+ * ```
838
+ */
839
+ declare function getLessonsForWeek(normalizedSchedule: NormalizedScheduleResponse, weekNumber: number): FlattenedScheduleItem[];
840
+ export { getLessonsForWeek }
841
+ export { getLessonsForWeek as getLessonsForWeek_alias_1 }
842
+ export { getLessonsForWeek as getLessonsForWeek_alias_2 }
843
+
844
+ /**
845
+ * Returns a cleanup callback for manually merged signals, when available.
846
+ */
847
+ export declare function getMergedSignalCleanup(signal: AbortSignal): (() => void) | undefined;
848
+
849
+ /**
850
+ * Returns the nearest upcoming lesson after the specified moment.
851
+ *
852
+ * A lesson starting exactly at `now` is not returned (use {@link getCurrentLesson} instead).
853
+ * A lesson that just ended at `now` is also not returned as current — the next one is.
854
+ *
855
+ * @param lessons - Lessons for one day (or any same-day set).
856
+ * @param now - Optional current moment override.
857
+ * @returns Next lesson or `null` when there is no upcoming lesson.
858
+ *
859
+ * @example
860
+ * ```ts
861
+ * const next = getNextLesson(todayLessons, new Date());
862
+ * ```
863
+ */
864
+ declare function getNextLesson<T extends LessonWithTime>(lessons: readonly T[], now?: Date, options?: {
865
+ onInvalidTime?: InvalidLessonTimeHook | undefined;
866
+ }): T | null;
867
+ export { getNextLesson }
868
+ export { getNextLesson as getNextLesson_alias_1 }
869
+ export { getNextLesson as getNextLesson_alias_2 }
870
+
871
+ /**
872
+ * Calculates retry behavior for the current attempt.
873
+ *
874
+ * If server-provided `Retry-After` exceeds a sane safety bound, retry is rejected
875
+ * to avoid long caller stalls caused by hostile or misconfigured upstreams.
876
+ */
877
+ export declare function getRetryDecision(config: Readonly<InternalClientConfig>, attempt: number, retryAfterHeader?: string | null): RetryDecision;
878
+
667
879
  /**
668
880
  * Calculates retry delay using `Retry-After` when present, otherwise exponential backoff.
669
881
  */
670
882
  export declare function getRetryDelayMs(config: Readonly<InternalClientConfig>, attempt: number, retryAfterHeader?: string | null): number;
671
883
 
672
- export declare interface InternalClientConfig<TRawDefault extends boolean = boolean> {
884
+ /**
885
+ * Returns lessons for the local current day.
886
+ *
887
+ * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.
888
+ * @param now - Optional current moment override for deterministic usage.
889
+ * @returns Lessons for today sorted by start time.
890
+ *
891
+ * @example
892
+ * ```ts
893
+ * const todayLessons = getTodayLessons(schedule, new Date());
894
+ * ```
895
+ */
896
+ declare function getTodayLessons(normalizedSchedule: NormalizedScheduleResponse, now?: Date): FlattenedScheduleItem[];
897
+ export { getTodayLessons }
898
+ export { getTodayLessons as getTodayLessons_alias_1 }
899
+ export { getTodayLessons as getTodayLessons_alias_2 }
900
+
901
+ /**
902
+ * Returns lessons for the next local calendar day.
903
+ *
904
+ * @param normalizedSchedule - Normalized schedule payload from {@link normalizeSchedule}.
905
+ * @param now - Optional current moment override for deterministic usage.
906
+ * @returns Lessons for tomorrow sorted by start time.
907
+ *
908
+ * @example
909
+ * ```ts
910
+ * const tomorrowLessons = getTomorrowLessons(schedule, new Date());
911
+ * ```
912
+ */
913
+ declare function getTomorrowLessons(normalizedSchedule: NormalizedScheduleResponse, now?: Date): FlattenedScheduleItem[];
914
+ export { getTomorrowLessons }
915
+ export { getTomorrowLessons as getTomorrowLessons_alias_1 }
916
+ export { getTomorrowLessons as getTomorrowLessons_alias_2 }
917
+
918
+ /**
919
+ * Groups lessons by weekday.
920
+ *
921
+ * Lessons with `day === null` (e.g., date-specific exams) are omitted from groups.
922
+ *
923
+ * @param lessons - Lessons to group.
924
+ * @returns Weekday map with arrays sorted by time for each day.
925
+ *
926
+ * @example
927
+ * ```ts
928
+ * const grouped = groupLessonsByDay(response.lessons);
929
+ * ```
930
+ */
931
+ declare function groupLessonsByDay(lessons: readonly FlattenedScheduleItem[]): FlattenedLessonsByDay;
932
+ export { groupLessonsByDay }
933
+ export { groupLessonsByDay as groupLessonsByDay_alias_1 }
934
+ export { groupLessonsByDay as groupLessonsByDay_alias_2 }
935
+
936
+ export declare interface InternalClientConfig {
673
937
  baseUrl: string;
674
938
  fetchImpl: typeof globalThis.fetch;
675
939
  signal: AbortSignal | undefined;
@@ -688,12 +952,35 @@ export declare interface InternalClientConfig<TRawDefault extends boolean = bool
688
952
  responseCache: Map<string, {
689
953
  expiresAt: number;
690
954
  value: unknown;
691
- accessedAt: number;
692
955
  }>;
693
956
  inFlightRequests: Map<string, Promise<unknown>>;
694
- defaultRaw: TRawDefault;
695
957
  }
696
958
 
959
+ /**
960
+ * Optional callback invoked when a lesson's `startLessonTime` or `endLessonTime`
961
+ * cannot be parsed as `HH:MM`. Use this to surface upstream data issues that the
962
+ * default behavior (sorting such lessons to the end, ignoring them for
963
+ * current/next lookups) would otherwise hide.
964
+ *
965
+ * The callback is fired at most once per malformed value per call site. It must
966
+ * not throw — exceptions from a hook are caught and discarded.
967
+ *
968
+ * @example
969
+ * ```ts
970
+ * const sorted = sortLessonsByTime(lessons, {
971
+ * onInvalidTime: (info) => logger.warn("malformed lesson time", info),
972
+ * });
973
+ * ```
974
+ */
975
+ export declare type InvalidLessonTimeHook = (info: {
976
+ field: "startLessonTime" | "endLessonTime";
977
+ value: string;
978
+ lesson: LessonWithTime;
979
+ }) => void;
980
+
981
+ /**
982
+ * Returns `true` if the error object represents an abort/cancellation.
983
+ */
697
984
  export declare function isAbortError(error: unknown): boolean;
698
985
 
699
986
  declare interface LessonStudentGroup {
@@ -707,6 +994,19 @@ export { LessonStudentGroup }
707
994
  export { LessonStudentGroup as LessonStudentGroup_alias_1 }
708
995
  export { LessonStudentGroup as LessonStudentGroup_alias_2 }
709
996
 
997
+ /**
998
+ * Minimal time fields required for lesson-time helpers.
999
+ * Used as a constraint for generic helpers like {@link sortLessonsByTime}.
1000
+ */
1001
+ declare type LessonWithTime = Pick<FlattenedScheduleItem, "startLessonTime" | "endLessonTime">;
1002
+ export { LessonWithTime }
1003
+ export { LessonWithTime as LessonWithTime_alias_1 }
1004
+ export { LessonWithTime as LessonWithTime_alias_2 }
1005
+
1006
+ export declare interface ListModule<T> {
1007
+ listAll(options?: ReadOptions): Promise<T[]>;
1008
+ }
1009
+
710
1010
  declare type Maybe<T> = T | null;
711
1011
  export { Maybe }
712
1012
  export { Maybe as Maybe_alias_1 }
@@ -716,15 +1016,8 @@ export { Maybe as Maybe_alias_2 }
716
1016
  * Combines multiple abort signals and/or a timeout into a single signal.
717
1017
  * When `AbortSignal.any` exists at runtime, delegates to the platform implementation.
718
1018
  * Otherwise uses a manual merge so all signals are respected.
719
- *
720
- * @param signalsOrFirst - Array of signals to merge, or a single signal (legacy signature)
721
- * @param timeoutMs - Optional timeout in milliseconds to include as an additional signal
722
- *
723
- * @remarks
724
- * Calling with no signals and no timeout (e.g. `mergeSignals([])`) returns a signal
725
- * that is never aborted — the caller is responsible for not doing this intentionally.
726
1019
  */
727
- export declare function mergeSignals(signalsOrFirst: AbortSignal[] | (AbortSignal | undefined), timeoutMs?: number): AbortSignal;
1020
+ export declare function mergeSignals(signals: readonly (AbortSignal | undefined)[], timeoutMs?: number): AbortSignal;
728
1021
 
729
1022
  /** Used when `AbortSignal.any` is unavailable; exposed for unit tests. */
730
1023
  export declare function mergeSignalsManual(signals: AbortSignal[], timeoutMs?: number): AbortSignal;
@@ -752,13 +1045,17 @@ export { NormalizedScheduleResponse as NormalizedScheduleResponse_alias_2 }
752
1045
  /**
753
1046
  * Transforms raw schedule response into normalized structure with flattened lessons.
754
1047
  */
755
- declare function normalizeSchedule(response: ScheduleResponse): NormalizedScheduleResponse;
1048
+ declare function normalizeSchedule(response: ScheduleResponse, options?: {
1049
+ validate?: boolean;
1050
+ endpoint?: string;
1051
+ }): NormalizedScheduleResponse;
756
1052
  export { normalizeSchedule }
757
1053
  export { normalizeSchedule as normalizeSchedule_alias_1 }
758
1054
  export { normalizeSchedule as normalizeSchedule_alias_2 }
759
1055
 
760
1056
  /**
761
1057
  * Parses response body as JSON when possible, otherwise returns text.
1058
+ * Returns `null` for empty non-JSON bodies.
762
1059
  * Throws `BsuirApiError` for declared JSON payloads that are empty/invalid.
763
1060
  */
764
1061
  export declare function parseBody(response: Response, maxResponseBytes: number): Promise<unknown>;
@@ -769,11 +1066,18 @@ export declare function parseBody(response: Response, maxResponseBytes: number):
769
1066
  */
770
1067
  export declare function parseCurrentWeek(payload: unknown): number;
771
1068
 
1069
+ /**
1070
+ *
1071
+ */
772
1072
  export declare function parseDdMmYyyy(value: string | null): Date | null;
773
1073
 
774
- export declare type QueryParams = Record<string, QueryValue>;
1074
+ declare type QueryParams = Record<string, QueryValue>;
1075
+ export { QueryParams }
1076
+ export { QueryParams as QueryParams_alias_1 }
775
1077
 
776
- export declare type QueryValue = string | number | boolean | null | undefined;
1078
+ declare type QueryValue = string | number | boolean | null | undefined;
1079
+ export { QueryValue }
1080
+ export { QueryValue as QueryValue_alias_1 }
777
1081
 
778
1082
  /**
779
1083
  * Read options used by all module methods.
@@ -784,13 +1088,17 @@ declare interface ReadOptions {
784
1088
  */
785
1089
  signal?: AbortSignal | undefined;
786
1090
  /**
787
- * When true, return raw API payload where supported.
1091
+ * Per-request cache mode.
788
1092
  */
789
- raw?: boolean;
1093
+ cache?: RequestCacheMode | undefined;
790
1094
  }
791
1095
  export { ReadOptions }
792
1096
  export { ReadOptions as ReadOptions_alias_1 }
793
1097
 
1098
+ declare type RequestCacheMode = "default" | "no-store" | "reload";
1099
+ export { RequestCacheMode }
1100
+ export { RequestCacheMode as RequestCacheMode_alias_1 }
1101
+
794
1102
  declare interface RequestHookContext {
795
1103
  method: RequestMethod;
796
1104
  path: string;
@@ -809,7 +1117,9 @@ declare function requestJson<T>(config: Readonly<InternalClientConfig>, path: st
809
1117
  export { requestJson }
810
1118
  export { requestJson as requestJson_alias_1 }
811
1119
 
812
- export declare type RequestMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
1120
+ declare type RequestMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
1121
+ export { RequestMethod }
1122
+ export { RequestMethod as RequestMethod_alias_1 }
813
1123
 
814
1124
  /**
815
1125
  * Low-level HTTP request options used by internal request pipeline.
@@ -835,6 +1145,21 @@ declare interface RequestOptions {
835
1145
  * Additional request headers.
836
1146
  */
837
1147
  headers?: HeadersInit | undefined;
1148
+ /**
1149
+ * Per-request cache mode for successful GET requests.
1150
+ *
1151
+ * - `"default"`: read from cache and write to cache.
1152
+ * - `"no-store"`: always bypass cache read/write.
1153
+ * - `"reload"`: bypass cache read, but write fresh response to cache.
1154
+ *
1155
+ * @defaultValue "default"
1156
+ */
1157
+ cache?: RequestCacheMode | undefined;
1158
+ /**
1159
+ * Optional response validator invoked for network responses before caching.
1160
+ * Used by internal modules to avoid repeated validation on cache hits.
1161
+ */
1162
+ responseValidator?: ((payload: unknown) => void) | undefined;
838
1163
  }
839
1164
  export { RequestOptions }
840
1165
  export { RequestOptions as RequestOptions_alias_1 }
@@ -850,14 +1175,55 @@ export { ResponseHookContext as ResponseHookContext_alias_1 }
850
1175
  /** HTTP status codes retriable by the request pipeline. */
851
1176
  export declare const RETRIABLE_STATUS_CODES: Set<number>;
852
1177
 
1178
+ export declare type RetryDecision = {
1179
+ retryable: true;
1180
+ delayMs: number;
1181
+ } | {
1182
+ retryable: false;
1183
+ rejectedDelayMs: number;
1184
+ };
1185
+
853
1186
  declare interface RetryHookContext extends RequestHookContext {
854
1187
  delayMs: number;
855
- reason: "http_status" | "network_error";
1188
+ reason: "http_status" | "network_error" | "retry_after_too_large";
856
1189
  status: number | undefined;
857
1190
  }
858
1191
  export { RetryHookContext }
859
1192
  export { RetryHookContext as RetryHookContext_alias_1 }
860
1193
 
1194
+ /**
1195
+ * A single day model produced by {@link buildScheduleDays}.
1196
+ */
1197
+ declare interface ScheduleDay {
1198
+ /** Local calendar date for this day. */
1199
+ date: Date;
1200
+ /** ISO-style date key in `"YYYY-MM-DD"` format for fast equality checks. */
1201
+ dateKey: string;
1202
+ /** BSUIR weekday name, or `null` for Sunday. */
1203
+ weekday: Weekday | null;
1204
+ /** Display label: weekday name, or `"Воскресенье"` for Sunday. */
1205
+ weekdayLabel: string;
1206
+ /** Lessons for this day sorted by start time. */
1207
+ lessons: FlattenedScheduleItem[];
1208
+ /** Whether this day matches the reference `now` date. */
1209
+ isToday: boolean;
1210
+ /** Whether there are any lessons on this day. */
1211
+ hasLessons: boolean;
1212
+ /**
1213
+ * Lesson active at `now`, or `null`.
1214
+ * Always `null` for days other than today, or when `includeCurrentAndNextLessons` is `false`.
1215
+ */
1216
+ currentLesson: FlattenedScheduleItem | null;
1217
+ /**
1218
+ * Next upcoming lesson after `now`, or `null`.
1219
+ * Always `null` for days other than today, or when `includeCurrentAndNextLessons` is `false`.
1220
+ */
1221
+ nextLesson: FlattenedScheduleItem | null;
1222
+ }
1223
+ export { ScheduleDay }
1224
+ export { ScheduleDay as ScheduleDay_alias_1 }
1225
+ export { ScheduleDay as ScheduleDay_alias_2 }
1226
+
861
1227
  declare interface ScheduleFilterOptions {
862
1228
  source?: "schedules" | "exams";
863
1229
  weekday?: Weekday;
@@ -894,35 +1260,123 @@ export { ScheduleItem }
894
1260
  export { ScheduleItem as ScheduleItem_alias_1 }
895
1261
  export { ScheduleItem as ScheduleItem_alias_2 }
896
1262
 
1263
+ declare interface ScheduleModule {
1264
+ getGroup(groupNumber: string, options?: ReadOptions): Promise<NormalizedScheduleResponse>;
1265
+ getEmployee(urlId: string, options?: ReadOptions): Promise<NormalizedScheduleResponse>;
1266
+ getGroupFiltered(groupNumber: string, filter: ScheduleFilterOptions, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
1267
+ getEmployeeFiltered(urlId: string, filter: ScheduleFilterOptions, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
1268
+ getGroupExams(groupNumber: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
1269
+ getEmployeeExams(urlId: string, options?: ReadOptions): Promise<FlattenedScheduleItem[]>;
1270
+ getGroupRaw(groupNumber: string, options?: ReadOptions): Promise<ScheduleResponse>;
1271
+ getGroupEnvelope(groupNumber: string, subgroup: number, options?: ReadOptions): Promise<ScheduleResponse>;
1272
+ getEmployeeRaw(urlId: string, options?: ReadOptions): Promise<ScheduleResponse>;
1273
+ getEmployeeEnvelope(urlId: string, subgroup: number, options?: ReadOptions): Promise<ScheduleResponse>;
1274
+ getGroupBySubgroup(groupNumber: string, subgroup: number, options: ReadOptions & {
1275
+ rawEnvelope: true;
1276
+ }): Promise<ScheduleResponse>;
1277
+ getGroupBySubgroup(groupNumber: string, subgroup: number, options: ReadOptions & {
1278
+ raw: true;
1279
+ rawEnvelope?: false | undefined;
1280
+ }): Promise<ScheduleItem[]>;
1281
+ getGroupBySubgroup(groupNumber: string, subgroup: number, options: ReadOptions & {
1282
+ raw: boolean;
1283
+ rawEnvelope?: false | undefined;
1284
+ }): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;
1285
+ getGroupBySubgroup(groupNumber: string, subgroup: number, options?: ReadOptions & {
1286
+ raw?: false | undefined;
1287
+ rawEnvelope?: false | undefined;
1288
+ }): Promise<FlattenedScheduleItem[]>;
1289
+ getEmployeeBySubgroup(urlId: string, subgroup: number, options: ReadOptions & {
1290
+ rawEnvelope: true;
1291
+ }): Promise<ScheduleResponse>;
1292
+ getEmployeeBySubgroup(urlId: string, subgroup: number, options: ReadOptions & {
1293
+ raw: true;
1294
+ rawEnvelope?: false | undefined;
1295
+ }): Promise<ScheduleItem[]>;
1296
+ getEmployeeBySubgroup(urlId: string, subgroup: number, options: ReadOptions & {
1297
+ raw: boolean;
1298
+ rawEnvelope?: false | undefined;
1299
+ }): Promise<ScheduleItem[] | FlattenedScheduleItem[]>;
1300
+ getEmployeeBySubgroup(urlId: string, subgroup: number, options?: ReadOptions & {
1301
+ raw?: false | undefined;
1302
+ rawEnvelope?: false | undefined;
1303
+ }): Promise<FlattenedScheduleItem[]>;
1304
+ getCurrentWeek(options?: ReadOptions): Promise<number>;
1305
+ getLastUpdateByGroup(params: {
1306
+ groupNumber: string;
1307
+ } | {
1308
+ id: number;
1309
+ }, options?: ReadOptions): Promise<ApiDateResponse>;
1310
+ getLastUpdateByEmployee(params: {
1311
+ urlId: string;
1312
+ } | {
1313
+ id: number;
1314
+ }, options?: ReadOptions): Promise<ApiDateResponse>;
1315
+ }
1316
+ export { ScheduleModule }
1317
+ export { ScheduleModule as ScheduleModule_alias_1 }
1318
+
897
1319
  declare interface ScheduleResponse {
898
1320
  employeeDto: Maybe<Employee>;
899
1321
  studentGroupDto: Maybe<StudentGroupCatalogItem>;
900
1322
  schedules: WeekScheduleMap | null;
1323
+ /** Additional schedules, e.g. for the next term. Shape not yet stable. */
1324
+ nextSchedules?: WeekScheduleMap | null;
901
1325
  exams: ScheduleItem[] | null;
902
1326
  startDate: Maybe<string>;
903
1327
  endDate: Maybe<string>;
904
1328
  startExamsDate: Maybe<string>;
905
1329
  endExamsDate: Maybe<string>;
1330
+ /** Current academic term identifier. Shape not yet stable. */
1331
+ currentTerm?: unknown;
1332
+ /** Next academic term identifier. Shape not yet stable. */
1333
+ nextTerm?: unknown;
1334
+ /** Current period within the term. Shape not yet stable. */
1335
+ currentPeriod?: unknown;
1336
+ /** Whether the group follows a zaochnik or distance learning schedule. */
1337
+ isZaochOrDist?: boolean | null;
906
1338
  }
907
1339
  export { ScheduleResponse }
908
1340
  export { ScheduleResponse as ScheduleResponse_alias_1 }
909
1341
  export { ScheduleResponse as ScheduleResponse_alias_2 }
910
1342
 
911
- declare type ScheduleResponseByRawOption<TRaw extends boolean | undefined, TRawDefault extends boolean> = TRaw extends true ? ScheduleResponse : TRaw extends false ? NormalizedScheduleResponse : TRawDefault extends true ? ScheduleResponse : NormalizedScheduleResponse;
912
-
913
1343
  /**
914
- * Writes response value to cache and performs eviction when size approaches capacity.
1344
+ * Writes response value to cache and performs expiration/LRU eviction.
1345
+ *
1346
+ * Throws if the value is not a JSON-compatible structure — the cache is designed to
1347
+ * hold parsed JSON only, and storing other shapes (Map/Set/Date/typed arrays/class
1348
+ * instances) would either fail later on read or silently lose data on
1349
+ * structuredClone-based eviction strategies.
915
1350
  */
916
- export declare function setCache(config: Readonly<InternalClientConfig>, key: string, value: unknown): void;
1351
+ export declare function setCache<T>(config: Readonly<InternalClientConfig>, key: string, value: T): T | undefined;
917
1352
 
918
1353
  /** Delays execution for a specified number of milliseconds. */
919
1354
  export declare function sleep(ms: number): Promise<void>;
920
1355
 
1356
+ /**
1357
+ * Returns a new lessons array sorted by start time, then by end time.
1358
+ * Invalid or missing time strings are kept at the end in original order.
1359
+ *
1360
+ * @param lessons - Lessons to sort.
1361
+ * @returns New sorted lessons array.
1362
+ *
1363
+ * @example
1364
+ * ```ts
1365
+ * const sorted = sortLessonsByTime(response.lessons);
1366
+ * ```
1367
+ */
1368
+ declare function sortLessonsByTime<T extends LessonWithTime>(lessons: readonly T[], options?: {
1369
+ onInvalidTime?: InvalidLessonTimeHook | undefined;
1370
+ }): T[];
1371
+ export { sortLessonsByTime }
1372
+ export { sortLessonsByTime as sortLessonsByTime_alias_1 }
1373
+ export { sortLessonsByTime as sortLessonsByTime_alias_2 }
1374
+
921
1375
  declare interface Speciality {
922
1376
  id: number;
923
1377
  name: string;
924
1378
  abbrev: string;
925
- educationForm: EducationForm[];
1379
+ educationForm: SpecialityEducationForm;
926
1380
  facultyId: number;
927
1381
  code: string;
928
1382
  }
@@ -930,6 +1384,15 @@ export { Speciality }
930
1384
  export { Speciality as Speciality_alias_1 }
931
1385
  export { Speciality as Speciality_alias_2 }
932
1386
 
1387
+ /**
1388
+ * The live IIS API returns `educationForm` as either a single object or an array
1389
+ * depending on the endpoint and context. Use `Array.isArray(educationForm)` to distinguish.
1390
+ */
1391
+ declare type SpecialityEducationForm = EducationForm | EducationForm[];
1392
+ export { SpecialityEducationForm }
1393
+ export { SpecialityEducationForm as SpecialityEducationForm_alias_1 }
1394
+ export { SpecialityEducationForm as SpecialityEducationForm_alias_2 }
1395
+
933
1396
  declare interface StudentGroupCatalogItem {
934
1397
  name: string;
935
1398
  facultyId: number;
@@ -957,14 +1420,24 @@ export { StudentGroupShort as StudentGroupShort_alias_2 }
957
1420
 
958
1421
  /**
959
1422
  * Reads a cached response value by key, applying TTL validation and LRU touch update.
1423
+ *
1424
+ * Returns the cached value directly (deep-frozen on write) instead of cloning on every
1425
+ * read. Callers that need a mutable copy must clone explicitly. This avoids paying
1426
+ * structuredClone() cost on every cache hit, which can dominate latency for large
1427
+ * schedule payloads on a hot cache path.
960
1428
  */
961
1429
  export declare function tryReadCache(config: Readonly<InternalClientConfig>, key: string): unknown;
962
1430
 
963
- declare type Weekday = "Понедельник" | "Вторник" | "Среда" | "Четверг" | "Пятница" | "Суббота";
1431
+ declare type Weekday = (typeof WEEKDAYS)[number];
964
1432
  export { Weekday }
965
1433
  export { Weekday as Weekday_alias_1 }
966
1434
  export { Weekday as Weekday_alias_2 }
967
1435
 
1436
+ declare const WEEKDAYS: readonly ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"];
1437
+ export { WEEKDAYS }
1438
+ export { WEEKDAYS as WEEKDAYS_alias_1 }
1439
+ export { WEEKDAYS as WEEKDAYS_alias_2 }
1440
+
968
1441
  declare type WeekScheduleMap = Partial<Record<Weekday, ScheduleItem[]>>;
969
1442
  export { WeekScheduleMap }
970
1443
  export { WeekScheduleMap as WeekScheduleMap_alias_1 }