diff --git a/.nvmrc b/.nvmrc index 25bf17f..2bd5a0a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 \ No newline at end of file +22 diff --git a/CHANGELOG.md b/CHANGELOG.md index c1d4d05..02e2e01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [4.0.0](https://github.com/KemingHe/OSU/compare/v3.1.1...v4.0.0) (2024-09-22) + + +### ⚠ BREAKING CHANGES + +* **package.json:** due to the sync/async import (bundling) separation, all previously defined import +will work as normal whilst async must be imported through @keminghe/osu/async + +### Bug Fixes + +* **package.json:** separated synd and async exports ([3ffb464](https://github.com/KemingHe/OSU/commit/3ffb464c36b6f69fdd2ef8967d2769073c63a681)) + ## [3.1.1](https://github.com/KemingHe/OSU/compare/v3.1.0...v3.1.1) (2024-09-21) ## [3.1.0](https://github.com/KemingHe/OSU/compare/v3.0.0...v3.1.0) (2024-09-21) diff --git a/README.md b/README.md index cb35358..b6a3bf1 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@   supports node18+ (link to nodejs.org)   @@ -42,20 +42,20 @@ > [!IMPORTANT] > -> **Now requires Node 18 and up!** Please upgrade your node version. +> **Async** accessors depend on [`jsdom`](https://github.com/jsdom/jsdom) and are `node`-only. For example: `import { getResearchPostingsAsync } from "@keminghe/osu/async";` > -> **Versions 1.1.0 and below are DEPRECATED!** Please upgrade to the latest version. +> Use import from the **synchronous** `@keminghe/osu` if you are working in a browswer environment. > [!NOTE] > -> Build by students, for students, with :heart:. **NOT** affiliated nor endorsed by official OSU. +> Build by students, for students, with :heart:. **NOT** affiliated by *official* OSU. > -> * Publicly-available data about OSU undergrad majors [here](https://undergrad.osu.edu/majors-and-academics/majors). -> * Publicly-available data about OSU student organizations [here](https://activities.osu.edu/involvement/student_organizations). +> * Publicly-available *official* data about OSU undergrad majors [here](https://undergrad.osu.edu/majors-and-academics/majors). +> * Publicly-available *official* data about OSU student orgs [here](https://activities.osu.edu/involvement/student_organizations). ## :gear: Installation -Requires [Node.js](https://nodejs.org/en/download/package-manager) >= **18** +Requires [Node.js](https://nodejs.org/en/download/package-manager) >= **8.1.0** (es2017) ```bash # Using npm: @@ -105,28 +105,42 @@ BUCKEYEMAIL_PATTERN.test("buckeyemail.1@buckeyemail.osu.edu"); // true ### Accessing All Undergrad Majors ```typescript -import { getUndergradMajors, type UndergradMajor } from "@keminghe/osu"; +import { + getUndergradMajors, + type UndergradMajor, + UndergradMajorSchema, +} from "@keminghe/osu"; const majors: UndergradMajor[] = getUndergradMajors(); +UndergradMajorSchema.array().parse(majors); console.log(majors); ``` ### Accessing All Student Organizations ```typescript -import { getStudentOrgs, type StudentOrg } from "@keminghe/osu"; +import { + getStudentOrgs, + type StudentOrg, + StudentOrgSchema, +} from "@keminghe/osu"; const orgs: StudentOrg[] = getStudentOrgs(); +StudentOrgSchema.array().parse(orgs); console.log(orgs); ``` -### Accessing All Undergrad Research Postings +### Accessing All Undergrad Research Postings (Async!) ```typescript -import { getResearchPostingsAsync } from "@keminghe/osu"; +import { ResearchPostingSchema, type ResearchPosting } from "@keminghe/osu"; + +// IMPORTANT: note the different async import path. +import { getResearchPostingsAsync } from "@keminghe/osu/async"; getResearchPostingsAsync() .then((researchPostings) => { + ResearchPostingSchema.array().parse(researchPostings); console.log(researchPostings); }) .catch((error) => { diff --git a/package.json b/package.json index e57e5fe..b15e320 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@keminghe/osu", - "version": "3.1.1", + "version": "4.0.0", "description": "Unofficial, publicly available data about The Ohio State University.", "keywords": [ "data", @@ -15,7 +15,25 @@ ], "main": "dist/index.js", "module": "dist/index.mjs", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js" + }, + "./async": { + "import": "./dist/index.async.mjs", + "require": "./dist/index.async.js" + } + }, "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "async": [ + "./dist/index.async.d.ts" + ] + } + }, + "sideEffects": false, "files": [ "dist", "README.md", @@ -50,8 +68,9 @@ "build": "tsup", "postbuild": "echo 'Build complete: src/ -> dist/'" }, + "packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b", "dependencies": { - "jsdom": "^25.0.0", + "jsdom": "^25.0.1", "zod": "^3.23.8" }, "devDependencies": { @@ -76,7 +95,7 @@ "vitest": "^2.1.1" }, "engines": { - "node": ">=14" + "node": ">=8.10.0" }, "os": [ "linux", @@ -86,6 +105,5 @@ "cpu": [ "x64", "arm64" - ], - "packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b" + ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 775eb83..4f8d20b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: jsdom: - specifier: ^25.0.0 - version: 25.0.0 + specifier: ^25.0.1 + version: 25.0.1 zod: specifier: ^3.23.8 version: 3.23.8 @@ -35,7 +35,7 @@ importers: version: 22.5.5 '@vitest/coverage-v8': specifier: ^2.1.1 - version: 2.1.1(vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.0)) + version: 2.1.1(vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.1)) commitizen: specifier: ^4.3.0 version: 4.3.0(@types/node@22.5.5)(typescript@5.6.2) @@ -71,7 +71,7 @@ importers: version: 4.3.2(typescript@5.6.2)(vite@5.4.7(@types/node@22.5.5)) vitest: specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5)(jsdom@25.0.0) + version: 2.1.1(@types/node@22.5.5)(jsdom@25.0.1) packages: @@ -1738,8 +1738,8 @@ packages: jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - jsdom@25.0.0: - resolution: {integrity: sha512-OhoFVT59T7aEq75TVw9xxEfkXgacpqAhQaYgP9y/fDqWQCMB/b1H66RfmPm/MaeaAIU9nDwMOVTlPN51+ao6CQ==} + jsdom@25.0.1: + resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} engines: {node: '>=18'} peerDependencies: canvas: ^2.11.2 @@ -2171,9 +2171,6 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - psl@1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2182,9 +2179,6 @@ packages: resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} engines: {node: '>=12.20'} - querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2233,9 +2227,6 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -2511,6 +2502,13 @@ packages: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tldts-core@6.1.47: + resolution: {integrity: sha512-6SWyFMnlst1fEt7GQVAAu16EGgFK0cLouH/2Mk6Ftlwhv3Ol40L0dlpGMcnnNiiOMyD2EV/aF3S+U2nKvvLvrA==} + + tldts@6.1.47: + resolution: {integrity: sha512-R/K2tZ5MiY+mVrnSkNJkwqYT2vUv1lcT6wJvd2emGaMJ7PHUGRY4e3tUsdFCXgqxi2QgbHjL3yJgXCo40v9Hxw==} + hasBin: true + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -2523,9 +2521,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - tough-cookie@4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} - engines: {node: '>=6'} + tough-cookie@5.0.0: + resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==} + engines: {node: '>=16'} tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -2628,10 +2626,6 @@ packages: universal-user-agent@6.0.1: resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} - universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -2644,9 +2638,6 @@ packages: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -3335,7 +3326,7 @@ snapshots: '@types/tough-cookie@4.0.5': {} - '@vitest/coverage-v8@2.1.1(vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.0))': + '@vitest/coverage-v8@2.1.1(vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -3349,7 +3340,7 @@ snapshots: std-env: 3.7.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.1(@types/node@22.5.5)(jsdom@25.0.0) + vitest: 2.1.1(@types/node@22.5.5)(jsdom@25.0.1) transitivePeerDependencies: - supports-color @@ -4450,7 +4441,7 @@ snapshots: jsbn@1.1.0: {} - jsdom@25.0.0: + jsdom@25.0.1: dependencies: cssstyle: 4.1.0 data-urls: 5.0.0 @@ -4465,7 +4456,7 @@ snapshots: rrweb-cssom: 0.7.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 4.1.4 + tough-cookie: 5.0.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 7.0.0 whatwg-encoding: 3.1.1 @@ -4861,16 +4852,12 @@ snapshots: proxy-from-env@1.1.0: {} - psl@1.9.0: {} - punycode@2.3.1: {} pupa@3.1.0: dependencies: escape-goat: 4.0.0 - querystringify@2.2.0: {} - queue-microtask@1.2.3: {} quick-lru@5.1.1: {} @@ -4952,8 +4939,6 @@ snapshots: require-from-string@2.0.2: optional: true - requires-port@1.0.0: {} - resolve-alpn@1.2.1: {} resolve-dir@1.0.1: @@ -5207,6 +5192,12 @@ snapshots: tinyspy@3.0.2: {} + tldts-core@6.1.47: {} + + tldts@6.1.47: + dependencies: + tldts-core: 6.1.47 + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -5217,12 +5208,9 @@ snapshots: dependencies: is-number: 7.0.0 - tough-cookie@4.1.4: + tough-cookie@5.0.0: dependencies: - psl: 1.9.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 + tldts: 6.1.47 tr46@1.0.1: dependencies: @@ -5307,8 +5295,6 @@ snapshots: universal-user-agent@6.0.1: {} - universalify@0.2.0: {} - universalify@2.0.1: {} update-notifier@7.1.0: @@ -5328,11 +5314,6 @@ snapshots: url-join@5.0.0: {} - url-parse@1.5.10: - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - util-deprecate@1.0.2: {} validate-npm-package-license@3.0.4: @@ -5377,7 +5358,7 @@ snapshots: '@types/node': 22.5.5 fsevents: 2.3.3 - vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.0): + vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.1): dependencies: '@vitest/expect': 2.1.1 '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.7(@types/node@22.5.5)) @@ -5400,7 +5381,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.5.5 - jsdom: 25.0.0 + jsdom: 25.0.1 transitivePeerDependencies: - less - lightningcss diff --git a/src/autoGenerated/latestAllStudentOrgs.ts b/src/autoGenerated/latestAllStudentOrgs.ts index 874e4a5..eaf1b78 100644 --- a/src/autoGenerated/latestAllStudentOrgs.ts +++ b/src/autoGenerated/latestAllStudentOrgs.ts @@ -1,6 +1,6 @@ // ./src/autoGenerated/latestAllStudentOrgs.ts // -// AUTO-GENERATED on 2024-09-21T19:54:50.205Z. DO NOT MODIFY. +// AUTO-GENERATED on 2024-09-22T19:01:00.679Z. DO NOT MODIFY. // For details see "@scripts/genStudentOrgs/". // Type imports. diff --git a/src/autoGenerated/latestAllUndergradMajors.ts b/src/autoGenerated/latestAllUndergradMajors.ts index 526b51f..b404164 100644 --- a/src/autoGenerated/latestAllUndergradMajors.ts +++ b/src/autoGenerated/latestAllUndergradMajors.ts @@ -1,6 +1,6 @@ // ./src/autoGenerated/latestAllUndergradMajors.ts // -// AUTO-GENERATED on 2024-09-21T19:54:50.260Z. DO NOT MODIFY. +// AUTO-GENERATED on 2024-09-22T19:01:00.754Z. DO NOT MODIFY. // For details see "./scripts/genUndergradMajors/". // Type imports. diff --git a/src/constants/__snapshots__/userAgents.test.ts.snap b/src/constants/__snapshots__/userAgents.test.ts.snap new file mode 100644 index 0000000..3b3bfb0 --- /dev/null +++ b/src/constants/__snapshots__/userAgents.test.ts.snap @@ -0,0 +1,21 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`The UserAgents constant array > matches the snapshot 1`] = ` +[ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.1", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.3", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.3", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/25.0 Chrome/121.0.0.0 Safari/537.3", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.3", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.3", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.3", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Unique/100.7.6266.6", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 OPR/112.0.0.", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/117.", + "Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.3", + "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.3", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.", +] +`; diff --git a/src/constants/userAgents.test.ts b/src/constants/userAgents.test.ts new file mode 100644 index 0000000..e70d9a9 --- /dev/null +++ b/src/constants/userAgents.test.ts @@ -0,0 +1,17 @@ +// ./src/constants/userAgents.test.ts +// +// Snapshot test for the UserAgents constant array. + +// Vitest essential imports. +import { describe, expect, it } from "vitest"; + +// Testing target import. +import UserAgents from "@src/constants/userAgents"; + +// ----------------------------------------------------------------------------- +// UserAgents constant array snapshot test suite. +describe("The UserAgents constant array", () => { + it("matches the snapshot", () => { + expect(UserAgents).toMatchSnapshot(); + }); +}); diff --git a/src/constants/userAgents.ts b/src/constants/userAgents.ts new file mode 100644 index 0000000..7333b84 --- /dev/null +++ b/src/constants/userAgents.ts @@ -0,0 +1,23 @@ +// ./src/constants/userAgents.ts +// +// Const array of user agents for fetching data from OSU websites. +// Ref: ./data/mostCommonDesktopUserAgents/07/28/2024.json + +const UserAgents: string[] = [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.1", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.3", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.3", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/25.0 Chrome/121.0.0.0 Safari/537.3", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.3", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.3", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.3", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Unique/100.7.6266.6", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 OPR/112.0.0.", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/117.", + "Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.3", + "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.3", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.", +]; +export default UserAgents; diff --git a/src/index.async.ts b/src/index.async.ts new file mode 100644 index 0000000..c837f0d --- /dev/null +++ b/src/index.async.ts @@ -0,0 +1,5 @@ +// ./src/index.async.ts +// +// OSU package entrypoint, specifically for jsdom (node-only) async utilities. + +export * from "@src/researchPostings/getResearchPostingsAsync"; diff --git a/src/index.test.ts b/src/index.test.ts index 67cd8ba..e8065cc 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,12 +1,13 @@ // ./src/index.test.ts // -// Integration test for the OSU package entrypoint. +// Integration test for the OSU package entrypoints. // Vitest essential imports. import { describe, expect, it } from "vitest"; -// Testing module imports. +// Testing target imports. import * as entrypoint from "@src/index"; +import * as asyncEntrypoint from "@src/index.async"; // ----------------------------------------------------------------------------- // Entrypoint integration test suite. @@ -49,6 +50,6 @@ describe("The OSU package entrypoint", () => { }); it("exports the getResearchPostingsAsync utility function", () => { - expect(entrypoint).toHaveProperty("getResearchPostingsAsync"); + expect(asyncEntrypoint).toHaveProperty("getResearchPostingsAsync"); }); }); diff --git a/src/index.ts b/src/index.ts index afce1e6..eb1688f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ // ./src/index.ts // -// OSU package entrypoint, for exporting all and utilities. +// OSU package entrypoint, specifically for browser-node agnostic utilities. // OSU name-dot-number and email utility exports export * from "@src/validators/validators"; @@ -13,4 +13,3 @@ export * from "@src/schemas/UndergradMajor"; export * from "@src/undergradMajors/getUndergradMajors"; export * from "@src/schemas/ResearchPosting"; -export * from "@src/researchPostings/getResearchPostingsAsync"; diff --git a/src/researchPostings/fetchUndergradResearchPostingsOnPageNum.ts b/src/researchPostings/fetchUndergradResearchPostingsOnPageNum.ts index 7715f0e..7332a4e 100644 --- a/src/researchPostings/fetchUndergradResearchPostingsOnPageNum.ts +++ b/src/researchPostings/fetchUndergradResearchPostingsOnPageNum.ts @@ -5,12 +5,13 @@ // jsdom essential imports. import { JSDOM } from "jsdom"; -import type { ResearchPosting } from "@src/schemas/ResearchPosting"; // Local type and util imports. +import type { ResearchPosting } from "@src/schemas/ResearchPosting"; import { getAttributeFromSelector, getTextContentFromElement, getTextContentFromSelector, + randomUserAgent, } from "@src/utils/fetchHelpers"; /** @@ -75,8 +76,7 @@ export async function fetchUndergradResearchPostingsOnPageNum( "Sec-Fetch-User": "?1", "Sec-Gpc": "1", "Upgrade-Insecure-Requests": "1", - "User-Agent": - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "User-Agent": randomUserAgent(), }, cache: "no-store", }); diff --git a/src/utils/fetchHelpers.randomUserAgent.test.ts b/src/utils/fetchHelpers.randomUserAgent.test.ts new file mode 100644 index 0000000..2cfa823 --- /dev/null +++ b/src/utils/fetchHelpers.randomUserAgent.test.ts @@ -0,0 +1,29 @@ +// ./src/utils/fetchHelpers.randomUserAgent.test.ts +// +// Unittests for the randomUserAgent function in fetchHelpers.ts. + +// Vitest essential imports. +import { describe, expect, it } from "vitest"; + +// Local constant import. +import UserAgents from "@src/constants/userAgents"; + +// Testing target import. +import { randomUserAgent } from "@src/utils/fetchHelpers"; + +// ----------------------------------------------------------------------------- +// Test suite for randomUserAgent function. +describe("The randomUserAgent fetch helper function", () => { + for (let i = 0; i < 10; i++) { + it("returns a random user agent string from the UserAgents constant", () => { + // Call the randomUserAgent function. + const userAgent: string = randomUserAgent(); + + // Expect the returned user agent to be a string. + expect(typeof userAgent).toBe("string"); + + // Expect the returned user agent to be a valid user agent string. + expect(UserAgents.includes(userAgent)).toBe(true); + }); + } +}); diff --git a/src/utils/fetchHelpers.ts b/src/utils/fetchHelpers.ts index 87fa79d..0a15301 100644 --- a/src/utils/fetchHelpers.ts +++ b/src/utils/fetchHelpers.ts @@ -2,6 +2,9 @@ // // Helper functions for fetching and querying data. +// Local constant import. +import UserAgents from "@src/constants/userAgents"; + // ----------------------------------------------------------------------------- export function getTextContentFromElement( element: Element, @@ -127,3 +130,16 @@ export async function randomDelay({ const delay: number = Math.floor(Math.random() * (max - min + 1) + min); return new Promise((resolve) => setTimeout(resolve, delay)); } + +// ----------------------------------------------------------------------------- +export function randomUserAgent(): string { + const randomAgent: string | undefined = + UserAgents[Math.floor(Math.random() * UserAgents.length)]; + + // randomeAgent is always defined, but must be checked for typescript. + /* v8 ignore next 3 */ + if (!randomAgent) { + throw new Error("Failed to generate random user agent."); + } + return randomAgent; +} diff --git a/tsup.config.ts b/tsup.config.ts index 79e059c..1f9c428 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -19,10 +19,15 @@ export default defineConfig( { // Core. ------------------------------------------------------------------- - entry : ["src/index.ts"], // Source entrypoint. - format : ["cjs", "esm"], // Output formats: CommonJS and ESM. - target : "node18", // Max backewards compatibility. - outDir : "dist", // Output directory. + // Source entrypoint, separated for better tree-shaking. + entry: [ + "src/index.ts", + "src/index.async.ts", + ], + + format: ["cjs", "esm"], // Output formats: CommonJS and ESM. + target: "es2017", // Max browser and node (12+) compatibility. + outDir: "dist", // Output directory. dts : true, // Generate declaration files. sourcemap: true, // Generate source maps, allow reverting to original code.