diff --git a/packages/condition/index.spec.ts b/packages/condition/index.spec.ts index 11c1058..b4ec9eb 100644 --- a/packages/condition/index.spec.ts +++ b/packages/condition/index.spec.ts @@ -1,5 +1,5 @@ import { expect, test, describe } from 'bun:test' -import { checkCondition } from '.' +import { check } from '.' // 定义属性 mock 字典的类型契约 interface MockProperties { @@ -10,12 +10,7 @@ interface MockProperties { * 属性注入高阶函数:模拟角色属性管理类 */ function withProp(prop: MockProperties) { - const p = { - get(key: string): any { - return prop[key] - }, - } - return (condition: string): boolean => checkCondition(p, condition) + return (condition: string): boolean => check(prop, condition) } describe('condition', () => { diff --git a/packages/condition/index.ts b/packages/condition/index.ts index ee7c03f..cd599da 100644 --- a/packages/condition/index.ts +++ b/packages/condition/index.ts @@ -1,17 +1,9 @@ -// 条件节点可以是一个纯字符串表达式(如 'AGE>18'),或者是一个无限嵌套自身的数组 export type ConditionNode = string | ConditionTree export interface ConditionTree extends Array {} -// 🌟 核心类型定义 2:定义满足有 .get 提取器属性的游戏对象约束(如玩家属性管理类或原生 Map) -export interface PropertyContainer { - get(key: string): any -} +export type PropertyContainer = Record -/** - * 词法解析器:将条件字符串切分为多维嵌套的语法树树(AST) - * @param condition 原始字符串表达式,例如 "AGE > 10 & (SEX = 1 | CHR >= 5)" - */ -function parseCondition(condition: string): ConditionTree { +export function parse(condition: string): ConditionTree { const conditions: ConditionTree = [] const length = condition.length const stack: ConditionTree[] = [] @@ -60,16 +52,8 @@ function parseCondition(condition: string): ConditionTree { return conditions } -/** - * 外部核心接口:判定某个角色的属性是否完美符合该文本条件限制 - * @param property 玩家或局内属性提取器 - * @param condition 原始条件表达式 - */ -export function checkCondition( - property: PropertyContainer, - condition: string, -): boolean { - const conditions = parseCondition(condition) +export function check(property: PropertyContainer, condition: string): boolean { + const conditions = parse(condition) return checkParsedConditions(property, conditions) } @@ -115,9 +99,6 @@ function checkParsedConditions( return ret } -/** - * 原子逻辑判定器:负责处理诸如 '>', '<', '=', '?', '!' 各种操作符的最终生死判定 - */ function checkProp(property: PropertyContainer, condition: string): boolean { const length = condition.length let i = condition.search(/[> { + const profile = { + times: 0, + talents: new Set([]), + achievements: new Set([]), + events: new Set([]), + } + const allocation = { + charm: 10, + intelligence: 10, + strength: 0, + money: 0, + } + + test('10003 branch 0 玉佩', () => { + const result = trigger( + 10003, + createState(allocation, [1001, 1002, 1003]), + profile, + ) + expect(result.state.life).toBe(1) + expect(result.state.events).toEqual(new Set([10003, 10004])) + expect(result.events).toEqual([10003, 10004]) + }) + + test('10003 branch 1 死了', () => { + const result = trigger(10003, createState(allocation, []), profile) + expect(result.state.life).toBe(0) + expect(result.state.events).toEqual(new Set([10003, 10000])) + expect(result.events).toEqual([10003, 10000]) + }) +}) diff --git a/packages/core/src/event.ts b/packages/core/src/event.ts new file mode 100644 index 0000000..4d928ed --- /dev/null +++ b/packages/core/src/event.ts @@ -0,0 +1,51 @@ +import type { EventEffect, Event } from '@remake/data/event' +import events from '@remake/data/event' +import type { Properties, Effect } from '@/state' +import type { GameState, ProfileState } from '@/state' +import { stateEffect, createFlatState } from '@/state' +import { check } from '@remake/condition' + +export function convertEffect(effect: EventEffect): Effect { + const converted = {} as Effect + const properties = [] as [keyof Properties, number][] + if (effect.CHR) properties.push(['charm', effect.CHR]) + if (effect.INT) properties.push(['intelligence', effect.INT]) + if (effect.STR) properties.push(['strength', effect.STR]) + if (effect.MNY) properties.push(['money', effect.MNY]) + if (effect.SPR) properties.push(['spirit', effect.SPR]) + if (properties.length > 0) + converted.properties = Object.fromEntries(properties) + if (effect.LIF) converted.life = effect.LIF + return converted +} + +export interface TriggerResult { + state: GameState + events: Event['id'][] +} + +export function trigger( + eventId: number, + state: GameState, + profile: ProfileState, +): TriggerResult { + const event = events.get(eventId) + if (!event) + throw new Error(`[@remake/core][event] id:${eventId} not found.`) + const effect = event.effect ? convertEffect(event.effect) : {} + effect.events = [eventId] + const newState = stateEffect(state, effect) + const flatState = createFlatState(newState, profile) + if (event.branch) { + for (const branch of event.branch) { + if (check(flatState, branch.condition)) { + const result = trigger(branch.event, newState, profile) + return { + state: result.state, + events: [eventId, ...result.events], + } + } + } + } + return { state: newState, events: [eventId] } +} diff --git a/packages/core/src/game.ts b/packages/core/src/game.ts new file mode 100644 index 0000000..56d8e92 --- /dev/null +++ b/packages/core/src/game.ts @@ -0,0 +1,4 @@ +export interface GameEffects { + save: (profile: any) => Promise + load: () => Promise +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 08dfa66..e69de29 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,2 +0,0 @@ -import { age } from '@remake/data/age' -import { checkCondition } from '@remake/condition' diff --git a/packages/core/src/state.spec.ts b/packages/core/src/state.spec.ts new file mode 100644 index 0000000..055da24 --- /dev/null +++ b/packages/core/src/state.spec.ts @@ -0,0 +1,212 @@ +import { expect, test, describe } from 'bun:test' +import { parse, type ConditionNode } from '@remake/condition' + +import achievements from '@remake/data/achievement' +import events from '@remake/data/event' +import talents from '@remake/data/talent' + +import { createFlatState } from './state' + +const A = ['AGE', 'CHR', 'INT', 'STR', 'MNY', 'SPR', 'LIF', 'TLT', 'EVT', 'TMS'] +const B = ['HAGE', 'HCHR', 'HINT', 'HSTR', 'HMNY', 'HSPR'] +const C = ['LAGE', 'LCHR', 'LINT', 'LSTR', 'LMNY', 'LSPR'] +const D = ['AEVT', 'ATLT', 'SUM'] +const FlatProperties = new Set([...A, ...B, ...C, ...D]) + +/** + * 核心递归辅助函数:从多维语法树节点中提取所有属性名 + */ +function extractKeysFromNode(node: ConditionNode, properties: Set) { + if (Array.isArray(node)) { + for (const subNode of node) { + extractKeysFromNode(subNode, properties) + } + } else { + if (node === '&' || node === '|') return + + // 匹配操作符(> { + const parsedConditions = parse(condition) + const properties = new Set() + extractKeysFromNode(parsedConditions, properties) + return properties +} + +describe('策划配置条件完整覆盖率测试', () => { + test('event [include, exclude, branch]', () => { + const unmappedKeys = new Set() + for (const item of events.values()) { + const conditionsToTrack: { label: string; text: string }[] = [] + if (item.include) { + conditionsToTrack.push({ label: 'include', text: item.include }) + } + if (item.exclude) { + conditionsToTrack.push({ label: 'exclude', text: item.exclude }) + } + + if (item.branch && Array.isArray(item.branch)) { + item.branch.forEach((branchObj, index) => { + if (branchObj && branchObj.condition) { + conditionsToTrack.push({ + label: `branch[${index}].condition (目标事件ID: ${branchObj.event})`, + text: branchObj.condition, + }) + } + }) + } + + for (const { label, text } of conditionsToTrack) { + const usedProperties = getPropertiesFromCondition(text) + for (const key of usedProperties) { + if (!FlatProperties.has(key)) { + unmappedKeys.add( + `[事件 ID: ${item.id}] 的 "${label}" 字段使用了未定义的键名: "${key}" (完整条件: "${text}")`, + ) + } + } + } + } + + if (unmappedKeys.size > 0) { + console.error('\n❌ 发现事件配置表存在未定义的 Property 键名:') + unmappedKeys.forEach(msg => console.error(msg)) + } + + expect(unmappedKeys.size).toBe(0) + }) + + test('talent [condition]', () => { + const unmappedKeys = new Set() + const allTalentItems = talents.values() + + for (const item of allTalentItems) { + const conditionStr = item.condition + if (!conditionStr) continue + + const usedProperties = getPropertiesFromCondition(conditionStr) + for (const key of usedProperties) { + if (!FlatProperties.has(key)) { + unmappedKeys.add( + `[天赋 ID: ${item.id}] 使用了未定义的键名: "${key}" (完整条件: "${conditionStr}")`, + ) + } + } + } + + if (unmappedKeys.size > 0) { + console.error('\n❌ 发现天赋配置表存在未定义的 Property 键名:') + unmappedKeys.forEach(msg => console.error(msg)) + } + + expect(unmappedKeys.size).toBe(0) + }) + + test('achievement [condition]', () => { + const unmappedKeys = new Set() + const allAchievementItems = achievements.values() + + for (const item of allAchievementItems) { + const conditionStr = item.condition + if (!conditionStr) continue + + const usedProperties = getPropertiesFromCondition(conditionStr) + for (const key of usedProperties) { + if (!FlatProperties.has(key)) { + unmappedKeys.add( + `[成就 ID: ${item.id}] 使用了未定义的键名: "${key}" (完整条件: "${conditionStr}")`, + ) + } + } + } + + if (unmappedKeys.size > 0) { + console.error('\n❌ 发现成就配置表存在未定义的 Property 键名:') + unmappedKeys.forEach(msg => console.error(msg)) + } + + expect(unmappedKeys.size).toBe(0) + }) +}) + +describe('FlatProperties', () => { + test('check key', () => { + const game = { + lowest: { + age: 1, + charm: 2, + intelligence: 3, + strength: 4, + money: 5, + spirit: 6, + }, + properties: { + age: 11, + charm: 12, + intelligence: 13, + strength: 14, + money: 15, + spirit: 16, + }, + highest: { + age: 21, + charm: 22, + intelligence: 23, + strength: 24, + money: 25, + spirit: 26, + }, + life: 31, + talents: new Set([33]), + events: new Set([34, 35]), + achievements: new Set([36]), + } + + const profile = { + times: 41, + external: 42, + talents: new Set([43]), + achievements: new Set([44]), + events: new Set([45, 46]), + } + + const flatState = createFlatState(game, profile) + expect(flatState['AGE']).toBe(game.properties.age) + expect(flatState['CHR']).toBe(game.properties.charm) + expect(flatState['INT']).toBe(game.properties.intelligence) + expect(flatState['STR']).toBe(game.properties.strength) + expect(flatState['MNY']).toBe(game.properties.money) + expect(flatState['SPR']).toBe(game.properties.spirit) + expect(flatState['LIF']).toBe(game.life) + expect(flatState['TLT']).toEqual(game.talents) + expect(flatState['EVT']).toEqual(game.events) + expect(flatState['LAGE']).toBe(game.lowest.age) + expect(flatState['HAGE']).toBe(game.highest.age) + expect(flatState['LCHR']).toBe(game.lowest.charm) + expect(flatState['HCHR']).toBe(game.highest.charm) + expect(flatState['LINT']).toBe(game.lowest.intelligence) + expect(flatState['HINT']).toBe(game.highest.intelligence) + expect(flatState['LSTR']).toBe(game.lowest.strength) + expect(flatState['HSTR']).toBe(game.highest.strength) + expect(flatState['LMNY']).toBe(game.lowest.money) + expect(flatState['HMNY']).toBe(game.highest.money) + expect(flatState['LSPR']).toBe(game.lowest.spirit) + expect(flatState['HSPR']).toBe(game.highest.spirit) + expect(flatState['TMS']).toBe(profile.times) + expect(flatState['AEVT']).toEqual(profile.events) + expect(flatState['ATLT']).toEqual(profile.talents) + expect(flatState['AACH']).toEqual(profile.achievements) + expect(flatState['SUM']).toBe( + Math.floor((22 + 23 + 24 + 25 + 26) * 2 + 21 / 2), + ) + }) +}) diff --git a/packages/core/src/state.ts b/packages/core/src/state.ts new file mode 100644 index 0000000..fd98a0e --- /dev/null +++ b/packages/core/src/state.ts @@ -0,0 +1,212 @@ +import type { Talent } from '@remake/data/talent' +import type { Event } from '@remake/data/event' +import type { Achievement } from '@remake/data/achievement' +import { produce, enableMapSet } from 'immer' +import { sum } from '@remake/vitex' + +enableMapSet() + +/** 基础的属性 */ +export interface Properties { + age: number + charm: number + intelligence: number + strength: number + money: number + spirit: number +} + +export type Allocation = Omit + +export interface GameState { + properties: Properties // 本局属性 + highest: Properties // 本局最高属性 + lowest: Properties // 本局最低属性 + life: number // 本局生命值 + talents: Set // 本局拥有的天赋 + events: Set // 本局触发过的事件 + achievements: Set // 本局达成的成就 +} + +/** 持久化存储的数据 */ +export interface ProfileState { + times: number // 游戏次数 + external?: Talent['id'] // 继承的天赋 + talents: Set // 拥有过的天赋 + events: Set // 触发过的事件 + achievements: Set // 达成的成就 + highest?: Properties // 历史最高属性 + lowest?: Properties // 历史最低属性 +} + +export function createState( + allocation: Allocation, + talents?: Iterable, +): GameState { + const properties = { ...allocation, age: -1, spirit: 0 } + return { + properties, + highest: { ...properties }, + lowest: { ...properties }, + life: 1, + talents: new Set(talents), + events: new Set(), + achievements: new Set(), + } +} +export interface FlatState { + AGE: GameState['properties']['age'] + CHR: GameState['properties']['charm'] + INT: GameState['properties']['intelligence'] + STR: GameState['properties']['strength'] + MNY: GameState['properties']['money'] + SPR: GameState['properties']['spirit'] + LIF: GameState['life'] + TLT: GameState['talents'] + EVT: GameState['events'] + LAGE: GameState['lowest']['age'] + HAGE: GameState['highest']['age'] + LCHR: GameState['lowest']['charm'] + HCHR: GameState['highest']['charm'] + LINT: GameState['lowest']['intelligence'] + HINT: GameState['highest']['intelligence'] + LSTR: GameState['lowest']['strength'] + HSTR: GameState['highest']['strength'] + LMNY: GameState['lowest']['money'] + HMNY: GameState['highest']['money'] + LSPR: GameState['lowest']['spirit'] + HSPR: GameState['highest']['spirit'] + + TMS: ProfileState['times'] + AEVT: ProfileState['events'] + ATLT: ProfileState['talents'] + AACH: ProfileState['achievements'] + + SUM: number +} + +interface FlatPropertiesTarget { + game: GameState + profile: ProfileState +} + +const FlatMappers = { + AGE: (state: FlatPropertiesTarget) => state.game.properties.age, + CHR: (state: FlatPropertiesTarget) => state.game.properties.charm, + INT: (state: FlatPropertiesTarget) => state.game.properties.intelligence, + STR: (state: FlatPropertiesTarget) => state.game.properties.strength, + MNY: (state: FlatPropertiesTarget) => state.game.properties.money, + SPR: (state: FlatPropertiesTarget) => state.game.properties.spirit, + LIF: (state: FlatPropertiesTarget) => state.game.life, + TLT: (state: FlatPropertiesTarget) => state.game.talents, + EVT: (state: FlatPropertiesTarget) => state.game.events, + LAGE: (state: FlatPropertiesTarget) => state.game.lowest.age, + HAGE: (state: FlatPropertiesTarget) => state.game.highest.age, + LCHR: (state: FlatPropertiesTarget) => state.game.lowest.charm, + HCHR: (state: FlatPropertiesTarget) => state.game.highest.charm, + LINT: (state: FlatPropertiesTarget) => state.game.lowest.intelligence, + HINT: (state: FlatPropertiesTarget) => state.game.highest.intelligence, + LSTR: (state: FlatPropertiesTarget) => state.game.lowest.strength, + HSTR: (state: FlatPropertiesTarget) => state.game.highest.strength, + LMNY: (state: FlatPropertiesTarget) => state.game.lowest.money, + HMNY: (state: FlatPropertiesTarget) => state.game.highest.money, + LSPR: (state: FlatPropertiesTarget) => state.game.lowest.spirit, + HSPR: (state: FlatPropertiesTarget) => state.game.highest.spirit, + TMS: (state: FlatPropertiesTarget) => state.profile.times, + AEVT: (state: FlatPropertiesTarget) => state.profile.events, + ATLT: (state: FlatPropertiesTarget) => state.profile.talents, + AACH: (state: FlatPropertiesTarget) => state.profile.achievements, + SUM: (state: FlatPropertiesTarget) => { + const { age, ...others } = state.game.highest + const s = sum(Object.values(others)) + return Math.floor(s * 2 + age / 2) + }, +} + +const flatPropertiesHandle = { + get(target: FlatPropertiesTarget, prop: string) { + if (prop in FlatMappers) { + return FlatMappers[prop as keyof typeof FlatMappers](target) + } + }, + set() { + return true + }, +} + +export function createFlatState(game: GameState, profile: ProfileState) { + return new Proxy( + { game, profile }, + flatPropertiesHandle, + ) as unknown as FlatState +} + +export interface Effect { + properties?: Partial + life?: number + talents?: Iterable + events?: Iterable + achievements?: Iterable +} + +export function stateEffect(state: GameState, effect: Effect) { + return produce(state, draft => { + for (const key in effect.properties) { + const prop = key as keyof Properties + const value = effect.properties[prop]! + draft.properties[prop] += value + draft.highest[prop] = Math.max( + draft.highest[prop], + draft.properties[prop], + ) + draft.lowest[prop] = Math.min( + draft.lowest[prop], + draft.properties[prop], + ) + } + if (effect.life) draft.life += effect.life + if (effect.talents) + draft.talents = new Set([...draft.talents, ...effect.talents]) + if (effect.events) + draft.events = new Set([...draft.events, ...effect.events]) + if (effect.achievements) + draft.achievements = new Set([ + ...draft.achievements, + ...effect.achievements, + ]) + }) +} + +function highestProperties(a: Properties, b?: Properties): Properties { + if (!b) return { ...a } + const result = {} as Properties + for (const key of Object.keys(a) as (keyof Properties)[]) { + result[key] = Math.max(a[key], b[key]) + } + return result +} + +function lowestProperties(a: Properties, b?: Properties): Properties { + if (!b) return { ...a } + const result = {} as Properties + for (const key of Object.keys(a) as (keyof Properties)[]) { + result[key] = Math.min(a[key], b[key]) + } + return result +} + +export function nextProfile( + profile: ProfileState, + state: GameState, + external?: Talent['id'], +) { + return { + times: profile.times + 1, + talents: profile.talents.union(state.talents), + events: profile.events.union(state.events), + achievements: profile.achievements.union(state.achievements), + highest: highestProperties(state.highest, profile.highest), + lowest: lowestProperties(state.lowest, profile.lowest), + external: external ?? profile.external, + } +} diff --git a/packages/core/src/talent.ts b/packages/core/src/talent.ts index e69de29..fd95c04 100644 --- a/packages/core/src/talent.ts +++ b/packages/core/src/talent.ts @@ -0,0 +1,21 @@ +import type { TalentEffect } from '@remake/data/talent' +import talents from '@remake/data/talent' +import type { Properties } from './state' +import { pick } from '@remake/vitex' + +const TalentEffectPropertiesKeyMapper = { + MNY: () => 'money', + STR: () => 'strength', + INT: () => 'intelligence', + CHR: () => 'charm', + SPR: () => 'spirit', + RND: () => pick(['money', 'strength', 'intelligence', 'charm', 'spirit'])!, +} as Record keyof Properties> + +export const count = talents.size + +export function get(talent: number) { + return talents.get(talent) +} + +export function random(talent: number) {} diff --git a/packages/data/package.json b/packages/data/package.json index ba64ee3..9a120ad 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -5,7 +5,7 @@ "main": "src/index.ts", "author": "Vick Scarlet ", "scripts": { - "build": "bunx --bun v-transform@3.0.0 transform -t ts -w src -d dist \"**/[!~$]*.xlsx\" ", + "build": "bunx --bun v-transform@3.0.1 transform -t ts -w src -d dist \"**/[!~$]*.xlsx\" ", "lint": "eslint .", "test": "bun test" }, diff --git a/packages/data/src/event.types.ts b/packages/data/src/event.types.ts index 66bb892..82380ce 100644 --- a/packages/data/src/event.types.ts +++ b/packages/data/src/event.types.ts @@ -19,6 +19,14 @@ export type EventEffect = { readonly AGE?: number } +/** 分支路线 */ +export type EventBranch = { + /** 分支条件 */ + condition: string + /** 分支事件ID */ + event: number +} + /** 事件 */ export type Event = { /** ID */ @@ -38,7 +46,7 @@ export type Event = { /** 有某事件时一定随机不到 */ readonly exclude?: string /** 分支路线 */ - readonly branch?: string[] + readonly branch?: EventBranch[] } // @vt-types-end @@ -55,4 +63,15 @@ export const transformers = { } return val }, + branch: (val?: any[]) => { + if (!val) return + for (const key in val) { + const [condition, eventId] = val[key]!.split(':') + const event = Number(eventId) + if (isNaN(event)) + throw new Error(`Invalid event ID in branch: ${eventId}`) + val[key] = { condition, event } + } + return val + }, } diff --git a/packages/vitex/index.ts b/packages/vitex/index.ts new file mode 100644 index 0000000..74ab3fc --- /dev/null +++ b/packages/vitex/index.ts @@ -0,0 +1,24 @@ +export type RNG = (max?: number, min?: number) => number +export function random(max: number, min: number = 0, rng?: RNG): number { + if (rng) return rng(max, min) + return Math.floor(Math.random() * (max - min + 1)) + min +} +export function pick(items: T[], rng?: RNG) { + if (items.length === 0) return null + return items[random(items.length - 1, 0, rng)] +} +export type WeightItem = [T, number] +export function pickWeight(items: WeightItem[], rng?: RNG) { + if (items.length === 0) return null + const totalWeight = items.reduce((sum, [, weight]) => sum + weight, 0) + let mark = random(totalWeight - 1, 0, rng) + for (const [item, weight] of items) { + if (mark < weight) return item + mark -= weight + } + return null +} + +export function sum(arr: number[]) { + return arr.reduce((sum, v) => sum + v, 0) +} diff --git a/packages/vitex/package.json b/packages/vitex/package.json new file mode 100644 index 0000000..12ad7e7 --- /dev/null +++ b/packages/vitex/package.json @@ -0,0 +1,14 @@ +{ + "name": "@remake/vitex", + "type": "module", + "version": "3.0.0", + "main": "./index.ts", + "author": "Vick Scarlet ", + "scripts": { + "lint": "eslint .", + "test": "bun test" + }, + "eslintConfig": { + "extends": "../../package.json" + } +} diff --git a/packages/vitex/tsconfig.json b/packages/vitex/tsconfig.json new file mode 100644 index 0000000..96bc27a --- /dev/null +++ b/packages/vitex/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "types": ["bun"], + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": true, + "noUnusedParameters": true, + "noPropertyAccessFromIndexSignature": false, + "noImplicitAny": true + }, + "include": ["**/*"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc24737..a8b6f74 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,30 +47,40 @@ importers: devDependencies: '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.6(@babel/core@7.29.7)(preact@10.29.7)(rollup@4.62.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)) + version: 2.10.6(@babel/core@7.29.7)(preact@10.29.7)(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0)) eslint-config-preact: specifier: ^2.0.0 version: 2.0.0(eslint@10.8.0(jiti@2.7.0)) vite: specifier: ^8.1.3 - version: 8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0) + version: 8.1.5(@types/node@26.1.2)(jiti@2.7.0) vitest: specifier: ^4.1.10 - version: 4.1.10(@types/node@26.1.2)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)) + version: 4.1.10(@types/node@26.1.2)(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0)) packages/condition: {} packages/core: - devDependencies: + dependencies: '@remake/condition': specifier: workspace:* version: link:../condition '@remake/data': specifier: workspace:* version: link:../data + '@remake/vitex': + specifier: workspace:* + version: link:../vitex + immer: + specifier: ^11.1.15 + version: 11.1.15 packages/data: {} + packages/vitex: {} + + thirdparty/bili-toy: {} + packages: '@babel/code-frame@7.29.7': @@ -187,162 +197,6 @@ packages: '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -419,11 +273,12 @@ packages: '@mdn/browser-compat-data@6.1.5': resolution: {integrity: sha512-PzdZZzRhcXvKB0begee28n5lvwAcinGKYuLZOVxHAZm+n7y01ddEGfdS1ZXRuVcV+ndG6mSEAE8vgudom5UjYg==} - '@napi-rs/wasm-runtime@1.1.6': - resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + '@napi-rs/wasm-runtime@1.2.0': + resolution: {integrity: sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 + '@emnapi/core': ^2.0.0-alpha.3 + '@emnapi/runtime': ^2.0.0-alpha.3 '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} @@ -565,144 +420,6 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.62.3': - resolution: {integrity: sha512-c0wdcekXtQvvn5Tsrk/+op/gUArrbWaFduBnTLP2l1cKLSQs4diMWjJw3m6A0DdzT8dAAX95KpkJ3qynCePbmw==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.62.3': - resolution: {integrity: sha512-3YjElDdWN+qXAFbJ/CzPV+0wspLqh54k/I6GfdYtEJRqg7buSgc1yPM3B+93j1M4neobtkATHZTmxK2AMVGfnA==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.62.3': - resolution: {integrity: sha512-Pch2pFNOxxz1hTjypIdPyRTR6riiwRl84+VcN9djS680fw+Co1nAJINrdpqp7KV0NvyuU8ilZXZCjd7ykJl1GQ==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.62.3': - resolution: {integrity: sha512-LEuncFUHFiF8t4yZVZvvZA1wk0pjAscRnsrn1EfTEmN4HXotBi2YtcnLRyaK6UbuczW7xZS5ES+81Rdz8Z0T6g==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.62.3': - resolution: {integrity: sha512-zvBUvsQUpOWALdDsk6qbS8bXf2VxmPisuudNDrY7x0p0jBdsoZl8HsHczIOgkQiZldmcacMKtBzpoGVNeIe2bQ==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.62.3': - resolution: {integrity: sha512-C2KmNrcSem/AMg984H/dev+si0lieQGdXdR/lYGJnuumXnFb9Y7QdiI62obFdLlxRYLBv4P0eUVIDbD4c1vVvw==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.62.3': - resolution: {integrity: sha512-ggXnsTAEzNQx74XpunRsiZ9aBZDsI7XIa0hm2nzR9f4WzH5/f/d73ZSDaC5ejJ8YLY4NW+V3wr0tjOaeCq8hqA==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.62.3': - resolution: {integrity: sha512-2vng+FlzNUhKZxtej3IUqJgbZoQk2M/dwQM20+ULV0R/E/8tr9/P6uEf2iiGIk4HL0zMKh5Jry7mUHdUOvyGgA==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.62.3': - resolution: {integrity: sha512-LLLFZKt4/Nraf9rxDkhiU8QVgLF4WmCkfr0L4fj0fPfIZFBib0DeiFk1hhaYKd03LFAFJcxHslhDFlNJLylf5Q==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.62.3': - resolution: {integrity: sha512-WJkdQCvS9sWNOUBJZfQRKpZGFBztRzcowI+nndmflKgU4XY+3a420FgTOSKTsVqJbnzSxeT4vaJalpOaPo2YCQ==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.62.3': - resolution: {integrity: sha512-PwHXCCS2n64/1Ot6rP1YEYA02MGYBcQlr8CSZZyrUG2O7NH6NklYmvr9v3Jy+5e/eDeNchc/ukmKJi9LuflMIQ==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.62.3': - resolution: {integrity: sha512-vUjxINQu3RC8NZS3ykk1gN65gIz8pAopOq2HXuZhiIxHdx7TFvDG+jgrdSgInu1Eza4/Rfi2VzZgyIgEH4WOaw==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.62.3': - resolution: {integrity: sha512-wzko4aJ13+0G3kGnviCg5gnXFKd40izKsrf2uOw12US4XqprkDrmwOpeW14aSNa37V8bfPcz5Fkob6LZ3BAPmA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.62.3': - resolution: {integrity: sha512-8120ue0JUMSwy11stlwnfdX3pPd+WZYGCDBwEHWtIHi6pOpZmsEF5QKB7a/UN+XFdqvobxz98kv8RTqikyCEBw==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.62.3': - resolution: {integrity: sha512-XLFHnR3tXMjbOCh2vtVJHmxt+995uJsTERQyseFDRA0xxMxyTZPLa3OIUlyFaO4mF/Lu0FjmWHCuPXJT1n/IOg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.62.3': - resolution: {integrity: sha512-se6yXvNGMIl0f+RQzyh7XAmia8/9kplQx424wnG2w0C1oi6XgO6Y8otKhdXFHbHs88Ihavzmvh1NWjuovE76BQ==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.62.3': - resolution: {integrity: sha512-gNoxRefktVIiGflpONuxWWXZAzIQG++z9qHO3xKwk4WdDMuQja3JHGfE1u0i3PfPDyvhypdk+WrgIJqLhGG7sg==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.62.3': - resolution: {integrity: sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.62.3': - resolution: {integrity: sha512-LBx9LYXvj2CBkMkjLdNAWLwH0MLMin7do2VcVo9kVPibGLkY0BQQut2fv7NVqkXqZ/CrAu9LqDHVV1xHCMpCPw==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.62.3': - resolution: {integrity: sha512-ABVf3Q0RCu7NcyCCOZQI0pJ3GuSdfSl8EXcy88QtdceIMIoCUdfhsJChZ64L9zVM2aJHjde1Bhn5uqSRcX9ySA==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.62.3': - resolution: {integrity: sha512-+2Cy/ldweGBLlPIKsQLF8U5N44a0KDdbrk1rAjHOM9M2K+kGdIVjHLmmrZIcx+9Ny3ke/1JomCsDI1ocb11+sg==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.62.3': - resolution: {integrity: sha512-dtZvzc8BedpSaFNy75x6uiWwAGTH+aZHDtdrqP6qk+WcLJrfti6sGje1ZJ9UxyzDLF23d/mV+PaMwuC0hL7UVA==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.62.3': - resolution: {integrity: sha512-Rj8Ra4noo+aYy7sKBggCx0407mws34kAb1ySyWuq5DAtFBQdkSwnsjCgPrhPe9cvgBKZIukpE+CVHvORCS93kQ==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.62.3': - resolution: {integrity: sha512-vp7N084ew/odXn2gi/mzm9mUkQu9l6AiN6dt4IeUM2Uvm9o+cVmP+YkqbMOteLbiGgqBBlJZjIMYVCfOOIVbVQ==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.62.3': - resolution: {integrity: sha512-MOG/3gTOn4Fwf574RVOaY61I5o6P90legkFADiTyn1hyjNydT+cerU2rLUwPdZkKKyJ+iT+K9p7WXK4LM1Ka6g==} - cpu: [x64] - os: [win32] - '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -947,8 +664,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.17.0: - resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} engines: {node: '>=0.4.0'} hasBin: true @@ -1010,8 +727,8 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - baseline-browser-mapping@2.11.4: - resolution: {integrity: sha512-s4+sLr9mZ/CyqeRritFeYV/Zx73OAtmaHn6kkBS1XRoJn1hrg3xIDUcpicAEX68tkcIN0iBCgti31C8zxtkhsQ==} + baseline-browser-mapping@2.11.6: + resolution: {integrity: sha512-69D/imtToCsIcAl8WBS2YaRwA4jO/j0HhU+hELqMEu9f54MoUtI6+XH5mrKU8rEFNEk/Ui1I2MK4/JkWacclGw==} engines: {node: '>=6.0.0'} hasBin: true @@ -1126,8 +843,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.396: - resolution: {integrity: sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==} + electron-to-chromium@1.5.398: + resolution: {integrity: sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ==} entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} @@ -1172,11 +889,6 @@ packages: resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} engines: {node: '>= 0.4'} - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} - engines: {node: '>=18'} - hasBin: true - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1399,6 +1111,9 @@ packages: resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} + immer@11.1.15: + resolution: {integrity: sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1652,8 +1367,8 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - minimatch@10.2.5: - resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} minimatch@3.1.5: @@ -1761,8 +1476,8 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss@8.5.23: - resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + postcss@8.5.24: + resolution: {integrity: sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw==} engines: {node: ^10 || ^12 || >=14} preact@10.29.7: @@ -1805,11 +1520,6 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.62.3: - resolution: {integrity: sha512-Gu0c0iH9FzgX1L1t7ByIbbS3Vmdz+6KHm/EsqmmC71gUQ82yvZRkTK6XzrFObSka91WUVdynqp6nsfilzr5k6Q==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} @@ -1929,8 +1639,8 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} ts-api-utils@2.5.0: @@ -2277,84 +1987,6 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.28.1': - optional: true - - '@esbuild/android-arm64@0.28.1': - optional: true - - '@esbuild/android-arm@0.28.1': - optional: true - - '@esbuild/android-x64@0.28.1': - optional: true - - '@esbuild/darwin-arm64@0.28.1': - optional: true - - '@esbuild/darwin-x64@0.28.1': - optional: true - - '@esbuild/freebsd-arm64@0.28.1': - optional: true - - '@esbuild/freebsd-x64@0.28.1': - optional: true - - '@esbuild/linux-arm64@0.28.1': - optional: true - - '@esbuild/linux-arm@0.28.1': - optional: true - - '@esbuild/linux-ia32@0.28.1': - optional: true - - '@esbuild/linux-loong64@0.28.1': - optional: true - - '@esbuild/linux-mips64el@0.28.1': - optional: true - - '@esbuild/linux-ppc64@0.28.1': - optional: true - - '@esbuild/linux-riscv64@0.28.1': - optional: true - - '@esbuild/linux-s390x@0.28.1': - optional: true - - '@esbuild/linux-x64@0.28.1': - optional: true - - '@esbuild/netbsd-arm64@0.28.1': - optional: true - - '@esbuild/netbsd-x64@0.28.1': - optional: true - - '@esbuild/openbsd-arm64@0.28.1': - optional: true - - '@esbuild/openbsd-x64@0.28.1': - optional: true - - '@esbuild/openharmony-arm64@0.28.1': - optional: true - - '@esbuild/sunos-x64@0.28.1': - optional: true - - '@esbuild/win32-arm64@0.28.1': - optional: true - - '@esbuild/win32-ia32@0.28.1': - optional: true - - '@esbuild/win32-x64@0.28.1': - optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@10.8.0(jiti@2.7.0))': dependencies: eslint: 10.8.0(jiti@2.7.0) @@ -2366,7 +1998,7 @@ snapshots: dependencies: '@eslint/object-schema': 3.0.5 debug: 4.4.3 - minimatch: 10.2.5 + minimatch: 10.2.6 transitivePeerDependencies: - supports-color @@ -2426,7 +2058,7 @@ snapshots: '@mdn/browser-compat-data@6.1.5': {} - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@napi-rs/wasm-runtime@1.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 @@ -2439,19 +2071,19 @@ snapshots: '@oxc-project/types@0.139.0': {} - '@preact/preset-vite@2.10.6(@babel/core@7.29.7)(preact@10.29.7)(rollup@4.62.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0))': + '@preact/preset-vite@2.10.6(@babel/core@7.29.7)(preact@10.29.7)(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7) - '@prefresh/vite': 2.4.12(preact@10.29.7)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)) - '@rollup/pluginutils': 5.4.0(rollup@4.62.3) + '@prefresh/vite': 2.4.12(preact@10.29.7)(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0)) + '@rollup/pluginutils': 5.4.0 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7) debug: 4.4.3 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0) - vite-prerender-plugin: 0.5.13(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)) + vite: 8.1.5(@types/node@26.1.2)(jiti@2.7.0) + vite-prerender-plugin: 0.5.13(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0)) zimmerframe: 1.1.4 transitivePeerDependencies: - preact @@ -2466,7 +2098,7 @@ snapshots: '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.12(preact@10.29.7)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0))': + '@prefresh/vite@2.4.12(preact@10.29.7)(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0))': dependencies: '@babel/core': 7.29.7 '@prefresh/babel-plugin': 0.5.3 @@ -2474,7 +2106,7 @@ snapshots: '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 preact: 10.29.7 - vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0) + vite: 8.1.5(@types/node@26.1.2)(jiti@2.7.0) transitivePeerDependencies: - supports-color @@ -2518,7 +2150,7 @@ snapshots: dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true '@rolldown/binding-win32-arm64-msvc@1.1.5': @@ -2534,88 +2166,11 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.2 - '@rollup/pluginutils@5.4.0(rollup@4.62.3)': + '@rollup/pluginutils@5.4.0': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.5 - optionalDependencies: - rollup: 4.62.3 - - '@rollup/rollup-android-arm-eabi@4.62.3': - optional: true - - '@rollup/rollup-android-arm64@4.62.3': - optional: true - - '@rollup/rollup-darwin-arm64@4.62.3': - optional: true - - '@rollup/rollup-darwin-x64@4.62.3': - optional: true - - '@rollup/rollup-freebsd-arm64@4.62.3': - optional: true - - '@rollup/rollup-freebsd-x64@4.62.3': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.62.3': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.62.3': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.62.3': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.62.3': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.62.3': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.62.3': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.62.3': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.62.3': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.62.3': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.62.3': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.62.3': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.62.3': - optional: true - - '@rollup/rollup-linux-x64-musl@4.62.3': - optional: true - - '@rollup/rollup-openbsd-x64@4.62.3': - optional: true - - '@rollup/rollup-openharmony-arm64@4.62.3': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.62.3': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.62.3': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.62.3': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.62.3': - optional: true '@standard-schema/spec@1.1.0': {} @@ -2712,7 +2267,7 @@ snapshots: '@typescript-eslint/types': 8.65.0 '@typescript-eslint/visitor-keys': 8.65.0 debug: 4.4.3 - minimatch: 10.2.5 + minimatch: 10.2.6 semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -2807,19 +2362,19 @@ snapshots: '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 chai: 6.2.2 - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.10(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0))': + '@vitest/mocker@4.1.10(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0) + vite: 8.1.5(@types/node@26.1.2)(jiti@2.7.0) '@vitest/pretty-format@4.1.10': dependencies: - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 '@vitest/runner@4.1.10': dependencies: @@ -2839,13 +2394,13 @@ snapshots: dependencies: '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 - acorn-jsx@5.3.2(acorn@8.17.0): + acorn-jsx@5.3.2(acorn@8.18.0): dependencies: - acorn: 8.17.0 + acorn: 8.18.0 - acorn@8.17.0: {} + acorn@8.18.0: {} ajv@6.15.0: dependencies: @@ -2931,7 +2486,7 @@ snapshots: balanced-match@4.0.4: {} - baseline-browser-mapping@2.11.4: {} + baseline-browser-mapping@2.11.6: {} boolbase@1.0.0: {} @@ -2946,9 +2501,9 @@ snapshots: browserslist@4.28.7: dependencies: - baseline-browser-mapping: 2.11.4 + baseline-browser-mapping: 2.11.6 caniuse-lite: 1.0.30001806 - electron-to-chromium: 1.5.396 + electron-to-chromium: 1.5.398 node-releases: 2.0.51 update-browserslist-db: 1.2.3(browserslist@4.28.7) @@ -3063,7 +2618,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.396: {} + electron-to-chromium@1.5.398: {} entities@4.5.0: {} @@ -3180,36 +2735,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.28.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 - optional: true - escalade@3.2.0: {} escape-string-regexp@4.0.0: {} @@ -3313,7 +2838,7 @@ snapshots: imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 - minimatch: 10.2.5 + minimatch: 10.2.6 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -3323,8 +2848,8 @@ snapshots: espree@11.2.0: dependencies: - acorn: 8.17.0 - acorn-jsx: 5.3.2(acorn@8.17.0) + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 5.0.1 esquery@1.7.0: @@ -3467,6 +2992,8 @@ snapshots: ignore@7.0.6: {} + immer@11.1.15: {} + imurmurhash@0.1.4: {} internal-slot@1.1.0: @@ -3699,7 +3226,7 @@ snapshots: math-intrinsics@1.1.0: {} - minimatch@10.2.5: + minimatch@10.2.6: dependencies: brace-expansion: 5.0.8 @@ -3809,7 +3336,7 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss@8.5.23: + postcss@8.5.24: dependencies: nanoid: 3.3.16 picocolors: 1.1.1 @@ -3879,38 +3406,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.1.5 - rollup@4.62.3: - dependencies: - '@types/estree': 1.0.9 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.3 - '@rollup/rollup-android-arm64': 4.62.3 - '@rollup/rollup-darwin-arm64': 4.62.3 - '@rollup/rollup-darwin-x64': 4.62.3 - '@rollup/rollup-freebsd-arm64': 4.62.3 - '@rollup/rollup-freebsd-x64': 4.62.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.3 - '@rollup/rollup-linux-arm-musleabihf': 4.62.3 - '@rollup/rollup-linux-arm64-gnu': 4.62.3 - '@rollup/rollup-linux-arm64-musl': 4.62.3 - '@rollup/rollup-linux-loong64-gnu': 4.62.3 - '@rollup/rollup-linux-loong64-musl': 4.62.3 - '@rollup/rollup-linux-ppc64-gnu': 4.62.3 - '@rollup/rollup-linux-ppc64-musl': 4.62.3 - '@rollup/rollup-linux-riscv64-gnu': 4.62.3 - '@rollup/rollup-linux-riscv64-musl': 4.62.3 - '@rollup/rollup-linux-s390x-gnu': 4.62.3 - '@rollup/rollup-linux-x64-gnu': 4.62.3 - '@rollup/rollup-linux-x64-musl': 4.62.3 - '@rollup/rollup-openbsd-x64': 4.62.3 - '@rollup/rollup-openharmony-arm64': 4.62.3 - '@rollup/rollup-win32-arm64-msvc': 4.62.3 - '@rollup/rollup-win32-ia32-msvc': 4.62.3 - '@rollup/rollup-win32-x64-gnu': 4.62.3 - '@rollup/rollup-win32-x64-msvc': 4.62.3 - fsevents: 2.3.3 - optional: true - safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 @@ -4067,7 +3562,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - tinyrainbow@3.1.0: {} + tinyrainbow@3.1.1: {} ts-api-utils@2.5.0(@typescript/typescript6@6.0.2): dependencies: @@ -4157,7 +3652,7 @@ snapshots: dependencies: punycode: 2.3.1 - vite-prerender-plugin@0.5.13(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)): + vite-prerender-plugin@0.5.13(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -4165,25 +3660,24 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0 - vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0) + vite: 8.1.5(@types/node@26.1.2)(jiti@2.7.0) - vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0): + vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.5 - postcss: 8.5.23 + postcss: 8.5.24 rolldown: 1.1.5 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.1.2 - esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 - vitest@4.1.10(@types/node@26.1.2)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)): + vitest@4.1.10(@types/node@26.1.2)(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)) + '@vitest/mocker': 4.1.10(vite@8.1.5(@types/node@26.1.2)(jiti@2.7.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -4199,8 +3693,8 @@ snapshots: tinybench: 2.9.0 tinyexec: 1.2.4 tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0) + tinyrainbow: 3.1.1 + vite: 8.1.5(@types/node@26.1.2)(jiti@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.1.2 diff --git a/thirdparty/bili-toy/package.json b/thirdparty/bili-toy/package.json index 2295582..98f693a 100644 --- a/thirdparty/bili-toy/package.json +++ b/thirdparty/bili-toy/package.json @@ -1,5 +1,5 @@ { - "name": "@remake/condition", + "name": "@remake/thirdparty-bili-toy", "type": "module", "version": "3.0.0", "main": "./index.ts", diff --git a/thirdparty/bili-toy/toy.d.ts b/thirdparty/bili-toy/toy.d.ts index 73ce630..057097b 100644 --- a/thirdparty/bili-toy/toy.d.ts +++ b/thirdparty/bili-toy/toy.d.ts @@ -1,194 +1,503 @@ -declare namespace toy { +/** + * Toy JS SDK 类型声明(面向创作者) + * + * 用法:把本文件放进你的 Toy 项目(如 `types/toy.d.ts`),TypeScript 会自动加载, + * 无需 import 即可获得 `window.toy` 的类型检查与补全。 + * + * 前置:页面 `` 中引入 + * + * 加载后 SDK 把实例挂到全局 `window.toy`,所有方法返回 Promise。 + * + * 通用约定: + * - 所有方法抛出的 Error,message 统一带 `[ToySDK]` 前缀。 + * - 数据类能力(用户 / 作者 / 视频互动)不抛错时通过 `status` 字段表达结果, + * 需要先判断 `status === 'ok'` 再读 `data` / `items`。 + * - 云存储与排行榜失败时 Promise reject,需要 try/catch。 + */ + +declare namespace ToySDK { + // --------------------------------------------------------------------------- + // 页面跳转 + // --------------------------------------------------------------------------- + + /** 站内跳转的目标页面类型。 */ + type NavigateType = 'video' | 'space' | 'search' | 'opus' | 'tribee' | 'toy' + + interface NavigateReq { + /** 目标页面类型。 */ + type: NavigateType /** - * B站 Toy 平台开放能力名称联合类型 + * 资源标识,随 `type` 变化: + * - video: BV 号,如 `BV1Hh411S7Ys` + * - space: 用户 mid + * - search: 搜索关键词(SDK 内部会做 URL 编码) + * - opus: 图文 / 动态 id + * - tribee: 小站 id + * - toy: toy id */ - type Ability = - | 'navigate' - | 'saveImageToAlbum' - | 'closeBrowser' - | 'getUserProfile' - | 'reportAction' - | 'getCloudStorage' - | 'setCloudStorage' - | 'removeCloudStorage' - | 'getAuthorProfile' - | 'getAuthorVideos' - | 'getAuthorRelation' - | 'getVideoUserActions' - | 'submitScore' - | 'getRankList' - | 'getMyRank' + id: string + /** 额外查询参数,拼接到目标 URL 上透传给目标页面。 */ + extra?: Record + } + + // --------------------------------------------------------------------------- + // 保存图片 + // --------------------------------------------------------------------------- + + /** `url` 与 `base64Data` 二选一,两者都不传则由客户端返回错误。 */ + interface SaveImageReq { + /** 网络图片地址。 */ + url?: string + /** base64 图片数据。 */ + base64Data?: string + /** 申请相册权限时展示给用户的提示文案。 */ + hintMsg?: string + } + + interface SaveImageResp { + /** 保存后的本地文件路径,由客户端返回。 */ + localPath: string + } + + interface ReportActionReq { + /** UP 主自定义的动作标识。 */ + userEventId: string + } + + // --------------------------------------------------------------------------- + // 用户信息 + // --------------------------------------------------------------------------- + + interface UserProfileResp { + /** 头像地址,SDK 已统一归一化为 https 与 p0 CDN 域名。 */ + avatar: string + /** 昵称。 */ + nickname: string + } + + // --------------------------------------------------------------------------- + // 数据类能力的状态枚举 + // --------------------------------------------------------------------------- + + /** + * 数据类能力的整体状态。 + * - `ok`: 成功 + * - `partial`: 批量请求部分成功,逐项状态见各 item 的 `status` + * - `unauthorized`: 未登录 + * - `denied`: 用户拒绝了数据使用确认 + * - `unsupported`: 当前环境不支持(如外部手机浏览器,SDK 会引导打开 B站 App) + * - `toy_context_unavailable`: 拿不到当前 toy 上下文 + * - `author_mismatch`: 请求的资源不属于当前 Toy 作者 + * - `video_not_found`: 视频不存在 + * - `video_invisible`: 视频对当前用户不可见 + * - `unavailable`: 依赖服务不可用 + * - `invalid_argument`: 参数非法 + */ + type ToyDataStatus = + | 'ok' + | 'partial' + | 'unauthorized' + | 'denied' + | 'unsupported' + | 'toy_context_unavailable' + | 'author_mismatch' + | 'video_not_found' + | 'video_invisible' + | 'unavailable' + | 'invalid_argument' + + /** 批量结果中单项的状态:与 `ToyDataStatus` 相同,但不会是 `partial`。 */ + type ToyItemStatus = Exclude + + // --------------------------------------------------------------------------- + // 作者资料 + // --------------------------------------------------------------------------- + + /** 作者认证信息。`role` / `type` 为后端数字枚举,SDK 未定义其取值含义。 */ + interface AuthorCertification { + role: number + title: string + description: string + type: number + } + + /** 充电聚合信息。 */ + interface ChargingSummary { + /** 充电人数。 */ + count: number + display?: { + show: boolean + text?: string + } + } + + /** 粉丝勋章配置。 */ + interface FanMedalConfig { + /** 勋章名称。 */ + name: string + /** 是否已开启粉丝勋章。 */ + enabled: boolean + /** 各等级的亲密度区间;最高等级无上限时 `maxIntimacy` 缺省。 */ + levels: Array<{ + level: number + minIntimacy: number + maxIntimacy?: number + }> + } + + interface AuthorProfile { + nickname: string + avatar: string + /** 个性签名。 */ + sign: string + /** 未认证时缺省。 */ + certification?: AuthorCertification + /** 关注数。 */ + following: number + /** 粉丝数。 */ + follower: number + /** 稿件数。 */ + archiveCount: number + /** 未开通充电时缺省。 */ + charging?: ChargingSummary + /** 未配置粉丝勋章时缺省。 */ + fanMedal?: FanMedalConfig + /** 生日,时间戳;未公开时缺省。 */ + birthday?: number + } + + interface AuthorProfileResp { + status: ToyDataStatus + /** `status` 非 `ok` 时缺省。 */ + data?: AuthorProfile + } + + // --------------------------------------------------------------------------- + // 作者视频 + // --------------------------------------------------------------------------- + + /** 视频引用:每项只能传 `aid` 或 `bvid` 之一,同时传或都不传会本地抛错。 */ + type AuthorVideoRef = + | { aid: number; bvid?: never } + | { bvid: string; aid?: never } + + interface AuthorVideosReq { + /** 1–50 项;SDK 会按 aid/bvid 去重并保留首次出现顺序。 */ + videos: AuthorVideoRef[] + } + + interface AuthorVideo { + aid: number + bvid: string + title: string + /** 封面地址,SDK 已归一化 CDN 域名。 */ + cover: string + description: string + /** 发布时间,时间戳。 */ + publishTime: number + /** 总时长,单位由后端定义。 */ + duration: number + /** 分区名称。 */ + partition?: string + /** 分 P 列表。 */ + pages: Array<{ + page: number + title: string + duration: number + }> + /** 稿件统计数据。 */ + stat: { + view: number + like: number + coin: number + favorite: number + share: number + comment: number + danmaku: number + } + /** 付费相关标记。 */ + pay: { + /** 是否充电专属。 */ + chargingPay: boolean + /** 是否付费稿件。 */ + paid: boolean + } + /** 所属合集 id;不属于任何合集时缺省。 */ + seasonId?: number + /** 排行信息;无排行数据时缺省。 */ + rank?: { + now: number + highest: number + } + } + + interface AuthorVideoItem { + /** 回显本次请求传入的引用,用于与请求项对应。 */ + ref: AuthorVideoRef + status: ToyItemStatus + /** `status` 非 `ok` 时缺省(如非当前作者、视频不可见)。 */ + data?: AuthorVideo + } + + interface AuthorVideosResp { + /** 整体状态;部分项失败时为 `partial`。 */ + status: ToyDataStatus + items: AuthorVideoItem[] + } + + // --------------------------------------------------------------------------- + // 作者互动关系 + // --------------------------------------------------------------------------- + + interface AuthorRelation { + /** 当前访问用户是否已关注该作者。 */ + isFollowing: boolean + /** 当前访问用户是否就是该 Toy 作者本人。 */ + isAuthor: boolean + /** 是否为老粉。 */ + isOldFan: boolean + /** 是否持有该作者的粉丝勋章。 */ + hasFanMedal: boolean + /** 勋章名称;无勋章时缺省。 */ + fanMedalName?: string + /** 勋章等级;无勋章时缺省。 */ + fanMedalLevel?: number + /** 勋章是否处于点亮状态;无勋章时缺省。 */ + isFanMedalActive?: boolean + /** 当前是否正在对该作者进行包月充电。 */ + isCharging: boolean + /** 关注时间,时间戳;未关注时缺省。 */ + followTime?: number + } + + interface AuthorRelationResp { + status: ToyDataStatus + /** `status` 非 `ok` 时缺省。 */ + data?: AuthorRelation + } + + // --------------------------------------------------------------------------- + // 视频互动数据 + // --------------------------------------------------------------------------- + + interface VideoUserActionsReq { + /** 1–50 个正整数 aid;SDK 会去重并保留首次出现顺序。 */ + aids: number[] + } + + interface VideoUserActionItem { + aid: number + status: ToyItemStatus + /** 是否已点赞;`status` 非 `ok` 时缺省。 */ + liked?: boolean + /** 已投币数;`status` 非 `ok` 时缺省。 */ + coinCount?: number + /** 是否已收藏;`status` 非 `ok` 时缺省。 */ + favorited?: boolean + } + + interface VideoUserActionsResp { + /** 整体状态;部分项失败时为 `partial`。 */ + status: ToyDataStatus + items: VideoUserActionItem[] + } + + // --------------------------------------------------------------------------- + // 排行榜 + // --------------------------------------------------------------------------- + + /** 榜单周期:总榜(永久)/ 月 / 周 / 日。 */ + type RankPeriod = 'all' | 'month' | 'week' | 'day' + + interface SubmitScoreReq { + /** 榜位,固定 1 / 2 / 3(含义由 toy 自定义),不传默认 1;非法值本地抛错。 */ + board?: number + /** + * 本次成绩的绝对分数,不是增量。整数,取值范围 -16777216 ~ 16777215, + * 允许 0 与负数;超出范围本地抛错。服务端只保留该榜位的历史最高分。 + */ + score: number + } + + interface SubmitScoreResp { + /** 我的总榜(all)历史最高分,已合并本次提交。 */ + score: number + } + + interface RankListReq { + /** 榜位,固定 1 / 2 / 3,不传默认 1。 */ + board?: number + /** 周期,不传按总榜 `all`。 */ + period?: RankPeriod + /** 返回名次数量;不传或超上限按后端默认(≤100)。 */ + limit?: number + } + + /** 榜单单行:名次 + 历史最高分 + 展示用昵称/头像,不含 uid。 */ + interface RankItem { + rank: number + score: number + nickname: string + /** 头像地址,SDK 已归一化 CDN 域名。 */ + avatar: string + } + + interface MyRankReq { + /** 榜位,固定 1 / 2 / 3,不传默认 1。 */ + board?: number + /** 周期,不传按总榜 `all`。 */ + period?: RankPeriod + } + + interface MyRankResp { + /** 是否已上榜。分数允许 0 / 负,判断是否上榜必须用本字段,不能用 `score`。 */ + ranked: boolean + /** 我的名次,从 1 起,唯一不并列(同分先达成者靠前);未上榜为 0。 */ + rank: number + /** 我的历史最高分;未上榜为 0。 */ + score: number + } + + // --------------------------------------------------------------------------- + // 媒体能力 + // --------------------------------------------------------------------------- + + interface MediaRelayOptions { + /** 摄像头朝向,不传默认使用前置摄像头。 */ + facingMode?: 'user' | 'environment' + } + + // --------------------------------------------------------------------------- + // window.toy 的公开 API 面 + // --------------------------------------------------------------------------- + + interface Toy { + /** + * 判断当前环境是否支持指定能力。传能力名(如 `'saveImageToAlbum'`)即可, + * 带不带 `toy.` 前缀都能匹配。 + * + * 端外 Web 不支持 `saveImageToAlbum` / `closeBrowser`,其余能力两端一致。 + */ + isSupport(ability: string): Promise /** - * 判断当前 B站 App 宿主环境是否支持指定开放能力 + * 跳转到指定页面。 + * + * 必须在用户手势事件(如 click)中调用:SDK 会检查 `navigator.userActivation`, + * 无有效用户激活时抛错。端内走 JSB 原生跳转,端外用 `window.open` 新开标签页。 */ - function isSupport(ability: Ability): Promise - - /** 跨页面/App组件跳转配置参数 */ - type NavigateRequest = { - /** 目标页面类型:video(视频), space(空间), search(搜索), opus(图文动态), tribee(社区), toy(其它小游戏) */ - type: 'video' | 'space' | 'search' | 'opus' | 'tribee' | 'toy' - /** 目标资源唯一标识 ID,如视频 BV 号、用户 mid、动态 id、游戏 id */ - id: string - /** 额外附加参数,会作为 Query 或者是透传参数传递给目标承载页面 */ - extra?: Record - } - /** - * 跳转到指定 B站 App 原生或 H5 页面(注意:必须由用户手势或点击事件同步触发) - */ - function navigate(req: NavigateRequest): Promise - - /** 保存图片到相册的参数约束 */ - type SaveImageToAlbumRequest = ( - | { - /** 网络图片的绝对 URL 地址 */ - url: string - base64?: never - } - | { - /** 带有 Data URI 前缀或纯 base64 的图片字符数据,体积最大硬限制 2M */ - base64: string - url?: never - } - ) & { - /** 客户端唤起操作系统申请相册写入权限时的引导提示文案 */ - hintMsg?: string - } - /** - * 保存图片到系统相册(此 API 仅在 B站 App 宿主环境内环境生效) - */ - function saveImageToAlbum( - req: SaveImageToAlbumRequest, - ): Promise<{ localPath: string }> + navigate(req: NavigateReq): Promise /** - * 关闭当前的 H5/小游戏浏览器容器,返回到上一级 App 原生界面 + * 保存图片到系统相册。**仅 B站 App 内可用**,Web 端调用直接抛错。 + * + * Web 端请改用标准浏览器下载能力(`` 或 canvas blob URL,需用户点击触发)。 */ - function closeBrowser(): Promise + saveImageToAlbum(req: SaveImageReq): Promise - /** 用户基本个人资料返回结果 */ - type UserProfile = { - /** 用户 mid */ - mid: string - /** 昵称 */ - nickname: string - /** 头像绝对 URL 地址 */ - avatar: string - /** 性别: 0-保密, 1-男, 2-女 */ - gender: 0 | 1 | 2 - } - /** - * 唤起 B站 原生授权弹窗,获取当前登录用户的公开个人资料 - */ - function getUserProfile(): Promise - - /** 行为汇报参数配置 */ - type ReportActionRequest = { - /** 行为埋点事件名/动作名 */ - action: string - /** 汇报携带的属性字典 */ - label?: Record - } - /** - * 向 B站 开放平台数仓上报当前玩家的游戏行为埋点数据 - */ - function reportAction(req: ReportActionRequest): Promise - - /** 获取云存储数据(托管数据)*/ - function getCloudStorage( - keys?: [...K], - ): Promise<{ [P in K[number]]?: string }> - /** 设置/写入云存储数据 */ - function setCloudStorage(items: Record): Promise - /** 移除指定的云存储键值对 */ - function removeCloudStorage(req: string[]): Promise - - /** UP主/作者档案资料 */ - type AuthorProfile = { - mid: string - name: string - face: string - fans: number - } - /** 获取关联当前活动/游戏的UP主详细档案 */ - function getAuthorProfile(): Promise - - /** 视频资产简单模型 */ - type VideoItem = { - bvid: string - title: string - pic: string - play: number - } - /** 获取当前活动/游戏的关联视频列表 */ - function getAuthorVideos(req: { - page: number - pageSize: number - }): Promise<{ list: VideoItem[]; total: number }> - - /** 玩家与该关联UP主的关系状态 */ - type AuthorRelation = { - /** 是否已关注该UP主 */ - isFollowing: boolean - /** 是否是该UP主的特粉/大航海成员 */ - isVipRelation: boolean - } - /** 获取当前登录玩家与目标UP主之间的社交关注链状态 */ - function getAuthorRelation(): Promise - - /** 用户针对某条视频的互动行为状态 */ - type VideoUserActions = { - like: boolean // 是否点赞 - coin: boolean // 是否投币 - fav: boolean // 是否收藏 - share: boolean // 是否分享 - } - /** 获取用户针对指定关联视频的点赞、投币、收藏、分享(三连)等原生行为状态 */ - function getVideoUserActions(req: { - bvid: string - }): Promise - - /** 排行榜单项得分模型 */ - type RankItem = { - rank: number - mid: string - nickname: string - avatar: string - score: number - } - - /** 提交积分排行榜得分参数 */ - type SubmitScoreRequest = { - /** 榜单唯一标识 ID */ - leaderboardId: string - /** 当前局内取得的纯数字分数 */ - score: number - /** 额外附加的自定义上下文字符串(如关卡详情等) */ - extra?: string - } - /** - * 提交当前玩家的得分到 B站 开放平台官方托管的活动排行榜 - */ - function submitScore( - req: SubmitScoreRequest, - ): Promise<{ isNewRecord: boolean }> + /** 关闭当前 WebView 容器。**仅 B站 App 内可用**,Web 端调用直接抛错。 */ + closeBrowser(): Promise /** - * 获取指定官方托管排行榜的分数列表 + * 获取当前登录用户的头像与昵称。 + * + * 首次调用需由用户手势触发,并由平台展示固定的用户数据确认弹窗(Toy 不能自定义弹窗内容); + * 用户拒绝、未登录或在外部手机浏览器中调用时 Promise reject(外部浏览器会先引导打开 B站 App)。 */ - function getRankList(req: { - leaderboardId: string - page: number - pageSize: number - }): Promise<{ list: RankItem[]; total: number }> + getUserProfile(): Promise + + /** 上报 UP 主自定义的用户动作。 */ + reportAction(req: ReportActionReq): Promise /** - * 获取当前登录玩家自己在指定排行榜中的实时名次、分数等信息 + * 获取当前 Toy 作者的公开资料、账号统计、稿件数、充电聚合与粉丝勋章配置。 + * 不能指定作者,固定取当前 Toy 的作者。 */ - function getMyRank(req: { leaderboardId: string }): Promise + getAuthorProfile(): Promise + + /** + * 批量获取当前 Toy 作者的视频公开信息。 + * 非当前作者或对当前用户不可见的视频,对应 item 只返回 `status`,不含 `data`。 + */ + getAuthorVideos(req: AuthorVideosReq): Promise + + /** + * 获取当前访问用户与当前 Toy 作者的关注、老粉、粉丝勋章状态, + * 以及当前是否正在对该作者进行包月充电。 + * + * 只校验登录态,不触发用户数据确认弹窗。外部手机浏览器返回 `status: 'unsupported'` + * 并引导打开 B站 App。 + */ + getAuthorRelation(): Promise + + /** + * 获取当前访问用户对当前作者视频的点赞、投币、收藏状态。 + * + * 只校验登录态,不触发用户数据确认弹窗。外部手机浏览器返回 + * `status: 'unsupported'` 且 `items` 为空数组。 + */ + getVideoUserActions(req: VideoUserActionsReq): Promise + + /** + * 读取云存储。不传或传空数组读取当前用户在该 Toy 下的全部数据; + * 未命中的 key 不出现在结果中。 + * + * 需用户已登录,按「登录用户 + Toy」双维度隔离,不触发用户数据确认。 + * key 不满足 `[a-zA-Z0-9_-]{1,128}` 时本地抛错。 + */ + getCloudStorage(keys?: string[]): Promise> + + /** + * 批量写入云存储(upsert),同 key 覆盖旧值。 + * + * `items` 必须是普通对象(传数组 / 字符串 / null 会本地抛错)。 + * key 只能含字母、数字、下划线、短横线且 ≤128 字节;value 为字符串, + * 字节上限由服务端校验(存对象请自行 `JSON.stringify`)。 + * 单个 Toy 的 key 数量上限由服务端拦截。 + */ + setCloudStorage(items: Record): Promise + + /** 批量删除云存储中指定的 key。key 格式非法时本地抛错。 */ + removeCloudStorage(keys: string[]): Promise + + /** + * 上报分数到排行榜。需用户已登录,首次提交前由平台完成用户数据确认。 + * 按「toy + 榜位 + 周期」隔离,同榜位只保留历史最高分,本次更低不覆盖。 + */ + submitScore(req: SubmitScoreReq): Promise + + /** + * 读取榜单,游客可读。返回前 `limit` 名,固定从高到低; + * 同分时先达成者靠前,名次唯一、不并列。 + */ + getRankList(req?: RankListReq): Promise + + /** 查询我在指定榜单的排名。需用户已登录;是否上榜必须用 `ranked` 判断。 */ + getMyRank(req?: MyRankReq): Promise + + /** + * 申请摄像头业务授权和系统权限,并返回中继的媒体流。 + * 必须在用户手势事件中调用。 + */ + requestCamera(options?: MediaRelayOptions): Promise + + /** + * 申请麦克风业务授权和系统权限,并返回中继的媒体流。 + * 不接受参数,且必须在用户手势事件中调用。 + */ + requestMicrophone(): Promise + + /** 停止媒体中继并释放摄像头或麦克风设备。 */ + stopMedia(stream: MediaStream): Promise + } } +// 全局声明。本文件是 ambient 声明文件(没有顶层 import / export), +// 放进项目后 TypeScript 自动加载,`window.toy` 与 `ToySDK.*` 均可直接使用。 interface Window { - /** 哔哩哔哩 Toy 开放平台(H5游戏容器/小程序)专属官方高级运行时 JavaScript-SDK 挂载点 */ - readonly toy: typeof toy + /** Toy JS SDK 实例,由 toy-sdk.js 加载后挂载到全局。 */ + toy: ToySDK.Toy } + +/** Toy JS SDK 实例(等价于 `window.toy`)。 */ +declare const toy: ToySDK.Toy