mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 01:16:15 -08:00
* test(performance): add the J1 launch-to-usable journey benchmark Implements plan items A1, A3 (J1 only) and the minimal A4 from .plans/2026-09-25-performance-journeys-ratchet.md. - journey-renderer-probe.ts: init-script probe counting DOM mutations, layout shifts and long tasks until the first source card is visible on /workspace with the splash removed; unit-tested with jsdom fixtures. - journey-main-ipc-capture.ts: counts bridge invocations from the preload's renderer-API trace channel up to a sentinel call the probe fires, so the IPC counter is exact without touching production code. - launch.journey.ts + playwright.journeys.config.ts: seeded profile (one M3U source, one Xtream portal on the loopback mock), one warm-up and five measured iterations, fresh process and data directory each, writing dist/performance/journeys/<timestamp>/summary.json with exact counters and P50/P90 wall-clock. - Nx target electron-backend-e2e:journeys and root script perf:journeys. - docs/architecture/performance-journeys.md, README, context and validation map entries. renderer.cdTicksToFirstCard and main.sqlStatementsBeforeReadyToShow are reported as unavailable with the reason instead of being faked. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(performance): gate the renderer load so the probes never race startup Review follow-up for #1698. - journey-renderer-gate.cjs: a main-process hook loaded with `-r` (the mechanism Playwright uses for its own loader) makes the first loadFile/loadURL navigate to about:blank and holds the real load until the test releases it. Playwright reports no page before a navigation commits, so this is what lets the renderer probe be registered on the page before the real document exists; the IPC capture is installed before the release too. A safety timeout releases the gate on its own and marks the iteration invalid. Unit-tested with a fake BrowserWindow. - launch-journey-app.ts: registers the probe on the parked page, releases the gate, waits for the real document to commit, fails fast when the probe is missing, and refuses an iteration whose gate timed out, saw a second load, or released before the probe was in place. - journey-renderer-probe.ts: entries delivered live after the terminal batch are buffered and filtered by the same cutoff as queued ones, and the cutoff is sampled in a timer queued from the first rAF, i.e. after the card's frame is painted, so the render task's long task and layout shift are consistently included. - launch-journey-record.ts: layoutShiftScore rounded to three decimals; a 0.0001 shift flipped in and out of the cutoff between iterations. - playwright.journeys.config.ts: reuse a mock server left on the journeys port locally (its fixtures are deterministic); CI still starts its own. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(performance): validate the journey gate after the probe completes Codex follow-up on #1698: the gate state returned by release() cannot see a reload or recovery navigation that happens before the first card. Re-read the live state once the renderer probe has finished and validate that instead, so an iteration spanning an extra navigation is rejected. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(performance): fail an iteration whose performance observers were unavailable Codex follow-up on #1698: a renderer that cannot observe layout-shift or longtask entries used to pass the probe with zero counters, which a ratchet could not tell apart from a genuine zero. The probe assertion now rejects such an iteration and names the missing observer. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: 4gray <fourgray@proton.me> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
308 lines
15 KiB
JSON
308 lines
15 KiB
JSON
{
|
|
"name": "iptvnator",
|
|
"version": "0.25.0",
|
|
"engines": {
|
|
"node": "^22.22.3 || ^24.15.0"
|
|
},
|
|
"license": "MIT",
|
|
"description": "IPTV player application.",
|
|
"homepage": "https://github.com/4gray/iptvnator",
|
|
"author": {
|
|
"name": "4gray",
|
|
"email": "fourgray@proton.me"
|
|
},
|
|
"keywords": [
|
|
"angular",
|
|
"electron",
|
|
"typescript",
|
|
"m3u",
|
|
"m3u8",
|
|
"player",
|
|
"iptv",
|
|
"video",
|
|
"tv"
|
|
],
|
|
"scripts": {
|
|
"postinstall": "electron-builder install-app-deps",
|
|
"build:frontend": "nx build web",
|
|
"build:frontend:pwa": "nx build web --configuration=pwa",
|
|
"build:backend": "nx build electron-backend --configuration=production",
|
|
"serve:frontend": "nx serve web --no-tui",
|
|
"serve:frontend:pwa": "nx serve web --configuration=pwa --no-tui",
|
|
"serve:backend": "nx serve electron-backend",
|
|
"serve:marketing-demo": "nx run-many --target=serve --projects=xtream-mock-server,electron-backend --parallel=2",
|
|
"serve:marketing-demo:web": "nx run-many --target=serve --projects=xtream-mock-server,web --parallel=2",
|
|
"test:frontend": "nx test web",
|
|
"test:backend": "nx test electron-backend",
|
|
"test:unit:all": "nx run-many --target=test --all --parallel=3",
|
|
"test:unit:ci": "nx run-many --target=test --all --parallel=3 --output-style=static",
|
|
"deps:nx:test": "node --test tools/dependencies/check-nx-version-sync.test.mjs",
|
|
"deps:nx:check": "node tools/dependencies/check-nx-version-sync.mjs",
|
|
"deps:nx:validate": "pnpm run deps:nx:test && pnpm run deps:nx:check",
|
|
"deps:vite:test": "node --test tools/dependencies/vite-transform-filter.test.mjs",
|
|
"deps:electron-builder:test": "node --test tools/dependencies/app-builder-lib-keychain-password.test.mjs",
|
|
"styles:inputs:test": "node --test tools/nx/check-stylesheet-inputs.test.mjs",
|
|
"styles:inputs:check": "node tools/nx/check-stylesheet-inputs.mjs",
|
|
"styles:inputs:validate": "pnpm run styles:inputs:test && pnpm run styles:inputs:check",
|
|
"coverage:tools:test": "node --test tools/coverage/coverage-integrity.test.mjs",
|
|
"coverage:unit:ci": "node tools/coverage/run-tier-a-coverage.mjs",
|
|
"coverage:merge": "node tools/coverage/merge-coverage.mjs",
|
|
"coverage:health": "node tools/coverage/coverage-health.mjs",
|
|
"coverage:policy:check": "node tools/coverage/check-coverage-policy.mjs",
|
|
"coverage:ci": "pnpm run coverage:tools:test && pnpm run coverage:policy:check && pnpm run coverage:unit:ci && pnpm run coverage:merge && node tools/coverage/coverage-health.mjs --require-report",
|
|
"coverage:e2e:summary": "node tools/coverage/e2e-semantic-summary.mjs",
|
|
"coverage:e2e:v8:web": "node tools/coverage/e2e-v8-web.mjs",
|
|
"typecheck:web": "tsc -p apps/web/tsconfig.app.json --noEmit",
|
|
"typecheck:backend": "tsc -p apps/electron-backend/tsconfig.app.json --noEmit",
|
|
"typecheck:ci": "pnpm run typecheck:web && pnpm run typecheck:backend",
|
|
"verify:package-layout": "node tools/packaging/verify-electron-package-layout.mjs",
|
|
"embedded-mpv:build-native:homebrew": "IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW=1 nx run electron-backend:build-embedded-mpv",
|
|
"embedded-mpv:build-runtime": "node tools/embedded-mpv/build-macos-runtime.mjs",
|
|
"embedded-mpv:build-runtime:linux": "node tools/embedded-mpv/build-linux-runtime.mjs",
|
|
"embedded-mpv:stage-runtime": "node tools/embedded-mpv/stage-runtime.mjs",
|
|
"embedded-mpv:stage-runtime:macos": "node tools/embedded-mpv/stage-macos-runtime.mjs",
|
|
"embedded-mpv:stage-runtime:windows-archive": "node tools/embedded-mpv/stage-windows-runtime-archive.mjs",
|
|
"embedded-mpv:windows-runtime-pin:check": "node tools/embedded-mpv/windows-runtime-pin.mjs",
|
|
"embedded-mpv:windows-runtime-pin:refresh": "node tools/embedded-mpv/update-windows-runtime-pin.mjs",
|
|
"serve:backend:embedded-mpv": "pnpm embedded-mpv:build-native:homebrew && IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW=1 IPTVNATOR_ENABLE_EMBEDDED_MPV_EXPERIMENT=1 pnpm serve:backend",
|
|
"package:app": "nx run electron-backend:make --prepackageOnly",
|
|
"make:app": "nx run electron-backend:make",
|
|
"smoke:packaged": "node tools/testing/launch-packaged-electron.mjs",
|
|
"serve:website": "nx serve website",
|
|
"build:website": "nx build website",
|
|
"i18n:check": "node tools/i18n/check-drift.mjs",
|
|
"perf:initial-bytes": "node tools/performance/measure-initial-bytes.mjs",
|
|
"perf:initial-bytes:check": "node tools/performance/measure-initial-bytes.mjs --summary dist/performance/initial-bytes.summary.json && node tools/performance/check-journey-ratchet.mjs --summary dist/performance/initial-bytes.summary.json --only launch/renderer.initialBytes",
|
|
"perf:journeys": "nx run electron-backend-e2e:journeys",
|
|
"perf:ratchet:check": "node tools/performance/check-journey-ratchet.mjs --summary dist/performance/journey-summary.json",
|
|
"perf:tools:test": "node --test tools/performance/measure-initial-bytes.test.mjs tools/performance/check-journey-ratchet.test.mjs tools/performance/check-baseline-direction.test.mjs",
|
|
"agents:validate": "node tools/skills/validate-agent-guidance.mjs",
|
|
"skills:validate": "node tools/skills/validate-repository-skills.mjs",
|
|
"release:artwork:dry-run": "tsx --tsconfig tsconfig.base.json tools/release/generate-marketing-artwork.ts --dry-run",
|
|
"release:artwork:manifest": "tsx --tsconfig tsconfig.base.json tools/release/generate-marketing-artwork.ts --manifest",
|
|
"release:artwork:generate": "tsx --tsconfig tsconfig.base.json tools/release/generate-marketing-artwork.ts --generate",
|
|
"release:artwork:validate": "tsx --tsconfig tsconfig.base.json tools/release/generate-marketing-artwork.ts --validate",
|
|
"release:notes:validate": "node tools/release/build-release-notes.mjs --validate",
|
|
"release:notes:github": "node tools/release/build-release-notes.mjs --format github",
|
|
"release:notes:changelog": "node tools/release/build-release-notes.mjs --format changelog",
|
|
"release:notes:blog": "node tools/release/build-release-notes.mjs --format blog",
|
|
"release:notes:telegram": "node tools/release/build-release-notes.mjs --format telegram",
|
|
"release:notes:reddit": "node tools/release/build-release-notes.mjs --format reddit",
|
|
"release:verify:draft": "node tools/release/verify-draft-release.mjs",
|
|
"release:cards:dry-run": "node tools/release/generate-highlight-cards.mjs --dry-run",
|
|
"release:cards:generate": "node tools/release/generate-highlight-cards.mjs --generate",
|
|
"release:screenshots": "tsx tools/release/capture-release-screenshots.ts",
|
|
"lint": "nx run-many --target=lint --all",
|
|
"build": "nx build electron-backend"
|
|
},
|
|
"private": true,
|
|
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319",
|
|
"dependencies": {
|
|
"@angular/animations": "22.1.6",
|
|
"@angular/cdk": "22.1.6",
|
|
"@angular/common": "22.1.6",
|
|
"@angular/compiler": "22.1.6",
|
|
"@angular/core": "22.1.6",
|
|
"@angular/forms": "22.1.6",
|
|
"@angular/material": "22.1.6",
|
|
"@angular/platform-browser": "22.1.6",
|
|
"@angular/platform-browser-dynamic": "22.1.6",
|
|
"@angular/router": "22.1.6",
|
|
"@ngrx/effects": "22.0.1",
|
|
"@ngrx/entity": "22.0.1",
|
|
"@ngrx/router-store": "22.0.1",
|
|
"@ngrx/signals": "22.0.1",
|
|
"@ngrx/store": "22.0.1",
|
|
"@ngrx/store-devtools": "22.0.1",
|
|
"@ngx-pwa/local-storage": "22.0.0",
|
|
"@yangkghjh/videojs-aspect-ratio-panel": "0.0.1",
|
|
"angularx-qrcode": "22.0.1",
|
|
"artplayer": "5.4.0",
|
|
"axios": "1.20.0",
|
|
"better-sqlite3": "13.0.3",
|
|
"date-fns": "4.4.0",
|
|
"drizzle-orm": "0.45.2",
|
|
"electron-conf": "1.3.0",
|
|
"electron-updater": "6.8.9",
|
|
"epg-parser": "^0.5.0",
|
|
"fix-path": "5.0.0",
|
|
"hls.js": "1.7.1",
|
|
"iptv-playlist-parser": "github:4gray/iptv-playlist-parser#v0.15.2-iptvnator.2",
|
|
"marked": "18.0.11",
|
|
"mpegts.js": "1.8.1",
|
|
"ms": "2.1.3",
|
|
"ngx-indexed-db": "22.0.0",
|
|
"ngx-skeleton-loader": "11.3.0",
|
|
"proxy-from-env": "2.1.0",
|
|
"rxjs": "7.8.2",
|
|
"saxes": "6.0.0",
|
|
"shaka-player": "5.2.4",
|
|
"video.js": "8.24.0",
|
|
"videojs-contrib-quality-levels": "4.1.0",
|
|
"videojs-quality-selector-hls": "1.1.1",
|
|
"zone.js": "0.16.3"
|
|
},
|
|
"devDependencies": {
|
|
"@angular-devkit/core": "22.1.8",
|
|
"@angular-devkit/schematics": "22.1.8",
|
|
"@angular-eslint/builder": "22.5.0",
|
|
"@angular-eslint/eslint-plugin": "22.5.0",
|
|
"@angular-eslint/eslint-plugin-template": "22.5.0",
|
|
"@angular-eslint/schematics": "22.5.0",
|
|
"@angular-eslint/template-parser": "22.5.0",
|
|
"@angular/build": "22.1.8",
|
|
"@angular/cli": "22.1.8",
|
|
"@angular/common": "22.1.6",
|
|
"@angular/compiler": "22.1.6",
|
|
"@angular/compiler-cli": "22.1.6",
|
|
"@angular/core": "22.1.6",
|
|
"@angular/forms": "22.1.6",
|
|
"@angular/language-service": "22.1.6",
|
|
"@angular/platform-browser": "22.1.6",
|
|
"@angular/platform-browser-dynamic": "22.1.6",
|
|
"@angular/router": "22.1.6",
|
|
"@angular/service-worker": "22.1.6",
|
|
"@astrojs/mdx": "7.0.7",
|
|
"@astrojs/sitemap": "3.7.4",
|
|
"@babel/core": "7.29.7",
|
|
"@electron/asar": "3.4.1",
|
|
"@eslint/eslintrc": "3.3.7",
|
|
"@eslint/js": "^9.38.0",
|
|
"@faker-js/faker": "10.6.0",
|
|
"@fontsource-variable/bricolage-grotesque": "5.3.0",
|
|
"@fontsource/crimson-pro": "5.3.0",
|
|
"@fontsource/dm-sans": "5.3.0",
|
|
"@fontsource/ibm-plex-mono": "5.3.0",
|
|
"@fontsource/jetbrains-mono": "5.3.0",
|
|
"@fontsource/roboto": "5.3.0",
|
|
"@ngrx/eslint-plugin": "22.0.1",
|
|
"@ngx-translate/core": "16.0.4",
|
|
"@ngx-translate/http-loader": "16.0.1",
|
|
"@nx/angular": "23.2.1",
|
|
"@nx/devkit": "23.2.1",
|
|
"@nx/esbuild": "23.2.1",
|
|
"@nx/eslint": "23.2.1",
|
|
"@nx/eslint-plugin": "23.2.1",
|
|
"@nx/jest": "23.2.1",
|
|
"@nx/js": "23.2.1",
|
|
"@nx/playwright": "23.2.1",
|
|
"@nx/web": "23.2.1",
|
|
"@nx/workspace": "23.2.1",
|
|
"@playwright/test": "^1.62.1",
|
|
"@schematics/angular": "22.1.8",
|
|
"@swc-node/register": "1.12.1",
|
|
"@swc/core": "1.16.1",
|
|
"@swc/helpers": "0.5.23",
|
|
"@tailwindcss/typography": "0.5.20",
|
|
"@types/better-sqlite3": "^9.6.0",
|
|
"@types/cors": "2.8.19",
|
|
"@types/express": "5.0.6",
|
|
"@types/jest": "^30.0.0",
|
|
"@types/jsdom": "21.1.7",
|
|
"@types/mocha": "9.0.0",
|
|
"@types/node": "20.19.9",
|
|
"@types/proxy-from-env": "1.0.4",
|
|
"@types/video.js": "7.3.58",
|
|
"@typescript-eslint/eslint-plugin": "^8.69.0",
|
|
"@typescript-eslint/parser": "^8.69.0",
|
|
"@typescript-eslint/utils": "^8.69.0",
|
|
"angular-eslint": "22.5.0",
|
|
"astro": "7.2.10",
|
|
"autoprefixer": "10.5.4",
|
|
"cookie": "2.0.1",
|
|
"cors": "2.8.6",
|
|
"drizzle-kit": "0.31.10",
|
|
"electron": "^43.3.0",
|
|
"electron-builder": "^26.15.7",
|
|
"electron-playwright-helpers": "1.8.2",
|
|
"esbuild": "0.28.2",
|
|
"eslint": "^9.8.0",
|
|
"eslint-config-prettier": "^10.1.8",
|
|
"eslint-plugin-import": "2.32.0",
|
|
"eslint-plugin-playwright": "^1.6.2",
|
|
"express": "5.2.1",
|
|
"github-slugger": "2.0.0",
|
|
"globals": "15.9.0",
|
|
"html-escaper": "3.0.3",
|
|
"istanbul-lib-coverage": "3.2.2",
|
|
"istanbul-lib-report": "3.0.1",
|
|
"istanbul-reports": "3.2.0",
|
|
"jest": "^30.5.1",
|
|
"jest-environment-jsdom": "^30.5.1",
|
|
"jest-environment-node": "^30.5.1",
|
|
"jest-preset-angular": "17.0.0",
|
|
"jest-util": "^30.5.1",
|
|
"jsdom": "26.1.0",
|
|
"jsonc-eslint-parser": "^2.1.0",
|
|
"material-design-icons-iconfont": "6.7.0",
|
|
"mrmime": "2.0.1",
|
|
"ng-mocks": "14.17.6",
|
|
"node-gyp": "12.4.0",
|
|
"nx": "23.2.1",
|
|
"nx-electron": "22.0.0",
|
|
"parse-srcset": "1.0.2",
|
|
"parse5": "8.0.1",
|
|
"prettier": "^3.9.6",
|
|
"sharp": "0.35.4",
|
|
"tailwindcss": "^3.4.19",
|
|
"ts-jest": "^29.4.12",
|
|
"ts-node": "10.9.2",
|
|
"tslib": "^2.8.1",
|
|
"tsx": "4.23.13",
|
|
"typescript": "6.0.3",
|
|
"typescript-eslint": "^8.69.0",
|
|
"yaml": "2.9.0",
|
|
"zod": "4.5.4"
|
|
},
|
|
"pnpm": {
|
|
"overrides": {
|
|
"@esbuild-kit/core-utils>esbuild": "0.25.12",
|
|
"@hono/node-server@1.19.9": "1.19.17",
|
|
"@humanfs/node@0.16.7": "0.16.8",
|
|
"@xmldom/xmldom@0.8.11": "0.8.15",
|
|
"ajv@6.12.6": "6.14.0",
|
|
"ajv@8.17.1": "8.20.0",
|
|
"brace-expansion@1.1.12": "1.1.18",
|
|
"browserslist@4.28.1": "4.28.8",
|
|
"defu@6.1.4": "6.1.6",
|
|
"devalue@5.6.2": "5.6.4",
|
|
"express-rate-limit@8.2.1": "8.3.0",
|
|
"fast-uri@3.1.0": "3.1.6",
|
|
"flatted@3.3.3": "3.4.2",
|
|
"follow-redirects@1.15.11": "1.16.0",
|
|
"form-data@4.0.5": "4.0.6",
|
|
"h3@1.15.5": "1.15.10",
|
|
"hono@4.12.0": "4.13.5",
|
|
"immutable@5.1.4": "5.1.9",
|
|
"ip-address@10.1.0": "10.5.0",
|
|
"js-yaml@4.1.1": "4.3.2",
|
|
"js-yaml@4.3.0": "4.3.2",
|
|
"jsdom>ws": "8.21.3",
|
|
"lodash-es@4.17.22": "4.18.1",
|
|
"minimatch@3.1.2": "3.1.5",
|
|
"node-forge@1.3.3": "1.4.0",
|
|
"path-to-regexp@0.1.12": "0.1.13",
|
|
"picomatch@2.3.1": "2.3.2",
|
|
"picomatch@4.0.3": "4.0.7",
|
|
"postcss-selector-parser@6.1.2": "6.1.4",
|
|
"postcss@8.5.6": "8.5.26",
|
|
"qs@6.14.1": "6.15.3",
|
|
"rollup@4.52.3": "4.59.0",
|
|
"serialize-javascript@6.0.2": "7.1.0",
|
|
"smol-toml@1.6.0": "1.7.1",
|
|
"smol-toml@1.6.1": "1.7.1",
|
|
"svgo@3.3.2": "3.3.3",
|
|
"svgo@4.0.0": "4.1.0",
|
|
"svgo@4.0.2": "4.1.0",
|
|
"undici@7.28.0": "7.29.0",
|
|
"uuid@8.3.2": "11.1.1",
|
|
"yaml@1.10.2": "1.10.3"
|
|
},
|
|
"patchedDependencies": {
|
|
"nx-electron@22.0.0": "patches/nx-electron@22.0.0.patch",
|
|
"app-builder-lib@26.15.7": "patches/app-builder-lib@26.15.7.patch",
|
|
"vite@8.1.5": "patches/vite@8.1.5.patch"
|
|
}
|
|
}
|
|
}
|