mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
efbee7cc4804d580b2ab80f7542669adaecfa363
2675
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
efbee7cc48 | test(downloads): cover offline detail journey | ||
|
|
89e708c591 | fix(downloads): prefer local playback in Xtream details | ||
|
|
0313dd7a9b | fix(downloads): preserve external launch priority | ||
|
|
95aa73058b | fix(downloads): prefer local playback in shared details | ||
|
|
34fa69e6f5 | fix(downloads): honor the global cover size | ||
|
|
727ea58bd7 | test(downloads): cover pending series navigation | ||
|
|
4f3567e1d0 | fix(downloads): open completed movies in details | ||
|
|
5ef5d6713f | docs(downloads): plan detail navigation follow-up | ||
|
|
211000a8e3 | docs(downloads): clarify detail-first offline behavior | ||
|
|
bcc2e36a05 | feat(downloads): finish manager MVP | ||
|
|
eb55c17a05 | feat(downloads): add active download queue | ||
|
|
8e1af010a9 | feat(downloads): add ready-to-watch library | ||
|
|
28770aad09 | fix(downloads): report library navigation failures | ||
|
|
ce251e5f9b | refactor(downloads): isolate library navigation | ||
|
|
bff9ca1491 | fix(downloads): stabilize malformed view model data | ||
|
|
aae5450f4c | test(downloads): close view model coverage gaps | ||
|
|
d6e1ee5d0c | feat(downloads): derive queue and library view model | ||
|
|
bc29f24eb2 | fix(downloads): make active count accessible | ||
|
|
117bd75f01 | fix(downloads): keep renderer download state global | ||
|
|
505face7e4 | docs(downloads): tighten manager validation plan | ||
|
|
6995b5765a | docs(downloads): plan manager MVP implementation | ||
|
|
b7f02c51b4 | docs(downloads): specify manager MVP redesign | ||
|
|
9bb1811984 |
fix(playback): escape commas in mpv header fields on the TS paths (#1323)
* fix(playback): escape commas in mpv header fields on the TS paths PR #1321 fixed the comma truncation of --http-header-fields inside the native embedded-mpv addon, but the same OPT_STRINGLIST parsing bites three TypeScript call sites that join header fields with ',': - external MPV CLI launch (--http-header-fields=...) - external MPV instance reuse (set_property http-header-fields) - embedded MPV frame-copy loadfile options (opt.http-header-fields); the helper's %len% quoting protects only the option-list level, mpv still stringlist-parses the value afterwards The Stalker MAG user agent contains "(KHTML, like Gecko)", so strict portals received a truncated X-User-Agent and rejected live streams with HTTP 400 (#910). Escape backslashes and commas per field with the same scheme as the native fix, via a shared util. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(playback): cover the reused-instance IPC header escaping Review follow-up (Greptile on #1323): the regression coverage only exercised the CLI spawn path. Capture the JSON IPC traffic of a second, reused mpv launch and assert the escaped MAG user agent survives the set_property http-header-fields transport, so later serialization changes cannot silently reintroduce truncated headers. net is mocked with a passthrough default because the VLC specs bind a real ephemeral port via createServer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
814161274b | fix(playback): escape commas in mpv http header fields (#1321) | ||
|
|
b14ce2452b | fix(packaging): add verified source mirror fallback (#1325) | ||
|
|
9f4e11d6de |
fix(playback): structure Shaka diagnostics (#1318)
* docs(playback): design structured Shaka diagnostics * fix(playback): structure Shaka diagnostics * docs(playback): document Shaka evidence boundary * docs(playback): fix Shaka validation commands * fix(playback): preserve Shaka fallback evidence * fix(playback): preserve Shaka text error evidence |
||
|
|
9a50e7385b | fix(playback): structure Video.js diagnostics (#1317) | ||
|
|
46c7713841 |
fix(ui): preserve EPG in narrow channel rows (#1312)
Preserve current-program context and enabled actions in narrow channel rows while aligning loaded rows, skeletons, and virtual-scroll geometry across M3U, Xtream, Stalker, Favorites, and Recent views. |
||
|
|
2ac0de752f |
fix(skills): align repository guidance with implementation (#1315)
* docs(skills): design implementation synchronization * docs(skills): plan implementation synchronization * fix(release): filter internal notes from public body * docs(release): synchronize release workflow guidance * fix(stalker): normalize catalog series flags * fix(stalker): preserve progress with scoped episode IDs * fix(playback): expose strict position persistence * docs(stalker): record series position compatibility * test(skills): validate repository skill contracts * fix(database): keep SQL trace values private * docs(skills): refresh Nx and SQLite ownership * docs(skills): align provider and UI guidance * docs(skills): tighten validated guidance * docs(release): require exact release pushes * style(electron): remove trailing blank line * fix(ci): classify repository skills coverage |
||
|
|
99d167993d |
fix(playback): structure HLS diagnostics (#1316)
* docs(playback): design structured HLS diagnostics * docs(playback): plan structured HLS diagnostics * fix(playback): structure HLS diagnostics * docs(playback): document structured HLS evidence * fix(playback): keep HLS startup logs private |
||
|
|
bf13849d69 |
fix(playback): avoid false codec diagnostics (#1314)
* docs(playback): design accurate native diagnostics * docs(playback): plan accurate native diagnostics * fix(playback): classify native source errors from evidence * fix(playback): preserve Video.js HTTP error context * fix(playback): show explicit HTTP playback errors * docs(playback): document native error evidence |
||
|
|
32ba209b63 |
fix(portals): restore fresh-import pins atomically (#1311)
* fix(portals): restore fresh-import pins atomically * fix(portals): preserve Xtream restore retry state * fix(portals): serialize Xtream restore revisions |
||
|
|
78df3e7dbb |
fix(portals): match Greek titles whichever sigma the provider typed (#1310)
Greek Σ has two lowercase forms — medial σ and word-final ς — and neither the candidate query nor the confirmation treated them as one letter. The GLOB scan built each character's class from a one-way reach that only arrived at ς when it started from ς, so a request for "ΑΣ" never admitted a stored "Ας". Classes are now built from a fold group — every character sharing an uppercase form — derived by scanning the cased ranges at module load the way ACCENTED_BY_BASE already is. It generalises past sigma on its own: dotless ı folds with i, long ſ with s, historic Cyrillic letterforms with В Д О С Т Ъ Ѣ. Only the 24 groups of 767 that a per-character fold would miss are kept. Admitting the row was only half of it. normalizeTitleKeys then compared "ασ" against "ας" and discarded it, because toLowerCase picks the sigma form by position. Both SQL tiers already folded them together — SQLite's trigram tokenizer does full Unicode folding natively, unlike LOWER() — so the JS confirmation was the only tier that did not, making this a pre-existing gap on the FTS path as well. Normalization now rewrites ς to σ after lowercasing, which is what Unicode case folding does. Guards unchanged: a case mapping that changes length (ß → SS, İ) or a GLOB metacharacter still returns null rather than a partial pattern. |
||
|
|
063662028a |
feat(portals): find the same movie in your other playlists (#1286)
* feat(portals): find the same movie in your other playlists A movie that exists in several imported Xtream playlists now shows a "Sources N" chip on its detail page and in the player. Switching playlist mid-film keeps the timecode, a preferred source can be pinned per movie, and a failed stream offers the alternatives instead of a dead end. The governing rule is that a guess is never presented as a fact. Every metadata value carries where it came from — `api` (the provider said so), `parsed` (inferred from the title) or `probe` (we contacted the stream). Facts render as plain tags, guesses are prefixed `~` in a warning colour, and an unknown value renders no tag at all plus a "check" affordance. Ranking and failover read through `factualOnly()`, so a filename claiming 4K is structurally unable to outrank a source that was actually reached. A probe that could not complete reports "unknown", never "unavailable". Scope is deliberately narrow: Xtream to Xtream, movies only, Electron only. Stalker never reaches the `content` table and M3U is a JSON blob whose search forces live content; both are additive later, since the candidate type already carries all three portal kinds. In the PWA every entry point is gated off and the chip renders nothing. Auto-failover is opt-in and off by default. Each source is tried at most once per session, so it terminates structurally, and the switch is never silent — the toast names the new playlist, offers an undo, and warns that the dub may differ only when both sides state an audio track as fact. Notable details: - Playlist names are routinely the pasted URL, credentials included. They are never rendered raw; a short host-only label is derived instead. - Quality is derived from pixel width, not height: a 2.39:1 1080p master is 1920x800, and bucketing that by height would publish "720p" as a fact. - Switching is a single `inlinePlayback.set()` so the player and engine survive and re-seek; the carried position is read before the 15s persistence throttle so it does not rewind. - Sources from one playlist collapse into a group, since the same film often appears there several times under different stream ids. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(portals): stop stale source resolutions from committing Addresses three defects Greptile found in the multi-source review. **Concurrent switches committed out of order.** Selecting a second source before the first resolution returned let the slower request overwrite the newer selection and repoint Undo at itself. `switchTo` now takes a sequence number and drops its result if a newer switch already committed. **Stale switches crossed movie sessions.** Navigating to another film while a resolution was in flight let the continuation activate the old film's source inside the new controller — and restart it from that session's zero resume position. The controller is now snapshotted per operation and the movie session is revalidated after every await. `check()` had the same hazard across its two awaits and is guarded the same way. **Short titles skipped discovery entirely.** The trigram tokenizer cannot index tokens under three characters, so "Up", "It" or "Us" produced an empty MATCH expression and the query was discarded before SQLite was consulted — the chip could never appear for those films. Discovery now falls back to a bounded scan when FTS structurally cannot serve the title; the existing two-tier normalized confirmation still rejects loose hits like "Upgrade". Each fix carries a regression test; all three were mutation-checked by removing the guard and confirming exactly those tests fail. The previous test asserting that short titles return nothing encoded the bug and has been replaced. The host spec passed 400 lines, so its fixtures moved to a shared module and the race suite into its own file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(portals): make the pin decide playback and keep failover going Second round of Greptile review findings. **A pin had no behavioural effect.** Loading a stored pin only decorated the row: Play still started the route's playlist and failover ranking ignored `isPinned`, so "make this the main source" survived a restart as an icon and nothing else. The primary action now starts from the pinned source when one is set, and the pin outranks everything else in failover ranking. **Failover stopped at the first unresolvable candidate.** An expired account or a failing `get_vod_info` on the top-ranked source ended the attempt, and since production calls `failover()` only once — on the original playback failure — a healthy lower-ranked source was never reached. It now continues through untried candidates. `switchTo` reports why it stopped so the loop can tell "could not resolve, try the next one" from "something newer owns the screen"; without that distinction a superseded switch would have spun forever, because only the former marks the candidate tried. **Identity ignored enrichment.** The key was `playlistId:contentId:title`, so when `get_vod_info` added a TMDB id and release year to an unchanged title the host saw no change, never reloaded, and kept yearless discovery and title-only pin keys — a `tmdb:`-keyed pin could never be found. The key now covers every field that affects matching. **A server refusing HEAD read as unavailable.** Some stream hosts answer 405 or 501 to HEAD yet serve the media over GET. The probe now retries once with the ranged GET the main process already supported, instead of caching a working source as failed and penalising it during failover. Greptile also flagged a missing token check after the resolve await in `switchTo`; that guard landed in |
||
|
|
b1f77c678e |
test(performance): prevent renderer heartbeat omission (#1308)
* test(performance): normalize sub-ms IPC clock skew * test(performance): prevent heartbeat coordinated omission |
||
|
|
deae0a2a4d |
fix(xtream): keep sparse VOD details playable (#1303)
* fix(xtream): keep sparse VOD details playable * fix(xtream): scope VOD fallback to active playlist * fix(xtream): render sparse VOD before recovery * fix(xtream): recover Similar VOD provider categories |
||
|
|
9b7776a901 |
chore(lint): hold tests to their own max-lines ceiling (#1306)
* chore(lint): hold tests to their own max-lines ceiling The flat 400-line cap treated a spec like a component. A spec is a flat list of independent cases, so hitting the cap there produces arbitrary `-2.spec.ts` splits and hides coverage instead of surfacing design debt — 65 of the 138 files over the limit were tests. Production code keeps 400. Tests (`**/*.spec.ts`, `**/*.e2e.ts`, and everything under `apps/*-e2e/**`) get 1200. Blank lines and comments no longer count, so a docblock can't be the reason a file must be split. Both limits now live in tools/eslint/max-lines-config.mjs, imported by eslint.config.mjs and the baseline generator alike. The generator decides who belongs on the list by running ESLint's own max-lines rule instead of counting lines itself — a private reimplementation would disagree with the rule the moment either side changed (a `//` inside a template literal is enough) and yield a baseline that turns CI red while looking correct. The baseline drops 126 -> 68 entries with nothing added, and six now-dead `eslint-disable max-lines` directives are removed. A new eslint-tools test asserts the committed baseline still matches what the generator produces, so a stale entry or a forgotten regeneration fails CI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(lint): classify eslint-tools in the coverage policy A project with a `test` target must be assigned a coverage tier, so adding eslint-tools broke `coverage:policy:check` before the unit suite even ran. Tier B alongside packaging and release-tools: these are Node tests over lint tooling, and a coverage percentage across a generated list would not mean anything. CI runs Tier B/C through its own `--run-non-tier-a` step, so the baseline-consistency test executes there rather than being skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
055170d188 |
test(performance): harden Xtream startup retry (#1307)
* test(performance): harden Xtream startup retry * test(performance): preserve Xtream teardown failures * test(performance): retry Xtream profile cleanup |
||
|
|
3c342bc555 | test(performance): preserve delayed worker samples (#1305) | ||
|
|
faec40ff7b |
test(performance): stabilize Xtream benchmark startup (#1304)
* test(performance): stabilize Xtream benchmark startup * test(performance): bound cancellation clock skew |
||
|
|
99a85da6b0 |
feat(packaging): register IPTVnator as the .m3u/.m3u8 handler (#1301)
* feat(packaging): register IPTVnator as the .m3u/.m3u8 handler Every runtime path for an OS-supplied playlist existed, but no packaging metadata claimed the file types — so the OS never offered IPTVnator as a handler and `open-file` could not fire from Finder. `fileAssociations` declares one entry per extension. Electron Builder derives all three platform registrations from it: macOS `CFBundleDocumentTypes` (the prerequisite for `open-file`), the NSIS registry entries, and, on Linux, the desktop entry's `MimeType` plus `/usr/share/mime/packages/iptvnator.xml` for deb/rpm/pacman. Neither platform needs a dedicated icon — both fall back to the app icon. Declaring `MimeType` under `linux.desktop.entry` would not have worked: Electron Builder assigns the association-derived value *after* spreading that object, so an explicit key there is silently overwritten. The per-association `mimeType` fields produce the same entry through the supported path. Registering the types also exposes a gap in the delivery side. The generated Linux `Exec` ends in `%U`, so file managers hand over a percent-encoded `file://` URI rather than a path, which `createPlaylistOpenRequest` would have resolved into a bogus relative path. It now decodes a `file://` candidate before the extension check. Suppressing the `%U` instead would mean putting an exec code in `linux.executableArgs`, which also passes it to the app as a real argument. Verified on macOS against a signed packaged bundle: Launch Services lists the app as a `public.m3u-playlist` handler, and an LS-initiated open imports the playlist both on a cold launch and against the already-running process. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(playlist): open every playlist of a multi-file selection `%U` is the plural exec code, so selecting several playlists in a Linux file manager is one launch carrying one argument per file. Both argv paths called `extractPlaylistOpenRequestFromArgv`, which returned at the first match, so everything after the first playlist was silently discarded — a gap this PR itself opened by making the desktop entry reachable in the first place. The extractor is now plural and returns every match in argument order, and the queue gained `enqueueAll` so a selection is pushed under a single flush: a delivery that fails partway leaves the untouched remainder queued in arrival order rather than interleaved. Covered by unit tests over a mixed argv (percent-encoded `file://` URI, a non-playlist argument, a second URI) and by a new Electron E2E that launches with two playlist arguments and asserts both are imported. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
bc4e3a2e2c |
test(performance): bound worker sampling finalization (#1302)
* test(performance): bound worker sampling finalization * test(performance): reject timed-out worker captures * test(performance): settle every worker sample |
||
|
|
f80eb4d1b9 |
fix(playlist): open playlists handed over by the OS (#1299)
Opening an .m3u/.m3u8 file from the command line or a file association did nothing. The renderer parsed `process.argv` and sent an `OPEN_FILE` IPC event that had no `ipcMain` handler and no preload channel, so `sendIpcEvent` logged it as an unknown type and dropped it. The path now belongs to the main process, which is where the OS actually delivers it: - argv is parsed on first launch (skipping the executable and Chromium switches) and normalized to an absolute path; - macOS gets an `open-file` listener registered before `whenReady`, since Launch Services never puts the path in argv; - the single-instance guard forwards a second launch's argv and working directory instead of discarding them, so opening a playlist against a running app works too. Requests are queued in the main process until the renderer subscribes to the `OPEN_FILE` push and drains the queue, which closes the startup race. The import itself reuses the existing file path, so persistence, playlist-scoped EPG and the navigation to the new playlist behave exactly like a dialog import; a failed open now surfaces a snackbar instead of silence. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
80af9257a0 |
refactor(portals): share external-button and position-writer logic (#1298)
The Xtream and Stalker VOD detail views each carried a private copy of two behaviours: deriving the Play/Stop button state from the active external (MPV/VLC) session, and throttled persistence of the inline player position. A Play button or a resume point that behaves differently per portal is the kind of divergence users notice, so both now read from one implementation. Extracts `createExternalPlaybackButtonState` and `createInlinePlaybackPositionWriter` into portal/shared/util, and lifts the Stalker VOD download errand into its own helper. Behaviour is unchanged; the shared helpers are deliberately identical to the copies they replace. This also brings both hosts back under the 400-line ESLint limit, neither of which was baselined: vod-details.component.ts 389 -> 333 stalker-catalog-detail.component 394 -> 325 vod-details-playback.service.ts 345 -> 275 Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
72f8cebd2e |
fix(e2e): reap data directories abandoned by earlier runs (#1296)
`removeDataDir` tolerates a locked directory rather than failing the run, but then abandons it, and nothing collects it on our behalf: Windows never clears %TEMP% on process exit, and the Unix equivalents only run on a schedule. Every teardown that lost that race leaked a database and user-data tree on developer machines and long-lived runners, invisibly, while CI stayed green. Sweeps leftover `iptvnator-electron-e2e-*` directories once per run, before the first one is created. Ownership is settled by pid rather than age: each run records its pid and the sweep asks the OS via `process.kill(pid, 0)`. - A live owner is kept, so a concurrent suite is never collected — this repo is routinely checked out into several worktrees at once. Age cannot answer this: writes land under `databases/` and `user-data/`, which never refreshes the root's mtime, so a run paused in a debugger looks arbitrarily old. - A dead owner is collected immediately. - An undeterminable owner (missing, empty or malformed marker) falls back to a 24h cutoff. The marker is published via rename so a half-written file cannot bypass that guard. - A live-looking owner past a week is collected anyway, since the OS recycles pids and a stranger inheriting one would otherwise pin the directory forever. Covered by a 10-test spec running on Linux, macOS and Windows, since `process.kill(pid, 0)` semantics are platform-specific. Each behaviour was verified to fail against the preceding implementation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c637a0520e |
chore(deps): bump softprops/action-gh-release from 2 to 3 (#1284)
* chore(deps): bump softprops/action-gh-release from 2 to 3 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore(ci): allow softprops/action-gh-release v3 in the Snap workflow policy The Snap supply-chain policy test pins the exact major of every action the build workflow may use, so bumping softprops/action-gh-release in the workflow without updating BUILD_ACTION_ALLOWLIST fails publish-snap-workflow.test.mjs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 4gray <serega05@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
55f68e73c8 |
chore(deps): bump actions/setup-node from 4 to 7 (#1285)
* chore(deps): bump actions/setup-node from 4 to 7 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 7. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore(ci): allow actions/setup-node v7 in the Snap workflow policy The Snap supply-chain policy test pins the exact major of every action the build workflow may use, so bumping actions/setup-node in the workflow without updating BUILD_ACTION_ALLOWLIST fails publish-snap-workflow.test.mjs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 4gray <serega05@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
553f45dedc |
chore(deps): bump actions/cache from 4 to 6 (#1281)
* chore(deps): bump actions/cache from 4 to 6 Bumps [actions/cache](https://github.com/actions/cache) from 4 to 6. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore(ci): allow actions/cache v6 in the Snap workflow policy The Snap supply-chain policy test pins the exact major of every action the build workflow may use, so bumping actions/cache in the workflow without updating BUILD_ACTION_ALLOWLIST fails publish-snap-workflow.test.mjs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 4gray <serega05@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a2fafcfc08 |
test(performance): add end-to-end Xtream benchmark harness (#1300)
* docs(performance): plan Xtream benchmark * feat(xtream-mock-server): add deterministic 100k fixture * style(xtream-mock-server): apply repository formatting * fix(xtream-mock-server): harden performance fixture data * feat(xtream-mock-server): add performance control plane * docs(performance): correct Xtream capture plan * fix(xtream-mock-server): harden performance controls * fix(xtream-mock-server): harden control lifecycle * feat(performance): add Xtream preload markers * feat(performance): trace Xtream main phases * feat(performance): mark Xtream store publications * feat(performance): trace Xtream database phases * feat(performance): trace Xtream delete cancellation * feat(performance): capture Xtream phase attribution * feat(performance): mark Sources Xtream refresh * test(performance): define Xtream benchmark evidence contracts * test(performance): add Xtream benchmark runner * test(performance): surface failure evidence writes * test(performance): align database read clock * test(performance): preserve capture failure contracts |
||
|
|
5e725a06f3 |
chore(deps): bump actions/deploy-pages from 4 to 5 (#1283)
Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 4 to 5. - [Release notes](https://github.com/actions/deploy-pages/releases) - [Commits](https://github.com/actions/deploy-pages/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/deploy-pages dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |