Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ jobs:
- name: Install dependencies
run: npm ci

# node-version-check.spec.ts boots dist/index.js under Node 18 to prove
# the version gate fails fast, so the unit job needs a real build too.
- name: Build
run: npm run build

- name: Run unit tests with coverage
run: npm run test:unit:coverage

Expand Down
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const integrationTestFiles = [
"auth-race.spec.ts",
"batch-routes.spec.ts",
"bets.routes.spec.ts",
"bet-store-persistence.spec.ts",
"concurrent-rounds.spec.ts",
"db-pool-config.spec.ts",
"decimal-precision.spec.ts",
Expand Down
45 changes: 45 additions & 0 deletions prisma/migrations/20260827000000_add_bet_record/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
-- ============================================================
-- Migration: 20260827000000_add_bet_record
-- Description: Durable bet-store table (Issue #519). Persists the
-- process-local bet store so bets survive restarts / multi-instance
-- deployments when DATA_STORE=postgres.
--
-- Apply : prisma migrate deploy
-- Rollback:
-- DROP TABLE IF EXISTS "BetRecord";
-- DELETE FROM _prisma_migrations WHERE migration_name = '20260827000000_add_bet_record';
-- ============================================================

-- CreateTable
CREATE TABLE "BetRecord" (
"id" TEXT NOT NULL,
"address" TEXT NOT NULL,
"amount" DECIMAL(20,8) NOT NULL,
"side" TEXT,
"predictedPrice" DECIMAL(18,8),
"mode" TEXT NOT NULL,
"roundId" TEXT,
"timestamp" TIMESTAMP(3) NOT NULL,
"status" TEXT NOT NULL,
"txHash" TEXT,
"submittedAt" TIMESTAMP(3),
"confirmedAt" TIMESTAMP(3),
"failedAt" TIMESTAMP(3),
"failureReason" VARCHAR(2000),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "BetRecord_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE INDEX "BetRecord_address_idx" ON "BetRecord"("address");

-- CreateIndex
CREATE INDEX "BetRecord_roundId_idx" ON "BetRecord"("roundId");

-- CreateIndex
CREATE INDEX "BetRecord_status_idx" ON "BetRecord"("status");

-- CreateIndex
CREATE INDEX "BetRecord_timestamp_idx" ON "BetRecord"("timestamp");
36 changes: 36 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -507,3 +507,39 @@ model MockPlatformStat {
activePlayers Int
totalBetsPlaced Int
}

/// Durable bet-store record (Issue #519).
///
/// Backs `src/data/bet-store.ts` when the store runs in postgres mode
/// (`DATA_STORE=postgres`, the default for `DATA_MODE=live`), so bet records
/// survive process restarts and multi-instance deployments instead of living
/// only in process-local Maps. The in-memory bet store (true mock mode) does
/// not touch this table.
///
/// Each row mirrors a `StoredBet`: `id` is the client-facing bet id
/// ("bet-N"), `status` is one of STUB | SUBMITTED | CONFIRMED | FAILED, and
/// `mode` is "updown" | "precision". Money fields are stored as Decimals per
/// the repo monetary-precision rule (see src/utils/decimal.util.ts).
model BetRecord {
id String @id
address String
amount Decimal @db.Decimal(20, 8)
side String?
predictedPrice Decimal? @db.Decimal(18, 8)
mode String
roundId String?
timestamp DateTime
status String
txHash String?
submittedAt DateTime?
confirmedAt DateTime?
failedAt DateTime?
failureReason String? @db.VarChar(2000)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@index([address])
@@index([roundId])
@@index([status])
@@index([timestamp])
}
26 changes: 26 additions & 0 deletions src/config/node-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Early Node.js version gate (startup).
*
* Imported as a side-effect from src/index.ts BEFORE any other module is
* required, so a server started on an unsupported Node version fails fast
* with a clear message instead of dying deep inside a dependency's require()
* chain (e.g. the ERR_REQUIRE_ESM crash from @stellar/stellar-sdk on
* Node 18, which previously masked the real cause).
*
* Skipped under NODE_ENV=test so test suites can import index.ts freely.
* Mirrors MIN_NODE_MAJOR in src/config/preflight.ts and the `engines` field
* in package.json.
*/
const MIN_NODE_MAJOR = 22;

if (process.env.NODE_ENV !== 'test') {
const major = parseInt(process.version.replace('v', '').split('.')[0], 10);
if (isNaN(major) || major < MIN_NODE_MAJOR) {
console.error(
`Application startup failed: Node.js v${MIN_NODE_MAJOR}.x or higher is required (running ${process.version}).`,
);
process.exit(1);
}
}

export {};
Loading
Loading