feat: TIP Phase 0+1 — monorepo, DB schema, API, scraper engine

Phase 0 - Foundation:
- Restructure into npm workspace monorepo (packages/core, api, scraper)
- PostgreSQL 17 + TimescaleDB schema (15 tables incl. hypertables)
- Docker Compose for local dev (PostgreSQL on 5433 + Qdrant)
- Express 5 API on port 3200 with 6 routes
- Seed script to migrate 159 transceivers + 42 standards from npm package
- Erik server setup script + PM2 ecosystem config

Phase 1 - Scraper Engine:
- Crawlee + Playwright framework with pg-boss scheduler
- FS.com scraper (PlaywrightCrawler, anti-bot workaround)
- Optcore.net scraper (WP REST API enumeration + PlaywrightCrawler)
  - Uses /wp-json/wp/v2/product to get 2000+ product URLs
  - Playwright renders individual product pages for price extraction
- Cisco TMG Matrix scraper (compatibility data)
- News RSS aggregator (optics.org, SPIE, Network World, Nature Photonics)
  - Keyword relevance scoring for transceiver/fiber topics
  - xml2js with malformed XML sanitization
- SHA-256 content hashing for change detection (skip unchanged records)
- pg-boss v10 with explicit queue creation before scheduling
This commit is contained in:
Rene Fichtmueller 2026-03-27 16:27:31 +13:00
parent ddd0a592aa
commit e9fb50a248
42 changed files with 11096 additions and 37 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,672 @@
# Optical Transceiver Evolution: Complete History & Database Reference (2001-2026)
> Deep research compiled from OFC proceedings, LightCounting, Cignal AI, IEEE, OIF, and industry publications.
> Last updated: 2026-03-27
---
## Table of Contents
1. [Form Factor Evolution Timeline](#1-form-factor-evolution-timeline)
2. [Speed Tier Evolution](#2-speed-tier-evolution)
3. [Key Standards & Adoption Timelines](#3-key-standards--adoption-timelines)
4. [CWDM vs DWDM Evolution](#4-cwdm-vs-dwdm-evolution)
5. [Major Transceiver Manufacturers](#5-major-transceiver-manufacturers)
6. [Next-Generation Technologies (2025-2030)](#6-next-generation-technologies-2025-2030)
7. [Market Data Points](#7-market-data-points)
8. [Database Schema Recommendations](#8-database-schema-recommendations)
9. [Hype Cycle Analysis](#9-hype-cycle-analysis)
---
## 1. Form Factor Evolution Timeline
### Complete Form Factor Database
| Form Factor | Year Introduced | Peak Adoption | Legacy/Decline | Max Speed | Connector | Lanes | Status |
|---|---|---|---|---|---|---|---|
| **GBIC** | 1995 | 2000-2004 | 2006+ | 2.5 Gbps | SC Duplex | 1 | Obsolete |
| **SFP** | 2001 | 2004-present | Still active (1G) | 4.25 Gbps | LC Duplex | 1 | Active (legacy speeds) |
| **XENPAK** | 2001 | 2002-2006 | 2007+ | 10 Gbps | SC Duplex | 1 | Obsolete |
| **X2** | 2003 | 2004-2008 | 2009+ | 10 Gbps | SC Duplex | 1 | Obsolete |
| **XFP** | 2002 (MSA), 2003 (adopted) | 2005-2012 | 2013+ | 10 Gbps (DWDM capable) | LC Duplex | 1 | Legacy |
| **SFP+** | 2006 | 2008-present | Still active | 16 Gbps | LC Duplex | 1 | Active |
| **QSFP** | 2006 | 2008-2012 | 2013+ | 4x1G = 4 Gbps | MPO-12 | 4 | Legacy |
| **CFP** | 2009 | 2010-2016 | 2017+ | 100 Gbps | LC Duplex/MPO | 10x10G | Legacy |
| **QSFP+** | 2012 | 2013-2020 | Declining | 40 Gbps | MPO-12 / LC | 4x10G | Active (declining) |
| **CFP2** | 2012 | 2014-2020 | 2021+ | 200 Gbps | LC Duplex | varies | Legacy (except coherent) |
| **CFP4** | 2014 | 2015-2019 | 2020+ | 100 Gbps | LC Duplex | 4x25G | Legacy |
| **QSFP28** | 2014 | 2016-2023 | Declining | 100 Gbps | LC / MPO-12 | 4x25G | Active (declining) |
| **SFP28** | 2014 | 2016-present | Still active | 25 Gbps | LC Duplex | 1 | Active |
| **OSFP** | 2016 (announced) | 2020-present | - | 800 Gbps (8x100G) | MPO-16 / LC | 8 | Active (growing) |
| **CSFP** | 2018 | 2019-present | - | 2x1 Gbps | LC (BiDi) | 2 (BiDi) | Niche |
| **QSFP56** | 2019 | 2020-2024 | Declining | 200 Gbps | MPO-12 / LC | 4x50G | Active (declining) |
| **QSFP-DD** | 2019 | 2021-present | - | 800 Gbps (8x100G) | MPO-16 / LC | 8 | Active (growing) |
| **SFP56** | 2020 (spec), 2024 (products) | 2024-present | - | 50 Gbps | LC Duplex | 1 | Active (emerging) |
| **QSFP112** | 2021 | 2022-present | - | 400 Gbps | MPO-12 / LC | 4x100G | Active |
| **SFP-DD** | 2017 (spec) | 2020-present | - | 2x25G = 50 Gbps | LC Duplex | 2 | Niche |
| **OSFP-XD** | 2022 | 2025-present | - | 1.6T (16x100G), 3.2T future | MPO-16 | 16 | Emerging |
| **QSFP-DD1600** | 2024 (spec in progress) | 2026+ (projected) | - | 1.6T (8x200G) | MPO-16 | 8 | Emerging |
| **OSFP1600** | 2022 (spec) | 2025-2026 | - | 1.6T (8x200G) | MPO-16 | 8 | Emerging |
### Form Factor Hype Cycle Phases
```
Phase 1: INTRODUCTION - Standard published, first samples
Phase 2: EARLY ADOPTION - Hyperscale/cloud first movers
Phase 3: MAINSTREAM - Broad enterprise deployment, pricing declines
Phase 4: MATURITY - Commoditized, price floor reached
Phase 5: DECLINE - Next generation overtakes, volume drops
Phase 6: LEGACY - Minimal new deployments, maintenance only
Phase 7: OBSOLETE - No longer manufactured
```
| Form Factor | Current Phase (2026) |
|---|---|
| GBIC | 7-OBSOLETE |
| XENPAK | 7-OBSOLETE |
| X2 | 7-OBSOLETE |
| XFP | 6-LEGACY |
| SFP (1G) | 4-MATURITY |
| SFP+ (10G) | 4-MATURITY |
| QSFP+ (40G) | 5-DECLINE |
| CFP/CFP2/CFP4 | 6-LEGACY (except CFP2-DCO) |
| SFP28 (25G) | 3-MAINSTREAM |
| QSFP28 (100G) | 4-MATURITY / 5-DECLINE |
| QSFP56 (200G) | 5-DECLINE |
| QSFP-DD (400G/800G) | 3-MAINSTREAM |
| OSFP (400G/800G) | 3-MAINSTREAM |
| QSFP112 (400G) | 2-EARLY ADOPTION |
| OSFP-XD (1.6T) | 1-INTRODUCTION |
| QSFP-DD1600 (1.6T) | 1-INTRODUCTION |
---
## 2. Speed Tier Evolution
### Speed Tier Database
| Speed | Year Standardized | Year Mainstream | Dominant Form Factor | Modulation | Lanes | Key Standard | Current Status |
|---|---|---|---|---|---|---|---|
| **1G** | 1998 (802.3z) | 2002 | SFP | NRZ | 1 | IEEE 802.3z | Mature/commodity |
| **10G** | 2002 (802.3ae) | 2007 | SFP+ | NRZ | 1 | IEEE 802.3ae | Mature/commodity |
| **25G** | 2016 (802.3by) | 2018 | SFP28 | NRZ | 1 | IEEE 802.3by | Mainstream |
| **40G** | 2010 (802.3ba) | 2013 | QSFP+ | NRZ | 4x10G | IEEE 802.3ba | Declining |
| **50G** | 2016 (802.3cd) | 2020 | SFP56 / QSFP28 | PAM4 (single lane) | 1 | IEEE 802.3cd | Niche |
| **100G** | 2010 (802.3ba) / 2014 (QSFP28) | 2017 | QSFP28 | NRZ (4x25G) | 4 | IEEE 802.3ba | Mainstream/declining |
| **200G** | 2017 (802.3bs) | 2020 | QSFP56 / QSFP-DD | PAM4 | 4x50G | IEEE 802.3bs | Active |
| **400G** | 2017 (802.3bs) | 2022 | QSFP-DD / OSFP | PAM4 | 8x50G or 4x100G | IEEE 802.3bs | Mainstream |
| **800G** | 2024 (802.3df) | 2024-2025 | OSFP / QSFP-DD | PAM4 | 8x100G | IEEE 802.3df | Rapid growth |
| **1.6T** | 2026 (802.3dj target) | 2026-2027 (projected) | OSFP-XD / OSFP1600 | PAM4 | 8x200G or 16x100G | IEEE 802.3dj | Emerging |
### Speed Tier Adoption S-Curves (Port Shipment Peak Years)
```
1G: Peak ~2010-2014, still shipping in volume for enterprise access
10G: Peak ~2016-2020, declining but high volume
25G: Peak ~2020-2024, server-side standard
40G: Peak ~2015-2019, largely replaced by 100G
100G: Peak ~2020-2024, transitioning to 400G
400G: Peak ~2024-2027 (projected), current mainstream for spine/core
800G: Peak ~2026-2029 (projected), AI backend standard
1.6T: Peak ~2028-2031 (projected), next-gen AI/HPC
```
### Modulation Technology Timeline
| Technology | Speed Range | Years Active | Key Characteristic |
|---|---|---|---|
| NRZ (Non-Return-to-Zero) | 1G-25G per lane | 1995-present | 1 bit per symbol, simple |
| PAM4 (4-level Pulse Amplitude) | 50G-200G per lane | 2017-present | 2 bits per symbol, requires DSP/FEC |
| Coherent (DP-QPSK/DP-16QAM) | 100G-800G per wavelength | 2011-present | Phase + amplitude, long-haul |
### Per-Lane Rate Evolution
| Year | Per-Lane Rate | Technology | Key Enabler |
|---|---|---|---|
| 2001-2005 | 1G | NRZ | DFB/VCSEL |
| 2006-2013 | 10G | NRZ | DFB/VCSEL, CDR |
| 2014-2018 | 25G | NRZ | EML, CDR |
| 2019-2022 | 50G | PAM4 | DSP (7nm/5nm) |
| 2022-2025 | 100G | PAM4 | DSP (5nm/3nm), SiPh |
| 2025-2028 | 200G | PAM4 | DSP (3nm), advanced FEC |
---
## 3. Key Standards & Adoption Timelines
### IEEE 802.3 Optical Ethernet Standards
| Standard | Year Ratified | Speed | Key PHY Types | Notes |
|---|---|---|---|---|
| 802.3z | 1998 | 1 Gbps | 1000BASE-SX, 1000BASE-LX | First Gigabit Ethernet |
| 802.3ae | June 2002 | 10 Gbps | 10GBASE-SR, -LR, -ER, -LX4 | First 10GbE, fiber only |
| 802.3aq | 2006 | 10 Gbps | 10GBASE-LRM | Long reach multimode |
| 802.3ba | June 2010 | 40/100 Gbps | 40GBASE-SR4/LR4, 100GBASE-SR10/LR4/ER4 | First multi-rate standard |
| 802.3bm | 2015 | 40/100 Gbps | 40GBASE-SR4 (OM3/OM4), 100GBASE-SR4 | Improved MMF reach |
| 802.3by | 2016 | 25 Gbps | 25GBASE-SR, 25GBASE-LR | Single-lane 25G |
| 802.3bs | Dec 2017 | 200/400 Gbps | 200GBASE-DR4, 400GBASE-SR16/DR4/FR8/LR8 | First PAM4 in standard |
| 802.3cd | Dec 2018 | 50/100/200 Gbps | 50GBASE-SR/LR/FR/CR, 100GBASE-DR/SR2 | Single-lane 50G NRZ |
| 802.3cm | 2020 | 400 Gbps | 400GBASE-SR4.2 | Short-reach MMF (BiDi SWDM) |
| 802.3ct | 2021 | 100 Gbps | 100GBASE-ZR | Coherent 100G pluggable |
| 802.3cu | 2021 | 100/400 Gbps | 100GBASE-FR1/LR1, 400GBASE-FR4 | Single-lambda 100G |
| 802.3ck | Sep 2022 | 100/200/400 Gbps | Electrical interfaces (100G/lane) | Defines 100G SerDes |
| 802.3db | Sep 2022 | 100/200/400 Gbps | 100GBASE-VR1, 400GBASE-VR4 | Very short reach |
| 802.3df | Feb 2024 | 400/800 Gbps | 800GBASE-DR8, 400GBASE-DR4-2 | 800G standard |
| 802.3dj | ~2026 (target) | 200/400/800/1600 Gbps | 200G/lane PHYs | 1.6T Ethernet |
### OIF Implementation Agreements
| Agreement | Year Published | Speed | Max Reach | Key Feature |
|---|---|---|---|---|
| VSR-5 OIF-05.0 | ~2010 | 100G | 100m | Very short reach coherent |
| 400ZR | Dec 2020 | 400G | 120km (amplified) | Pluggable coherent DWDM in QSFP-DD/OSFP |
| 400ZR+ (vendor-specific) | 2021 | 400G | 450-600km | Extended reach, oFEC |
| 800ZR (in progress) | 2024-2025 | 800G | 80-120km | Next-gen pluggable coherent |
| 1600ZR (in progress) | 2025+ | 1.6T | TBD | Future coherent standard |
| CEI-112G | 2021 | 112 Gbps/lane | Chip-to-module | 100G PAM4 electrical interface |
| CEI-224G | 2025 (target) | 224 Gbps/lane | Chip-to-module | 200G PAM4 electrical interface |
### Multi-Source Agreements (MSAs)
| MSA | Year Published | Speed | Technology | Reach | Key Members |
|---|---|---|---|---|---|
| SFP MSA | 2000 | 1-4G | Various | Varies | Finisar, JDS, Agilent |
| XFP MSA | 2002 | 10G | Various | Varies | Finisar + 10 companies |
| SFP+ MSA (SFF-8431) | 2006 | 10G | NRZ | Varies | Industry-wide |
| QSFP+ MSA (SFF-8436) | 2009 | 40G | 4x10G NRZ | Varies | Industry-wide |
| CFP MSA | 2009 | 100G | 10x10G/4x25G | Varies | Industry-wide |
| QSFP28 MSA (SFF-8665) | 2014 | 100G | 4x25G NRZ | Varies | Industry-wide |
| 100G PSM4 MSA | Mar 2014 | 100G | 4x25G parallel SM | 500m | Corning, Intel, Luxtera, etc. |
| 100G CWDM4 MSA | Sep 2014 | 100G | 4x25G CWDM | 2km | Avago, Finisar, JDSU, etc. |
| SFP28 MSA (SFF-8402) | 2014 | 25G | NRZ | Varies | Industry-wide |
| 25G Ethernet Consortium | 2014 | 25/50G | NRZ | Varies | Arista, Broadcom, Google, Microsoft |
| 100G Lambda MSA | Sep 2017 | 100G/400G | Single-lambda 100G PAM4 | 2-40km | Alibaba, Cisco, Intel, +39 members |
| QSFP-DD MSA | 2017 | 200-800G | 8-lane double density | Varies | Broadcom, Cisco, Finisar, etc. |
| OSFP MSA | 2016 | 400-800G | 8-lane octal | Varies | Arista, Broadcom, Mellanox, etc. |
| OpenZR+ MSA | May 2020 | 100-400G | Coherent DWDM | 1000+km | Acacia, Cisco, Juniper, Lumentum |
| OSFP-XD MSA | 2022 | 1.6-3.2T | 16-lane | Varies | Industry-wide |
| CMIS (Common Mgmt Interface) | v5.0: 2020, v5.3: 2024 | All | Management spec | - | Industry-wide |
---
## 4. CWDM vs DWDM Evolution
### CWDM Technical Specifications
| Parameter | Value |
|---|---|
| Standard | ITU-T G.694.2 |
| Wavelength Range | 1270-1610 nm |
| Channel Spacing | 20 nm |
| Total Channels | 18 (full grid) |
| Practical Channels | 8-16 (water peak limits 1370-1410nm) |
| Laser Type | Uncooled DFB |
| Max Reach | ~70 km (unamplified) |
| Max Per-Channel Speed | 100 Gbps (current), 25G most common |
| Amplification | None (passive) |
| Cost | Lower (uncooled lasers, wider tolerance) |
#### CWDM Wavelength Grid
| Channel | Wavelength (nm) | Band | Notes |
|---|---|---|---|
| 1 | 1271 | O-band | Commonly used |
| 2 | 1291 | O-band | Commonly used |
| 3 | 1311 | O-band | Commonly used |
| 4 | 1331 | O-band | Commonly used |
| 5 | 1351 | E-band | Water peak region |
| 6 | 1371 | E-band | Water peak region |
| 7 | 1391 | S-band | Water peak region (limited to 40km) |
| 8 | 1411 | S-band | Water peak region (limited to 40km) |
| 9 | 1431 | S-band | |
| 10 | 1451 | S-band | |
| 11 | 1471 | C-band edge | Commonly used |
| 12 | 1491 | S/C-band | Commonly used |
| 13 | 1511 | C-band | Commonly used |
| 14 | 1531 | C-band | Commonly used |
| 15 | 1551 | C-band | Commonly used |
| 16 | 1571 | L-band | Commonly used |
| 17 | 1591 | L-band | |
| 18 | 1611 | L-band | |
### DWDM Technical Specifications
| Parameter | Value |
|---|---|
| Standard | ITU-T G.694.1 |
| C-Band Range | 1528.77-1563.86 nm (191.7-196.1 THz) |
| L-Band Range | 1565-1625 nm |
| Channel Spacing (100 GHz) | 0.8 nm, ~40 channels in C-band |
| Channel Spacing (50 GHz) | 0.4 nm, ~80 channels in C-band |
| Channel Spacing (25 GHz) | 0.2 nm, ~160 channels (flex grid) |
| Laser Type | Cooled DFB / Tunable |
| Max Reach | 3000+ km (amplified with EDFA/Raman) |
| Max Per-Channel Speed | 800 Gbps (coherent pluggable) |
| Amplification | EDFA, Raman |
| Flex Grid | Supports variable channel widths (12.5 GHz granularity) |
### Coherent Optics Evolution
| Generation | Year | Per-Wavelength Rate | Modulation | Baud Rate | Form Factor |
|---|---|---|---|---|---|
| Gen 1 | 2011 | 40G | DP-QPSK | 10-12 GBd | Line card (chassis) |
| Gen 2 | 2012 | 100G | DP-QPSK | 32 GBd | Line card / CFP |
| Gen 3 | 2016 | 200G | DP-16QAM | 32-45 GBd | CFP2-DCO |
| Gen 4 | 2018 | 400G | DP-16QAM | 64 GBd | CFP2-DCO |
| Gen 5 (400ZR) | 2021 | 400G | DP-16QAM | 60 GBd | QSFP-DD / OSFP |
| Gen 6 (ZR+) | 2022 | 400G | DP-16QAM (enhanced) | 64 GBd | QSFP-DD / OSFP |
| Gen 7 (800ZR) | 2024 | 800G | DP-64QAM / prob-shaped | 100+ GBd | QSFP-DD / OSFP |
| Gen 8 (1600ZR) | 2026+ | 1.6T | TBD | 130+ GBd | OSFP / OSFP-XD |
### C+L Band Capacity Evolution
| Year | Typical System Capacity | Technology |
|---|---|---|
| 2005 | 40x10G = 400 Gbps | C-band, 100GHz grid |
| 2010 | 80x40G = 3.2 Tbps | C-band, 50GHz grid |
| 2015 | 80x100G = 8 Tbps | C-band, 50GHz grid, coherent |
| 2020 | 80x400G = 32 Tbps | C-band, flex grid |
| 2024 | 80x800G = 64 Tbps | C-band, flex grid |
| 2025+ | 120+x800G = 96+ Tbps | C+L band, flex grid |
### Key WDM Transceiver Types by Speed
| Speed | CWDM Variants | DWDM Variants |
|---|---|---|
| 1G | SFP CWDM (18 wavelengths) | SFP DWDM (C-band) |
| 10G | SFP+ CWDM, XFP CWDM | XFP/SFP+ DWDM (tunable) |
| 25G | SFP28 CWDM | SFP28 DWDM |
| 40G | QSFP+ CWDM4 | CFP DWDM (coherent) |
| 100G | QSFP28 CWDM4 | QSFP28 DWDM / CFP2-DCO |
| 400G | (not practical) | QSFP-DD/OSFP ZR/ZR+ |
| 800G | (not practical) | OSFP/QSFP-DD 800ZR/ZR+ |
---
## 5. Major Transceiver Manufacturers
### Manufacturer Database
| Company | HQ | Founding | Key Milestones | Specialty | 2024 Revenue (transceivers) | Market Position |
|---|---|---|---|---|---|---|
| **Coherent Corp.** | Pittsburgh, USA | 1971 (as II-VI) | Acquired Finisar ($3.2B, 2019), Coherent ($6.56B, 2022) | Coherent, Datacom, InP lasers | ~$2.5B+ | #2 globally, #1 telecom |
| **Zhongji Innolight** | Suzhou, China | 2008 | #1 globally 2023, 50%+ Nvidia wallet share | Datacom, 800G/1.6T | ~$3.3B (114% YoY growth) | #1 globally |
| **Lumentum** | San Jose, USA | 2015 (spun off JDS Uniphase) | Acquired Cloud Light ($750M, 2024), Oclaro ($1.8B, 2018) | Coherent, lasers, 3D sensing | ~$1.5B | #3 globally |
| **Broadcom (Optical)** | San Jose, USA | Broadcom acquired original Avago/LSI/Broadcom | Key DSP/PAM4 supplier | DSP chips, SiPh, VCSEL | ~$1B+ | Major component supplier |
| **Cisco (Silicon Photonics)** | San Jose, USA | Acquired Luxtera ($660M, 2019), Acacia ($4.6B, 2021) | Integrated SiPh transceivers | SiPh, coherent (via Acacia) | Internal consumption + merchant | #4-5 globally |
| **Eoptolink** | Shenzhen, China | 2004 | 175% revenue growth 2024, #3 globally | Datacom, LPO, SiPh | ~$1.2B | #3 globally |
| **HG Genuine** | Wuhan, China | 2001 | ByteDance/TikTok supplier since 2021 | Datacom, access optics | ~$600M+ | #8 globally |
| **Accelink Technologies** | Wuhan, China | 2001 | Chinese cloud supplier | Telecom, passive components | ~$600M+ | #5 globally |
| **Hisense Broadband** | Qingdao, China | 2003 (Hisense subsidiary) | PON/access market leader | Access, PON, 5G | ~$600M+ | #6 globally |
| **Source Photonics** | West Hills, USA / China | 2002 | Chinese cloud supplier | Access, enterprise, DC | ~$400M | #9 globally |
| **Applied Optoelectronics (AOI)** | Sugar Land, USA | 1997 | CATV and DC optics | VCSEL, DFB, DC transceivers | ~$200M | Niche |
| **Intel Silicon Photonics** | Santa Clara, USA | SiPh division ~2010 | 100G PSM4, 1.6T SiPh engines | Silicon photonics platform | Sold to third parties (Jabil etc.) | Technology leader |
| **ColorChip** | Yokneam, Israel | 2001 | Acquired by Source Photonics 2018 | PLC-based transceivers | (merged) | Acquired |
| **Broadex Technologies** | Chengdu, China | 2016 | Fast-growing Chinese supplier | Datacom, 400G/800G | ~$300M | Emerging |
| **Centera Photonics** | Taiwan | 2007 | 800G/1.6T development | Datacom transceivers | ~$150M | Regional |
### Market Share Trends (Global Optical Transceiver Revenue)
| Year | #1 | #2 | #3 | Chinese in Top 10 | Key Shift |
|---|---|---|---|---|---|
| 2015 | Finisar | Lumentum/JDSU | Avago/Broadcom | 2-3 | US/Japan dominance |
| 2018 | Finisar | II-VI | Lumentum | 3-4 | Pre-merger era |
| 2020 | Coherent (II-VI+Finisar) | Innolight | Lumentum | 4-5 | Chinese rise begins |
| 2022 | Innolight = Coherent (~$1.4B each) | Lumentum | Accelink | 5-6 | Chinese parity |
| 2023 | Innolight | Coherent | Lumentum | 7 of top 10 | Chinese dominance |
| 2024 | Innolight ($3.3B) | Coherent (~$2.5B) | Eoptolink ($1.2B) | 7 of top 10 | AI-driven surge |
### Major M&A Timeline
| Year | Acquirer | Target | Value | Impact |
|---|---|---|---|---|
| 2013 | Oclaro | Opnext | $180M | Combined coherent portfolio |
| 2015 | Lumentum spins off | from JDS Uniphase | - | Created independent photonics leader |
| 2018 | II-VI | Finisar | $3.2B | Created #1 transceiver company |
| 2018 | Lumentum | Oclaro | $1.8B | Strengthened InP/coherent capabilities |
| 2019 | Cisco | Luxtera | $660M | Silicon photonics integration |
| 2019 | Cisco | Acacia Communications | $4.6B | Coherent DSP leadership |
| 2021 | Intel | (SiPh division established) | Internal | 100G-1.6T silicon photonics engines |
| 2022 | II-VI | Coherent Inc. (laser co.) | $6.56B | Renamed to Coherent Corp. |
| 2024 | Lumentum | Cloud Light Technology | $750M | DC infrastructure boost |
| 2024 | Nvidia | (investing in optical supply chain) | Various | Vertical integration signal |
---
## 6. Next-Generation Technologies (2025-2030)
### 1.6T Transceivers
| Parameter | Gen1 (16x100G) | Gen2 (8x200G) |
|---|---|---|
| Timeline | 2025 (shipping) | 2026 (maturing) |
| Lane Rate | 100G PAM4 | 200G PAM4 |
| Lane Count | 16 | 8 |
| Form Factor | OSFP-XD | OSFP1600, OSFP, QSFP-DD1600 |
| DSP Process | 5nm | 3nm |
| Power (retimed) | ~25-30W | ~17-26W |
| Power (LPO) | ~8-12W | ~5W |
| Key DSPs | Broadcom Sian2, Marvell Aries | Broadcom Sian3, Marvell next-gen |
### Co-Packaged Optics (CPO) Timeline
| Year | Milestone |
|---|---|
| 2021 | Broadcom Tomahawk 4 + Humboldt = first CPO chipset |
| 2022 | Broadcom Tomahawk 5 + Bailly = first volume-production CPO |
| 2025 Q1 | NVIDIA announces first 1.6T CPO system (Micro Ring Modulators) |
| 2025 Q2 | NVIDIA Quantum-X SiPh switch ships |
| 2025 | TSMC COUPE platform adopted by NVIDIA, Broadcom |
| 2025 | Meta tests Broadcom CPO for 1M+ link-hours |
| 2025 Nov | Ayar Labs integrates TeraPHY into GUC ASIC workflow |
| 2026 H2 | NVIDIA Spectrum-X Photonics system ships |
| 2026-2027 | Broad CPO commercialization begins |
| 2028-2030 | Large-scale CPO deployment in hyperscale |
### CPO vs LPO vs Traditional DSP Comparison
| Feature | Traditional (DSP) | LPO (Linear Drive) | CPO (Co-Packaged) |
|---|---|---|---|
| Power Consumption | Baseline | -30 to -50% | -50 to -84% |
| Latency | ~100ns (DSP) | <15ns reduction | Near-zero electrical path |
| Serviceability | Hot-swappable | Hot-swappable | Requires board replacement |
| Maturity | Production | Shipping (NVIDIA, Meta) | Pre-production/early access |
| Cost | Baseline | Lower (no DSP in module) | Higher initially, lower at scale |
| Best For | Long reach, interop | Short reach (<2km), AI clusters | Ultra-dense, scale-up AI |
| Market Share (2025) | ~60% of 800G/1.6T | ~30% | ~5% |
| Market Share (2030, projected) | ~30% | ~40% | ~30% |
### Silicon Photonics Adoption
| Year | SiPh Share of Transceivers | Key Driver |
|---|---|---|
| 2018 | ~14% | 100G PSM4 (Intel) |
| 2020 | ~20% | 400G DR4 ramp |
| 2022 | ~25% | 400G mainstream |
| 2024 | ~35% | 800G ramp |
| 2025 | ~40-45% | 800G mainstream, 1.6T intro |
| 2030 (proj.) | ~60% | LPO + CPO adoption |
### O-Band vs C-Band Data Center Trends
| Parameter | O-Band (1310nm) | C-Band (1550nm) |
|---|---|---|
| Primary Use | Data center interconnect (<10km) | Metro/long-haul, DCI (>10km) |
| Technology | Direct detect, PAM4 | Coherent or PAM4 WDM |
| Standards | DR, FR, LR variants | ZR, ZR+, DWDM |
| Advantages | Lower cost, simpler, lower dispersion | Higher capacity, longer reach |
| Trend | Dominant for intra-DC | Growing for inter-DC via ZR/ZR+ |
| 800G Example | 800G-DR8 (O-band, 500m) | 800ZR (C-band, 120km) |
---
## 7. Market Data Points
### Global Optical Transceiver Market Size
| Year | Market Size (USD) | YoY Growth | Key Driver |
|---|---|---|---|
| 2019 | ~$6.5B | - | 100G mainstream |
| 2020 | ~$7.0B | +8% | COVID + cloud demand |
| 2021 | ~$8.0B | +14% | 400G ramp begins |
| 2022 | ~$9.0B | +13% | 400G mainstream deployment |
| 2023 | ~$10.5B | +17% | AI infrastructure begins |
| 2024 | ~$13.6B | +30% | AI explosion, 800G ramp |
| 2025 (est.) | ~$15.6-16B | +15-18% | 800G mainstream, 1.6T intro |
| 2029 (proj.) | ~$25B | CAGR 13% | 1.6T mainstream |
| 2034 (proj.) | ~$46B | CAGR 17% | CPO + next-gen |
### Port Shipment Data
| Metric | 2023 | 2024 | 2025 (est.) |
|---|---|---|---|
| Total transceiver units deployed | ~15M | ~22.5M | ~34.5M |
| 400G+800G unit shipments | ~6M | 20M+ | 30M+ (est.) |
| Quarterly record (400/800G) | <3M | 5M+ (Q2 2024) | 7M+ (projected) |
| 400G/800G YoY growth | - | +250% | +60% (800G specifically) |
| 800G as % of high-speed | ~20% | ~35% | ~50% (est.) |
### Average Selling Price (ASP) Trends
| Speed | Launch ASP | 2024 ASP | ASP Decline Pattern |
|---|---|---|---|
| 1G SFP | ~$500 (2001) | ~$5-15 | >95% decline over 20 years |
| 10G SFP+ | ~$500 (2007) | ~$15-40 | >90% decline over 15 years |
| 25G SFP28 | ~$100 (2016) | ~$15-30 | ~75% decline over 8 years |
| 40G QSFP+ | ~$300 (2012) | ~$30-80 | ~80% decline over 12 years |
| 100G QSFP28 | ~$1,000 (2015) | ~$50-120 | ~90% decline, 60% in last 5 years |
| 400G QSFP-DD | ~$800-1,200 (2020) | ~$120-250 | ~75% decline, SR8 50% in 1 year |
| 800G OSFP | ~$800-1,000 (2023) | ~$360-450 | Early decline, still premium |
| 1.6T OSFP-XD | ~$1,300-1,500 (2025) | $1,300-1,500 | Launch pricing, projected ~$1,100 by 2027 |
### ASP Decline Model (Typical Pattern)
```
Year 0 (Launch): 100% (premium pricing)
Year 1: 80-90% (early adoption)
Year 2: 60-70% (volume ramp)
Year 3: 40-50% (mainstream)
Year 4: 30-40% (commoditization begins)
Year 5+: 20-30% (commodity, Chinese competition)
Year 7+: 10-15% (floor pricing)
```
### Market Segment Split (2024-2025)
| Segment | 2024 Share | 2025 Share (est.) | Growth Driver |
|---|---|---|---|
| Data Centers | 45-55% | 55-60% | AI/ML clusters, hyperscale |
| Telecommunications | 30-40% | 25-30% | 5G, coherent metro/long-haul |
| Enterprise Networking | 14-20% | 12-15% | LAN/WAN upgrades to 100G |
| Other (defense, govt, research) | 5-10% | 5-8% | Specialty applications |
### Datacom vs Telecom Module Revenue
| Year | Datacom Revenue | Telecom Revenue | Datacom Share |
|---|---|---|---|
| 2020 | ~$4B | ~$3B | 57% |
| 2022 | ~$5.5B | ~$3.5B | 61% |
| 2024 | ~$9B+ | ~$4B | 69% |
| 2025 (est.) | ~$12B+ | ~$4B | 75% |
### Coherent Pluggable Shipments
| Year | 400ZR/ZR+ Units | Key Milestone |
|---|---|---|
| 2021 | <50K | First GA shipments |
| 2022 | ~100-150K | Initial ramp |
| 2023 | ~300K | Broad deployment |
| 2024 | ~500K | Fastest adopted coherent ever |
| 2025 (est.) | ~600K+ | 800ZR enters market |
### AI Optics Market Specifically
| Year | AI Optics Market | Notes |
|---|---|---|
| 2023 | ~$3B | GPU interconnect demand begins |
| 2024 | ~$5B | NVIDIA GB200 drives 800G demand |
| 2025 | ~$7-8B | 800G mainstream for AI |
| 2026 (est.) | ~$10B+ | 1.6T for AI clusters |
### Vendor Count Per Standard (approximate)
| Standard/Type | Vendor Count (2025) | Notes |
|---|---|---|
| 1G SFP | 100+ | Fully commoditized |
| 10G SFP+ | 80+ | Commoditized |
| 25G SFP28 | 50+ | Maturing |
| 100G QSFP28 | 40+ | Maturing |
| 400G QSFP-DD | 25+ | Mainstream competition |
| 400G ZR/ZR+ | 10-15 | Specialized |
| 800G OSFP/QSFP-DD | 15-20 | Growing |
| 800G ZR/ZR+ | 5-8 | Emerging |
| 1.6T OSFP/OSFP-XD | 8-12 | Early stage |
---
## 8. Database Schema Recommendations
### Core Tables
```
TABLE: form_factors
- id, name, year_introduced, year_mainstream, year_decline, year_obsolete
- max_speed_gbps, connector_type, lane_count, width_mm, depth_mm
- power_class_w, backward_compatible_with, msa_spec_url
- status (emerging/active/declining/legacy/obsolete)
TABLE: speed_tiers
- id, speed_gbps, year_standardized, year_mainstream, year_peak, year_decline
- primary_ieee_standard, modulation_type, lanes_config
- typical_launch_asp_usd, current_asp_usd
TABLE: standards
- id, name, organization (IEEE/OIF/MSA), year_published, year_ratified
- speed_gbps, reach_km, key_phy_types, status
TABLE: manufacturers
- id, name, hq_country, year_founded, specialties
- annual_revenue_usd, market_rank, key_products
TABLE: market_data (time series)
- id, year, quarter, metric_name, metric_value, unit
- segment (datacom/telecom/enterprise), source
TABLE: products
- id, manufacturer_id, form_factor_id, speed_tier_id
- model_name, wavelength_nm, reach_km, fiber_type
- modulation, fec_type, power_w, temperature_range
- year_launched, current_asp_usd, status
TABLE: technology_transitions
- id, technology_name, category (modulation/integration/packaging)
- year_introduced, year_mainstream, year_peak
- market_share_pct, hype_cycle_phase
TABLE: acquisitions
- id, acquirer_id, target_name, year, value_usd, strategic_rationale
```
---
## 9. Hype Cycle Analysis
### Technology Hype Cycle Positions (2026)
```
PEAK OF INFLATED EXPECTATIONS:
- Co-Packaged Optics (CPO)
- 3.2T transceivers
- Optical compute interconnect
SLOPE OF ENLIGHTENMENT:
- 1.6T pluggable transceivers
- Linear Drive Optics (LPO)
- 200G/lane PAM4
- Near-Packaged Optics (NPO)
PLATEAU OF PRODUCTIVITY:
- 800G pluggable transceivers
- 400ZR/ZR+ coherent pluggables
- Silicon photonics (in 400G/800G)
- PAM4 modulation
ENTERING DECLINE:
- 400G pluggable (mainstream, starting decline)
- 100G QSFP28 (commoditized)
- NRZ modulation (for new designs)
OBSOLESCENCE TRAJECTORY:
- 40G QSFP+
- 10G XFP
- CFP/CFP2/CFP4 (except DCO)
- CWDM for high-speed (>100G)
```
### Form Factor Hype Cycles (Historical Overlay)
```
Innovation Peak Trough Slope Plateau
Trigger Hype Disillusion Enlighten Productivity
GBIC 1995 1998 2002 - 2000-2004
SFP 2001 2003 - 2004 2005-forever
XFP 2002 2005 2008 - 2006-2012
SFP+ 2006 2008 - 2009 2010-forever
QSFP+ 2012 2014 - 2015 2016-2022
QSFP28 2014 2016 - 2017 2018-2024
QSFP-DD 2019 2021 - 2022 2023-present
OSFP 2016 2020 - 2022 2023-present
OSFP-XD 2022 2025 - 2026(est) 2027(est)
```
### Speed Tier Lifecycle Model
Each speed tier follows a predictable ~10-year lifecycle:
```
Years 0-2: INTRODUCTION - Standards ratified, samples shipping, $$$$ pricing
Years 2-4: GROWTH - Volume ramps, multiple vendors, pricing drops 40-60%
Years 4-6: MAINSTREAM - Peak shipments, broad adoption, pricing drops another 30-50%
Years 6-8: MATURITY - Pricing floor, commoditized, Chinese competition dominant
Years 8-10: DECLINE - Next-gen overtakes, volumes drop, maintenance-only
Years 10+: LEGACY - Minimal shipments, long-tail demand
```
| Speed | Introduction | Growth | Mainstream | Maturity | Decline | Legacy |
|---|---|---|---|---|---|---|
| 1G | 1998-2002 | 2002-2005 | 2005-2010 | 2010-2016 | 2016-2020 | 2020+ |
| 10G | 2002-2006 | 2006-2010 | 2010-2016 | 2016-2022 | 2022+ | - |
| 25G | 2014-2017 | 2017-2019 | 2019-2023 | 2023-2026 | 2026+ | - |
| 40G | 2010-2013 | 2013-2015 | 2015-2019 | 2019-2022 | 2022+ | - |
| 100G | 2014-2017 | 2017-2020 | 2020-2024 | 2024-2026 | 2026+ | - |
| 400G | 2020-2022 | 2022-2024 | 2024-2027 | 2027-2030 | 2030+ | - |
| 800G | 2023-2025 | 2025-2027 | 2027-2030 | 2030-2033 | 2033+ | - |
| 1.6T | 2025-2027 | 2027-2029 | 2029-2032 | 2032-2035 | 2035+ | - |
---
## Sources & References
### Market Research
- [Cignal AI - 800GbE Optics Shipments](https://cignal.ai/2025/05/800gbe-optics-shipments-to-grow-60-in-2025/)
- [Cignal AI - 20M 400G/800G Shipments 2024](https://cignal.ai/2025/01/over-20-million-400g-800g-datacom-optical-module-shipments-expected-for-2024/)
- [LightCounting - Silicon Photonics May 2025](https://www.lightcounting.com/newsletter/en/may-2025-silicon-photonics-linear-drive-pluggable-and-cpo-updated-november-2025-334)
- [LightCounting - AI Optics Jan 2025](https://www.lightcounting.com/newsletter/en/january-2025-optics-for-ai-clusters-319)
- [Mordor Intelligence - Optical Transceiver Market](https://www.mordorintelligence.com/industry-reports/optical-transceiver-market)
- [MarketsAndMarkets - Optical Transceiver Market 2030](https://www.marketsandmarkets.com/Market-Reports/optical-transceiver-market-161339599.html)
- [Fortune Business Insights - Optical Transceiver Market](https://www.fortunebusinessinsights.com/optical-transceiver-market-108985)
### Standards & Specifications
- [IEEE 802.3 Working Group Archive](https://www.ieee802.org/3/archive.html)
- [IEEE 802.3 - Wikipedia](https://en.wikipedia.org/wiki/IEEE_802.3)
- [OIF 400ZR Implementation Agreement](https://www.oiforum.com/technical-work/hot-topics/400zr-2/)
- [OpenZR+ MSA](https://openzrplus.org/)
- [100G Lambda MSA](https://100glambda.com/)
- [CWDM4 MSA](https://cwdm4-msa.org/)
- [100G PSM4 MSA](http://www.psm4.org/)
### Form Factors & Technology
- [Prooptix - History of Form Factors](https://www.prooptix.com/news/transceiver-form-factors/)
- [Meticulous Research - 25 Years of Optical Transceiver Evolution](https://www.meticulousresearch.com/blog/207/evolution-of-optical-transceiver-technologies-in-the-last-25-years)
- [Vitex - Transceiver Form Factors Guide](https://www.vitextech.com/blogs/blog/transceiver-form-factors)
- [FS.com - High-Speed Transceivers Guide](https://www.fs.com/blog/a-comprehensive-guide-to-highspeed-transceivers-400g-800g-and-the-leap-to-16t-13767.html)
### Coherent Optics & WDM
- [WWT - 400G-ZR & ZR+ Guide](https://www.wwt.com/blog/400gzr-and-zr-the-latest-in-pluggable-coherent-dwdm)
- [Acacia/Cisco - 2024 Coherent Optics Review](https://acacia-inc.com/blog/a-look-back-at-2024-whats-ahead-for-coherent-optics-in-2025/)
- [Smartoptics - CWDM DWDM Explained](https://smartoptics.com/knowledgebank-post/cwdm-dwdm-explained/)
- [FS.com - DWDM/CWDM ITU Channels Guide](https://www.fs.com/blog/dwdmcwdm-wavelength-itu-channels-guide-3149.html)
### Manufacturer & Industry Analysis
- [Iamfabian - 800G/1.6T Transceiver Battle](https://iamfabian.substack.com/p/pluggables-power-and-geopolitics)
- [Deep Fundamental - Optical Module Market Deep Dive](https://deepfundamental.substack.com/p/deep-dive-optical-module-market)
- [Chinese Suppliers Dominate 2024 Rankings](https://www.opticaltransceivermodules.com/news/chinese-optical-transceiver-suppliers-dominate-global-rankings-225829.html)
- [Coherent Corp - Wikipedia](https://en.wikipedia.org/wiki/Coherent_Corp.)
### Next-Gen Technology
- [EDN - Co-Packaged Optics 2026](https://www.edn.com/where-co-packaged-optics-cpo-technology-stands-in-2026/)
- [IDTechEx - CPO Market Forecast](https://www.idtechex.com/en/research-report/co-packaged-optics-cpo/1138)
- [Eoptolink - Gen2 1.6T at OFC 2025](https://www.eoptolink.com/news/361-eoptolink-launches-its-gen2-1-6t-osfp-and-osfp-rhs-transceiver-family-at-ofc-2025)
- [Broadcom OFC 2025 Advances](https://investors.broadcom.com/news-releases/news-release-details/broadcom-advances-optical-connectivity-ai-infrastructure)
- [Jabil 1.6T Transceiver Launch](https://investors.jabil.com/news/news-details/2025/Jabil-Launches-1-6T-Pluggable-Transceiver-to-Support-Growing-Demand-for-Intra-Data-Center-and-AI-Connectivity/default.aspx)

33
docker-compose.yml Normal file
View File

@ -0,0 +1,33 @@
services:
postgres:
image: timescale/timescaledb:latest-pg17
container_name: tip-postgres
environment:
POSTGRES_DB: transceiver_db
POSTGRES_USER: tip
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-tip_dev_2026}
ports:
- "5433:5432"
volumes:
- tip_pgdata:/var/lib/postgresql/data
- ./sql:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U tip -d transceiver_db"]
interval: 5s
timeout: 5s
retries: 5
qdrant:
image: qdrant/qdrant:latest
container_name: tip-qdrant
ports:
- "6333:6333"
- "6334:6334"
volumes:
- tip_qdrant:/qdrant/storage
environment:
QDRANT__SERVICE__GRPC_PORT: 6334
volumes:
tip_pgdata:
tip_qdrant:

16
ecosystem.config.js Normal file
View File

@ -0,0 +1,16 @@
module.exports = {
apps: [
{
name: "tip-api",
script: "packages/api/dist/index.js",
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: "512M",
env: {
NODE_ENV: "production",
API_PORT: 3200,
},
},
],
};

5660
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +1,28 @@
{ {
"name": "transceiver-db", "name": "transceiver-intelligence-platform",
"version": "1.0.0", "version": "0.1.0",
"description": "Open-source optical transceiver database. 89 products, 39 IEEE/MSA standards, 16 form factors, 9 speed tiers. SFP to 800G OSFP.", "private": true,
"main": "dist/index.js", "description": "Transceiver Intelligence Platform — the world's most comprehensive optical transceiver & network switch database",
"types": "dist/index.d.ts", "workspaces": [
"packages/*"
],
"scripts": { "scripts": {
"build": "tsc", "build": "npm run build --workspaces",
"prepublishOnly": "npm run build" "build:core": "npm run build -w packages/core",
"build:api": "npm run build -w packages/api",
"dev": "npm run dev -w packages/api",
"migrate": "tsx scripts/migrate.ts",
"seed": "tsx scripts/seed-from-npm.ts",
"db:reset": "npm run migrate && npm run seed"
}, },
"author": "Rene Fichtmueller",
"license": "MIT", "license": "MIT",
"keywords": [
"transceiver",
"optics",
"sfp",
"qsfp",
"networking",
"fiber",
"ieee",
"telecom",
"osfp",
"qsfp-dd",
"optical",
"datacenter",
"100g",
"400g",
"800g"
],
"files": [
"dist",
"LICENSE",
"README.md"
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/renefichtmueller/transceiver-db" "url": "https://github.com/renefichtmueller/transceiver-db"
}, },
"author": "Rene Fichtmueller",
"engines": {
"node": ">=14"
},
"devDependencies": { "devDependencies": {
"tsx": "^4.19",
"typescript": "^5.9.3" "typescript": "^5.9.3"
} }
} }

28
packages/api/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "@tip/api",
"version": "0.1.0",
"private": true,
"description": "TIP REST API server",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js"
},
"dependencies": {
"express": "^5.1.0",
"pg": "^8.13.1",
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"helmet": "^8.0.0",
"express-rate-limit": "^7.5.0",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/express": "^5.0.0",
"@types/pg": "^8.11.11",
"@types/cors": "^2.8.17",
"typescript": "^5.9.3",
"tsx": "^4.19.0"
}
}

View File

@ -0,0 +1,20 @@
import { config } from "dotenv";
import { join } from "path";
config({ path: join(__dirname, "..", "..", "..", ".env") });
export const cfg = {
port: parseInt(process.env.API_PORT || "3200"),
nodeEnv: process.env.NODE_ENV || "development",
db: {
host: process.env.POSTGRES_HOST || "localhost",
port: parseInt(process.env.POSTGRES_PORT || "5432"),
database: process.env.POSTGRES_DB || "transceiver_db",
user: process.env.POSTGRES_USER || "tip",
password: process.env.POSTGRES_PASSWORD || "tip_dev_2026",
},
qdrant: {
host: process.env.QDRANT_HOST || "localhost",
port: parseInt(process.env.QDRANT_PORT || "6333"),
},
} as const;

View File

@ -0,0 +1,17 @@
import { Pool } from "pg";
import { cfg } from "../config";
export const pool = new Pool({
host: cfg.db.host,
port: cfg.db.port,
database: cfg.db.database,
user: cfg.db.user,
password: cfg.db.password,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
pool.on("error", (err) => {
console.error("Unexpected PostgreSQL error:", err);
});

View File

@ -0,0 +1,211 @@
import { pool } from "./client";
export interface SearchParams {
q?: string;
form_factor?: string;
speed?: string;
speed_gbps?: number;
category?: string;
fiber_type?: string;
reach_min?: number;
reach_max?: number;
wdm_type?: string;
coherent?: boolean;
market_status?: string;
limit?: number;
offset?: number;
}
export async function searchTransceivers(params: SearchParams) {
const conditions: string[] = [];
const values: any[] = [];
let idx = 1;
if (params.q) {
conditions.push(`search_vector @@ plainto_tsquery('english', $${idx})`);
values.push(params.q);
idx++;
}
if (params.form_factor) {
conditions.push(`form_factor = $${idx}`);
values.push(params.form_factor);
idx++;
}
if (params.speed) {
conditions.push(`speed = $${idx}`);
values.push(params.speed);
idx++;
}
if (params.speed_gbps) {
conditions.push(`speed_gbps = $${idx}`);
values.push(params.speed_gbps);
idx++;
}
if (params.category) {
conditions.push(`category = $${idx}`);
values.push(params.category);
idx++;
}
if (params.fiber_type) {
conditions.push(`fiber_type = $${idx}`);
values.push(params.fiber_type);
idx++;
}
if (params.reach_min) {
conditions.push(`reach_meters >= $${idx}`);
values.push(params.reach_min);
idx++;
}
if (params.reach_max) {
conditions.push(`reach_meters <= $${idx}`);
values.push(params.reach_max);
idx++;
}
if (params.wdm_type) {
conditions.push(`wdm_type = $${idx}`);
values.push(params.wdm_type);
idx++;
}
if (params.coherent !== undefined) {
conditions.push(`coherent = $${idx}`);
values.push(params.coherent);
idx++;
}
if (params.market_status) {
conditions.push(`market_status = $${idx}`);
values.push(params.market_status);
idx++;
}
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
const limit = params.limit || 50;
const offset = params.offset || 0;
// Add relevance ranking when full-text search is used
const orderBy = params.q
? `ORDER BY ts_rank(search_vector, plainto_tsquery('english', $1)) DESC`
: `ORDER BY speed_gbps DESC, reach_meters ASC`;
const query = `
SELECT t.*, v.name as vendor_name
FROM transceivers t
LEFT JOIN vendors v ON t.vendor_id = v.id
${where}
${orderBy}
LIMIT ${limit} OFFSET ${offset}
`;
const countQuery = `SELECT COUNT(*) FROM transceivers ${where}`;
const [dataResult, countResult] = await Promise.all([
pool.query(query, values),
pool.query(countQuery, values),
]);
return {
data: dataResult.rows,
total: parseInt(countResult.rows[0].count),
limit,
offset,
};
}
export async function getTransceiverById(id: string) {
const result = await pool.query(
`SELECT t.*, v.name as vendor_name, s.name as standard_full_name
FROM transceivers t
LEFT JOIN vendors v ON t.vendor_id = v.id
LEFT JOIN standards s ON t.standard_id = s.id
WHERE t.id = $1 OR t.slug = $1`,
[id]
);
return result.rows[0] || null;
}
export async function searchSwitches(params: SearchParams) {
const conditions: string[] = [];
const values: any[] = [];
let idx = 1;
if (params.q) {
conditions.push(`sw.search_vector @@ plainto_tsquery('english', $${idx})`);
values.push(params.q);
idx++;
}
if (params.category) {
conditions.push(`sw.category = $${idx}`);
values.push(params.category);
idx++;
}
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
const limit = params.limit || 50;
const offset = params.offset || 0;
const query = `
SELECT sw.*, v.name as vendor_name
FROM switches sw
LEFT JOIN vendors v ON sw.vendor_id = v.id
${where}
ORDER BY sw.max_speed_gbps DESC NULLS LAST
LIMIT ${limit} OFFSET ${offset}
`;
const result = await pool.query(query, values);
return { data: result.rows, limit, offset };
}
export async function getSwitchById(id: string) {
const result = await pool.query(
`SELECT sw.*, v.name as vendor_name
FROM switches sw
LEFT JOIN vendors v ON sw.vendor_id = v.id
WHERE sw.id = $1`,
[id]
);
return result.rows[0] || null;
}
export async function getCompatibleTransceivers(switchId: string) {
const result = await pool.query(
`SELECT t.*, c.status, c.verified_by, c.notes as compat_notes
FROM compatibility c
JOIN transceivers t ON c.transceiver_id = t.id
WHERE c.switch_id = $1 AND c.status = 'compatible'
ORDER BY t.speed_gbps DESC`,
[switchId]
);
return result.rows;
}
export async function listVendors(type?: string) {
const query = type
? `SELECT * FROM vendors WHERE type = $1 ORDER BY name`
: `SELECT * FROM vendors ORDER BY name`;
const result = await pool.query(query, type ? [type] : []);
return result.rows;
}
export async function listStandards(speed?: string) {
const query = speed
? `SELECT * FROM standards WHERE speed = $1 ORDER BY year_ratified DESC`
: `SELECT * FROM standards ORDER BY year_ratified DESC`;
const result = await pool.query(query, speed ? [speed] : []);
return result.rows;
}
export async function getDbStats() {
const result = await pool.query(`
SELECT
(SELECT COUNT(*) FROM vendors) as vendor_count,
(SELECT COUNT(*) FROM standards) as standard_count,
(SELECT COUNT(*) FROM transceivers) as transceiver_count,
(SELECT COUNT(*) FROM switches) as switch_count,
(SELECT COUNT(*) FROM compatibility) as compatibility_count,
(SELECT COUNT(*) FROM breakouts) as breakout_count,
(SELECT COUNT(*) FROM knowledge_base) as kb_count,
(SELECT COUNT(*) FROM documents) as document_count,
(SELECT COUNT(*) FROM news_articles) as news_count
`);
return result.rows[0];
}

57
packages/api/src/index.ts Normal file
View File

@ -0,0 +1,57 @@
import express from "express";
import cors from "cors";
import helmet from "helmet";
import rateLimit from "express-rate-limit";
import { cfg } from "./config";
import { transceiverRouter } from "./routes/transceivers";
import { switchRouter } from "./routes/switches";
import { vendorRouter } from "./routes/vendors";
import { standardRouter } from "./routes/standards";
import { healthRouter } from "./routes/health";
const app = express();
// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
app.use(
rateLimit({
windowMs: 60 * 1000,
max: 200,
standardHeaders: true,
legacyHeaders: false,
})
);
// Routes
app.use("/api/transceivers", transceiverRouter);
app.use("/api/switches", switchRouter);
app.use("/api/vendors", vendorRouter);
app.use("/api/standards", standardRouter);
app.use("/api/health", healthRouter);
// Root
app.get("/", (_req, res) => {
res.json({
name: "Transceiver Intelligence Platform",
version: "0.1.0",
endpoints: [
"GET /api/transceivers?q=&form_factor=&speed=&category=&fiber_type=&wdm_type=&coherent=",
"GET /api/transceivers/:id",
"GET /api/switches?q=&category=",
"GET /api/switches/:id",
"GET /api/switches/:id/compatibility",
"GET /api/vendors?type=",
"GET /api/standards?speed=",
"GET /api/health",
],
});
});
// Start
app.listen(cfg.port, () => {
console.log(`\n TIP API running on http://localhost:${cfg.port}`);
console.log(` Environment: ${cfg.nodeEnv}`);
console.log(` Database: ${cfg.db.host}:${cfg.db.port}/${cfg.db.database}\n`);
});

View File

@ -0,0 +1,32 @@
import { Router, Request, Response } from "express";
import { getDbStats } from "../db/queries";
import { pool } from "../db/client";
export const healthRouter = Router();
// GET /api/health — Health check with DB stats
healthRouter.get("/", async (_req: Request, res: Response) => {
try {
const start = Date.now();
const stats = await getDbStats();
const latencyMs = Date.now() - start;
res.json({
success: true,
status: "healthy",
version: "0.1.0",
uptime: process.uptime(),
database: {
connected: true,
latency_ms: latencyMs,
stats,
},
});
} catch (err) {
res.status(503).json({
success: false,
status: "unhealthy",
database: { connected: false, error: String(err) },
});
}
});

View File

@ -0,0 +1,15 @@
import { Router, Request, Response } from "express";
import { listStandards } from "../db/queries";
export const standardRouter = Router();
// GET /api/standards — List all standards
standardRouter.get("/", async (req: Request, res: Response) => {
try {
const standards = await listStandards(req.query.speed ? String(req.query.speed) : undefined);
res.json({ success: true, data: standards, total: standards.length });
} catch (err) {
console.error("List standards error:", err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});

View File

@ -0,0 +1,46 @@
import { Router, Request, Response } from "express";
import { searchSwitches, getSwitchById, getCompatibleTransceivers } from "../db/queries";
export const switchRouter = Router();
// GET /api/switches — Search/list switches
switchRouter.get("/", async (req: Request, res: Response) => {
try {
const result = await searchSwitches({
q: String(req.query.q || ""),
category: req.query.category ? String(req.query.category) : undefined,
limit: req.query.limit ? parseInt(String(req.query.limit)) : 50,
offset: req.query.offset ? parseInt(String(req.query.offset)) : 0,
});
res.json({ success: true, ...result });
} catch (err) {
console.error("Search switches error:", err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});
// GET /api/switches/:id — Get single switch
switchRouter.get("/:id", async (req: Request, res: Response) => {
try {
const sw = await getSwitchById(String(req.params.id));
if (!sw) {
res.status(404).json({ success: false, error: "Switch not found" });
return;
}
res.json({ success: true, data: sw });
} catch (err) {
console.error("Get switch error:", err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});
// GET /api/switches/:id/compatibility — Compatible transceivers for a switch
switchRouter.get("/:id/compatibility", async (req: Request, res: Response) => {
try {
const transceivers = await getCompatibleTransceivers(String(req.params.id));
res.json({ success: true, data: transceivers, total: transceivers.length });
} catch (err) {
console.error("Get compatibility error:", err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});

View File

@ -0,0 +1,45 @@
import { Router, Request, Response } from "express";
import { searchTransceivers, getTransceiverById } from "../db/queries";
export const transceiverRouter = Router();
// GET /api/transceivers — Search/list transceivers
transceiverRouter.get("/", async (req: Request, res: Response) => {
try {
const q = (p: string) => req.query[p] ? String(req.query[p]) : undefined;
const result = await searchTransceivers({
q: q("q"),
form_factor: q("form_factor"),
speed: q("speed"),
speed_gbps: q("speed_gbps") ? parseFloat(q("speed_gbps")!) : undefined,
category: q("category"),
fiber_type: q("fiber_type"),
reach_min: q("reach_min") ? parseInt(q("reach_min")!) : undefined,
reach_max: q("reach_max") ? parseInt(q("reach_max")!) : undefined,
wdm_type: q("wdm_type"),
coherent: q("coherent") === "true" ? true : q("coherent") === "false" ? false : undefined,
market_status: q("market_status"),
limit: q("limit") ? parseInt(q("limit")!) : 50,
offset: q("offset") ? parseInt(q("offset")!) : 0,
});
res.json({ success: true, ...result });
} catch (err) {
console.error("Search transceivers error:", err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});
// GET /api/transceivers/:id — Get single transceiver
transceiverRouter.get("/:id", async (req: Request, res: Response) => {
try {
const transceiver = await getTransceiverById(String(req.params.id));
if (!transceiver) {
res.status(404).json({ success: false, error: "Transceiver not found" });
return;
}
res.json({ success: true, data: transceiver });
} catch (err) {
console.error("Get transceiver error:", err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});

View File

@ -0,0 +1,15 @@
import { Router, Request, Response } from "express";
import { listVendors } from "../db/queries";
export const vendorRouter = Router();
// GET /api/vendors — List all vendors
vendorRouter.get("/", async (req: Request, res: Response) => {
try {
const vendors = await listVendors(req.query.type ? String(req.query.type) : undefined);
res.json({ success: true, data: vendors, total: vendors.length });
} catch (err) {
console.error("List vendors error:", err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});

View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@ -0,0 +1,45 @@
{
"name": "@tip/core",
"version": "1.0.0",
"description": "Core optical transceiver database. 159 products, 42 IEEE/MSA standards, 16 form factors, 9 speed tiers.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build"
},
"license": "MIT",
"keywords": [
"transceiver",
"optics",
"sfp",
"qsfp",
"networking",
"fiber",
"ieee",
"telecom",
"osfp",
"qsfp-dd",
"optical",
"datacenter",
"100g",
"400g",
"800g"
],
"files": [
"dist",
"LICENSE",
"README.md"
],
"repository": {
"type": "git",
"url": "https://github.com/renefichtmueller/transceiver-db"
},
"author": "Rene Fichtmueller",
"engines": {
"node": ">=14"
},
"devDependencies": {
"typescript": "^5.9.3"
}
}

View File

@ -0,0 +1,20 @@
/**
* Breakout cable configurations.
* Maps high-speed ports to multiple lower-speed connections.
*/
import type { Breakout } from "./types";
export const breakouts: readonly Breakout[] = [
{ id: "40g-4x10g-sr", from: "40GBASE-SR4", to: "4x 10GBASE-SR", formFactor: "QSFP+ to 4x SFP+", description: "Break out one 40G QSFP+ port into four 10G SFP+ ports. Uses MPO-to-LC harness.", cableType: "Passive", maxLength: "5m (passive) / 100m (active)", speedPerLane: "10G" },
{ id: "40g-4x10g-aoc", from: "40GBASE-SR4", to: "4x 10GBASE-SR", formFactor: "QSFP+ to 4x SFP+ AOC", description: "Active optical breakout cable from 40G QSFP+ to four 10G SFP+.", cableType: "Active", maxLength: "1-30m", speedPerLane: "10G" },
{ id: "100g-4x25g-sr", from: "100GBASE-SR4", to: "4x 25GBASE-SR", formFactor: "QSFP28 to 4x SFP28", description: "Break out one 100G QSFP28 port into four 25G SFP28 ports.", cableType: "Passive", maxLength: "5m (passive) / 100m (active)", speedPerLane: "25G" },
{ id: "100g-4x25g-dac", from: "100G QSFP28", to: "4x 25G SFP28", formFactor: "QSFP28 to 4x SFP28 DAC", description: "Passive copper breakout DAC from 100G QSFP28 to four 25G SFP28.", cableType: "Passive", maxLength: "1-5m", speedPerLane: "25G" },
{ id: "400g-4x100g-dr", from: "400GBASE-DR4", to: "4x 100GBASE-DR", formFactor: "QSFP-DD to 4x QSFP28 (MPO-12 to 4x LC)", description: "Break out one 400G DR4 port into four 100G DR ports. Parallel SMF to duplex LC.", cableType: "Passive", maxLength: "500m (fiber reach)", speedPerLane: "100G" },
{ id: "400g-4x100g-fr", from: "400GBASE-XDR4", to: "4x 100GBASE-FR1", formFactor: "QSFP-DD to 4x QSFP28", description: "Break out one 400G XDR4 port into four 100G FR1 ports. 2km reach per lane.", cableType: "Passive", maxLength: "2km (fiber reach)", speedPerLane: "100G" },
{ id: "400g-4x100g-lr", from: "400G-PLR4", to: "4x 100GBASE-LR1", formFactor: "QSFP-DD to 4x QSFP28", description: "Break out one 400G PLR4 port into four 100G LR1 ports. 10km reach per lane.", cableType: "Passive", maxLength: "10km (fiber reach)", speedPerLane: "100G" },
{ id: "800g-2x400g-dr", from: "800GBASE-DR8", to: "2x 400GBASE-DR4", formFactor: "OSFP to 2x QSFP-DD", description: "Break out one 800G DR8 port into two 400G DR4 ports.", cableType: "Passive", maxLength: "500m (fiber reach)", speedPerLane: "100G" },
{ id: "800g-8x100g-dr", from: "800GBASE-DR8", to: "8x 100GBASE-DR", formFactor: "OSFP to 8x QSFP28", description: "Break out one 800G DR8 port into eight 100G DR ports.", cableType: "Passive", maxLength: "500m (fiber reach)", speedPerLane: "100G" },
{ id: "200g-4x50g-sr", from: "200GBASE-SR4", to: "4x 50GBASE-SR", formFactor: "QSFP56 to 4x SFP56", description: "Break out one 200G SR4 port into four 50G SR ports.", cableType: "Passive", maxLength: "100m (OM4)", speedPerLane: "50G" },
{ id: "200g-2x100g-dr", from: "200GBASE-DR4", to: "2x 100GBASE-DR", formFactor: "QSFP56 to 2x QSFP28", description: "Break out one 200G DR4 port into two 100G DR ports.", cableType: "Passive", maxLength: "500m (fiber reach)", speedPerLane: "50G" },
];

View File

@ -0,0 +1,217 @@
/**
* Transceiver product database 159 optical transceiver products.
* Covers GBIC, XFP, SFP, SFP+, SFP28, SFP56, QSFP+, QSFP28, QSFP56,
* QSFP-DD, OSFP, CFP/CFP2/CFP4, CFP2-DCO, CXP, DAC, and AOC.
*
* Speeds: 1G, 10G, 25G, 40G, 50G, 100G, 200G, 400G, 800G.
*
* Data sourced from publicly available IEEE standards, MSA specifications,
* vendor datasheets, and industry documentation.
*/
import type { Transceiver, VendorCompat } from "./types";
// Vendor shorthand helpers (internal)
const V_CISCO: VendorCompat = { vendor: "Cisco", partPattern: "SFP-*-*" };
const V_JUNIPER: VendorCompat = { vendor: "Juniper", partPattern: "EX-SFP-*" };
const V_ARISTA: VendorCompat = { vendor: "Arista", partPattern: "SFP-*-*" };
const V_HUAWEI: VendorCompat = { vendor: "Huawei", partPattern: "SFP-*-*" };
const V_NOKIA: VendorCompat = { vendor: "Nokia", partPattern: "3HE*" };
const V_HPE: VendorCompat = { vendor: "HPE/Aruba", partPattern: "J*" };
const V_DELL: VendorCompat = { vendor: "Dell", partPattern: "407-*" };
const V_EXTREME: VendorCompat = { vendor: "Extreme", partPattern: "10*" };
const V_NVIDIA: VendorCompat = { vendor: "NVIDIA", partPattern: "MMS4*|MMA1*" };
function allMajorVendors(): VendorCompat[] {
return [V_CISCO, V_JUNIPER, V_ARISTA, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME];
}
function dcVendors(): VendorCompat[] {
return [V_CISCO, V_JUNIPER, V_ARISTA, V_HUAWEI, V_DELL, V_EXTREME];
}
function carrierVendors(): VendorCompat[] {
return [V_CISCO, V_JUNIPER, V_HUAWEI, V_NOKIA];
}
/**
* Complete transceiver database all 159 products.
*/
export const transceivers: readonly Transceiver[] = [
// ── GBIC — 1G Legacy ──
{ id: "gbic-sx", standard: "1000BASE-SX", formFactor: "GBIC", speed: "1G", speedGbps: 1, reachMeters: 550, reachLabel: "550m", fiberType: "MMF", wavelengths: "850nm", connector: "SC", powerConsumptionW: 1.5, tempRange: "COM", category: "Legacy", priceTier: "Budget", useCase: "Legacy 1G multimode short-reach links in older switches using GBIC slots.", vendors: [{ vendor: "Cisco", partPattern: "WS-G5484" }, { vendor: "Juniper", partPattern: "SRX-SFP-1GE-SX" }], tags: ["1G", "legacy", "multimode", "short-reach", "GBIC"] },
{ id: "gbic-lx", standard: "1000BASE-LX", formFactor: "GBIC", speed: "1G", speedGbps: 1, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "SC", powerConsumptionW: 1.5, tempRange: "COM", category: "Legacy", priceTier: "Budget", useCase: "Legacy 1G single-mode links up to 10km in older GBIC-slot equipment.", vendors: [{ vendor: "Cisco", partPattern: "WS-G5486" }], tags: ["1G", "legacy", "singlemode", "10km", "GBIC"] },
// ── SFP — 1G ──
{ id: "sfp-sx", standard: "1000BASE-SX", formFactor: "SFP", speed: "1G", speedGbps: 1, reachMeters: 550, reachLabel: "550m", fiberType: "MMF", wavelengths: "850nm", connector: "LC", powerConsumptionW: 0.8, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "Standard 1G multimode short-reach for data center and campus backbone links.", vendors: [{ vendor: "Cisco", partPattern: "GLC-SX-MMD" }, { vendor: "Juniper", partPattern: "EX-SFP-1GE-SX" }, { vendor: "Arista", partPattern: "SFP-1G-SX" }, { vendor: "Huawei", partPattern: "SFP-GE-SX" }, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["1G", "multimode", "short-reach", "campus", "SFP"] },
{ id: "sfp-lx", standard: "1000BASE-LX", formFactor: "SFP", speed: "1G", speedGbps: 1, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 0.8, tempRange: "COM", category: "Metro", priceTier: "Budget", useCase: "1G single-mode for campus/metro links up to 10km. Most common 1G SFP.", vendors: [{ vendor: "Cisco", partPattern: "GLC-LH-SMD" }, { vendor: "Juniper", partPattern: "EX-SFP-1GE-LX" }, { vendor: "Arista", partPattern: "SFP-1G-LX" }, { vendor: "Huawei", partPattern: "SFP-GE-LX-SM1310" }, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["1G", "singlemode", "10km", "metro", "campus", "SFP"] },
{ id: "sfp-zx", standard: "1000BASE-ZX", formFactor: "SFP", speed: "1G", speedGbps: 1, reachMeters: 80000, reachLabel: "80km", fiberType: "SMF", wavelengths: "1550nm", connector: "LC", powerConsumptionW: 1.0, tempRange: "COM", category: "LongHaul", priceTier: "Standard", useCase: "1G long-haul single-mode up to 80km for inter-city or metro ring links.", vendors: [{ vendor: "Cisco", partPattern: "GLC-ZX-SMD" }, { vendor: "Juniper", partPattern: "SFP-1GE-LH" }, V_HUAWEI, V_NOKIA], tags: ["1G", "singlemode", "80km", "long-haul", "SFP"] },
{ id: "sfp-t", standard: "1000BASE-T", formFactor: "SFP", speed: "1G", speedGbps: 1, reachMeters: 100, reachLabel: "100m", fiberType: "Copper", wavelengths: "N/A", connector: "RJ45", powerConsumptionW: 1.0, tempRange: "COM", category: "Access", priceTier: "Budget", useCase: "1G copper SFP for short-reach RJ45 connections to servers or endpoints.", vendors: allMajorVendors(), tags: ["1G", "copper", "RJ45", "access", "SFP"] },
{ id: "sfp-bidi-1310-1550", standard: "1000BASE-BX10", formFactor: "SFP", speed: "1G", speedGbps: 1, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm TX / 1550nm RX", connector: "LC", powerConsumptionW: 0.8, tempRange: "COM", category: "BiDi", priceTier: "Standard", useCase: "Bidirectional 1G over a single fiber strand. Sold in pairs (upstream/downstream). Saves fiber.", vendors: [{ vendor: "Cisco", partPattern: "GLC-BX-U / GLC-BX-D" }, { vendor: "Juniper", partPattern: "SFP-1GE-BX" }, V_ARISTA, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["1G", "BiDi", "single-fiber", "singlemode", "10km", "SFP"] },
{ id: "sfp-cwdm-1470", standard: "SFP CWDM", formFactor: "SFP", speed: "1G", speedGbps: 1, reachMeters: 80000, reachLabel: "40-80km", fiberType: "SMF", wavelengths: "1470-1610nm (8 channels, 20nm spacing)", connector: "LC", powerConsumptionW: 1.0, tempRange: "COM", category: "CWDM", priceTier: "Standard", useCase: "1G CWDM for multiplexing up to 8 channels on a single fiber pair. Cost-effective WDM.", vendors: [{ vendor: "Cisco", partPattern: "CWDM-SFP-*" }, { vendor: "Juniper", partPattern: "SFP-1GE-CWDM*" }, V_HUAWEI, V_NOKIA], tags: ["1G", "CWDM", "WDM", "multiplexing", "SFP"] },
// ── XFP — 10G Legacy ──
{ id: "xfp-sr", standard: "10GBASE-SR", formFactor: "XFP", speed: "10G", speedGbps: 10, reachMeters: 300, reachLabel: "300m", fiberType: "MMF", wavelengths: "850nm", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "Legacy", priceTier: "Budget", useCase: "Legacy 10G short-reach for older platforms with XFP slots. Replaced by SFP+ in modern gear.", vendors: [{ vendor: "Cisco", partPattern: "XFP-10G-MM-SR" }, { vendor: "Juniper", partPattern: "XFP-10G-S" }], tags: ["10G", "legacy", "multimode", "short-reach", "XFP"] },
{ id: "xfp-lr", standard: "10GBASE-LR", formFactor: "XFP", speed: "10G", speedGbps: 10, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "Legacy", priceTier: "Budget", useCase: "Legacy 10G single-mode for metro links on older XFP-based routers.", vendors: [{ vendor: "Cisco", partPattern: "XFP-10GLR-OC192SR" }, { vendor: "Juniper", partPattern: "XFP-10G-L-OC192-SR1" }], tags: ["10G", "legacy", "singlemode", "10km", "XFP"] },
{ id: "xfp-er", standard: "10GBASE-ER", formFactor: "XFP", speed: "10G", speedGbps: 10, reachMeters: 40000, reachLabel: "40km", fiberType: "SMF", wavelengths: "1550nm", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "Legacy", priceTier: "Standard", useCase: "Legacy 10G extended reach for metro/regional links on XFP platforms.", vendors: [{ vendor: "Cisco", partPattern: "XFP-10GER-OC192IR" }, { vendor: "Juniper", partPattern: "XFP-10GE-ER" }], tags: ["10G", "legacy", "singlemode", "40km", "XFP", "extended-reach"] },
// ── SFP+ — 10G ──
{ id: "sfpp-sr", standard: "10GBASE-SR", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 300, reachLabel: "300m (OM3) / 400m (OM4)", fiberType: "MMF", wavelengths: "850nm", connector: "LC", powerConsumptionW: 1.0, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "The workhorse of 10G data center connectivity. Short-reach multimode for server-to-switch links.", vendors: [{ vendor: "Cisco", partPattern: "SFP-10G-SR" }, { vendor: "Juniper", partPattern: "EX-SFP-10GE-SR" }, { vendor: "Arista", partPattern: "SFP-10G-SR" }, { vendor: "Huawei", partPattern: "SFP-10G-USR" }, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["10G", "multimode", "short-reach", "data-center", "server", "SFP+"] },
{ id: "sfpp-lr", standard: "10GBASE-LR", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 1.0, tempRange: "COM", category: "Metro", priceTier: "Budget", useCase: "10G single-mode for campus backbone and metro links up to 10km.", vendors: [{ vendor: "Cisco", partPattern: "SFP-10G-LR" }, { vendor: "Juniper", partPattern: "EX-SFP-10GE-LR" }, { vendor: "Arista", partPattern: "SFP-10G-LR" }, { vendor: "Huawei", partPattern: "SFP-10G-LR" }, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["10G", "singlemode", "10km", "metro", "campus", "SFP+"] },
{ id: "sfpp-er", standard: "10GBASE-ER", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 40000, reachLabel: "40km", fiberType: "SMF", wavelengths: "1550nm", connector: "LC", powerConsumptionW: 1.5, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "10G extended reach for metro rings and inter-building links up to 40km.", vendors: [{ vendor: "Cisco", partPattern: "SFP-10G-ER" }, { vendor: "Juniper", partPattern: "EX-SFP-10GE-ER" }, { vendor: "Arista", partPattern: "SFP-10G-ER" }, V_HUAWEI, V_NOKIA], tags: ["10G", "singlemode", "40km", "metro", "extended-reach", "SFP+"] },
{ id: "sfpp-zr", standard: "10GBASE-ZR", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 80000, reachLabel: "80km", fiberType: "SMF", wavelengths: "1550nm", connector: "LC", powerConsumptionW: 1.5, tempRange: "COM", category: "LongHaul", priceTier: "Standard", useCase: "10G long-reach for inter-city metro and regional network links up to 80km.", vendors: [{ vendor: "Cisco", partPattern: "SFP-10G-ZR" }, { vendor: "Juniper", partPattern: "SFP-10GE-ZR" }, V_HUAWEI, V_NOKIA], tags: ["10G", "singlemode", "80km", "long-haul", "SFP+"] },
{ id: "sfpp-bidi-10", standard: "10GBASE-BX", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1270nm TX / 1330nm RX", connector: "LC", powerConsumptionW: 1.0, tempRange: "COM", category: "BiDi", priceTier: "Standard", useCase: "Bidirectional 10G over single fiber. Pairs required (upstream/downstream). Halves fiber count.", vendors: [{ vendor: "Cisco", partPattern: "SFP-10G-BXU-I / SFP-10G-BXD-I" }, { vendor: "Juniper", partPattern: "SFP-10GE-BX" }, V_ARISTA, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["10G", "BiDi", "single-fiber", "singlemode", "10km", "SFP+"] },
{ id: "sfpp-cwdm", standard: "10G SFP+ CWDM", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 80000, reachLabel: "40-80km", fiberType: "SMF", wavelengths: "1270-1610nm (18 channels, 20nm spacing)", connector: "LC", powerConsumptionW: 1.2, tempRange: "COM", category: "CWDM", priceTier: "Standard", useCase: "10G CWDM SFP+ for WDM multiplexing. Up to 18 channels over a single fiber pair.", vendors: [{ vendor: "Cisco", partPattern: "CWDM-SFP10G-*" }, { vendor: "Juniper", partPattern: "SFP-10GE-CWDM*" }, V_HUAWEI, V_NOKIA], tags: ["10G", "CWDM", "WDM", "multiplexing", "SFP+"] },
{ id: "sfpp-dwdm", standard: "10G SFP+ DWDM", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 80000, reachLabel: "40-80km", fiberType: "SMF", wavelengths: "C-band (1528-1565nm, 80+ channels, 100GHz/50GHz spacing)", connector: "LC", powerConsumptionW: 1.5, tempRange: "COM", category: "DWDM", priceTier: "Premium", useCase: "10G DWDM for high-density wavelength multiplexing. 80+ channels on C-band. Fixed or tunable.", vendors: [{ vendor: "Cisco", partPattern: "DWDM-SFP10G-*" }, { vendor: "Juniper", partPattern: "SFP-10GE-DWDM*" }, V_HUAWEI, V_NOKIA], tags: ["10G", "DWDM", "WDM", "C-band", "tunable", "SFP+"] },
{ id: "sfpp-t", standard: "10GBASE-T", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 30, reachLabel: "30m", fiberType: "Copper", wavelengths: "N/A", connector: "RJ45", powerConsumptionW: 2.5, tempRange: "COM", category: "Access", priceTier: "Budget", useCase: "10G copper SFP+ for short-reach RJ45 connections. Higher power than fiber variants.", vendors: allMajorVendors(), tags: ["10G", "copper", "RJ45", "access", "SFP+"] },
{ id: "sfpp-usr", standard: "10GBASE-USR", ieeeReference: "SFF-8431", formFactor: "SFP+", speed: "10G", speedGbps: 10, lanes: 1, laneRate: "10.3125 Gbaud", modulation: "NRZ", reachMeters: 100, reachLabel: "10-100m (OM3/OM4)", fiberType: "MMF", wavelengths: "850nm", connector: "LC", powerConsumptionW: 1.0, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "Ultra short-reach 10G for within-rack and adjacent-rack connections.", vendors: [{ vendor: "Cisco", partPattern: "SFP-10G-SR" }, { vendor: "Huawei", partPattern: "SFP-10G-USR" }], tags: ["10G", "multimode", "ultra-short-reach", "data-center", "SFP+"], generation: "Gen1 NRZ", marketStatus: "Mainstream" },
{ id: "sfpp-lrm", standard: "10GBASE-LRM", ieeeReference: "IEEE 802.3aq", formFactor: "SFP+", speed: "10G", speedGbps: 10, lanes: 1, laneRate: "10.3125 Gbaud", modulation: "NRZ", reachMeters: 220, reachLabel: "220m (FDDI/OM1 legacy MMF)", fiberType: "MMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 1.5, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "10G over legacy multimode fiber (OM1/OM2). Uses 1310nm to achieve 220m on older fiber.", vendors: [{ vendor: "Cisco", partPattern: "SFP-10G-LRM" }, { vendor: "Juniper", partPattern: "EX-SFP-10GE-LRM" }], tags: ["10G", "multimode", "legacy-fiber", "LRM", "SFP+"], generation: "Gen1 NRZ", marketStatus: "Legacy", yearIntroduced: 2006 },
// ── SFP28 — 25G ──
{ id: "sfp28-sr", standard: "25GBASE-SR", ieeeReference: "IEEE 802.3by", formFactor: "SFP28", speed: "25G", speedGbps: 25, lanes: 1, laneRate: "25.78125 Gbaud", modulation: "NRZ", reachMeters: 100, reachLabel: "70m (OM3) / 100m (OM4)", fiberType: "MMF", wavelengths: "850nm", connector: "LC", powerConsumptionW: 1.0, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "Standard 25G data center server access. Replaced 10G SFP+ in hyperscale deployments.", vendors: [{ vendor: "Cisco", partPattern: "SFP-25G-SR-S" }, { vendor: "Juniper", partPattern: "SFP-25G-SR" }, { vendor: "Arista", partPattern: "SFP-25G-SR" }, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["25G", "multimode", "short-reach", "data-center", "server", "SFP28"], generation: "Gen1 NRZ", marketStatus: "Mainstream", yearIntroduced: 2016 },
{ id: "sfp28-lr", standard: "25GBASE-LR", ieeeReference: "IEEE 802.3cc", formFactor: "SFP28", speed: "25G", speedGbps: 25, lanes: 1, laneRate: "25.78125 Gbaud", modulation: "NRZ", reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 1.0, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "25G single-mode for campus backbone and 5G fronthaul links up to 10km.", vendors: [{ vendor: "Cisco", partPattern: "SFP-25G-LR-S" }, { vendor: "Juniper", partPattern: "SFP-25G-LR" }, { vendor: "Arista", partPattern: "SFP-25G-LR" }, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["25G", "singlemode", "10km", "metro", "5G-fronthaul", "SFP28"], generation: "Gen1 NRZ", marketStatus: "Growth", yearIntroduced: 2017 },
{ id: "sfp28-er", standard: "25GBASE-ER", ieeeReference: "IEEE 802.3cc", formFactor: "SFP28", speed: "25G", speedGbps: 25, lanes: 1, laneRate: "25.78125 Gbaud", modulation: "NRZ", reachMeters: 30000, reachLabel: "30km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 1.5, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "25G extended reach for metro and 5G midhaul applications.", vendors: [{ vendor: "Cisco", partPattern: "SFP-25G-ER-S" }, { vendor: "Juniper", partPattern: "SFP-25G-ER" }], tags: ["25G", "singlemode", "30km", "metro", "5G-midhaul", "SFP28"], generation: "Gen1 NRZ", marketStatus: "Growth", yearIntroduced: 2017 },
{ id: "sfp28-bidi", standard: "25GBASE-BX", formFactor: "SFP28", speed: "25G", speedGbps: 25, lanes: 1, modulation: "NRZ", reachMeters: 10000, reachLabel: "10-30km", fiberType: "SMF", wavelengths: "1270nm TX / 1330nm RX (or reverse)", connector: "LC", powerConsumptionW: 1.2, tempRange: "COM", category: "BiDi", priceTier: "Standard", useCase: "Bidirectional 25G over single fiber. Sold in pairs. Ideal for 5G fronthaul with limited fiber.", vendors: [{ vendor: "Cisco", partPattern: "SFP-25G-BX*" }, V_JUNIPER, V_ARISTA, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["25G", "BiDi", "single-fiber", "singlemode", "5G", "SFP28"], generation: "Gen1 NRZ", marketStatus: "Growth" },
{ id: "sfp28-cwdm", standard: "25G SFP28 CWDM", formFactor: "SFP28", speed: "25G", speedGbps: 25, lanes: 1, modulation: "NRZ", reachMeters: 40000, reachLabel: "10-40km", fiberType: "SMF", wavelengths: "1270-1610nm (CWDM channels, 20nm spacing)", connector: "LC", powerConsumptionW: 1.5, tempRange: "COM", category: "CWDM", priceTier: "Standard", useCase: "25G CWDM for wavelength multiplexing. Enables multiple 25G channels over a single fiber pair for 5G midhaul aggregation.", vendors: carrierVendors(), tags: ["25G", "CWDM", "WDM", "5G", "aggregation", "SFP28"], generation: "Gen1 NRZ", marketStatus: "Growth" },
{ id: "sfp28-lr-ind", standard: "25GBASE-LR Industrial", ieeeReference: "IEEE 802.3cc", formFactor: "SFP28", speed: "25G", speedGbps: 25, lanes: 1, modulation: "NRZ", reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 1.2, tempRange: "IND", category: "5G", priceTier: "Premium", useCase: "Industrial temperature 25G SFP28 for 5G fronthaul in outdoor cabinets. Rated -40C to +85C.", vendors: carrierVendors(), tags: ["25G", "industrial", "outdoor", "5G", "fronthaul", "SFP28"], generation: "Gen1 NRZ", marketStatus: "Growth" },
// ── SFP56 — 50G ──
{ id: "sfp56-sr", standard: "50GBASE-SR", ieeeReference: "IEEE 802.3cd", formFactor: "SFP56", speed: "50G", speedGbps: 50, lanes: 1, laneRate: "26.5625 Gbaud", modulation: "PAM4", reachMeters: 100, reachLabel: "70m (OM3) / 100m (OM4)", fiberType: "MMF", wavelengths: "850nm", connector: "LC", powerConsumptionW: 1.5, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "50G single-lane for emerging high-density server access. Used in 200G-SR4 breakout scenarios.", vendors: dcVendors(), tags: ["50G", "multimode", "short-reach", "PAM4", "SFP56", "data-center"], generation: "Gen2 PAM4", marketStatus: "Growth", yearIntroduced: 2019 },
{ id: "sfp56-lr", standard: "50GBASE-LR", formFactor: "SFP56", speed: "50G", speedGbps: 50, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 1.5, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "50G single-mode for high-bandwidth campus and metro links.", vendors: dcVendors(), tags: ["50G", "PAM4", "singlemode", "10km", "SFP56"] },
// ── QSFP+ — 40G ──
{ id: "qsfpp-sr4", standard: "40GBASE-SR4", formFactor: "QSFP+", speed: "40G", speedGbps: 40, reachMeters: 150, reachLabel: "100m (OM3) / 150m (OM4)", fiberType: "MMF", wavelengths: "850nm", connector: "MPO-12", powerConsumptionW: 2.5, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "Standard 40G multimode for data center spine-leaf links using MPO cabling.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-40G-SR4" }, { vendor: "Juniper", partPattern: "QSFP-40G-SR4" }, { vendor: "Arista", partPattern: "QSFP-40G-SR4" }, { vendor: "Huawei", partPattern: "QSFP-40G-SR4" }, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["40G", "multimode", "short-reach", "data-center", "MPO", "QSFP+"] },
{ id: "qsfpp-lr4", standard: "40GBASE-LR4", formFactor: "QSFP+", speed: "40G", speedGbps: 40, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1271/1291/1311/1331nm (4 CWDM lanes)", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "40G single-mode for campus backbone and DCI links up to 10km using LC duplex fiber.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-40G-LR4" }, { vendor: "Juniper", partPattern: "QSFP-40G-LR4" }, { vendor: "Arista", partPattern: "QSFP-40G-LR4" }, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["40G", "singlemode", "10km", "metro", "CWDM", "DCI", "QSFP+"] },
{ id: "qsfpp-er4", standard: "40GBASE-ER4", formFactor: "QSFP+", speed: "40G", speedGbps: 40, reachMeters: 40000, reachLabel: "40km", fiberType: "SMF", wavelengths: "1271/1291/1311/1331nm (4 CWDM lanes)", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "Metro", priceTier: "Premium", useCase: "40G extended reach for metro and DCI links up to 40km.", vendors: carrierVendors(), tags: ["40G", "singlemode", "40km", "metro", "extended-reach", "QSFP+"] },
{ id: "qsfpp-sr-bidi", standard: "40GBASE-SR-BiDi", formFactor: "QSFP+", speed: "40G", speedGbps: 40, reachMeters: 150, reachLabel: "100m (OM3) / 150m (OM4)", fiberType: "MMF", wavelengths: "832nm / 918nm BiDi", connector: "LC", powerConsumptionW: 2.5, tempRange: "COM", category: "BiDi", priceTier: "Standard", useCase: "40G BiDi multimode over LC duplex. Enables 40G upgrade reusing existing 10G LC cabling.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-40G-SR-BD" }, { vendor: "Arista", partPattern: "QSFP-40G-SRBD" }, V_JUNIPER, V_HUAWEI, V_DELL, V_EXTREME], tags: ["40G", "BiDi", "multimode", "LC-reuse", "data-center", "QSFP+"] },
// ── QSFP28 — 100G ──
{ id: "qsfp28-sr4", standard: "100GBASE-SR4", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 100, reachLabel: "70m (OM3) / 100m (OM4)", fiberType: "MMF", wavelengths: "850nm", connector: "MPO-12", powerConsumptionW: 3.5, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "Standard 100G multimode for data center spine-leaf. The most deployed 100G optic. Breakout to 4x25G possible.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-100G-SR4-S" }, { vendor: "Juniper", partPattern: "QSFP-100G-SR4" }, { vendor: "Arista", partPattern: "QSFP-100G-SR4" }, { vendor: "Huawei", partPattern: "QSFP-100G-SR4" }, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["100G", "multimode", "short-reach", "data-center", "MPO", "breakout", "QSFP28", "IXP"] },
{ id: "qsfp28-sr1", standard: "100GBASE-SR1", ieeeReference: "IEEE 802.3cd", formFactor: "QSFP28", speed: "100G", speedGbps: 100, lanes: 1, laneRate: "53.125 Gbaud", modulation: "PAM4", reachMeters: 100, reachLabel: "70m (OM3) / 100m (OM4)", fiberType: "MMF", wavelengths: "850nm", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "100G single-lane over duplex LC MMF. Uses PAM4 modulation. Simpler and cheaper than SR4.", vendors: dcVendors(), tags: ["100G", "multimode", "short-reach", "PAM4", "duplex-LC", "QSFP28"], generation: "Gen2 PAM4", marketStatus: "Growth", yearIntroduced: 2018 },
{ id: "qsfp28-sr2", standard: "100GBASE-SR2", ieeeReference: "100G Lambda MSA", formFactor: "QSFP28", speed: "100G", speedGbps: 100, lanes: 2, laneRate: "26.5625 Gbaud", modulation: "PAM4", reachMeters: 100, reachLabel: "100m (OM4)", fiberType: "MMF", wavelengths: "850nm", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "100G SR2 uses 2x50G PAM4 over duplex LC. No MPO needed. Popular in DCs moving to duplex fiber.", vendors: dcVendors(), tags: ["100G", "multimode", "duplex-LC", "short-reach", "data-center", "PAM4", "QSFP28"], generation: "Gen2 PAM4", marketStatus: "Mainstream", yearIntroduced: 2018 },
{ id: "qsfp28-dr1", standard: "100GBASE-DR1", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 500, reachLabel: "500m", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "100G single-lane single-mode for intra-campus data center. Uses PAM4 modulation.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-100G-DR-S" }, { vendor: "Arista", partPattern: "QSFP-100G-DR" }, V_JUNIPER, V_HUAWEI, V_DELL, V_EXTREME], tags: ["100G", "singlemode", "500m", "data-center", "PAM4", "single-lane", "QSFP28"] },
{ id: "qsfp28-fr1", standard: "100GBASE-FR1", ieeeReference: "IEEE 802.3cu", formFactor: "QSFP28", speed: "100G", speedGbps: 100, lanes: 1, laneRate: "53.125 Gbaud", modulation: "PAM4", reachMeters: 2000, reachLabel: "2km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "DCI", priceTier: "Standard", useCase: "100G single-lambda for 2km campus DCI. Interworks with 400GBASE-FR4 breakout.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-100G-FR-S" }, { vendor: "Juniper", partPattern: "QSFP-100G-FR" }, { vendor: "Arista", partPattern: "QSFP-100G-FR" }, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["100G", "singlemode", "2km", "DCI", "campus", "PAM4", "QSFP28"], generation: "Gen2 PAM4", marketStatus: "Growth", yearIntroduced: 2021 },
{ id: "qsfp28-lr1", standard: "100GBASE-LR1", ieeeReference: "IEEE 802.3cu", formFactor: "QSFP28", speed: "100G", speedGbps: 100, lanes: 1, laneRate: "53.125 Gbaud", modulation: "PAM4", reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "IXP", priceTier: "Standard", useCase: "100G single-lambda for 10km. The new IXP standard (100G LR-1) replacing LR4 at major exchanges.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-100G-LR1-S" }, { vendor: "Juniper", partPattern: "QSFP-100G-LR1" }, { vendor: "Nokia", partPattern: "3HE*" }, V_ARISTA, V_HUAWEI, V_HPE, V_DELL, V_EXTREME], tags: ["100G", "singlemode", "10km", "IXP", "LR-1", "PAM4", "QSFP28"], generation: "Gen2 PAM4", marketStatus: "Growth", yearIntroduced: 2021 },
{ id: "qsfp28-lr4", standard: "100GBASE-LR4", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm (4 LAN-WDM lanes)", connector: "LC", powerConsumptionW: 4.5, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "100G single-mode for metro DCI and campus backbone up to 10km. Uses 4x25G LAN-WDM lanes.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-100G-LR4-S" }, { vendor: "Juniper", partPattern: "QSFP-100G-LR4" }, { vendor: "Arista", partPattern: "QSFP-100G-LR4" }, { vendor: "Huawei", partPattern: "QSFP-100G-LR4" }, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["100G", "singlemode", "10km", "metro", "DCI", "LAN-WDM", "QSFP28", "IXP"] },
{ id: "qsfp28-cwdm4", standard: "100G CWDM4", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 2000, reachLabel: "2km", fiberType: "SMF", wavelengths: "1271/1291/1311/1331nm (4 CWDM lanes)", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "DCI", priceTier: "Budget", useCase: "100G CWDM4 for short-reach DCI up to 2km. Lower cost than LR4 for inter-building links.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-100G-CWDM4-S" }, { vendor: "Juniper", partPattern: "QSFP-100G-CWDM4" }, { vendor: "Arista", partPattern: "QSFP-100G-CWDM4" }, V_HUAWEI, V_DELL, V_EXTREME], tags: ["100G", "CWDM4", "singlemode", "2km", "DCI", "cost-effective", "QSFP28"] },
{ id: "qsfp28-er4", standard: "100GBASE-ER4", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 40000, reachLabel: "40km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm (4 LAN-WDM lanes)", connector: "LC", powerConsumptionW: 4.5, tempRange: "COM", category: "Metro", priceTier: "Premium", useCase: "100G extended reach for metro rings and longer DCI links up to 40km.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-100G-ER4L" }, { vendor: "Juniper", partPattern: "QSFP-100G-ER4" }, V_HUAWEI, V_NOKIA], tags: ["100G", "singlemode", "40km", "metro", "extended-reach", "QSFP28"] },
{ id: "qsfp28-zr4", standard: "100GBASE-ZR4", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 80000, reachLabel: "80km", fiberType: "SMF", wavelengths: "1296/1300/1305/1309nm (4 LAN-WDM lanes)", connector: "LC", powerConsumptionW: 5.0, tempRange: "COM", category: "LongHaul", priceTier: "Premium", useCase: "100G long-haul for regional networks up to 80km without amplification.", vendors: carrierVendors(), tags: ["100G", "singlemode", "80km", "long-haul", "regional", "QSFP28"] },
{ id: "qsfp28-lr8", standard: "100G LR8", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "8 CWDM wavelengths (1271-1411nm)", connector: "LC", powerConsumptionW: 4.0, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "100G using 8 CWDM lanes at 12.5G each. For platforms that do not support 25G-per-lane optics.", vendors: carrierVendors(), tags: ["100G", "singlemode", "10km", "CWDM", "8-lane", "legacy-platform", "QSFP28"] },
{ id: "qsfp28-psm4", standard: "100G PSM4", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 500, reachLabel: "500m", fiberType: "SMF", wavelengths: "1310nm (4 parallel fibers)", connector: "MPO-12", powerConsumptionW: 3.5, tempRange: "COM", category: "DataCenter", priceTier: "Budget", useCase: "100G parallel single-mode for intra-DC links up to 500m. MPO-12 connector.", vendors: dcVendors(), tags: ["100G", "singlemode", "500m", "parallel", "MPO", "data-center", "QSFP28"] },
{ id: "qsfp28-sr-bidi", standard: "100GBASE-SR BiDi", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 100, reachLabel: "70m (OM3) / 100m (OM4)", fiberType: "MMF", wavelengths: "832nm / 918nm BiDi (PAM4)", connector: "LC", powerConsumptionW: 3.5, tempRange: "COM", category: "BiDi", priceTier: "Standard", useCase: "100G BiDi over LC duplex multimode. Enables 100G upgrade reusing existing 10G/25G LC MMF cabling.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-100G-SR1.2" }, { vendor: "Arista", partPattern: "QSFP-100G-SRBD" }], tags: ["100G", "BiDi", "multimode", "LC-reuse", "data-center", "QSFP28"] },
{ id: "qsfp28-dwdm", standard: "100G QSFP28 DWDM Tunable", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 80000, reachLabel: "80km", fiberType: "SMF", wavelengths: "C-band tunable (1528-1565nm)", connector: "LC", powerConsumptionW: 5.0, tempRange: "COM", category: "DWDM", priceTier: "Premium", useCase: "100G DWDM tunable for metro/regional WDM networks. Integrates with ROADM systems.", vendors: carrierVendors(), tags: ["100G", "DWDM", "tunable", "C-band", "metro", "ROADM", "QSFP28"] },
{ id: "qsfp28-zr-coherent", standard: "100GBASE-ZR (Coherent)", ieeeReference: "IEEE 802.3ct", formFactor: "QSFP28", speed: "100G", speedGbps: 100, lanes: 1, laneRate: "~64 Gbaud", modulation: "DP-QPSK (coherent)", reachMeters: 80000, reachLabel: "80km+ (DWDM amplified)", fiberType: "SMF", wavelengths: "C-band (tunable, DWDM)", connector: "LC", powerConsumptionW: 5.0, tempRange: "COM", category: "Coherent", priceTier: "Premium", useCase: "100G coherent pluggable for DWDM long-haul. Phase/amplitude modulation with coherent detection.", vendors: carrierVendors(), tags: ["100G", "coherent", "DWDM", "long-haul", "C-band", "tunable", "QSFP28"], generation: "Coherent", marketStatus: "Mainstream", yearIntroduced: 2021 },
// ── CXP — 100G/120G Legacy ──
{ id: "cxp-sr10", standard: "100GBASE-SR10", formFactor: "CXP", speed: "100G", speedGbps: 100, reachMeters: 150, reachLabel: "100m (OM3) / 150m (OM4)", fiberType: "MMF", wavelengths: "850nm (10 lanes x 10G)", connector: "MPO-24", powerConsumptionW: 6.0, tempRange: "COM", category: "Legacy", priceTier: "Standard", useCase: "Legacy 100G/120G CXP form factor for InfiniBand and early 100G Ethernet.", vendors: [{ vendor: "Cisco", partPattern: "CXP-100G-SR10" }], tags: ["100G", "120G", "legacy", "InfiniBand", "CXP", "multimode"] },
// ── CFP / CFP2 / CFP4 — 100G ──
{ id: "cfp-lr4", standard: "100GBASE-LR4 CFP", formFactor: "CFP", speed: "100G", speedGbps: 100, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm", connector: "LC", powerConsumptionW: 24.0, tempRange: "COM", category: "Legacy", priceTier: "Standard", useCase: "First-generation 100G CFP form factor. Large footprint, being replaced by CFP2/QSFP28.", vendors: carrierVendors(), tags: ["100G", "singlemode", "10km", "legacy", "CFP"] },
{ id: "cfp2-lr4", standard: "100GBASE-LR4 CFP2", formFactor: "CFP2", speed: "100G", speedGbps: 100, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm", connector: "LC", powerConsumptionW: 9.0, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "100G CFP2 for carrier/service provider routers with CFP2 slots.", vendors: carrierVendors(), tags: ["100G", "singlemode", "10km", "carrier", "CFP2"] },
{ id: "cfp4-lr4", standard: "100GBASE-LR4 CFP4", formFactor: "CFP4", speed: "100G", speedGbps: 100, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm", connector: "LC", powerConsumptionW: 6.0, tempRange: "COM", category: "Metro", priceTier: "Standard", useCase: "Compact 100G CFP4 for high-density router line cards.", vendors: carrierVendors(), tags: ["100G", "singlemode", "10km", "carrier", "high-density", "CFP4"] },
// ── CFP2-DCO — Coherent 100G-400G ──
{ id: "cfp2dco-100g", standard: "100G CFP2-DCO Coherent", formFactor: "CFP2-DCO", speed: "100G", speedGbps: 100, reachMeters: 2000000, reachLabel: "2000km+", fiberType: "SMF", wavelengths: "C-band tunable (1528-1565nm)", connector: "LC", powerConsumptionW: 18.0, tempRange: "COM", category: "Coherent", priceTier: "Premium", useCase: "100G coherent for long-haul and submarine links. DP-QPSK modulation.", vendors: carrierVendors(), tags: ["100G", "coherent", "DP-QPSK", "long-haul", "submarine", "DWDM", "tunable", "CFP2-DCO"] },
{ id: "cfp2dco-200g", standard: "200G CFP2-DCO Coherent", formFactor: "CFP2-DCO", speed: "200G", speedGbps: 200, reachMeters: 1000000, reachLabel: "1000km+", fiberType: "SMF", wavelengths: "C-band tunable (1528-1565nm)", connector: "LC", powerConsumptionW: 20.0, tempRange: "COM", category: "Coherent", priceTier: "Premium", useCase: "200G coherent for long-haul transport. DP-16QAM or DP-QPSK modulation.", vendors: carrierVendors(), tags: ["200G", "coherent", "DP-16QAM", "long-haul", "DWDM", "tunable", "CFP2-DCO"] },
{ id: "cfp2dco-400g", standard: "400G CFP2-DCO Coherent", formFactor: "CFP2-DCO", speed: "400G", speedGbps: 400, reachMeters: 600000, reachLabel: "600km+", fiberType: "SMF", wavelengths: "C-band tunable (1528-1565nm)", connector: "LC", powerConsumptionW: 22.0, tempRange: "COM", category: "Coherent", priceTier: "Premium", useCase: "400G coherent for long-haul and DCI. DP-16QAM modulation at high baud rate.", vendors: carrierVendors(), tags: ["400G", "coherent", "DP-16QAM", "long-haul", "DCI", "DWDM", "tunable", "CFP2-DCO"] },
// ── QSFP56 — 200G ──
{ id: "qsfp56-sr4", standard: "200GBASE-SR4", ieeeReference: "IEEE 802.3cd", formFactor: "QSFP56", speed: "200G", speedGbps: 200, lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", reachMeters: 100, reachLabel: "70m (OM3) / 100m (OM4)", fiberType: "MMF", wavelengths: "850nm (4x50G PAM4)", connector: "MPO-12", powerConsumptionW: 7.0, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "200G multimode for data center links. 4 lanes at 50G PAM4 each.", vendors: [{ vendor: "Cisco", partPattern: "QSFP-200G-SR4" }, { vendor: "Arista", partPattern: "QSFP-200G-SR4" }, V_JUNIPER, V_HUAWEI, V_DELL, V_EXTREME], tags: ["200G", "multimode", "PAM4", "data-center", "QSFP56"], generation: "Gen2 PAM4", marketStatus: "Mainstream", yearIntroduced: 2019, breakoutCapable: true, breakoutTo: "4x50GBASE-SR or 2x100GBASE-SR2" },
{ id: "qsfp56-dr4", standard: "200GBASE-DR4", ieeeReference: "IEEE 802.3cd", formFactor: "QSFP56", speed: "200G", speedGbps: 200, lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", reachMeters: 500, reachLabel: "500m", fiberType: "SMF", wavelengths: "1310nm (4x50G PAM4 parallel)", connector: "MPO-12", powerConsumptionW: 7.0, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "200G parallel single-mode for intra-DC links up to 500m. Breakout to 4x50G possible.", vendors: dcVendors(), tags: ["200G", "singlemode", "500m", "parallel", "breakout", "data-center", "QSFP56"], generation: "Gen2 PAM4", marketStatus: "Mainstream", yearIntroduced: 2019, breakoutCapable: true, breakoutTo: "2x100GBASE-DR or 4x50G" },
{ id: "qsfp56-fr4", standard: "200GBASE-FR4", ieeeReference: "IEEE 802.3cu", formFactor: "QSFP56", speed: "200G", speedGbps: 200, lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", reachMeters: 2000, reachLabel: "2km", fiberType: "SMF", wavelengths: "1271/1291/1311/1331nm (CWDM4)", connector: "LC", powerConsumptionW: 7.0, tempRange: "COM", category: "DCI", priceTier: "Standard", useCase: "200G for short-reach DCI up to 2km. Uses CWDM wavelengths over LC duplex fiber.", vendors: dcVendors(), tags: ["200G", "CWDM", "singlemode", "2km", "DCI", "QSFP56"], generation: "Gen2 PAM4", marketStatus: "Mainstream", yearIntroduced: 2021 },
{ id: "qsfp56-lr4", standard: "200GBASE-LR4", ieeeReference: "IEEE 802.3cu", formFactor: "QSFP56", speed: "200G", speedGbps: 200, lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm (4 LAN-WDM lanes x 50G)", connector: "LC", powerConsumptionW: 8.0, tempRange: "COM", category: "Metro", priceTier: "Premium", useCase: "200G metro DCI links up to 10km using LAN-WDM.", vendors: carrierVendors(), tags: ["200G", "singlemode", "10km", "metro", "DCI", "LAN-WDM", "QSFP56"], generation: "Gen2 PAM4", marketStatus: "Mainstream", yearIntroduced: 2021 },
// ── QSFP-DD — 400G ──
{ id: "qsfpdd-sr8", standard: "400GBASE-SR8", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 100, reachLabel: "70m (OM3) / 100m (OM4)", fiberType: "MMF", wavelengths: "850nm (8x50G PAM4)", connector: "MPO-16", powerConsumptionW: 12.0, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "400G multimode for short-reach data center links. 8 lanes at 50G PAM4.", vendors: [{ vendor: "Cisco", partPattern: "QDD-400G-SR8" }, { vendor: "Arista", partPattern: "QDD-400G-SR8" }, V_JUNIPER, V_HUAWEI, V_DELL, V_EXTREME], tags: ["400G", "multimode", "short-reach", "data-center", "MPO-16", "PAM4", "QSFP-DD"] },
{ id: "qsfpdd-sr4-2", standard: "400GBASE-SR4.2", ieeeReference: "IEEE 802.3cm", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, lanes: 4, laneRate: "53.125 Gbaud", modulation: "PAM4", reachMeters: 100, reachLabel: "100m (OM4)", fiberType: "MMF", wavelengths: "850nm + 910nm (bidirectional)", connector: "MPO-12", powerConsumptionW: 12.0, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "400G BiDi over MPO-12 using two wavelengths. Saves fiber vs SR8.", vendors: dcVendors(), tags: ["400G", "multimode", "BiDi", "data-center", "MPO-12", "cabling-reuse", "QSFP-DD"], generation: "Gen2 PAM4", marketStatus: "Mainstream", yearIntroduced: 2020, breakoutCapable: true, breakoutTo: "2x200GBASE-SR4 or 4x100GBASE-SR" },
{ id: "qsfpdd-dr4", standard: "400GBASE-DR4", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 500, reachLabel: "500m", fiberType: "SMF", wavelengths: "1310nm (4x100G PAM4 parallel)", connector: "MPO-12", powerConsumptionW: 12.0, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "400G parallel single-mode for intra-DC. Breakout to 4x100G DR1 possible. Key DCI building block.", vendors: [{ vendor: "Cisco", partPattern: "QDD-400G-DR4-S" }, { vendor: "Juniper", partPattern: "QDD-400G-DR4" }, { vendor: "Arista", partPattern: "QDD-400G-DR4" }, V_HUAWEI, V_DELL, V_EXTREME], tags: ["400G", "singlemode", "500m", "parallel", "breakout", "data-center", "DCI", "QSFP-DD"] },
{ id: "qsfpdd-fr4", standard: "400GBASE-FR4", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 2000, reachLabel: "2km", fiberType: "SMF", wavelengths: "1271/1291/1311/1331nm (4 CWDM lanes x 100G)", connector: "LC", powerConsumptionW: 12.0, tempRange: "COM", category: "DCI", priceTier: "Standard", useCase: "400G FR4 for DCI up to 2km using CWDM4 over LC duplex. Most popular 400G DCI optic.", vendors: [{ vendor: "Cisco", partPattern: "QDD-400G-FR4-S" }, { vendor: "Juniper", partPattern: "QDD-400G-FR4" }, { vendor: "Arista", partPattern: "QDD-400G-FR4" }, V_HUAWEI, V_NOKIA, V_HPE, V_DELL, V_EXTREME], tags: ["400G", "CWDM", "singlemode", "2km", "DCI", "popular", "QSFP-DD"] },
{ id: "qsfpdd-lr4", standard: "400GBASE-LR4", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm (4 LAN-WDM lanes x 100G)", connector: "LC", powerConsumptionW: 14.0, tempRange: "COM", category: "Metro", priceTier: "Premium", useCase: "400G for metro DCI and campus backbone up to 10km.", vendors: [{ vendor: "Cisco", partPattern: "QDD-400G-LR4-S" }, { vendor: "Juniper", partPattern: "QDD-400G-LR4" }, V_HUAWEI, V_NOKIA], tags: ["400G", "singlemode", "10km", "metro", "DCI", "LAN-WDM", "QSFP-DD"] },
{ id: "qsfpdd-lr8", standard: "400GBASE-LR8", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "8 CWDM wavelengths (1271-1411nm, 8x50G)", connector: "LC", powerConsumptionW: 14.0, tempRange: "COM", category: "Metro", priceTier: "Premium", useCase: "400G LR8 using 8 CWDM lanes at 50G each. For platforms with 50G-per-lane electronics.", vendors: carrierVendors(), tags: ["400G", "CWDM8", "singlemode", "10km", "metro", "8-lane", "QSFP-DD"] },
{ id: "qsfpdd-er4", standard: "400GBASE-ER4", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 40000, reachLabel: "40km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm (4 LAN-WDM lanes, amplified)", connector: "LC", powerConsumptionW: 16.0, tempRange: "COM", category: "Metro", priceTier: "Premium", useCase: "400G extended reach for metro rings up to 40km.", vendors: carrierVendors(), tags: ["400G", "singlemode", "40km", "metro", "extended-reach", "SOA", "QSFP-DD"] },
{ id: "qsfpdd-xdr4", standard: "400GBASE-XDR4 (4x100G-FR)", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, lanes: 4, laneRate: "53.125 Gbaud", modulation: "PAM4", reachMeters: 2000, reachLabel: "2km", fiberType: "SMF", wavelengths: "1310nm", connector: "MPO-12", powerConsumptionW: 12.0, tempRange: "COM", category: "DCI", priceTier: "Standard", useCase: "400G XDR4 = 4 parallel SMF lanes at 100G each for 2km. Breaks out to 4x100G-FR1.", vendors: [{ vendor: "Arista", partPattern: "QDD-400G-XDR4" }, { vendor: "Juniper", partPattern: "QDD-4X100G-FR" }, V_CISCO, V_HUAWEI, V_DELL, V_EXTREME], tags: ["400G", "singlemode", "2km", "parallel-SMF", "DCI", "breakout", "PAM4", "QSFP-DD"], generation: "Gen2 PAM4", marketStatus: "Growth", breakoutCapable: true, breakoutTo: "4x100GBASE-FR1" },
{ id: "qsfpdd-plr4", standard: "400G-PLR4 (4x100G-LR)", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, lanes: 4, laneRate: "53.125 Gbaud", modulation: "PAM4", reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "MPO-12", powerConsumptionW: 14.0, tempRange: "COM", category: "Metro", priceTier: "Premium", useCase: "400G parallel-LR for 10km. 4 parallel SMF lanes. Breaks out to 4x100G-LR1.", vendors: [{ vendor: "Arista", partPattern: "QDD-400G-PLR4" }, { vendor: "Juniper", partPattern: "QDD-400G-PLR4" }, V_CISCO, V_HUAWEI], tags: ["400G", "singlemode", "10km", "parallel-SMF", "metro", "breakout", "QSFP-DD"], generation: "Gen2 PAM4", marketStatus: "Growth", breakoutCapable: true, breakoutTo: "4x100GBASE-LR1" },
{ id: "qsfpdd-zr", standard: "400G-ZR (OIF)", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 120000, reachLabel: "120km (unamplified)", fiberType: "SMF", wavelengths: "C-band tunable (1528-1565nm), DP-16QAM", connector: "LC", powerConsumptionW: 18.0, tempRange: "COM", category: "Coherent", priceTier: "Premium", useCase: "400G-ZR coherent in QSFP-DD form factor. Industry standard (OIF) for DCI up to 120km unamplified.", vendors: [{ vendor: "Cisco", partPattern: "QDD-400G-ZR-S" }, { vendor: "Juniper", partPattern: "QDD-400G-ZR" }, { vendor: "Arista", partPattern: "QDD-400G-ZR" }, V_HUAWEI, V_NOKIA], tags: ["400G", "coherent", "ZR", "DP-16QAM", "DCI", "120km", "OIF", "pluggable", "QSFP-DD"] },
{ id: "qsfpdd-zrp", standard: "400G-ZR+ (OpenZR+)", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 2000000, reachLabel: "500km+ (amplified)", fiberType: "SMF", wavelengths: "C-band tunable, flexible modulation (QPSK/8QAM/16QAM)", connector: "LC", powerConsumptionW: 20.0, tempRange: "COM", category: "Coherent", priceTier: "Premium", useCase: "400G-ZR+ coherent with flexible modulation for metro-to-long-haul. OpenZR+ MSA standard.", vendors: [{ vendor: "Cisco", partPattern: "QDD-400G-ZRP-S" }, { vendor: "Juniper", partPattern: "QDD-400G-ZRP" }, { vendor: "Arista", partPattern: "QDD-400G-ZRP" }, V_HUAWEI, V_NOKIA], tags: ["400G", "coherent", "ZR+", "OpenZR+", "flexible-modulation", "metro", "long-haul", "DCI", "pluggable", "QSFP-DD"] },
// ── OSFP — 400G / 800G ──
{ id: "osfp-sr8", standard: "400GBASE-SR8 OSFP", formFactor: "OSFP", speed: "400G", speedGbps: 400, reachMeters: 100, reachLabel: "100m (OM4)", fiberType: "MMF", wavelengths: "850nm (8x50G PAM4)", connector: "MPO-16", powerConsumptionW: 15.0, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "400G multimode OSFP for platforms with OSFP cages.", vendors: [{ vendor: "Arista", partPattern: "OSFP-400G-SR8" }, V_CISCO, V_JUNIPER, V_HUAWEI, V_DELL, V_EXTREME], tags: ["400G", "multimode", "data-center", "OSFP"] },
{ id: "osfp-dr4", standard: "400GBASE-DR4 OSFP", formFactor: "OSFP", speed: "400G", speedGbps: 400, reachMeters: 500, reachLabel: "500m", fiberType: "SMF", wavelengths: "1310nm (4x100G PAM4)", connector: "MPO-12", powerConsumptionW: 15.0, tempRange: "COM", category: "DataCenter", priceTier: "Standard", useCase: "400G parallel single-mode OSFP for next-gen data center fabrics.", vendors: dcVendors(), tags: ["400G", "singlemode", "500m", "data-center", "OSFP"] },
{ id: "osfp-fr4", standard: "400GBASE-FR4 OSFP", formFactor: "OSFP", speed: "400G", speedGbps: 400, reachMeters: 2000, reachLabel: "2km", fiberType: "SMF", wavelengths: "1271/1291/1311/1331nm (4 CWDM x 100G)", connector: "LC", powerConsumptionW: 15.0, tempRange: "COM", category: "DCI", priceTier: "Standard", useCase: "400G FR4 in OSFP form factor for DCI links up to 2km.", vendors: dcVendors(), tags: ["400G", "CWDM", "singlemode", "2km", "DCI", "OSFP"] },
{ id: "osfp-800g-sr8", standard: "800GBASE-SR8", formFactor: "OSFP", speed: "800G", speedGbps: 800, reachMeters: 50, reachLabel: "30m (OM3) / 50m (OM4)", fiberType: "MMF", wavelengths: "850nm (8x100G PAM4)", connector: "MPO-16", powerConsumptionW: 22.0, tempRange: "COM", category: "DataCenter", priceTier: "Premium", useCase: "800G multimode for AI/ML GPU cluster interconnects in hyperscale data centers.", vendors: [{ vendor: "Cisco", partPattern: "OSFP-800G-SR8" }, { vendor: "Arista", partPattern: "OSFP-800G-SR8" }], tags: ["800G", "multimode", "data-center", "AI", "GPU", "hyperscale", "OSFP"] },
{ id: "osfp-800g-dr8", standard: "800GBASE-DR8", formFactor: "OSFP", speed: "800G", speedGbps: 800, reachMeters: 500, reachLabel: "500m", fiberType: "SMF", wavelengths: "1310nm (8x100G PAM4 parallel)", connector: "MPO-16", powerConsumptionW: 22.0, tempRange: "COM", category: "DataCenter", priceTier: "Premium", useCase: "800G parallel single-mode for hyperscale DC fabrics. Breakout to 2x400G or 8x100G possible.", vendors: [{ vendor: "Cisco", partPattern: "OSFP-800G-DR8" }, { vendor: "Arista", partPattern: "OSFP-800G-DR8" }], tags: ["800G", "singlemode", "500m", "parallel", "breakout", "hyperscale", "data-center", "OSFP"] },
{ id: "osfp-800g-2fr4", standard: "800G-2FR4", formFactor: "OSFP", speed: "800G", speedGbps: 800, reachMeters: 2000, reachLabel: "2km", fiberType: "SMF", wavelengths: "8 CWDM wavelengths (4 per fiber direction)", connector: "CS", powerConsumptionW: 22.0, tempRange: "COM", category: "DCI", priceTier: "Premium", useCase: "800G for DCI up to 2km using duplex CS connector. Dual FR4 in a single module.", vendors: dcVendors(), tags: ["800G", "CWDM", "singlemode", "2km", "DCI", "CS-connector", "OSFP"] },
{ id: "osfp-800g-zr", standard: "800G-ZR", formFactor: "OSFP", speed: "800G", speedGbps: 800, reachMeters: 120000, reachLabel: "80-120km", fiberType: "SMF", wavelengths: "C-band tunable, DP-64QAM/DP-16QAM", connector: "LC", powerConsumptionW: 25.0, tempRange: "COM", category: "Coherent", priceTier: "Premium", useCase: "800G coherent pluggable for DCI. Next-gen after 400G-ZR.", vendors: [{ vendor: "Cisco", partPattern: "OSFP-800G-ZR" }, { vendor: "Arista", partPattern: "OSFP-800G-ZR" }, V_JUNIPER, V_HUAWEI, V_NOKIA], tags: ["800G", "coherent", "ZR", "DCI", "pluggable", "next-gen", "OSFP"] },
// ── DAC — Direct Attach Cables ──
{ id: "dac-sfpp-1m", standard: "10G SFP+ DAC", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 5, reachLabel: "1-5m", fiberType: "Copper", wavelengths: "N/A", connector: "None", powerConsumptionW: 0.5, tempRange: "COM", category: "DAC", priceTier: "Budget", useCase: "10G passive copper DAC for in-rack server-to-switch links. Lowest cost and latency option.", vendors: allMajorVendors(), tags: ["10G", "DAC", "copper", "passive", "in-rack", "low-latency", "SFP+"] },
{ id: "dac-sfp28-3m", standard: "25G SFP28 DAC", formFactor: "SFP28", speed: "25G", speedGbps: 25, reachMeters: 5, reachLabel: "1-5m", fiberType: "Copper", wavelengths: "N/A", connector: "None", powerConsumptionW: 0.5, tempRange: "COM", category: "DAC", priceTier: "Budget", useCase: "25G passive copper DAC for in-rack 25G server connections.", vendors: allMajorVendors(), tags: ["25G", "DAC", "copper", "passive", "in-rack", "leaf-spine", "SFP28"] },
{ id: "dac-qsfpp-3m", standard: "40G QSFP+ DAC", formFactor: "QSFP+", speed: "40G", speedGbps: 40, reachMeters: 5, reachLabel: "1-5m", fiberType: "Copper", wavelengths: "N/A", connector: "None", powerConsumptionW: 0.5, tempRange: "COM", category: "DAC", priceTier: "Budget", useCase: "40G passive copper DAC for spine-to-leaf and storage links within a rack.", vendors: allMajorVendors(), tags: ["40G", "DAC", "copper", "passive", "in-rack", "QSFP+"] },
{ id: "dac-qsfp28-3m", standard: "100G QSFP28 DAC", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 5, reachLabel: "1-5m", fiberType: "Copper", wavelengths: "N/A", connector: "None", powerConsumptionW: 1.0, tempRange: "COM", category: "DAC", priceTier: "Budget", useCase: "100G passive copper DAC for spine-leaf and storage interconnects within racks.", vendors: allMajorVendors(), tags: ["100G", "DAC", "copper", "passive", "in-rack", "QSFP28"] },
{ id: "dac-qsfpdd-3m", standard: "400G QSFP-DD DAC", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 3, reachLabel: "1-3m", fiberType: "Copper", wavelengths: "N/A", connector: "None", powerConsumptionW: 1.5, tempRange: "COM", category: "DAC", priceTier: "Budget", useCase: "400G passive copper DAC for in-rack high-bandwidth interconnects.", vendors: dcVendors(), tags: ["400G", "DAC", "copper", "passive", "in-rack", "QSFP-DD"] },
{ id: "dac-osfp-800g", standard: "800G OSFP DAC", formFactor: "OSFP", speed: "800G", speedGbps: 800, reachMeters: 2, reachLabel: "1-2m", fiberType: "Copper", wavelengths: "N/A", connector: "None", powerConsumptionW: 2.0, tempRange: "COM", category: "DAC", priceTier: "Standard", useCase: "800G DAC for GPU-to-switch and AI cluster interconnects. Shortest latency option for 800G.", vendors: [{ vendor: "Cisco", partPattern: "OSFP-800G-CU*" }, { vendor: "Arista", partPattern: "OSFP-800G-DAC*" }], tags: ["800G", "DAC", "copper", "AI", "GPU", "OSFP"] },
// ── AOC — Active Optical Cables ──
{ id: "aoc-sfpp-10m", standard: "10G SFP+ AOC", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 100, reachLabel: "1-100m", fiberType: "MMF", wavelengths: "850nm (embedded)", connector: "None", powerConsumptionW: 1.0, tempRange: "COM", category: "AOC", priceTier: "Budget", useCase: "10G AOC for inter-rack links beyond DAC reach. Lighter than copper.", vendors: allMajorVendors(), tags: ["10G", "AOC", "multimode", "inter-rack", "SFP+"] },
{ id: "aoc-qsfp28-30m", standard: "100G QSFP28 AOC", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 100, reachLabel: "1-100m", fiberType: "MMF", wavelengths: "850nm (embedded)", connector: "None", powerConsumptionW: 3.0, tempRange: "COM", category: "AOC", priceTier: "Budget", useCase: "100G AOC for inter-rack DC links. Lighter and cheaper than SR4 + MPO patch cords.", vendors: allMajorVendors(), tags: ["100G", "AOC", "multimode", "inter-rack", "data-center", "QSFP28"] },
{ id: "aoc-qsfpdd-30m", standard: "400G QSFP-DD AOC", formFactor: "QSFP-DD", speed: "400G", speedGbps: 400, reachMeters: 100, reachLabel: "1-100m", fiberType: "MMF", wavelengths: "850nm (embedded)", connector: "None", powerConsumptionW: 10.0, tempRange: "COM", category: "AOC", priceTier: "Standard", useCase: "400G AOC for short inter-rack links in high-density data centers.", vendors: dcVendors(), tags: ["400G", "AOC", "multimode", "inter-rack", "QSFP-DD"] },
// ── Industrial Temperature Variants ──
{ id: "sfpp-lr-ind", standard: "10GBASE-LR Industrial", formFactor: "SFP+", speed: "10G", speedGbps: 10, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 1.2, tempRange: "IND", category: "Access", priceTier: "Standard", useCase: "10G industrial-temp (-40 to +85C) for outdoor deployments, telecom shelters, and cell towers.", vendors: allMajorVendors(), tags: ["10G", "industrial", "outdoor", "telecom", "cell-tower", "SFP+"] },
{ id: "sfp-lx-ind", standard: "1000BASE-LX Industrial", formFactor: "SFP", speed: "1G", speedGbps: 1, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1310nm", connector: "LC", powerConsumptionW: 1.0, tempRange: "IND", category: "Access", priceTier: "Standard", useCase: "1G industrial-temp for outdoor access networks, utility SCADA, and harsh environments.", vendors: allMajorVendors(), tags: ["1G", "industrial", "outdoor", "SCADA", "utility", "SFP"] },
{ id: "qsfp28-lr4-ind", standard: "100GBASE-LR4 Industrial", formFactor: "QSFP28", speed: "100G", speedGbps: 100, reachMeters: 10000, reachLabel: "10km", fiberType: "SMF", wavelengths: "1295/1300/1305/1310nm", connector: "LC", powerConsumptionW: 5.0, tempRange: "IND", category: "Metro", priceTier: "Premium", useCase: "100G industrial-temp for telecom outdoor cabinets and cell-site aggregation.", vendors: carrierVendors(), tags: ["100G", "industrial", "outdoor", "telecom", "QSFP28"] },
];
// ── Search & Filter Functions ──
/**
* Search transceivers by any keyword. Searches across standard, form factor,
* speed, use case, tags, and vendor names.
*/
export function searchTransceivers(query: string): Transceiver[] {
const q = query.toLowerCase();
return transceivers.filter(
(t) =>
t.standard.toLowerCase().includes(q) ||
t.formFactor.toLowerCase().includes(q) ||
t.speed.toLowerCase().includes(q) ||
t.useCase.toLowerCase().includes(q) ||
t.category.toLowerCase().includes(q) ||
t.wavelengths.toLowerCase().includes(q) ||
t.tags.some((tag) => tag.toLowerCase().includes(q)) ||
t.vendors.some((v) => v.vendor.toLowerCase().includes(q)) ||
(t.modulation && t.modulation.toLowerCase().includes(q)) ||
(t.generation && t.generation.toLowerCase().includes(q))
);
}
/** Filter by form factor (e.g., "SFP+", "QSFP-DD", "OSFP"). */
export function getByFormFactor(formFactor: string): Transceiver[] {
return transceivers.filter(
(t) => t.formFactor.toLowerCase() === formFactor.toLowerCase()
);
}
/** Filter by speed tier (e.g., "10G", "100G", "400G", "800G"). */
export function getBySpeed(speed: string): Transceiver[] {
return transceivers.filter(
(t) => t.speed.toLowerCase() === speed.toLowerCase()
);
}
/** Filter by maximum reach in meters. Returns transceivers that reach at least `minMeters`. */
export function getByReach(minMeters: number): Transceiver[] {
return transceivers.filter((t) => t.reachMeters >= minMeters);
}
/** Filter by product category (e.g., "DataCenter", "Coherent", "DAC"). */
export function getByCategory(category: string): Transceiver[] {
return transceivers.filter(
(t) => t.category.toLowerCase() === category.toLowerCase()
);
}
/** Get a single transceiver by its unique ID. */
export function getById(id: string): Transceiver | undefined {
return transceivers.find((t) => t.id === id);
}

View File

@ -0,0 +1,37 @@
/**
* transceiver-db Open-source optical transceiver database
*
* 159 products, 42 IEEE/MSA standards, 16 form factors, 9 speed tiers.
* From 1G SFP to 800G OSFP. Zero dependencies.
*/
export {
transceivers,
searchTransceivers,
getByFormFactor,
getBySpeed,
getByReach,
getByCategory,
getById,
} from "./database";
export { standards, getStandard, searchStandards } from "./standards";
export { competitors, getCompetitor } from "./market";
export { breakouts } from "./breakouts";
export type {
Transceiver,
Standard,
Competitor,
Breakout,
FormFactor,
FiberType,
ConnectorType,
TempRange,
ProductCategory,
PriceTier,
MarketStatus,
VendorCompat,
} from "./types";

View File

@ -0,0 +1,28 @@
/**
* Competitor landscape neutral industry data.
* No vendor bias. No sales language.
*/
import type { Competitor } from "./types";
export const competitors: readonly Competitor[] = [
{ name: "Cisco", type: "OEM", headquarters: "San Jose, USA", marketPosition: "Largest networking vendor globally. Sells branded optics. Acquired Acacia Communications for coherent technology.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "QSFP+", "QSFP28", "QSFP-DD", "OSFP", "CFP2-DCO"], speedTiersOffered: ["1G", "10G", "25G", "40G", "100G", "400G", "800G"], strengths: ["Dominant installed base", "End-to-end solution", "TAC support tied to branded optics"], weaknesses: ["Significant price premium over compatible optics", "Vendor lock-in practices"] },
{ name: "Juniper Networks", type: "OEM", headquarters: "Sunnyvale, USA (acquired by HPE 2024)", marketPosition: "Strong in service provider and large enterprise. MX/PTX series for routing, QFX/EX for switching.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "QSFP+", "QSFP28", "QSFP-DD", "CFP2-DCO"], speedTiersOffered: ["1G", "10G", "25G", "40G", "100G", "400G"], strengths: ["Junos OS reliability", "Strong SP/carrier presence", "PTX for massive scale"], weaknesses: ["Optics premium over compatible alternatives", "Smaller market share than Cisco"] },
{ name: "Arista Networks", type: "OEM", headquarters: "Santa Clara, USA", marketPosition: "Dominant in hyperscale data centers and cloud. Largest DC switching vendor by port volume. Leader in 400G/800G deployments.", formFactorsOffered: ["SFP+", "SFP28", "QSFP+", "QSFP28", "QSFP-DD", "OSFP"], speedTiersOffered: ["10G", "25G", "40G", "100G", "400G", "800G"], strengths: ["EOS software quality", "Hyperscale dominance", "Early 800G adoption", "Liquid-cooled optics for AI"], weaknesses: ["Optics premium over compatible alternatives", "Primarily DC focused"] },
{ name: "Huawei", type: "OEM", headquarters: "Shenzhen, China", marketPosition: "Largest carrier/SP equipment vendor globally. Strong in EMEA, APAC, Middle East. CloudEngine for DC switching.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "QSFP+", "QSFP28", "QSFP-DD", "OSFP", "CFP2-DCO"], speedTiersOffered: ["1G", "10G", "25G", "40G", "100G", "400G", "800G"], strengths: ["Aggressive pricing vs Western OEMs", "Massive carrier installed base", "Strong in coherent/DWDM"], weaknesses: ["Geopolitical restrictions in some markets"] },
{ name: "Nokia", type: "OEM", headquarters: "Espoo, Finland", marketPosition: "Major carrier/SP vendor. 7750 SR series for routing, 7250 IXR for DC. Strong in IXP infrastructure.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "QSFP+", "QSFP28", "QSFP-DD", "CFP2-DCO"], speedTiersOffered: ["1G", "10G", "25G", "40G", "100G", "400G", "800G"], strengths: ["IXP platform dominance", "FP5 silicon for high-performance routing", "Strong coherent optics"], weaknesses: ["Optics premium", "Smaller DC switching presence"] },
{ name: "FLEXOPTIX", type: "Compatible", headquarters: "Darmstadt, Germany", marketPosition: "Premium compatible optics vendor with unique FlexBox hardware programmer. Codes transceivers on-site for any vendor. 300+ supported switch/router vendors. Lifetime warranty. Strong in service provider, IXP, and enterprise markets across EMEA.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "SFP56", "QSFP+", "QSFP28", "QSFP56", "QSFP-DD", "OSFP", "CFP", "CFP2", "CFP4", "CFP2-DCO", "XFP", "CXP"], speedTiersOffered: ["1G", "10G", "25G", "40G", "50G", "100G", "200G", "400G", "800G"], strengths: ["FlexBox: on-site hardware programmer — recode any transceiver for any vendor in seconds", "300+ supported vendors (largest compatibility matrix in the industry)", "Multi-vendor infrastructure support — one transceiver works across Cisco, Juniper, Arista, Nokia, Huawei and 295+ more", "Lifetime warranty on all products", "Technical support by network engineers, not call center scripts", "Same-day shipping from German warehouse", "Strong NOG/peering community presence"], weaknesses: ["Not the cheapest option (premium quality positioning)", "European-focused logistics (expanding globally)"] },
{ name: "FS.COM", type: "Whitebox", headquarters: "Wilmington, USA (manufacturing in China)", marketPosition: "Major compatible optics vendor. Massive online catalog. Tests compatibility with Cisco, Arista, Juniper, NVIDIA.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "SFP56", "QSFP+", "QSFP28", "QSFP56", "QSFP-DD", "OSFP"], speedTiersOffered: ["1G", "10G", "25G", "40G", "50G", "100G", "200G", "400G", "800G", "1.6T"], strengths: ["Aggressive pricing", "Huge catalog", "Online ordering", "NVIDIA InfiniBand compatibility"], weaknesses: ["Pre-coded only — no field recoding", "Limited field customization", "Quality varies by batch", "No equivalent to FlexBox programmer"] },
{ name: "Innolight Technology", type: "Manufacturer", headquarters: "Suzhou, China", marketPosition: "Top optical transceiver manufacturer by revenue. Primary supplier to NVIDIA and major hyperscalers. Dominates 800G market.", formFactorsOffered: ["SFP28", "QSFP28", "QSFP56", "QSFP-DD", "OSFP", "OSFP-XD"], speedTiersOffered: ["25G", "100G", "200G", "400G", "800G", "1.6T"], strengths: ["Primary supplier to NVIDIA", "Massive manufacturing scale", "Silicon photonics leader", "800G/1.6T early adopter"], weaknesses: ["Primarily sells to hyperscalers/OEMs", "Not direct to enterprise"] },
{ name: "Coherent Corp", type: "Manufacturer", headquarters: "Saxonburg, USA", marketPosition: "Top-tier transceiver manufacturer (formerly II-VI/Finisar). Vertically integrated. Strong in coherent optics and telecom.", formFactorsOffered: ["SFP+", "SFP28", "QSFP28", "QSFP-DD", "OSFP", "CFP2-DCO", "OSFP-XD"], speedTiersOffered: ["10G", "25G", "100G", "400G", "800G", "1.6T"], strengths: ["Vertical integration", "Coherent optics leadership", "InP and SiPh capabilities"], weaknesses: ["Premium pricing", "OEM/hyperscale focus"] },
{ name: "Broadcom", type: "Manufacturer", headquarters: "San Jose, USA", marketPosition: "Major silicon + optics vendor. Tomahawk switch ASICs plus transceiver modules. Co-packaged optics initiative.", formFactorsOffered: ["SFP+", "SFP28", "QSFP28", "QSFP-DD", "OSFP"], speedTiersOffered: ["10G", "25G", "100G", "400G", "800G"], strengths: ["Switch ASIC + optics synergy", "Co-packaged optics leadership"], weaknesses: ["Premium pricing", "Mostly hyperscale/OEM channel"] },
{ name: "HPE/Aruba", type: "OEM", headquarters: "Houston, USA", marketPosition: "Strong in campus/enterprise networking. Aruba CX for modern campus. Juniper acquisition expanding portfolio.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "QSFP+", "QSFP28", "QSFP-DD"], speedTiersOffered: ["1G", "10G", "25G", "40G", "100G", "400G"], strengths: ["Campus/enterprise dominance", "Aruba CX modern OS", "Juniper integration expanding"], weaknesses: ["Optics premium", "Less DC presence (pre-Juniper)"] },
{ name: "Dell Technologies", type: "OEM", headquarters: "Round Rock, USA", marketPosition: "PowerSwitch for DC switching. Strong server attach rate driving optics demand.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "QSFP+", "QSFP28", "QSFP-DD"], speedTiersOffered: ["1G", "10G", "25G", "40G", "100G", "400G"], strengths: ["Server + switch bundle deals", "OS10/FTOS flexibility", "Open networking friendly"], weaknesses: ["Optics premium", "Smaller networking market share"] },
{ name: "Extreme Networks", type: "OEM", headquarters: "Morrisville, USA", marketPosition: "Consolidated multiple brands (Brocade, Avaya Networking, Enterasys, Aerohive). Strong in campus and education.", formFactorsOffered: ["SFP", "SFP+", "SFP28", "QSFP+", "QSFP28"], speedTiersOffered: ["1G", "10G", "25G", "40G", "100G"], strengths: ["Campus/education market", "Fabric Connect technology", "Unified management"], weaknesses: ["Optics premium", "Complex legacy product lines"] },
];
/** Find a competitor by name (partial match). */
export function getCompetitor(name: string): Competitor | undefined {
const q = name.toLowerCase();
return competitors.find((c) => c.name.toLowerCase().includes(q));
}

View File

@ -0,0 +1,82 @@
/**
* IEEE 802.3 and MSA standards reference 42 standards.
*/
import type { Standard } from "./types";
export const standards: readonly Standard[] = [
// 1G
{ standard: "1000BASE-SX", ieeeReference: "IEEE 802.3z", speed: "1G", lanes: 1, laneRate: "1.25 Gbaud", modulation: "NRZ", fiberType: "MMF (OM1-OM4)", wavelength: "850nm", maxReachMeters: 550, maxReachLabel: "220m (OM1) / 550m (OM2+)", connector: "LC/SC", fecRequired: false, formFactors: ["SFP", "GBIC"], yearRatified: 1998, notes: "Original Gigabit Ethernet multimode standard." },
{ standard: "1000BASE-LX", ieeeReference: "IEEE 802.3z", speed: "1G", lanes: 1, laneRate: "1.25 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1310nm", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC/SC", fecRequired: false, formFactors: ["SFP", "GBIC"], yearRatified: 1998, notes: "Standard 1G single-mode. Also works on MMF with mode conditioning patch cable." },
{ standard: "1000BASE-ZX", ieeeReference: "Vendor-defined (not IEEE)", speed: "1G", lanes: 1, laneRate: "1.25 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1550nm", maxReachMeters: 80000, maxReachLabel: "70-80km", connector: "LC", fecRequired: false, formFactors: ["SFP"], yearRatified: 0, notes: "Not an IEEE standard. Vendor-defined. Uses 1550nm for extended reach." },
{ standard: "1000BASE-T", ieeeReference: "IEEE 802.3ab", speed: "1G", lanes: 4, laneRate: "250 Mbaud", modulation: "PAM5", fiberType: "Copper (Cat5e+)", wavelength: "N/A", maxReachMeters: 100, maxReachLabel: "100m", connector: "RJ45", fecRequired: false, formFactors: ["SFP"], yearRatified: 1999, notes: "Gigabit over copper. SFP form factor draws ~1W." },
{ standard: "1000BASE-BX10", ieeeReference: "IEEE 802.3ah", speed: "1G", lanes: 1, laneRate: "1.25 Gbaud", modulation: "NRZ", fiberType: "SMF (single fiber)", wavelength: "1310/1490nm", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC", fecRequired: false, formFactors: ["SFP"], yearRatified: 2004, notes: "Bidirectional over single fiber strand. Sold in pairs." },
// 10G
{ standard: "10GBASE-SR", ieeeReference: "IEEE 802.3ae", speed: "10G", lanes: 1, laneRate: "10.3125 Gbaud", modulation: "NRZ", fiberType: "MMF (OM3/OM4)", wavelength: "850nm", maxReachMeters: 400, maxReachLabel: "300m (OM3) / 400m (OM4)", connector: "LC", fecRequired: false, formFactors: ["SFP+", "XFP"], yearRatified: 2002, notes: "Most deployed 10G optic worldwide." },
{ standard: "10GBASE-LR", ieeeReference: "IEEE 802.3ae", speed: "10G", lanes: 1, laneRate: "10.3125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1310nm", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC", fecRequired: false, formFactors: ["SFP+", "XFP"], yearRatified: 2002, notes: "Standard 10G single-mode. Backbone of campus and metro networks." },
{ standard: "10GBASE-ER", ieeeReference: "IEEE 802.3ae", speed: "10G", lanes: 1, laneRate: "10.3125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1550nm", maxReachMeters: 40000, maxReachLabel: "40km", connector: "LC", fecRequired: false, formFactors: ["SFP+", "XFP"], yearRatified: 2002, notes: "Extended reach 10G for metro rings and inter-city links." },
{ standard: "10GBASE-ZR", ieeeReference: "Vendor-defined (not IEEE)", speed: "10G", lanes: 1, laneRate: "10.3125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1550nm", maxReachMeters: 80000, maxReachLabel: "80km", connector: "LC", fecRequired: false, formFactors: ["SFP+", "XFP"], yearRatified: 0, notes: "Not an IEEE standard. Vendor-defined. 80km reach with high-power laser." },
{ standard: "10GBASE-T", ieeeReference: "IEEE 802.3an", speed: "10G", lanes: 4, laneRate: "2.5 Gbaud", modulation: "PAM16/DSQ128", fiberType: "Copper (Cat6a/Cat7)", wavelength: "N/A", maxReachMeters: 100, maxReachLabel: "100m (Cat6a)", connector: "RJ45", fecRequired: false, formFactors: ["SFP+"], yearRatified: 2006, notes: "10G over copper. 30m on Cat6, 100m on Cat6a." },
{ standard: "10GBASE-LRM", ieeeReference: "IEEE 802.3aq", speed: "10G", lanes: 1, laneRate: "10.3125 Gbaud", modulation: "NRZ", fiberType: "MMF (legacy OM1/OM2)", wavelength: "1310nm", maxReachMeters: 220, maxReachLabel: "220m", connector: "LC", fecRequired: false, formFactors: ["SFP+"], yearRatified: 2006, notes: "10G over legacy multimode fiber." },
// 25G
{ standard: "25GBASE-SR", ieeeReference: "IEEE 802.3by", speed: "25G", lanes: 1, laneRate: "25.78125 Gbaud", modulation: "NRZ", fiberType: "MMF (OM3/OM4)", wavelength: "850nm", maxReachMeters: 100, maxReachLabel: "70m (OM3) / 100m (OM4)", connector: "LC", fecRequired: true, formFactors: ["SFP28"], yearRatified: 2016, notes: "Standard 25G data center server access." },
{ standard: "25GBASE-LR", ieeeReference: "IEEE 802.3cc", speed: "25G", lanes: 1, laneRate: "25.78125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1310nm", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC", fecRequired: false, formFactors: ["SFP28"], yearRatified: 2017, notes: "25G single-mode for campus/metro. Critical for 5G fronthaul (eCPRI)." },
{ standard: "25GBASE-ER", ieeeReference: "IEEE 802.3cc", speed: "25G", lanes: 1, laneRate: "25.78125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1310nm", maxReachMeters: 30000, maxReachLabel: "30km", connector: "LC", fecRequired: true, formFactors: ["SFP28"], yearRatified: 2017, notes: "Extended reach 25G for metro and 5G midhaul." },
// 40G
{ standard: "40GBASE-SR4", ieeeReference: "IEEE 802.3ba", speed: "40G", lanes: 4, laneRate: "10.3125 Gbaud", modulation: "NRZ", fiberType: "MMF (OM3/OM4)", wavelength: "850nm", maxReachMeters: 150, maxReachLabel: "100m (OM3) / 150m (OM4)", connector: "MPO-12", fecRequired: false, formFactors: ["QSFP+"], yearRatified: 2010, notes: "4x10G parallel optics. Can break out to 4x10GBASE-SR." },
{ standard: "40GBASE-LR4", ieeeReference: "IEEE 802.3ba", speed: "40G", lanes: 4, laneRate: "10.3125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1310nm (4 CWDM wavelengths)", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC", fecRequired: false, formFactors: ["QSFP+"], yearRatified: 2010, notes: "4 CWDM wavelengths over duplex LC." },
{ standard: "40GBASE-ER4", ieeeReference: "IEEE 802.3bm", speed: "40G", lanes: 4, laneRate: "10.3125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1310nm (4 CWDM wavelengths)", maxReachMeters: 40000, maxReachLabel: "40km", connector: "LC", fecRequired: false, formFactors: ["QSFP+"], yearRatified: 2015, notes: "Extended reach 40G for metro ring and DCI." },
// 100G
{ standard: "100GBASE-SR4", ieeeReference: "IEEE 802.3bm", speed: "100G", lanes: 4, laneRate: "25.78125 Gbaud", modulation: "NRZ", fiberType: "MMF (OM3/OM4)", wavelength: "850nm", maxReachMeters: 100, maxReachLabel: "70m (OM3) / 100m (OM4)", connector: "MPO-12", fecRequired: true, formFactors: ["QSFP28"], yearRatified: 2015, notes: "4x25G parallel. Breakout to 4x25GBASE-SR." },
{ standard: "100GBASE-SR2", ieeeReference: "100G Lambda MSA", speed: "100G", lanes: 2, laneRate: "26.5625 Gbaud", modulation: "PAM4", fiberType: "MMF (OM4)", wavelength: "850nm", maxReachMeters: 100, maxReachLabel: "100m (OM4)", connector: "LC", fecRequired: true, formFactors: ["QSFP28"], yearRatified: 2018, notes: "MSA-defined. 2x50G PAM4 over duplex LC." },
{ standard: "100GBASE-DR", ieeeReference: "IEEE 802.3cd", speed: "100G", lanes: 1, laneRate: "53.125 Gbaud", modulation: "PAM4", fiberType: "SMF", wavelength: "1310nm", maxReachMeters: 500, maxReachLabel: "500m", connector: "LC", fecRequired: true, formFactors: ["QSFP28", "SFP-DD"], yearRatified: 2018, notes: "Single-lambda 100G. Key for leaf-spine architectures." },
{ standard: "100GBASE-FR1", ieeeReference: "IEEE 802.3cu", speed: "100G", lanes: 1, laneRate: "53.125 Gbaud", modulation: "PAM4", fiberType: "SMF", wavelength: "1310nm", maxReachMeters: 2000, maxReachLabel: "2km", connector: "LC", fecRequired: true, formFactors: ["QSFP28"], yearRatified: 2021, notes: "Single-lambda 100G for 2km." },
{ standard: "100GBASE-LR1", ieeeReference: "IEEE 802.3cu", speed: "100G", lanes: 1, laneRate: "53.125 Gbaud", modulation: "PAM4", fiberType: "SMF", wavelength: "1310nm", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC", fecRequired: true, formFactors: ["QSFP28"], yearRatified: 2021, notes: "Single-lambda 100G for 10km. New IXP standard replacing LR4." },
{ standard: "100GBASE-LR4", ieeeReference: "IEEE 802.3ba", speed: "100G", lanes: 4, laneRate: "25.78125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1310nm (4 LAN-WDM wavelengths)", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC", fecRequired: false, formFactors: ["QSFP28", "CFP", "CFP2", "CFP4"], yearRatified: 2010, notes: "4x25G LAN-WDM over duplex LC. Being replaced by LR1 single-lambda." },
{ standard: "100GBASE-CWDM4", ieeeReference: "100G CWDM4 MSA", speed: "100G", lanes: 4, laneRate: "25.78125 Gbaud", modulation: "NRZ", fiberType: "SMF", wavelength: "1271/1291/1311/1331nm", maxReachMeters: 2000, maxReachLabel: "2km", connector: "LC", fecRequired: false, formFactors: ["QSFP28"], yearRatified: 2014, notes: "MSA-defined. Lower-cost alternative to LR4 for 2km." },
{ standard: "100GBASE-PSM4", ieeeReference: "100G PSM4 MSA", speed: "100G", lanes: 4, laneRate: "25.78125 Gbaud", modulation: "NRZ", fiberType: "SMF (parallel)", wavelength: "1310nm", maxReachMeters: 500, maxReachLabel: "500m", connector: "MPO-12", fecRequired: false, formFactors: ["QSFP28"], yearRatified: 2014, notes: "MSA-defined. 4x25G parallel single-mode." },
{ standard: "100GBASE-ZR", ieeeReference: "IEEE 802.3ct", speed: "100G", lanes: 1, laneRate: "~64 Gbaud", modulation: "DP-QPSK (coherent)", fiberType: "SMF (DWDM)", wavelength: "C-band (tunable)", maxReachMeters: 80000, maxReachLabel: "80km+ (DWDM amplified)", connector: "LC", fecRequired: true, formFactors: ["QSFP28"], yearRatified: 2021, notes: "Coherent 100G over DWDM systems." },
// 200G
{ standard: "200GBASE-SR4", ieeeReference: "IEEE 802.3cd", speed: "200G", lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", fiberType: "MMF (OM4)", wavelength: "850nm", maxReachMeters: 100, maxReachLabel: "70m (OM3) / 100m (OM4)", connector: "MPO-12", fecRequired: true, formFactors: ["QSFP56", "QSFP-DD"], yearRatified: 2018, notes: "4x50G PAM4 parallel." },
{ standard: "200GBASE-DR4", ieeeReference: "IEEE 802.3cd", speed: "200G", lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", fiberType: "SMF (parallel)", wavelength: "1310nm", maxReachMeters: 500, maxReachLabel: "500m", connector: "MPO-12", fecRequired: true, formFactors: ["QSFP56", "QSFP-DD"], yearRatified: 2018, notes: "4x50G parallel SMF. Can break out to 2x100G-DR or 4x50G." },
{ standard: "200GBASE-FR4", ieeeReference: "IEEE 802.3cu", speed: "200G", lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", fiberType: "SMF", wavelength: "1310nm (4 CWDM wavelengths)", maxReachMeters: 2000, maxReachLabel: "2km", connector: "LC", fecRequired: true, formFactors: ["QSFP56", "QSFP-DD"], yearRatified: 2021, notes: "4x50G CWDM over duplex LC for 2km reach." },
{ standard: "200GBASE-LR4", ieeeReference: "IEEE 802.3cu", speed: "200G", lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", fiberType: "SMF", wavelength: "1310nm (4 CWDM wavelengths)", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC", fecRequired: true, formFactors: ["QSFP56", "QSFP-DD"], yearRatified: 2021, notes: "4x50G CWDM over duplex LC for 10km reach." },
// 400G
{ standard: "400GBASE-SR8", ieeeReference: "IEEE 802.3cm", speed: "400G", lanes: 8, laneRate: "26.5625 Gbaud", modulation: "PAM4", fiberType: "MMF (OM4)", wavelength: "850nm", maxReachMeters: 100, maxReachLabel: "100m (OM4)", connector: "MPO-16", fecRequired: true, formFactors: ["QSFP-DD", "OSFP"], yearRatified: 2020, notes: "8x50G PAM4 parallel." },
{ standard: "400GBASE-SR4.2", ieeeReference: "IEEE 802.3cm", speed: "400G", lanes: 4, laneRate: "26.5625 Gbaud", modulation: "PAM4", fiberType: "MMF", wavelength: "850nm + 910nm (BiDi)", maxReachMeters: 100, maxReachLabel: "100m", connector: "MPO-12", fecRequired: true, formFactors: ["QSFP-DD", "OSFP"], yearRatified: 2020, notes: "BiDi 400G over MPO-12 using two wavelengths." },
{ standard: "400GBASE-DR4", ieeeReference: "IEEE 802.3bs", speed: "400G", lanes: 4, laneRate: "53.125 Gbaud", modulation: "PAM4", fiberType: "SMF (parallel)", wavelength: "1310nm", maxReachMeters: 500, maxReachLabel: "500m", connector: "MPO-12", fecRequired: true, formFactors: ["QSFP-DD", "OSFP"], yearRatified: 2017, notes: "4x100G parallel SMF. THE key 400G data center optic." },
{ standard: "400GBASE-FR4", ieeeReference: "IEEE 802.3cu", speed: "400G", lanes: 4, laneRate: "53.125 Gbaud", modulation: "PAM4", fiberType: "SMF", wavelength: "1271/1291/1311/1331nm (CWDM4)", maxReachMeters: 2000, maxReachLabel: "2km", connector: "LC", fecRequired: true, formFactors: ["QSFP-DD", "OSFP"], yearRatified: 2021, notes: "4x100G CWDM over duplex LC." },
{ standard: "400GBASE-LR4-10", ieeeReference: "IEEE 802.3cu", speed: "400G", lanes: 4, laneRate: "53.125 Gbaud", modulation: "PAM4", fiberType: "SMF", wavelength: "1271/1291/1311/1331nm (CWDM4)", maxReachMeters: 10000, maxReachLabel: "10km", connector: "LC", fecRequired: true, formFactors: ["QSFP-DD", "OSFP"], yearRatified: 2021, notes: "4x100G CWDM for 10km. Standard for metro/IXP 400G." },
{ standard: "400GBASE-ZR (OIF 400ZR)", ieeeReference: "OIF-400ZR-01.0", speed: "400G", lanes: 1, laneRate: "~60 Gbaud", modulation: "DP-16QAM (coherent)", fiberType: "SMF", wavelength: "C-band (tunable, 75 GHz DWDM grid)", maxReachMeters: 120000, maxReachLabel: "up to 120km (amplified)", connector: "LC", fecRequired: true, formFactors: ["QSFP-DD", "OSFP", "CFP2-DCO"], yearRatified: 2020, notes: "OIF interoperable coherent 400G. Collapses IP/optical layers." },
// 800G
{ standard: "800GBASE-SR8", ieeeReference: "IEEE 802.3df", speed: "800G", lanes: 8, laneRate: "106.25 Gbaud", modulation: "PAM4", fiberType: "MMF (OM4)", wavelength: "850nm", maxReachMeters: 50, maxReachLabel: "50m (OM3) / 100m (OM4)", connector: "2x MPO-12", fecRequired: true, formFactors: ["OSFP"], yearRatified: 2024, notes: "8x100G PAM4 parallel." },
{ standard: "800GBASE-DR8", ieeeReference: "IEEE 802.3df", speed: "800G", lanes: 8, laneRate: "106.25 Gbaud", modulation: "PAM4", fiberType: "SMF (parallel)", wavelength: "1310nm", maxReachMeters: 500, maxReachLabel: "500m", connector: "2x MPO-12", fecRequired: true, formFactors: ["OSFP", "QSFP-DD800"], yearRatified: 2024, notes: "Primary 800G DC optic. 8x100G parallel SMF." },
{ standard: "OIF 800ZR", ieeeReference: "OIF-800ZR", speed: "800G", lanes: 1, laneRate: "~90 Gbaud", modulation: "DP-16QAM / DP-64QAM (coherent)", fiberType: "SMF", wavelength: "C-band (tunable)", maxReachMeters: 120000, maxReachLabel: "up to 120km+ (amplified)", connector: "LC", fecRequired: true, formFactors: ["OSFP", "QSFP-DD800", "CFP2-DCO"], yearRatified: 2024, notes: "800G pluggable coherent. Building on 400ZR success for DCI." },
];
/** Find a standard by exact or partial name. */
export function getStandard(name: string): Standard | undefined {
const q = name.toLowerCase();
return standards.find((s) => s.standard.toLowerCase() === q) ||
standards.find((s) => s.standard.toLowerCase().includes(q));
}
/** Search standards by keyword (speed, modulation, IEEE reference, etc.). */
export function searchStandards(query: string): Standard[] {
const q = query.toLowerCase();
return standards.filter(
(s) =>
s.standard.toLowerCase().includes(q) ||
s.speed.toLowerCase().includes(q) ||
s.ieeeReference.toLowerCase().includes(q) ||
s.modulation.toLowerCase().includes(q) ||
s.notes.toLowerCase().includes(q)
);
}

137
packages/core/src/types.ts Normal file
View File

@ -0,0 +1,137 @@
/**
* Core type definitions for the transceiver database.
*/
export type FormFactor =
| "SFP"
| "SFP+"
| "SFP28"
| "SFP56"
| "QSFP+"
| "QSFP28"
| "QSFP56"
| "QSFP-DD"
| "OSFP"
| "CFP"
| "CFP2"
| "CFP4"
| "CFP2-DCO"
| "XFP"
| "GBIC"
| "CXP"
| "SFP-DD"
| "SFP56-DD"
| "QSFP-DD800"
| "OSFP-XD";
export type FiberType = "MMF" | "SMF" | "MMF/SMF" | "Copper" | "N/A";
export type ConnectorType =
| "LC"
| "SC"
| "MPO-12"
| "MPO-16"
| "MPO-24"
| "RJ45"
| "None"
| "CS"
| "SN"
| "2xMPO-12";
export type TempRange = "COM" | "IND";
export type ProductCategory =
| "DataCenter"
| "Metro"
| "LongHaul"
| "DCI"
| "Access"
| "Coherent"
| "CWDM"
| "DWDM"
| "BiDi"
| "AOC"
| "DAC"
| "Breakout"
| "Legacy"
| "IXP"
| "5G"
| "AI";
export type PriceTier = "Budget" | "Standard" | "Premium";
export type MarketStatus = "Mainstream" | "Growth" | "Emerging" | "Legacy" | "EOL";
export interface VendorCompat {
vendor: string;
partPattern: string;
}
export interface Transceiver {
id: string;
standard: string;
ieeeReference?: string;
formFactor: FormFactor;
speed: string;
speedGbps: number;
lanes?: number;
laneRate?: string;
modulation?: string;
reachMeters: number;
reachLabel: string;
fiberType: FiberType;
wavelengths: string;
connector: ConnectorType;
powerConsumptionW: number;
tempRange: TempRange;
category: ProductCategory;
priceTier: PriceTier;
useCase: string;
vendors: VendorCompat[];
tags: string[];
generation?: string;
marketStatus?: MarketStatus;
yearIntroduced?: number;
breakoutCapable?: boolean;
breakoutTo?: string;
}
export interface Standard {
standard: string;
ieeeReference: string;
speed: string;
lanes: number;
laneRate: string;
modulation: string;
fiberType: string;
wavelength: string;
maxReachMeters: number;
maxReachLabel: string;
connector: string;
fecRequired: boolean;
formFactors: string[];
yearRatified: number;
notes: string;
}
export interface Competitor {
name: string;
type: "OEM" | "Whitebox" | "Manufacturer" | "Distributor" | "Compatible";
headquarters: string;
marketPosition: string;
formFactorsOffered: string[];
speedTiersOffered: string[];
strengths: string[];
weaknesses: string[];
}
export interface Breakout {
id: string;
from: string;
to: string;
formFactor: string;
description: string;
cableType: "Passive" | "Active";
maxLength: string;
speedPerLane: string;
}

View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@ -0,0 +1,31 @@
{
"name": "@tip/scraper",
"version": "0.1.0",
"private": true,
"description": "TIP scraper engine — Crawlee + Playwright for competitor pricing, stock, datasheets, FAQs",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"dev": "tsx src/index.ts",
"scrape:fs": "tsx src/scrapers/fs-com.ts",
"scrape:cisco": "tsx src/scrapers/cisco-tmg.ts",
"scrape:optcore": "tsx src/scrapers/optcore.ts",
"scrape:news": "tsx src/scrapers/news.ts",
"scrape:all": "tsx src/index.ts --all"
},
"dependencies": {
"crawlee": "^3.12.0",
"playwright": "^1.50.0",
"pg": "^8.13.1",
"pg-boss": "^10.1.5",
"dotenv": "^16.4.7",
"cheerio": "^1.0.0",
"xml2js": "^0.6.2"
},
"devDependencies": {
"@types/pg": "^8.11.11",
"@types/xml2js": "^0.4.14",
"typescript": "^5.9.3",
"tsx": "^4.19.0"
}
}

View File

@ -0,0 +1,69 @@
/**
* TIP Scraper Engine Main entry point.
*
* Usage:
* tsx src/index.ts Start scheduler (production mode)
* tsx src/index.ts --all Run all scrapers once
* tsx src/index.ts --fs Run FS.com scraper once
* tsx src/index.ts --cisco Run Cisco TMG scraper once
* tsx src/index.ts --optcore Run Optcore scraper once
* tsx src/index.ts --news Run news aggregator once
*/
import { createScheduler, registerSchedules, registerWorkers } from "./scheduler";
import { scrapeFs } from "./scrapers/fs-com";
import { scrapeCiscoTmg } from "./scrapers/cisco-tmg";
import { scrapeOptcore } from "./scrapers/optcore";
import { scrapeNews } from "./scrapers/news";
import { pool } from "./utils/db";
const args = process.argv.slice(2);
async function runOnce(): Promise<void> {
if (args.includes("--fs") || args.includes("--all")) {
await scrapeFs();
}
if (args.includes("--cisco") || args.includes("--all")) {
await scrapeCiscoTmg();
}
if (args.includes("--optcore") || args.includes("--all")) {
await scrapeOptcore();
}
if (args.includes("--news") || args.includes("--all")) {
await scrapeNews();
}
await pool.end();
}
async function runScheduler(): Promise<void> {
console.log("=== TIP Scraper Engine ===\n");
console.log("Mode: Scheduler (pg-boss)\n");
const boss = await createScheduler();
await registerSchedules(boss);
await registerWorkers(boss);
console.log("\nScheduler running. Press Ctrl+C to stop.\n");
// Graceful shutdown
const shutdown = async () => {
console.log("\nShutting down...");
await boss.stop();
await pool.end();
process.exit(0);
};
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
}
if (args.some((a) => ["--all", "--fs", "--cisco", "--optcore", "--news"].includes(a))) {
runOnce().catch((err) => {
console.error("Fatal:", err);
process.exit(1);
});
} else {
runScheduler().catch((err) => {
console.error("Fatal:", err);
process.exit(1);
});
}

View File

@ -0,0 +1,127 @@
/**
* pg-boss Job Scheduler manages scrape jobs with adaptive timing.
*
* Job types:
* scrape:pricing:fs Every 4 hours for FS.com prices/stock
* scrape:pricing:optcore Every 6 hours for Optcore prices/stock
* scrape:compat:cisco Weekly for OEM compatibility matrices
* scrape:news Every 6 hours for trade press and news
* scrape:docs Weekly for manuals and datasheets
* scrape:faq Weekly for vendor FAQ/troubleshooting pages
*/
import PgBoss from "pg-boss";
import { config } from "dotenv";
import { join } from "path";
config({ path: join(__dirname, "..", "..", "..", ".env") });
const connectionString = `postgres://${process.env.POSTGRES_USER || "tip"}:${process.env.POSTGRES_PASSWORD || "tip_dev_2026"}@${process.env.POSTGRES_HOST || "localhost"}:${process.env.POSTGRES_PORT || "5433"}/${process.env.POSTGRES_DB || "transceiver_db"}`;
export async function createScheduler(): Promise<PgBoss> {
const boss = new PgBoss({
connectionString,
retryLimit: 3,
retryDelay: 30,
retryBackoff: true,
expireInSeconds: 300, // 5 min timeout per job
monitorStateIntervalSeconds: 30,
});
boss.on("error", (error) => console.error("pg-boss error:", error));
await boss.start();
console.log("pg-boss scheduler started");
return boss;
}
export async function registerSchedules(boss: PgBoss): Promise<void> {
// pg-boss v10: create queues before scheduling
const queues = [
"scrape:pricing:fs",
"scrape:pricing:optcore",
"scrape:compat:cisco",
"scrape:news",
"scrape:faq",
"scrape:docs",
];
for (const q of queues) {
await boss.createQueue(q).catch(() => { /* already exists */ });
}
// FS.com pricing (every 4 hours — JS rendering is slow)
await boss.schedule("scrape:pricing:fs", "0 */4 * * *", {}, {
retryLimit: 2,
expireInSeconds: 3600,
});
// Optcore pricing (every 6 hours — WP API enumeration + Playwright)
await boss.schedule("scrape:pricing:optcore", "0 */6 * * *", {}, {
retryLimit: 2,
expireInSeconds: 7200,
});
// Compatibility matrices (every Sunday at 3am)
await boss.schedule("scrape:compat:cisco", "0 3 * * 0", {}, {
retryLimit: 3,
expireInSeconds: 3600,
});
// News aggregation (every 6 hours)
await boss.schedule("scrape:news", "0 */6 * * *", {}, {
retryLimit: 2,
expireInSeconds: 1800,
});
// FAQ/KB scraping (every Wednesday at 2am)
await boss.schedule("scrape:faq", "0 2 * * 3", {}, {
retryLimit: 3,
expireInSeconds: 3600,
});
// Document/datasheet check (every Saturday at 4am)
await boss.schedule("scrape:docs", "0 4 * * 6", {}, {
retryLimit: 3,
expireInSeconds: 7200,
});
console.log("All schedules registered");
}
export async function registerWorkers(boss: PgBoss): Promise<void> {
// Lazy-load scrapers to avoid circular deps
const { scrapeFs } = await import("./scrapers/fs-com");
const { scrapeCiscoTmg } = await import("./scrapers/cisco-tmg");
const { scrapeOptcore } = await import("./scrapers/optcore");
const { scrapeNews } = await import("./scrapers/news");
await boss.work("scrape:pricing:fs", async (_job) => {
console.log(`[${new Date().toISOString()}] Running: FS.com pricing`);
await scrapeFs();
});
await boss.work("scrape:pricing:optcore", async (_job) => {
console.log(`[${new Date().toISOString()}] Running: Optcore pricing`);
await scrapeOptcore();
});
await boss.work("scrape:compat:cisco", async (_job) => {
console.log(`[${new Date().toISOString()}] Running: Cisco TMG`);
await scrapeCiscoTmg();
});
await boss.work("scrape:news", async (_job) => {
console.log(`[${new Date().toISOString()}] Running: News aggregation`);
await scrapeNews();
});
await boss.work("scrape:faq", async (_job) => {
console.log(`[${new Date().toISOString()}] FAQ scraper — not yet implemented`);
});
await boss.work("scrape:docs", async (_job) => {
console.log(`[${new Date().toISOString()}] Docs scraper — not yet implemented`);
});
console.log("All workers registered");
}

View File

@ -0,0 +1,155 @@
/**
* Cisco TMG Matrix Scraper Transceiver Compatibility
*
* Source: tmgmatrix.cisco.com
* Extracts: Switch model Transceiver compatibility data
* Stores: switches, compatibility table
*
* The TMG Matrix has a JSON API behind the scenes.
*/
import { CheerioCrawler } from "crawlee";
import { pool, ensureVendor } from "../utils/db";
const TMG_BASE = "https://tmgmatrix.cisco.com";
interface TmgEntry {
switchModel: string;
switchSeries: string;
transceiverPid: string;
transceiverDescription: string;
speed: string;
reach: string;
cableType: string;
connector: string;
minSoftware: string;
}
async function upsertCiscoSwitch(vendorId: string, model: string, series: string): Promise<string> {
const result = await pool.query(
`INSERT INTO switches (vendor_id, model, series, category, layer, managed)
VALUES ($1, $2, $3, 'DataCenter', 'L3', true)
ON CONFLICT (vendor_id, model) DO UPDATE SET series = EXCLUDED.series
RETURNING id`,
[vendorId, model, series]
);
return result.rows[0].id;
}
async function upsertCompatibility(
switchId: string,
transceiverId: string,
firmwareMin: string
): Promise<void> {
await pool.query(
`INSERT INTO compatibility (switch_id, transceiver_id, verified_by, verification_method, status, firmware_min, source_url)
VALUES ($1, $2, 'Cisco TMG Matrix', 'vendor_matrix', 'compatible', $3, $4)
ON CONFLICT (switch_id, transceiver_id) DO UPDATE SET firmware_min = EXCLUDED.firmware_min`,
[switchId, transceiverId, firmwareMin || null, TMG_BASE]
);
}
export async function scrapeCiscoTmg(): Promise<void> {
console.log("=== Cisco TMG Matrix Scraper Starting ===\n");
const ciscoVendorId = await ensureVendor(
"Cisco",
"oem",
"https://www.cisco.com",
undefined
);
const entries: TmgEntry[] = [];
// TMG Matrix uses a search API
// First, try the public HTML interface
const crawler = new CheerioCrawler({
maxConcurrency: 1,
maxRequestsPerMinute: 10, // Very respectful — Cisco rate limits aggressively
async requestHandler({ request, $, log }) {
log.info(`Scraping: ${request.url}`);
// The TMG Matrix renders a table with compatibility data
$("table tbody tr, .matrix-row, [class*='result-row']").each((_i, el) => {
const $row = $(el);
const cells = $row.find("td").map((_j, td) => $(td).text().trim()).get();
if (cells.length >= 4) {
entries.push({
switchModel: cells[0] || "",
switchSeries: cells[0]?.split(" ")[0] || "Nexus",
transceiverPid: cells[1] || "",
transceiverDescription: cells[2] || "",
speed: cells[3] || "",
reach: cells[4] || "",
cableType: cells[5] || "",
connector: cells[6] || "",
minSoftware: cells[7] || "",
});
}
});
},
});
// Start with Nexus switches (most relevant for Flexoptix)
await crawler.run([
`${TMG_BASE}/public/tmg?searchValue=Nexus+9000`,
`${TMG_BASE}/public/tmg?searchValue=Nexus+3000`,
`${TMG_BASE}/public/tmg?searchValue=Nexus+7000`,
`${TMG_BASE}/public/tmg?searchValue=Catalyst+9000`,
]);
console.log(`\nEntries found: ${entries.length}`);
// Write to database
let switches = 0;
let compat = 0;
for (const entry of entries) {
if (!entry.switchModel || !entry.transceiverPid) continue;
try {
const switchId = await upsertCiscoSwitch(
ciscoVendorId,
entry.switchModel,
entry.switchSeries
);
switches++;
// Try to match transceiver in our DB
const txResult = await pool.query(
`SELECT id FROM transceivers
WHERE part_number = $1
OR slug LIKE $2
OR standard_name ILIKE $3
LIMIT 1`,
[
entry.transceiverPid,
`%${entry.transceiverPid.toLowerCase().replace(/[^a-z0-9]/g, "")}%`,
`%${entry.speed}%${entry.reach}%`,
]
);
if (txResult.rows.length > 0) {
await upsertCompatibility(switchId, txResult.rows[0].id, entry.minSoftware);
compat++;
}
} catch (err) {
// Skip duplicates silently
}
}
console.log(`Switches upserted: ${switches}`);
console.log(`Compatibility entries: ${compat}`);
console.log("=== Cisco TMG Scraper Complete ===\n");
}
if (require.main === module) {
scrapeCiscoTmg()
.then(() => pool.end())
.catch((err) => {
console.error("Fatal:", err);
pool.end();
process.exit(1);
});
}

View File

@ -0,0 +1,277 @@
/**
* FS.com Scraper Prices, Stock, Product Catalog
*
* FS.com renders products client-side (JS), so we use PlaywrightCrawler.
* Categories: /c/optical-transceivers-9
*
* Respects: robots.txt, rate limiting (2s between requests)
*/
import { PlaywrightCrawler } from "crawlee";
import { ensureVendor, upsertPriceObservation, findOrCreateScrapedTransceiver, pool } from "../utils/db";
import { contentHash, parsePrice, parseStockLevel, parseQuantity } from "../utils/hash";
const BASE_URL = "https://www.fs.com";
const CATEGORY_URLS = [
"/c/1g-sfp-modules-702",
"/c/10g-sfp-plus-modules-703",
"/c/25g-sfp28-modules-704",
"/c/40g-qsfp-plus-modules-705",
"/c/100g-qsfp28-modules-706",
"/c/400g-qsfp-dd-modules-3102",
"/c/800g-osfp-modules-3449",
];
interface FsProduct {
partNumber: string;
name: string;
price: number;
currency: string;
stockLevel: string;
quantity?: number;
url: string;
formFactor?: string;
speedGbps?: number;
speed?: string;
reachLabel?: string;
}
function detectFormFactor(text: string): string | undefined {
const lower = text.toLowerCase();
if (lower.includes("osfp") && !lower.includes("qsfp")) return "OSFP";
if (lower.includes("qsfp-dd800") || lower.includes("qsfp-dd 800")) return "QSFP-DD800";
if (lower.includes("qsfp-dd")) return "QSFP-DD";
if (lower.includes("qsfp56")) return "QSFP56";
if (lower.includes("qsfp28")) return "QSFP28";
if (lower.includes("qsfp+") || lower.includes("qsfp plus")) return "QSFP+";
if (lower.includes("sfp56")) return "SFP56";
if (lower.includes("sfp28")) return "SFP28";
if (lower.includes("sfp+") || lower.includes("sfp plus")) return "SFP+";
if (lower.includes("sfp") && !lower.includes("qsfp")) return "SFP";
if (lower.includes("cfp2")) return "CFP2";
if (lower.includes("xfp")) return "XFP";
return undefined;
}
function detectSpeed(text: string): { speed: string; speedGbps: number } | undefined {
const patterns: [RegExp, string, number][] = [
[/800\s*g/i, "800G", 800],
[/400\s*g/i, "400G", 400],
[/200\s*g/i, "200G", 200],
[/100\s*g/i, "100G", 100],
[/50\s*g/i, "50G", 50],
[/40\s*g/i, "40G", 40],
[/25\s*g/i, "25G", 25],
[/10\s*g/i, "10G", 10],
[/1\s*g\b/i, "1G", 1],
];
for (const [re, speed, gbps] of patterns) {
if (re.test(text)) return { speed, speedGbps: gbps };
}
return undefined;
}
function detectReach(text: string): string | undefined {
const match = text.match(/(\d+)\s*(m|km)\b/i);
if (match) return `${match[1]}${match[2].toLowerCase()}`;
return undefined;
}
export async function scrapeFs(): Promise<void> {
console.log("=== FS.com Scraper Starting ===\n");
const vendorId = await ensureVendor(
"FS.COM",
"compatible",
"https://www.fs.com",
"https://www.fs.com/c/optical-transceivers-9"
);
console.log(`Vendor ID: ${vendorId}`);
const products: FsProduct[] = [];
let pagesScraped = 0;
const crawler = new PlaywrightCrawler({
maxConcurrency: 1,
maxRequestsPerMinute: 15,
requestHandlerTimeoutSecs: 60,
headless: true,
launchContext: {
launchOptions: {
args: ["--disable-blink-features=AutomationControlled"],
},
},
async requestHandler({ page, request, log }) {
const url = request.url;
log.info(`Scraping: ${url}`);
// Wait for product list to render
await page.waitForTimeout(3000);
// Try multiple selectors — FS.com changes DOM frequently
const productData = await page.evaluate(() => {
const results: Array<{
name: string;
href: string;
price: string;
stock: string;
partNumber: string;
}> = [];
// Strategy 1: Look for product links with prices nearby
const productLinks = document.querySelectorAll(
'a[href*="/products/"], a[href*="/product/"], .product-item a, .o-list-product a, [class*="product"] a[href]'
);
for (const link of productLinks) {
const el = link as HTMLAnchorElement;
const name = el.textContent?.trim() || "";
const href = el.getAttribute("href") || "";
if (!name || name.length < 5 || !href) continue;
// Find price in parent/sibling elements
const container =
el.closest('[class*="product"]') ||
el.closest('[class*="item"]') ||
el.closest("li") ||
el.parentElement?.parentElement;
let price = "";
let stock = "";
if (container) {
const priceEl = container.querySelector(
'[class*="price"], [class*="Price"], .o-price, span[data-price]'
);
price = priceEl?.textContent?.trim() || "";
const stockEl = container.querySelector(
'[class*="stock"], [class*="Stock"], [class*="avail"], .o-stock'
);
stock = stockEl?.textContent?.trim() || "";
}
// Extract part number from URL or text
const pn = href.split("/").pop()?.replace(".html", "")?.replace("#", "") || "";
if (name && (price || href.includes("/product"))) {
results.push({ name, href, price, stock, partNumber: pn });
}
}
// Strategy 2: Look for any element with $ or US$ price pattern
if (results.length === 0) {
const allText = document.querySelectorAll("*");
for (const el of allText) {
const text = el.textContent || "";
if (/US?\$\s*\d+\.\d{2}/.test(text) && text.length < 200) {
const linkEl = el.closest("a") || el.querySelector("a");
if (linkEl) {
results.push({
name: linkEl.textContent?.trim() || text.slice(0, 100),
href: linkEl.getAttribute("href") || "",
price: text.match(/US?\$\s*[\d,.]+/)?.[0] || "",
stock: "",
partNumber: "",
});
}
}
}
}
return results;
});
for (const item of productData) {
if (!item.name || !item.price) continue;
const { price, currency } = parsePrice(item.price);
const speedInfo = detectSpeed(item.name);
if (price > 0) {
products.push({
partNumber: item.partNumber || item.name.slice(0, 50),
name: item.name,
price,
currency,
stockLevel: item.stock ? parseStockLevel(item.stock) : "on_request",
quantity: item.stock ? parseQuantity(item.stock) : undefined,
url: item.href.startsWith("http") ? item.href : `${BASE_URL}${item.href}`,
formFactor: detectFormFactor(item.name),
speedGbps: speedInfo?.speedGbps,
speed: speedInfo?.speed,
reachLabel: detectReach(item.name),
});
}
}
pagesScraped++;
log.info(` Found ${productData.length} items on page`);
},
});
const startUrls = CATEGORY_URLS.map((path) => `${BASE_URL}${path}`);
await crawler.run(startUrls);
console.log(`\nPages scraped: ${pagesScraped}`);
console.log(`Products found: ${products.length}`);
// Deduplicate by partNumber
const uniqueProducts = new Map<string, FsProduct>();
for (const p of products) {
const key = p.partNumber || p.name;
if (!uniqueProducts.has(key)) {
uniqueProducts.set(key, p);
}
}
// Write to database
let written = 0;
let skipped = 0;
for (const p of uniqueProducts.values()) {
try {
const transceiverId = await findOrCreateScrapedTransceiver({
partNumber: p.partNumber,
vendorId,
formFactor: p.formFactor,
speedGbps: p.speedGbps,
speed: p.speed,
reachLabel: p.reachLabel,
category: "DataCenter",
});
const hash = contentHash({ price: p.price, stock: p.stockLevel, qty: p.quantity });
const isNew = await upsertPriceObservation({
transceiverId,
sourceVendorId: vendorId,
price: p.price,
currency: p.currency,
stockLevel: p.stockLevel,
quantityAvailable: p.quantity,
url: p.url,
contentHash: hash,
});
if (isNew) written++;
else skipped++;
} catch (err) {
console.error(` Error: ${p.partNumber}:`, (err as Error).message);
}
}
console.log(`\nDatabase: ${written} new, ${skipped} unchanged (${uniqueProducts.size} unique)`);
console.log("=== FS.com Scraper Complete ===\n");
}
if (require.main === module) {
scrapeFs()
.then(() => pool.end())
.catch((err) => {
console.error("Fatal:", err);
pool.end();
process.exit(1);
});
}

View File

@ -0,0 +1,269 @@
/**
* News Aggregator Optics & Fiber Trade Press RSS Scraper
*
* Sources:
* - optics.org (photonics industry news)
* - SPIE Newsroom (photonics research)
* - Network World (data center / networking)
* - Light Reading (telecom)
* - Telecom Ramblings (industry commentary)
*
* Stores articles in news_articles table.
* Relevance filtering: keyword scoring for transceiver/optics topics.
*/
import { pool } from "../utils/db";
import { contentHash } from "../utils/hash";
import { parseStringPromise } from "xml2js";
// Categories allowed by news_articles CHECK constraint
type NewsCategory = "product_launch" | "market_report" | "standard" | "m_and_a" | "factory" | "event";
interface RssFeed {
name: string;
url: string;
category: NewsCategory;
}
interface NewsArticle {
title: string;
sourceUrl: string;
summary: string;
publishedAt: Date;
source: string;
category: NewsCategory | null;
relevanceScore: number;
contentHash: string;
}
const FEEDS: RssFeed[] = [
{
name: "Optics.org",
url: "https://optics.org/rss/news",
category: "market_report",
},
{
name: "SPIE Newsroom",
url: "https://www.spie.org/newsroom/rss.xml",
category: "market_report",
},
{
name: "Network World - Data Center",
url: "https://www.networkworld.com/category/data-center/index.rss",
category: "market_report",
},
{
name: "CableFree",
url: "https://www.cablefree.net/rss",
category: "market_report",
},
{
name: "Nature Photonics",
url: "https://www.nature.com/nphoton.rss",
category: "standard",
},
];
// Keywords for relevance scoring
const HIGH_RELEVANCE = [
"transceiver", "sfp", "qsfp", "xfp", "cfp", "osfp",
"optical module", "fiber optic", "wavelength", "dwdm", "cwdm",
"400g", "800g", "1.6t", "coherent", "pluggable",
"ofc", "ecoc", "cioe",
];
const MEDIUM_RELEVANCE = [
"data center", "datacenter", "interconnect", "bandwidth",
"switch", "router", "cisco", "arista", "juniper",
"100g", "40g", "25g", "10g",
"silicon photonics", "photonic",
"ii-vi", "coherent", "lumentum", "inphi",
"flexoptix", "prolabs",
];
function scoreRelevance(title: string, summary: string): number {
const text = `${title} ${summary}`.toLowerCase();
let score = 0;
for (const kw of HIGH_RELEVANCE) {
if (text.includes(kw)) score += 3;
}
for (const kw of MEDIUM_RELEVANCE) {
if (text.includes(kw)) score += 1;
}
return score;
}
async function fetchFeed(feed: RssFeed): Promise<NewsArticle[]> {
const articles: NewsArticle[] = [];
try {
const resp = await fetch(feed.url, {
headers: {
"User-Agent": "Mozilla/5.0 (compatible; TIP-NewsBot/1.0; +https://flexoptix.net)",
Accept: "application/rss+xml, application/xml, text/xml",
},
signal: AbortSignal.timeout(15000),
});
if (!resp.ok) {
console.warn(` Feed ${feed.name} returned ${resp.status}`);
return [];
}
const rawXml = await resp.text();
// Sanitize common RSS issues: unescaped & in URLs, attribute-without-value
const xml = rawXml
.replace(/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;|#x[\dA-Fa-f]+;)/g, "&amp;")
.replace(/(<\w[^>]*)\s+(\w+)=([^"'\s>]+)(?=[\s/>])/g, '$1 $2="$3"');
const parsed = await parseStringPromise(xml, { explicitArray: false, strict: false });
// strict: false makes keys uppercase; support both
const rss = parsed?.rss || parsed?.RSS;
const channel = rss?.channel || rss?.CHANNEL || parsed?.feed || parsed?.FEED;
if (!channel) return [];
const items = channel.item || channel.ITEM || channel.entry || channel.ENTRY || [];
const itemArray = Array.isArray(items) ? items : [items];
for (const item of itemArray) {
const title = extractText(item.title || item.TITLE) || "";
const url = extractLink(item) || "";
const summary = extractText(
item.description || item.DESCRIPTION || item.summary || item.SUMMARY || item["content:encoded"]
) || "";
const pubDate = item.pubDate || item.PUBDATE || item.published || item.updated || "";
if (!title || !url) continue;
const publishedAt = pubDate ? new Date(pubDate) : new Date();
if (isNaN(publishedAt.getTime())) continue;
// Skip articles older than 7 days
const ageMs = Date.now() - publishedAt.getTime();
if (ageMs > 7 * 24 * 60 * 60 * 1000) continue;
const relevanceScore = scoreRelevance(title, summary);
const hash = contentHash({ title, url });
articles.push({
title: title.slice(0, 500),
sourceUrl: url.slice(0, 1000),
summary: stripHtml(summary).slice(0, 2000),
publishedAt,
source: feed.name,
category: feed.category as NewsCategory,
relevanceScore,
contentHash: hash,
});
}
} catch (err) {
console.warn(` Feed ${feed.name} error:`, (err as Error).message);
}
return articles;
}
function extractText(value: unknown): string {
if (!value) return "";
if (typeof value === "string") return value;
if (typeof value === "object" && value !== null) {
const obj = value as Record<string, unknown>;
return String(obj._ || obj["#text"] || "");
}
return String(value);
}
function extractLink(item: Record<string, unknown>): string {
const link = item.link || item.LINK;
if (typeof link === "string") return link;
if (Array.isArray(link)) {
const rel = (link as Array<Record<string, unknown>>).find(
(l) => !l["$"] || (l["$"] as Record<string, string>).rel === "alternate"
);
return String((rel?.["$"] as Record<string, string>)?.href || rel?._ || "");
}
if (typeof link === "object" && link !== null) {
const l = link as Record<string, unknown>;
return String((l["$"] as Record<string, string>)?.href || l._ || "");
}
return "";
}
function stripHtml(html: string): string {
return html
.replace(/<[^>]+>/g, " ")
.replace(/&nbsp;/g, " ")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/\s+/g, " ")
.trim();
}
async function upsertArticle(article: NewsArticle): Promise<boolean> {
const result = await pool.query(
`INSERT INTO news_articles (title, source_url, summary, published_at, source, category, relevance_score, content_hash)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT (source_url) DO UPDATE
SET relevance_score = EXCLUDED.relevance_score,
content_hash = EXCLUDED.content_hash
RETURNING (xmax = 0) AS inserted`,
[
article.title,
article.sourceUrl,
article.summary,
article.publishedAt,
article.source,
article.category,
article.relevanceScore,
article.contentHash,
]
);
return result.rows[0]?.inserted ?? true;
}
export async function scrapeNews(): Promise<void> {
console.log("=== News Scraper Starting ===\n");
let totalFetched = 0;
let totalWritten = 0;
let totalRelevant = 0;
for (const feed of FEEDS) {
console.log(`Fetching: ${feed.name} (${feed.url})`);
const articles = await fetchFeed(feed);
console.log(`${articles.length} articles (last 7 days)`);
for (const article of articles) {
totalFetched++;
if (article.relevanceScore > 0) totalRelevant++;
try {
const isNew = await upsertArticle(article);
if (isNew) totalWritten++;
} catch (err) {
console.error(` Error saving article:`, (err as Error).message);
}
}
// Rate limit between feeds
await new Promise((r) => setTimeout(r, 1000));
}
console.log(`\nFetched: ${totalFetched} articles`);
console.log(`Relevant (score > 0): ${totalRelevant}`);
console.log(`Written: ${totalWritten} new`);
console.log("=== News Scraper Complete ===\n");
}
if (require.main === module) {
scrapeNews()
.then(() => pool.end())
.catch((err) => {
console.error("Fatal:", err);
pool.end();
process.exit(1);
});
}

View File

@ -0,0 +1,297 @@
/**
* Optcore.net Scraper Most transparent pricing in the industry.
* Prices start at $5.50, fully public, no bot protection.
*
* Strategy: WP REST API to enumerate transceiver product URLs,
* then PlaywrightCrawler to render each page and extract price.
*
* Optcore uses Flatsome WooCommerce with Cloudflare Rocket Loader
* (JS lazy-loading) static HTML has no product data.
*/
import { PlaywrightCrawler } from "crawlee";
import { ensureVendor, upsertPriceObservation, findOrCreateScrapedTransceiver, pool } from "../utils/db";
import { contentHash, parsePrice, parseStockLevel } from "../utils/hash";
const BASE_URL = "https://www.optcore.net";
// Transceiver category IDs from /wp-json/wp/v2/product_cat
// Filtered to optical transceiver categories with products
const TRANSCEIVER_CATEGORY_IDS = [
309, // 10G SFP+
173, // 1G SFP
76, // 100G QSFP28
79, // 25G SFP28
73, // 40G QSFP+
311, // 10G BiDi SFP+
313, // 10G CWDM SFP+
312, // 10G DWDM SFP+
333, // 10G XFP
1088, // 10GBase-T SFP+
59, // 8G/10G/16G SFP+
1102, // BiDi SFP
4097, // 400G QSFP-DD
77, // 100G CFP/CFP2/CFP4
4101, // 200G QSFP56
4092, // 50G SFP56
6441, // 800G OSFP
];
interface OptcoreProduct {
partNumber: string;
name: string;
price: number;
currency: string;
stockLevel: string;
url: string;
formFactor?: string;
speedGbps?: number;
speed?: string;
reachLabel?: string;
}
function detectFormFactor(text: string): string | undefined {
const lower = text.toLowerCase();
if (lower.includes("osfp") && !lower.includes("qsfp")) return "OSFP";
if (lower.includes("qsfp-dd")) return "QSFP-DD";
if (lower.includes("qsfp56")) return "QSFP56";
if (lower.includes("qsfp28")) return "QSFP28";
if (lower.includes("qsfp+") || lower.includes("qsfp plus")) return "QSFP+";
if (lower.includes("sfp28")) return "SFP28";
if (lower.includes("sfp56")) return "SFP56";
if (lower.includes("sfp+") || lower.includes("sfp plus")) return "SFP+";
if (lower.includes("cfp4")) return "CFP4";
if (lower.includes("cfp2")) return "CFP2";
if (lower.includes("cfp")) return "CFP";
if (lower.includes("xfp")) return "XFP";
if (lower.includes("sfp") && !lower.includes("qsfp")) return "SFP";
return undefined;
}
function detectSpeed(text: string): { speed: string; speedGbps: number } | undefined {
const patterns: [RegExp, string, number][] = [
[/800\s*g/i, "800G", 800],
[/400\s*g/i, "400G", 400],
[/200\s*g/i, "200G", 200],
[/100\s*g/i, "100G", 100],
[/50\s*g/i, "50G", 50],
[/40\s*g/i, "40G", 40],
[/25\s*g/i, "25G", 25],
[/16\s*g/i, "16G", 16],
[/10\s*g/i, "10G", 10],
[/1000\s*base/i, "1G", 1],
[/1\s*g\b/i, "1G", 1],
];
for (const [re, speed, gbps] of patterns) {
if (re.test(text)) return { speed, speedGbps: gbps };
}
return undefined;
}
function detectReach(text: string): string | undefined {
const match = text.match(/(\d+)\s*(m|km)\b/i);
if (match) return `${match[1]}${match[2].toLowerCase()}`;
return undefined;
}
/**
* Fetch product URLs for transceiver categories via WP REST API.
* Returns up to 2000 product URLs with title + slug for metadata.
*/
async function fetchTransceiverUrls(): Promise<Array<{ url: string; title: string; partNumber: string }>> {
const results: Array<{ url: string; title: string; partNumber: string }> = [];
const seen = new Set<string>();
for (const catId of TRANSCEIVER_CATEGORY_IDS) {
let page = 1;
let hasMore = true;
while (hasMore) {
const apiUrl = `${BASE_URL}/wp-json/wp/v2/product?product_cat=${catId}&per_page=100&page=${page}&_fields=slug,link,title`;
try {
const resp = await fetch(apiUrl, {
headers: { "User-Agent": "Mozilla/5.0 (compatible; TIP-Scraper/1.0)" },
signal: AbortSignal.timeout(15000),
});
if (!resp.ok) break;
const totalPages = parseInt(resp.headers.get("X-WP-TotalPages") || "1");
const products: Array<{ slug: string; link: string; title: { rendered: string } }> = await resp.json();
for (const p of products) {
if (!seen.has(p.slug)) {
seen.add(p.slug);
results.push({
url: p.link,
title: p.title.rendered,
partNumber: p.slug,
});
}
}
hasMore = page < totalPages;
page++;
// Rate limit: 10 req/sec max
await new Promise((r) => setTimeout(r, 100));
} catch {
hasMore = false;
}
}
}
return results;
}
export async function scrapeOptcore(): Promise<void> {
console.log("=== Optcore.net Scraper Starting ===\n");
const vendorId = await ensureVendor(
"Optcore",
"compatible",
"https://www.optcore.net",
"https://www.optcore.net/product-category/optical-transceiver/"
);
console.log(`Vendor ID: ${vendorId}`);
// Step 1: Enumerate transceiver product URLs via WP REST API
console.log("Fetching product URLs via WP REST API...");
const productMeta = await fetchTransceiverUrls();
console.log(`Found ${productMeta.length} transceiver product URLs`);
// Build a map for quick metadata lookup
const metaByUrl = new Map(productMeta.map((p) => [p.url, p]));
const products: OptcoreProduct[] = [];
let pagesScraped = 0;
// Step 2: Render each product page with Playwright to extract price
const crawler = new PlaywrightCrawler({
maxConcurrency: 3,
maxRequestsPerMinute: 30,
requestHandlerTimeoutSecs: 30,
headless: true,
launchContext: {
launchOptions: {
args: ["--disable-blink-features=AutomationControlled", "--no-sandbox"],
},
},
async requestHandler({ page, request, log }) {
const url = request.url;
log.info(`Scraping: ${url}`);
// Wait for WooCommerce price element to appear
try {
await page.waitForSelector(".woocommerce-Price-amount, .price .amount, [class*=\"price\"]", {
timeout: 8000,
});
} catch {
// Price element not found — might be out of stock or JS failed
log.warning(`No price element found: ${url}`);
pagesScraped++;
return;
}
const data = await page.evaluate(() => {
// Product title
const title =
document.querySelector("h1.product_title, h1.entry-title, h1")?.textContent?.trim() || "";
// Price — WooCommerce renders: <span class="price"><bdi><span class="woocommerce-Price-currencySymbol">$</span>5.50</bdi></span>
const priceEl = document.querySelector(
".price ins .woocommerce-Price-amount, .price .woocommerce-Price-amount, .woocommerce-Price-amount"
);
const priceText = priceEl?.textContent?.trim() || "";
// Stock
const stockEl = document.querySelector(".stock, .availability, [class*=\"stock\"]");
const stockText = stockEl?.textContent?.trim() || "";
return { title, priceText, stockText };
});
const meta = metaByUrl.get(url);
const name = data.title || meta?.title || url.split("/").filter(Boolean).pop() || "";
const partNumber = meta?.partNumber || url.split("/").filter(Boolean).pop() || "";
const { price, currency } = parsePrice(data.priceText);
if (price > 0) {
const speedInfo = detectSpeed(name);
products.push({
partNumber,
name,
price,
currency,
stockLevel: data.stockText ? parseStockLevel(data.stockText) : "in_stock",
url,
formFactor: detectFormFactor(name),
speedGbps: speedInfo?.speedGbps,
speed: speedInfo?.speed,
reachLabel: detectReach(name),
});
}
pagesScraped++;
},
});
const urls = productMeta.map((p) => p.url);
await crawler.run(urls);
console.log(`\nPages scraped: ${pagesScraped}`);
console.log(`Products with price: ${products.length}`);
// Deduplicate
const unique = new Map<string, OptcoreProduct>();
for (const p of products) {
if (!unique.has(p.partNumber)) unique.set(p.partNumber, p);
}
// Write to DB
let written = 0;
let skipped = 0;
for (const p of unique.values()) {
try {
const transceiverId = await findOrCreateScrapedTransceiver({
partNumber: p.partNumber,
vendorId,
formFactor: p.formFactor,
speedGbps: p.speedGbps,
speed: p.speed,
reachLabel: p.reachLabel,
category: "DataCenter",
});
const hash = contentHash({ price: p.price, stock: p.stockLevel });
const isNew = await upsertPriceObservation({
transceiverId,
sourceVendorId: vendorId,
price: p.price,
currency: p.currency,
stockLevel: p.stockLevel,
url: p.url,
contentHash: hash,
});
if (isNew) written++;
else skipped++;
} catch (err) {
console.error(` Error: ${p.partNumber}:`, (err as Error).message);
}
}
console.log(`\nDatabase: ${written} new, ${skipped} unchanged (${unique.size} unique)`);
console.log("=== Optcore.net Scraper Complete ===\n");
}
if (require.main === module) {
scrapeOptcore()
.then(() => pool.end())
.catch((err) => {
console.error("Fatal:", err);
pool.end();
process.exit(1);
});
}

View File

@ -0,0 +1,123 @@
import { Pool } from "pg";
import { config } from "dotenv";
import { join } from "path";
config({ path: join(__dirname, "..", "..", "..", "..", ".env") });
export const pool = new Pool({
host: process.env.POSTGRES_HOST || "localhost",
port: parseInt(process.env.POSTGRES_PORT || "5433"),
database: process.env.POSTGRES_DB || "transceiver_db",
user: process.env.POSTGRES_USER || "tip",
password: process.env.POSTGRES_PASSWORD || "tip_dev_2026",
max: 10,
});
export async function upsertPriceObservation(params: {
transceiverId: string;
sourceVendorId: string;
price: number;
currency: string;
stockLevel: string;
quantityAvailable?: number;
leadTimeDays?: number;
url?: string;
contentHash: string;
}): Promise<boolean> {
// Check if price changed via content hash
const existing = await pool.query(
`SELECT content_hash FROM price_observations
WHERE transceiver_id = $1 AND source_vendor_id = $2
ORDER BY time DESC LIMIT 1`,
[params.transceiverId, params.sourceVendorId]
);
if (existing.rows.length > 0 && existing.rows[0].content_hash === params.contentHash) {
return false; // No change
}
await pool.query(
`INSERT INTO price_observations (time, transceiver_id, source_vendor_id, price, currency, stock_level, quantity_available, lead_time_days, url, content_hash)
VALUES (NOW(), $1, $2, $3, $4, $5, $6, $7, $8, $9)`,
[
params.transceiverId,
params.sourceVendorId,
params.price,
params.currency,
params.stockLevel,
params.quantityAvailable || null,
params.leadTimeDays || null,
params.url || null,
params.contentHash,
]
);
return true; // New observation written
}
export async function findOrCreateScrapedTransceiver(params: {
partNumber: string;
vendorId: string;
formFactor?: string;
speedGbps?: number;
speed?: string;
reachMeters?: number;
reachLabel?: string;
fiberType?: string;
wavelengths?: string;
category?: string;
}): Promise<string> {
// Try to match existing transceiver by part number + vendor
const existing = await pool.query(
`SELECT id FROM transceivers WHERE part_number = $1 AND vendor_id = $2`,
[params.partNumber, params.vendorId]
);
if (existing.rows.length > 0) {
return existing.rows[0].id;
}
// Create new transceiver entry
const slug = `scraped-${params.partNumber.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
const result = await pool.query(
`INSERT INTO transceivers (slug, part_number, vendor_id, form_factor, speed_gbps, speed, reach_meters, reach_label, fiber_type, wavelengths, category, market_status)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, 'Mainstream')
ON CONFLICT (slug) DO UPDATE SET updated_at = NOW()
RETURNING id`,
[
slug,
params.partNumber,
params.vendorId,
params.formFactor || "SFP",
params.speedGbps || 0,
params.speed || "Unknown",
params.reachMeters || 0,
params.reachLabel || "",
params.fiberType || "",
params.wavelengths || "",
params.category || "DataCenter",
]
);
return result.rows[0].id;
}
export async function getVendorId(name: string): Promise<string | null> {
const result = await pool.query(`SELECT id FROM vendors WHERE name = $1`, [name]);
return result.rows[0]?.id || null;
}
export async function ensureVendor(
name: string,
type: string,
website?: string,
shopUrl?: string
): Promise<string> {
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
const result = await pool.query(
`INSERT INTO vendors (name, slug, type, website, shop_url, is_competitor)
VALUES ($1, $2, $3, $4, $5, true)
ON CONFLICT (name) DO UPDATE SET shop_url = COALESCE(EXCLUDED.shop_url, vendors.shop_url)
RETURNING id`,
[name, slug, type, website || null, shopUrl || null]
);
return result.rows[0].id;
}

View File

@ -0,0 +1,71 @@
import { createHash } from "crypto";
/**
* Generate SHA-256 content hash for change detection.
* Only hashes the fields that matter (price, stock, quantity).
*/
export function contentHash(data: Record<string, unknown>): string {
const normalized = JSON.stringify(data, Object.keys(data).sort());
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
}
/**
* Parse price string into number.
* Handles: "$12.50", "12,50 €", "US$12.50", "12.50 USD"
*/
export function parsePrice(raw: string): { price: number; currency: string } {
const cleaned = raw.replace(/[^\d.,]/g, "").replace(",", ".");
const price = parseFloat(cleaned);
const currency = raw.includes("€")
? "EUR"
: raw.includes("£")
? "GBP"
: raw.includes("¥")
? "CNY"
: "USD";
return { price: isNaN(price) ? 0 : price, currency };
}
/**
* Determine stock level from various text representations.
*/
export function parseStockLevel(
raw: string
): "in_stock" | "low_stock" | "out_of_stock" | "on_request" | "discontinued" {
const lower = raw.toLowerCase();
if (lower.includes("in stock") || lower.includes("auf lager") || lower.includes("available"))
return "in_stock";
if (lower.includes("low stock") || lower.includes("few left") || lower.includes("limited"))
return "low_stock";
if (
lower.includes("out of stock") ||
lower.includes("sold out") ||
lower.includes("nicht verfügbar") ||
lower.includes("unavailable")
)
return "out_of_stock";
if (lower.includes("discontinued") || lower.includes("eol") || lower.includes("end of life"))
return "discontinued";
return "on_request";
}
/**
* Extract numeric quantity from stock text.
* "23 in stock" 23, "500+ available" 500
*/
export function parseQuantity(raw: string): number | undefined {
const match = raw.match(/(\d+)\+?\s*(in stock|available|auf lager|stück|units|pcs)/i);
return match ? parseInt(match[1]) : undefined;
}
/**
* Parse lead time from text.
* "Ships in 3-5 days" 5, "2 weeks" 14
*/
export function parseLeadTime(raw: string): number | undefined {
const dayMatch = raw.match(/(\d+)\s*(business\s+)?days?/i);
if (dayMatch) return parseInt(dayMatch[1]);
const weekMatch = raw.match(/(\d+)\s*weeks?/i);
if (weekMatch) return parseInt(weekMatch[1]) * 7;
return undefined;
}

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

42
scripts/migrate.ts Normal file
View File

@ -0,0 +1,42 @@
import { readFileSync, readdirSync } from "fs";
import { join } from "path";
import { Pool } from "pg";
import { config } from "dotenv";
config();
const pool = new Pool({
host: process.env.POSTGRES_HOST || "localhost",
port: parseInt(process.env.POSTGRES_PORT || "5432"),
database: process.env.POSTGRES_DB || "transceiver_db",
user: process.env.POSTGRES_USER || "tip",
password: process.env.POSTGRES_PASSWORD || "tip_dev_2026",
});
async function migrate(): Promise<void> {
const sqlDir = join(__dirname, "..", "sql");
const files = readdirSync(sqlDir)
.filter((f) => f.endsWith(".sql"))
.sort();
console.log(`Found ${files.length} migration files`);
const client = await pool.connect();
try {
for (const file of files) {
const sql = readFileSync(join(sqlDir, file), "utf-8");
console.log(`Running: ${file}...`);
await client.query(sql);
console.log(` Done: ${file}`);
}
console.log("\nAll migrations completed successfully.");
} catch (err) {
console.error("Migration failed:", err);
process.exit(1);
} finally {
client.release();
await pool.end();
}
}
migrate();

280
scripts/seed-from-npm.ts Normal file
View File

@ -0,0 +1,280 @@
/**
* Seed PostgreSQL from the existing @tip/core npm package data.
* Imports: 159 transceivers, 42 standards, 12 competitors, 11 breakouts.
*/
import { Pool } from "pg";
import { config } from "dotenv";
import { join } from "path";
config();
// Dynamic import of core package (ESM compat)
async function loadCoreData() {
const corePath = join(__dirname, "..", "packages", "core", "src");
// We need to use tsx to run this, so we can import .ts files directly
const { transceivers } = await import(join(corePath, "database"));
const { standards } = await import(join(corePath, "standards"));
const { competitors } = await import(join(corePath, "market"));
const { breakouts } = await import(join(corePath, "breakouts"));
return { transceivers, standards, competitors, breakouts };
}
const pool = new Pool({
host: process.env.POSTGRES_HOST || "localhost",
port: parseInt(process.env.POSTGRES_PORT || "5432"),
database: process.env.POSTGRES_DB || "transceiver_db",
user: process.env.POSTGRES_USER || "tip",
password: process.env.POSTGRES_PASSWORD || "tip_dev_2026",
});
function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "");
}
async function seedVendors(client: any, competitors: readonly any[]): Promise<Map<string, string>> {
console.log("\nSeeding vendors...");
const vendorIdMap = new Map<string, string>();
// Insert Flexoptix first as primary vendor
const flexResult = await client.query(
`INSERT INTO vendors (name, slug, type, headquarters, country, website, shop_url, is_competitor, market_position, specialties, strengths, weaknesses)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT (name) DO UPDATE SET updated_at = NOW()
RETURNING id`,
[
"FLEXOPTIX",
"flexoptix",
"compatible",
"Mainz, Germany",
"Germany",
"https://www.flexoptix.net",
"https://www.flexoptix.net/en/",
false,
"Premium compatible optics with FlexBox programmer, 300+ vendor support",
["compatible optics", "FlexBox", "all speeds", "premium quality"],
["FlexBox programmer", "300+ vendor support", "lifetime warranty", "German engineering"],
[],
]
);
vendorIdMap.set("FLEXOPTIX", flexResult.rows[0].id);
console.log(" Inserted: FLEXOPTIX (primary)");
for (const comp of competitors) {
if (comp.name === "FLEXOPTIX") continue; // Already inserted
const typeMap: Record<string, string> = {
OEM: "oem",
Whitebox: "compatible",
Manufacturer: "manufacturer",
Distributor: "distributor",
Compatible: "compatible",
};
const result = await client.query(
`INSERT INTO vendors (name, slug, type, headquarters, is_competitor, market_position, specialties, strengths, weaknesses)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT (name) DO UPDATE SET updated_at = NOW()
RETURNING id`,
[
comp.name,
slugify(comp.name),
typeMap[comp.type] || "oem",
comp.headquarters,
true,
comp.marketPosition,
[...comp.formFactorsOffered, ...comp.speedTiersOffered],
comp.strengths,
comp.weaknesses,
]
);
vendorIdMap.set(comp.name, result.rows[0].id);
console.log(` Inserted: ${comp.name}`);
}
console.log(` Total vendors: ${vendorIdMap.size}`);
return vendorIdMap;
}
async function seedStandards(client: any, standards: readonly any[]): Promise<Map<string, string>> {
console.log("\nSeeding standards...");
const standardIdMap = new Map<string, string>();
for (const std of standards) {
const body = std.ieeeReference?.startsWith("IEEE")
? "IEEE"
: std.standard.includes("ZR")
? "OIF"
: std.ieeeReference
? "IEEE"
: "de_facto";
const result = await client.query(
`INSERT INTO standards (name, ieee_reference, body, speed, speed_gbps, lanes, lane_rate, modulation, fiber_type, wavelength, max_reach_meters, max_reach_label, connector, fec_required, form_factors, year_ratified, notes)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
ON CONFLICT (name) DO UPDATE SET notes = EXCLUDED.notes
RETURNING id`,
[
std.standard,
std.ieeeReference || null,
body,
std.speed,
parseFloat(std.speed) || null,
std.lanes,
std.laneRate,
std.modulation,
std.fiberType,
std.wavelength,
std.maxReachMeters,
std.maxReachLabel,
std.connector,
std.fecRequired,
std.formFactors,
std.yearRatified,
std.notes,
]
);
standardIdMap.set(std.standard, result.rows[0].id);
}
console.log(` Total standards: ${standardIdMap.size}`);
return standardIdMap;
}
async function seedTransceivers(
client: any,
transceivers: readonly any[],
standardIdMap: Map<string, string>
): Promise<void> {
console.log("\nSeeding transceivers...");
let count = 0;
for (const t of transceivers) {
const standardId = standardIdMap.get(t.standard) || null;
// Detect WDM type
let wdmType = null;
if (t.category === "CWDM" || t.wavelengths?.includes("CWDM")) wdmType = "CWDM";
if (t.category === "DWDM" || t.wavelengths?.includes("DWDM") || t.standard?.includes("DWDM"))
wdmType = "DWDM";
// Detect coherent
const coherent =
t.category === "Coherent" ||
t.standard?.includes("ZR") ||
t.modulation?.includes("DP-") ||
false;
await client.query(
`INSERT INTO transceivers (
slug, standard_name, standard_id, ieee_reference, form_factor,
speed, speed_gbps, lanes, lane_rate, modulation,
reach_meters, reach_label, fiber_type, wavelengths, connector,
power_consumption_w, temp_range, category, price_tier, use_case,
vendor_compat, tags, generation, market_status, year_introduced,
breakout_capable, breakout_to, wdm_type, coherent
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
$11, $12, $13, $14, $15, $16, $17, $18, $19, $20,
$21, $22, $23, $24, $25, $26, $27, $28, $29
) ON CONFLICT (slug) DO UPDATE SET updated_at = NOW()`,
[
t.id,
t.standard,
standardId,
t.ieeeReference || null,
t.formFactor,
t.speed,
t.speedGbps,
t.lanes || null,
t.laneRate || null,
t.modulation || null,
t.reachMeters,
t.reachLabel,
t.fiberType,
t.wavelengths,
t.connector,
t.powerConsumptionW,
t.tempRange,
t.category,
t.priceTier,
t.useCase,
JSON.stringify(t.vendors),
t.tags,
t.generation || null,
t.marketStatus || "Mainstream",
t.yearIntroduced || null,
t.breakoutCapable || false,
t.breakoutTo || null,
wdmType,
coherent,
]
);
count++;
}
console.log(` Total transceivers: ${count}`);
}
async function seedBreakouts(client: any, breakouts: readonly any[]): Promise<void> {
console.log("\nSeeding breakouts...");
for (const b of breakouts) {
await client.query(
`INSERT INTO breakouts (slug, from_standard, to_standard, form_factor, description, cable_type, max_length, speed_per_lane)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT (slug) DO UPDATE SET from_standard = $2`,
[b.id, b.from, b.to, b.formFactor, b.description, b.cableType, b.maxLength, b.speedPerLane]
);
}
console.log(` Total breakouts: ${breakouts.length}`);
}
async function main(): Promise<void> {
console.log("=== TIP Seed: Importing from @tip/core ===\n");
const { transceivers, standards, competitors, breakouts } = await loadCoreData();
console.log(`Source data: ${transceivers.length} transceivers, ${standards.length} standards, ${competitors.length} competitors, ${breakouts.length} breakouts`);
const client = await pool.connect();
try {
await client.query("BEGIN");
const vendorIdMap = await seedVendors(client, competitors);
const standardIdMap = await seedStandards(client, standards);
await seedTransceivers(client, transceivers, standardIdMap);
await seedBreakouts(client, breakouts);
await client.query("COMMIT");
console.log("\n=== Seed completed successfully ===");
// Print summary
const counts = await client.query(`
SELECT
(SELECT COUNT(*) FROM vendors) as vendors,
(SELECT COUNT(*) FROM standards) as standards,
(SELECT COUNT(*) FROM transceivers) as transceivers,
(SELECT COUNT(*) FROM breakouts) as breakouts
`);
console.log("\nDatabase summary:");
console.log(` Vendors: ${counts.rows[0].vendors}`);
console.log(` Standards: ${counts.rows[0].standards}`);
console.log(` Transceivers: ${counts.rows[0].transceivers}`);
console.log(` Breakouts: ${counts.rows[0].breakouts}`);
} catch (err) {
await client.query("ROLLBACK");
console.error("\nSeed failed:", err);
process.exit(1);
} finally {
client.release();
await pool.end();
}
}
main();

73
scripts/setup-erik.sh Executable file
View File

@ -0,0 +1,73 @@
#!/bin/bash
# TIP: Setup script for Erik server (.82)
# Run as root or with sudo
set -euo pipefail
echo "=== TIP: Erik Server Setup ==="
echo ""
# 1. PostgreSQL 17 + TimescaleDB
echo "--- Installing PostgreSQL 17 + TimescaleDB ---"
apt-get update
apt-get install -y gnupg2 lsb-release
# TimescaleDB repo (includes PostgreSQL 17)
echo "deb https://packagecloud.io/timescale/timescaledb/debian/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/timescaledb.list
curl -fsSL https://packagecloud.io/timescale/timescaledb/gpgkey | gpg --dearmor -o /etc/apt/trusted.gpg.d/timescaledb.gpg
apt-get update
apt-get install -y timescaledb-2-postgresql-17
# Enable TimescaleDB
timescaledb-tune --quiet --yes
# pgvector
apt-get install -y postgresql-17-pgvector
systemctl restart postgresql
systemctl enable postgresql
# Create DB and user
sudo -u postgres psql <<SQL
CREATE USER tip WITH PASSWORD '${POSTGRES_PASSWORD:-***REDACTED***}';
CREATE DATABASE transceiver_db OWNER tip;
GRANT ALL PRIVILEGES ON DATABASE transceiver_db TO tip;
\c transceiver_db
CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
SQL
echo "PostgreSQL 17 + TimescaleDB + pgvector ready."
# 2. Qdrant
echo ""
echo "--- Installing Qdrant (Docker) ---"
docker pull qdrant/qdrant:latest
docker run -d \
--name tip-qdrant \
--restart unless-stopped \
-p 6333:6333 \
-p 6334:6334 \
-v /opt/tip/qdrant:/qdrant/storage \
qdrant/qdrant:latest
echo "Qdrant running on port 6333."
# 3. App directory
echo ""
echo "--- Setting up app directory ---"
mkdir -p /opt/tip
cd /opt/tip
echo "=== Setup complete ==="
echo ""
echo "Next steps:"
echo " 1. Clone repo: git clone <repo> /opt/tip/app"
echo " 2. cd /opt/tip/app && npm install"
echo " 3. cp .env.example .env && edit .env"
echo " 4. npm run migrate"
echo " 5. npm run seed"
echo " 6. pm2 start ecosystem.config.js"

7
sql/001-extensions.sql Normal file
View File

@ -0,0 +1,7 @@
-- TIP: Transceiver Intelligence Platform
-- Migration 001: Extensions
CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE EXTENSION IF NOT EXISTS vector; -- pgvector
CREATE EXTENSION IF NOT EXISTS pg_trgm; -- Trigram similarity for fuzzy search
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID generation

469
sql/002-core-tables.sql Normal file
View File

@ -0,0 +1,469 @@
-- TIP: Transceiver Intelligence Platform
-- Migration 002: Core Tables
-- ============================================================
-- VENDORS (Hersteller, Distributoren, Reseller, OEMs)
-- ============================================================
CREATE TABLE IF NOT EXISTS vendors (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL UNIQUE,
slug TEXT NOT NULL UNIQUE,
type TEXT NOT NULL CHECK (type IN ('manufacturer','distributor','oem','reseller','compatible')),
headquarters TEXT,
country TEXT,
website TEXT,
shop_url TEXT,
api_available BOOLEAN DEFAULT FALSE,
api_endpoint TEXT,
logo_r2_key TEXT,
founded_year INTEGER,
revenue_usd BIGINT,
employee_count INTEGER,
market_position TEXT,
specialties TEXT[] DEFAULT '{}',
scrape_config JSONB DEFAULT '{}',
last_scraped TIMESTAMPTZ,
is_competitor BOOLEAN DEFAULT FALSE,
is_factory BOOLEAN DEFAULT FALSE,
factory_locations TEXT[] DEFAULT '{}',
certifications TEXT[] DEFAULT '{}',
strengths TEXT[] DEFAULT '{}',
weaknesses TEXT[] DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- STANDARDS (IEEE, OIF, MSA)
-- ============================================================
CREATE TABLE IF NOT EXISTS standards (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL UNIQUE,
ieee_reference TEXT,
body TEXT CHECK (body IN ('IEEE','OIF','MSA','de_facto','proprietary')),
speed TEXT,
speed_gbps NUMERIC,
lanes INTEGER,
lane_rate TEXT,
lane_rate_gbps NUMERIC,
modulation TEXT,
fiber_type TEXT,
wavelength TEXT,
max_reach_meters INTEGER,
max_reach_label TEXT,
connector TEXT,
fec_required BOOLEAN DEFAULT FALSE,
form_factors TEXT[] DEFAULT '{}',
year_draft INTEGER,
year_ratified INTEGER,
year_revised INTEGER,
status TEXT DEFAULT 'ratified' CHECK (status IN ('draft','ratified','revised','superseded')),
superseded_by TEXT,
member_count INTEGER,
notes TEXT,
url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- TRANSCEIVERS
-- ============================================================
CREATE TABLE IF NOT EXISTS transceivers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT NOT NULL UNIQUE,
vendor_id UUID REFERENCES vendors(id),
part_number TEXT,
standard_name TEXT,
standard_id UUID REFERENCES standards(id),
ieee_reference TEXT,
form_factor TEXT NOT NULL,
speed TEXT NOT NULL,
speed_gbps NUMERIC NOT NULL,
lanes INTEGER,
lane_rate TEXT,
lane_rate_gbps NUMERIC,
modulation TEXT,
reach_meters INTEGER NOT NULL,
reach_label TEXT,
fiber_type TEXT,
wavelengths TEXT,
connector TEXT,
power_consumption_w NUMERIC,
temp_range TEXT DEFAULT 'COM' CHECK (temp_range IN ('COM','IND')),
category TEXT,
dom_support BOOLEAN DEFAULT TRUE,
digital_diagnostics TEXT,
-- CWDM/DWDM
wdm_type TEXT CHECK (wdm_type IN ('CWDM','DWDM',NULL)),
channel_count INTEGER,
channel_spacing_ghz NUMERIC,
tunable BOOLEAN DEFAULT FALSE,
itu_grid TEXT,
-- Coherent
coherent BOOLEAN DEFAULT FALSE,
baud_rate_gbaud NUMERIC,
fec_type TEXT,
dsp_vendor TEXT,
-- Lifecycle
year_introduced INTEGER,
year_mainstream INTEGER,
year_peak INTEGER,
year_decline INTEGER,
market_status TEXT DEFAULT 'Mainstream' CHECK (market_status IN ('Mainstream','Growth','Emerging','Legacy','EOL')),
hype_cycle_phase TEXT,
generation TEXT,
-- Pricing
price_tier TEXT CHECK (price_tier IN ('Budget','Standard','Premium')),
msrp_usd NUMERIC,
street_price_usd NUMERIC,
-- Technical
optical_budget_db NUMERIC,
tx_power_min_dbm NUMERIC,
tx_power_max_dbm NUMERIC,
rx_sensitivity_dbm NUMERIC,
-- Breakout
breakout_capable BOOLEAN DEFAULT FALSE,
breakout_to TEXT,
-- Storage
datasheet_r2_key TEXT,
image_r2_key TEXT,
-- Meta
use_case TEXT,
tags TEXT[] DEFAULT '{}',
vendor_compat JSONB DEFAULT '[]',
notes TEXT,
-- Search vector (auto-populated by trigger)
search_vector TSVECTOR,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- SWITCHES
-- ============================================================
CREATE TABLE IF NOT EXISTS switches (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
vendor_id UUID REFERENCES vendors(id),
model TEXT NOT NULL,
series TEXT,
category TEXT CHECK (category IN ('DataCenter','Campus','Edge','Core','SP','Industrial')),
layer TEXT CHECK (layer IN ('L2','L3','L2/L3')),
managed BOOLEAN DEFAULT TRUE,
-- Ports
ports_config JSONB DEFAULT '{}',
total_ports INTEGER,
uplink_speed_gbps NUMERIC,
max_speed_gbps NUMERIC,
-- Performance
switching_capacity_tbps NUMERIC,
forwarding_rate_mpps NUMERIC,
latency_ns NUMERIC,
buffer_mb NUMERIC,
-- ASIC
asic_vendor TEXT,
asic_model TEXT,
asic_generation TEXT,
-- Features
poe_support TEXT DEFAULT 'None',
stacking_support BOOLEAN DEFAULT FALSE,
vxlan_support BOOLEAN DEFAULT FALSE,
evpn_support BOOLEAN DEFAULT FALSE,
bgp_support BOOLEAN DEFAULT FALSE,
mpls_support BOOLEAN DEFAULT FALSE,
openconfig_support BOOLEAN DEFAULT FALSE,
sonic_compatible BOOLEAN DEFAULT FALSE,
macsec_support BOOLEAN DEFAULT FALSE,
-- Lifecycle
release_date DATE,
eos_date DATE,
eol_date DATE,
last_support_date DATE,
lifecycle_status TEXT DEFAULT 'Active' CHECK (lifecycle_status IN ('Active','EoS_Announced','EoL','Legacy')),
successor_model TEXT,
-- Physical
rack_units NUMERIC,
max_power_w NUMERIC,
typical_power_w NUMERIC,
weight_kg NUMERIC,
airflow TEXT,
-- Pricing
msrp_usd NUMERIC,
street_price_usd NUMERIC,
-- Documentation
manual_r2_key TEXT,
datasheet_r2_key TEXT,
config_guide_r2_key TEXT,
compatibility_list_url TEXT,
-- Meta
tags TEXT[] DEFAULT '{}',
search_vector TSVECTOR,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(vendor_id, model)
);
-- ============================================================
-- COMPATIBILITY (Switch <-> Transceiver)
-- ============================================================
CREATE TABLE IF NOT EXISTS compatibility (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
switch_id UUID REFERENCES switches(id) ON DELETE CASCADE,
transceiver_id UUID REFERENCES transceivers(id) ON DELETE CASCADE,
verified_by TEXT,
verification_date DATE,
verification_method TEXT CHECK (verification_method IN ('tested','vendor_matrix','datasheet','community')),
status TEXT DEFAULT 'compatible' CHECK (status IN ('compatible','incompatible','partial','unknown')),
notes TEXT,
firmware_min TEXT,
known_issues TEXT,
source_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(switch_id, transceiver_id)
);
-- ============================================================
-- BREAKOUTS
-- ============================================================
CREATE TABLE IF NOT EXISTS breakouts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT NOT NULL UNIQUE,
from_standard TEXT NOT NULL,
to_standard TEXT NOT NULL,
form_factor TEXT,
description TEXT,
cable_type TEXT CHECK (cable_type IN ('Passive','Active')),
max_length TEXT,
speed_per_lane TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- TEMPLATES (FlexBox Coding + Switch Config)
-- ============================================================
CREATE TABLE IF NOT EXISTS templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
type TEXT NOT NULL CHECK (type IN ('flexbox_coding','switch_config')),
name TEXT NOT NULL,
description TEXT,
switch_vendor TEXT,
switch_series TEXT,
transceiver_type TEXT,
speed_gbps NUMERIC,
technology TEXT,
template_content TEXT NOT NULL,
variables JSONB DEFAULT '{}',
tags TEXT[] DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- DOCUMENTS (PDFs in R2)
-- ============================================================
CREATE TABLE IF NOT EXISTS documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entity_type TEXT NOT NULL CHECK (entity_type IN ('transceiver','switch','vendor','standard')),
entity_id UUID,
doc_type TEXT NOT NULL CHECK (doc_type IN ('manual','datasheet','config_guide','compatibility_list','faq','whitepaper')),
title TEXT,
filename TEXT,
r2_key TEXT NOT NULL,
source_url TEXT,
file_size_bytes BIGINT,
page_count INTEGER,
ocr_status TEXT DEFAULT 'pending' CHECK (ocr_status IN ('pending','processing','completed','failed')),
ocr_text TEXT,
language TEXT DEFAULT 'en',
content_hash TEXT,
last_checked TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- KNOWLEDGE BASE (FAQs, Troubleshooting)
-- ============================================================
CREATE TABLE IF NOT EXISTS knowledge_base (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
category TEXT NOT NULL CHECK (category IN ('troubleshooting','faq','best_practice','known_issue','compatibility_tip')),
subcategory TEXT,
question TEXT NOT NULL,
answer TEXT NOT NULL,
source_vendor TEXT,
source_url TEXT,
applies_to_form_factors TEXT[] DEFAULT '{}',
applies_to_speeds TEXT[] DEFAULT '{}',
applies_to_vendors TEXT[] DEFAULT '{}',
severity TEXT CHECK (severity IN ('critical','high','medium','low','info')),
resolution_steps JSONB,
last_verified TIMESTAMPTZ,
helpful_count INTEGER DEFAULT 0,
tags TEXT[] DEFAULT '{}',
search_vector TSVECTOR,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- FACTORIES
-- ============================================================
CREATE TABLE IF NOT EXISTS factories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
vendor_id UUID REFERENCES vendors(id),
name TEXT NOT NULL,
country TEXT NOT NULL,
city TEXT,
factory_type TEXT CHECK (factory_type IN ('manufacturing','assembly','r_and_d','headquarters')),
products TEXT[] DEFAULT '{}',
capacity_units_month INTEGER,
employee_count INTEGER,
certifications TEXT[] DEFAULT '{}',
expansion_planned BOOLEAN DEFAULT FALSE,
expansion_details TEXT,
source_url TEXT,
last_verified TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- NEWS ARTICLES
-- ============================================================
CREATE TABLE IF NOT EXISTS news_articles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
source TEXT NOT NULL,
source_url TEXT NOT NULL UNIQUE,
published_at TIMESTAMPTZ,
author TEXT,
summary TEXT,
full_text TEXT,
category TEXT CHECK (category IN ('product_launch','market_report','standard','m_and_a','factory','event')),
event TEXT,
mentioned_vendors TEXT[] DEFAULT '{}',
mentioned_products TEXT[] DEFAULT '{}',
mentioned_standards TEXT[] DEFAULT '{}',
sentiment_score NUMERIC,
relevance_score NUMERIC,
content_hash TEXT,
tags TEXT[] DEFAULT '{}',
search_vector TSVECTOR,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- BLOG DRAFTS
-- ============================================================
CREATE TABLE IF NOT EXISTS blog_drafts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
topic TEXT CHECK (topic IN ('hype_cycle','price_trend','new_product','comparison','tutorial')),
target_audience TEXT CHECK (target_audience IN ('sales','technical','customer','seo')),
outline JSONB,
draft_content TEXT,
data_sources JSONB,
status TEXT DEFAULT 'draft' CHECK (status IN ('draft','review','approved','published')),
generated_by TEXT,
word_count INTEGER,
seo_keywords TEXT[] DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- TRIGGERS: Auto-update search_vector
-- ============================================================
-- Transceiver search vector
CREATE OR REPLACE FUNCTION transceivers_search_vector_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector :=
setweight(to_tsvector('english', COALESCE(NEW.standard_name, '')), 'A') ||
setweight(to_tsvector('english', COALESCE(NEW.form_factor, '')), 'A') ||
setweight(to_tsvector('english', COALESCE(NEW.speed, '')), 'A') ||
setweight(to_tsvector('english', COALESCE(NEW.use_case, '')), 'B') ||
setweight(to_tsvector('english', COALESCE(NEW.category, '')), 'B') ||
setweight(to_tsvector('english', COALESCE(NEW.wavelengths, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(NEW.modulation, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(NEW.generation, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(array_to_string(NEW.tags, ' '), '')), 'D');
NEW.updated_at := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER transceivers_search_update
BEFORE INSERT OR UPDATE ON transceivers
FOR EACH ROW EXECUTE FUNCTION transceivers_search_vector_update();
-- Switch search vector
CREATE OR REPLACE FUNCTION switches_search_vector_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector :=
setweight(to_tsvector('english', COALESCE(NEW.model, '')), 'A') ||
setweight(to_tsvector('english', COALESCE(NEW.series, '')), 'A') ||
setweight(to_tsvector('english', COALESCE(NEW.category, '')), 'B') ||
setweight(to_tsvector('english', COALESCE(NEW.asic_vendor, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(NEW.asic_model, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(array_to_string(NEW.tags, ' '), '')), 'D');
NEW.updated_at := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER switches_search_update
BEFORE INSERT OR UPDATE ON switches
FOR EACH ROW EXECUTE FUNCTION switches_search_vector_update();
-- Knowledge base search vector
CREATE OR REPLACE FUNCTION kb_search_vector_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector :=
setweight(to_tsvector('english', COALESCE(NEW.question, '')), 'A') ||
setweight(to_tsvector('english', COALESCE(NEW.answer, '')), 'B') ||
setweight(to_tsvector('english', COALESCE(NEW.category, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(array_to_string(NEW.tags, ' '), '')), 'D');
NEW.updated_at := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER kb_search_update
BEFORE INSERT OR UPDATE ON knowledge_base
FOR EACH ROW EXECUTE FUNCTION kb_search_vector_update();
-- News search vector
CREATE OR REPLACE FUNCTION news_search_vector_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector :=
setweight(to_tsvector('english', COALESCE(NEW.title, '')), 'A') ||
setweight(to_tsvector('english', COALESCE(NEW.summary, '')), 'B') ||
setweight(to_tsvector('english', COALESCE(NEW.source, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(array_to_string(NEW.tags, ' '), '')), 'D');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER news_search_update
BEFORE INSERT OR UPDATE ON news_articles
FOR EACH ROW EXECUTE FUNCTION news_search_vector_update();

122
sql/003-timeseries.sql Normal file
View File

@ -0,0 +1,122 @@
-- TIP: Transceiver Intelligence Platform
-- Migration 003: TimescaleDB Hypertables
-- ============================================================
-- PRICE OBSERVATIONS (Real-time competitor pricing)
-- ============================================================
CREATE TABLE IF NOT EXISTS price_observations (
time TIMESTAMPTZ NOT NULL,
transceiver_id UUID NOT NULL,
source_vendor_id UUID NOT NULL,
price NUMERIC NOT NULL,
currency TEXT DEFAULT 'USD',
stock_level TEXT CHECK (stock_level IN ('in_stock','low_stock','out_of_stock','on_request','discontinued')),
quantity_available INTEGER,
lead_time_days INTEGER,
min_order_qty INTEGER,
url TEXT,
content_hash TEXT
);
SELECT create_hypertable('price_observations', by_range('time'),
if_not_exists => TRUE);
-- ============================================================
-- STOCK OBSERVATIONS (Separate stock tracking)
-- ============================================================
CREATE TABLE IF NOT EXISTS stock_observations (
time TIMESTAMPTZ NOT NULL,
transceiver_id UUID NOT NULL,
source_vendor_id UUID NOT NULL,
in_stock BOOLEAN NOT NULL,
quantity_available INTEGER,
lead_time_days INTEGER,
content_hash TEXT
);
SELECT create_hypertable('stock_observations', by_range('time'),
if_not_exists => TRUE);
-- ============================================================
-- MARKET METRICS (Hype Cycle input data)
-- ============================================================
CREATE TABLE IF NOT EXISTS market_metrics (
time TIMESTAMPTZ NOT NULL,
technology TEXT NOT NULL,
metric_type TEXT NOT NULL CHECK (metric_type IN (
'vendor_count','shipment_share','asp_decline_rate',
'media_hype_index','patent_filings','port_shipments',
'revenue_usd','asp_usd'
)),
value NUMERIC NOT NULL,
source TEXT,
notes TEXT
);
SELECT create_hypertable('market_metrics', by_range('time'),
if_not_exists => TRUE);
-- ============================================================
-- CONTINUOUS AGGREGATES
-- ============================================================
-- Daily price aggregates
CREATE MATERIALIZED VIEW IF NOT EXISTS price_daily
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 day', time) AS bucket,
transceiver_id,
source_vendor_id,
AVG(price) AS avg_price,
MIN(price) AS min_price,
MAX(price) AS max_price,
last(stock_level, time) AS latest_stock,
COUNT(*) AS observation_count
FROM price_observations
GROUP BY bucket, transceiver_id, source_vendor_id
WITH NO DATA;
-- Weekly price aggregates
CREATE MATERIALIZED VIEW IF NOT EXISTS price_weekly
WITH (timescaledb.continuous) AS
SELECT
time_bucket('7 days', time) AS bucket,
transceiver_id,
source_vendor_id,
AVG(price) AS avg_price,
MIN(price) AS min_price,
MAX(price) AS max_price,
COUNT(*) AS observation_count
FROM price_observations
GROUP BY bucket, transceiver_id, source_vendor_id
WITH NO DATA;
-- ============================================================
-- RETENTION POLICIES
-- ============================================================
-- Raw price data: keep 90 days
SELECT add_retention_policy('price_observations', INTERVAL '90 days',
if_not_exists => TRUE);
-- Raw stock data: keep 90 days
SELECT add_retention_policy('stock_observations', INTERVAL '90 days',
if_not_exists => TRUE);
-- Market metrics: keep 10 years (small volume)
-- No retention policy needed
-- ============================================================
-- REFRESH POLICIES for continuous aggregates
-- ============================================================
SELECT add_continuous_aggregate_policy('price_daily',
start_offset => INTERVAL '3 days',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour',
if_not_exists => TRUE);
SELECT add_continuous_aggregate_policy('price_weekly',
start_offset => INTERVAL '30 days',
end_offset => INTERVAL '7 days',
schedule_interval => INTERVAL '1 day',
if_not_exists => TRUE);

75
sql/004-indexes.sql Normal file
View File

@ -0,0 +1,75 @@
-- TIP: Transceiver Intelligence Platform
-- Migration 004: Indexes
-- ============================================================
-- FULL-TEXT SEARCH INDEXES (GIN on tsvector)
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_transceivers_search ON transceivers USING GIN(search_vector);
CREATE INDEX IF NOT EXISTS idx_switches_search ON switches USING GIN(search_vector);
CREATE INDEX IF NOT EXISTS idx_kb_search ON knowledge_base USING GIN(search_vector);
CREATE INDEX IF NOT EXISTS idx_news_search ON news_articles USING GIN(search_vector);
-- ============================================================
-- ARRAY INDEXES (GIN on text[])
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_transceivers_tags ON transceivers USING GIN(tags);
CREATE INDEX IF NOT EXISTS idx_switches_tags ON switches USING GIN(tags);
CREATE INDEX IF NOT EXISTS idx_vendors_specialties ON vendors USING GIN(specialties);
CREATE INDEX IF NOT EXISTS idx_standards_form_factors ON standards USING GIN(form_factors);
CREATE INDEX IF NOT EXISTS idx_kb_form_factors ON knowledge_base USING GIN(applies_to_form_factors);
CREATE INDEX IF NOT EXISTS idx_kb_speeds ON knowledge_base USING GIN(applies_to_speeds);
CREATE INDEX IF NOT EXISTS idx_news_vendors ON news_articles USING GIN(mentioned_vendors);
-- ============================================================
-- JSONB INDEXES
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_transceivers_vendor_compat ON transceivers USING GIN(vendor_compat);
CREATE INDEX IF NOT EXISTS idx_switches_ports ON switches USING GIN(ports_config);
-- ============================================================
-- B-TREE INDEXES (lookups, sorting)
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_transceivers_form_factor ON transceivers(form_factor);
CREATE INDEX IF NOT EXISTS idx_transceivers_speed_gbps ON transceivers(speed_gbps);
CREATE INDEX IF NOT EXISTS idx_transceivers_category ON transceivers(category);
CREATE INDEX IF NOT EXISTS idx_transceivers_market_status ON transceivers(market_status);
CREATE INDEX IF NOT EXISTS idx_transceivers_reach ON transceivers(reach_meters);
CREATE INDEX IF NOT EXISTS idx_transceivers_fiber_type ON transceivers(fiber_type);
CREATE INDEX IF NOT EXISTS idx_transceivers_wdm_type ON transceivers(wdm_type);
CREATE INDEX IF NOT EXISTS idx_transceivers_coherent ON transceivers(coherent);
CREATE INDEX IF NOT EXISTS idx_switches_vendor ON switches(vendor_id);
CREATE INDEX IF NOT EXISTS idx_switches_category ON switches(category);
CREATE INDEX IF NOT EXISTS idx_switches_lifecycle ON switches(lifecycle_status);
CREATE INDEX IF NOT EXISTS idx_switches_max_speed ON switches(max_speed_gbps);
CREATE INDEX IF NOT EXISTS idx_compatibility_switch ON compatibility(switch_id);
CREATE INDEX IF NOT EXISTS idx_compatibility_transceiver ON compatibility(transceiver_id);
CREATE INDEX IF NOT EXISTS idx_documents_entity ON documents(entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_documents_ocr_status ON documents(ocr_status);
CREATE INDEX IF NOT EXISTS idx_kb_category ON knowledge_base(category);
CREATE INDEX IF NOT EXISTS idx_kb_severity ON knowledge_base(severity);
CREATE INDEX IF NOT EXISTS idx_news_published ON news_articles(published_at DESC);
CREATE INDEX IF NOT EXISTS idx_news_category ON news_articles(category);
CREATE INDEX IF NOT EXISTS idx_news_event ON news_articles(event);
CREATE INDEX IF NOT EXISTS idx_vendors_type ON vendors(type);
CREATE INDEX IF NOT EXISTS idx_vendors_competitor ON vendors(is_competitor) WHERE is_competitor = TRUE;
-- ============================================================
-- TRIGRAM INDEXES (fuzzy search on names)
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_vendors_name_trgm ON vendors USING GIN(name gin_trgm_ops);
CREATE INDEX IF NOT EXISTS idx_switches_model_trgm ON switches USING GIN(model gin_trgm_ops);
CREATE INDEX IF NOT EXISTS idx_standards_name_trgm ON standards USING GIN(name gin_trgm_ops);
-- ============================================================
-- TIMESERIES INDEXES
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_price_transceiver ON price_observations(transceiver_id, time DESC);
CREATE INDEX IF NOT EXISTS idx_price_source ON price_observations(source_vendor_id, time DESC);
CREATE INDEX IF NOT EXISTS idx_stock_transceiver ON stock_observations(transceiver_id, time DESC);
CREATE INDEX IF NOT EXISTS idx_metrics_technology ON market_metrics(technology, metric_type, time DESC);