refactor: make match-collector export its types, and consume them in frontend
This commit is contained in:
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