From 52d731c335ba64e2216d530724c9bbe5580075c9 Mon Sep 17 00:00:00 2001 From: caxtonacollins Date: Thu, 26 Mar 2026 17:51:51 +0100 Subject: [PATCH 1/5] feat: add creator filter parser for list requests (#28) - parse verified and search filter inputs from raw query objects - reject unsupported filter keys with a descriptive error - keep parser pure and reusable across list handlers --- src/modules/creators/creators.filter.ts | 68 +++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/modules/creators/creators.filter.ts diff --git a/src/modules/creators/creators.filter.ts b/src/modules/creators/creators.filter.ts new file mode 100644 index 0000000..4065be5 --- /dev/null +++ b/src/modules/creators/creators.filter.ts @@ -0,0 +1,68 @@ +// src/modules/creators/creators.filter.ts +// Parser for creator list filter input. Reusable across list handlers. + +/** + * Supported filter keys for creator list requests. + */ +export const SUPPORTED_CREATOR_FILTERS = ['verified', 'search'] as const; + +export type CreatorFilterKey = (typeof SUPPORTED_CREATOR_FILTERS)[number]; + +/** + * Parsed creator filter input ready for use in list queries. + */ +export interface CreatorFilterInput { + verified?: boolean; + search?: string; +} + +/** + * Parse and validate raw query filter input for creator list requests. + * + * - Accepts only supported filter keys; rejects unknown ones with an error + * - Coerces `verified` string to boolean + * - Trims `search` string + * - Repeated calls with the same input return the same result + * + * @param raw - Raw query object (e.g. req.query) + * @returns Parsed filter input + * @throws Error if unsupported filter keys are present + * + * @example + * parseCreatorFilters({ verified: 'true', search: 'jazz' }) + * // => { verified: true, search: 'jazz' } + * + * @example + * parseCreatorFilters({ unknown: 'value' }) + * // throws Error: Unsupported filter key(s): unknown + */ +export function parseCreatorFilters( + raw: Record +): CreatorFilterInput { + const filterKeys = Object.keys(raw).filter(key => + SUPPORTED_CREATOR_FILTERS.includes(key as CreatorFilterKey) + ); + + const unsupported = Object.keys(raw).filter( + key => !SUPPORTED_CREATOR_FILTERS.includes(key as CreatorFilterKey) + ); + + if (unsupported.length > 0) { + throw new Error(`Unsupported filter key(s): ${unsupported.join(', ')}`); + } + + const result: CreatorFilterInput = {}; + + if (filterKeys.includes('verified') && raw.verified !== undefined) { + result.verified = raw.verified === 'true' || raw.verified === true; + } + + if (filterKeys.includes('search') && typeof raw.search === 'string') { + const trimmed = raw.search.trim(); + if (trimmed.length > 0) { + result.search = trimmed; + } + } + + return result; +} From 297b2065107d8a55d61333dbf7ce5e3d740b8f97 Mon Sep 17 00:00:00 2001 From: caxtonacollins Date: Thu, 26 Mar 2026 17:52:05 +0100 Subject: [PATCH 2/5] feat: add creator handle normalization helper (#29) - normalize casing, spacing, and special characters in creator handles - idempotent: repeated calls with same input return same result - small and reusable across creator flows --- src/modules/creators/creators.handle.ts | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/modules/creators/creators.handle.ts diff --git a/src/modules/creators/creators.handle.ts b/src/modules/creators/creators.handle.ts new file mode 100644 index 0000000..b901c95 --- /dev/null +++ b/src/modules/creators/creators.handle.ts @@ -0,0 +1,31 @@ +// src/modules/creators/creators.handle.ts +// Helper for normalizing creator handles before use in API responses or lookups. + +/** + * Normalize a creator handle for consistent storage and lookup. + * + * - Trims leading/trailing whitespace + * - Converts to lowercase + * - Collapses internal whitespace to a single underscore + * - Strips characters that are not alphanumeric, hyphens, or underscores + * + * Repeated calls with the same input always return the same result (idempotent). + * + * @param handle - Raw handle input + * @returns Normalized handle string + * + * @example + * normalizeCreatorHandle(' JazzKing ') // 'jazzking' + * normalizeCreatorHandle('Jazz King') // 'jazz_king' + * normalizeCreatorHandle('Jazz--King') // 'jazz--king' → 'jazz-king' + * normalizeCreatorHandle('Jazz__King') // 'jazz__king' → 'jazz_king' + */ +export function normalizeCreatorHandle(handle: string): string { + return handle + .trim() + .toLowerCase() + .replace(/\s+/g, '_') // internal spaces → underscore + .replace(/[^a-z0-9_-]/g, '') // strip unsupported characters + .replace(/-{2,}/g, '-') // collapse consecutive hyphens + .replace(/_{2,}/g, '_'); // collapse consecutive underscores +} From e8853025686500844142b38e42b2acdc37aab48e Mon Sep 17 00:00:00 2001 From: caxtonacollins Date: Thu, 26 Mar 2026 17:52:19 +0100 Subject: [PATCH 3/5] feat: add public creator stats response helper (#30) - define PublicCreatorStats shape for public endpoints - serializePublicCreatorStats formats CreatorMetrics for API responses - reusable across creator endpoints with one consistent output shape --- src/modules/creators/creators.stats.ts | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/modules/creators/creators.stats.ts diff --git a/src/modules/creators/creators.stats.ts b/src/modules/creators/creators.stats.ts new file mode 100644 index 0000000..d8f132a --- /dev/null +++ b/src/modules/creators/creators.stats.ts @@ -0,0 +1,43 @@ +// src/modules/creators/creators.stats.ts +// Helper for formatting public creator stats in API responses. + +import { CreatorMetrics } from '../../types/profile.types'; + +/** + * Public-facing creator stats shape. + * + * Keeps the response focused on what clients need for public endpoints. + * Avoids leaking internal or sensitive metric fields. + */ +export interface PublicCreatorStats { + holderCount: number; + totalSupply: number; + totalVolume: number; + lastActivityAt?: Date; +} + +/** + * Format a CreatorMetrics object into a public stats response. + * + * Centralizes the public stats shape so all creator endpoints + * return a consistent structure. + * + * @param metrics - Internal creator metrics + * @returns Public stats object safe for API responses + * + * @example + * serializePublicCreatorStats({ holderCount: 10, totalSupply: 100, totalVolume: 500 }) + * // => { holderCount: 10, totalSupply: 100, totalVolume: 500 } + */ +export function serializePublicCreatorStats( + metrics: CreatorMetrics +): PublicCreatorStats { + return { + holderCount: metrics.holderCount, + totalSupply: metrics.totalSupply, + totalVolume: metrics.totalVolume, + ...(metrics.lastActivityAt !== undefined + ? { lastActivityAt: metrics.lastActivityAt } + : {}), + }; +} From a1baf402f2196613549fbdf8e2f041df3568547b Mon Sep 17 00:00:00 2001 From: caxtonacollins Date: Thu, 26 Mar 2026 17:52:36 +0100 Subject: [PATCH 4/5] feat: add default pagination constants module (#31) - centralize DEFAULT_PAGE_SIZE, DEFAULT_PAGE, DEFAULT_OFFSET, MAX_PAGE_SIZE, MIN_PAGE_SIZE - reusable for all current and future list endpoints - single source of truth for pagination defaults --- src/constants/pagination.constants.ts | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/constants/pagination.constants.ts diff --git a/src/constants/pagination.constants.ts b/src/constants/pagination.constants.ts new file mode 100644 index 0000000..af5bb2e --- /dev/null +++ b/src/constants/pagination.constants.ts @@ -0,0 +1,28 @@ +// src/constants/pagination.constants.ts +// Shared pagination defaults for list endpoints. +// Import from here to keep defaults consistent and easy to change centrally. + +/** + * Default number of items returned per page. + */ +export const DEFAULT_PAGE_SIZE = 20; + +/** + * Default page index (1-based). + */ +export const DEFAULT_PAGE = 1; + +/** + * Default offset for offset-based pagination. + */ +export const DEFAULT_OFFSET = 0; + +/** + * Maximum allowed page size to prevent oversized queries. + */ +export const MAX_PAGE_SIZE = 100; + +/** + * Minimum allowed page size. + */ +export const MIN_PAGE_SIZE = 1; From 3a193b18041d75a059b853e2dee5900be66062d2 Mon Sep 17 00:00:00 2001 From: caxtonacollins Date: Thu, 26 Mar 2026 17:56:42 +0100 Subject: [PATCH 5/5] fix: remove duplicate creatorProfile field on User model --- prisma/schema/user.prisma | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/prisma/schema/user.prisma b/prisma/schema/user.prisma index a165eb4..78bc0e5 100644 --- a/prisma/schema/user.prisma +++ b/prisma/schema/user.prisma @@ -13,7 +13,6 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - creatorProfile CreatorProfile? stellarWallet StellarWallet? - creatorProfile CreatorProfile? + creatorProfile CreatorProfile? }