From 49fc0fb069344521fa7c4eba5f4abfbc41f86f80 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 20 Sep 2025 00:11:51 -0700 Subject: [PATCH] feat: Implement GridAdapter and EntityAdapter - Add GridAdapter for managing user grid item instances - Support CRUD, position management, and uncap operations - Add EntityAdapter for read-only canonical game data - Separate user instances from game reference data - Export all adapters from index --- src/lib/api/adapters/entity.adapter.ts | 193 ++++++++++++ src/lib/api/adapters/grid.adapter.ts | 404 +++++++++++++++++++++++++ src/lib/api/adapters/index.ts | 22 +- 3 files changed, 617 insertions(+), 2 deletions(-) create mode 100644 src/lib/api/adapters/entity.adapter.ts create mode 100644 src/lib/api/adapters/grid.adapter.ts diff --git a/src/lib/api/adapters/entity.adapter.ts b/src/lib/api/adapters/entity.adapter.ts new file mode 100644 index 00000000..801c024b --- /dev/null +++ b/src/lib/api/adapters/entity.adapter.ts @@ -0,0 +1,193 @@ +/** + * Entity Adapter + * + * Handles read-only access to canonical game data (weapons, characters, summons). + * This data represents the official game information that users reference + * but cannot modify. + * + * @module adapters/entity + */ + +import { BaseAdapter } from './base.adapter' +import type { AdapterOptions } from './types' + +/** + * Canonical weapon data from the game + */ +export interface Weapon { + id: string + granblueId: string + name: { + en?: string + ja?: string + } + rarity: number + element: number + proficiency: number + series?: number + weaponType?: number + minHp?: number + maxHp?: number + minAttack?: number + maxAttack?: number + flbHp?: number + flbAttack?: number + ulbHp?: number + ulbAttack?: number + transcendenceHp?: number + transcendenceAttack?: number + awakenings?: Array<{ + id: string + name: Record + level: number + }> +} + +/** + * Canonical character data from the game + */ +export interface Character { + id: string + granblueId: string + name: { + en?: string + ja?: string + } + rarity: number + element: number + proficiency1?: number + proficiency2?: number + series?: number + minHp?: number + maxHp?: number + minAttack?: number + maxAttack?: number + flbHp?: number + flbAttack?: number + ulbHp?: number + ulbAttack?: number + transcendenceHp?: number + transcendenceAttack?: number + special?: boolean + seasonalId?: string + awakenings?: Array<{ + id: string + name: Record + level: number + }> +} + +/** + * Canonical summon data from the game + */ +export interface Summon { + id: string + granblueId: string + name: { + en?: string + ja?: string + } + rarity: number + element: number + series?: number + minHp?: number + maxHp?: number + minAttack?: number + maxAttack?: number + flbHp?: number + flbAttack?: number + ulbHp?: number + ulbAttack?: number + transcendenceHp?: number + transcendenceAttack?: number + subaura?: boolean + cooldown?: number +} + +/** + * Entity adapter for accessing canonical game data + */ +export class EntityAdapter extends BaseAdapter { + constructor(options?: AdapterOptions) { + super({ + ...options, + baseURL: options?.baseURL || '/api/v1', + // Cache entity data for longer since it rarely changes + cacheTime: options?.cacheTime || 300000 // 5 minutes default + }) + } + + /** + * Gets canonical weapon data by ID + */ + async getWeapon(id: string): Promise { + return this.request(`/weapons/${id}`, { + method: 'GET', + cacheTTL: 600000 // Cache for 10 minutes + }) + } + + /** + * Gets canonical character data by ID + */ + async getCharacter(id: string): Promise { + return this.request(`/characters/${id}`, { + method: 'GET', + cacheTTL: 600000 // Cache for 10 minutes + }) + } + + /** + * Gets canonical summon data by ID + */ + async getSummon(id: string): Promise { + return this.request(`/summons/${id}`, { + method: 'GET', + cacheTTL: 600000 // Cache for 10 minutes + }) + } + + /** + * Batch fetch multiple weapons + */ + async getWeapons(ids: string[]): Promise { + // Fetch in parallel with individual caching + const promises = ids.map(id => this.getWeapon(id)) + return Promise.all(promises) + } + + /** + * Batch fetch multiple characters + */ + async getCharacters(ids: string[]): Promise { + const promises = ids.map(id => this.getCharacter(id)) + return Promise.all(promises) + } + + /** + * Batch fetch multiple summons + */ + async getSummons(ids: string[]): Promise { + const promises = ids.map(id => this.getSummon(id)) + return Promise.all(promises) + } + + /** + * Clears entity cache + */ + clearEntityCache(type?: 'weapons' | 'characters' | 'summons') { + if (type) { + this.clearCache(`/${type}`) + } else { + // Clear all entity caches + this.clearCache('/weapons') + this.clearCache('/characters') + this.clearCache('/summons') + } + } +} + +/** + * Default entity adapter instance + */ +export const entityAdapter = new EntityAdapter() \ No newline at end of file diff --git a/src/lib/api/adapters/grid.adapter.ts b/src/lib/api/adapters/grid.adapter.ts new file mode 100644 index 00000000..0b9cc969 --- /dev/null +++ b/src/lib/api/adapters/grid.adapter.ts @@ -0,0 +1,404 @@ +/** + * Grid Adapter + * + * Handles all grid item operations including CRUD, positioning, and uncap management. + * This adapter manages user instances of weapons, characters, and summons within parties. + * + * @module adapters/grid + */ + +import { BaseAdapter } from './base.adapter' +import type { AdapterOptions } from './types' + +/** + * Common grid item structure + */ +interface BaseGridItem { + id: string + partyId: string + position: number + uncapLevel?: number + transcendenceStage?: number +} + +/** + * Grid weapon specific fields + */ +export interface GridWeapon extends BaseGridItem { + weaponId: string + mainhand?: boolean + element?: number + weaponKeys?: Array<{ + id: string + slot: number + }> + axModifier1?: string + axModifier2?: string + axStrength1?: number + axStrength2?: number + awakeningId?: string + awakeningLevel?: number +} + +/** + * Grid character specific fields + */ +export interface GridCharacter extends BaseGridItem { + characterId: string + perpetualModifiers?: Record + awakeningId?: string + awakeningLevel?: number + rings?: Array<{ + modifier: string + strength: number + }> + earring?: { + modifier: string + strength: number + } +} + +/** + * Grid summon specific fields + */ +export interface GridSummon extends BaseGridItem { + summonId: string + main?: boolean + friend?: boolean + quickSummon?: boolean +} + +/** + * Parameters for creating grid items + */ +export interface CreateGridWeaponParams { + partyId: string + weaponId: string + position: number + mainhand?: boolean + uncapLevel?: number + transcendenceStage?: number +} + +export interface CreateGridCharacterParams { + partyId: string + characterId: string + position: number + uncapLevel?: number + transcendenceStage?: number +} + +export interface CreateGridSummonParams { + partyId: string + summonId: string + position: number + main?: boolean + friend?: boolean + quickSummon?: boolean + uncapLevel?: number + transcendenceStage?: number +} + +/** + * Parameters for updating uncap levels + */ +export interface UpdateUncapParams { + id?: string + partyId: string + position?: number + uncapLevel: number + transcendenceStep?: number +} + +/** + * Parameters for updating positions + */ +export interface UpdatePositionParams { + partyId: string + id: string + position: number + container?: string +} + +/** + * Parameters for swapping positions + */ +export interface SwapPositionsParams { + partyId: string + sourceId: string + targetId: string +} + +/** + * Conflict resolution parameters + */ +export interface ResolveConflictParams { + partyId: string + incomingId: string + position: number + conflictingIds: string[] +} + +/** + * Grid adapter for managing user's grid item instances + */ +export class GridAdapter extends BaseAdapter { + constructor(options?: AdapterOptions) { + super({ + ...options, + baseURL: options?.baseURL || '/api/v1' + }) + } + + // Weapon operations + + /** + * Creates a new grid weapon instance + */ + async createWeapon(params: CreateGridWeaponParams): Promise { + return this.request('/grid_weapons', { + method: 'POST', + body: params + }) + } + + /** + * Updates a grid weapon instance + */ + async updateWeapon(id: string, params: Partial): Promise { + return this.request(`/grid_weapons/${id}`, { + method: 'PUT', + body: params + }) + } + + /** + * Deletes a grid weapon instance + */ + async deleteWeapon(params: { id?: string; partyId: string; position?: number }): Promise { + return this.request('/grid_weapons', { + method: 'DELETE', + body: params + }) + } + + /** + * Updates weapon uncap level + */ + async updateWeaponUncap(params: UpdateUncapParams): Promise { + return this.request('/grid_weapons/update_uncap', { + method: 'POST', + body: params + }) + } + + /** + * Resolves weapon conflicts + */ + async resolveWeaponConflict(params: ResolveConflictParams): Promise { + return this.request('/grid_weapons/resolve', { + method: 'POST', + body: params + }) + } + + /** + * Updates weapon position + */ + async updateWeaponPosition(params: UpdatePositionParams): Promise { + const { partyId, id, ...positionData } = params + return this.request(`/parties/${partyId}/grid_weapons/${id}/position`, { + method: 'PUT', + body: positionData + }) + } + + /** + * Swaps two weapon positions + */ + async swapWeapons(params: SwapPositionsParams): Promise<{ + source: GridWeapon + target: GridWeapon + }> { + const { partyId, ...swapData } = params + return this.request(`/parties/${partyId}/grid_weapons/swap`, { + method: 'POST', + body: swapData + }) + } + + // Character operations + + /** + * Creates a new grid character instance + */ + async createCharacter(params: CreateGridCharacterParams): Promise { + return this.request('/grid_characters', { + method: 'POST', + body: params + }) + } + + /** + * Updates a grid character instance + */ + async updateCharacter(id: string, params: Partial): Promise { + return this.request(`/grid_characters/${id}`, { + method: 'PUT', + body: params + }) + } + + /** + * Deletes a grid character instance + */ + async deleteCharacter(params: { id?: string; partyId: string; position?: number }): Promise { + return this.request('/grid_characters', { + method: 'DELETE', + body: params + }) + } + + /** + * Updates character uncap level + */ + async updateCharacterUncap(params: UpdateUncapParams): Promise { + return this.request('/grid_characters/update_uncap', { + method: 'POST', + body: params + }) + } + + /** + * Resolves character conflicts + */ + async resolveCharacterConflict(params: ResolveConflictParams): Promise { + return this.request('/grid_characters/resolve', { + method: 'POST', + body: params + }) + } + + /** + * Updates character position + */ + async updateCharacterPosition(params: UpdatePositionParams): Promise { + const { partyId, id, ...positionData } = params + return this.request(`/parties/${partyId}/grid_characters/${id}/position`, { + method: 'PUT', + body: positionData + }) + } + + /** + * Swaps two character positions + */ + async swapCharacters(params: SwapPositionsParams): Promise<{ + source: GridCharacter + target: GridCharacter + }> { + const { partyId, ...swapData } = params + return this.request(`/parties/${partyId}/grid_characters/swap`, { + method: 'POST', + body: swapData + }) + } + + // Summon operations + + /** + * Creates a new grid summon instance + */ + async createSummon(params: CreateGridSummonParams): Promise { + return this.request('/grid_summons', { + method: 'POST', + body: params + }) + } + + /** + * Updates a grid summon instance + */ + async updateSummon(id: string, params: Partial): Promise { + return this.request(`/grid_summons/${id}`, { + method: 'PUT', + body: params + }) + } + + /** + * Deletes a grid summon instance + */ + async deleteSummon(params: { id?: string; partyId: string; position?: number }): Promise { + return this.request('/grid_summons', { + method: 'DELETE', + body: params + }) + } + + /** + * Updates summon uncap level + */ + async updateSummonUncap(params: UpdateUncapParams): Promise { + return this.request('/grid_summons/update_uncap', { + method: 'POST', + body: params + }) + } + + /** + * Updates summon quick summon setting + */ + async updateQuickSummon(params: { + id?: string + partyId: string + position?: number + quickSummon: boolean + }): Promise { + return this.request('/grid_summons/update_quick_summon', { + method: 'POST', + body: params + }) + } + + /** + * Updates summon position + */ + async updateSummonPosition(params: UpdatePositionParams): Promise { + const { partyId, id, ...positionData } = params + return this.request(`/parties/${partyId}/grid_summons/${id}/position`, { + method: 'PUT', + body: positionData + }) + } + + /** + * Swaps two summon positions + */ + async swapSummons(params: SwapPositionsParams): Promise<{ + source: GridSummon + target: GridSummon + }> { + const { partyId, ...swapData } = params + return this.request(`/parties/${partyId}/grid_summons/swap`, { + method: 'POST', + body: swapData + }) + } + + /** + * Clears grid-specific cache + */ + clearGridCache(partyId?: string) { + if (partyId) { + this.clearCache(`/parties/${partyId}/grid`) + } else { + this.clearCache('/grid') + } + } +} + +/** + * Default grid adapter instance + */ +export const gridAdapter = new GridAdapter() \ No newline at end of file diff --git a/src/lib/api/adapters/index.ts b/src/lib/api/adapters/index.ts index b44fc590..203c230d 100644 --- a/src/lib/api/adapters/index.ts +++ b/src/lib/api/adapters/index.ts @@ -30,8 +30,26 @@ export type { GridUpdateResponse } from './party.adapter' -// export { GridAdapter } from './grid.adapter' -// export { EntityAdapter } from './entity.adapter' +export { GridAdapter, gridAdapter } from './grid.adapter' +export type { + GridWeapon, + GridCharacter, + GridSummon, + CreateGridWeaponParams, + CreateGridCharacterParams, + CreateGridSummonParams, + UpdateUncapParams, + UpdatePositionParams, + SwapPositionsParams, + ResolveConflictParams +} from './grid.adapter' + +export { EntityAdapter, entityAdapter } from './entity.adapter' +export type { + Weapon, + Character, + Summon +} from './entity.adapter' // Reactive resources using Svelte 5 runes export * from './resources' \ No newline at end of file