chuvsu-js 5.2.0 → 5.2.2

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 CHANGED
@@ -1,5 +1,8 @@
1
1
  # chuvsu-js
2
2
 
3
+ > [!WARNING]
4
+ > Ооочень плохой вайбкод, библиотека будет переделана и не раз. Есть очень много пространства для упрощения использования и сохранения крутой архитектуры.
5
+
3
6
  Типизированная библиотека для сервисов ЧГУ им. И. Н. Ульянова:
4
7
 
5
8
  - `tt.chuvsu.ru` — расписания групп, преподавателей, аудиторий и вебинары;
@@ -2,4 +2,8 @@
2
2
  export declare const GLOBALSIGN_GCC_R3_DV_TLS_CA_2020 = "-----BEGIN CERTIFICATE-----\nMIIEsDCCA5igAwIBAgIQd70OB0LV2enQSdd00CpvmjANBgkqhkiG9w0BAQsFADBM\nMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv\nYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMDA3MjgwMDAwMDBaFw0y\nOTAzMTgwMDAwMDBaMFMxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu\nIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIEdDQyBSMyBEViBUTFMgQ0EgMjAy\nMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxnlJV/de+OpwyvCXAJ\nIcxPCqkFPh1lttW2oljS3oUqPKq8qX6m7K0OVKaKG3GXi4CJ4fHVUgZYE6HRdjqj\nhhnuHY6EBCBegcUFgPG0scB12Wi8BHm9zKjWxo3Y2bwhO8Fvr8R42pW0eINc6OTb\nQXC0VWFCMVzpcqgz6X49KMZowAMFV6XqtItcG0cMS//9dOJs4oBlpuqX9INxMTGp\n6EASAF9cnlAGy/RXkVS9nOLCCa7pCYV+WgDKLTF+OK2Vxw3RUJ/p8009lQeUARv2\nUCcNNPCifYX1xIspvarkdjzLwzOdLahDdQbJON58zN4V+lMj0msg+c0KnywPIRp3\nBMkCAwEAAaOCAYUwggGBMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEF\nBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUDZjA\nc3+rvb3ZR0tJrQpKDKw+x3wwHwYDVR0jBBgwFoAUj/BLf6guRSSuTVD6Y5qL3uLd\nG7wwewYIKwYBBQUHAQEEbzBtMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcDIuZ2xv\nYmFsc2lnbi5jb20vcm9vdHIzMDsGCCsGAQUFBzAChi9odHRwOi8vc2VjdXJlLmds\nb2JhbHNpZ24uY29tL2NhY2VydC9yb290LXIzLmNydDA2BgNVHR8ELzAtMCugKaAn\nhiVodHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL3Jvb3QtcjMuY3JsMEcGA1UdIARA\nMD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu\nLmNvbS9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAy8j/c550ea86oCkf\nr2W+ptTCYe6iVzvo7H0V1vUEADJOWelTv07Obf+YkEatdN1Jg09ctgSNv2h+LMTk\nKRZdAXmsE3N5ve+z1Oa9kuiu7284LjeS09zHJQB4DJJJkvtIbjL/ylMK1fbMHhAW\ni0O194TWvH3XWZGXZ6ByxTUIv1+kAIql/Mt29PmKraTT5jrzcVzQ5A9jw16yysuR\nXRrLODlkS1hyBjsfyTNZrmL1h117IFgntBA5SQNVl9ckedq5r4RSAU85jV8XK5UL\nREjRZt2I6M9Po9QL7guFLu4sPFJpwR1sPJvubS2THeo7SxYoNDtdyBHs7euaGcMa\nD/fayQ==\n-----END CERTIFICATE-----";
3
3
  /** GlobalSign Root CA - R3 */
4
4
  export declare const GLOBALSIGN_ROOT_R3 = "-----BEGIN CERTIFICATE-----\nMIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G\nA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp\nZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4\nMTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG\nA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8\nRgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT\ngHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm\nKPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\nQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ\nXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw\nDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o\nLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU\nRUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp\njjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK\n6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX\nmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs\nMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\nWD9f\n-----END CERTIFICATE-----";
5
+ /** GlobalSign GCC R46 DV TLS CA 2025 (intermediate) */
6
+ export declare const GLOBALSIGN_GCC_R46_DV_TLS_CA_2025 = "-----BEGIN CERTIFICATE-----\nMIIFfDCCA2SgAwIBAgIRAIRDWIEh21qyhJn/roBnC6QwDQYJKoZIhvcNAQELBQAw\nRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNV\nBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwHhcNMjUwOTE3MDI1NTQ0WhcNMjkwNjIz\nMDAwMDAwWjBUMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1z\nYTEqMCgGA1UEAxMhR2xvYmFsU2lnbiBHQ0MgUjQ2IERWIFRMUyBDQSAyMDI1MIIB\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApT+6pung92y3k3sp2FQMmKv2\nOqcOBNr9uk8IJUf/EyIHKfYRcIdgRuUu5T2PxIsvGYCAHQ3uSG/p6E1a16aMu9LV\nEG9DMXcW+rx6Why5IDOIOvRHjfPyQzAGurD4V/e71U4fW81n7pKIDtcWDFeSXoai\nt1cemN088vJH/L4iCSeQQ1qWsmo9VV4F8jrwWJEjOSrLLYBGHvvuteMlXzpjX2A7\npKcjOooEYU78ygS2K0YOdyN7W2hIUvScVv5yG3sA/q4/tWXY1kyUC/Bb70IgL0/n\nwAYMia+OgDo5RPb+U8RUsBN4QH9qxgM75nBiOlqh/aTrrMYfYrbWOR3vioZQDQID\nAQABo4IBVTCCAVEwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMB\nMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAhp6X+lq/TDYeXNjntH74sp\nZI3sMB8GA1UdIwQYMBaAFANcq3OBh6jMsKbVlOI2lkn/BZksMHsGCCsGAQUFBwEB\nBG8wbTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuZ2xvYmFsc2lnbi5jb20vcm9v\ndHI0NjA7BggrBgEFBQcwAoYvaHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNvbS9j\nYWNlcnQvcm9vdHI0Ni5jcnQwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n\nbG9iYWxzaWduLmNvbS9yb290cjQ2LmNybDAhBgNVHSAEGjAYMAgGBmeBDAECATAM\nBgorBgEEAaAyCgEDMA0GCSqGSIb3DQEBCwUAA4ICAQAqoA/ThPjFFFZKSDkoKTKx\nKA/K5/1469Z9wYkqd2P5btK/Hi+pUOpuJo39cHmXaNJrmVgYwTxo4n3VAB7DQWsm\nOXYOz5wOGLl6Aj5eR8aJ0GXGz1zS2iXsWwFjakS2WfLC25HE2t6XI2pQgNG0HyEY\noS90ddxMW51dsm5fUDy9AMGitsgHFjzibI7UbHr4i3srXk08E4Br8N7Sz0YAxjhC\nPANw4A8FaB9Q17CpGZI47CRLJSr4kzxk7sIB39FoqPYd5gtfL6NugWt/x2oRQ1xM\nNVa5QQDWRbiVxYoWkBmXwAWLYir/eRiEB195/CbzTDCEtqQBaId9jTFONPjkm0TZ\nryomaCABk93+GUYgeZzb/ovJ4QS6wxXTzq+ZDxHCTPF+dksaqD/CW6jmCJvEZqPy\nNhgQ96lPYc7ejqY9yET/4CtYMNPupjVbfepeLzE3n/wnTLKlR3n9V0dIhCjcrlJl\nm7WCAJy9nxkx0xmfUFzhFweDOCDsxfsdJ8GnOcFD2b0hyEIDnC+OSVF2G8MCwfn9\n0yfY/6Vg8dmStmLCpfmlndG9aFhxQkuXTBn/5Ojy/mJaT2mt8Idr2m3pxVSpGL1h\nqfCsCW+4rC3XQhQlmfaBc0205o3G8jVadKn/1yNrraM/BOBGESALCWJZTca/Z3+z\nzTYbjxDvBHjVJOwXrqfjyQ==\n-----END CERTIFICATE-----";
7
+ /** GlobalSign Root R46 */
8
+ export declare const GLOBALSIGN_ROOT_R46 = "-----BEGIN CERTIFICATE-----\nMIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA\nMEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD\nVQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy\nMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt\nc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ\nOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG\nvGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud\n316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo\n0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE\ny132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF\nzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE\n+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN\nI/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs\nx2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa\nByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC\n4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\nHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4\n7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg\nJuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti\n2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk\npnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF\nFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt\nrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk\nZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5\nu+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP\n4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6\nN3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3\nvouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6\n-----END CERTIFICATE-----";
5
9
  export declare const CHUVSU_CA_CERTS: string[];
@@ -49,7 +49,74 @@ mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
49
49
  Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
50
50
  WD9f
51
51
  -----END CERTIFICATE-----`;
52
+ /** GlobalSign GCC R46 DV TLS CA 2025 (intermediate) */
53
+ export const GLOBALSIGN_GCC_R46_DV_TLS_CA_2025 = `-----BEGIN CERTIFICATE-----
54
+ MIIFfDCCA2SgAwIBAgIRAIRDWIEh21qyhJn/roBnC6QwDQYJKoZIhvcNAQELBQAw
55
+ RjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNV
56
+ BAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwHhcNMjUwOTE3MDI1NTQ0WhcNMjkwNjIz
57
+ MDAwMDAwWjBUMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1z
58
+ YTEqMCgGA1UEAxMhR2xvYmFsU2lnbiBHQ0MgUjQ2IERWIFRMUyBDQSAyMDI1MIIB
59
+ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApT+6pung92y3k3sp2FQMmKv2
60
+ OqcOBNr9uk8IJUf/EyIHKfYRcIdgRuUu5T2PxIsvGYCAHQ3uSG/p6E1a16aMu9LV
61
+ EG9DMXcW+rx6Why5IDOIOvRHjfPyQzAGurD4V/e71U4fW81n7pKIDtcWDFeSXoai
62
+ t1cemN088vJH/L4iCSeQQ1qWsmo9VV4F8jrwWJEjOSrLLYBGHvvuteMlXzpjX2A7
63
+ pKcjOooEYU78ygS2K0YOdyN7W2hIUvScVv5yG3sA/q4/tWXY1kyUC/Bb70IgL0/n
64
+ wAYMia+OgDo5RPb+U8RUsBN4QH9qxgM75nBiOlqh/aTrrMYfYrbWOR3vioZQDQID
65
+ AQABo4IBVTCCAVEwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMB
66
+ MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAhp6X+lq/TDYeXNjntH74sp
67
+ ZI3sMB8GA1UdIwQYMBaAFANcq3OBh6jMsKbVlOI2lkn/BZksMHsGCCsGAQUFBwEB
68
+ BG8wbTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuZ2xvYmFsc2lnbi5jb20vcm9v
69
+ dHI0NjA7BggrBgEFBQcwAoYvaHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNvbS9j
70
+ YWNlcnQvcm9vdHI0Ni5jcnQwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
71
+ bG9iYWxzaWduLmNvbS9yb290cjQ2LmNybDAhBgNVHSAEGjAYMAgGBmeBDAECATAM
72
+ BgorBgEEAaAyCgEDMA0GCSqGSIb3DQEBCwUAA4ICAQAqoA/ThPjFFFZKSDkoKTKx
73
+ KA/K5/1469Z9wYkqd2P5btK/Hi+pUOpuJo39cHmXaNJrmVgYwTxo4n3VAB7DQWsm
74
+ OXYOz5wOGLl6Aj5eR8aJ0GXGz1zS2iXsWwFjakS2WfLC25HE2t6XI2pQgNG0HyEY
75
+ oS90ddxMW51dsm5fUDy9AMGitsgHFjzibI7UbHr4i3srXk08E4Br8N7Sz0YAxjhC
76
+ PANw4A8FaB9Q17CpGZI47CRLJSr4kzxk7sIB39FoqPYd5gtfL6NugWt/x2oRQ1xM
77
+ NVa5QQDWRbiVxYoWkBmXwAWLYir/eRiEB195/CbzTDCEtqQBaId9jTFONPjkm0TZ
78
+ ryomaCABk93+GUYgeZzb/ovJ4QS6wxXTzq+ZDxHCTPF+dksaqD/CW6jmCJvEZqPy
79
+ NhgQ96lPYc7ejqY9yET/4CtYMNPupjVbfepeLzE3n/wnTLKlR3n9V0dIhCjcrlJl
80
+ m7WCAJy9nxkx0xmfUFzhFweDOCDsxfsdJ8GnOcFD2b0hyEIDnC+OSVF2G8MCwfn9
81
+ 0yfY/6Vg8dmStmLCpfmlndG9aFhxQkuXTBn/5Ojy/mJaT2mt8Idr2m3pxVSpGL1h
82
+ qfCsCW+4rC3XQhQlmfaBc0205o3G8jVadKn/1yNrraM/BOBGESALCWJZTca/Z3+z
83
+ zTYbjxDvBHjVJOwXrqfjyQ==
84
+ -----END CERTIFICATE-----`;
85
+ /** GlobalSign Root R46 */
86
+ export const GLOBALSIGN_ROOT_R46 = `-----BEGIN CERTIFICATE-----
87
+ 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
116
+ -----END CERTIFICATE-----`;
52
117
  export const CHUVSU_CA_CERTS = [
53
118
  GLOBALSIGN_GCC_R3_DV_TLS_CA_2020,
54
119
  GLOBALSIGN_ROOT_R3,
120
+ GLOBALSIGN_GCC_R46_DV_TLS_CA_2025,
121
+ GLOBALSIGN_ROOT_R46,
55
122
  ];
@@ -597,9 +597,6 @@ export class TimetableRepository {
597
597
  if (!observation.subject.trim()) {
598
598
  throw new Error(`Empty lesson subject in ${source.sourceKey}/${observation.key}`);
599
599
  }
600
- if (!observation.type.trim()) {
601
- throw new Error(`Empty lesson type in ${source.sourceKey}/${observation.key}`);
602
- }
603
600
  const relations = [
604
601
  ["group", observation.groups, observation.groups.values.map((value) => value.group)],
605
602
  ["teacher", observation.teachers, observation.teachers.values],
@@ -62,6 +62,7 @@ export interface LessonSeries {
62
62
  academicYearStartYear: number;
63
63
  period: AcademicPeriod;
64
64
  subject: string;
65
+ /** Free-form portal label; empty when no source exposes one. */
65
66
  type: string;
66
67
  slotNumber?: number;
67
68
  time?: TimeRange;
@@ -84,6 +85,7 @@ export interface LessonOccurrence {
84
85
  nominalDate: LocalDate;
85
86
  scheduledDate: LocalDate;
86
87
  subject: string;
88
+ /** Free-form portal label; empty when no source exposes one. */
87
89
  type: string;
88
90
  slotNumber?: number;
89
91
  time?: TimeRange;
@@ -105,6 +107,7 @@ interface ObservationBase {
105
107
  /** Stable only inside one source page projection. */
106
108
  key: string;
107
109
  subject: string;
110
+ /** Free-form portal label; empty when this source does not expose one. */
108
111
  type: string;
109
112
  slotNumber?: number;
110
113
  time?: TimeRange;
@@ -2,8 +2,8 @@ import { parseHtml, parseTeacher, parseTime, parseWeekParity, parseWeeks, text,
2
2
  import { parseSemesterScheduleWith, parseSessionScheduleWith, } from "./full-schedule.js";
3
3
  import { parseGroupsString } from "./groups.js";
4
4
  import { parseSubstituteForDiv, parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
5
- import { LESSON_TYPE_GLOBAL_RE, LESSON_TYPE_RE, FLEXIBLE_LESSON_TYPE_RE_I, SUBGROUP_ANNOTATION_RE, SUBGROUP_RE, WEEKS_GLOBAL_RE, WEEKS_RE, } from "./patterns.js";
6
- import { containsGroupCode, hasDistanceMarker, linesAfterSubject, stripDistanceMarker, } from "./entry-parts.js";
5
+ import { LESSON_TYPE_GLOBAL_RE, SUBGROUP_ANNOTATION_RE, SUBGROUP_RE, WEEKS_GLOBAL_RE, WEEKS_RE, } from "./patterns.js";
6
+ import { containsGroupCode, hasDistanceMarker, linesAfterSubject, parseLessonTypeAfterSubject, stripDistanceMarker, } from "./entry-parts.js";
7
7
  export function parseRoomInfo(html) {
8
8
  const doc = parseHtml(html);
9
9
  // Name: <span class="htext"><nobr>Аудитория <span style="color: blue;">NAME</span></nobr></span>
@@ -104,10 +104,11 @@ function parseRoomSemesterEntry(el) {
104
104
  cleanText = cleanText.replace(text(div), "");
105
105
  }
106
106
  const subjectEl = td.querySelector('span[style*="color: blue"]');
107
- const subject = subjectEl ? text(subjectEl) : "";
107
+ if (!subjectEl)
108
+ return null;
109
+ const subject = text(subjectEl);
108
110
  if (!subject)
109
111
  return null;
110
- const typeMatch = cleanText.match(LESSON_TYPE_RE);
111
112
  const weeksMatch = cleanText.match(WEEKS_RE);
112
113
  const subgroupMatch = cleanText.match(SUBGROUP_RE);
113
114
  const weekParity = parseWeekParity(cleanHtml);
@@ -127,7 +128,7 @@ function parseRoomSemesterEntry(el) {
127
128
  const groups = parseGroupsString(groupsLine);
128
129
  return {
129
130
  subject,
130
- type: typeMatch?.[1] ?? "",
131
+ type: parseLessonTypeAfterSubject(subjectEl),
131
132
  weeks: parseWeeks(weeksMatch?.[1] ?? ""),
132
133
  teacher: teacherLine ? parseTeacher(teacherLine) : undefined,
133
134
  groups: groups.length > 0 ? groups : undefined,
@@ -149,13 +150,14 @@ function parseRoomSessionEntry(td) {
149
150
  const fullHtml = td.innerHTML ?? "";
150
151
  const plainText = text(td);
151
152
  const subjectEl = td.querySelector('span[style*="color: blue"]');
152
- const subject = subjectEl ? text(subjectEl) : "";
153
+ if (!subjectEl)
154
+ return null;
155
+ const subject = text(subjectEl);
153
156
  if (!plainText || !subject)
154
157
  return null;
155
158
  const timeMatch = fullHtml.match(/<br\s*\/?>\s*(\d{2}:\d{2})\s*-\s*(\d{2}:\d{2})/);
156
159
  if (!timeMatch)
157
160
  return null;
158
- const typeMatch = plainText.match(FLEXIBLE_LESSON_TYPE_RE_I);
159
161
  const parts = linesAfterSubject(fullHtml, subject);
160
162
  const teacherLine = parts.find((part) => !/^\d{2}:\d{2}\s*-\s*\d{2}:\d{2}$/.test(part) &&
161
163
  !containsGroupCode(stripDistanceMarker(part)));
@@ -165,7 +167,7 @@ function parseRoomSessionEntry(td) {
165
167
  return {
166
168
  lesson: {
167
169
  subject,
168
- type: typeMatch?.[1].replace(/\.$/, "").toLowerCase() ?? "",
170
+ type: parseLessonTypeAfterSubject(subjectEl).toLowerCase(),
169
171
  teacher: teacherLine ? parseTeacher(stripDistanceMarker(teacherLine)) : undefined,
170
172
  groups: groups.length > 0 ? groups : undefined,
171
173
  subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
@@ -6,3 +6,8 @@ export declare function linesAfterSubject(html: string, subject: string): string
6
6
  export declare function parseEntryRoom(html: string, subject: string): string;
7
7
  export declare function stripDistanceMarker(value: string): string;
8
8
  export declare function containsGroupCode(value: string): boolean;
9
+ /**
10
+ * Lesson type is free-form portal data. Its stable marker is position: the first
11
+ * parenthesized value immediately after the blue subject span.
12
+ */
13
+ export declare function parseLessonTypeAfterSubject(subjectElement: Element): string;
@@ -2,6 +2,9 @@ const BR_RE = /<br\s*\/?>/i;
2
2
  const TAG_RE = /<[^>]*>/g;
3
3
  const DISTANCE_RE = /(?:^|[^\p{L}\p{N}])(?:дистанционно|ДОТ)(?=$|[^\p{L}\p{N}])/iu;
4
4
  const GROUP_TOKEN_RE = /^[A-ZА-ЯЁ]{1,}(?:-[A-ZА-ЯЁa-zа-яё0-9]+)+$/u;
5
+ const LEADING_PARENTHESIZED_RE = /^\s*\(([^()]*)\)/u;
6
+ const WEEK_METADATA_RE = /(?:^|\s)нед\.?(?:\s|$)/iu;
7
+ const SUBGROUP_METADATA_RE = /^\d+\s*подгруппа$/iu;
5
8
  export function entryHtmlLines(html) {
6
9
  return html.split(BR_RE).map((line) => line.trim());
7
10
  }
@@ -50,3 +53,25 @@ export function stripDistanceMarker(value) {
50
53
  export function containsGroupCode(value) {
51
54
  return value.split(/\s+/).some((token) => GROUP_TOKEN_RE.test(token));
52
55
  }
56
+ /**
57
+ * Lesson type is free-form portal data. Its stable marker is position: the first
58
+ * parenthesized value immediately after the blue subject span.
59
+ */
60
+ export function parseLessonTypeAfterSubject(subjectElement) {
61
+ let suffix = "";
62
+ for (let sibling = subjectElement.nextSibling; sibling; sibling = sibling.nextSibling) {
63
+ if (sibling.tagName?.toLowerCase() === "br")
64
+ break;
65
+ suffix += sibling.textContent ?? "";
66
+ }
67
+ const candidate = suffix.match(LEADING_PARENTHESIZED_RE)?.[1]
68
+ ?.trim()
69
+ .replace(/\.$/, "") ?? "";
70
+ if (!candidate ||
71
+ WEEK_METADATA_RE.test(candidate) ||
72
+ SUBGROUP_METADATA_RE.test(candidate) ||
73
+ candidate.toUpperCase() === "ДОТ") {
74
+ return "";
75
+ }
76
+ return candidate;
77
+ }
@@ -1,7 +1,7 @@
1
1
  import { parseHtml, parseTeacher, parseTime, parseWeekParity, parseWeeks, text, } from "../../common/parse.js";
2
2
  import { parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
3
- import { FLEXIBLE_LESSON_TYPE_RE_I, LESSON_TYPE_RE, SUBGROUP_RE, WEEKS_RE, } from "./patterns.js";
4
- import { hasDistanceMarker, containsGroupCode, linesAfterSubject, parseEntryRoom, stripDistanceMarker, } from "./entry-parts.js";
3
+ import { SUBGROUP_RE, WEEKS_RE } from "./patterns.js";
4
+ import { hasDistanceMarker, containsGroupCode, linesAfterSubject, parseLessonTypeAfterSubject, parseEntryRoom, stripDistanceMarker, } from "./entry-parts.js";
5
5
  export function parseGroupSchedule(html) {
6
6
  const doc = parseHtml(html);
7
7
  // Session layout has date-based cells with ids like "trd20251224"
@@ -99,10 +99,11 @@ function parseSemesterEntry(el) {
99
99
  cleanText = cleanText.replace(text(div), "");
100
100
  }
101
101
  const subjectEl = td.querySelector('span[style*="color: blue"]');
102
- const subject = subjectEl ? text(subjectEl) : "";
102
+ if (!subjectEl)
103
+ return null;
104
+ const subject = text(subjectEl);
103
105
  if (!subject)
104
106
  return null;
105
- const typeMatch = cleanText.match(LESSON_TYPE_RE);
106
107
  const weeksMatch = cleanText.match(WEEKS_RE);
107
108
  const room = parseEntryRoom(cleanHtml, subject);
108
109
  const teacherLine = linesAfterSubject(cleanHtml, subject).find((line) => {
@@ -114,7 +115,7 @@ function parseSemesterEntry(el) {
114
115
  return {
115
116
  room: room || undefined,
116
117
  subject,
117
- type: typeMatch?.[1] ?? "",
118
+ type: parseLessonTypeAfterSubject(subjectEl),
118
119
  weeks: parseWeeks(weeksMatch?.[1] ?? ""),
119
120
  teacher: teacherLine ? parseTeacher(teacherLine) : undefined,
120
121
  subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
@@ -170,13 +171,14 @@ function parseSessionEntry(td) {
170
171
  return null;
171
172
  const possibleChanges = (td.getAttribute("class") ?? "").includes("want") || undefined;
172
173
  const subjectEl = td.querySelector('span[style*="color: blue"]');
173
- const subject = subjectEl ? text(subjectEl) : "";
174
+ if (!subjectEl)
175
+ return null;
176
+ const subject = text(subjectEl);
174
177
  if (!subject)
175
178
  return null;
176
179
  const room = parseEntryRoom(fullHtml, subject);
177
180
  // Type: parenthesized text after </span>, case-insensitive
178
- const typeMatch = plainText.match(FLEXIBLE_LESSON_TYPE_RE_I);
179
- const type = typeMatch ? typeMatch[1].replace(/\.$/, "").toLowerCase() : "";
181
+ const type = parseLessonTypeAfterSubject(subjectEl).toLowerCase();
180
182
  const subgroupMatch = plainText.match(SUBGROUP_RE);
181
183
  const parts = linesAfterSubject(fullHtml, subject);
182
184
  const teacherPart = parts.find((part) => stripDistanceMarker(part).length > 0 &&
@@ -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|\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";
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|\u043A\u0440";
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|\u043A\u0440)\\.?|\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,4 +1,4 @@
1
- export const LESSON_TYPE_PATTERN = "лк|пр|лб|зачо|зач|экз|конс|кп|из|гз|крп";
1
+ export const LESSON_TYPE_PATTERN = "лк|пр|лб|зачо|зач|экз|конс|кп|из|гз|крп|кр";
2
2
  export const FLEXIBLE_LESSON_TYPE_PATTERN = `(?:${LESSON_TYPE_PATTERN})\\.?|Экз`;
3
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");
@@ -2,8 +2,8 @@ import { parseHtml, parseTime, parseWeekParity, parseWeeks, text, } from "../../
2
2
  import { parseSemesterScheduleWith, parseSessionScheduleWith, } from "./full-schedule.js";
3
3
  import { parseGroupsString } from "./groups.js";
4
4
  import { parseSubstituteForDiv, parseSubstitutionDiv, parseTransferDiv, } from "./overlays.js";
5
- import { FLEXIBLE_LESSON_TYPE_RE_I, LESSON_TYPE_RE, SUBGROUP_RE, WEEKS_RE, } from "./patterns.js";
6
- import { containsGroupCode, hasDistanceMarker, linesAfterSubject, parseEntryRoom, stripDistanceMarker, } from "./entry-parts.js";
5
+ import { SUBGROUP_RE, WEEKS_RE } from "./patterns.js";
6
+ import { containsGroupCode, hasDistanceMarker, linesAfterSubject, parseEntryRoom, parseLessonTypeAfterSubject, stripDistanceMarker, } from "./entry-parts.js";
7
7
  export function parseTeacherSchedule(html) {
8
8
  const doc = parseHtml(html);
9
9
  if (doc.querySelector('td[id^="trd2"]')) {
@@ -49,10 +49,11 @@ function parseTeacherSemesterEntry(el) {
49
49
  cleanText = cleanText.replace(text(div), "");
50
50
  }
51
51
  const subjectEl = td.querySelector('span[style*="color: blue"]');
52
- const subject = subjectEl ? text(subjectEl) : "";
52
+ if (!subjectEl)
53
+ return null;
54
+ const subject = text(subjectEl);
53
55
  if (!subject)
54
56
  return null;
55
- const typeMatch = cleanText.match(LESSON_TYPE_RE);
56
57
  const weeksMatch = cleanText.match(WEEKS_RE);
57
58
  const room = parseEntryRoom(cleanHtml, subject);
58
59
  const groupsLine = linesAfterSubject(cleanHtml, subject).find((line) => containsGroupCode(stripDistanceMarker(line))) ?? "";
@@ -62,7 +63,7 @@ function parseTeacherSemesterEntry(el) {
62
63
  return {
63
64
  room: room || undefined,
64
65
  subject,
65
- type: typeMatch?.[1] ?? "",
66
+ type: parseLessonTypeAfterSubject(subjectEl),
66
67
  weeks: parseWeeks(weeksMatch?.[1] ?? ""),
67
68
  groups: groups.length > 0 ? groups : undefined,
68
69
  subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
@@ -79,12 +80,13 @@ function parseTeacherSessionEntry(td) {
79
80
  return null;
80
81
  const possibleChanges = (td.getAttribute("class") ?? "").includes("want") || undefined;
81
82
  const subjectEl = td.querySelector('span[style*="color: blue"]');
82
- const subject = subjectEl ? text(subjectEl) : "";
83
+ if (!subjectEl)
84
+ return null;
85
+ const subject = text(subjectEl);
83
86
  if (!subject)
84
87
  return null;
85
88
  const room = parseEntryRoom(fullHtml, subject);
86
- const typeMatch = plainText.match(FLEXIBLE_LESSON_TYPE_RE_I);
87
- const type = typeMatch ? typeMatch[1].replace(/\.$/, "").toLowerCase() : "";
89
+ const type = parseLessonTypeAfterSubject(subjectEl).toLowerCase();
88
90
  const subgroupMatch = plainText.match(SUBGROUP_RE);
89
91
  const timeMatch = fullHtml.match(/<br\s*\/?>\s*(\d{2}:\d{2})\s*-\s*(\d{2}:\d{2})/);
90
92
  if (!timeMatch)
@@ -71,6 +71,7 @@ export interface ParsedLesson {
71
71
  /** Missing means this source projection does not expose room information. */
72
72
  room?: string | null;
73
73
  subject: string;
74
+ /** Free-form portal label; empty when this source does not expose one. */
74
75
  type: string;
75
76
  /** Recurrence range; absent for concrete session lessons or unknown evidence. */
76
77
  weeks?: WeekRange;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chuvsu-js",
3
- "version": "5.2.0",
3
+ "version": "5.2.2",
4
4
  "description": "Библиотека для расписания и личного кабинета ЧГУ им. И. Н. Ульянова",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",