/** * 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' import type { GridWeapon, GridCharacter, GridSummon } from '$lib/types/api/party' import { DEFAULT_ADAPTER_CONFIG } from './config' import { validateGridWeapon, validateGridCharacter, validateGridSummon } from '$lib/utils/gridValidation' // GridWeapon, GridCharacter, and GridSummon types are imported from types/api/party // Re-export for test files and consumers export type { GridWeapon, GridCharacter, GridSummon } /** * Parameters for creating grid items */ export interface CreateGridWeaponParams { partyId: string weaponId: string position: number mainhand?: boolean | undefined uncapLevel?: number | undefined transcendenceStep?: number | undefined } export interface CreateGridCharacterParams { partyId: string characterId: string position: number uncapLevel?: number | undefined transcendenceStep?: number | undefined } export interface CreateGridSummonParams { partyId: string summonId: string position: number main?: boolean | undefined friend?: boolean | undefined quickSummon?: boolean | undefined uncapLevel?: number | undefined transcendenceStep?: number | undefined } /** * Parameters for updating uncap levels */ export interface UpdateUncapParams { id?: string | undefined partyId: string position?: number | undefined uncapLevel: number transcendenceStep?: number | undefined } /** * 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 { // Weapon operations /** * Creates a new grid weapon instance */ async createWeapon(params: CreateGridWeaponParams, headers?: Record): Promise { const response = await this.request<{ gridWeapon: GridWeapon }>('/grid_weapons', { method: 'POST', body: { weapon: params }, headers }) // Validate and normalize response const validated = validateGridWeapon(response.gridWeapon) if (!validated) { throw new Error('API returned incomplete GridWeapon data') } return validated } /** * Updates a grid weapon instance */ async updateWeapon(id: string, params: Partial, headers?: Record): Promise { const response = await this.request<{ gridWeapon: GridWeapon }>(`/grid_weapons/${id}`, { method: 'PUT', body: { weapon: params }, headers }) return response.gridWeapon } /** * Deletes a grid weapon instance */ async deleteWeapon(params: { id?: string; partyId: string; position?: number }, headers?: Record): Promise { // If we have an ID, use it in the URL (standard Rails REST) if (params.id) { return this.request(`/grid_weapons/${params.id}`, { method: 'DELETE', headers }) } // Otherwise, send params in body for position-based delete return this.request('/grid_weapons/delete_by_position', { method: 'DELETE', body: params, headers }) } /** * Updates weapon uncap level */ async updateWeaponUncap(params: UpdateUncapParams, headers?: Record): Promise { const response = await this.request<{ gridWeapon: GridWeapon }>('/grid_weapons/update_uncap', { method: 'POST', body: { weapon: { id: params.id, partyId: params.partyId, uncapLevel: params.uncapLevel, transcendenceStep: params.transcendenceStep } }, headers }) return response.gridWeapon } /** * Resolves weapon conflicts */ async resolveWeaponConflict(params: ResolveConflictParams, headers?: Record): Promise { const response = await this.request<{ gridWeapon: GridWeapon }>('/grid_weapons/resolve', { method: 'POST', body: { resolve: params }, headers }) return response.gridWeapon } /** * Updates weapon position */ async updateWeaponPosition(params: UpdatePositionParams, headers?: Record): Promise { const { id, position, container, partyId } = params const response = await this.request<{ gridWeapon: GridWeapon }>(`/parties/${partyId}/grid_weapons/${id}/position`, { method: 'PUT', body: { position, container }, headers }) return response.gridWeapon } /** * Swaps two weapon positions */ async swapWeapons(params: SwapPositionsParams, headers?: Record): Promise<{ source: GridWeapon target: GridWeapon }> { const { partyId, sourceId, targetId } = params return this.request(`/parties/${partyId}/grid_weapons/swap`, { method: 'POST', body: { source_id: sourceId, target_id: targetId }, headers }) } // Character operations /** * Creates a new grid character instance */ async createCharacter(params: CreateGridCharacterParams, headers?: Record): Promise { const response = await this.request<{ gridCharacter: GridCharacter }>('/grid_characters', { method: 'POST', body: { character: params }, headers }) // Validate and normalize response const validated = validateGridCharacter(response.gridCharacter) if (!validated) { throw new Error('API returned incomplete GridCharacter data') } return validated } /** * Updates a grid character instance */ async updateCharacter(id: string, params: Partial, headers?: Record): Promise { const response = await this.request<{ gridCharacter: GridCharacter }>(`/grid_characters/${id}`, { method: 'PUT', body: { character: params }, headers }) return response.gridCharacter } /** * Deletes a grid character instance */ async deleteCharacter(params: { id?: string; partyId: string; position?: number }, headers?: Record): Promise { // If we have an ID, use it in the URL (standard Rails REST) if (params.id) { return this.request(`/grid_characters/${params.id}`, { method: 'DELETE', headers }) } // Otherwise, send params in body for position-based delete return this.request('/grid_characters/delete_by_position', { method: 'DELETE', body: params, headers }) } /** * Updates character uncap level */ async updateCharacterUncap(params: UpdateUncapParams, headers?: Record): Promise { const response = await this.request<{ gridCharacter: GridCharacter }>('/grid_characters/update_uncap', { method: 'POST', body: { character: { id: params.id, partyId: params.partyId, uncapLevel: params.uncapLevel, transcendenceStep: params.transcendenceStep } }, headers }) return response.gridCharacter } /** * Resolves character conflicts */ async resolveCharacterConflict(params: ResolveConflictParams, headers?: Record): Promise { const response = await this.request<{ gridCharacter: GridCharacter }>('/grid_characters/resolve', { method: 'POST', body: { resolve: params }, headers }) return response.gridCharacter } /** * Updates character position */ async updateCharacterPosition(params: UpdatePositionParams, headers?: Record): Promise { const { id, position, container, partyId } = params const response = await this.request<{ gridCharacter: GridCharacter }>(`/parties/${partyId}/grid_characters/${id}/position`, { method: 'PUT', body: { position, container }, headers }) return response.gridCharacter } /** * Swaps two character positions */ async swapCharacters(params: SwapPositionsParams, headers?: Record): Promise<{ source: GridCharacter target: GridCharacter }> { const { partyId, sourceId, targetId } = params return this.request(`/parties/${partyId}/grid_characters/swap`, { method: 'POST', body: { source_id: sourceId, target_id: targetId }, headers }) } // Summon operations /** * Creates a new grid summon instance */ async createSummon(params: CreateGridSummonParams, headers?: Record): Promise { const response = await this.request<{ gridSummon: GridSummon }>('/grid_summons', { method: 'POST', body: { summon: params }, headers }) // Validate and normalize response const validated = validateGridSummon(response.gridSummon) if (!validated) { throw new Error('API returned incomplete GridSummon data') } return validated } /** * Updates a grid summon instance */ async updateSummon(id: string, params: Partial, headers?: Record): Promise { const response = await this.request<{ gridSummon: GridSummon }>(`/grid_summons/${id}`, { method: 'PUT', body: { summon: params }, headers }) return response.gridSummon } /** * Deletes a grid summon instance */ async deleteSummon(params: { id?: string; partyId: string; position?: number }, headers?: Record): Promise { // If we have an ID, use it in the URL (standard Rails REST) if (params.id) { return this.request(`/grid_summons/${params.id}`, { method: 'DELETE', headers }) } // Otherwise, send params in body for position-based delete return this.request('/grid_summons/delete_by_position', { method: 'DELETE', body: params, headers }) } /** * Updates summon uncap level */ async updateSummonUncap(params: UpdateUncapParams, headers?: Record): Promise { const response = await this.request<{ gridSummon: GridSummon }>('/grid_summons/update_uncap', { method: 'POST', body: { summon: { id: params.id, partyId: params.partyId, uncapLevel: params.uncapLevel, transcendenceStep: params.transcendenceStep } }, headers }) return response.gridSummon } /** * 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, headers?: Record): Promise { const { id, position, container, partyId } = params const response = await this.request<{ gridSummon: GridSummon }>(`/parties/${partyId}/grid_summons/${id}/position`, { method: 'PUT', body: { position, container }, headers }) return response.gridSummon } /** * Swaps two summon positions */ async swapSummons(params: SwapPositionsParams, headers?: Record): Promise<{ source: GridSummon target: GridSummon }> { const { partyId, sourceId, targetId } = params return this.request(`/parties/${partyId}/grid_summons/swap`, { method: 'POST', body: { source_id: sourceId, target_id: targetId }, headers }) } /** * 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(DEFAULT_ADAPTER_CONFIG)