- Add two-phase sync: bulk catalog fetch then targeted batch enrichment
(50 SKUs/call, ~10 extra calls) to retrieve self_configure_price field
which is absent from paginated responses
- Write self_configure_price as flexoptix-selfconfigure marketplace rows
in price_observations (343/346 products enriched on first run)
- Add list_price support (flexoptix-msrp marketplace) for future use
- Use API-provided type field (Transceiver/Cables/Accessories) in
categoryFor() instead of inferring from title alone
- Add productType field to CatalogProduct interface
Two bugs blocked all Flexoptix stock_observations:
1. db.ts: compatible_brands null param needs ::text[] cast; price_currency needs
::bpchar cast in stock_observations INSERT
2. flexoptix-api-sync.ts: UPDATE transceivers used $1 in WHERE IS NOT NULL clause
without explicit type cast — PostgreSQL cant infer type of untyped null params
in WHERE context. Fixed with $1::text / $2::text casts.
Result: 346 stock observations written on first run after fix.
Root cause of the persistent sync:flexoptix-catalog HTTP 401: line 397 used
'?? null' which only coerces null/undefined. With FLEXOPTIX_API_TOKEN='' (empty
string set in .env), token stayed '' and line 485's 'token ?? getBearerToken()'
returned '' instead of performing the username/password login — sending an empty
'Bearer ' header that the products endpoint rejected with 401.
Fix: '|| null' coerces empty string to null so the bearer-login fallback fires.
Verified: sync now completes (username/password -> customer token -> products 200,
3 products/price/stock writes on limit=50). Credentials were correct all along.