dragon-item-parser: introduce item parser library
Some checks failed
Dragon Item Parser CI / build-and-test (push) Successful in 1m3s
pipeline / lint-and-format (push) Failing after 4m8s
pipeline / build-and-push-images (push) Has been skipped

This commit is contained in:
2026-04-25 19:48:06 +02:00
parent a98e3c6589
commit e82ad73de1
17 changed files with 7689 additions and 5022 deletions

View File

@@ -0,0 +1,49 @@
name: Dragon Item Parser CI
on:
push:
paths:
- 'dragon-item-parser/**'
branches: [main]
pull_request:
paths:
- 'dragon-item-parser/**'
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: dragon-item-parser
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Check formatting
run: npm run format:check
- name: Run linting
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Verify build output
run: |
test -f dist/index.js
test -f dist/index.d.ts
test -f dist/item.js
test -f dist/item.d.ts

3
dragon-item-parser/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
node_modules/
dist/
*.log

View File

@@ -0,0 +1,10 @@
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "avoid",
"endOfLine": "lf"
}

View File

@@ -0,0 +1,19 @@
import { defineConfig } from 'eslint/config'
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import prettier from 'eslint-config-prettier'
export default defineConfig([
js.configs.recommended,
tseslint.configs.recommended,
prettier,
{
ignores: ['dist/**', 'node_modules/**']
},
{
rules: {
semi: 'off',
'prefer-const': 'error'
}
}
])

2886
dragon-item-parser/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
{
"name": "dragon-item-parser",
"version": "1.0.0",
"description": "Parse League of Legends item stats from CommunityDragon data",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"format": "prettier --write .",
"format:check": "prettier --check .",
"prepublishOnly": "npm run build"
},
"keywords": [
"league-of-legends",
"cdragon",
"communitydragon",
"item",
"parser",
"lol"
],
"author": "",
"license": "MIT",
"repository": {
"type": "git",
"url": ""
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@typescript-eslint/eslint-plugin": "^8.53.1",
"@typescript-eslint/parser": "^8.53.1",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.8.3",
"typescript": "^5.0.0",
"typescript-eslint": "^8.53.1",
"vitest": "^3.0.0"
}
}

View File

@@ -0,0 +1,2 @@
export type { ItemStats, CDragonItem, ItemWithStats } from './item.js'
export { parseItemStats, parseItem, parseItems } from './item.js'

View File

@@ -0,0 +1,202 @@
/**
* Item stats parsed from CommunityDragon item description HTML
*/
export interface ItemStats {
// Offensive stats
attackDamage?: number
abilityPower?: number
attackSpeed?: number
criticalStrikeChance?: number
lifeSteal?: number
omnivamp?: number
physicalVamp?: number
spellVamp?: number
// Defensive stats
health?: number
armor?: number
magicResist?: number
// Resource stats
mana?: number
baseManaRegen?: number
baseHealthRegen?: number
// Movement stats
moveSpeed?: number
// Ability stats
abilityHaste?: number
// Penetration stats (usually percentages)
armorPenetration?: number
magicPenetration?: number
lethality?: number
magicPenetrationFlat?: number
// Other percentage stats
healAndShieldPower?: number
tenacity?: number
slowResist?: number
}
/**
* Stat name mappings from CDragon description text to stat keys
*/
const STAT_MAPPINGS: Record<string, keyof ItemStats> = {
'Attack Damage': 'attackDamage',
'Ability Power': 'abilityPower',
'Attack Speed': 'attackSpeed',
'Critical Strike Chance': 'criticalStrikeChance',
'Life Steal': 'lifeSteal',
Omnivamp: 'omnivamp',
'Physical Vamp': 'physicalVamp',
'Spell Vamp': 'spellVamp',
Health: 'health',
Armor: 'armor',
'Magic Resist': 'magicResist',
Mana: 'mana',
'Base Mana Regen': 'baseManaRegen',
'Base Health Regen': 'baseHealthRegen',
'Move Speed': 'moveSpeed',
'Ability Haste': 'abilityHaste',
'Armor Penetration': 'armorPenetration',
'Magic Penetration': 'magicPenetration',
Lethality: 'lethality',
'Heal and Shield Power': 'healAndShieldPower',
Tenacity: 'tenacity',
'Slow Resist': 'slowResist'
}
/**
* Parse a stat value string to a number
* Handles both flat values (e.g., "25") and percentages (e.g., "25%")
*/
function parseStatValue(valueStr: string): { value: number; isPercentage: boolean } {
const trimmed = valueStr.trim()
const isPercentage = trimmed.includes('%')
const numStr = trimmed.replace('%', '').replace(',', '').trim()
const value = parseFloat(numStr)
return { value, isPercentage }
}
/**
* Extract stats section from the description HTML
*/
function extractStatsSection(description: string): string | null {
const statsMatch = description.match(/<stats>(.*?)<\/stats>/s)
return statsMatch ? statsMatch[1] : null
}
/**
* Parse individual stat lines from the stats section
* Format: <attention> value </attention> statName
*/
function parseStatLines(
statsSection: string
): Array<{ value: number; isPercentage: boolean; statName: string }> {
const results: Array<{ value: number; isPercentage: boolean; statName: string }> = []
// Match patterns like: <attention> 25</attention> Move Speed
// or: <attention> 25%</attention> Attack Speed
const statRegex = /<attention>\s*([^<]+)<\/attention>\s*([^<]+)/g
let match
while ((match = statRegex.exec(statsSection)) !== null) {
const valueStr = match[1]
const statName = match[2].trim()
const { value, isPercentage } = parseStatValue(valueStr)
if (!isNaN(value) && statName) {
results.push({ value, isPercentage, statName })
}
}
return results
}
/**
* Parse item stats from CDragon description HTML
*
* @param description - The HTML description string from CDragon items.json
* @returns Parsed ItemStats object with all recognized stats
*
* @example
* ```ts
* const stats = parseItemStats(
* '<mainText><stats><attention> 25</attention> Move Speed</stats><br><br></mainText>'
* )
* // Returns: { moveSpeed: 25 }
* ```
*/
export function parseItemStats(description: string): ItemStats {
const stats: ItemStats = {}
if (!description) {
return stats
}
const statsSection = extractStatsSection(description)
if (!statsSection) {
return stats
}
const statLines = parseStatLines(statsSection)
for (const { value, statName } of statLines) {
const statKey = STAT_MAPPINGS[statName]
if (statKey) {
// For percentage stats that are stored as decimals (e.g., 25% -> 25)
// We store the percentage value as-is for consistency with game data
stats[statKey] = value
}
}
return stats
}
/**
* Item data structure from CDragon
*/
export interface CDragonItem {
id: number
name: string
description: string
active?: boolean
inStore?: boolean
from?: number[]
to?: number[]
categories?: string[]
maxStacks?: number
requiredChampion?: string
requiredAlly?: string
price?: number
priceTotal?: number
iconPath: string
}
/**
* Item with parsed stats
*/
export interface ItemWithStats extends CDragonItem {
stats: ItemStats
}
/**
* Parse a CDragon item and add parsed stats
*/
export function parseItem(item: CDragonItem): ItemWithStats {
return {
...item,
stats: parseItemStats(item.description)
}
}
/**
* Parse an array of CDragon items and add parsed stats
*/
export function parseItems(items: CDragonItem[]): ItemWithStats[] {
return items.map(parseItem)
}

View File

@@ -0,0 +1,235 @@
import { describe, it, expect } from 'vitest'
import { parseItemStats, parseItem, parseItems } from '../src/item.js'
describe('parseItemStats', () => {
describe('basic stats', () => {
it('should parse Move Speed', () => {
// Boots (id: 1001)
const description =
'<mainText><stats><attention> 25</attention> Move Speed</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.moveSpeed).toBe(25)
})
it('should parse Attack Damage', () => {
// Long Sword (id: 1036)
const description =
'<mainText><stats><attention> 10</attention> Attack Damage</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.attackDamage).toBe(10)
})
it('should parse Ability Power', () => {
// Amplifying Tome (id: 1052)
const description =
'<mainText><stats><attention> 20</attention> Ability Power</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.abilityPower).toBe(20)
})
it('should parse Health', () => {
// Ruby Crystal (id: 1028)
const description =
'<mainText><stats><attention> 150</attention> Health</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.health).toBe(150)
})
it('should parse Armor', () => {
// Cloth Armor (id: 1029)
const description =
'<mainText><stats><attention> 15</attention> Armor</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.armor).toBe(15)
})
it('should parse Magic Resist', () => {
// Null-Magic Mantle (id: 1033)
const description =
'<mainText><stats><attention> 20</attention> Magic Resist</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.magicResist).toBe(20)
})
it('should parse Mana', () => {
// Sapphire Crystal (id: 1027)
const description =
'<mainText><stats><attention> 300</attention> Mana</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.mana).toBe(300)
})
})
describe('percentage stats', () => {
it('should parse Attack Speed percentage', () => {
// Dagger (id: 1042)
const description =
'<mainText><stats><attention> 10%</attention> Attack Speed</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.attackSpeed).toBe(10)
})
it('should parse Critical Strike Chance', () => {
// Cloak of Agility (id: 1018)
const description =
'<mainText><stats><attention> 15%</attention> Critical Strike Chance</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.criticalStrikeChance).toBe(15)
})
it('should parse Life Steal', () => {
// Vampiric Scepter (id: 1053)
const description =
'<mainText><stats><attention> 15</attention> Attack Damage<br><attention> 7%</attention> Life Steal</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.attackDamage).toBe(15)
expect(stats.lifeSteal).toBe(7)
})
it('should parse Base Mana Regen percentage', () => {
// Faerie Charm (id: 1004)
const description =
'<mainText><stats><attention> 50%</attention> Base Mana Regen</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.baseManaRegen).toBe(50)
})
it('should parse Base Health Regen percentage', () => {
// Rejuvenation Bead (id: 1006)
const description =
'<mainText><stats><attention> 100%</attention> Base Health Regen</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.baseHealthRegen).toBe(100)
})
})
describe('multiple stats', () => {
it("should parse multiple stats from Doran's Blade", () => {
// Doran's Blade (id: 1055)
const description =
'<mainText><stats><attention> 10</attention> Attack Damage<br><attention> 80</attention> Health<br><attention> 2.5%</attention> Omnivamp</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.attackDamage).toBe(10)
expect(stats.health).toBe(80)
expect(stats.omnivamp).toBe(2.5)
})
it("should parse multiple stats from Doran's Ring", () => {
// Doran's Ring (id: 1056)
const description =
'<mainText><stats><attention> 18</attention> Ability Power<br><attention> 90</attention> Health</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.abilityPower).toBe(18)
expect(stats.health).toBe(90)
})
it("should parse multiple stats from Seeker's Armguard", () => {
// Seeker's Armguard (id: 2420)
const description =
'<mainText><stats><attention> 40</attention> Ability Power<br><attention> 25</attention> Armor</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.abilityPower).toBe(40)
expect(stats.armor).toBe(25)
})
it('should parse complex item with many stats', () => {
// Overlord's Bloodmail (id: 2501)
const description =
'<mainText><stats><attention> 30</attention> Attack Damage<br><attention> 550</attention> Health</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.attackDamage).toBe(30)
expect(stats.health).toBe(550)
})
it('should parse item with Ability Haste', () => {
// Unending Despair (id: 2502)
const description =
'<mainText><stats><attention> 400</attention> Health<br><attention> 50</attention> Armor<br><attention> 15</attention> Ability Haste</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.health).toBe(400)
expect(stats.armor).toBe(50)
expect(stats.abilityHaste).toBe(15)
})
it('should parse item with Mana and Ability Haste', () => {
// Blackfire Torch (id: 2503)
const description =
'<mainText><stats><attention> 80</attention> Ability Power<br><attention> 600</attention> Mana<br><attention> 20</attention> Ability Haste</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.abilityPower).toBe(80)
expect(stats.mana).toBe(600)
expect(stats.abilityHaste).toBe(20)
})
})
describe('edge cases', () => {
it('should return empty object for empty description', () => {
const stats = parseItemStats('')
expect(stats).toEqual({})
})
it('should return empty object for description without stats', () => {
const description = '<mainText><stats></stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats).toEqual({})
})
it('should handle description with only passive text', () => {
// Emberknife (id: 1035) - no stats, only passives
const description =
'<mainText><stats></stats><br><br> <passive>7%</passive> Omnivamp against jungle monsters<br><li><passive>Sear:</passive> Damaging jungle monsters burns them for <magicDamage> magic damage</magicDamage> over 5 seconds.</mainText>'
const stats = parseItemStats(description)
expect(stats).toEqual({})
})
it('should handle decimal values', () => {
const description =
'<mainText><stats><attention> 2.5%</attention> Omnivamp</stats><br><br></mainText>'
const stats = parseItemStats(description)
expect(stats.omnivamp).toBe(2.5)
})
})
})
describe('parseItem', () => {
it('should parse item and add stats', () => {
const item = {
id: 1001,
name: 'Boots',
description:
'<mainText><stats><attention> 25</attention> Move Speed</stats><br><br></mainText>',
iconPath: '/path/to/icon.png'
}
const result = parseItem(item)
expect(result.id).toBe(1001)
expect(result.name).toBe('Boots')
expect(result.stats.moveSpeed).toBe(25)
})
})
describe('parseItems', () => {
it('should parse multiple items', () => {
const items = [
{
id: 1001,
name: 'Boots',
description:
'<mainText><stats><attention> 25</attention> Move Speed</stats><br><br></mainText>',
iconPath: '/path/to/boots.png'
},
{
id: 1036,
name: 'Long Sword',
description:
'<mainText><stats><attention> 10</attention> Attack Damage</stats><br><br></mainText>',
iconPath: '/path/to/sword.png'
}
]
const results = parseItems(items)
expect(results).toHaveLength(2)
expect(results[0].stats.moveSpeed).toBe(25)
expect(results[1].stats.attackDamage).toBe(10)
})
})

View File

@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "test"]
}

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
include: ['test/**/*.test.ts']
}
})

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@
"format:check": "prettier --check ."
},
"dependencies": {
"dragon-item-parser": "file:../dragon-item-parser",
"@nuxt/eslint": "^1.12.1",
"@nuxt/fonts": "^0.11.3",
"@nuxt/image": "^1.10.0",

View File

@@ -1,6 +1,7 @@
import { readFile, existsSync } from 'fs'
import { join } from 'path'
import { promisify } from 'util'
import { parseItemStats, type ItemStats } from 'dragon-item-parser'
const readFileAsync = promisify(readFile)
@@ -78,14 +79,20 @@ async function fetchFromCache<T>(
}
/**
* Get items data from cache
* Get items data from cache with parsed stats
*/
async function getItems(patch?: string): Promise<CDragonItem[]> {
return fetchFromCache<CDragonItem[]>(
async function getItems(patch?: string): Promise<CDragonItemWithStats[]> {
const items = await fetchFromCache<CDragonItem[]>(
'items.json',
'plugins/rcp-be-lol-game-data/global/default/v1/items.json',
{ patch }
)
// Parse stats from description for each item
return items.map(item => ({
...item,
stats: parseItemStats(item.description || '')
}))
}
/**
@@ -148,6 +155,10 @@ interface CDragonItem {
}
}
interface CDragonItemWithStats extends CDragonItem {
stats: ItemStats
}
interface CDragonPerk {
id: number
name: string
@@ -197,6 +208,7 @@ export {
export type {
CDragonItem,
CDragonItemWithStats,
CDragonPerk,
CDragonPerkStyle,
CDragonPerkStyles,

View File

@@ -1,3 +1,5 @@
import type { ItemStats } from 'dragon-item-parser'
declare global {
type ChampionsResponse = {
data: Ref<Array<Champion>>
@@ -29,6 +31,7 @@ declare global {
from?: number[]
price?: number
priceTotal?: number
stats?: ItemStats
}
type SummonerSpell = {
id: number

View File

@@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"dragon-item-parser": "file:../dragon-item-parser",
"mongodb": "^7.2.0"
},
"devDependencies": {
@@ -468,19 +469,25 @@
}
},
"node_modules/@eslint/config-array": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"version": "0.21.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
"integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
"dev": true,
"dependencies": {
"@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
"minimatch": "^3.1.5"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/config-array/node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/@eslint/config-array/node_modules/brace-expansion": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
@@ -528,19 +535,19 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
"integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
"integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
"ajv": "^6.14.0",
"debug": "^4.3.2",
"espree": "^10.0.1",
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.1",
"minimatch": "^3.1.2",
"minimatch": "^3.1.5",
"strip-json-comments": "^3.1.1"
},
"engines": {
@@ -550,6 +557,12 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/eslintrc/node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
@@ -582,9 +595,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
"integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
"dev": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -616,27 +629,40 @@
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
"integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dev": true,
"dependencies": {
"@humanfs/types": "^0.15.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
"integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
"dev": true,
"dependencies": {
"@humanfs/core": "^0.19.1",
"@humanfs/core": "^0.19.2",
"@humanfs/types": "^0.15.0",
"@humanwhocodes/retry": "^0.4.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/types": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
"integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
"dev": true,
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -928,9 +954,9 @@
}
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@@ -986,10 +1012,13 @@
"dev": true
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/brace-expansion": {
"version": "5.0.5",
@@ -1003,15 +1032,6 @@
"node": "18 || 20 || >=22"
}
},
"node_modules/brace-expansion/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/bson": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz",
@@ -1106,6 +1126,11 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
"node_modules/dragon-item-parser": {
"version": "1.0.0",
"resolved": "file:../dragon-item-parser",
"license": "MIT"
},
"node_modules/esbuild": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
@@ -1160,24 +1185,24 @@
}
},
"node_modules/eslint": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.1",
"@eslint/config-array": "^0.21.2",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.39.2",
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "9.39.4",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"ajv": "^6.12.4",
"ajv": "^6.14.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
@@ -1196,7 +1221,7 @@
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"minimatch": "^3.1.5",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@@ -1261,6 +1286,12 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/eslint/node_modules/brace-expansion": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
@@ -1463,7 +1494,6 @@
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -1473,11 +1503,10 @@
}
},
"node_modules/get-tsconfig": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
"integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
"integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
"dev": true,
"license": "MIT",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
@@ -1858,7 +1887,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
@@ -1877,7 +1905,6 @@
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}

View File

@@ -15,6 +15,7 @@
"license": "ISC",
"description": "",
"dependencies": {
"dragon-item-parser": "file:../dragon-item-parser",
"mongodb": "^7.2.0"
},
"devDependencies": {