chuvsu-js 4.1.5 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -12
- package/dist/common/parse.js +1 -1
- package/dist/tt/parse/audience.js +14 -22
- package/dist/tt/parse/entry-parts.d.ts +7 -0
- package/dist/tt/parse/entry-parts.js +49 -0
- package/dist/tt/parse/full-schedule.js +21 -14
- package/dist/tt/parse/patterns.d.ts +2 -2
- package/dist/tt/parse/patterns.js +3 -3
- package/dist/tt/parse/teacher.js +12 -14
- package/dist/tt/types.d.ts +2 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -18,18 +18,6 @@ npm install chuvsu-js
|
|
|
18
18
|
|
|
19
19
|
## Быстрый старт
|
|
20
20
|
|
|
21
|
-
### Проверка расписаний на живых данных
|
|
22
|
-
|
|
23
|
-
Укажите `TT_EMAIL` и `TT_PASSWORD` в `.env`, затем запустите:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
pnpm test:live:schedules
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
По умолчанию проверяются 50 групп. Доступны `--limit=100`, `--all` и
|
|
30
|
-
`--group=КТ-41-24` (аргументы передаются после `--`). Значения авторизации
|
|
31
|
-
скрипт не выводит.
|
|
32
|
-
|
|
33
21
|
### Расписание (TtClient)
|
|
34
22
|
|
|
35
23
|
```ts
|
package/dist/common/parse.js
CHANGED
|
@@ -28,7 +28,7 @@ export function parseWeekParity(html) {
|
|
|
28
28
|
return match[1] === "**" ? "even" : "odd";
|
|
29
29
|
}
|
|
30
30
|
export function parseTeacher(s) {
|
|
31
|
-
const trimmed = s.trim();
|
|
31
|
+
const trimmed = s.replace(/\s*\(\s*ДОТ\s*\)\s*$/iu, "").trim();
|
|
32
32
|
if (!trimmed)
|
|
33
33
|
return { name: "" };
|
|
34
34
|
const posMatch = trimmed.match(/^(доц\.|проф\.|ст\.преп\.|ст\. преп\.|преп\.|асс\.|зав\.каф\.)\s*/);
|
|
@@ -3,6 +3,7 @@ import { parseSemesterScheduleWith } from "./full-schedule.js";
|
|
|
3
3
|
import { parseGroupsString } from "./groups.js";
|
|
4
4
|
import { parseSubstituteForDiv, parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
|
|
5
5
|
import { LESSON_TYPE_GLOBAL_RE, LESSON_TYPE_RE, SUBGROUP_ANNOTATION_RE, SUBGROUP_RE, WEEKS_GLOBAL_RE, WEEKS_RE, } from "./patterns.js";
|
|
6
|
+
import { linesAfterSubject, stripDistanceMarker } from "./entry-parts.js";
|
|
6
7
|
const DISTANCE_RE = /дистанционно|ДОТ/i;
|
|
7
8
|
export function parseAudienceInfo(html) {
|
|
8
9
|
const doc = parseHtml(html);
|
|
@@ -111,28 +112,19 @@ function parseAudienceSemesterEntry(el) {
|
|
|
111
112
|
const weeksMatch = cleanText.match(WEEKS_RE);
|
|
112
113
|
const subgroupMatch = cleanText.match(SUBGROUP_RE);
|
|
113
114
|
const weekParity = parseWeekParity(cleanHtml);
|
|
114
|
-
// Audience entries
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
.
|
|
120
|
-
.map((
|
|
121
|
-
.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.replace(LESSON_TYPE_GLOBAL_RE, "")
|
|
128
|
-
.replace(WEEKS_GLOBAL_RE, "")
|
|
129
|
-
.replace(SUBGROUP_ANNOTATION_RE, "")
|
|
130
|
-
.trim();
|
|
131
|
-
if (cleaned)
|
|
132
|
-
textLines.push(cleaned);
|
|
133
|
-
}
|
|
134
|
-
const teacherLine = textLines[0] ?? "";
|
|
135
|
-
const groupsLine = textLines.slice(1).join(" ").trim();
|
|
115
|
+
// Audience entries: subject line, then teacher line, then group line(s).
|
|
116
|
+
const parts = linesAfterSubject(cleanHtml, subject);
|
|
117
|
+
const teacherLine = stripDistanceMarker(parts[0] ?? "");
|
|
118
|
+
const groupsLine = parts
|
|
119
|
+
.slice(1)
|
|
120
|
+
.map(stripDistanceMarker)
|
|
121
|
+
.map((line) => line
|
|
122
|
+
.replace(LESSON_TYPE_GLOBAL_RE, "")
|
|
123
|
+
.replace(WEEKS_GLOBAL_RE, "")
|
|
124
|
+
.replace(SUBGROUP_ANNOTATION_RE, "")
|
|
125
|
+
.trim())
|
|
126
|
+
.filter(Boolean)
|
|
127
|
+
.join(" ");
|
|
136
128
|
return {
|
|
137
129
|
room: "",
|
|
138
130
|
subject,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function entryHtmlLines(html: string): string[];
|
|
2
|
+
export declare function entryTextLines(html: string): string[];
|
|
3
|
+
export declare function stripHtml(html: string): string;
|
|
4
|
+
export declare function linesAfterSubject(html: string, subject: string): string[];
|
|
5
|
+
export declare function parseEntryRoom(html: string, subject: string): string;
|
|
6
|
+
export declare function stripDistanceMarker(value: string): string;
|
|
7
|
+
export declare function containsGroupCode(value: string): boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const BR_RE = /<br\s*\/?>/i;
|
|
2
|
+
const TAG_RE = /<[^>]*>/g;
|
|
3
|
+
const DISTANCE_RE = /дистанционно|ДОТ/i;
|
|
4
|
+
const GROUP_TOKEN_RE = /^[A-ZА-ЯЁ]{1,}(?:-[A-ZА-ЯЁa-zа-яё0-9]+)+$/u;
|
|
5
|
+
export function entryHtmlLines(html) {
|
|
6
|
+
return html.split(BR_RE).map((line) => line.trim());
|
|
7
|
+
}
|
|
8
|
+
export function entryTextLines(html) {
|
|
9
|
+
return entryHtmlLines(html)
|
|
10
|
+
.map(stripHtml)
|
|
11
|
+
.filter((line) => line.length > 0);
|
|
12
|
+
}
|
|
13
|
+
export function stripHtml(html) {
|
|
14
|
+
return html.replace(TAG_RE, " ").replace(/\s+/g, " ").trim();
|
|
15
|
+
}
|
|
16
|
+
export function linesAfterSubject(html, subject) {
|
|
17
|
+
const lines = entryTextLines(html);
|
|
18
|
+
const index = lines.findIndex((line) => line.includes(subject));
|
|
19
|
+
return index < 0 ? [] : lines.slice(index + 1);
|
|
20
|
+
}
|
|
21
|
+
export function parseEntryRoom(html, subject) {
|
|
22
|
+
const lines = entryHtmlLines(html);
|
|
23
|
+
const line = lines.find((part) => stripHtml(part).includes(subject));
|
|
24
|
+
if (!line)
|
|
25
|
+
return "";
|
|
26
|
+
const lineText = stripHtml(line);
|
|
27
|
+
const subjectIndex = lineText.indexOf(subject);
|
|
28
|
+
if (subjectIndex < 0)
|
|
29
|
+
return "";
|
|
30
|
+
const beforeSubject = lineText
|
|
31
|
+
.slice(0, subjectIndex)
|
|
32
|
+
.replace(/^\*+\s*/, "")
|
|
33
|
+
.trim();
|
|
34
|
+
if (DISTANCE_RE.test(stripHtml(html))) {
|
|
35
|
+
return "Дистанционно (ДОТ)";
|
|
36
|
+
}
|
|
37
|
+
if (!beforeSubject)
|
|
38
|
+
return "";
|
|
39
|
+
return beforeSubject;
|
|
40
|
+
}
|
|
41
|
+
export function stripDistanceMarker(value) {
|
|
42
|
+
return value
|
|
43
|
+
.replace(/\(\s*ДОТ\s*\)/giu, " ")
|
|
44
|
+
.replace(/\s+/g, " ")
|
|
45
|
+
.trim();
|
|
46
|
+
}
|
|
47
|
+
export function containsGroupCode(value) {
|
|
48
|
+
return value.split(/\s+/).some((token) => GROUP_TOKEN_RE.test(token));
|
|
49
|
+
}
|
|
@@ -2,6 +2,7 @@ import { parseHtml, parseTeacher, parseTime, parseWeekParity, parseWeeks, text,
|
|
|
2
2
|
import { getLessonNumber } from "../utils/index.js";
|
|
3
3
|
import { parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
|
|
4
4
|
import { FLEXIBLE_LESSON_TYPE_RE_I, LESSON_TYPE_RE, SUBGROUP_RE, WEEKS_RE, } from "./patterns.js";
|
|
5
|
+
import { containsGroupCode, linesAfterSubject, parseEntryRoom, stripDistanceMarker, } from "./entry-parts.js";
|
|
5
6
|
const DISTANCE_RE = /дистанционно|ДОТ/i;
|
|
6
7
|
export function parseFullSchedule(html, educationType) {
|
|
7
8
|
const doc = parseHtml(html);
|
|
@@ -30,6 +31,13 @@ export function parseSemesterScheduleWith(doc, entryParser) {
|
|
|
30
31
|
}
|
|
31
32
|
if (!currentDay)
|
|
32
33
|
continue;
|
|
34
|
+
const selfStudyMarker = row.querySelector('span[style*="color: blue"]');
|
|
35
|
+
if (selfStudyMarker &&
|
|
36
|
+
/^День самостоятельной работы$/i.test(text(selfStudyMarker)) &&
|
|
37
|
+
selfStudyMarker.closest("tr") === row) {
|
|
38
|
+
currentDay.isSelfStudyDay = true;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
33
41
|
const timeCell = row.querySelector("td.trf");
|
|
34
42
|
const dataCell = row.querySelector("td.trdata:not(.trf)");
|
|
35
43
|
if (!timeCell || !dataCell)
|
|
@@ -98,20 +106,23 @@ function parseSemesterEntry(el) {
|
|
|
98
106
|
return null;
|
|
99
107
|
const typeMatch = cleanText.match(LESSON_TYPE_RE);
|
|
100
108
|
const weeksMatch = cleanText.match(WEEKS_RE);
|
|
101
|
-
const
|
|
102
|
-
const
|
|
109
|
+
const room = parseEntryRoom(cleanHtml, subject);
|
|
110
|
+
const teacherLine = linesAfterSubject(cleanHtml, subject).find((line) => {
|
|
111
|
+
const candidate = stripDistanceMarker(line);
|
|
112
|
+
return candidate.length > 0 && !SUBGROUP_RE.test(candidate);
|
|
113
|
+
}) ?? "";
|
|
103
114
|
const subgroupMatch = cleanText.match(SUBGROUP_RE);
|
|
104
115
|
const weekParity = parseWeekParity(cleanHtml);
|
|
105
116
|
return {
|
|
106
|
-
room
|
|
117
|
+
room,
|
|
107
118
|
subject,
|
|
108
119
|
type: typeMatch?.[1] ?? "",
|
|
109
120
|
weeks: parseWeeks(weeksMatch?.[1] ?? ""),
|
|
110
|
-
teacher: parseTeacher(
|
|
121
|
+
teacher: parseTeacher(teacherLine),
|
|
111
122
|
groups: [],
|
|
112
123
|
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
113
124
|
weekParity,
|
|
114
|
-
isDistance: DISTANCE_RE.test(cleanText) || DISTANCE_RE.test(
|
|
125
|
+
isDistance: DISTANCE_RE.test(cleanText) || DISTANCE_RE.test(room),
|
|
115
126
|
substitutions: substitutions.length > 0 ? substitutions : undefined,
|
|
116
127
|
possibleChanges,
|
|
117
128
|
};
|
|
@@ -168,20 +179,16 @@ function parseSessionEntry(td) {
|
|
|
168
179
|
const subject = subjectEl ? text(subjectEl) : "";
|
|
169
180
|
if (!subject)
|
|
170
181
|
return null;
|
|
171
|
-
|
|
172
|
-
const roomMatch = fullHtml.match(/^([^<]*?)\s*<span/);
|
|
173
|
-
const room = roomMatch ? roomMatch[1].trim() : "";
|
|
182
|
+
const room = parseEntryRoom(fullHtml, subject);
|
|
174
183
|
// Type: parenthesized text after </span>, case-insensitive
|
|
175
184
|
const typeMatch = plainText.match(FLEXIBLE_LESSON_TYPE_RE_I);
|
|
176
185
|
const type = typeMatch ? typeMatch[1].replace(/\.$/, "").toLowerCase() : "";
|
|
177
186
|
const subgroupMatch = plainText.match(SUBGROUP_RE);
|
|
178
|
-
const parts = fullHtml
|
|
179
|
-
|
|
180
|
-
.map((part) => part.replace(/<[^>]*>/g, "").trim())
|
|
181
|
-
.filter((part) => part.length > 0);
|
|
182
|
-
const teacherPart = parts.find((part) => !part.includes(subject) &&
|
|
187
|
+
const parts = linesAfterSubject(fullHtml, subject);
|
|
188
|
+
const teacherPart = parts.find((part) => stripDistanceMarker(part).length > 0 &&
|
|
183
189
|
!/^\d{2}:\d{2}\s*-\s*\d{2}:\d{2}$/.test(part) &&
|
|
184
|
-
!SUBGROUP_RE.test(part)
|
|
190
|
+
!SUBGROUP_RE.test(part) &&
|
|
191
|
+
!containsGroupCode(stripDistanceMarker(part))) ?? "";
|
|
185
192
|
// Time: after <br>, format HH:MM - HH:MM
|
|
186
193
|
const timeMatch = fullHtml.match(/<br\s*\/?>\s*(\d{2}:\d{2})\s*-\s*(\d{2}:\d{2})/);
|
|
187
194
|
if (!timeMatch)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export declare const LESSON_TYPE_PATTERN = "\u043B\u043A|\u043F\u0440|\u043B\u0431|\u0437\u0430\u0447\u043E|\u0437\u0430\u0447|\u044D\u043A\u0437|\u043A\u043E\u043D\u0441|\u043A\u043F";
|
|
2
|
-
export declare const FLEXIBLE_LESSON_TYPE_PATTERN = "(?:\u043B\u043A|\u043F\u0440|\u043B\u0431|\u0437\u0430\u0447\u043E|\u0437\u0430\u0447|\u044D\u043A\u0437|\u043A\u043E\u043D\u0441|\u043A\u043F)\\.?|\u042D\u043A\u0437";
|
|
1
|
+
export declare const LESSON_TYPE_PATTERN = "\u043B\u043A|\u043F\u0440|\u043B\u0431|\u0437\u0430\u0447\u043E|\u0437\u0430\u0447|\u044D\u043A\u0437|\u043A\u043E\u043D\u0441|\u043A\u043F|\u0438\u0437|\u0433\u0437|\u043A\u0440\u043F";
|
|
2
|
+
export declare const FLEXIBLE_LESSON_TYPE_PATTERN = "(?:\u043B\u043A|\u043F\u0440|\u043B\u0431|\u0437\u0430\u0447\u043E|\u0437\u0430\u0447|\u044D\u043A\u0437|\u043A\u043E\u043D\u0441|\u043A\u043F|\u0438\u0437|\u0433\u0437|\u043A\u0440\u043F)\\.?|\u042D\u043A\u0437";
|
|
3
3
|
export declare const LESSON_TYPE_RE: RegExp;
|
|
4
4
|
export declare const LESSON_TYPE_RE_I: RegExp;
|
|
5
5
|
export declare const FLEXIBLE_LESSON_TYPE_RE_I: RegExp;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export const LESSON_TYPE_PATTERN = "
|
|
1
|
+
export const LESSON_TYPE_PATTERN = "лк|пр|лб|зачо|зач|экз|конс|кп|из|гз|крп";
|
|
2
2
|
export const FLEXIBLE_LESSON_TYPE_PATTERN = `(?:${LESSON_TYPE_PATTERN})\\.?|Экз`;
|
|
3
|
-
export const LESSON_TYPE_RE = new RegExp(`\\((${LESSON_TYPE_PATTERN})\\)
|
|
3
|
+
export const LESSON_TYPE_RE = new RegExp(`\\((${LESSON_TYPE_PATTERN})\\)`, "i");
|
|
4
4
|
export const LESSON_TYPE_RE_I = new RegExp(`\\((${LESSON_TYPE_PATTERN})\\)`, "i");
|
|
5
5
|
export const FLEXIBLE_LESSON_TYPE_RE_I = new RegExp(`\\((${FLEXIBLE_LESSON_TYPE_PATTERN})\\)`, "i");
|
|
6
|
-
export const LESSON_TYPE_GLOBAL_RE = new RegExp(`\\((${LESSON_TYPE_PATTERN})\\)`, "
|
|
6
|
+
export const LESSON_TYPE_GLOBAL_RE = new RegExp(`\\((${LESSON_TYPE_PATTERN})\\)`, "gi");
|
|
7
7
|
export const WEEKS_RE = /\(([^)]*нед\.?[^)]*)\)/;
|
|
8
8
|
export const WEEKS_GLOBAL_RE = /\([^)]*нед\.?[^)]*\)/g;
|
|
9
9
|
export const SUBGROUP_RE = /(\d+)\s*подгруппа/;
|
package/dist/tt/parse/teacher.js
CHANGED
|
@@ -4,6 +4,7 @@ import { parseSemesterScheduleWith } from "./full-schedule.js";
|
|
|
4
4
|
import { parseGroupsString } from "./groups.js";
|
|
5
5
|
import { parseSubstituteForDiv, parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
|
|
6
6
|
import { FLEXIBLE_LESSON_TYPE_RE_I, LESSON_TYPE_RE, SUBGROUP_RE, WEEKS_RE, } from "./patterns.js";
|
|
7
|
+
import { containsGroupCode, linesAfterSubject, parseEntryRoom, stripDistanceMarker, } from "./entry-parts.js";
|
|
7
8
|
const DISTANCE_RE = /дистанционно|ДОТ/i;
|
|
8
9
|
export function parseTeacherFullSchedule(html, educationType) {
|
|
9
10
|
const doc = parseHtml(html);
|
|
@@ -56,20 +57,20 @@ function parseTeacherSemesterEntry(el) {
|
|
|
56
57
|
return null;
|
|
57
58
|
const typeMatch = cleanText.match(LESSON_TYPE_RE);
|
|
58
59
|
const weeksMatch = cleanText.match(WEEKS_RE);
|
|
59
|
-
const
|
|
60
|
-
const
|
|
60
|
+
const room = parseEntryRoom(cleanHtml, subject);
|
|
61
|
+
const groupsLine = linesAfterSubject(cleanHtml, subject).find((line) => containsGroupCode(stripDistanceMarker(line))) ?? "";
|
|
61
62
|
const subgroupMatch = cleanText.match(SUBGROUP_RE);
|
|
62
63
|
const weekParity = parseWeekParity(cleanHtml);
|
|
63
64
|
return {
|
|
64
|
-
room
|
|
65
|
+
room,
|
|
65
66
|
subject,
|
|
66
67
|
type: typeMatch?.[1] ?? "",
|
|
67
68
|
weeks: parseWeeks(weeksMatch?.[1] ?? ""),
|
|
68
69
|
teacher: { name: "" },
|
|
69
|
-
groups: parseGroupsString(
|
|
70
|
+
groups: parseGroupsString(stripDistanceMarker(groupsLine)),
|
|
70
71
|
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
71
72
|
weekParity,
|
|
72
|
-
isDistance: DISTANCE_RE.test(cleanText) || DISTANCE_RE.test(
|
|
73
|
+
isDistance: DISTANCE_RE.test(cleanText) || DISTANCE_RE.test(room),
|
|
73
74
|
substitutions: substitutions.length > 0 ? substitutions : undefined,
|
|
74
75
|
possibleChanges,
|
|
75
76
|
};
|
|
@@ -121,20 +122,17 @@ function parseTeacherSessionEntry(td) {
|
|
|
121
122
|
const subject = subjectEl ? text(subjectEl) : "";
|
|
122
123
|
if (!subject)
|
|
123
124
|
return null;
|
|
124
|
-
const
|
|
125
|
-
const room = roomMatch ? roomMatch[1].trim() : "";
|
|
125
|
+
const room = parseEntryRoom(fullHtml, subject);
|
|
126
126
|
const typeMatch = plainText.match(FLEXIBLE_LESSON_TYPE_RE_I);
|
|
127
127
|
const type = typeMatch ? typeMatch[1].replace(/\.$/, "").toLowerCase() : "";
|
|
128
128
|
const subgroupMatch = plainText.match(SUBGROUP_RE);
|
|
129
129
|
const timeMatch = fullHtml.match(/<br\s*\/?>\s*(\d{2}:\d{2})\s*-\s*(\d{2}:\d{2})/);
|
|
130
130
|
if (!timeMatch)
|
|
131
131
|
return null;
|
|
132
|
-
const parts = fullHtml
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const groupsPart = parts.find((part) => !part.includes(subject) &&
|
|
137
|
-
!/^\d{2}:\d{2}\s*-\s*\d{2}:\d{2}$/.test(part)) ?? "";
|
|
132
|
+
const parts = linesAfterSubject(fullHtml, subject);
|
|
133
|
+
const groupsPart = parts.find((part) => stripDistanceMarker(part).length > 0 &&
|
|
134
|
+
!/^\d{2}:\d{2}\s*-\s*\d{2}:\d{2}$/.test(part) &&
|
|
135
|
+
containsGroupCode(stripDistanceMarker(part))) ?? "";
|
|
138
136
|
return {
|
|
139
137
|
entry: {
|
|
140
138
|
room,
|
|
@@ -142,7 +140,7 @@ function parseTeacherSessionEntry(td) {
|
|
|
142
140
|
type,
|
|
143
141
|
weeks: { from: 0, to: 0 },
|
|
144
142
|
teacher: { name: "" },
|
|
145
|
-
groups: parseGroupsString(groupsPart),
|
|
143
|
+
groups: parseGroupsString(stripDistanceMarker(groupsPart)),
|
|
146
144
|
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
147
145
|
isDistance: DISTANCE_RE.test(plainText) || DISTANCE_RE.test(room),
|
|
148
146
|
possibleChanges,
|
package/dist/tt/types.d.ts
CHANGED
|
@@ -100,6 +100,8 @@ export interface FullScheduleSlot {
|
|
|
100
100
|
export interface FullScheduleDay {
|
|
101
101
|
weekday: string;
|
|
102
102
|
date?: Date;
|
|
103
|
+
/** True when portal marks this weekday as a self-study day. */
|
|
104
|
+
isSelfStudyDay?: boolean;
|
|
103
105
|
slots: FullScheduleSlot[];
|
|
104
106
|
}
|
|
105
107
|
export interface LessonTimeSlot {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chuvsu-js",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Node.js library for ChuvSU student portal (lk.chuvsu.ru) and schedule (tt.chuvsu.ru)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
45
45
|
"build": "pnpm clean && tsc",
|
|
46
46
|
"test": "pnpm build && node --test",
|
|
47
|
-
"test:live:schedules": "pnpm build && node --env-file=.env
|
|
47
|
+
"test:live:schedules": "pnpm build && node --env-file=.env utils/testSchedules.mjs",
|
|
48
|
+
"fixtures:audit": "pnpm build && node utils/auditScheduleExpectations.mjs",
|
|
49
|
+
"fixtures:collect": "pnpm build && node --env-file=.env utils/collectScheduleFixtures.mjs"
|
|
48
50
|
}
|
|
49
51
|
}
|