- Add listGlobalItemsForAdmin: paginated with batched tag/ownerCount queries
- Add updateGlobalItemById: partial update in transaction, syncs tags
- Add deleteGlobalItem: nullifies FK refs, removes tag associations before delete
- Create src/server/routes/admin-items.ts with GET/GET:id/PUT/DELETE endpoints
- Mount adminItemRoutes at /items in admin.ts (protected by requireAuth+requireAdmin)
- Extend global-item.service.test.ts with 13 new tests (all passing)
Closes ADMN-02, ADMN-03, ADMN-04 (server side)
PGlite's WASM worker kept an open async handle, causing Bun to detect
a resource leak and exit with code 100 despite all tests passing.
Adds a preload script that closes the cached client via afterAll.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- storage.service.ts: use dynamic import() inside each function so the
current @aws-sdk mock is always picked up regardless of module load order
- images.test.ts + image.service.test.ts: replace module-level storage.service
mock with @aws-sdk/client-s3 mock to avoid contaminating storage.service.test.ts
- routes/auth.test.ts: remove unnecessary oauth.service mock (no test uses
verifyAccessToken) which was contaminating oauth.service.test.ts
- middleware/auth.test.ts: complete oauth.service mock shape with all exports
All 464 tests now pass in a single bun test run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CatalogSearchOverlay: replace handleAddStub with real openAddToCollection/openAddToThread routing based on catalogSearchMode
- ConfirmDialog + __root.tsx: swap t() for Trans component on deleteItemMessage, deleteCandidateMessage, pickWinnerMessage — fixes <bold> rendering as literal text
- Biome format pass: fix 23 lint/format errors across scripts, services, tests
- Planning: mark all UAT and verification gaps resolved for phases 07, 11, 16, 20, 21, 22, 24, 32, 34; close debug sessions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auto-fixed formatting issues and removed unused imports introduced
by background execution agents across currency, i18n, and sharing code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create all 6 German namespace JSON files (common, collection, threads, setups, onboarding, settings)
- Register German locale in i18n configuration with supportedLngs
- Add key parity test ensuring en/de have identical key structures
- All 19 locale parity tests pass, all 15 formatter tests pass
Phase 34, Plan 05
- Create useLanguage() hook following useCurrency/useWeightUnit pattern
- Update formatPrice() to use Intl.NumberFormat for locale-aware currency display
- Update formatWeight() to use Intl.NumberFormat for locale-aware number formatting
- Update formatDualPrice() to pass locale through
- Update useFormatters() to pass locale to all formatters
- Add formatter tests for en/de locales (15 tests passing)
Phase 34, Plan 03
Create share.service.ts with token generation (128-bit base64url),
CRUD operations, validation, and visibility transition side effects.
Add share endpoints under /api/setups/:id/shares, shared access at
/api/shared/:token, and /s/:token short URL redirect.
Plan: 32-02 (Setup Sharing System - Share Link Backend)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace isPublic boolean with visibility enum (private/link/public) across
the full stack. Add shares table to schema for future share link support.
Update all services, routes, schemas, hooks, components, and tests.
Plan: 32-01 (Setup Sharing System - Schema Migration)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Creates /api/account routes with password change (verifies current first),
email update, has-password check, and account deletion with public setup
anonymization. Adds Zod validation schemas and registers routes in index.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements LogtoManagementClient with token caching, password verification,
password update, email update, user deletion, and has-password check.
All methods proxy to Logto Management API via M2M credentials.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove unused functions and imports from route tests, fix array index key
warnings in skeleton components, apply biome formatting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create src/server/routes/discovery.ts with GET /setups, /items, /categories handlers
- Register discoveryRoutes in src/server/index.ts with browseTier rate limiting
- Add auth skip for /api/discovery/* GET requests in auth middleware
- Create tests/routes/discovery.test.ts with 10 tests covering all endpoints and pagination
- New catalog.ts with catalogToolDefinitions and registerCatalogTools
- upsert_catalog_item: single item upsert with full attribution fields (SEED-03)
- bulk_upsert_catalog: batch upsert up to 100 items with created/updated counts
- Registered in createMcpServer after image tools
- 6 new MCP catalog tool tests passing
- Add upsertGlobalItemSchema and bulkUpsertGlobalItemsSchema to schemas.ts
- Add UpsertGlobalItemInput and BulkUpsertGlobalItemsInput types to types.ts
- Implement upsertGlobalItem with onConflictDoUpdate and tag sync
- Implement bulkUpsertGlobalItems processing array in single transaction
- Fix migration 0003 to only add new columns + unique constraint
- All 21 tests pass including 8 new upsert operation tests
- Add createRateLimit(maxAttempts, windowMs) factory function
- Rewrite rateLimit export to delegate to factory (backward compatible)
- Keep shared store, getClientIp, cleanup, and _resetForTesting unchanged
- Add createRateLimit factory test suite with 5 test cases
- All existing rateLimit middleware tests still pass
- Add userId param to createAuthorizationCode calls
- Remove userId param from exchangeCode and refreshAccessToken calls
(now derived from stored records)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- OAuth: add userId to oauth_codes schema and migration, derive userId
from stored auth code/token record instead of passing separately
- Auth middleware tests: destructure {db, userId} from createTestDb,
pass userId to createApiKey, fix error message assertion
- MCP tests: add missing await on getCollectionSummary and
createSecondTestUser calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Share PGlite instance per test file (TRUNCATE RESTART IDENTITY instead
of creating new instance per test) — tests run in ~9s vs minutes
- Add missing 'brand' column to items table migration
- Fix corrupt 0002 snapshot (merge conflict artifacts)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix unused function parameters (prefix with _)
- Fix unused imports in test files
- Fix import ordering in test files
- Auto-fix formatting issues across 22 files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add fabMenuOpen, openFabMenu, closeFabMenu to UIStore
- Add catalogSearchOpen, catalogSearchMode, openCatalogSearch, closeCatalogSearch
- openCatalogSearch also closes FAB menu (natural flow)
- Create useTags hook with 5-min staleTime cache
- Add optional tags parameter to useGlobalItems for tag filtering
- searchGlobalItems now accepts tagNames param with AND intersection logic
- Owner count uses items.globalItemId instead of removed itemGlobalLinks
- Removed linkItemToGlobal and unlinkItemFromGlobal functions
- Route handlers now async with tags query param support
- Rewrote tests to async PGlite pattern, added tag filtering tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- getThreadWithCandidates LEFT JOINs globalItems with COALESCE for name, weight, price, image
- createCandidate accepts and stores globalItemId
- resolveThread branches: reference item (globalItemId set) vs standalone (full data copy)
- Removed link/unlink endpoints from items route (replaced by direct globalItemId FK)
- 6 new tests for catalog-linked candidates and branched resolution
- getAllItems and getItemById LEFT JOIN globalItems with COALESCE for name, weight, price, image
- createItem accepts globalItemId and purchasePriceCents, stores brand+model as fallback name
- duplicateItem preserves globalItemId and purchasePriceCents
- updateItem type includes globalItemId and purchasePriceCents
- 10 new tests for reference item creation and merged data retrieval
- Add auth redirect in root layout for unauthenticated users
- Proxy OIDC routes (/login, /callback, /logout) through Vite dev server
- Strip Secure flag from OIDC cookies in dev mode (HTTP localhost)
- Disable retry on auth query to prevent stale cookie loops
- Fix SQLite .get()/.all()/.run() calls in category and global-item
services for PostgreSQL compatibility
- Add userId scoping to category service functions
- Add OIDC error logging in auth middleware
- Apply linter auto-formatting across affected files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- GET /api/users/:id/profile: public profile with public setups (no auth)
- PUT /api/auth/profile: update own profile (requires auth)
- GET /api/setups/:id/public: public setup view with items (no auth)
- Auth middleware skips public profile and public setup GET endpoints
- Register profileRoutes at /api/users in index.ts
- Add getOrCreateUncategorized to category service (Rule 3 fix)
- 10 route tests covering auth, public access, and 404 cases
- GET /api/global-items with optional q search parameter
- GET /api/global-items/:id with ownerCount
- POST /api/items/:id/link to link user item to global item
- DELETE /api/items/:id/link to unlink
- Route registered in index.ts
- 10 route tests covering all endpoints
- 10 test cases covering search, owner count, link/unlink, seed idempotency
- Added globalItems/itemGlobalLinks tables to SQLite schema
- Added Zod schemas and types for global items
- Created 18-item bikepacking gear seed data JSON
- Profile CRUD tests: updateProfile, getPublicProfile, getPublicSetupWithItems
- Setup service isPublic tests: create with isPublic, toggle, list includes isPublic
- Replace Bun.write/mkdir with uploadImage() from storage.service
- Remove uploadsDir parameter from fetchImageFromUrl
- Update tests to mock storage service instead of checking filesystem
Route and MCP test files were calling createTestDb() without await,
causing db to be a Promise object instead of a Drizzle instance.
Also rewrote auth route tests for OIDC-based auth (merge picked old version).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>