mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 22:26:16 -08:00
Merge origin/master into claude/bold-napier-17a16f
Resolves conflicts with #1692-#1695: keeps both perf scripts, both README paragraphs, one context-map row, and merges the J1 launch sections into the performance-journeys document next to the renderer.initialBytes contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
60d276e573
40 files changed
+2325
-70
No files matched your search
@@ -12,7 +12,7 @@
|
||||
|
||||
- Date display should follow the user-selected app language from `TranslateService`.
|
||||
- When a template renders localized month or weekday names, pass the normalized app locale explicitly to `DatePipe`.
|
||||
- Angular locale data is registered in [apps/web/src/app/app-date-locales.ts](/apps/web/src/app/app-date-locales.ts).
|
||||
- Angular locale data is loaded lazily per language by `registerAppDateLocale()` in [apps/web/src/app/app-date-locales.ts](/apps/web/src/app/app-date-locales.ts): `main.ts` awaits the initial language's data before bootstrapping, and every `TranslateService.use()` call site registers the data first (through `AppDateLocaleService`), because the switch re-renders dates with the new locale and a locale without data throws. English needs no data. `AppDateLocaleService.use()` applies only the language requested last when switches overlap, and a locale whose chunk fails to load falls back to English formatting under that locale id until a later call loads it.
|
||||
- App language aliases are normalized in [libs/ui/pipes/src/lib/date-format.util.ts](/libs/ui/pipes/src/lib/date-format.util.ts):
|
||||
- `ary` -> `ar-MA`
|
||||
- `by` -> `be`
|
||||
|
||||
@@ -4,8 +4,8 @@ IPTVnator measures performance through a small set of everyday user journeys.
|
||||
Each journey has deterministic counters that are asserted exactly, and
|
||||
wall-clock timings that are recorded as evidence. Counters are ratcheted in CI:
|
||||
a committed baseline may only be lowered, and only with the measured output as
|
||||
evidence. This document is the contract for that loop. The journey harness
|
||||
lives in `apps/electron-backend-e2e/src/journeys` and
|
||||
evidence. This document is the contract for that loop. The journey harness lives in
|
||||
`apps/electron-backend-e2e/src/journeys` and
|
||||
`apps/electron-backend-e2e/src/performance/journey-*.ts`; the ratchet scripts
|
||||
live in `tools/performance/`.
|
||||
|
||||
@@ -18,8 +18,9 @@ live in `tools/performance/`.
|
||||
| J3 `playback` | click on a channel | HTML5 `playing` event |
|
||||
| J4 `search` | six-character query typed into global search | results list settled |
|
||||
|
||||
J1 is instrumented by the launch benchmark below. J2 to J4 follow the plan in
|
||||
`.plans/` and are added one thread at a time; each thread names its journey
|
||||
J1 is instrumented today: `renderer.initialBytes` from the built output, and
|
||||
the runtime counters of the launch benchmark below. J2 to J4 follow the plan
|
||||
in `.plans/` and are added one thread at a time; each thread names its journey
|
||||
and counter in the PR description.
|
||||
|
||||
## Running the journeys
|
||||
@@ -161,6 +162,131 @@ numbers so `tools/performance/check-journey-ratchet.mjs` can compare them with
|
||||
`tools/performance/journey-baselines.json`. A J1 baseline is added once the
|
||||
numbers are stable on the CI runner; until then the summary is evidence only.
|
||||
|
||||
## `renderer.initialBytes`
|
||||
|
||||
The bytes a browser fetches before Angular can bootstrap, read from the built
|
||||
`dist/apps/web/index.html`:
|
||||
|
||||
- `index.html` itself,
|
||||
- every same-origin `<script src>`, including `assets/app-config.js`,
|
||||
- every `<link rel="stylesheet">`,
|
||||
- every `<link rel="modulepreload">` chunk.
|
||||
|
||||
Manifest, icons, external URLs, commented-out tags and lazy chunks are not
|
||||
counted, so the value is the same for every language: a non-English launch
|
||||
additionally fetches that language's Angular locale chunk (about 2 KB), which
|
||||
belongs to the per-profile J1 benchmark rather than to this counter. A file
|
||||
that `index.html` references but the build did not emit is an error, never
|
||||
zero bytes. The value is raw (uncompressed) size, which is what the renderer
|
||||
parses. It is Angular's "Initial total" plus `index.html` and
|
||||
`assets/app-config.js` (about 4 KB together), so it sits slightly above the
|
||||
rounded figure the build prints; never copy that figure into a baseline, use
|
||||
the script's output. The bundle embeds only the app version from
|
||||
`package.json` (a named import, which esbuild tree-shakes), not the whole
|
||||
file, so editing scripts or dependencies does not move the counter.
|
||||
|
||||
```bash
|
||||
pnpm nx build web # production configuration
|
||||
pnpm run perf:initial-bytes # human-readable breakdown
|
||||
pnpm --silent run perf:initial-bytes -- --json # machine-readable; --silent keeps pnpm's headers out of stdout
|
||||
node tools/performance/measure-initial-bytes.mjs --summary dist/performance/journey-summary.json
|
||||
```
|
||||
|
||||
`--summary` writes the journey summary shape (`journeys.<journey>.counters`)
|
||||
that the ratchet checker consumes. `--dist <dir>` points the script at another
|
||||
build output, for example the `electron-performance` configuration.
|
||||
|
||||
The measurement script is `tools/performance/measure-initial-bytes.mjs`; its
|
||||
Node tests run with `pnpm nx test performance-tools` (Tier B in the coverage
|
||||
policy) and lint with `pnpm nx lint performance-tools`.
|
||||
|
||||
## Ratchet
|
||||
|
||||
`tools/performance/journey-baselines.json` holds one entry per journey and
|
||||
counter:
|
||||
|
||||
```json
|
||||
{
|
||||
"journeys": {
|
||||
"launch": {
|
||||
"renderer.initialBytes": {
|
||||
"value": 2739510,
|
||||
"unit": "bytes",
|
||||
"updatedAt": "2026-09-26",
|
||||
"evidencePr": 1693,
|
||||
"measuredWith": "pnpm nx build web && pnpm run perf:initial-bytes"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`tools/performance/check-journey-ratchet.mjs` compares a journey summary with
|
||||
that file:
|
||||
|
||||
- a counter above its `value` fails; counters are exact, there is no slack;
|
||||
- a wall-clock entry carries `toleranceRatio` and fails above
|
||||
`value × toleranceRatio`;
|
||||
- a baseline with no measurement in the summary fails, so dropping a
|
||||
measurement cannot disable the ratchet; a counter is read only from
|
||||
`journeys.<journey>.counters` and a wall-clock entry (one with
|
||||
`toleranceRatio`) only from `journeys.<journey>.wallClock`, so a value in
|
||||
the wrong section also counts as missing;
|
||||
- a measurement below its baseline passes and prints a "tighten" hint;
|
||||
- a measured counter without a baseline is noted, not failed;
|
||||
- checking nothing fails: an empty baselines file, or `--only` naming an
|
||||
entry that does not exist, cannot exit 0.
|
||||
|
||||
`--only <journey>/<counter>` (repeatable) restricts the check to the named
|
||||
baselines. A script that measures one counter writes its own summary file
|
||||
and checks only its counter, so it neither overwrites another measurement's
|
||||
summary nor fails the other baselines as unmeasured.
|
||||
|
||||
```bash
|
||||
pnpm run perf:initial-bytes:check # measure dist/apps/web into dist/performance/initial-bytes.summary.json, check only that counter
|
||||
pnpm run perf:ratchet:check # check every baseline against dist/performance/journey-summary.json
|
||||
```
|
||||
|
||||
CI runs `perf:initial-bytes:check` in the `Initial bytes ratchet` job of
|
||||
`.github/workflows/ci.yml` after a production build of `apps/web`, and uploads
|
||||
`dist/performance/` as the `performance-journey-summary` artifact. Like the
|
||||
rest of that workflow it runs for pull requests that target `master` and for
|
||||
pushes to `master`; a stacked PR that targets another branch gets no run until
|
||||
it is retargeted, so dispatch one with `gh workflow run ci.yml --ref <branch>`
|
||||
when you need the number. A PR that grows the counter fails that job.
|
||||
|
||||
That runner is the canonical measurer: take baseline values from its output,
|
||||
not from a local build. A local macOS build of the code before #1695 is 2
|
||||
bytes smaller in `main.js` (the eager locale imports); since #1695 the two
|
||||
have been byte-identical. (An apparent 556-byte platform difference during
|
||||
the first measurements was otherwise `package.json` text embedded in
|
||||
`main.js`, which moved with every script edit; #1692 fixed that by importing
|
||||
only the version.)
|
||||
|
||||
The job also refuses a weakened baselines file:
|
||||
`tools/performance/check-baseline-direction.mjs` compares
|
||||
`journey-baselines.json` with the revision the change is measured against
|
||||
(the target branch of a pull request, the previous head of a `master` push,
|
||||
`master` for a manual dispatch) and fails when any
|
||||
entry's enforced limit (`value × toleranceRatio`) went up, a tolerance widened
|
||||
or an entry disappeared, so a PR cannot grow the payload and raise the
|
||||
baseline to match. Lowered limits and new entries pass.
|
||||
|
||||
Baselines only move down. Lower `value` in the same PR as the change that
|
||||
earned it, set `updatedAt` and `evidencePr`, and paste the measurement output
|
||||
into the PR. Never raise a value to make a PR pass: if growth is a deliberate
|
||||
trade-off, say so in the PR and let the maintainer decide.
|
||||
|
||||
## Adding a counter
|
||||
|
||||
1. Produce the value from the built output or from a deterministic probe, not
|
||||
from source heuristics. Missing inputs must fail the measurement.
|
||||
2. Emit it under `journeys.<journey>.counters.<name>` in the summary JSON.
|
||||
3. Cover the extraction and the failure modes with `node --test` and register
|
||||
the test file in `tools/performance/project.json`.
|
||||
4. Validate the counter before it becomes a guardrail: one PR must show that
|
||||
lowering it moved wall-clock in the same journey.
|
||||
|
||||
## Adding a journey
|
||||
|
||||
1. Add `apps/electron-backend-e2e/src/journeys/<journey>.journey.ts`. Seed the
|
||||
|
||||
@@ -120,7 +120,6 @@ and JSON summary output. CI uploads the merged Tier A report to Codecov with the
|
||||
| Web app browser flows | `pnpm nx run web-e2e:e2e -- --project=chromium` |
|
||||
| Electron flows | `pnpm nx run electron-backend-e2e:e2e` |
|
||||
| VOD multi-source | `pnpm nx run electron-backend-e2e:e2e-ci--src/vod-multi-source.e2e.ts` |
|
||||
| Performance journeys | `pnpm run perf:journeys` (see [performance journeys](performance-journeys.md)) |
|
||||
|
||||
Use atomized E2E targets when available, for example
|
||||
`pnpm nx run web-e2e:e2e-ci--src/xtream.e2e.ts`.
|
||||
@@ -154,6 +153,28 @@ Identical English fallback values are reported as warnings by default; use
|
||||
`node tools/i18n/check-drift.mjs --fail-on-identical` for a stricter translation
|
||||
audit.
|
||||
|
||||
## Performance
|
||||
|
||||
```bash
|
||||
pnpm nx build web
|
||||
pnpm run perf:initial-bytes # breakdown only
|
||||
pnpm run perf:initial-bytes:check # measure, then compare with the committed baseline
|
||||
pnpm nx test performance-tools
|
||||
pnpm run perf:journeys # J1 launch benchmark, writes dist/performance/journeys/<timestamp>/summary.json
|
||||
```
|
||||
|
||||
`perf:initial-bytes` reads the built `dist/apps/web/index.html` and sums the
|
||||
bytes on the initial path (the J1 counter `renderer.initialBytes`).
|
||||
`perf:initial-bytes:check` then fails if the value exceeds
|
||||
`tools/performance/journey-baselines.json`; baselines only move down. CI runs
|
||||
the same check in the `Initial bytes ratchet` job of `ci.yml` for PRs that
|
||||
target `master` and for `master` pushes (dispatch it with
|
||||
`gh workflow run ci.yml --ref <branch>` for a stacked branch). `perf:journeys` builds the `electron-performance` configuration and runs the
|
||||
J1 launch benchmark against the Xtream mock; its probe specs run with
|
||||
`pnpm nx run electron-backend-e2e:test-performance-harness`. The contract, what
|
||||
counts and how to add a counter or a journey are in the
|
||||
[performance journeys](performance-journeys.md) document.
|
||||
|
||||
## Logging
|
||||
|
||||
Runtime playback and EPG debug logs should use the existing logger or trace
|
||||
|
||||
@@ -14,7 +14,7 @@ are not prerequisites for reading repository contracts.
|
||||
| Bootstrap, project placement, dependencies, aliases and lint configuration; root Nx config and project-local project.json files | [Nx boundaries](../architecture/nx-workspace-boundaries.md), [security overrides](../architecture/dependency-security-overrides.md) | [Nx architecture](../../.codex/skills/iptvnator-nx-architecture/SKILL.md) |
|
||||
| Angular conventions; docs and skills maintenance | [Agent workflow](../development/agent-workflow.md) | Use the area's skill below |
|
||||
| Unit, E2E, lint and coverage; `tools/coverage` | [Validation map](../architecture/validation-map.md) | Use the area's validation section |
|
||||
| Performance journeys, benchmark probes and the ratchet; `apps/electron-backend-e2e/src/journeys`, `apps/electron-backend-e2e/src/performance` | [Performance journeys](../architecture/performance-journeys.md) | None |
|
||||
| Performance journeys, counters, benchmark probes and the CI ratchet; `apps/electron-backend-e2e/src/journeys`, `apps/electron-backend-e2e/src/performance`, `tools/performance` | [Performance journeys](../architecture/performance-journeys.md) | Read the contract directly |
|
||||
| Electron entry/events/preload and CDP; `apps/electron-backend` | [Debugging and trace flags](../development/electron-debugging.md), [Electron security](../architecture/electron-security.md) | Use the available global electron skill for automation |
|
||||
| Releases, notes, screenshots, native assets, Linux manager metadata; `tools/release` | [Release pipeline](../architecture/release-pipeline.md), [note format](../../.changes/README.md) | [Release notes](../../.codex/skills/release-notes/SKILL.md), [release cut](../../.codex/skills/release-cut/SKILL.md) |
|
||||
|
||||
|
||||
Reference in new issue
Block a user