chore: add tests
This commit is contained in:
parent
3395a315a6
commit
32509d7812
|
|
@ -9,9 +9,12 @@
|
||||||
"preview": "pnpm --filter sample-game preview"
|
"preview": "pnpm --filter sample-game preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3",
|
||||||
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": ["esbuild"]
|
"onlyBuiltDependencies": [
|
||||||
|
"esbuild"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup",
|
"build": "tsup",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@preact/signals-core": "^1.5.1",
|
"@preact/signals-core": "^1.5.1",
|
||||||
|
|
@ -29,6 +31,7 @@
|
||||||
"phaser": "^3.80.1",
|
"phaser": "^3.80.1",
|
||||||
"preact": "^10.19.3",
|
"preact": "^10.19.3",
|
||||||
"tsup": "^8.0.2",
|
"tsup": "^8.0.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3",
|
||||||
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,286 @@
|
||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import { InputMapper, createInputMapper, type InputMapperOptions } from './InputMapper.js';
|
||||||
|
import Phaser from 'phaser';
|
||||||
|
|
||||||
|
// Mock Phaser
|
||||||
|
function createMockScene() {
|
||||||
|
const inputEvents = new Map<string, Function[]>();
|
||||||
|
|
||||||
|
const mockScene = {
|
||||||
|
input: {
|
||||||
|
on: vi.fn((event: string, callback: Function) => {
|
||||||
|
if (!inputEvents.has(event)) {
|
||||||
|
inputEvents.set(event, []);
|
||||||
|
}
|
||||||
|
inputEvents.get(event)!.push(callback);
|
||||||
|
}),
|
||||||
|
off: vi.fn((event: string, callback: Function) => {
|
||||||
|
const handlers = inputEvents.get(event);
|
||||||
|
if (handlers) {
|
||||||
|
const index = handlers.indexOf(callback);
|
||||||
|
if (index > -1) {
|
||||||
|
handlers.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_emit: (event: string, ...args: any[]) => {
|
||||||
|
const handlers = inputEvents.get(event);
|
||||||
|
if (handlers) {
|
||||||
|
handlers.forEach(handler => handler(...args));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as Phaser.Scene;
|
||||||
|
|
||||||
|
return {
|
||||||
|
mockScene,
|
||||||
|
inputEvents,
|
||||||
|
emitPointerDown: (pointer: Partial<Phaser.Input.Pointer>) => {
|
||||||
|
inputEvents.get('pointerdown')?.forEach(handler => handler(pointer));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMockGameObject() {
|
||||||
|
const events = new Map<string, Function[]>();
|
||||||
|
|
||||||
|
const mockObj = {
|
||||||
|
setInteractive: vi.fn(),
|
||||||
|
on: vi.fn((event: string, handler: Function) => {
|
||||||
|
if (!events.has(event)) {
|
||||||
|
events.set(event, []);
|
||||||
|
}
|
||||||
|
events.get(event)!.push(handler);
|
||||||
|
}),
|
||||||
|
off: vi.fn((event: string, handler: Function) => {
|
||||||
|
const handlers = events.get(event);
|
||||||
|
if (handlers) {
|
||||||
|
const index = handlers.indexOf(handler);
|
||||||
|
if (index > -1) {
|
||||||
|
handlers.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_emit: (event: string, ...args: any[]) => {
|
||||||
|
events.get(event)?.forEach(handler => handler(...args));
|
||||||
|
},
|
||||||
|
} as unknown as Phaser.GameObjects.GameObject;
|
||||||
|
|
||||||
|
return {
|
||||||
|
mockObj,
|
||||||
|
emitPointerDown: () => events.get('pointerdown')?.forEach(handler => handler()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('InputMapper', () => {
|
||||||
|
let options: InputMapperOptions;
|
||||||
|
let onSubmit: ReturnType<typeof vi.fn>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
onSubmit = vi.fn();
|
||||||
|
options = {
|
||||||
|
onSubmit,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('should create instance with options', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
expect(mapper).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createInputMapper', () => {
|
||||||
|
it('should create instance via factory', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const mapper = createInputMapper(mockScene, options);
|
||||||
|
expect(mapper).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mapGridClick', () => {
|
||||||
|
it('should register pointerdown listener', () => {
|
||||||
|
const { mockScene, inputEvents } = createMockScene();
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
|
||||||
|
mapper.mapGridClick(
|
||||||
|
{ x: 50, y: 50 },
|
||||||
|
{ x: 100, y: 100 },
|
||||||
|
{ cols: 3, rows: 3 },
|
||||||
|
vi.fn(() => null),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockScene.input.on).toHaveBeenCalledWith('pointerdown', expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onSubmit with command from onCellClick', () => {
|
||||||
|
const { mockScene, emitPointerDown } = createMockScene();
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
const onCellClick = vi.fn((col, row) => `place ${col} ${row}`);
|
||||||
|
|
||||||
|
mapper.mapGridClick(
|
||||||
|
{ x: 50, y: 50 },
|
||||||
|
{ x: 100, y: 100 },
|
||||||
|
{ cols: 3, rows: 3 },
|
||||||
|
onCellClick,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Simulate pointer at position (120, 130)
|
||||||
|
// Local: (120-100, 130-100) = (20, 30)
|
||||||
|
// Cell: (floor(20/50), floor(30/50)) = (0, 0)
|
||||||
|
emitPointerDown({ x: 120, y: 130 });
|
||||||
|
|
||||||
|
expect(onCellClick).toHaveBeenCalledWith(0, 0);
|
||||||
|
expect(onSubmit).toHaveBeenCalledWith('place 0 0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore clicks outside grid', () => {
|
||||||
|
const { mockScene, emitPointerDown } = createMockScene();
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
const onCellClick = vi.fn(() => 'click');
|
||||||
|
|
||||||
|
mapper.mapGridClick(
|
||||||
|
{ x: 50, y: 50 },
|
||||||
|
{ x: 100, y: 100 },
|
||||||
|
{ cols: 3, rows: 3 },
|
||||||
|
onCellClick,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Click outside grid (negative local coordinates)
|
||||||
|
emitPointerDown({ x: 50, y: 50 });
|
||||||
|
|
||||||
|
expect(onCellClick).not.toHaveBeenCalled();
|
||||||
|
expect(onSubmit).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore clicks outside grid bounds', () => {
|
||||||
|
const { mockScene, emitPointerDown } = createMockScene();
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
const onCellClick = vi.fn(() => 'click');
|
||||||
|
|
||||||
|
mapper.mapGridClick(
|
||||||
|
{ x: 50, y: 50 },
|
||||||
|
{ x: 100, y: 100 },
|
||||||
|
{ cols: 3, rows: 3 },
|
||||||
|
onCellClick,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Click at col 5 (beyond cols 0-2)
|
||||||
|
// Local X: 300-100 = 200, col = floor(200/50) = 4
|
||||||
|
emitPointerDown({ x: 300, y: 120 });
|
||||||
|
|
||||||
|
expect(onCellClick).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call onSubmit when onCellClick returns null', () => {
|
||||||
|
const { mockScene, emitPointerDown } = createMockScene();
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
const onCellClick = vi.fn(() => null);
|
||||||
|
|
||||||
|
mapper.mapGridClick(
|
||||||
|
{ x: 50, y: 50 },
|
||||||
|
{ x: 100, y: 100 },
|
||||||
|
{ cols: 3, rows: 3 },
|
||||||
|
onCellClick,
|
||||||
|
);
|
||||||
|
|
||||||
|
emitPointerDown({ x: 120, y: 130 });
|
||||||
|
|
||||||
|
expect(onCellClick).toHaveBeenCalled();
|
||||||
|
expect(onSubmit).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mapObjectClick', () => {
|
||||||
|
it('should set game objects as interactive', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const { mockObj: obj1 } = createMockGameObject();
|
||||||
|
const { mockObj: obj2 } = createMockGameObject();
|
||||||
|
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
|
||||||
|
mapper.mapObjectClick([obj1, obj2], vi.fn(() => null));
|
||||||
|
|
||||||
|
expect(obj1.setInteractive).toHaveBeenCalledWith({ useHandCursor: true });
|
||||||
|
expect(obj2.setInteractive).toHaveBeenCalledWith({ useHandCursor: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onSubmit when object is clicked', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const { mockObj, emitPointerDown } = createMockGameObject();
|
||||||
|
const onClick = vi.fn((obj) => `clicked ${obj}`);
|
||||||
|
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
|
||||||
|
mapper.mapObjectClick([mockObj], onClick);
|
||||||
|
|
||||||
|
emitPointerDown();
|
||||||
|
|
||||||
|
expect(onClick).toHaveBeenCalledWith(mockObj);
|
||||||
|
expect(onSubmit).toHaveBeenCalledWith(`clicked ${mockObj}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip objects without setInteractive', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const nonInteractiveObj = {} as Phaser.GameObjects.GameObject;
|
||||||
|
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
|
||||||
|
// Should not throw
|
||||||
|
expect(() => {
|
||||||
|
mapper.mapObjectClick([nonInteractiveObj], vi.fn());
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call onSubmit when onClick returns null', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const { mockObj, emitPointerDown } = createMockGameObject();
|
||||||
|
const onClick = vi.fn(() => null);
|
||||||
|
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
|
||||||
|
mapper.mapObjectClick([mockObj], onClick);
|
||||||
|
emitPointerDown();
|
||||||
|
|
||||||
|
expect(onClick).toHaveBeenCalled();
|
||||||
|
expect(onSubmit).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('destroy', () => {
|
||||||
|
it('should remove pointerdown listener from mapGridClick', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
|
||||||
|
mapper.mapGridClick(
|
||||||
|
{ x: 50, y: 50 },
|
||||||
|
{ x: 100, y: 100 },
|
||||||
|
{ cols: 3, rows: 3 },
|
||||||
|
vi.fn(() => null),
|
||||||
|
);
|
||||||
|
|
||||||
|
mapper.destroy();
|
||||||
|
|
||||||
|
expect(mockScene.input.off).toHaveBeenCalledWith('pointerdown', expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove object listeners from mapObjectClick', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const { mockObj } = createMockGameObject();
|
||||||
|
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
mapper.mapObjectClick([mockObj], vi.fn(() => null));
|
||||||
|
mapper.destroy();
|
||||||
|
|
||||||
|
expect(mockObj.off).toHaveBeenCalledWith('pointerdown', expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be safe to call without any mappings', () => {
|
||||||
|
const { mockScene } = createMockScene();
|
||||||
|
const mapper = new InputMapper(mockScene, options);
|
||||||
|
|
||||||
|
expect(() => mapper.destroy()).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
import Phaser from 'phaser';
|
||||||
|
|
||||||
|
export interface InputMapperOptions {
|
||||||
|
onSubmit: (input: string) => string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InputMapper {
|
||||||
|
private scene: Phaser.Scene;
|
||||||
|
private onSubmit: (input: string) => string | null;
|
||||||
|
private pointerDownCallback: ((pointer: Phaser.Input.Pointer) => void) | null = null;
|
||||||
|
/** Track interactive objects registered via mapObjectClick for cleanup */
|
||||||
|
private trackedObjects: Array<{
|
||||||
|
obj: Phaser.GameObjects.GameObject;
|
||||||
|
handler: () => void;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
constructor(scene: Phaser.Scene, options: InputMapperOptions) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.onSubmit = options.onSubmit;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapGridClick(
|
||||||
|
cellSize: { x: number; y: number },
|
||||||
|
offset: { x: number; y: number },
|
||||||
|
gridDimensions: { cols: number; rows: number },
|
||||||
|
onCellClick: (col: number, row: number) => string | null,
|
||||||
|
): void {
|
||||||
|
const pointerDown = (pointer: Phaser.Input.Pointer) => {
|
||||||
|
const localX = pointer.x - offset.x;
|
||||||
|
const localY = pointer.y - offset.y;
|
||||||
|
|
||||||
|
if (localX < 0 || localY < 0) return;
|
||||||
|
|
||||||
|
const col = Math.floor(localX / cellSize.x);
|
||||||
|
const row = Math.floor(localY / cellSize.y);
|
||||||
|
|
||||||
|
if (col < 0 || col >= gridDimensions.cols || row < 0 || row >= gridDimensions.rows) return;
|
||||||
|
|
||||||
|
const cmd = onCellClick(col, row);
|
||||||
|
if (cmd) {
|
||||||
|
this.onSubmit(cmd);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.pointerDownCallback = pointerDown;
|
||||||
|
this.scene.input.on('pointerdown', pointerDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapObjectClick<T>(
|
||||||
|
gameObjects: Phaser.GameObjects.GameObject[],
|
||||||
|
onClick: (obj: T) => string | null,
|
||||||
|
): void {
|
||||||
|
for (const obj of gameObjects) {
|
||||||
|
if ('setInteractive' in obj && typeof (obj as any).setInteractive === 'function') {
|
||||||
|
const interactiveObj = obj as any;
|
||||||
|
interactiveObj.setInteractive({ useHandCursor: true });
|
||||||
|
|
||||||
|
const handler = () => {
|
||||||
|
const cmd = onClick(obj as unknown as T);
|
||||||
|
if (cmd) {
|
||||||
|
this.onSubmit(cmd);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interactiveObj.on('pointerdown', handler);
|
||||||
|
this.trackedObjects.push({ obj, handler });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
// Remove global pointerdown listener from mapGridClick
|
||||||
|
if (this.pointerDownCallback) {
|
||||||
|
this.scene.input.off('pointerdown', this.pointerDownCallback);
|
||||||
|
this.pointerDownCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove per-object pointerdown listeners from mapObjectClick
|
||||||
|
for (const { obj, handler } of this.trackedObjects) {
|
||||||
|
if ('off' in obj && typeof (obj as any).off === 'function') {
|
||||||
|
(obj as any).off('pointerdown', handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.trackedObjects = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createInputMapper(
|
||||||
|
scene: Phaser.Scene,
|
||||||
|
options: InputMapperOptions,
|
||||||
|
): InputMapper {
|
||||||
|
return new InputMapper(scene, options);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,270 @@
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { PromptHandler, type PromptHandlerOptions } from './PromptHandler.js';
|
||||||
|
import type { PromptEvent } from 'boardgame-core';
|
||||||
|
|
||||||
|
// 内联 AsyncQueue 实现用于测试(避免导入 boardgame-core 内部模块)
|
||||||
|
class AsyncQueue<T> {
|
||||||
|
private items: T[] = [];
|
||||||
|
private resolvers: ((value: T) => void)[] = [];
|
||||||
|
|
||||||
|
push(item: T): void {
|
||||||
|
if (this.resolvers.length > 0) {
|
||||||
|
const resolve = this.resolvers.shift()!;
|
||||||
|
resolve(item);
|
||||||
|
} else {
|
||||||
|
this.items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushAll(items: Iterable<T>): void {
|
||||||
|
for (const item of items) {
|
||||||
|
this.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async pop(): Promise<T> {
|
||||||
|
if (this.items.length > 0) {
|
||||||
|
return this.items.shift()!;
|
||||||
|
}
|
||||||
|
return new Promise<T>((resolve) => {
|
||||||
|
this.resolvers.push(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get length(): number {
|
||||||
|
return this.items.length - this.resolvers.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock types
|
||||||
|
interface MockCommandContext {
|
||||||
|
commands: {
|
||||||
|
promptQueue: AsyncQueue<PromptEvent>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMockPromptEvent(
|
||||||
|
schema: any = { name: 'test', params: [] },
|
||||||
|
tryCommitImpl?: (input: string | any) => string | null,
|
||||||
|
): PromptEvent {
|
||||||
|
return {
|
||||||
|
schema,
|
||||||
|
tryCommit: tryCommitImpl ?? vi.fn(() => null),
|
||||||
|
cancel: vi.fn(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMockCommands(): MockCommandContext['commands'] {
|
||||||
|
return {
|
||||||
|
promptQueue: new AsyncQueue<PromptEvent>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('PromptHandler', () => {
|
||||||
|
let options: PromptHandlerOptions;
|
||||||
|
let mockCommands: MockCommandContext['commands'];
|
||||||
|
let onPrompt: ReturnType<typeof vi.fn>;
|
||||||
|
let onCancel: ReturnType<typeof vi.fn>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
onPrompt = vi.fn();
|
||||||
|
onCancel = vi.fn();
|
||||||
|
mockCommands = createMockCommands();
|
||||||
|
options = {
|
||||||
|
commands: mockCommands as any,
|
||||||
|
onPrompt,
|
||||||
|
onCancel,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('should create instance with options', () => {
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createPromptHandler', () => {
|
||||||
|
it('should create instance via factory', async () => {
|
||||||
|
const { createPromptHandler } = await import('./PromptHandler.js');
|
||||||
|
const handler = createPromptHandler(options);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('start', () => {
|
||||||
|
it('should listen for prompts when started', async () => {
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
handler.start();
|
||||||
|
|
||||||
|
const mockEvent = createMockPromptEvent();
|
||||||
|
mockCommands.promptQueue.push(mockEvent);
|
||||||
|
|
||||||
|
// Wait for async operation
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
expect(onPrompt).toHaveBeenCalledWith(mockEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not listen when not started', async () => {
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
// Don't call start()
|
||||||
|
|
||||||
|
const mockEvent = createMockPromptEvent();
|
||||||
|
mockCommands.promptQueue.push(mockEvent);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
expect(onPrompt).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('submit', () => {
|
||||||
|
it('should submit to active prompt successfully', async () => {
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
handler.start();
|
||||||
|
|
||||||
|
const mockEvent = createMockPromptEvent();
|
||||||
|
mockCommands.promptQueue.push(mockEvent);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
const result = handler.submit('test input');
|
||||||
|
|
||||||
|
expect(mockEvent.tryCommit).toHaveBeenCalledWith('test input');
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return error when submit fails', async () => {
|
||||||
|
const mockEvent = createMockPromptEvent(
|
||||||
|
{ name: 'test', params: [] },
|
||||||
|
vi.fn(() => 'Invalid input'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
handler.start();
|
||||||
|
|
||||||
|
mockCommands.promptQueue.push(mockEvent);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
const result = handler.submit('bad input');
|
||||||
|
|
||||||
|
expect(result).toBe('Invalid input');
|
||||||
|
expect(onPrompt).toHaveBeenCalledWith(mockEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store input as pending when no active prompt', () => {
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
// Don't start, so no active prompt
|
||||||
|
|
||||||
|
const result = handler.submit('pending input');
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should auto-submit pending input when new prompt arrives', async () => {
|
||||||
|
const tryCommitFn = vi.fn(() => null);
|
||||||
|
const mockEvent = createMockPromptEvent(
|
||||||
|
{ name: 'test', params: [] },
|
||||||
|
tryCommitFn,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
|
||||||
|
// Submit before starting (pending input)
|
||||||
|
handler.submit('pending input');
|
||||||
|
|
||||||
|
// Start and push event
|
||||||
|
handler.start();
|
||||||
|
mockCommands.promptQueue.push(mockEvent);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
expect(tryCommitFn).toHaveBeenCalledWith('pending input');
|
||||||
|
expect(onPrompt).not.toHaveBeenCalled(); // Auto-submitted, no UI needed
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cancel', () => {
|
||||||
|
it('should cancel active prompt', async () => {
|
||||||
|
const mockEvent = createMockPromptEvent();
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
handler.start();
|
||||||
|
|
||||||
|
mockCommands.promptQueue.push(mockEvent);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
handler.cancel('user cancelled');
|
||||||
|
|
||||||
|
expect(mockEvent.cancel).toHaveBeenCalledWith('user cancelled');
|
||||||
|
expect(onCancel).toHaveBeenCalledWith('user cancelled');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onCancel without active prompt', () => {
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
handler.cancel('no prompt');
|
||||||
|
|
||||||
|
expect(onCancel).toHaveBeenCalledWith('no prompt');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('stop', () => {
|
||||||
|
it('should stop listening for new prompts after stop', async () => {
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
handler.start();
|
||||||
|
|
||||||
|
// Push first event, it should be handled
|
||||||
|
const mockEvent1 = createMockPromptEvent();
|
||||||
|
mockCommands.promptQueue.push(mockEvent1);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
expect(onPrompt).toHaveBeenCalledWith(mockEvent1);
|
||||||
|
onPrompt.mockClear();
|
||||||
|
|
||||||
|
// Now stop
|
||||||
|
handler.stop();
|
||||||
|
|
||||||
|
// Push another event, should not be handled
|
||||||
|
const mockEvent2 = createMockPromptEvent();
|
||||||
|
mockCommands.promptQueue.push(mockEvent2);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
// The second event should not trigger onPrompt
|
||||||
|
// (Note: due to async nature, we check it wasn't called AFTER stop)
|
||||||
|
expect(onPrompt).not.toHaveBeenCalledWith(mockEvent2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel active prompt on stop', async () => {
|
||||||
|
const mockEvent = createMockPromptEvent();
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
handler.start();
|
||||||
|
|
||||||
|
mockCommands.promptQueue.push(mockEvent);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
handler.stop();
|
||||||
|
|
||||||
|
expect(mockEvent.cancel).toHaveBeenCalledWith('Handler stopped');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('destroy', () => {
|
||||||
|
it('should call stop', async () => {
|
||||||
|
const mockEvent = createMockPromptEvent();
|
||||||
|
const handler = new PromptHandler(options);
|
||||||
|
handler.start();
|
||||||
|
|
||||||
|
mockCommands.promptQueue.push(mockEvent);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
handler.destroy();
|
||||||
|
|
||||||
|
expect(mockEvent.cancel).toHaveBeenCalledWith('Handler stopped');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
import type { IGameContext, PromptEvent } from 'boardgame-core';
|
||||||
|
|
||||||
|
export interface PromptHandlerOptions {
|
||||||
|
commands: IGameContext<any>['commands'];
|
||||||
|
onPrompt: (prompt: PromptEvent) => void;
|
||||||
|
onCancel: (reason?: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PromptHandler {
|
||||||
|
private commands: IGameContext<any>['commands'];
|
||||||
|
private onPrompt: (prompt: PromptEvent) => void;
|
||||||
|
private onCancel: (reason?: string) => void;
|
||||||
|
private activePrompt: PromptEvent | null = null;
|
||||||
|
private isListening = false;
|
||||||
|
private pendingInput: string | null = null;
|
||||||
|
|
||||||
|
constructor(options: PromptHandlerOptions) {
|
||||||
|
this.commands = options.commands;
|
||||||
|
this.onPrompt = options.onPrompt;
|
||||||
|
this.onCancel = options.onCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
start(): void {
|
||||||
|
this.isListening = true;
|
||||||
|
this.listenForPrompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
private listenForPrompt(): void {
|
||||||
|
if (!this.isListening) return;
|
||||||
|
|
||||||
|
this.commands.promptQueue.pop()
|
||||||
|
.then((promptEvent) => {
|
||||||
|
this.activePrompt = promptEvent;
|
||||||
|
|
||||||
|
// 如果有等待的输入,自动提交
|
||||||
|
if (this.pendingInput) {
|
||||||
|
const input = this.pendingInput;
|
||||||
|
this.pendingInput = null;
|
||||||
|
const error = this.activePrompt.tryCommit(input);
|
||||||
|
if (error === null) {
|
||||||
|
this.activePrompt = null;
|
||||||
|
this.listenForPrompt();
|
||||||
|
} else {
|
||||||
|
// 提交失败,把 prompt 交给 UI 显示错误
|
||||||
|
this.onPrompt(promptEvent);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onPrompt(promptEvent);
|
||||||
|
})
|
||||||
|
.catch((reason) => {
|
||||||
|
this.activePrompt = null;
|
||||||
|
this.onCancel(reason?.message || 'Cancelled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit an input string to the current prompt.
|
||||||
|
* @returns null on success (input accepted), error string on validation failure
|
||||||
|
*/
|
||||||
|
submit(input: string): string | null {
|
||||||
|
if (!this.activePrompt) {
|
||||||
|
// 没有活跃 prompt,保存为待处理输入
|
||||||
|
this.pendingInput = input;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = this.activePrompt.tryCommit(input);
|
||||||
|
if (error === null) {
|
||||||
|
this.activePrompt = null;
|
||||||
|
this.listenForPrompt();
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(reason?: string): void {
|
||||||
|
if (this.activePrompt) {
|
||||||
|
this.activePrompt.cancel(reason);
|
||||||
|
this.activePrompt = null;
|
||||||
|
}
|
||||||
|
this.onCancel(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(): void {
|
||||||
|
this.isListening = false;
|
||||||
|
if (this.activePrompt) {
|
||||||
|
this.activePrompt.cancel('Handler stopped');
|
||||||
|
this.activePrompt = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPromptHandler(
|
||||||
|
options: PromptHandlerOptions,
|
||||||
|
): PromptHandler {
|
||||||
|
return new PromptHandler(options);
|
||||||
|
}
|
||||||
|
|
@ -1,199 +1,11 @@
|
||||||
import Phaser from 'phaser';
|
export {
|
||||||
import type { IGameContext, PromptEvent } from 'boardgame-core';
|
InputMapper,
|
||||||
|
createInputMapper,
|
||||||
|
type InputMapperOptions,
|
||||||
|
} from './InputMapper.js';
|
||||||
|
|
||||||
// ─── InputMapper ───────────────────────────────────────────────────────────
|
export {
|
||||||
|
PromptHandler,
|
||||||
export interface InputMapperOptions {
|
createPromptHandler,
|
||||||
onSubmit: (input: string) => string | null;
|
type PromptHandlerOptions,
|
||||||
}
|
} from './PromptHandler.js';
|
||||||
|
|
||||||
export class InputMapper {
|
|
||||||
private scene: Phaser.Scene;
|
|
||||||
private onSubmit: (input: string) => string | null;
|
|
||||||
private pointerDownCallback: ((pointer: Phaser.Input.Pointer) => void) | null = null;
|
|
||||||
/** Track interactive objects registered via mapObjectClick for cleanup */
|
|
||||||
private trackedObjects: Array<{
|
|
||||||
obj: Phaser.GameObjects.GameObject;
|
|
||||||
handler: () => void;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, options: InputMapperOptions) {
|
|
||||||
this.scene = scene;
|
|
||||||
this.onSubmit = options.onSubmit;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapGridClick(
|
|
||||||
cellSize: { x: number; y: number },
|
|
||||||
offset: { x: number; y: number },
|
|
||||||
gridDimensions: { cols: number; rows: number },
|
|
||||||
onCellClick: (col: number, row: number) => string | null,
|
|
||||||
): void {
|
|
||||||
const pointerDown = (pointer: Phaser.Input.Pointer) => {
|
|
||||||
const localX = pointer.x - offset.x;
|
|
||||||
const localY = pointer.y - offset.y;
|
|
||||||
|
|
||||||
if (localX < 0 || localY < 0) return;
|
|
||||||
|
|
||||||
const col = Math.floor(localX / cellSize.x);
|
|
||||||
const row = Math.floor(localY / cellSize.y);
|
|
||||||
|
|
||||||
if (col < 0 || col >= gridDimensions.cols || row < 0 || row >= gridDimensions.rows) return;
|
|
||||||
|
|
||||||
const cmd = onCellClick(col, row);
|
|
||||||
if (cmd) {
|
|
||||||
this.onSubmit(cmd);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.pointerDownCallback = pointerDown;
|
|
||||||
this.scene.input.on('pointerdown', pointerDown);
|
|
||||||
}
|
|
||||||
|
|
||||||
mapObjectClick<T>(
|
|
||||||
gameObjects: Phaser.GameObjects.GameObject[],
|
|
||||||
onClick: (obj: T) => string | null,
|
|
||||||
): void {
|
|
||||||
for (const obj of gameObjects) {
|
|
||||||
if ('setInteractive' in obj && typeof (obj as any).setInteractive === 'function') {
|
|
||||||
const interactiveObj = obj as any;
|
|
||||||
interactiveObj.setInteractive({ useHandCursor: true });
|
|
||||||
|
|
||||||
const handler = () => {
|
|
||||||
const cmd = onClick(obj as unknown as T);
|
|
||||||
if (cmd) {
|
|
||||||
this.onSubmit(cmd);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
interactiveObj.on('pointerdown', handler);
|
|
||||||
this.trackedObjects.push({ obj, handler });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
// Remove global pointerdown listener from mapGridClick
|
|
||||||
if (this.pointerDownCallback) {
|
|
||||||
this.scene.input.off('pointerdown', this.pointerDownCallback);
|
|
||||||
this.pointerDownCallback = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove per-object pointerdown listeners from mapObjectClick
|
|
||||||
for (const { obj, handler } of this.trackedObjects) {
|
|
||||||
if ('off' in obj && typeof (obj as any).off === 'function') {
|
|
||||||
(obj as any).off('pointerdown', handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.trackedObjects = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createInputMapper(
|
|
||||||
scene: Phaser.Scene,
|
|
||||||
options: InputMapperOptions,
|
|
||||||
): InputMapper {
|
|
||||||
return new InputMapper(scene, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── PromptHandler ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export interface PromptHandlerOptions {
|
|
||||||
commands: IGameContext<any>['commands'];
|
|
||||||
onPrompt: (prompt: PromptEvent) => void;
|
|
||||||
onCancel: (reason?: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PromptHandler {
|
|
||||||
private commands: IGameContext<any>['commands'];
|
|
||||||
private onPrompt: (prompt: PromptEvent) => void;
|
|
||||||
private onCancel: (reason?: string) => void;
|
|
||||||
private activePrompt: PromptEvent | null = null;
|
|
||||||
private isListening = false;
|
|
||||||
private pendingInput: string | null = null;
|
|
||||||
|
|
||||||
constructor(options: PromptHandlerOptions) {
|
|
||||||
this.commands = options.commands;
|
|
||||||
this.onPrompt = options.onPrompt;
|
|
||||||
this.onCancel = options.onCancel;
|
|
||||||
}
|
|
||||||
|
|
||||||
start(): void {
|
|
||||||
this.isListening = true;
|
|
||||||
this.listenForPrompt();
|
|
||||||
}
|
|
||||||
|
|
||||||
private listenForPrompt(): void {
|
|
||||||
if (!this.isListening) return;
|
|
||||||
|
|
||||||
this.commands.promptQueue.pop()
|
|
||||||
.then((promptEvent) => {
|
|
||||||
this.activePrompt = promptEvent;
|
|
||||||
|
|
||||||
// 如果有等待的输入,自动提交
|
|
||||||
if (this.pendingInput) {
|
|
||||||
const input = this.pendingInput;
|
|
||||||
this.pendingInput = null;
|
|
||||||
const error = this.activePrompt.tryCommit(input);
|
|
||||||
if (error === null) {
|
|
||||||
this.activePrompt = null;
|
|
||||||
this.listenForPrompt();
|
|
||||||
} else {
|
|
||||||
// 提交失败,把 prompt 交给 UI 显示错误
|
|
||||||
this.onPrompt(promptEvent);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onPrompt(promptEvent);
|
|
||||||
})
|
|
||||||
.catch((reason) => {
|
|
||||||
this.activePrompt = null;
|
|
||||||
this.onCancel(reason?.message || 'Cancelled');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit an input string to the current prompt.
|
|
||||||
* @returns null on success (input accepted), error string on validation failure
|
|
||||||
*/
|
|
||||||
submit(input: string): string | null {
|
|
||||||
if (!this.activePrompt) {
|
|
||||||
// 没有活跃 prompt,保存为待处理输入
|
|
||||||
this.pendingInput = input;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const error = this.activePrompt.tryCommit(input);
|
|
||||||
if (error === null) {
|
|
||||||
this.activePrompt = null;
|
|
||||||
this.listenForPrompt();
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel(reason?: string): void {
|
|
||||||
if (this.activePrompt) {
|
|
||||||
this.activePrompt.cancel(reason);
|
|
||||||
this.activePrompt = null;
|
|
||||||
}
|
|
||||||
this.onCancel(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
stop(): void {
|
|
||||||
this.isListening = false;
|
|
||||||
if (this.activePrompt) {
|
|
||||||
this.activePrompt.cancel('Handler stopped');
|
|
||||||
this.activePrompt = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createPromptHandler(
|
|
||||||
options: PromptHandlerOptions,
|
|
||||||
): PromptHandler {
|
|
||||||
return new PromptHandler(options);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'node',
|
||||||
|
include: ['src/**/*.test.ts'],
|
||||||
|
},
|
||||||
|
});
|
||||||
296
pnpm-lock.yaml
296
pnpm-lock.yaml
|
|
@ -11,6 +11,9 @@ importers:
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.3.3
|
specifier: ^5.3.3
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
|
vitest:
|
||||||
|
specifier: ^3.2.4
|
||||||
|
version: 3.2.4(lightningcss@1.32.0)
|
||||||
|
|
||||||
packages/framework:
|
packages/framework:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
|
@ -35,6 +38,9 @@ importers:
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.3.3
|
specifier: ^5.3.3
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
|
vitest:
|
||||||
|
specifier: ^3.2.4
|
||||||
|
version: 3.2.4(lightningcss@1.32.0)
|
||||||
|
|
||||||
packages/sample-game:
|
packages/sample-game:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -749,9 +755,44 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^5.2.0 || ^6 || ^7 || ^8
|
vite: ^5.2.0 || ^6 || ^7 || ^8
|
||||||
|
|
||||||
|
'@types/chai@5.2.3':
|
||||||
|
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2':
|
||||||
|
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||||
|
|
||||||
'@types/estree@1.0.8':
|
'@types/estree@1.0.8':
|
||||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||||
|
|
||||||
|
'@vitest/expect@3.2.4':
|
||||||
|
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
|
||||||
|
|
||||||
|
'@vitest/mocker@3.2.4':
|
||||||
|
resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
|
||||||
|
peerDependencies:
|
||||||
|
msw: ^2.4.9
|
||||||
|
vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
msw:
|
||||||
|
optional: true
|
||||||
|
vite:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@vitest/pretty-format@3.2.4':
|
||||||
|
resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
|
||||||
|
|
||||||
|
'@vitest/runner@3.2.4':
|
||||||
|
resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
|
||||||
|
|
||||||
|
'@vitest/snapshot@3.2.4':
|
||||||
|
resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
|
||||||
|
|
||||||
|
'@vitest/spy@3.2.4':
|
||||||
|
resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
|
||||||
|
|
||||||
|
'@vitest/utils@3.2.4':
|
||||||
|
resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
|
||||||
|
|
||||||
acorn@8.16.0:
|
acorn@8.16.0:
|
||||||
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
@ -760,6 +801,10 @@ packages:
|
||||||
any-promise@1.3.0:
|
any-promise@1.3.0:
|
||||||
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
||||||
|
|
||||||
|
assertion-error@2.0.1:
|
||||||
|
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
babel-plugin-transform-hook-names@1.0.2:
|
babel-plugin-transform-hook-names@1.0.2:
|
||||||
resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==}
|
resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -791,6 +836,14 @@ packages:
|
||||||
caniuse-lite@1.0.30001784:
|
caniuse-lite@1.0.30001784:
|
||||||
resolution: {integrity: sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==}
|
resolution: {integrity: sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==}
|
||||||
|
|
||||||
|
chai@5.3.3:
|
||||||
|
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
check-error@2.1.3:
|
||||||
|
resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||||
engines: {node: '>= 14.16.0'}
|
engines: {node: '>= 14.16.0'}
|
||||||
|
|
@ -825,6 +878,10 @@ packages:
|
||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
deep-eql@5.0.2:
|
||||||
|
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
detect-libc@2.1.2:
|
detect-libc@2.1.2:
|
||||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -853,6 +910,9 @@ packages:
|
||||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||||
engines: {node: '>=0.12'}
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
|
es-module-lexer@1.7.0:
|
||||||
|
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||||
|
|
||||||
esbuild@0.21.5:
|
esbuild@0.21.5:
|
||||||
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
|
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
@ -870,9 +930,16 @@ packages:
|
||||||
estree-walker@2.0.2:
|
estree-walker@2.0.2:
|
||||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||||
|
|
||||||
eventemitter3@5.0.4:
|
eventemitter3@5.0.4:
|
||||||
resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
|
resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
|
||||||
|
|
||||||
|
expect-type@1.3.0:
|
||||||
|
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
fdir@6.5.0:
|
fdir@6.5.0:
|
||||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
@ -912,6 +979,9 @@ packages:
|
||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
|
js-tokens@9.0.1:
|
||||||
|
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
||||||
|
|
||||||
jsesc@3.1.0:
|
jsesc@3.1.0:
|
||||||
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -1010,6 +1080,9 @@ packages:
|
||||||
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
|
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
|
||||||
|
loupe@3.2.1:
|
||||||
|
resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
|
||||||
|
|
||||||
lru-cache@5.1.1:
|
lru-cache@5.1.1:
|
||||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||||
|
|
||||||
|
|
@ -1050,6 +1123,10 @@ packages:
|
||||||
pathe@2.0.3:
|
pathe@2.0.3:
|
||||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||||
|
|
||||||
|
pathval@2.0.1:
|
||||||
|
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
|
||||||
|
engines: {node: '>= 14.16'}
|
||||||
|
|
||||||
phaser@3.90.0:
|
phaser@3.90.0:
|
||||||
resolution: {integrity: sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==}
|
resolution: {integrity: sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==}
|
||||||
|
|
||||||
|
|
@ -1113,6 +1190,9 @@ packages:
|
||||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
siginfo@2.0.0:
|
||||||
|
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||||
|
|
||||||
simple-code-frame@1.3.0:
|
simple-code-frame@1.3.0:
|
||||||
resolution: {integrity: sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w==}
|
resolution: {integrity: sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w==}
|
||||||
|
|
||||||
|
|
@ -1128,6 +1208,15 @@ packages:
|
||||||
resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==}
|
resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
|
stackback@0.0.2:
|
||||||
|
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||||
|
|
||||||
|
std-env@3.10.0:
|
||||||
|
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
||||||
|
|
||||||
|
strip-literal@3.1.0:
|
||||||
|
resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
|
||||||
|
|
||||||
sucrase@3.35.1:
|
sucrase@3.35.1:
|
||||||
resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
|
resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
|
||||||
engines: {node: '>=16 || 14 >=14.17'}
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
|
|
@ -1147,6 +1236,9 @@ packages:
|
||||||
thenify@3.3.1:
|
thenify@3.3.1:
|
||||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||||
|
|
||||||
|
tinybench@2.9.0:
|
||||||
|
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||||
|
|
||||||
tinyexec@0.3.2:
|
tinyexec@0.3.2:
|
||||||
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
||||||
|
|
||||||
|
|
@ -1154,6 +1246,18 @@ packages:
|
||||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
tinypool@1.1.1:
|
||||||
|
resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
|
||||||
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
|
|
||||||
|
tinyrainbow@2.0.0:
|
||||||
|
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
tinyspy@4.0.4:
|
||||||
|
resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
tree-kill@1.2.2:
|
tree-kill@1.2.2:
|
||||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
@ -1194,6 +1298,11 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
browserslist: '>= 4.21.0'
|
browserslist: '>= 4.21.0'
|
||||||
|
|
||||||
|
vite-node@3.2.4:
|
||||||
|
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
vite-prerender-plugin@0.5.13:
|
vite-prerender-plugin@0.5.13:
|
||||||
resolution: {integrity: sha512-IKSpYkzDBsKAxa05naRbj7GvNVMSdww/Z/E89oO3xndz+gWnOBOKOAbEXv7qDhktY/j3vHgJmoV1pPzqU2tx9g==}
|
resolution: {integrity: sha512-IKSpYkzDBsKAxa05naRbj7GvNVMSdww/Z/E89oO3xndz+gWnOBOKOAbEXv7qDhktY/j3vHgJmoV1pPzqU2tx9g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -1230,6 +1339,39 @@ packages:
|
||||||
terser:
|
terser:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
vitest@3.2.4:
|
||||||
|
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@edge-runtime/vm': '*'
|
||||||
|
'@types/debug': ^4.1.12
|
||||||
|
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||||
|
'@vitest/browser': 3.2.4
|
||||||
|
'@vitest/ui': 3.2.4
|
||||||
|
happy-dom: '*'
|
||||||
|
jsdom: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@edge-runtime/vm':
|
||||||
|
optional: true
|
||||||
|
'@types/debug':
|
||||||
|
optional: true
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
'@vitest/browser':
|
||||||
|
optional: true
|
||||||
|
'@vitest/ui':
|
||||||
|
optional: true
|
||||||
|
happy-dom:
|
||||||
|
optional: true
|
||||||
|
jsdom:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
yallist@3.1.1:
|
yallist@3.1.1:
|
||||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||||
|
|
||||||
|
|
@ -1730,12 +1872,63 @@ snapshots:
|
||||||
tailwindcss: 4.2.2
|
tailwindcss: 4.2.2
|
||||||
vite: 5.4.21(lightningcss@1.32.0)
|
vite: 5.4.21(lightningcss@1.32.0)
|
||||||
|
|
||||||
|
'@types/chai@5.2.3':
|
||||||
|
dependencies:
|
||||||
|
'@types/deep-eql': 4.0.2
|
||||||
|
assertion-error: 2.0.1
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2': {}
|
||||||
|
|
||||||
'@types/estree@1.0.8': {}
|
'@types/estree@1.0.8': {}
|
||||||
|
|
||||||
|
'@vitest/expect@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 5.2.3
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
chai: 5.3.3
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
'@vitest/mocker@3.2.4(vite@5.4.21(lightningcss@1.32.0))':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
estree-walker: 3.0.3
|
||||||
|
magic-string: 0.30.21
|
||||||
|
optionalDependencies:
|
||||||
|
vite: 5.4.21(lightningcss@1.32.0)
|
||||||
|
|
||||||
|
'@vitest/pretty-format@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
'@vitest/runner@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
pathe: 2.0.3
|
||||||
|
strip-literal: 3.1.0
|
||||||
|
|
||||||
|
'@vitest/snapshot@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
magic-string: 0.30.21
|
||||||
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
'@vitest/spy@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
tinyspy: 4.0.4
|
||||||
|
|
||||||
|
'@vitest/utils@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
loupe: 3.2.1
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
acorn@8.16.0: {}
|
acorn@8.16.0: {}
|
||||||
|
|
||||||
any-promise@1.3.0: {}
|
any-promise@1.3.0: {}
|
||||||
|
|
||||||
|
assertion-error@2.0.1: {}
|
||||||
|
|
||||||
babel-plugin-transform-hook-names@1.0.2(@babel/core@7.29.0):
|
babel-plugin-transform-hook-names@1.0.2(@babel/core@7.29.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.29.0
|
'@babel/core': 7.29.0
|
||||||
|
|
@ -1761,6 +1954,16 @@ snapshots:
|
||||||
|
|
||||||
caniuse-lite@1.0.30001784: {}
|
caniuse-lite@1.0.30001784: {}
|
||||||
|
|
||||||
|
chai@5.3.3:
|
||||||
|
dependencies:
|
||||||
|
assertion-error: 2.0.1
|
||||||
|
check-error: 2.1.3
|
||||||
|
deep-eql: 5.0.2
|
||||||
|
loupe: 3.2.1
|
||||||
|
pathval: 2.0.1
|
||||||
|
|
||||||
|
check-error@2.1.3: {}
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
readdirp: 4.1.2
|
readdirp: 4.1.2
|
||||||
|
|
@ -1787,6 +1990,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
deep-eql@5.0.2: {}
|
||||||
|
|
||||||
detect-libc@2.1.2: {}
|
detect-libc@2.1.2: {}
|
||||||
|
|
||||||
dom-serializer@2.0.0:
|
dom-serializer@2.0.0:
|
||||||
|
|
@ -1816,6 +2021,8 @@ snapshots:
|
||||||
|
|
||||||
entities@4.5.0: {}
|
entities@4.5.0: {}
|
||||||
|
|
||||||
|
es-module-lexer@1.7.0: {}
|
||||||
|
|
||||||
esbuild@0.21.5:
|
esbuild@0.21.5:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@esbuild/aix-ppc64': 0.21.5
|
'@esbuild/aix-ppc64': 0.21.5
|
||||||
|
|
@ -1875,8 +2082,14 @@ snapshots:
|
||||||
|
|
||||||
estree-walker@2.0.2: {}
|
estree-walker@2.0.2: {}
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
|
||||||
eventemitter3@5.0.4: {}
|
eventemitter3@5.0.4: {}
|
||||||
|
|
||||||
|
expect-type@1.3.0: {}
|
||||||
|
|
||||||
fdir@6.5.0(picomatch@4.0.4):
|
fdir@6.5.0(picomatch@4.0.4):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
picomatch: 4.0.4
|
picomatch: 4.0.4
|
||||||
|
|
@ -1902,6 +2115,8 @@ snapshots:
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
|
js-tokens@9.0.1: {}
|
||||||
|
|
||||||
jsesc@3.1.0: {}
|
jsesc@3.1.0: {}
|
||||||
|
|
||||||
json5@2.2.3: {}
|
json5@2.2.3: {}
|
||||||
|
|
@ -1963,6 +2178,8 @@ snapshots:
|
||||||
|
|
||||||
load-tsconfig@0.2.5: {}
|
load-tsconfig@0.2.5: {}
|
||||||
|
|
||||||
|
loupe@3.2.1: {}
|
||||||
|
|
||||||
lru-cache@5.1.1:
|
lru-cache@5.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
yallist: 3.1.1
|
yallist: 3.1.1
|
||||||
|
|
@ -2005,6 +2222,8 @@ snapshots:
|
||||||
|
|
||||||
pathe@2.0.3: {}
|
pathe@2.0.3: {}
|
||||||
|
|
||||||
|
pathval@2.0.1: {}
|
||||||
|
|
||||||
phaser@3.90.0:
|
phaser@3.90.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
eventemitter3: 5.0.4
|
eventemitter3: 5.0.4
|
||||||
|
|
@ -2075,6 +2294,8 @@ snapshots:
|
||||||
|
|
||||||
semver@6.3.1: {}
|
semver@6.3.1: {}
|
||||||
|
|
||||||
|
siginfo@2.0.0: {}
|
||||||
|
|
||||||
simple-code-frame@1.3.0:
|
simple-code-frame@1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
kolorist: 1.8.0
|
kolorist: 1.8.0
|
||||||
|
|
@ -2085,6 +2306,14 @@ snapshots:
|
||||||
|
|
||||||
stack-trace@1.0.0-pre2: {}
|
stack-trace@1.0.0-pre2: {}
|
||||||
|
|
||||||
|
stackback@0.0.2: {}
|
||||||
|
|
||||||
|
std-env@3.10.0: {}
|
||||||
|
|
||||||
|
strip-literal@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
js-tokens: 9.0.1
|
||||||
|
|
||||||
sucrase@3.35.1:
|
sucrase@3.35.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/gen-mapping': 0.3.13
|
'@jridgewell/gen-mapping': 0.3.13
|
||||||
|
|
@ -2107,6 +2336,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
any-promise: 1.3.0
|
any-promise: 1.3.0
|
||||||
|
|
||||||
|
tinybench@2.9.0: {}
|
||||||
|
|
||||||
tinyexec@0.3.2: {}
|
tinyexec@0.3.2: {}
|
||||||
|
|
||||||
tinyglobby@0.2.15:
|
tinyglobby@0.2.15:
|
||||||
|
|
@ -2114,6 +2345,12 @@ snapshots:
|
||||||
fdir: 6.5.0(picomatch@4.0.4)
|
fdir: 6.5.0(picomatch@4.0.4)
|
||||||
picomatch: 4.0.4
|
picomatch: 4.0.4
|
||||||
|
|
||||||
|
tinypool@1.1.1: {}
|
||||||
|
|
||||||
|
tinyrainbow@2.0.0: {}
|
||||||
|
|
||||||
|
tinyspy@4.0.4: {}
|
||||||
|
|
||||||
tree-kill@1.2.2: {}
|
tree-kill@1.2.2: {}
|
||||||
|
|
||||||
ts-interface-checker@0.1.13: {}
|
ts-interface-checker@0.1.13: {}
|
||||||
|
|
@ -2156,6 +2393,24 @@ snapshots:
|
||||||
escalade: 3.2.0
|
escalade: 3.2.0
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
|
|
||||||
|
vite-node@3.2.4(lightningcss@1.32.0):
|
||||||
|
dependencies:
|
||||||
|
cac: 6.7.14
|
||||||
|
debug: 4.4.3
|
||||||
|
es-module-lexer: 1.7.0
|
||||||
|
pathe: 2.0.3
|
||||||
|
vite: 5.4.21(lightningcss@1.32.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
|
||||||
vite-prerender-plugin@0.5.13(vite@5.4.21(lightningcss@1.32.0)):
|
vite-prerender-plugin@0.5.13(vite@5.4.21(lightningcss@1.32.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
kolorist: 1.8.0
|
kolorist: 1.8.0
|
||||||
|
|
@ -2175,6 +2430,47 @@ snapshots:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
lightningcss: 1.32.0
|
lightningcss: 1.32.0
|
||||||
|
|
||||||
|
vitest@3.2.4(lightningcss@1.32.0):
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 5.2.3
|
||||||
|
'@vitest/expect': 3.2.4
|
||||||
|
'@vitest/mocker': 3.2.4(vite@5.4.21(lightningcss@1.32.0))
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
'@vitest/runner': 3.2.4
|
||||||
|
'@vitest/snapshot': 3.2.4
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
chai: 5.3.3
|
||||||
|
debug: 4.4.3
|
||||||
|
expect-type: 1.3.0
|
||||||
|
magic-string: 0.30.21
|
||||||
|
pathe: 2.0.3
|
||||||
|
picomatch: 4.0.4
|
||||||
|
std-env: 3.10.0
|
||||||
|
tinybench: 2.9.0
|
||||||
|
tinyexec: 0.3.2
|
||||||
|
tinyglobby: 0.2.15
|
||||||
|
tinypool: 1.1.1
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
vite: 5.4.21(lightningcss@1.32.0)
|
||||||
|
vite-node: 3.2.4(lightningcss@1.32.0)
|
||||||
|
why-is-node-running: 2.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- msw
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
siginfo: 2.0.0
|
||||||
|
stackback: 0.0.2
|
||||||
|
|
||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|
||||||
zimmerframe@1.1.4: {}
|
zimmerframe@1.1.4: {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue