refactor: make match-collector export its types, and consume them in frontend
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { debounce, isEmpty } from '~/utils/helpers'
|
||||
|
||||
import type { ChampionSummary, LaneData, ChampionData } from 'match_collector'
|
||||
// Constants
|
||||
const CHAMPIONS_API_URL = '/api/champions'
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { CDRAGON_BASE, mapPath } from '~/utils/cdragon'
|
||||
|
||||
import type { Perk, Item } from '~/types/cdragon'
|
||||
|
||||
const props = defineProps<{
|
||||
keystoneId: number
|
||||
itemId: number
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { CDRAGON_BASE, mapPath } from '~/utils/cdragon'
|
||||
|
||||
import type { PerkStyle, Perk } from '~/types/cdragon'
|
||||
|
||||
interface RuneBuild {
|
||||
count: number
|
||||
primaryStyle: number
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { FirstBackGroup } from 'match_collector'
|
||||
import type { Item } from '~/types/cdragon'
|
||||
|
||||
defineProps<{
|
||||
firstBacks: FirstBackGroup[]
|
||||
itemMap: Map<number, Item>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { Item } from '~/types/cdragon'
|
||||
|
||||
interface ItemData {
|
||||
data: number
|
||||
count: number
|
||||
|
||||
@@ -5,8 +5,10 @@ import CompactRuneSelector from '~/components/build/CompactRuneSelector.vue'
|
||||
import ItemRow from '~/components/build/ItemRow.vue'
|
||||
import FirstBack from '~/components/build/FirstBack.vue'
|
||||
|
||||
import type { Build } from 'match_collector'
|
||||
|
||||
const props = defineProps<{
|
||||
builds: Builds
|
||||
builds: Array<Build>
|
||||
}>()
|
||||
|
||||
// State
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { CDRAGON_BASE, mapPath } from '~/utils/cdragon'
|
||||
|
||||
import type { Item } from '~/types/cdragon'
|
||||
import type { ItemTag } from 'match_collector'
|
||||
|
||||
interface Props {
|
||||
item: Item
|
||||
size?: number
|
||||
|
||||
@@ -7,6 +7,9 @@ import {
|
||||
type ParsedDescription
|
||||
} from 'dragon-item-parser'
|
||||
|
||||
import type { Item } from '~/types/cdragon'
|
||||
import type { ItemTag } from 'match_collector'
|
||||
|
||||
interface Props {
|
||||
item: Item | null
|
||||
show: boolean
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { LinePath as svgdomarrowsLinePath } from 'svg-dom-arrows'
|
||||
|
||||
import type { ItemTree } from 'match_collector'
|
||||
import type { Item } from '~/types/cdragon'
|
||||
|
||||
defineProps<{
|
||||
tree: ItemTree
|
||||
parentCount?: number
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { CDRAGON_BASE } from '~/utils/cdragon'
|
||||
import MatchupSpectrum from './Spectrum.vue'
|
||||
|
||||
import type { MatchupData } from 'match_collector'
|
||||
|
||||
defineProps<{
|
||||
matchups?: Array<MatchupData>
|
||||
championId: number
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { ref } from 'vue'
|
||||
import { CDRAGON_BASE } from '~/utils/cdragon'
|
||||
|
||||
import type { MatchupData } from 'match_collector'
|
||||
|
||||
defineProps<{
|
||||
matchups?: Array<MatchupData>
|
||||
championId: number
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LaneData } from 'match_collector'
|
||||
|
||||
defineProps<{
|
||||
championName?: string
|
||||
championLanes?: Array<LaneData>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { LANE_IMAGES, lanePositionToIndex, POSITIONS_STR } from '~/utils/cdragon'
|
||||
|
||||
import type { LaneData } from 'match_collector'
|
||||
|
||||
defineProps<{
|
||||
championName?: string
|
||||
championLanes?: Array<LaneData>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { LANE_IMAGES, lanePositionToIndex, POSITIONS_STR } from '~/utils/cdragon'
|
||||
|
||||
import type { LaneData } from 'match_collector'
|
||||
|
||||
defineProps<{
|
||||
championName?: string
|
||||
championLanes?: Array<LaneData>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PerkStyle, Perk } from '~/types/cdragon'
|
||||
|
||||
const props = defineProps<{
|
||||
primaryStyleId: number
|
||||
secondaryStyleId: number
|
||||
@@ -8,13 +10,14 @@ const props = defineProps<{
|
||||
const primaryStyle: Ref<PerkStyle> = ref({ id: 0, name: '', iconPath: '', slots: [] })
|
||||
const secondaryStyle: Ref<PerkStyle> = ref({ id: 0, name: '', iconPath: '', slots: [] })
|
||||
|
||||
const { data: perks_data }: PerksResponse = await useFetch('/api/cdragon/perks')
|
||||
const { data: perks_data }: { data: Ref<Array<Perk>> } = await useFetch('/api/cdragon/perks')
|
||||
const perks = reactive(new Map())
|
||||
for (const perk of perks_data.value) {
|
||||
perks.set(perk.id, perk)
|
||||
}
|
||||
|
||||
const { data: stylesData }: PerkStylesResponse = await useFetch('/api/cdragon/perkstyles')
|
||||
const { data: stylesData }: { data: Ref<{ styles: Array<PerkStyle> }> } =
|
||||
await useFetch('/api/cdragon/perkstyles')
|
||||
watch(
|
||||
() => props.primaryStyleId,
|
||||
async (_newP, _oldP) => {
|
||||
|
||||
@@ -10,6 +10,9 @@ import {
|
||||
} from 'chart.js'
|
||||
import { Bar } from 'vue-chartjs'
|
||||
|
||||
import type { Champion } from '~/types/cdragon'
|
||||
import type { LaneData } from 'match_collector'
|
||||
|
||||
// Register
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { LaneData } from 'match_collector'
|
||||
import type { Champion } from '~/types/cdragon'
|
||||
|
||||
defineProps<{
|
||||
title: string
|
||||
tier: Array<{ champion: Champion; lane: LaneData }>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { Builds } from 'match_collector'
|
||||
|
||||
/**
|
||||
* Composable for managing build data
|
||||
*/
|
||||
|
||||
@@ -17,26 +17,7 @@ export default withNuxt([
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
// Add global types from our API definitions
|
||||
ChampionSummary: 'readonly',
|
||||
LaneData: 'readonly',
|
||||
ChampionData: 'readonly',
|
||||
ItemTree: 'readonly',
|
||||
ItemTag: 'readonly',
|
||||
Builds: 'readonly',
|
||||
PerkStyle: 'readonly',
|
||||
PerksResponse: 'readonly',
|
||||
PerkStylesResponse: 'readonly',
|
||||
Champion: 'readonly',
|
||||
ChampionsResponse: 'readonly',
|
||||
ChampionResponse: 'readonly',
|
||||
ItemResponse: 'readonly',
|
||||
MatchupData: 'readonly',
|
||||
Item: 'readonly',
|
||||
SummonerSpell: 'readonly',
|
||||
Perk: 'readonly',
|
||||
FirstBackGroup: 'readonly'
|
||||
...globals.node
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
|
||||
83
frontend/package-lock.json
generated
83
frontend/package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"chart.js": "^4.5.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"dragon-item-parser": "file:../dragon-item-parser",
|
||||
"match_collector": "file:../match_collector",
|
||||
"mongodb": "^6.10.0",
|
||||
"nuxt": "^3.17.5",
|
||||
"nuxt-umami": "^3.2.1",
|
||||
@@ -9909,6 +9910,88 @@
|
||||
"resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz",
|
||||
"integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ=="
|
||||
},
|
||||
"node_modules/match_collector": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "file:../match_collector",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"dragon-item-parser": "file:../dragon-item-parser",
|
||||
"mongodb": "^7.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/match_collector/node_modules/@types/whatwg-url": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
|
||||
"integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
|
||||
"dependencies": {
|
||||
"@types/webidl-conversions": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/match_collector/node_modules/bson": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz",
|
||||
"integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/match_collector/node_modules/mongodb": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.2.0.tgz",
|
||||
"integrity": "sha512-F/2+BMZtLVhY30ioZp0dAmZ+IRZMBqI+nrv6t5+9/1AIwCa8sMRC3jBf81lpxMhnZgqq8CoUD503Z1oZWq1/sw==",
|
||||
"dependencies": {
|
||||
"@mongodb-js/saslprep": "^1.3.0",
|
||||
"bson": "^7.2.0",
|
||||
"mongodb-connection-string-url": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/credential-providers": "^3.806.0",
|
||||
"@mongodb-js/zstd": "^7.0.0",
|
||||
"gcp-metadata": "^7.0.1",
|
||||
"kerberos": "^7.0.0",
|
||||
"mongodb-client-encryption": ">=7.0.0 <7.1.0",
|
||||
"snappy": "^7.3.2",
|
||||
"socks": "^2.8.6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@aws-sdk/credential-providers": {
|
||||
"optional": true
|
||||
},
|
||||
"@mongodb-js/zstd": {
|
||||
"optional": true
|
||||
},
|
||||
"gcp-metadata": {
|
||||
"optional": true
|
||||
},
|
||||
"kerberos": {
|
||||
"optional": true
|
||||
},
|
||||
"mongodb-client-encryption": {
|
||||
"optional": true
|
||||
},
|
||||
"snappy": {
|
||||
"optional": true
|
||||
},
|
||||
"socks": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/match_collector/node_modules/mongodb-connection-string-url": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz",
|
||||
"integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==",
|
||||
"dependencies": {
|
||||
"@types/whatwg-url": "^13.0.0",
|
||||
"whatwg-url": "^14.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.27.1",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"format:check": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"match_collector": "file:../match_collector",
|
||||
"dragon-item-parser": "file:../dragon-item-parser",
|
||||
"@nuxt/eslint": "^1.12.1",
|
||||
"@nuxt/fonts": "^0.11.3",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
import type { ChampionData } from 'match_collector'
|
||||
|
||||
const route = useRoute()
|
||||
const championAlias = route.params.alias as string
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { LANE_IMAGES, lanePositionToIndex, POSITIONS_STR } from '~/utils/cdragon'
|
||||
|
||||
import type { LaneData, ChampionData, Champion } from 'match_collector'
|
||||
|
||||
const route = useRoute()
|
||||
const lane = route.params.lane as string
|
||||
|
||||
const { data: championsData }: ChampionsResponse = await useFetch('/api/cdragon/champion-summary')
|
||||
const { data: championsData }: { data: Ref<Array<Champion>> } = await useFetch(
|
||||
'/api/cdragon/champion-summary'
|
||||
)
|
||||
|
||||
const { data: championsLanes }: { data: Ref<Array<ChampionData>> } =
|
||||
await useFetch('/api/champions')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MongoClient } from 'mongodb'
|
||||
import type { ChampionData } from 'match_collector'
|
||||
import { connectToDatabase, fetchLatestPatch } from '../../utils/mongo'
|
||||
|
||||
async function championInfos(client: MongoClient, patch: string, championAlias: string) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MongoClient } from 'mongodb'
|
||||
import type { ChampionData } from 'match_collector'
|
||||
import { connectToDatabase, fetchLatestPatch } from '../utils/mongo'
|
||||
|
||||
async function champions(client: MongoClient, patch: string) {
|
||||
@@ -11,7 +12,6 @@ async function champions(client: MongoClient, patch: string) {
|
||||
if (x.lanes != undefined && x.lanes != null) {
|
||||
for (const lane of x.lanes) {
|
||||
delete lane.builds
|
||||
delete lane.runes
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
declare global {
|
||||
/**
|
||||
* Item tags derived from purchase patterns
|
||||
*/
|
||||
type ItemTag = 'ahead' | 'behind' | 'region_euw' | 'region_eun' | 'region_na' | 'region_kr'
|
||||
|
||||
/**
|
||||
* Represents an item in the build tree
|
||||
*/
|
||||
interface ItemTree {
|
||||
count: number
|
||||
data: number
|
||||
children: ItemTree[]
|
||||
tags: ItemTag[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a complete build with runes and items
|
||||
*/
|
||||
interface Build {
|
||||
runeKeystone: number
|
||||
runes: Rune[]
|
||||
items: ItemTree
|
||||
bootsFirst: number
|
||||
count: number
|
||||
boots: Array<{ count: number; data: number }>
|
||||
suppItems: Array<{ count: number; data: number }>
|
||||
startItems: Array<{ count: number; data: number }>
|
||||
pickrate: number
|
||||
firstBacks?: FirstBackGroup[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents champion build information (array of builds)
|
||||
*/
|
||||
type Builds = Array<Build>
|
||||
|
||||
/**
|
||||
* Represents a rune configuration
|
||||
*/
|
||||
interface Rune {
|
||||
count: number
|
||||
primaryStyle: number
|
||||
secondaryStyle: number
|
||||
selections: number[]
|
||||
pickrate: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents counter data for a champion
|
||||
*/
|
||||
interface MatchupData {
|
||||
championId: number
|
||||
winrate: number
|
||||
games: number
|
||||
championName: string
|
||||
championAlias: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an item in a first back item set
|
||||
*/
|
||||
interface FirstBackItemSetEntry {
|
||||
itemId: number
|
||||
count: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an item set (combination of items)
|
||||
*/
|
||||
interface ItemSet {
|
||||
items: FirstBackItemSetEntry[]
|
||||
totalGold: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a grouped first back by item set
|
||||
*/
|
||||
interface FirstBackGroup {
|
||||
itemSet: ItemSet
|
||||
count: number
|
||||
pickrate: number
|
||||
avgTimestamp: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents lane-specific champion data
|
||||
*/
|
||||
interface LaneData {
|
||||
data: string
|
||||
count: number
|
||||
winningMatches: number
|
||||
losingMatches: number
|
||||
winrate: number
|
||||
pickrate: number
|
||||
builds?: Builds
|
||||
summonerSpells: Array<{ id: number; count: number; pickrate: number }>
|
||||
matchups?: MatchupData[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents complete champion data
|
||||
*/
|
||||
interface ChampionData {
|
||||
id: number
|
||||
name: string
|
||||
alias: string
|
||||
gameCount: number
|
||||
winrate: number
|
||||
pickrate: number
|
||||
lanes: LaneData[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Champion summary from CDragon
|
||||
*/
|
||||
interface ChampionSummary {
|
||||
id: number
|
||||
name: string
|
||||
alias: string
|
||||
squarePortraitPath: string
|
||||
// Add other relevant fields as needed
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
@@ -1,12 +1,5 @@
|
||||
import type { ItemStats } from 'dragon-item-parser'
|
||||
|
||||
declare global {
|
||||
type ChampionsResponse = {
|
||||
data: Ref<Array<Champion>>
|
||||
}
|
||||
type ChampionResponse = {
|
||||
data: Ref<ChampionFull>
|
||||
}
|
||||
type Champion = {
|
||||
name: string
|
||||
alias: string
|
||||
@@ -18,9 +11,6 @@ declare global {
|
||||
squarePortraitPath: string
|
||||
title: string
|
||||
}
|
||||
type ItemResponse = {
|
||||
data: Ref<Array<Item>>
|
||||
}
|
||||
type Item = {
|
||||
id: number
|
||||
iconPath: string
|
||||
@@ -38,23 +28,16 @@ declare global {
|
||||
iconPath: string
|
||||
name: string
|
||||
}
|
||||
type PerksResponse = {
|
||||
data: Ref<Array<Perk>>
|
||||
}
|
||||
type Perk = {
|
||||
id: number
|
||||
name: string
|
||||
iconPath: string
|
||||
}
|
||||
type PerkStylesResponse = {
|
||||
data: Ref<{ styles: Array<PerkStyle> }>
|
||||
}
|
||||
type PerkStyle = {
|
||||
id: number
|
||||
name: string
|
||||
iconPath: string
|
||||
slots: Array<{ perks: Array<number> }>
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
export type { Champion, ChampionFull, Item, SummonerSpell, Perk, PerkStyle }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { Build, ItemTree } from 'match_collector'
|
||||
|
||||
/**
|
||||
* Gets all late game items from the item tree (items beyond first level)
|
||||
* Returns a flat array of unique items with their counts
|
||||
@@ -55,7 +57,9 @@ function trimTreeDepth(tree: ItemTree, maxDepth: number, currentDepth: number =
|
||||
count: tree.count,
|
||||
data: tree.data,
|
||||
children: [],
|
||||
tags: tree.tags
|
||||
tags: tree.tags,
|
||||
boughtWhen: tree.boughtWhen,
|
||||
platformCount: tree.platformCount
|
||||
}
|
||||
|
||||
// If we haven't reached maxDepth, include children
|
||||
|
||||
1
match_collector/.gitignore
vendored
1
match_collector/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
node_modules
|
||||
dist
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
{
|
||||
"name": "match_collector",
|
||||
"version": "1.0.0",
|
||||
"main": "index.ts",
|
||||
"main": "dist/lib.js",
|
||||
"types": "dist/lib.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/lib.d.ts",
|
||||
"import": "./dist/lib.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"lint": "eslint ./src",
|
||||
"lint:fix": "eslint --fix ./src",
|
||||
"format": "prettier --write ./src",
|
||||
"format:check": "prettier --check ./src",
|
||||
"dev": "node --import=tsx src/index.ts"
|
||||
},
|
||||
"author": "",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { MongoClient } from 'mongodb'
|
||||
import {
|
||||
ItemTree,
|
||||
GoldAdvantageTag,
|
||||
PlatformCounts,
|
||||
treeInit,
|
||||
treeMerge,
|
||||
treeCutBranches,
|
||||
@@ -15,14 +13,27 @@ import { PLATFORM_KEYS } from './platform'
|
||||
import {
|
||||
initItemDict as initFirstBackItemDict,
|
||||
extractFirstBackFromMatch,
|
||||
groupFirstBacksByItemSet,
|
||||
FirstBackData,
|
||||
FirstBackGroup
|
||||
groupFirstBacksByItemSet
|
||||
} from './first_back'
|
||||
|
||||
import { Match, Timeline, Participant, Frame } from './api'
|
||||
import type {
|
||||
Rune,
|
||||
InternalBuild,
|
||||
InternalBuildWithStartItems,
|
||||
InternalLaneData,
|
||||
InternalChampionData,
|
||||
FirstBackData
|
||||
} from './types'
|
||||
|
||||
function sameArrays(array1: Array<number>, array2: Array<number>) {
|
||||
// Type aliases for internal use
|
||||
type Builds = InternalBuild[]
|
||||
type Build = InternalBuild
|
||||
type BuildWithStartItems = InternalBuildWithStartItems
|
||||
type LaneData = InternalLaneData
|
||||
type ChampionData = InternalChampionData
|
||||
|
||||
function sameArrays(array1: number[], array2: number[]) {
|
||||
if (array1.length != array2.length) return false
|
||||
for (const e of array1) {
|
||||
if (!array2.includes(e)) return false
|
||||
@@ -55,77 +66,6 @@ function arrayRemovePercentage(
|
||||
}
|
||||
}
|
||||
|
||||
type Rune = {
|
||||
count: number
|
||||
primaryStyle: number
|
||||
secondaryStyle: number
|
||||
selections: Array<number>
|
||||
pickrate?: number
|
||||
}
|
||||
type Build = {
|
||||
runeKeystone: number
|
||||
runes: Array<Rune>
|
||||
items: ItemTree
|
||||
bootsFirstCount: number
|
||||
bootsFirst?: number
|
||||
count: number
|
||||
suppItems: Array<{ data: number; count: number }>
|
||||
boots: Array<{ data: number; count: number }>
|
||||
pickrate?: number
|
||||
// First back data (collected during processing, grouped in finalize)
|
||||
firstBacksRaw?: FirstBackData[]
|
||||
firstBacks?: FirstBackGroup[]
|
||||
}
|
||||
|
||||
type BuildWithStartItems = {
|
||||
runeKeystone: number
|
||||
runes: Array<Rune>
|
||||
items: ItemTree
|
||||
bootsFirst?: number
|
||||
bootsFirstCount: number
|
||||
count: number
|
||||
startItems: Array<{ data: number; count: number }>
|
||||
suppItems: Array<{ data: number; count: number }>
|
||||
boots: Array<{ data: number; count: number }>
|
||||
pickrate?: number
|
||||
firstBacksRaw?: FirstBackData[]
|
||||
firstBacks?: FirstBackGroup[]
|
||||
}
|
||||
|
||||
type Builds = Build[]
|
||||
type Champion = {
|
||||
id: number
|
||||
name: string
|
||||
alias: string
|
||||
}
|
||||
type MatchupData = {
|
||||
championId: number
|
||||
winrate: number
|
||||
games: number
|
||||
championName: string
|
||||
championAlias: string
|
||||
}
|
||||
|
||||
type LaneData = {
|
||||
data: string
|
||||
count: number
|
||||
winningMatches: number
|
||||
losingMatches: number
|
||||
winrate: number
|
||||
pickrate: number
|
||||
builds: Builds
|
||||
matchups?: Array<MatchupData>
|
||||
summonerSpells: Array<{ id: number; count: number; pickrate?: number }>
|
||||
// Region distribution for this lane (used for tag derivation)
|
||||
regionDistribution?: PlatformCounts
|
||||
}
|
||||
type ChampionData = {
|
||||
champion: Champion
|
||||
winningMatches: number
|
||||
losingMatches: number
|
||||
lanes: Array<LaneData>
|
||||
}
|
||||
|
||||
// Helper function to create rune configuration from participant
|
||||
function createRuneConfiguration(participant: Participant): Rune {
|
||||
const primaryStyle = participant.perks.styles[0].style
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Match, Timeline } from './api'
|
||||
import type { BackEvent, ItemSet, FirstBackData, FirstBackGroup } from './types'
|
||||
|
||||
// Re-export types for backward compatibility
|
||||
export type { BackEvent, ItemSet, FirstBackData, FirstBackGroup }
|
||||
|
||||
// Item dictionary for gold information
|
||||
const itemDict = new Map<
|
||||
@@ -49,38 +53,6 @@ function shouldTrackItem(itemId: number): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
// A single back event with all items purchased
|
||||
export type BackEvent = {
|
||||
timestamp: number
|
||||
items: Array<{
|
||||
itemId: number
|
||||
gold: number
|
||||
}>
|
||||
totalGold: number // Total gold value of items bought
|
||||
}
|
||||
|
||||
// Item set - a unique combination of items bought together
|
||||
export type ItemSet = {
|
||||
items: Array<{ itemId: number; count: number }> // Items with quantities
|
||||
totalGold: number // Total gold value of items
|
||||
}
|
||||
|
||||
// First back data with item set
|
||||
export type FirstBackData = {
|
||||
timestamp: number
|
||||
itemSet: ItemSet
|
||||
}
|
||||
|
||||
// Grouped first back by item set
|
||||
export type FirstBackGroup = {
|
||||
// The item set combination
|
||||
itemSet: ItemSet
|
||||
// Stats for this item set
|
||||
count: number
|
||||
pickrate: number // Overall pickrate for this item set
|
||||
avgTimestamp: number
|
||||
}
|
||||
|
||||
// Create a unique key for an item set (sorted by itemId for consistency)
|
||||
function itemSetKey(items: Array<{ itemId: number; count: number }>): string {
|
||||
const sorted = [...items].sort((a, b) => a.itemId - b.itemId)
|
||||
|
||||
@@ -6,31 +6,7 @@ import {
|
||||
} from './platform'
|
||||
|
||||
import type { PlatformCounts } from './platform'
|
||||
|
||||
type GoldAdvantageTag = 'ahead' | 'behind' | 'even'
|
||||
|
||||
// Item tags that can be derived from purchase patterns
|
||||
type ItemTag = 'ahead' | 'behind' | 'region_euw' | 'region_eun' | 'region_na' | 'region_kr'
|
||||
|
||||
type ItemTree = {
|
||||
data: number | undefined
|
||||
count: number
|
||||
children: Array<ItemTree>
|
||||
|
||||
// Gold advantage tracking
|
||||
boughtWhen: {
|
||||
aheadCount: number
|
||||
behindCount: number
|
||||
evenCount: number
|
||||
meanGold: number
|
||||
}
|
||||
|
||||
// Platform tracking
|
||||
platformCount: PlatformCounts
|
||||
|
||||
// Derived tags for display
|
||||
tags: Array<ItemTag>
|
||||
}
|
||||
import type { GoldAdvantageTag, ItemTag, ItemTree } from './types'
|
||||
|
||||
function treeInit(): ItemTree {
|
||||
return {
|
||||
@@ -330,7 +306,6 @@ function treeDeriveTags(itemtree: ItemTree, expectedRegionDistribution?: Platfor
|
||||
}
|
||||
|
||||
export {
|
||||
ItemTree,
|
||||
PlatformCounts,
|
||||
GoldAdvantageTag,
|
||||
ItemTag,
|
||||
|
||||
27
match_collector/src/lib.ts
Normal file
27
match_collector/src/lib.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Match Collector Library
|
||||
* Exports all shared types for use by other projects (e.g., frontend)
|
||||
*/
|
||||
|
||||
// Export all types
|
||||
export type {
|
||||
ItemTag,
|
||||
GoldAdvantageTag,
|
||||
PlatformCounts,
|
||||
ItemTree,
|
||||
Rune,
|
||||
ItemCountEntry,
|
||||
FirstBackItemSetEntry,
|
||||
ItemSet,
|
||||
FirstBackGroup,
|
||||
Build,
|
||||
Builds,
|
||||
MatchupData,
|
||||
SummonerSpellData,
|
||||
LaneData,
|
||||
ChampionData,
|
||||
ChampionSummary,
|
||||
Champion,
|
||||
BackEvent,
|
||||
FirstBackData
|
||||
} from './types'
|
||||
256
match_collector/src/types.ts
Normal file
256
match_collector/src/types.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Shared types between match_collector and frontend
|
||||
*/
|
||||
|
||||
/**
|
||||
* Item tags derived from purchase patterns
|
||||
*/
|
||||
export type ItemTag = 'ahead' | 'behind' | 'region_euw' | 'region_eun' | 'region_na' | 'region_kr'
|
||||
|
||||
/**
|
||||
* Gold advantage tag for item purchases
|
||||
*/
|
||||
export type GoldAdvantageTag = 'ahead' | 'behind' | 'even'
|
||||
|
||||
/**
|
||||
* Platform counts for region tracking
|
||||
*/
|
||||
export interface PlatformCounts {
|
||||
euw: number
|
||||
eun: number
|
||||
na: number
|
||||
kr: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an item in the build tree
|
||||
*/
|
||||
export interface ItemTree {
|
||||
data: number | undefined
|
||||
count: number
|
||||
children: ItemTree[]
|
||||
tags: ItemTag[]
|
||||
// Gold advantage tracking (used during processing)
|
||||
boughtWhen: {
|
||||
aheadCount: number
|
||||
behindCount: number
|
||||
evenCount: number
|
||||
meanGold: number
|
||||
}
|
||||
// Platform tracking (used during processing)
|
||||
platformCount: PlatformCounts
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a rune configuration
|
||||
*/
|
||||
export interface Rune {
|
||||
count: number
|
||||
primaryStyle: number
|
||||
secondaryStyle: number
|
||||
selections: number[]
|
||||
pickrate?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an item entry with count
|
||||
*/
|
||||
export interface ItemCountEntry {
|
||||
count: number
|
||||
data: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an item in a first back item set
|
||||
*/
|
||||
export interface FirstBackItemSetEntry {
|
||||
itemId: number
|
||||
count: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an item set (combination of items)
|
||||
*/
|
||||
export interface ItemSet {
|
||||
items: FirstBackItemSetEntry[]
|
||||
totalGold: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal type for first back data during processing
|
||||
*/
|
||||
export interface FirstBackData {
|
||||
timestamp: number
|
||||
itemSet: ItemSet
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a grouped first back by item set
|
||||
*/
|
||||
export interface FirstBackGroup {
|
||||
itemSet: ItemSet
|
||||
count: number
|
||||
pickrate: number
|
||||
avgTimestamp: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal build type with processing fields
|
||||
*/
|
||||
export interface InternalBuild {
|
||||
runeKeystone: number
|
||||
runes: Rune[]
|
||||
items: ItemTree
|
||||
bootsFirstCount: number
|
||||
bootsFirst?: number
|
||||
count: number
|
||||
suppItems: ItemCountEntry[]
|
||||
boots: ItemCountEntry[]
|
||||
pickrate?: number
|
||||
firstBacksRaw?: FirstBackData[]
|
||||
firstBacks?: FirstBackGroup[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal build type with start items
|
||||
*/
|
||||
export interface InternalBuildWithStartItems {
|
||||
runeKeystone: number
|
||||
runes: Rune[]
|
||||
items: ItemTree
|
||||
bootsFirst?: number
|
||||
bootsFirstCount: number
|
||||
count: number
|
||||
startItems: ItemCountEntry[]
|
||||
suppItems: ItemCountEntry[]
|
||||
boots: ItemCountEntry[]
|
||||
pickrate?: number
|
||||
firstBacksRaw?: FirstBackData[]
|
||||
firstBacks?: FirstBackGroup[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a complete build with runes and items (final output format)
|
||||
*/
|
||||
export interface Build {
|
||||
runeKeystone: number
|
||||
runes: Rune[]
|
||||
items: ItemTree
|
||||
bootsFirst: number
|
||||
count: number
|
||||
boots: ItemCountEntry[]
|
||||
suppItems: ItemCountEntry[]
|
||||
startItems: ItemCountEntry[]
|
||||
pickrate: number
|
||||
firstBacks?: FirstBackGroup[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents champion build information (array of builds)
|
||||
*/
|
||||
export type Builds = Build[]
|
||||
|
||||
/**
|
||||
* Represents counter data for a champion
|
||||
*/
|
||||
export interface MatchupData {
|
||||
championId: number
|
||||
winrate: number
|
||||
games: number
|
||||
championName: string
|
||||
championAlias: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents summoner spell data
|
||||
*/
|
||||
export interface SummonerSpellData {
|
||||
id: number
|
||||
count: number
|
||||
pickrate?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal lane data with processing fields
|
||||
*/
|
||||
export interface InternalLaneData {
|
||||
data: string
|
||||
count: number
|
||||
winningMatches: number
|
||||
losingMatches: number
|
||||
winrate: number
|
||||
pickrate: number
|
||||
builds: InternalBuild[]
|
||||
matchups?: MatchupData[]
|
||||
summonerSpells: SummonerSpellData[]
|
||||
regionDistribution?: PlatformCounts
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents lane-specific champion data (final output format)
|
||||
*/
|
||||
export interface LaneData {
|
||||
data: string
|
||||
count: number
|
||||
winningMatches: number
|
||||
losingMatches: number
|
||||
winrate: number
|
||||
pickrate: number
|
||||
builds?: Builds
|
||||
summonerSpells: SummonerSpellData[]
|
||||
matchups?: MatchupData[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal champion data with processing fields
|
||||
*/
|
||||
export interface InternalChampionData {
|
||||
champion: Champion
|
||||
winningMatches: number
|
||||
losingMatches: number
|
||||
lanes: InternalLaneData[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents complete champion data (final output format)
|
||||
*/
|
||||
export interface ChampionData {
|
||||
id: number
|
||||
name: string
|
||||
alias: string
|
||||
gameCount: number
|
||||
winrate: number
|
||||
pickrate: number
|
||||
lanes: LaneData[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Champion summary from CDragon
|
||||
*/
|
||||
export interface ChampionSummary {
|
||||
id: number
|
||||
name: string
|
||||
alias: string
|
||||
squarePortraitPath: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal type for champion info
|
||||
*/
|
||||
export interface Champion {
|
||||
id: number
|
||||
name: string
|
||||
alias: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal type for back event
|
||||
*/
|
||||
export interface BackEvent {
|
||||
timestamp: number
|
||||
items: Array<{
|
||||
itemId: number
|
||||
gold: number
|
||||
}>
|
||||
totalGold: number
|
||||
}
|
||||
@@ -1,6 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node"],
|
||||
"declaration": true
|
||||
}
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user