* test(performance): count startup phases and SQL statements for the J1 launch journey
Implements plan item A2. With IPTVNATOR_PERF_CAPTURE=1 the main process
keeps named counters and registers a main-only performance:read-counters
IPC handler; without the flag nothing is counted and the handler does not
exist.
- debug-trace.ts owns the registry; traceStartupPhase replaces the
trace('startup', ...) sites and counts main.startupPhases.
- The database worker counts executed statements through better-sqlite3's
Statement prototype (the verbose callback expands every statement and
made bulk inserts 2-4x slower) and posts the count over its message
port, flushed before every other worker message. The main-thread shared
connection is counted through a new connection observer in the shared
database library.
- The first main window freezes main.modulesRegisteredBeforeWindow at
creation and main.sqlStatementsBeforeReadyToShow at ready-to-show.
- The journey gate drops the ready-to-show that Electron emits for the
about:blank detour, so the app sees the real document's first paint,
and taps the counters handler; the J1 record reads both counters after
the renderer probe completes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(database): require one SQL statement per exec during initialization
The performance capture counts one exec call as one statement, because
SQL cannot be split reliably in the counter (trigger bodies contain
semicolons). The historical-upgrade driver now wraps exec on every
connection initDatabase opens and fails on a batch, so that counting
assumption holds for the fresh profile and all historical schemas.
Documents the definition in the counter and the architecture docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(performance): count SQL statements only for the launch journey
Codex review: the M3U import, refresh-cancellation and Xtream benchmarks
also run with IPTVNATOR_PERF_CAPTURE=1, so the statement hook wrapped
every row of their bulk inserts and changed what they measure.
SQL counting now also needs IPTVNATOR_PERF_COUNT_SQL=1, which only the
launch journey sets; a harness test fails if another source sets it.
Startup phases, the window snapshot and the read handler stay on the
capture flag. Without SQL counting no ready-to-show listener is attached,
so a zero is never reported for statements nobody counted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
4.1 KiB
Database
Shared database library for IPTVnator providing Drizzle ORM schema and connection utilities.
Usage
// Full access (electron-backend)
import { getDatabase, initDatabase } from '@iptvnator/shared/database';
// Read-only access (agent-backend)
import { getReadOnlyDatabase } from '@iptvnator/shared/database';
// Schema and types
import { content, categories, playlists, type Content } from '@iptvnator/shared/database';
Exports
Schema (schema.ts)
- Tables:
playlists,categories,content,recentlyViewed,favorites - Types:
Playlist,Category,Content,RecentlyViewed,Favorite(andNew*variants)
Connection (connection.ts)
getDatabase(options?)- Full read-write accessgetReadOnlyDatabase()- Read-only access for agent queriesinitDatabase(options?)- Initialize with custom optionscloseDatabase()- Close connectiongetDatabasePath()- Get database file path
Connection observer (connection-observer.ts)
setDatabaseConnectionObserver(observer | null)- Called byinitDatabasewith each connection it opens, before any statement runs on it. The Electron main process registers one only withIPTVNATOR_PERF_CAPTURE=1, to count main-thread SQL statements. The module has no runtime dependencies and is also importable as@iptvnator/shared/database/connection-observer.
Database Location
The SQLite database is stored at: ~/.iptvnator/databases/iptvnator.db
Upgrade Compatibility And Migrations
Users may skip releases. The application must apply every required migration in
dependency order when opening an older database, preserving user data without
requiring intermediate application installations or a database reset. This is
the repository policy mirrored in AGENTS.md and CLAUDE.md.
src/lib/connection.ts owns initialization: createTables() creates missing
schema objects, then runMigrations() applies column/index migrations and
dedicated schema/data upgrades. One-off data migrations can record completion
in app_state. Keep existing migration paths when adding new ones.
When changing initialization:
- Create required tables before migrating them.
CREATE TABLE IF NOT EXISTSleaves existing columns unchanged. - Add missing columns before creating indexes or triggers, or running queries,
that depend on those columns. Put indexes depending on migrated columns in
INDEX_MIGRATION_STATEMENTS, afterCOLUMN_MIGRATION_STATEMENTS, rather than in the earlier schema creation phase. - Preserve existing data and make migrations safe on repeated startup. Record completion only after the associated migration succeeds.
- Exercise the actual initialization path with real SQLite and historical schema fixtures containing representative playlists, favorites, history, and playback positions. Verify both schema changes and preservation of those rows; mocked SQL calls alone do not establish upgrade compatibility.
- Include direct upgrades across skipped releases, the previous release, a fresh install, and repeated startup. If a release changes migration ordering, cover each distinct affected historical schema.
The #1580 index-ordering fix is included in 0.24 through PR #1550.
src/lib/connection-upgrades.spec.ts exercises initDatabase() with real
SQLite under the Electron runtime, using fresh-install schema snapshots from
tags 0.19–0.23 and a fresh current database. The epg_channel_id column is absent
in 0.19, present from 0.20, and indexed from 0.23. Each case checks current Drizzle
tables, columns/types, and named indexes/uniqueness, as well as preserved user
rows, foreign keys, database integrity, index availability, and repeated startup;
an existing EPG index must keep its definition and root page. Snapshots live in
src/lib/testing/fixtures/ and are independent of the current schema, so moving
the index ahead of its column migration makes the 0.19 case fail again.
Run this coverage with pnpm nx test database --runInBand. The Electron UI and
worker upgrade flow is also covered by
pnpm nx run electron-backend-e2e:e2e-ci--src/legacy-playlist-migration.e2e.ts.