From 5a16b0dbbbc6b1bc7a2e6c414a392aeafe3d7d73 Mon Sep 17 00:00:00 2001 From: Last2014 Date: Mon, 19 Jan 2026 19:51:25 +0900 Subject: [PATCH] First Commit --- .gitignore | 3 + package.json | 38 ++ pnpm-lock.yaml | 361 ++++++++++++++++++ src/1.6.11/generateAuthURI.ts | 27 ++ src/1.6.11/map.ts | 2 + src/1.6.11/types/api/map.ts | 18 + src/1.6.11/types/api/me/index.ts | 8 + src/1.6.11/types/api/me/notification/index.ts | 58 +++ src/1.6.11/types/api/me/notification/read.ts | 13 + src/1.6.11/types/api/me/settings.ts | 62 +++ src/1.6.11/types/api/serverinfo-api.ts | 73 ++++ src/1.6.11/types/api/ueuse/index.ts | 21 + src/1.6.11/types/api/users/follow.ts | 23 ++ src/1.6.11/types/api/users/index.ts | 54 +++ src/1.6.11/types/auth.ts | 38 ++ src/1.6.11/types/modules/error/auth.ts | 10 + src/1.6.11/types/modules/error/critical.ts | 6 + src/1.6.11/types/modules/error/follow.ts | 6 + src/1.6.11/types/modules/error/input.ts | 6 + src/1.6.11/types/modules/error/ueuse.ts | 6 + src/1.6.11/types/modules/error/update.ts | 13 + src/1.6.11/types/modules/error/upload.ts | 44 +++ src/1.6.11/types/modules/role.ts | 10 + src/1.6.11/types/modules/ueuse.ts | 204 ++++++++++ src/index.ts | 114 ++++++ src/lib/error.ts | 7 + src/lib/fetch.ts | 68 ++++ tsconfig.json | 28 ++ 28 files changed, 1321 insertions(+) create mode 100644 .gitignore create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/1.6.11/generateAuthURI.ts create mode 100644 src/1.6.11/map.ts create mode 100644 src/1.6.11/types/api/map.ts create mode 100644 src/1.6.11/types/api/me/index.ts create mode 100644 src/1.6.11/types/api/me/notification/index.ts create mode 100644 src/1.6.11/types/api/me/notification/read.ts create mode 100644 src/1.6.11/types/api/me/settings.ts create mode 100644 src/1.6.11/types/api/serverinfo-api.ts create mode 100644 src/1.6.11/types/api/ueuse/index.ts create mode 100644 src/1.6.11/types/api/users/follow.ts create mode 100644 src/1.6.11/types/api/users/index.ts create mode 100644 src/1.6.11/types/auth.ts create mode 100644 src/1.6.11/types/modules/error/auth.ts create mode 100644 src/1.6.11/types/modules/error/critical.ts create mode 100644 src/1.6.11/types/modules/error/follow.ts create mode 100644 src/1.6.11/types/modules/error/input.ts create mode 100644 src/1.6.11/types/modules/error/ueuse.ts create mode 100644 src/1.6.11/types/modules/error/update.ts create mode 100644 src/1.6.11/types/modules/error/upload.ts create mode 100644 src/1.6.11/types/modules/role.ts create mode 100644 src/1.6.11/types/modules/ueuse.ts create mode 100644 src/index.ts create mode 100644 src/lib/error.ts create mode 100644 src/lib/fetch.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..099dea8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/dist +/node_modules +*test* \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..bfe8343 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "better-uwuzu-sdk", + "version": "1.0.0", + "description": "A better uwuzu SDK.", + "main": "dist/index.js", + "types": "dist/types/index.d.ts", + "type": "module", + "scripts": { + "build": "tsc && tsc-alias", + "prepare": "tsc && tsc-alias" + }, + "keywords": [ + "uwuzu", + "sns", + "sdk", + "javascript", + "typescript", + "js", + "ts" + ], + "author": { + "name": "Last2014", + "email": "info@last2014.com", + "url": "https://about.last2014.com" + }, + "license": "MIT", + "packageManager": "pnpm@10.17.0", + "devDependencies": { + "@types/node": "^25.0.3", + "fs": "0.0.1-security", + "tsc-alias": "^1.8.16", + "typescript": "^5.9.3", + "uuid": "^13.0.0" + }, + "dependencies": { + "@types/semver": "^7.7.1" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..a8c2514 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,361 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@types/semver': + specifier: ^7.7.1 + version: 7.7.1 + devDependencies: + '@types/node': + specifier: ^25.0.3 + version: 25.0.3 + fs: + specifier: 0.0.1-security + version: 0.0.1-security + tsc-alias: + specifier: ^1.8.16 + version: 1.8.16 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + uuid: + specifier: ^13.0.0 + version: 13.0.0 + +packages: + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@types/node@25.0.3': + resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + fs@0.0.1-security: + resolution: {integrity: sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mylas@2.1.14: + resolution: {integrity: sha512-BzQguy9W9NJgoVn2mRWzbFrFWWztGCcng2QI9+41frfk+Athwgx3qhqhvStz7ExeUUu7Kzw427sNzHpEZNINog==} + engines: {node: '>=16.0.0'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + plimit-lit@1.6.1: + resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} + engines: {node: '>=12'} + + queue-lit@1.5.2: + resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==} + engines: {node: '>=12'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tsc-alias@1.8.16: + resolution: {integrity: sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==} + engines: {node: '>=16.20.2'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + hasBin: true + +snapshots: + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@types/node@25.0.3': + dependencies: + undici-types: 7.16.0 + + '@types/semver@7.7.1': {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + array-union@2.1.0: {} + + binary-extensions@2.3.0: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + commander@9.5.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + fs@0.0.1-security: {} + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + ignore@5.3.2: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mylas@2.1.14: {} + + normalize-path@3.0.0: {} + + path-type@4.0.0: {} + + picomatch@2.3.1: {} + + plimit-lit@1.6.1: + dependencies: + queue-lit: 1.5.2 + + queue-lit@1.5.2: {} + + queue-microtask@1.2.3: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + resolve-pkg-maps@1.0.0: {} + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + slash@3.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tsc-alias@1.8.16: + dependencies: + chokidar: 3.6.0 + commander: 9.5.0 + get-tsconfig: 4.13.0 + globby: 11.1.0 + mylas: 2.1.14 + normalize-path: 3.0.0 + plimit-lit: 1.6.1 + + typescript@5.9.3: {} + + undici-types@7.16.0: {} + + uuid@13.0.0: {} diff --git a/src/1.6.11/generateAuthURI.ts b/src/1.6.11/generateAuthURI.ts new file mode 100644 index 0000000..8635c3f --- /dev/null +++ b/src/1.6.11/generateAuthURI.ts @@ -0,0 +1,27 @@ +import type { generateAuthURIOptions } from "1.6.11/types/auth"; +import { v4 as uuidv4 } from "uuid"; + +/** ユーザー認可によるトークン取得のURIを生成します。 */ +export default function generateAuthURI(options: generateAuthURIOptions) { + const session = options.session ?? uuidv4(); + const uri = new URL("/api/auth", options.origin); + uri.searchParams.set("session", session); + uri.searchParams.set("client", options.name); + uri.searchParams.set("scope", options.scope.join(",")); + if (options.icon) + uri.searchParams.set("icon", options.icon.toString()); + if (options.about) + uri.searchParams.set("about", options.about); + if (options.callback) { + if (options.callback.insertSession) + options.callback.url.searchParams.set(options.callback.insertSession, session); + uri.searchParams.set("icon", options.callback.toString()); + } + + return { + /** 生成されたURI */ + uri: uri, + /** session */ + session: session, + }; +} \ No newline at end of file diff --git a/src/1.6.11/map.ts b/src/1.6.11/map.ts new file mode 100644 index 0000000..a85e335 --- /dev/null +++ b/src/1.6.11/map.ts @@ -0,0 +1,2 @@ +import { ApiMap } from "1.6.11/types/api/map"; +export default ApiMap; \ No newline at end of file diff --git a/src/1.6.11/types/api/map.ts b/src/1.6.11/types/api/map.ts new file mode 100644 index 0000000..3cfe33c --- /dev/null +++ b/src/1.6.11/types/api/map.ts @@ -0,0 +1,18 @@ +import ServerInfo from "1.6.11/types/api/serverinfo-api"; +import Users from "1.6.11/types/api/users"; +import UsersFollow from "1.6.11/types/api/users/follow"; +import Me from "1.6.11/types/api/me"; +import MeNotification from "1.6.11/types/api/me/notification"; +import MeNotificationRead from "1.6.11/types/api/me/notification/read"; +import MeSettings from "1.6.11/types/api/me/settings"; +import Ueuse from "1.6.11/types/api/ueuse"; + +export type ApiMap = + & ServerInfo + & Me + & MeNotification + & MeNotificationRead + & MeSettings + & Users + & UsersFollow + & Ueuse; \ No newline at end of file diff --git a/src/1.6.11/types/api/me/index.ts b/src/1.6.11/types/api/me/index.ts new file mode 100644 index 0000000..4d267ea --- /dev/null +++ b/src/1.6.11/types/api/me/index.ts @@ -0,0 +1,8 @@ +import { UserResponse } from "1.6.11/types/api/users"; + +export default interface Me { + "me/": { + body: never; + response: UserResponse; + }; +}; \ No newline at end of file diff --git a/src/1.6.11/types/api/me/notification/index.ts b/src/1.6.11/types/api/me/notification/index.ts new file mode 100644 index 0000000..c32eb4b --- /dev/null +++ b/src/1.6.11/types/api/me/notification/index.ts @@ -0,0 +1,58 @@ +import InputError from "1.6.11/types/modules/error/input"; +import AuthError from "1.6.11/types/modules/error/auth"; + +export default interface MeNotification { + "me/notification/": { + body?: { + /** 制限数 */ + limit?: number; + /** ページ */ + page?: number; + }; + response: { + /** 成功かどうか */ + success: true; + /** 通知 */ + data: { + /** 送信元 */ + from: { + /** ユーザー名(表示名) */ + username: string; + /** ユーザーID */ + userid: string; + /** アイコン画像URL */ + user_icon: string; + /** ヘッダー画像URL */ + user_header: string; + }; + /** カテゴリ */ + category: "system" | "favorite" | + "reply" | "reuse" | + "ueuse" | "follow" | + "mention" | "other" | + "login"; + /** タイトル */ + title: string; + /** 内容 */ + text: string; + /** + * 送信時刻 + * YYYY-MM-DD HH:MM:SS形式です。 + */ + datetime: string; + /** + * 関連するuniqid + * 例えば、返信やリユーズ、メンションなどの場合に参照ユーズのuniqidが入ります。 + */ + valueid?: string; + /** 既読かどうか */ + is_checked: boolean; + }[]; + } | InputError | AuthError | { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: "notification_not_found"; + } + } +}; \ No newline at end of file diff --git a/src/1.6.11/types/api/me/notification/read.ts b/src/1.6.11/types/api/me/notification/read.ts new file mode 100644 index 0000000..d7cf6e3 --- /dev/null +++ b/src/1.6.11/types/api/me/notification/read.ts @@ -0,0 +1,13 @@ +import InputError from "1.6.11/types/modules/error/input"; +import AuthError from "1.6.11/types/modules/error/auth"; +import UpdateError from "1.6.11/types/modules/error/update"; + +export default interface MeNotificationRead { + "me/notification/read": { + body: never; + response: { + /** 成功かどうか */ + success: true; + } | InputError | AuthError | UpdateError; + }; +}; \ No newline at end of file diff --git a/src/1.6.11/types/api/me/settings.ts b/src/1.6.11/types/api/me/settings.ts new file mode 100644 index 0000000..0b947cb --- /dev/null +++ b/src/1.6.11/types/api/me/settings.ts @@ -0,0 +1,62 @@ +import InputError from "1.6.11/types/modules/error/input"; +import AuthError from "1.6.11/types/modules/error/auth"; +import UpdateError from "1.6.11/types/modules/error/update"; +import { Upload1Error, Upload2Error, UploadCommonError } from "1.6.11/types/modules/error/upload"; + +type AtLeastOne = { + [K in keyof T]: Required> & Partial> +}[keyof T]; + +type EmptyString = ""; + +type UsernameErrors = + B extends { username: EmptyString } + ? { + success: false; + error_code: + | "表示名を入力してください。(USERNAME_INPUT_PLEASE)"; + } + : B extends { username: string } + ? { + success: false; + error_code: + | "ユーザーネームは50文字以内で入力してください。(USERNAME_OVER_MAX_COUNT)"; + } + : never; + +type ProfileErrors = + B extends { profile: string } + ? { + success: false; + error_code: + | "プロフィールは1024文字以内で入力してください。(INPUT_OVER_MAX_COUNT)"; + } + : never; + +type Response = + | { + /** 成功かどうか */ + success: true; + } + | InputError + | AuthError + | UpdateError + | UploadCommonError + | Upload1Error + | Upload2Error + | UsernameErrors + | ProfileErrors; + +export default interface MeSettings { + "me/settings/": { + body: AtLeastOne<{ + username: string | EmptyString; + profile: string; + icon: string; + header: string; + }>; + response: Response< + MeSettings["me/settings/"]["body"] + >; + }; +} diff --git a/src/1.6.11/types/api/serverinfo-api.ts b/src/1.6.11/types/api/serverinfo-api.ts new file mode 100644 index 0000000..7dbb976 --- /dev/null +++ b/src/1.6.11/types/api/serverinfo-api.ts @@ -0,0 +1,73 @@ +export default interface ServerInfo { + "serverinfo-api": { + body: never; + response: { + server_info: { + /** サーバー名 */ + server_name: string; + /** サーバーアイコン画像URL */ + server_icon: string; + /** サーバー説明文 */ + server_description: string; + /** サーバー管理者 */ + adminstor: { + /** 名前 */ + name: string; + /** メールアドレス */ + email: string; + }; + /** 利用規約URL */ + terms_url: string; + /** プライバシーポリシーURL */ + privacy_policy_url: string; + /** 最大文字数 */ + max_ueuse_length: number; + /** 招待制である */ + invitation_code: boolean; + /** アカウント移行が許可されている */ + account_migration: boolean; + /** 統計 */ + usage: { + /** ユーザー数 */ + users: number; + /** ユーズ数 */ + ueuse: number; + }; + }; + /** ソフトウェア */ + software: { + /** + * 名称 + * 公式uwuzuである場合はuwuzuです。 + * 改変uwuzuでかつその改変に名称がある場合はuwuzu以外です。 + */ + name: string; + /** + * バージョン + * vは入りません。 + */ + version: string; + /** + * リポジトリURL + * 公式uwuzuである場合はhttps://github.com/Daichimarukana/uwuzuです。 + * 改変uwuzuでかつその改変内容が公開されている場合は各リポジトリURLです。 + */ + repository: string; + }; + /** お知らせ */ + server_notice: { + /** タイトル */ + title: string; + /** 内容 */ + note: string; + /** 作成者(ユーザーID) */ + editor: string; + /** + * 作成時刻 + * YYYY-MM-DD HH:MM:SS形式です。 + */ + datetime: string; + }[]; + }; + }; +} \ No newline at end of file diff --git a/src/1.6.11/types/api/ueuse/index.ts b/src/1.6.11/types/api/ueuse/index.ts new file mode 100644 index 0000000..808f970 --- /dev/null +++ b/src/1.6.11/types/api/ueuse/index.ts @@ -0,0 +1,21 @@ +import InputError from "1.6.11/types/modules/error/input"; +import AuthError from "1.6.11/types/modules/error/auth"; +import { ueuseModule } from "1.6.11/types/modules/ueuse"; +import ueuseError from "1.6.11/types/modules/error/ueuse"; + +export default interface Ueuse { + "ueuse/": { + body?: { + /** 制限数 */ + limit?: number; + /** ページ */ + page?: number; + }; + response: { + /** 成功かどうか */ + success: true; + /** ユーズ(LTL) */ + data: ueuseModule[]; + } | InputError | AuthError | ueuseError; + } +}; \ No newline at end of file diff --git a/src/1.6.11/types/api/users/follow.ts b/src/1.6.11/types/api/users/follow.ts new file mode 100644 index 0000000..ccf7a2d --- /dev/null +++ b/src/1.6.11/types/api/users/follow.ts @@ -0,0 +1,23 @@ +import InputError from "1.6.11/types/modules/error/input"; +import AuthError from "1.6.11/types/modules/error/auth"; +import UpdateError, { CouldNotComplete } from "1.6.11/types/modules/error/update"; +import ToYouNotAllowed from "1.6.11/types/modules/error/follow"; +import { UserDataNotFound } from "1.6.11/types/modules/error/critical"; + +interface Follow { + body: { + /** ユーザーID */ + userid: T; + }; + response: { + /** 成功かどうか */ + success: true; + /** ユーザーID */ + userid: T; + } | InputError | AuthError | UpdateError | CouldNotComplete | ToYouNotAllowed | UserDataNotFound; +} + +export default interface UsersFollow { + "users/follow": Follow; + "users/unfollow": Follow; +} \ No newline at end of file diff --git a/src/1.6.11/types/api/users/index.ts b/src/1.6.11/types/api/users/index.ts new file mode 100644 index 0000000..c4affe2 --- /dev/null +++ b/src/1.6.11/types/api/users/index.ts @@ -0,0 +1,54 @@ +import Role from "1.6.11/types/modules/role"; +import InputError from "1.6.11/types/modules/error/input"; +import AuthError from "1.6.11/types/modules/error/auth"; +import { UserDataNotFound } from "1.6.11/types/modules/error/critical"; + +export type UserResponse = { + /** 成功かどうか */ + success: true; + /** ユーザー名(表示名) */ + username: string; + /** ユーザーID */ + userid: string; + /** プロフィール */ + profile: string; + /** アイコン画像URL */ + user_icon: string; + /** ヘッダー画像URL */ + user_header: string; + /** + * 登録時刻 + * YYYY-MM-DD HH:MM:SS形式です。 + */ + registered_date: string; + /** フォロー */ + followee: string[]; + /** フォロー数 */ + followee_cnt: number; + /** フォロワー */ + follower: string[]; + /** フォロワー数 */ + follower_cnt: number; + /** ユーズ数 */ + ueuse_cnt: number; + /** Botである */ + isBot: boolean; + /** 管理者である */ + isAdmin: boolean; + /** ロール */ + role: Role[]; + /** オンラインステータス */ + online_status: "Online" | "Away" | "Offline" | null; + /** 言語 */ + language: "ja-JP"; +} | InputError | AuthError | UserDataNotFound; + +export default interface Users { + "users/": { + body: { + /** ユーザーID */ + userid: string; + }; + response: UserResponse; + }; +}; \ No newline at end of file diff --git a/src/1.6.11/types/auth.ts b/src/1.6.11/types/auth.ts new file mode 100644 index 0000000..2fc367f --- /dev/null +++ b/src/1.6.11/types/auth.ts @@ -0,0 +1,38 @@ +export type scope = + "read:me" | "write:me" | + "read:ueuse" | "write:ueuse" | + "read:users" | + "write:follow" | + "write:favorite" | + "read:notifications" | "write:notifications" | + "read:bookmark" | "write:bookmark"; + +export interface generateAuthURIOptions { + /** uwuzuサーバーのorigin */ + origin: string; + /** * アプリケーションの名称 */ + name: string; + /** 要求する権限 */ + scope: scope[]; + /** アプリケーションの説明 */ + about?: string; + /** アプリケーションのアイコンURL */ + icon?: URL; + /** 許可された際に移動するURL */ + callback?: { + /** コールバックURL */ + url: URL; + /** + * sessionをURLパラメータに挿入します。 + * 指定することでcallback.urlにcallback.insertSessionという名前でsessionを挿入します。 + * 指定しない場合は挿入しません。 + */ + insertSession?: string; + }; + /** + * sessionを明示的に指定します。 + * sessionはサーバー全体で一意になるようにしてください。 + * 指定しない場合はUUID v4を使用します。 + */ + session?: string; +} \ No newline at end of file diff --git a/src/1.6.11/types/modules/error/auth.ts b/src/1.6.11/types/modules/error/auth.ts new file mode 100644 index 0000000..74eb64e --- /dev/null +++ b/src/1.6.11/types/modules/error/auth.ts @@ -0,0 +1,10 @@ +export default interface AuthError { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: + "this_account_has_been_frozen" | + "token_invalid" | + "not_allow_scope" | + "token_invalid_scope"; +} \ No newline at end of file diff --git a/src/1.6.11/types/modules/error/critical.ts b/src/1.6.11/types/modules/error/critical.ts new file mode 100644 index 0000000..5310d0d --- /dev/null +++ b/src/1.6.11/types/modules/error/critical.ts @@ -0,0 +1,6 @@ +export type UserDataNotFound = { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: "critical_error_userdata_not_found"; +}; \ No newline at end of file diff --git a/src/1.6.11/types/modules/error/follow.ts b/src/1.6.11/types/modules/error/follow.ts new file mode 100644 index 0000000..2df6590 --- /dev/null +++ b/src/1.6.11/types/modules/error/follow.ts @@ -0,0 +1,6 @@ +export default interface ToYouNotAllowed { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: "you_cant_it_to_yourself"; +} \ No newline at end of file diff --git a/src/1.6.11/types/modules/error/input.ts b/src/1.6.11/types/modules/error/input.ts new file mode 100644 index 0000000..0500159 --- /dev/null +++ b/src/1.6.11/types/modules/error/input.ts @@ -0,0 +1,6 @@ +export default interface InputError { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: "input_not_found"; +} \ No newline at end of file diff --git a/src/1.6.11/types/modules/error/ueuse.ts b/src/1.6.11/types/modules/error/ueuse.ts new file mode 100644 index 0000000..38dc6d8 --- /dev/null +++ b/src/1.6.11/types/modules/error/ueuse.ts @@ -0,0 +1,6 @@ +export default interface ueuseError { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: "ueuse_not_found"; +} \ No newline at end of file diff --git a/src/1.6.11/types/modules/error/update.ts b/src/1.6.11/types/modules/error/update.ts new file mode 100644 index 0000000..9f5cc6a --- /dev/null +++ b/src/1.6.11/types/modules/error/update.ts @@ -0,0 +1,13 @@ +export default interface UpdateError { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: "update_failed"; +} + +export interface CouldNotComplete { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: "could_not_complete"; +} \ No newline at end of file diff --git a/src/1.6.11/types/modules/error/upload.ts b/src/1.6.11/types/modules/error/upload.ts new file mode 100644 index 0000000..99e7e4e --- /dev/null +++ b/src/1.6.11/types/modules/error/upload.ts @@ -0,0 +1,44 @@ +export interface UploadCommonError { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: + "ERROR" | + "使用できない画像形式です。(FILE_UPLOAD_DEKINAKATTA)"; +} + +export interface Upload1Error { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: + "アップロード失敗!(1)エラーコード:FILE_DEKASUGUI_PHP_INI_KAKUNIN" | + "アップロード失敗!(1)エラーコード:FILE_DEKASUGUI_HTML_KAKUNIN" | + "アップロード失敗!(1)エラーコード:FILE_SUKOSHIDAKE_UPLOAD" | + "アップロード失敗!(1)エラーコード:FILE_UPLOAD_DEKINAKATTA" | + "アップロード失敗!(1)エラーコード:TMP_FOLDER_NAI" | + "アップロード失敗!(1)エラーコード:FILE_KAKIKOMI_SIPPAI" | + "アップロード失敗!(1)エラーコード:PHPINFO()_KAKUNIN" | + "アップロード失敗!(1)エラーコード:TMP_FILE_NAI" | + "アップロード失敗!(1)エラーコード:SAVE_FOLDER_KAKIKOMI_KENNAI" | + "アップロード失敗!(1)エラーコード:MOVE_UPLOAD_FILE_SIPPAI" | + "アップロード失敗!(1)エラーコード: S3ERROR"; +} + +export interface Upload2Error { + /** 成功かどうか */ + success: false; + /** エラーコード */ + error_code: + "アップロード失敗!(2)エラーコード:FILE_DEKASUGUI_PHP_INI_KAKUNIN" | + "アップロード失敗!(2)エラーコード:FILE_DEKASUGUI_HTML_KAKUNIN" | + "アップロード失敗!(2)エラーコード:FILE_SUKOSHIDAKE_UPLOAD" | + "アップロード失敗!(2)エラーコード:FILE_UPLOAD_DEKINAKATTA" | + "アップロード失敗!(2)エラーコード:TMP_FOLDER_NAI" | + "アップロード失敗!(2)エラーコード:FILE_KAKIKOMI_SIPPAI" | + "アップロード失敗!(2)エラーコード:PHPINFO()_KAKUNIN" | + "アップロード失敗!(2)エラーコード:TMP_FILE_NAI" | + "アップロード失敗!(2)エラーコード:SAVE_FOLDER_KAKIKOMI_KENNAI" | + "アップロード失敗!(2)エラーコード:MOVE_UPLOAD_FILE_SIPPAI" | + "アップロード失敗!(2)エラーコード: S3ERROR"; +} \ No newline at end of file diff --git a/src/1.6.11/types/modules/role.ts b/src/1.6.11/types/modules/role.ts new file mode 100644 index 0000000..1f3695d --- /dev/null +++ b/src/1.6.11/types/modules/role.ts @@ -0,0 +1,10 @@ +export default interface Role { + /** ロール名(表示名) */ + name: string; + /** HEXカラー(#なし) */ + color: string; + /** エフェクト */ + effect: "none" | "shine" | "rainbow"; + /** ロールID */ + id: string; +} \ No newline at end of file diff --git a/src/1.6.11/types/modules/ueuse.ts b/src/1.6.11/types/modules/ueuse.ts new file mode 100644 index 0000000..2c587ee --- /dev/null +++ b/src/1.6.11/types/modules/ueuse.ts @@ -0,0 +1,204 @@ +type Abi = { + /** + * 追記 + * 追記がない場合は空文字列です。 + */ + abi: string; + /** 追記時刻 */ + abidatetime: string; +} | { + /** + * 追記 + * 追記が入力できないため常に空文字列です。 + */ + abi: ""; + /** + * 追記時刻 + * 追記が入力できないため常に0000-00-00 00:00:00です。 + */ + abidatetime: "0000-00-00 00:00:00"; +} + +type Media = + | { + /** + * 画像1URL + * 画像1がないため常に空文字列です。 + */ + photo1?: undefined; + /** + * 画像2URL + * 画像2がないため常に空文字列です。 + * 画像2は画像1が入力されていることの内包条件です。 + */ + photo2?: undefined; + /** + * 画像3URL + * 画像3がないため常に空文字列です。 + * 画像3は画像2が入力されていることの内包条件です。 + */ + photo3?: undefined; + /** + * 画像4URL + * 画像4がないため常に空文字列です。 + * 画像4は画像3が入力されていることの内包条件です。 + */ + photo4?: undefined; + /** + * 動画URL + * 動画がないため常に空文字列です。 + */ + video1?: undefined; + } | { + /** 画像1URL */ + photo1: string; + /** + * 画像2URL + * 画像2がない場合は空文字列です。 + * 画像2は画像1が入力されていることの内包条件です。 + */ + photo2?: string; + /** + * 画像3URL + * 画像3がない場合は空文字列です。 + * 画像3は画像2が入力されていることの内包条件です。 + */ + photo3?: string; + /** + * 画像4URL + * 画像4がない場合は空文字列です。 + * 画像4は画像3が入力されていることの内包条件です。 + */ + photo4?: string; + /** + * 動画URL + * 動画がないため常に空文字列です。 + */ + video1?: undefined; + } | { + /** 動画URL */ + video1: string; + /** + * 画像1URL + * 画像1がないため常に空文字列です。 + */ + photo1?: undefined; + /** + * 画像2URL + * 画像2がないため常に空文字列です。 + * 画像2は画像1が入力されていることの内包条件です。 + */ + photo2?: undefined; + /** + * 画像3URL + * 画像3がないため常に空文字列です。 + * 画像3は画像1が入力されていることの内包条件です。 + */ + photo3?: undefined; + /** + * 画像4URL + * 画像4がないため常に空文字列です。 + * 画像4は画像1が入力されていることの内包条件です。 + */ + photo4?: undefined; + }; + +interface ueuseBase { + /** ユニークID */ + uniqid: string; + /** 送信者 */ + account: { + /** ユーザー名 */ + username: string; + /** ユーザーID */ + userid: string; + /** ユーザーアイコン画像URL */ + user_icon: string; + /** ユーザーヘッダー画像URL */ + user_header: string; + /** Botフラグ(自主設定)かどうか */ + is_bot: boolean; + }; + /** いいねしたユーザーID */ + favorite: string[]; + /** いいね数 */ + favorite_cnt: number; + /** 返信数 */ + reply_cnt: number; + /** リユーズ+引用数 */ + reuse_cnt: number; + /** 送信時刻 */ + datetime: string; + /** NSFW(自主設定)かどうか */ + nsfw: boolean; +} + +interface NormalUeuse extends ueuseBase { + /** 本文 */ + text: string; + /** + * 返信先ID + * 返信でないため空文字列です。 + */ + replyid: ""; + /** + * リユーズ/引用元ID + * リユーズ/引用でないため空文字列です。 + */ + reuseid: ""; +} + +interface ReplyUeuse extends ueuseBase { + /** 本文 */ + text: string; + /** 返信先ID */ + replyid: string; + /** + * リユーズ/引用元ID + * リユーズ/引用でないため空文字列です。 + */ + reuseid: ""; +} + +interface Reuse extends ueuseBase { + /** + * 本文 + * リユーズであるため常に空文字列です。 + */ + text: ""; + /** + * 返信先ID + * 返信でないため空文字列です。 + */ + replyid: ""; + /** リユーズ/引用元ID */ + reuseid: string; + /** + * 追記 + * リユーズであるため常に空文字列です。 + */ + abi: ""; + /** + * 追記時刻 + * リユーズであるため常に0000-00-00 00:00:00です。 + */ + abidatetime: "0000-00-00 00:00:00"; +} + +interface QuoteReuse extends ueuseBase { + /** 本文 */ + text: string; + /** + * 返信先ID + * 返信でないため空文字列です。 + */ + replyid: ""; + /** 引用元ID */ + reuseid: string; +} + +export type ueuseModule = + | (NormalUeuse & Abi & Media) + | (ReplyUeuse & Abi & Media) + | (QuoteReuse & Abi & Media) + | Reuse; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..1b1e71d --- /dev/null +++ b/src/index.ts @@ -0,0 +1,114 @@ +import uwuzuError from "@/lib/error"; +import uwuzuFetch from "@/lib/fetch"; + +interface sdkOptions { + /** uwuzuサーバーのorigin */ + origin: string; + /** + * 通信に失敗した際の再試行回数です。 + * 全て失敗した場合はエラーを発生します。 + * 指定しない場合は5回が設定されます。 + */ + retry?: number; + /** + * 通信に失敗した際に動作する再試行間の待機時間(ミリ秒)です。 + * 再試行毎に2倍にされます。 + * 指定しない場合は500msが設定されます。 + */ + waiting?: number; +} + +/* 必須かどうかの推論 */ +type BodyArgs = + M[E] extends { body: never } ? [] : + M[E] extends { body: infer B } ? [body: B] : + M[E] extends { body?: infer B } ? [body?: B] : + []; + +export default class uwuzu< + M extends { [K in keyof M]: { body?: any; response: any } } +> { + readonly origin: string; + readonly retry: number; + readonly waiting: number; + private _token: string | null = null; + + constructor(options: sdkOptions) { + this.origin = options.origin; + this.retry = options.retry ?? 5; + this.waiting = options.waiting ?? 500; + + if (this.retry < 1) throw new uwuzuError("Invalid retry count."); + if (this.waiting < 1) throw new uwuzuError("Invalid base waiting time."); + if (options.origin !== new URL(options.origin).origin) + throw new uwuzuError("Invalid origin."); + } + + /** APIトークン */ + get token(): string | null { + return this._token; + } + + set token(token: string) { + if (token.length !== 64) throw new uwuzuError("Invalid token."); + this._token = token; + } + + /** APIリクエスト */ + // 型あり + public async request( + endpoint: E, + ...args: BodyArgs + ): Promise; + // 型なし + public async request( + endpoint: string, + body?: any + ): Promise; + + public async request( + endpoint: string, + ...args: any[] + ): Promise { + let bodyParsed: any = args[0] ?? {}; + + if (typeof bodyParsed === "object") { + bodyParsed = { + ...bodyParsed, + token: this._token, + }; + bodyParsed = JSON.stringify(bodyParsed); + } + + const req = await uwuzuFetch( + this.origin, + this.retry, + this.waiting, + "endpoint", + endpoint as string, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + cache: "no-store", + body: bodyParsed, + } + ); + + let res = await req.json(); + + if (res["0"] !== undefined && res.success === true) { + res.success = undefined; + + const data = Object.values(res).filter(Boolean); + + return { + success: true, + data, + }; + } + + return res; + } +} \ No newline at end of file diff --git a/src/lib/error.ts b/src/lib/error.ts new file mode 100644 index 0000000..df9b157 --- /dev/null +++ b/src/lib/error.ts @@ -0,0 +1,7 @@ +/** better-uwuzu-sdkのエラー */ +export default class uwuzuError extends Error { + constructor(message: string) { + super(message); + this.name = "uwuzuError"; + } +} \ No newline at end of file diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts new file mode 100644 index 0000000..3870b84 --- /dev/null +++ b/src/lib/fetch.ts @@ -0,0 +1,68 @@ +import uwuzuError from "@/lib/error"; + +export function generateURL( + origin: string, + endpoint: string, +) { + const uri = new URL(`/api/${endpoint}`, origin); + return uri.toString(); +} + +export default async function uwuzuFetch( + origin: string, + retryCount: number, + waitingTime: number, + inputType: "endpoint" | "other", + input: string, + init?: RequestInit, +) { + const waiting = (ms: number) => + new Promise(resolve => setTimeout(resolve, ms)); + + if (inputType === "endpoint") + input = generateURL(origin, input); + + let lastError; + + for (let i = 0; i < retryCount; i++) { + try { + if (init?.signal?.aborted) { + throw new DOMException("Aborted", "AbortError"); + } + + const req = await fetch(input, init); + + if ( + Math.floor(req.status / 200) === 1 || + Math.floor(req.status / 300) === 1 + ) { + return req; + } + + if (Math.floor(req.status / 400) === 1) + throw new uwuzuError(`Client error: HTTP${req.status} - ${req.statusText}`); + + lastError = new Error(`Request failed: HTTP${req.status} - ${req.statusText}`); + } catch (err) { + if (err instanceof DOMException && err.name === "AbortError") { + throw err; + } + + if ( + err instanceof uwuzuError && + err.name === "Client error" && + err.message.startsWith("HTTP4") + ) + throw err; + + lastError = err; + } + + if (i < retryCount - 1) { + const waitTime = waitingTime * 2 ** i; + await waiting(waitTime); + } + } + + throw lastError ?? new Error("Unknown Error"); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cceeb89 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "ESNext", + "moduleResolution": "bundler", + "outDir": "./dist", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "declarationDir": "./dist/types", + "strict": true, + "skipLibCheck": true, + "baseUrl": "./src/", + "typeRoots": [ + "./node_modules/@types", + ], + "paths": { + "@/*": ["./*"], + "1.6.11/*": ["./1.6.11/*"], + }, + }, + "tsc-alias": { + "resolveFullPaths": true, + }, +} \ No newline at end of file