{"id":39357015,"url":"https://github.com/cnguy/kayn","last_synced_at":"2026-01-18T02:38:36.977Z","repository":{"id":19226050,"uuid":"86015141","full_name":"cnguy/kayn","owner":"cnguy","description":"superagent-inspired Node.js lib (w/ **some** TypeScript support) for accessing Riot's League of Legend's API (discord: cnguy#3614)","archived":false,"fork":false,"pushed_at":"2022-12-09T01:16:35.000Z","size":2189,"stargazers_count":135,"open_issues_count":39,"forks_count":32,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-09-22T07:55:12.431Z","etag":null,"topics":["api","callbacks","javascript","javascript-riot-api-wrapper","league","league-api","leagueoflegends","nodejs","nodejs-api","nodejs-riot-api-wrapper","promises","riot","riot-games-api","typescript","typescript-definitions","wrapper","wrapper-api"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cnguy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-24T01:42:22.000Z","updated_at":"2024-11-15T05:56:16.000Z","dependencies_parsed_at":"2022-09-12T10:50:52.378Z","dependency_job_id":null,"html_url":"https://github.com/cnguy/kayn","commit_stats":null,"previous_names":["chautnguyen/kindred-api","cnguy/kindred-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cnguy/kayn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cnguy%2Fkayn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cnguy%2Fkayn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cnguy%2Fkayn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cnguy%2Fkayn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cnguy","download_url":"https://codeload.github.com/cnguy/kayn/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cnguy%2Fkayn/sbom","scorecard":{"id":28864,"data":{"date":"2025-08-11","repo":{"name":"github.com/cnguy/kayn","commit":"43b087a89a1de3bc5cfefc47566359d6deb0016c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.9,"checks":[{"name":"Code-Review","score":1,"reason":"Found 3/20 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 13 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"76 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-h726-x36v-rx45","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-35q2-47q7-3pc3","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-rq8g-5pc5-wrhr","Warn: Project is vulnerable to: GHSA-p28h-cc7q-c4fg","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-hr2v-3952-633q","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-qrmc-fj45-qfc2","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-xf7w-r453-m56c","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-44pw-h2cw-w3vq","Warn: Project is vulnerable to: GHSA-jp4x-w63m-7wgm","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-fvqr-27wr-82fm","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-2m96-9w4j-wgv7","Warn: Project is vulnerable to: GHSA-7px7-7xjx-hxm8","Warn: Project is vulnerable to: GHSA-x5pg-88wf-qq4p","Warn: Project is vulnerable to: GHSA-p9wx-2529-fp83","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-fhjf-83wg-r2j9","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-6394-6h9h-cfjg","Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg","Warn: Project is vulnerable to: GHSA-6g33-f262-xjp4","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-2m39-62fm-q8r3","Warn: Project is vulnerable to: GHSA-mf6x-7mm4-x2g7","Warn: Project is vulnerable to: GHSA-mxhp-79qh-mcx6","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-14T18:33:30.590Z","repository_id":19226050,"created_at":"2025-08-14T18:33:30.590Z","updated_at":"2025-08-14T18:33:30.590Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28526582,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","callbacks","javascript","javascript-riot-api-wrapper","league","league-api","leagueoflegends","nodejs","nodejs-api","nodejs-riot-api-wrapper","promises","riot","riot-games-api","typescript","typescript-definitions","wrapper","wrapper-api"],"created_at":"2026-01-18T02:38:36.790Z","updated_at":"2026-01-18T02:38:36.953Z","avatar_url":"https://github.com/cnguy.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"A small Node.js library to work with Riot's League of Legend's API.\n\n[![NPM](https://nodei.co/npm/kayn.png)](https://nodei.co/npm/kayn/)\n\n[![Build Status](https://travis-ci.org/cnguy/kayn.svg?branch=master)](https://travis-ci.org/cnguy/kayn)\n[![API Cov. Badge](/_pictures/api_cov.png?raw=true \"API Cov. Badge\")](https://github.com/cnguy/kayn/blob/master/ENDPOINTS.md)\n[![codecov](https://codecov.io/gh/cnguy/kayn/branch/master/graph/badge.svg)](https://codecov.io/gh/cnguy/kayn)\n[![dependencies Status](https://david-dm.org/cnguy/kayn/status.svg)](https://david-dm.org/cnguy/kayn)\n\n\u003cdetails\u003e\u003csummary\u003eSimple example using promises and callbacks\u003c/summary\u003e\n\n\u003cp\u003e\n\n####\n\n```javascript\nconst _kayn = require('kayn')\nconst Kayn = _kayn.Kayn\nconst REGIONS = _kayn.REGIONS\n\nconst kayn = Kayn(/* process.env.RIOT_LOL_API_KEY */)(/* optional config */)\n\nkayn.Summoner.by\n    .name('Contractz')\n    .region(REGIONS.NORTH_AMERICA) // same as 'na'\n    .callback(function(unhandledError, summoner) {\n        kayn.Matchlist.by\n            .accountID(summoner.accountId)\n            /* Note that region falls back to default if unused. */\n            .query({\n                season: 11,\n                queue: [420, 440],\n            })\n            .then(function(matchlist) {\n                console.log('actual matches:', matchlist.matches)\n                console.log('total number of games:', matchlist.totalGames)\n            })\n            .catch(console.error)\n    })\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eSame example (as the above) using async/await, destructuring, and template strings\u003c/summary\u003e\n\n\u003cp\u003e\n\n####\n\n```javascript\nimport { Kayn, REGIONS } from 'kayn'\n\nconst kayn = Kayn(/* process.env.RIOT_LOL_API_KEY */)(/* optional config */)\n\nconst main = async () =\u003e {\n    const { accountId } = await kayn.Summoner.by.name('Contractz')\n    // ^ default region is used, which is `na` unless specified in config\n    const { matches, totalGames } = await kayn.Matchlist.by\n        .accountID(accountId)\n        .query({ season: 11, champion: 67 })\n        .region(REGIONS.NORTH_AMERICA)\n\n    console.log('actual matches:', matches)\n    console.log(`total number of games: ${totalGames}`)\n}\n\nmain()\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample of getting match information from 100 matches at once\u003c/summary\u003e\n\n\u003cp\u003e\n\n####\n```javascript\nconst getChampionIdFromMatch = (match, accountId) =\u003e {\n    for (let i in match.participantIdentities) {\n        if (\n            match.participantIdentities[i].player.currentAccountId ===\n            accountId\n        ) {\n            return match.participants[parseInt(i)].championId\n        }\n    }\n}\n\nconst main = async kayn =\u003e {\n    const { accountId } = await kayn.SummonerV4.by.name('Contractz')\n    const rankGameIds = (await kayn.MatchlistV4.by\n        .accountID(accountId)\n        .query({ queue: 420 })).matches.map(el =\u003e el.gameId)\n    const championIds = await Promise.all(\n        rankGameIds.map(async gameId =\u003e {\n            const matchDetail = await kayn.MatchV4.get(gameId).region('na')\n            return getChampionIdFromMatch(matchDetail, accountId)\n        }),\n    )\n    console.log(championIds.slice(0, 5), championIds.length)\n}\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample of getting DDragon information of banned champions in a game\u003c/summary\u003e\n\n\u003cp\u003e\n\n####\n\n```javascript\nconst main = async (kayn) =\u003e {\n    const match = await kayn.Match.get(2877485196)\n    const bans = match.teams.map(m =\u003e m.bans).reduce((t, c) =\u003e t.concat(c), [])\n    const ids = bans.map(b =\u003e b.championId)\n    const ddragonChampions = await kayn.DDragon.Champion.listDataByIdWithParentAsId()\n    const champions = ids.map(id =\u003e ddragonChampions.data[id])\n    console.log(champions)\n}\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n[More Examples](#more-examples)\n\u003cdetails\u003e\u003csummary\u003eExample Selected Implementations\u003c/summary\u003e\n\n\u003cp\u003e\n\n####\n- [Get last 10 matches asynchronously and efficiently (vs slower version as well)](https://github.com/cnguy/kayn/blob/master/examples/async.await/v4/get-last-10-ranked-matches-efficiently.js)\n- [Get champion name, win status, season id, and game date for past 5 ranked games of a player](https://github.com/cnguy/kayn/blob/master/examples/async.await/v4/get-detailed-info-from-last-5-ranked-matches.js)\n\n... [More Examples](#more-examples)\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n# Table of Contents:\n* [Features](#features)\n* [Methods](#methods)\n* [Installation \u0026 (Riot API) Usage](#installation-and-usage)\n* [DDragon Usage](#ddragon-usage)\n* [Configuration](#configuration)\n* [My Project](#my-project)\n* [Bugs / Changelog / Disclaimer](#bugs)\n* [FAQ](#faq)\n\n# Features\n\n## Rate Limiting\n\nHandled by [Colorfulstan](https://github.com/Colorfulstan)'s wonderful [riot-ratelimiter](https://github.com/Colorfulstan/RiotRateLimiter-node).\n\nSee [RATELIMITING.md](https://github.com/cnguy/kayn/blob/master/RATELIMITING.md).\n\n## All Endpoints Covered\n\n## Caching\n\nCurrently supports a basic JS cache (for simple scripts), node-lru-cache, and Redis.\n\n## Compatible with Callbacks, Promises, Async / Await\n\n## TypeScript Support\n\nWorks immediately upon installation.\n\nAs of v0.8.0, full DTO's are provided thanks to [MingweiSamuel](https://github.com/MingweiSamuel)'s [auto-updated Swagger JSON](https://github.com/MingweiSamuel/riotapi-schema).\n\n# Methods\n\nCheck out [ENDPOINTS.md](https://github.com/cnguy/kayn/blob/master/ENDPOINTS.md) to see kayn's methods, as well as the endpoints covered.\n\n## Documentation\n\nThe auto-generated ESDoc documentation can be found [here](http://kayn.surge.sh).\n\n# Installation and Usage\n\nThe minimum required [Node.js version is v7.6.0](https://nodejs.org/en/blog/release/v7.6.0/) for native async/await support (there's only a mere line in the codebase, though).\n\n## npm\n\n```sh\nnpm i --save kayn\n```\n\n## yarn\n\n```sh\nyarn add kayn\n```\n\n### Quick Setup with [Default Config](https://github.com/cnguy/kayn/blob/master/lib/KaynConfig.js)\n\n```javascript\nconst { Kayn, REGIONS } = require('kayn')\nconst kayn = Kayn('RGAPI-my-api-key')(/*{\n    region: REGIONS.NORTH_AMERICA,\n    apiURLPrefix: 'https://%s.api.riotgames.com',\n    locale: 'en_US',\n    debugOptions: {\n        isEnabled: true,\n        showKey: false,\n    },\n    requestOptions: {\n        shouldRetry: true,\n        numberOfRetriesBeforeAbort: 3,\n        delayBeforeRetry: 1000,\n        burst: false,\n        shouldExitOn403: false,\n    },\n    cacheOptions: {\n        cache: null,\n        timeToLives: {\n            useDefault: false,\n            byGroup: {},\n            byMethod: {},\n        },\n    },\n}*/)\n```\n\n**Note: Any config passed in is deeply merged with the default config.**\n\n### Environment Variables\n\n```javascript\nconst kayn = Kayn(/* process.env.RIOT_LOL_API_KEY */)(myConfig)\n```\n\nAlthough it is possible to manually pass in the API key, it is preferable to store the key in a secret file (which should not be committed).\n\nThis allows `kayn` to be constructed like in the above code.\n\n```sh\n# filename: .env\nRIOT_LOL_API_KEY=RGAPI-my-api-key\n```\n\n### Callbacks\n\n```javascript\nkayn.Summoner.by.name('Contractz').callback(function(err, summoner) {\n    // do something\n})\n```\n\n### Promises\n\n```javascript\nkayn.Summoner.by.name('Contractz')\n    .then(summoner =\u003e doSomething(summoner))\n    .then(console.log)\n    .catch(error =\u003e console.error(error))\n```\n\n### Async / Await\n\n```javascript\nconst main = async () =\u003e {\n    const ctz = await kayn.Summoner.by.name('Contractz')\n}\n```\n\n### Region\n\nThis forces a request to target a specific region instead of the default region set in `kayn`'s config. If `.region()` is not used, `kayn` will use the default region to make requests.\n\n```javascript\nkayn.Summoner.by.name('hide on bush')\n    .region(REGIONS.KOREA)\n    .callback(function(error, summoner) {\n        doSomething(summoner)\n    })\n```\n\n#### Region without Throwing\n\nThere is another utility method in case if you want to avoid handling exceptions caused by `.region()`. This method simply catches `.region()`'s exception, and so it will fall back to the default region as well.\n\n```javascript\nkayn.Summoner.by.name('hide on bush')\n    .regionNoThrow(null) // No error thrown. Uses default region.\n\nkayn.Summoner.by.name('hide on bush')\n    .regionNoThrow(3) // Same as above.\n\nkayn.Summoner.by.name('hide on bush')\n    .regionNoThrow('kr524') // Same as above.\n```\n\n### Query Parameters\n\nYou can pass in strings, numbers, or arrays as values. Just pass in whatever Riot expects. :)\n\n```javascript\nkayn.Matchlist.by.accountID(3440481)\n    .region(REGIONS.KOREA)\n    .query({\n        season: 9,\n        queue: [420, 440],\n    })\n    .callback(function(err, matchlist) {\n        console.log(matchlist.matches.length)\n    })\n```\n\n### Request Errors\n\nErrors as of v0.8.7 return the following error object:\n\n```javascript\n{\n    statusCode: 42, // some random number\n    url: '', // the debug URL that is used in logging as well\n    error: {} // the rest of the error object\n}\n```\n\n# DDragon Usage\n\n### Version\n\nThis forces a request to target a specific version and is no longer mandatory as of `v0.8.22`.\n\n```javascript\nkayn.DDragon.Champion.list()\n    .version('8.15.1') /* Explicit */\n    .callback(function(error, champions) {\n        console.log(champions)\n    })\n\n// Let's say the config region is North America and that the latest version\n// for the champion endpoint in NA is 8.24.1\nkayn.DDragon.Champion.list() // Implicitly targets 8.24.1\n    .callback(function(error, champions) {\n        console.log(champions)\n    })\n\n// Same thing as above, but gets the versions for a different region from the configuration.\nkayn.DDragon.Champion.list().region('br')\n    .callback(function(error, champions) {\n        console.log(champions)\n    })\n```\n\n#### Notes about Optional Version Argument\n\nWhenever you make a request that does not have a version passed in, `kayn` will automatically grab all the JSON versions associated with your default region or through the `region()` method.\n\nIf you do not have caching enabled, note that each request with no version passed will always send an additional request for grabbing the version. Otherwise, it follows standard caching.\n\n##### No Cache Example\n```javascript\n// Calls the Realm list endpoint to get the version for North America's champion data. It then gets the champions.\nkaynWithNoCache.DDragon.Champion.list()\n\n// Gets versions for 'kr' instead of default region.\nkaynWithNoCache.DDragon.Champion.list().region('kr')\n```\n\n##### Cache Example\n```javascript\n// Calls Kayn.Realm.list(), caches it, and then gets the version for North America's champion data. It then gets the champions. \nkaynWithCache.DDragon.Champion.list()\n// Retrieves the cached version (because we already called the realm endpoint under the hood) for North America's champion data and then gets the champions.\nkaynWithCache.DDragon.Champion.list()\n```\n\n### Region\n\nThis is only for /cdn/data/___/___.json-esque requests. It is a helper method that allows `kayn` to not force the user to have to pass in a version.\n\n```javascript\nkayn.DDragon.Champion.list()\n    .region('kr')\n    .locale('ko_KR')\n```\n\n### Locale\n\nThis forces a request to target a specific locale instead of the default locale set in `kayn`'s config. If `.locale()` is not used, `kayn` will use the default locale to make requests.\n\n```javascript\nkayn.DDragon.Champion.list()\n    .version('8.15.1')\n    .locale('sg_SG')\n    .callback(function(error, champions) {\n        console.log(champions)\n    })\n\nkayn.DDragon.Champion.list()\n    .version('8.15.1')\n    /* Locale not specified. Uses default locale, which is 'en_US' in the config and is adjustable. */\n    .callback(function(error, champions) {\n        console.log(champions)\n    })\n```\n\n## Realm -\u003e Version Example\n\nThis example firstly hits the `Realm` endpoint, which grabs a list of versions where each version corresponds with some type of DDragon endpoint (`Champion`, `Item`, etc). I then grab the version associated with the `Champion` endpoint to get the latest static champion list for the NA region. Note that `kayn.DDragon.Realm.list` uses the default region or takes in a region specified, which is why I am able to avoid passing in extra arguments.\n\n```javascript\nconst main = async () =\u003e {\n    const kayn = Kayn('RGAPI-my-api-key')({\n        region: REGIONS.NORTH_AMERICA,\n        locale: 'en_US',\n        debugOptions: {\n            isEnabled: true,\n            showKey: false,\n        },\n        requestOptions: {}, // Doesn't apply to DDragon requests\n        cacheOptions: {\n            cache: new LRUCache({ max: 5000 }),\n            timeToLives: {\n                useDefault: true, // Cache DDragon by default!\n            },\n        },\n    })\n\n    /*\n        kayn.DDragon.Realm.list('na') =\u003e\n        {\n        \t\"n\": {\n        \t\t\"item\": \"8.17.1\",\n        \t\t\"rune\": \"7.23.1\",\n        \t\t\"mastery\": \"7.23.1\",\n        \t\t\"summoner\": \"8.17.1\",\n          \t\t\"champion\": \"8.17.1\",\n          \t\t\"profileicon\": \"8.17.1\",\n        \t\t\"map\": \"8.17.1\",\n          \t\t\"language\": \"8.17.1\",\n        \t\t\"sticker\": \"8.17.1\"\n          \t},\n        \t\"v\": \"8.17.1\",\n          \t\"l\": \"en_US\",\n          \t\"cdn\": \"https://ddragon.leagueoflegends.com/cdn\",\n          \t\"dd\": \"8.17.1\",\n          \t\"lg\": \"8.17.1\",\n          \t\"css\": \"8.17.1\",\n          \t\"profileiconmax\": 28,\n          \t\"store\": null\n        }\n    */\n\n    // Same as `const championVersion = data.n.champion`.\n    const { n: { champion: championVersion } } = await kayn.DDragon.Realm.list(/* default region */)\n    const championList = await kayn.DDragon.Champion.list().version(championVersion)\n    console.log(championList)\n}\n```\n\n## dataById and dataByIdWithParentAsId\n\nAs of v0.8.19, the following DDragon.Champion functions have been added:\n\n```javascript\nDDragon.Champion.getDataById(championName: string)\nDDragon.Champion.getDataByIdWithParentAsId(championName: string)\nDDragon.Champion.listDataById()\nDDragon.Champion.listDataByIdWithParentAsId()\nDDragon.Champion.listFullDataById()\nDDragon.Champion.listFullDataByIdWithParentAsId()\n```\n\nGiven:\n\n```json\n{\n  ...\n\t\"data\": {\n        ...\n\t\t\"Aatrox\": {\n            ...\n\t\t\t\"id\": \"Aatrox\",\n\t\t\t\"key\": \"266\"\n\t\t}\n\t}\n}\n```\n\n`someFunctionDataById` changes the shape to:\n\n```json\n{\n  ...\n\t\"data\": {\n        ...\n\t\t\"Aatrox\": {\n            ...\n\t\t\t\"id\": \"266\",\n\t\t\t\"key\": \"Aatrox\"\n\t\t}\n\t}\n}\n```\n\nwhile `someFunctionDataByIdWithParentAsId` changes the shape to:\n\n```json\n{\n  ...\n\t\"data\": {\n    ...\n\t\t\"266\": {\n            ...\n\t\t\t\"id\": \"266\",\n\t\t\t\"key\": \"Aatrox\"\n\t\t}\n\t}\n}\n```\n\nThese functions also cache their own data, separate from the functions that make the actual HTTP requests. They also have their own method names, and are cached under the 'DDRAGON' namespace.\n\n# More Examples\n\n* [Regular JavaScript](https://github.com/cnguy/kayn/tree/master/examples/es5)\n* [Async Await](https://github.com/cnguy/kayn/tree/master/examples/async.await)\n\n# Configuration\n\n### region\n\nDefault: 'na'\n\n### locale\n\nDefault: 'en_US'\n\n### apiURLPrefix\n\nDefault: '`https://%s.api.riotgames.com`'\n\n## Request Options\n\n### numberOfRetriesBeforeAbort\n\nDefault: 3 attempts.\n\n### delayBeforeRetry\n\nDefault: 1000 ms (1 second).\n\nThis option will be scrapped in the future in favor for more flexibility (linear, exponential, random, etc).\n\n### burst\n\nDefault: false.\n\nDisabled by default in favor of `spread`.\n\n`true` =\u003e `riotratelimiter` will use its burst strategy.\n\n`false` =\u003e `riotratelimiter` will use its spread strategy.\n\n### shouldExitOn403\n\nDefault: false.\n\nThis option will force the process to quit if your API key is blacklisted or invalid.\n\n## Cache Options\n\nTo cache, firstly create some cache that implements the `get` and `set` functions that `kayn` interfaces with, and then pass that cache instance to `cacheOptions.cache`.\n\n`ttls` are method ttls. This part is pretty inconvenient right now. Suggestions are welcome.\n\nCurrent caches:\n* basic in-memory cache\n* [(node) lru-cache](https://github.com/isaacs/node-lru-cache)\n* [Redis cache](https://github.com/NodeRedis/node_redis)\n\nFor the last two caches, the options that they take are the same options that their respective docs list out. In other words, I basically export wrappers that takes in the options and just passes it to the actual cache client.\n\n```javascript\nimport { Kayn, REGIONS, METHOD_NAMES, BasicJSCache, LRUCache, RedisCache } from 'kayn'\n\nconst redisCache = new RedisCache({\n    host: 'localhost',\n    port: 5000,\n    keyPrefix: 'kayn',\n    password: 'hello-world',\n    // etc...\n})\n\nconst lruCache = new LRUCache({\n    max: 500,\n    dispose: (key, value) =\u003e {},\n    length: (value, key) =\u003e 1,\n    // maxAge intentionally is disabled\n})\n\nconst basicCache = new BasicJSCache()\n\nconst myCache = redisCache // or basicCache/lruCache\n\nconst kayn = Kayn(/* optional key */)({\n    region: 'na',\n    locale: 'en_US',\n    debugOptions: {\n        isEnabled: true,\n        showKey: false,\n    },\n    requestOptions: {\n        shouldRetry: true,\n        numberOfRetriesBeforeAbort: 3,\n        delayBeforeRetry: 1000,\n    },\n    cacheOptions: {\n        cache: myCache,\n        timeToLives: {\n            useDefault: true,\n            byGroup: {\n                DDRAGON: 1000 * 60 * 60 * 24 * 30, // cache for a month\n            },\n            byMethod: {\n                [METHOD_NAMES.SUMMONER.GET_BY_SUMMONER_NAME]: 1000, // ms\n            },\n        },\n    },\n})\n\nkayn.Summoner.by\n    .name('Contractz')\n    .then(() =\u003e kayn.Summoner.by.name('Contractz'))\n\n/*\n200 @ https://na1.api.riotgames.com/lol/summoner/v3/summoners/by-name/Contractz\nCACHE HIT @ https://na1.api.riotgames.com/lol/summoner/v3/summoners/by-name/Contractz\n*/\n```\n\n### TTLs???\n\nCheck out `Enums/default-ttls` and `Enums/method-names` to find the constants that you can use as keys within `byGroup` and `byMethod`.\n\nHere is the order in which ttl's resolve (highest-priority first):\n1. byMethod\n2. byGroup\n3. useDefault\n\nNote that if you're using the `ttls` prop before v0.8.9, you're perfectly fine. `ttls` is the source of truth, and has the highest priotity over the above 3 ways.\n\n#### byMethod\n\n`byMethod` takes pairs of `Enums/method-names`, which are just unique (string-type) identifiers and the ttl value you desire.\n\n```javascript\ncacheOptions: {\n    timeToLives: {\n        byMethod: {\n            [METHOD_NAMES.SUMMONER.GET_BY_SUMMONER_NAME]: 1000,\n        }\n    }\n}\n\n// Same as passing this:\n// 'SUMMONER.GET_BY_SUMMONER_NAME': 1000,\n// Constants just help for auto-complete.\n```\n\n#### byGroup\n\n`byGroup` takes pairs as well. The key difference is that it follows how Riot groups their API methods on their developer interface. You can check `Enums/method-names` once again to see how methods are grouped toegher.\n\n`byGroup` has lower priority than `byMethod`. This as you'll see is flexible.\n\n```javascript\ncacheOptions: {\n    timeToLives: {\n        byGroup: {\n            MATCH: 60 * 60 * 24 * 30, // 30 days\n        }\n        byMethod: {\n            [METHOD_NAMES.MATCH.GET_MATCHLIST]: 1000,\n        }\n    }\n}\n\n// Enums/method-names\n/*\nconst MATCH = {\n    GET_MATCH: 'MATCH.GET_MATCH',\n    GET_MATCHLIST: 'MATCH.GET_MATCHLIST',\n    GET_RECENT_MATCHLIST: 'MATCH.GET_RECENT_MATCHLIST',\n    GET_MATCH_TIMELINE: 'MATCH.GET_MATCH_TIMELINE',\n    GET_MATCH_IDS_BY_TOURNAMENT_CODE: 'MATCH.GET_MATCH_IDS_BY_TOURNAMENT_CODE',\n    GET_MATCH_BY_TOURNAMENT_CODE: 'MATCH.GET_MATCH_BY_TOURNAMENT_CODE',\n}\n*/\n```\n\nWhat this does is set the cache ttl of every single one of the above match method to 30 days. However, since `byMethod` has higher priority, we are then able to overwrite the `Matchlist.by.accountID` ttl, making it only be cached for a second instead.\n\nThis is good because the other match methods rarely change, while matchlists can change every 20 minutes.\n\n#### useDefault\n\nSimply set `useDefault` in `timeToLives` to true. This option basically sets ttls I thought made some sense. `useDefault` has the lowest priority, which means you can set it to `true`, and then overwrite it on a case-by-case basis using `byGroup` and `byMethod`.\n\n### Flushing the Cache\n\n```javascript\n// BasicJSCache O(1)\n// synchronous\nkayn.flushCache()\n// this has been turned into a promise so that it can be chained.\n// still can just be called normally though.\n// the `data` parameter returns \"OK\" just like in the RedisCache.\nasync1\n  .then(() =\u003e kayn.flushCache())\n  .then(console.log) // prints OK always. there's no way to get an error.\n  .catch(console.err)\n\n// RedisCache O(N)\n// asynchronous\nkayn.flushCache(function (err, ok) {\n  console.log(ok === \"OK\")\n})\n\nconst flush = async () =\u003e {\n  try {\n    await kayn.flushCache() // returns \"OK\", but not really necessary to store.\n  } catch (exception) {\n    console.log(exception)\n  }\n}\n\nasync1\n  .then(() =\u003e async2())\n  .then(() =\u003e kayn.flushCache())\n  .then(console.log)\n  .catch(console.log)\n```\n## Debug Options\n\n### showKey\n\nWhen logging, URLs printed out on the screen will also have the API key query string attached to it, allowing the user to conveniently inspect the response if necessary.\n\n### loggers\n\n`kayn` now uses [debug](https://www.npmjs.com/package/debug) for all logging purposes.\n\nHere are the current namespaces:\n\nkayn\n  * init\n  * request\n    * incoming\n      * success\n      * error\n    * outgoing\n  * cache\n    * set\n    * get\n\nTo enable debugging, firstly make sure `config.debugOptions.isEnabled` is `true`. Then, run your program with the desired DEBUG environment variables.\n\nFor example, if you wish to only see the request errors (404, 420, 503, 500, etc), run:\n\n```sh\nDEBUG=kayn:request:incoming:error \u003ccommand\u003e\n# DEBUG=kayn:*:error works too.\n```\n\n...where command runs the script/server/whatever (`npm run start`, `yarn start`, `node index.js`).\n\nTo enable all loggers, simply run:\n```sh\nDEBUG=kayn:* \u003ccommand\u003e\n```\n\n# My Project\n\nIf you're interested in what I have built using this library, here's a small web application I made, along with the original reddit post.\n\nOne Tricks:\n* site: http://onetricks.net\n* reddit link: https://www.reddit.com/r/leagueoflegends/comments/5x1c5c/hi_i_made_a_small_website_to_compile_a_list_of/\n* src: https://github.com/cnguy/OneTricks\n\nHere are the requests stats for anyone interested.\n\nNote that my requests stats are inflated since I'm not really caching at the moment (lol).\n\n![Alt text](/_pictures/number_of_requests.png?raw=true \"onetricks.net\")\n\n![Alt text](/_pictures/status_codes.png?raw=true \"onetricks.net\")\n\n# Bugs\n\nFeel free to make an issue (bug, typos, questions, suggestions, whatever) or pull request to fix an issue. Just remember to run `prettier` (via `yarn lint`).\n\nPackage commands:\n\n* `yarn lint`\nfor `prettier` (will add `eslint` to `prettier` soon)\n* `yarn example`\nto run the various files in `./examples`\n* `yarn build`\nto build. `yarn example` runs this command\n* `yarn test`\n\n## General Workflow\n\nHere's my general workflow when it comes to `kayn`.\n\nNote: You don't have to worry about editor configuration as long as you follow the steps.\n\n* If possible, create a unit test (make more if applicable)\n* Set `describe.only` on your test(s)\n* Run `yarn test` to make sure test is failing\n* Write implementation and run `yarn test` on completion\n* You can manually test requests in `example.js` using your own API key\n    * Preferrable just to use a .env file with kayn's default key (RIOT_LOL_API_KEY)\n    * Run `yarn example` and verify behavior manually\n* Remove `describe.only` from your tests and run the entire test suite\n* When tests pass and manual testing went well, run `yarn lint`\n* Commit and push! For forks, make sure you check out a new branch\n\n# Changelog\n\n[CHANGELOG.md](https://github.com/cnguy/kayn/blob/master/CHANGELOG.md).\n\nAs long this library is pre-1.0.0, breaking changes may be made, but will be documented and will generally not be drastic. Upon 1.0.0, SemVer will be followed strictly.\n\n# Disclaimer\n\n`kayn` isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing League of Legends. League of Legends and Riot Games are trademarks or registered trademarks of Riot Games, Inc. League of Legends © Riot Games, Inc.\n\n# FAQ\n\n## My requests seem to take a long time.\n\nYou are most likely using the default `spread` rate limiting strategy, which spreads out your requests over rate limit periods.\n\nSet `requestOptions.burst` to `true` to burst your requests instead.\n\n## I'm getting (a lot of) 429's.\n\nIf you're getting 429's, you're most likely processing huge amounts of requests that probably needs to be broken into smaller pieces (while also setting `requestOptions.burst` to `false`), needs effective caching, and/or requires a more powerful, but smaller library like [riot-lol-api](https://github.com/Neamar/riot-lol-api), which also happens to be made by a Riot employee IIRC. [TeemoJS](https://github.com/MingweiSamuel/TeemoJS) would probably work well too!\n\nOccasionally, if `requestOptions.burst = true`, the rate limiter may get out of sync if you're running thousands of concurrent requests (like [onetricks.net](onetricks.net) when building stats), which can cause 429's that will propagate until you're blacklisted.\n\nIt is thus ideal to use the `spread` strategy when working on apps that process a ton of requests because there is a much lower risk of getting a 429. However, for small or dev scripts, bursting your requests is a lot better.\n\n# External Links\n\n* [Batching Node.js Asynchronous Requests via Promises using Riot League of Legends API](http://chau.codes/batching-node-js-asynchronous-requests-via-promises-using-riot-league-of-legends-api/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcnguy%2Fkayn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcnguy%2Fkayn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcnguy%2Fkayn/lists"}