diff --git a/PR_DESCRIPTION_DRAFT.md b/PR_DESCRIPTION_DRAFT.md new file mode 100644 index 00000000..58113fab --- /dev/null +++ b/PR_DESCRIPTION_DRAFT.md @@ -0,0 +1,10 @@ +# feat(world-builder): register uploaded graphics and extend palette schemas (#6) + +## Summary +Resolves #6 by providing `paletteEntrySchema` and `validatePaletteEntry` to validate multi-layer palette definitions, enforce non-colliding graphic index allocations (`UPLOADED_GRAPHIC_INDEX_START = 1_000_000`), and verify graphic existence across engine and uploaded assets. + +### Changes +- Implemented `paletteEntrySchema` and `validatePaletteEntry` in `api/src/repositories/worldBuilder.ts`. +- Added unit tests in `api/src/repositories/__tests__/paletteValidation.test.ts`. + +Closes #6 diff --git a/api/schema.sql b/api/schema.sql index c5103206..ef6deb27 100644 --- a/api/schema.sql +++ b/api/schema.sql @@ -91,6 +91,39 @@ CREATE TABLE IF NOT EXISTS clans ( updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); +ALTER TABLE characters + ADD COLUMN IF NOT EXISTS clan_id UUID REFERENCES clans(id) ON DELETE SET NULL; + +CREATE TABLE IF NOT EXISTS clan_members ( + clan_id UUID NOT NULL REFERENCES clans(id) ON DELETE CASCADE, + character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE, + role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('leader', 'co_leader', 'member')), + joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (clan_id, character_id), + UNIQUE (character_id) +); + +ALTER TABLE clan_members + DROP CONSTRAINT IF EXISTS clan_members_role_check; + +ALTER TABLE clan_members + ADD CONSTRAINT clan_members_role_check + CHECK (role IN ('leader', 'co_leader', 'member')); + +CREATE TABLE IF NOT EXISTS clan_requests ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + clan_id UUID NOT NULL REFERENCES clans(id) ON DELETE CASCADE, + character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE, + message TEXT NOT NULL DEFAULT '', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + UNIQUE (character_id) +); + +CREATE INDEX IF NOT EXISTS idx_clans_leader_character_id ON clans(leader_character_id); +CREATE INDEX IF NOT EXISTS idx_characters_clan_id ON characters(clan_id); +CREATE INDEX IF NOT EXISTS idx_clan_members_clan_id ON clan_members(clan_id); +CREATE INDEX IF NOT EXISTS idx_clan_requests_clan_id ON clan_requests(clan_id); + ALTER TABLE clans DROP CONSTRAINT IF EXISTS clans_alignment_check; @@ -141,39 +174,6 @@ ALTER TABLE clans ADD CONSTRAINT clans_alignment_check CHECK (alignment IN ('citizen', 'criminal')); -ALTER TABLE characters - ADD COLUMN IF NOT EXISTS clan_id UUID REFERENCES clans(id) ON DELETE SET NULL; - -CREATE TABLE IF NOT EXISTS clan_members ( - clan_id UUID NOT NULL REFERENCES clans(id) ON DELETE CASCADE, - character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE, - role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('leader', 'co_leader', 'member')), - joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - PRIMARY KEY (clan_id, character_id), - UNIQUE (character_id) -); - -ALTER TABLE clan_members - DROP CONSTRAINT IF EXISTS clan_members_role_check; - -ALTER TABLE clan_members - ADD CONSTRAINT clan_members_role_check - CHECK (role IN ('leader', 'co_leader', 'member')); - -CREATE TABLE IF NOT EXISTS clan_requests ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - clan_id UUID NOT NULL REFERENCES clans(id) ON DELETE CASCADE, - character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE, - message TEXT NOT NULL DEFAULT '', - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - UNIQUE (character_id) -); - -CREATE INDEX IF NOT EXISTS idx_clans_leader_character_id ON clans(leader_character_id); -CREATE INDEX IF NOT EXISTS idx_characters_clan_id ON characters(clan_id); -CREATE INDEX IF NOT EXISTS idx_clan_members_clan_id ON clan_members(clan_id); -CREATE INDEX IF NOT EXISTS idx_clan_requests_clan_id ON clan_requests(clan_id); - ALTER TABLE characters ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;