chat-bet-parse
Version:
TypeScript package for parsing sports betting contract text into structured data types compatible with SQL Server stored procedures
640 lines (464 loc) โข 27.1 kB
Markdown
# chat-bet-parse
## 0.6.11
### Patch Changes
- **Support for letter-first quarter/period formats in single-team game totals**: Fixed parser to recognize `q1`, `q2`, `q3`, `q4`, `p1`, `p2`, `p3` period formats when no league prefix is provided.
### ๐ฏ Problem Solved
Previously, single-team game totals with letter-first period notation (e.g., `q1` instead of `1q`) required a league prefix to parse correctly.
**Before:**
```typescript
const result = parseChat('YG pacers q1 u61 @ -111 = risk 3300');
// โ InvalidContractTypeError: Unable to determine contract type from: "pacers q1 u61"
```
**After:**
```typescript
const result = parseChat('YG pacers q1 u61 @ -111 = risk 3300');
// โ
Successfully parses as TotalPoints contract
console.log(result.contract.Match.Team1); // "pacers"
console.log(result.contract.Line); // 61
console.log(result.contract.Period); // { PeriodTypeCode: 'Q', PeriodNumber: 1 }
```
### ๐ง Technical Details
Updated the contract type detection regex for single-team game totals to include letter-first period formats (`q1|q2|q3|q4|p1|p2|p3`), matching the pattern already used for two-team game totals.
### โ
Impact
- All 510 tests passing
- 2 new tests added for quarter period totals with and without league prefix
- Both `YG pacers q1 u61 @ -111 = risk 3300` and `YG NBA pacers q1 u61 @ -111 = risk 3300` now parse correctly
## 0.6.10
### Patch Changes
- **Comma-Thousands Syntax Support**: Added support for American thousands formatting in bet sizes with strict validation.
### ๐ฏ Problem Solved
Previously, the parser only accepted sizes without commas. Users couldn't input sizes like `20,000` or `$15,550` - they had to write `20000` or use k-notation like `$15.55k`.
**Before:**
```typescript
const result = parseChat('YG Lakers @ +120 = $1,500');
// โ InvalidSizeFormatError: Invalid size format: "= $1,500"
```
**After:**
```typescript
const result = parseChat('YG Lakers @ +120 = $1,500');
// โ
Successfully parses as $1,500
console.log(result.bet.Size); // 1500
// Works in all size contexts
parseChat('YG Lakers @ +120 = risk 20,000');
parseChat('YGP Lakers @ +120 & Warriors @ -110 = $5k tw $15,550');
```
### ๐ง Technical Details
**Valid comma-thousands formats:**
- `1,234` - four digits
- `12,345` - five digits
- `1,234,567` - multiple comma groups
- `$15,550` - with dollar sign
- Works with: `risk 20,000`, `tw $15,550`, `= $1,500`
**Validation enforces American thousands formatting:**
- โ
Valid: Groups of exactly 3 digits after first 1-3 digits
- โ Invalid: `20,30` (only 2 digits after comma)
- โ Invalid: `1,2345` (4 digits after comma)
- โ Invalid: `123,45` (2 digits after comma)
**Additional fix:** Parser now handles missing spaces around `@` symbol (e.g., `o39.5@-122` now works correctly).
### โ
Impact
- All 508 tests passing
- Supports comma-thousands in all size contexts (risk, towin, size)
- Strict validation prevents invalid comma placement
- 7 new tests covering valid and invalid comma patterns
## 0.6.9
### Patch Changes
- **Support for Team Names Starting with Numbers (49ers, 76ers)**: Fixed parser regex patterns to correctly handle team names that start with "49" or "76", such as "49ers" and "76ers".
### ๐ฏ Problem Solved
Previously, the parser failed to recognize team names starting with digits in single-team game total formats, causing parse errors.
**Before:**
```typescript
const result = parseChat('YG NBA 11/28/2025 76ers u226.5 @ -115 = 1k');
// โ InvalidContractTypeError: Unable to determine contract type
```
**After:**
```typescript
const result = parseChat('YG NBA 11/28/2025 76ers u226.5 @ -115 = 1k');
// โ
Successfully parses as TotalPoints contract
console.log(result.contract.Match.Contestant1_RawName); // "76ers"
console.log(result.contract.Line); // 226.5
```
### ๐ง Technical Details
Updated 5 regex patterns in the parser to allow optional "49" or "76" numeric prefixes:
- Single team game total shorthand (`76ers u226.5`)
- Single team with period (`76ers 1H u110`)
- Period extraction for spreads, team totals, and game totals
Pattern format: `(?:49|76)?[a-zA-Z\s&.-]+` allows optional "49" or "76" prefix followed by standard team name characters, preventing unintended matches while supporting these specific NFL/NBA teams.
### โ
Impact
- All 499 tests passing
- Supports both "49ers" (NFL) and "76ers" (NBA) in all bet formats
- Precise regex ensures no false positives with other numeric patterns
## 0.6.8
### Patch Changes
- **Automatic ToWin Calculation for Parlays and Round Robins**: When parsing parlays (YGP) and round robins (YGRR) with fair odds (`useFair: true`), the library now automatically calculates `bet.ToWin` from leg prices using fair parlay odds mathematics.
### ๐ฏ Problem Solved
Previously, `bet.ToWin` was `undefined` for parlays and round robins when no explicit `tw` override was provided. This caused downstream errors in systems that expected both `Risk` and `ToWin` to be populated.
**Before:**
```typescript
const result = parseChat('YGP Lakers @ +120 & Warriors @ -110 = $100');
console.log(result.bet.ToWin); // undefined โ
```
**After:**
```typescript
const result = parseChat('YGP Lakers @ +120 & Warriors @ -110 = $100');
console.log(result.bet.ToWin); // 320.00 โ
console.log(result.useFair); // true
```
### ๐ How It Works
**Parlay Calculation:**
1. Convert each leg's American odds to decimal odds (+120 โ 2.20, -110 โ 1.909090...)
2. Multiply all decimal odds together to get parlay multiplier
3. Calculate `ToWin = Risk ร (multiplier - 1)`
**Round Robin Calculation:**
1. Generate all nCr combinations (or at-most combinations for notation like 4c3-)
2. Calculate fair ToWin for each parlay combination
3. Sum all ToWins for total expected payout
### ๐ Examples
```typescript
// 2-leg parlay
parseChat('YGP Lakers @ +120 & Warriors @ -110 = $100')
// โ { bet: { Risk: 100, ToWin: 320 }, useFair: true }
// 3-leg parlay
parseChat('YGP Lakers @ +120 & Warriors @ -110 & Celtics @ +105 = $100')
// โ { bet: { Risk: 100, ToWin: 761 }, useFair: true }
// Round robin with per-selection risk
parseChat('YGRR 4c2 Lakers @ +120 & Warriors @ -110 & Celtics @ +105 & Nets @ +115 = $100 per')
// โ { bet: { Risk: 600, ToWin: 1986.56 }, useFair: true, riskType: 'perSelection' }
// With explicit tw override (not fair odds)
parseChat('YGP Lakers @ +120 & Warriors @ -110 = $100 tw $500')
// โ { bet: { Risk: 100, ToWin: 500 }, useFair: false }
```
### โ
Impact
- `bet.ToWin` is now always populated for fills with fair odds
- Use `useFair: true` to distinguish calculated ToWin from explicit overrides
- All 497 tests passing
- **Correct Total Risk Calculation for Round Robins**: When parsing round robins with per-selection risk mode (e.g., `$100 per`), the library now correctly calculates total risk as `per-parlay risk ร number of parlays`.
### ๐ฏ Problem Solved
Previously, the library only returned the per-parlay risk amount instead of calculating the total risk across all parlays.
**Before:**
```typescript
const result = parseChat('YGRR 4c2 ... = $100 per');
console.log(result.bet.Risk); // 100 โ (only per-parlay amount)
```
**After:**
```typescript
const result = parseChat('YGRR 4c2 ... = $100 per');
console.log(result.bet.Risk); // 600 โ
($100 per ร 6 parlays)
```
### ๐ Examples
```typescript
// 4c2 = C(4,2) = 6 parlays
parseChat('YGRR 4c2 Lakers @ +120 & Warriors @ -110 & Celtics @ +105 & Nets @ +115 = $100 per')
// โ { bet: { Risk: 600 }, riskType: 'perSelection' }
// 4c3- (at-most) = C(4,2) + C(4,3) = 6 + 4 = 10 parlays
parseChat('YGRR 4c3- Lakers @ +120 & Warriors @ -110 & Celtics @ +105 & Nets @ +115 = $100 per')
// โ { bet: { Risk: 1000 }, riskType: 'perSelection' }
// Total risk mode (unchanged behavior)
parseChat('YGRR 4c2 ... = $600 total')
// โ { bet: { Risk: 600 }, riskType: 'total' }
```
### ๐ง New Helper Functions
- `calculateCombination(n, r)`: Calculates C(n,r) = "n choose r"
- `calculateTotalParlays(totalLegs, parlaySize, isAtMost)`: Calculates total number of parlays
- `americanToDecimalOdds(odds)`: Converts American odds to decimal
- `calculateParlayFairToWin(legPrices, risk)`: Calculates fair ToWin for a single parlay
- `calculateRoundRobinFairToWin(legPrices, risk, riskType, parlaySize, isAtMost)`: Calculates fair ToWin for all round robin combinations
### ๐ Migration Guide
See `RECENT_CHANGES_FOR_UI.md` for detailed migration instructions for UI developers.
## 0.6.3
### Patch Changes
- **Timezone-Agnostic Date Handling**: All date parsing and creation now uses UTC to ensure consistent behavior across different server timezones
### ๐ฏ Problem Solved
Previously, dates were created using local timezone (e.g., `new Date(year, month, day)`), which caused test failures when running in different timezones:
- **Local (Eastern timezone)**: `2025-10-26T04:00:00.000Z` (midnight Eastern = 4am UTC)
- **CI/CD (UTC timezone)**: `2025-10-26T00:00:00.000Z` (midnight UTC)
### ๐ Solution
All date creation now uses `Date.UTC()` to produce timezone-agnostic dates at midnight UTC:
```typescript
// Before (timezone-dependent)
new Date(2025, 9, 26) // Different results in different timezones
// After (timezone-agnostic)
new Date(Date.UTC(2025, 9, 26)) // Always 2025-10-26T00:00:00.000Z
```
### ๐ Files Modified
- `src/parsers/utils.ts`: Updated date creation and validation to use UTC
- `src/tracking/mappers.ts`: Updated ExecutionDtm extraction and fallback logic to use UTC
- Test files: Updated expectations to use `Date.UTC()` and UTC getter methods
- Test fixtures: Updated all date expectations from local time to UTC midnight
### โ
Impact
- Tests now pass consistently in any timezone (Eastern, UTC, PST, etc.)
- Parser produces identical results regardless of server location
- All 485 tests passing in both local and CI/CD environments
## 0.6.2
### Patch Changes
- **Tracking Mapper EventDate Priority Enhancement**: The tracking mapper now prioritizes dates embedded in bet text (`contract.Match.Date`) over execution timestamps when determining `EventDate`
### ๐ฏ EventDate Priority Order
1. Writein contracts' explicit `EventDate`
2. Caller-provided `options.eventDate`
3. **Date parsed from bet text (`contract.Match.Date`)** โ NEW
4. Date extracted from `ExecutionDtm` timestamp
5. Today's date (fallback)
### ๐ What Changed
Before this fix, when parsing a bet like `"YG NBA 10/26/2025 Pacers u230 @ -110 = 1k"` executed on 10/25/2025, the mapper would use the execution date (10/25) instead of the date specified in the bet text (10/26).
**Before:**
```typescript
const result = parseChat('YG NBA 10/26/2025 Pacers u230 @ -110 = 1k');
const spec = mapParseResultToContractLegSpec(result);
// EventDate = 2025-10-25 (from ExecutionDtm) โ
```
**After:**
```typescript
const result = parseChat('YG NBA 10/26/2025 Pacers u230 @ -110 = 1k');
const spec = mapParseResultToContractLegSpec(result);
// EventDate = 2025-10-26 (from bet text) โ
```
### ๐งช Test Coverage
- Added 11 new Date Handling tests covering:
- Full date formats (MM/DD/YYYY)
- Partial date formats (MM/DD) with referenceDate inference
- Priority ordering verification (options.eventDate > Match.Date > ExecutionDtm)
- All 485 tests passing
### ๐ Files Modified
- `src/tracking/mappers.ts`: Added `contract.Match.Date` check in EventDate determination
- `tests/unit/tracking.test.ts`: Added 7 new comprehensive Date Handling tests
- `tests/fixtures/game-totals.fixtures.ts`: Added 4 NBA date parsing test cases
## 0.6.0
### Minor Changes
- **DEPRECATION: Grading Mapper Functions**: The grading mapper module (`src/grading/mappers.ts`) is now deprecated in favor of the tracking mapper module. The grading mapper now wraps the tracking mapper for backward compatibility.
**Deprecated Functions:**
- `mapParseResultToSqlParameters` โ Use `mapParseResultToContractLegSpec` instead
- `validateGradingParameters` โ Use `validateContractLegSpec` instead
**Migration:**
```typescript
// Before (DEPRECATED)
import { mapParseResultToSqlParameters } from 'chat-bet-parse/grading';
const params = mapParseResultToSqlParameters(result, { matchScheduledDate: date });
// After (RECOMMENDED)
import { mapParseResultToContractLegSpec } from 'chat-bet-parse/tracking';
const spec = mapParseResultToContractLegSpec(result, { eventDate: date });
```
**Why?** Both mappers were doing the same conceptual work with 95% duplicate code. Consolidating to the tracking mapper eliminates duplication and provides a single source of truth. The `ChatBetGradingClient` continues to work unchanged.
- **Individual Player Props with Match.Player Field**: Add dedicated fields for individual player props to properly distinguish them from team-based props
### ๐ฏ New Match Fields
- **`Match.Player`** - Player name for individual props (e.g., "Cooper Flagg", "B. Falter")
```typescript
// Before (WRONG): Player stored in Team1
{ Match: { Team1: "Cooper Flagg" }, ContestantType: "Individual" }
// After (CORRECT): Player stored in dedicated field
{ Match: { Player: "Cooper Flagg", PlayerTeam: "DAL" }, ContestantType: "Individual" }
```
- **`Match.PlayerTeam`** - Optional team affiliation from "(TEAM)" syntax
```
YG NBA Cooper Flagg (DAL) pts o16.5 @ +100 = 2k
โ Match.Player = "Cooper Flagg"
โ Match.PlayerTeam = "DAL"
```
### ๐ Breaking Changes
- **`Match.Team1` is now optional** (was previously required)
- Individual player props now use `Match.Player` instead of `Match.Team1`
- SQL mapper returns `undefined` (not `null`) for missing `Contestant1_RawName` on individual props
### ๐ Parser Enhancements
- **Smart contestant type detection**: Automatically determines Individual vs TeamLeague based on prop keywords
- **Multi-word player name support**: New `extractContestantAndProp()` utility handles names like "Cooper Flagg pts"
- **Team affiliation parsing**: `parsePlayerWithTeam()` extracts player and team from "(TEAM)" syntax
- **Team prop differentiation**: Use "team passing yards" keyword to distinguish team-level props from player props
### ๐ฆ SQL Mapping Updates
- **Individual props**: `Contestant1_RawName = PlayerTeam || undefined`, `SelectedContestant_RawName = Player`
- **Team props**: `Contestant1_RawName = Team1` (unchanged)
- No SQL schema changes required - existing `ContractLegSpecTableType` handles both cases
### ๐งช Test Coverage
- Updated 9 test files with individual player prop test cases
- Added support for "Cooper Flagg", "B. Falter", and Player123/Team123 patterns
- All 441 tests passing
### ๐ Files Modified
- `src/types/index.ts`: Extended Match interface with Player and PlayerTeam fields
- `src/parsers/utils.ts`: Added parsePlayerWithTeam() and extractContestantAndProp()
- `src/parsers/index.ts`: Updated parsePropOU() and parsePropYN() for individual props
- `src/tracking/mappers.ts`: Updated extractMatchInfo() for correct SQL mapping
- Test fixtures updated to use "team passing yards" syntax for team-level props
- **Parlay/Round Robin Support in Mappers**: Grading and tracking mappers now handle parlay and round robin bets by returning arrays of mapped parameters
### ๐ฏ Mapper Updates
- **`mapParseResultToSqlParameters()`** - Returns array for parlay/round robin
```typescript
// Straight bet (backward compatible)
const params = mapParseResultToSqlParameters(straightBet, options);
// โ Single GradingSqlParameters object
// Parlay/Round Robin (new)
const params = mapParseResultToSqlParameters(parlayBet, options);
// โ Array of GradingSqlParameters (one per leg)
```
- **`mapParseResultToContractLegSpec()`** - Returns array for parlay/round robin
```typescript
// Straight bet (backward compatible)
const spec = mapParseResultToContractLegSpec(straightBet, options);
// โ Single ContractLegSpec object
// Parlay/Round Robin (new)
const specs = mapParseResultToContractLegSpec(parlayBet, options);
// โ Array of ContractLegSpec (one per leg with LegSequence and Price)
```
### ๐ Features
- **Recursive leg mapping**: Each parlay/round robin leg is mapped individually using the same logic as straight bets
- **Automatic LegSequence**: Combo legs automatically get assigned proper 1-based LegSequence values
- **Price handling**: Combo legs include Price field (null for straight bets per SQL spec)
- **Validation updates**: Allow `undefined` Contestant1 for individual props (PropOU/PropYN with Individual type)
### ๐ Breaking Changes
- Return type changed from `GradingSqlParameters` to `GradingSqlParameters | GradingSqlParameters[]`
- Return type changed from `ContractLegSpec` to `ContractLegSpec | ContractLegSpec[]`
- Code using these mappers must handle both single and array return types
### ๐งช Test Coverage
- New test suite `tests/unit/mappers.test.ts` with 31 comprehensive tests
- Covers all contract types, parlay/round robin mapping, validation
- Updated individual prop tests to expect correct Contestant1 mapping
- All 472 tests passing
### ๐ Files Modified
- `src/grading/mappers.ts`: Updated mapParseResultToSqlParameters() to handle combos
- `src/tracking/mappers.ts`: Updated mapParseResultToContractLegSpec() to handle combos
- `tests/unit/mappers.test.ts`: New comprehensive test suite for grading mappers
## 0.5.0
### Minor Changes
- **Extended Size Syntax for Straight Bets**: Add new size specification formats that allow explicit control over risk and to-win amounts
### ๐ฏ New Size Formats
- **`tw` / `to win`** - Specify risk and to-win explicitly
```
YG Lakers ML @ -110 = $110 tw $100
```
Perfect for tickets that show risk amount even with negative odds, or when dealing with short-pay due to rounded odds.
- **`tp` / `to pay`** - Specify risk and total payout (parser calculates to-win)
```
YG Warriors ML @ -120 = $120 tp $220
```
Useful when ticket shows total payout instead of profit.
- **`risk`** - Specify only risk amount (parser calculates to-win from price)
```
YG Knicks -2.5 @ -110 = risk $110
```
- **`towin`** - Specify only to-win amount (parser calculates risk from price)
```
YG Bulls ML @ +150 = towin $150
```
### ๐ Automatic Risk/ToWin Calculation
- **Parser now always populates `bet.Risk` and `bet.ToWin`** for all straight bets
- Clients **no longer need** their own `calculateRiskAndToWin()` utility functions
- Moved calculation logic into parser library for single source of truth
- Smart inference: fills in missing Risk or ToWin based on Price when only one value specified
- Full backward compatibility: existing `Size` syntax still works and auto-calculates both values
### ๐ฆ Type Updates
- `Bet` interface now includes `Risk?: number` and `ToWin?: number`
- `ParsedTokens` and `WriteInTokens` interfaces updated to support new fields
- New `ParsedStraightSize` interface for size parsing results
### ๐ง Implementation Details
- Added `calculateRiskAndToWin()`, `calculateToWinFromRisk()`, `calculateRiskFromToWin()` to utils
- New `parseStraightSize()` function handles all size formats (DRY implementation)
- Updated `parseChatOrder()` and `parseChatFill()` to always populate Risk/ToWin
- 7 new tests for extended syntax, all existing 437 tests still passing
- **Changed**: +311 lines, -17 lines across 5 files
### ๐ Migration Guide
See [MIGRATION_GUIDE_v0.5.0.md](./MIGRATION_GUIDE_v0.5.0.md) for detailed instructions on:
- Removing client-side `calculateRiskAndToWin()` utilities
- Updating components to use `result.bet.Risk` and `result.bet.ToWin` directly
- Testing recommendations
## 0.4.3
### Patch Changes
- **Case-insensitive league detection**: Support league codes in any case (NBA, nba, Nba, etc.)
- **CBB alias support**: Add CBB as alias for CBK (College Basketball), similar to existing FCS โ CFB mapping
- **Expanded prop keywords**: Add 120+ prop betting keywords across baseball, football, and basketball with automatic Individual/TeamLeague classification
- Keywords are matched longest-first and include contestantType metadata that flows through to ContractLegSpec
- Examples: player stats (Individual), team props (TeamLeague)
- Backward compatible: existing API unchanged, sim-dash gets PropContestantType automatically
- **Fix dollar+k-notation parsing**: Correctly parse `$11k` โ `$11,000` (previously incorrectly parsed as `$11`)
- **Writein legs in parlays/round robins**: Add test coverage for writein contracts as legs in YGP/IWP/YGRR/IWRR bets
- Syntax: `YGP writein 12/25/2024 Event description @ +200 & Lakers @ +120 = $100`
- Known limitation: Writein descriptions cannot contain ampersand character
## 0.4.2
### Patch Changes
- **DRY refactoring**: Consolidate duplicated parsing and validation logic
- Merged size parsing functions into shared helper
- Consolidated keyword parsing with `extractAndValidateKeywords` helper
- Extracted contract validation to `shared/contractValidation.ts` module
- Replaced duplicate switch statements with contract parser factory pattern
- Fixed parlay/round robin size parsing to support k-notation and decimal formats
- Reduced codebase by ~410 lines while maintaining 100% test coverage
- **Timezone-independent testing**: Fix ExecutionDtm date handling test to work across all timezones (was failing in CI/CD)
## 0.4.1
### Patch Changes
- **Documentation update**: Update README and archive implementation guides
- Add parlay (YGP/IWP) and round robin (YGRR/IWRR) support to features
- Document two-discriminator type system (chatType + betType)
- Add comprehensive EBNF grammar for parlays and round robins
- Add 60+ new syntax examples for parlays and round robins
- Update API documentation with type guards and result structures
## 0.4.0
### Minor Changes
- **Parlay betting support**: Implement YGP/IWP parlay parsing
- 2-10 leg parlays with `&` separator
- Fair odds calculation or explicit to-win override (`tw` keyword)
- Support for `pusheslose`/`tieslose` and `freebet` flags
- Leg-level rotation numbers, game numbers, dates, leagues, and periods
- Multiline format support
- Size formats: decimal thousands, k-notation, dollar+k (`$11k`)
- All contract types supported in parlay legs (including writein)
- **Round robin betting support**: Implement YGRR/IWRR round robin parsing with nCr notation
- nCr notation: `4c2` (4 teams, 2-team parlays), `5c3`, `6c4`, etc.
- At-most modifier with trailing dash: `4c3-` (at most 3: creates 2s and 3s)
- Per-selection vs total risk types: `= $100 per` or `= $600 total`
- Same parlay features: to-win override, flags, leg properties
- Multiline format support
- **Event dates for straight bets**: Add date support for YG/IW bets
- Multiple date formats: YYYY-MM-DD, MM/DD/YYYY, YYYY/MM/DD, MM-DD-YYYY
- Positional dates: `YG 12/25/2024 Lakers @ +150 = 2.5`
- Keyword dates: `YG date:12/25/2024 Lakers @ +150 = 2.5`
- Smart year inference for MM/DD format
- Calendar date validation
- **League keywords for writeins**: Add league support for YGW/IWW
- Syntax: `YGW league:NBA 12/25/2024 Custom event @ +200 = 1.5`
- Works with both positional and keyword date formats
- **ParseOptions**: New options object for parser configuration
- `referenceDate?: Date` - for date parsing context
- Passed to all parse functions: `parseChat(message, options)`
- **Type system refactor**: Implement two-discriminator type system
- `chatType`: 'order' | 'fill'
- `betType`: 'straight' | 'parlay' | 'roundRobin'
- Type guards: `isParlay()`, `isRoundRobin()`, `isStraightBet()`
- Discriminated unions for type-safe result handling
## 0.3.7
### Minor Changes
- Add Writein contract support for custom betting events
### ๐๏ธ New Features
- **Writein Contracts**: New contract type for custom betting events not tied to specific sports matches
- Support for format: `IW/YG writein DATE DESCRIPTION [@ price] [= size]`
- Multiple date format support: YYYY-MM-DD, MM/DD/YYYY, YYYY/MM/DD, MM-DD-YYYY, and equivalents without year
- Smart year inference (current year if future/today, next year if past)
- Calendar date validation (rejects invalid dates like Feb 30th)
- Description validation (10-255 characters, no newlines)
- Full price and size parsing support (@ +150, = 3.0, k-notation, etc.)
- Case-insensitive "writein" detection
- **Enhanced Type System**: New `ContractWritein` interface with `EventDate` and `Description` properties
- **Comprehensive Error Handling**:
- `InvalidWriteinFormatError` for format issues
- `InvalidWriteinDateError` for date parsing failures
- `InvalidWriteinDescriptionError` for description validation
- **Grading System Integration**: Updated SQL Server grading support for Writein contracts
- **Extensive Testing**: 18 new tests covering positive and error scenarios
## 0.3.0
### Minor Changes
- 354c740: Add SQL Server grading client and expand parser support
### ๐๏ธ New Features
- **SQL Server Grading Client**: New `ChatBetGradingClient` for automated contract grading against SQL Server databases
- Support for all major contract types: TotalPoints, TotalPointsContestant, HandicapContestantML, HandicapContestantLine
- Comprehensive error handling with `GradingConnectionError` and `GradingDataError`
- Flexible configuration with connection pooling options
- Factory functions for easy client creation
- **Enhanced Parser Support**: Extended parsing capabilities for F3 periods and individual player props
- F3 (first three innings) period parsing for baseball games
- Individual player detection for props (e.g., "B. Name Ks o1.5")
- Improved team name handling with special characters
## 0.2.0
### Minor Changes
- 744258e: Initial release of chat-bet-parse
- ๐ Multi-sport support (MLB, NBA) with extensible framework
- ๐ Rich parsing for chat orders (IW) and fills (YG) with different size interpretations
- ๐ Full TypeScript support with discriminated unions
- ๐๏ธ SQL Server ready types for stored procedure integration
- โก Zero runtime dependencies
- ๐งช Comprehensive test suite with 141 tests and 100% coverage
- ๐ Complete EBNF grammar documentation
- โ
README validation tests to ensure documentation accuracy