Commit Graph
323 Commits
Author SHA1 Message Date
4gray d6a35cfa31 fix(agents): validate visible HTML and image source sets 2026-09-20 20:57:34 +02:00
4gray 98e6fd3983 fix(agents): use GitHub-compatible heading slugs 2026-09-20 20:08:22 +02:00
4gray bdf7017a97 fix(agents): parse prose and rendered HTML anchors 2026-09-20 19:44:07 +02:00
4gray 3a2497476b docs(agents): compact root guidance and preserve task-specific knowledge 2026-09-20 18:18:51 +02:00
dependabot[bot] d4df0fd81a chore(deps-dev): bump @types/better-sqlite3 from 7.6.13 to 9.6.0 (#1501)
Bumps [@types/better-sqlite3](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/better-sqlite3) from 7.6.13 to 9.6.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/better-sqlite3)

---
updated-dependencies:
- dependency-name: "@types/better-sqlite3"
  dependency-version: 9.6.0
  dependency-type: direct:development
  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>
2026-09-19 19:42:19 +02:00
4grayandClaude Fable 5.1 f13d0c55da build(deps): resolve the eight open Dependabot security alerts (#1635)
Bump astro 7.2.4 → 7.2.10 (critical, website build) and retarget the pinned
pnpm overrides for the transitive alerts: js-yaml → 4.3.2 (the one runtime
path, via electron-updater), smol-toml → 1.7.1 (new key for nx's exact 1.6.1
pin), svgo → 4.1.0 (new key for astro's 4.0.2 resolution) and hono → 4.13.5.
Every target stays inside its parent's declared range except nx's exact
smol-toml pin, which is now recorded as the deliberate exception in
docs/architecture/dependency-security-overrides.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 18:02:07 +02:00
4grayandClaude Fable 5.1 01c423ac43 fix(portals): stop treating a slow panel as a dead host; IPv4 fallback budget in Electron (#1621)
* fix(portals): apply the IPv6->IPv4 fallback budget in the Electron process

The 2500 ms happy-eyeballs attempt timeout from #1404 only ever ran in the
web backend. The Electron main process and its playlist-refresh and EPG
workers kept Node's 250 ms default, so a dual-stack panel hostname behind a
VPN or a slow link failed every connection attempt in a row and tripped the
host connectivity guard. The module now lives in `@iptvnator/shared/host-health`
and every Node isolate that opens connections applies it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portals): stop treating a slow panel as a dead one in the host guard

axios raises the same ECONNABORTED whether the SYN went unanswered or the
panel accepted the connection and then thought for longer than the request
budget. Two such timeouts opened the breaker and every request to the panel
was refused for 30 s with "portal is not responding" — the shape behind the
"connection keeps dropping" reports on 0.23 and nightly.

Both transports now report whether the TCP connection was established
(`onConnect`: Electron through a per-request observed agent instead of the
shared keep-alive globalAgent, the web backend through the transport that owns
the ClientRequest), and `classifyHostRequestFailure(error, { connected })`
downgrades a host-level code observed after the handshake to inconclusive.
Redirect attribution keeps precedence. A host that never accepts the
connection trips the guard exactly as before.

The Xtream mock gains a `silent:silent` scenario whose detail actions accept
and never answer, plus a real-socket regression spec for the guard.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portals): let an accepted connection clear the host-failure streak

Review finding: an unanswered SYN, then an accepted-but-slow timeout, then
another unanswered SYN still reached the two-failure threshold, because the
middle request was merely not counted. An accepted TCP connection is the
reachability the guard measures, so it now reads as `responded` and clears
the streak like an HTTP response would. Regression coverage for the mixed
sequence on one flapping loopback origin (Electron) and through the proxy
route (web backend).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portals): credit an accepted connection when it happens, not when the request settles

Review findings. A request that connected and then hung for 30 s cleared,
on its eventual timeout, the failures later requests had recorded while it
waited — reopening a host that had just died on evidence older than theirs.
The connect hook now reports the connection the moment it fires through a
new `HostConnectivityGuard.reportConnected`, which clears the failure streak
but closes no open or half-open breaker (the trial keeps its slot until it
settles), and the settled timeout is inconclusive.

Electron also skips the socket observer while an environment proxy
(`http_proxy` / `https_proxy` / `all_proxy`) applies to the request: through
a proxy the socket connects to the proxy, whose handshake proves nothing
about the portal, so those requests keep the pre-observer behaviour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portals): decide the proxy exemption with axios' own resolution

Review findings. The hand-rolled environment check ignored `no_proxy`, so a
LAN portal exempted from the proxy lost its connect observer and slow
requests to it still tripped the breaker; it also read the variables with
`??`, letting an empty lowercase one mask a populated uppercase one that
axios would honour. The decision now calls `proxy-from-env`'s
`getProxyForUrl`, the same pinned package axios' http adapter uses,
declared as a direct dependency so the packaged app carries it.

The validated-axios spec clears and restores every proxy variable around each
case, so a runner that exports a proxy cannot change what the cases prove.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 14:50:50 +02:00
4grayandClaude Fable 5.1 634a66b1e4 build(deps): declare node-gyp for the embedded MPV native build (#1625)
`apps/electron-backend/build-embedded-mpv.js` resolves the compiler with
`require.resolve('node-gyp/bin/node-gyp.js')` and its comment claimed the
package was "a declared devDependency" — it never was. node-gyp reached the
tree only as a transitive of `@electron/rebuild`, in pnpm's hidden hoist
(`node_modules/.pnpm/node_modules`). pnpm's `.bin` shims export that
directory on NODE_PATH, which is why `pnpm nx …`, `pnpm run build:backend`
and CI kept building the addon, while a plain
`node apps/electron-backend/build-embedded-mpv.js` on a clean install failed
with "Unable to resolve node-gyp".

Declare node-gyp 12.4.0 (the version already in the lockfile store) as a root
devDependency so the resolution no longer depends on a shim implementation
detail, correct the stale comment, and record the contract in the
embedded-MPV architecture doc plus the Agent Bootstrap notes.

No packaged-build change: the no-runtime skip and its
`embedded-mpv-unavailable.txt` marker are untouched.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 11:30:50 +02:00
4gray e9eca1c386 chore(deps): upgrade Angular to 22.1 and Nx to 23.2 (#1603)
* chore(deps): upgrade Angular to 22.1 and Nx to 23.2

* fix(deps): complete Angular migrations after rebasing on master

* fix(ci): use the Node pin for Windows runtime refresh

* docs(deps): synchronize the workspace-shell Node requirements
2026-09-14 19:02:40 +02:00
4grayandClaude Opus 5 61ac15372c perf(website): serve fonts and the avatar locally, load comments on demand
Every page pulled its three typefaces from fonts.googleapis.com and
fonts.gstatic.com, each blog post fetched the author avatar from
githubusercontent.com, and the giscus client script ran on page load. That is
four outside origins contacted before a reader does anything, each costing a
DNS lookup and a TLS handshake on the critical path.

Fonts now come from the @fontsource packages the app already uses and are
emitted as .woff2 beside the site; the avatar is a 3 KB file in public/; and
the giscus embed is created by a "Show comments" button that carries the
configuration as data attributes, so the script is only built when a reader
asks for it.

A delivered page now makes no third-party request at all, verified across the
whole build. The variable Bricolage package names itself "Bricolage Grotesque
Variable", so that exact name leads the display stack in the Tailwind config.
The giscus test now checks the button configuration and asserts the client
script is absent from the delivered HTML.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 17:19:04 +02:00
dependabot[bot] de81e3b238 chore(deps): bump the npm-minor-patch group across 1 directory with 19 updates (#1528)
Bumps the npm-minor-patch group with 19 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [axios](https://github.com/axios/axios) | `1.19.0` | `1.20.0` |
| [hls.js](https://github.com/video-dev/hls.js) | `1.7.0` | `1.7.1` |
| [marked](https://github.com/markedjs/marked) | `18.0.9` | `18.0.11` |
| [@astrojs/sitemap](https://github.com/withastro/astro/tree/HEAD/packages/integrations/sitemap) | `3.7.3` | `3.7.4` |
| [@eslint/eslintrc](https://github.com/eslint/eslintrc) | `3.3.6` | `3.3.7` |
| [@faker-js/faker](https://github.com/faker-js/faker) | `10.5.0` | `10.6.0` |
| [@swc/core](https://github.com/swc-project/swc/tree/HEAD/packages/core) | `1.15.47` | `1.16.1` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.67.0` | `8.69.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.67.0` | `8.69.0` |
| [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils) | `8.67.0` | `8.69.0` |
| [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) | `30.4.2` | `30.5.1` |
| [jest-environment-jsdom](https://github.com/jestjs/jest/tree/HEAD/packages/jest-environment-jsdom) | `30.4.1` | `30.5.1` |
| [jest-environment-node](https://github.com/jestjs/jest/tree/HEAD/packages/jest-environment-node) | `30.4.1` | `30.5.1` |
| [jest-util](https://github.com/jestjs/jest/tree/HEAD/packages/jest-util) | `30.4.1` | `30.5.1` |
| [ng-mocks](https://github.com/help-me-mom/ng-mocks) | `14.17.1` | `14.17.3` |
| [sharp](https://github.com/lovell/sharp) | `0.35.3` | `0.35.4` |
| [tsx](https://github.com/privatenumber/tsx) | `4.23.12` | `4.23.13` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.67.0` | `8.69.0` |
| [zod](https://github.com/colinhacks/zod) | `4.3.6` | `4.5.4` |



Updates `axios` from 1.19.0 to 1.20.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.19.0...v1.20.0)

Updates `hls.js` from 1.7.0 to 1.7.1
- [Release notes](https://github.com/video-dev/hls.js/releases)
- [Changelog](https://github.com/video-dev/hls.js/blob/master/docs/release-process.md)
- [Commits](https://github.com/video-dev/hls.js/compare/v1.7.0...v1.7.1)

Updates `marked` from 18.0.9 to 18.0.11
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](https://github.com/markedjs/marked/compare/v18.0.9...v18.0.11)

Updates `@astrojs/sitemap` from 3.7.3 to 3.7.4
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/main/packages/integrations/sitemap/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/@astrojs/sitemap@3.7.4/packages/integrations/sitemap)

Updates `@eslint/eslintrc` from 3.3.6 to 3.3.7
- [Release notes](https://github.com/eslint/eslintrc/releases)
- [Changelog](https://github.com/eslint/eslintrc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslintrc/compare/eslintrc-v3.3.6...eslintrc-v3.3.7)

Updates `@faker-js/faker` from 10.5.0 to 10.6.0
- [Release notes](https://github.com/faker-js/faker/releases)
- [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md)
- [Commits](https://github.com/faker-js/faker/compare/v10.5.0...v10.6.0)

Updates `@swc/core` from 1.15.47 to 1.16.1
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/commits/v1.16.1/packages/core)

Updates `@typescript-eslint/eslint-plugin` from 8.67.0 to 8.69.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.67.0 to 8.69.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/parser)

Updates `@typescript-eslint/utils` from 8.67.0 to 8.69.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/utils)

Updates `jest` from 30.4.2 to 30.5.1
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.5.1/packages/jest)

Updates `jest-environment-jsdom` from 30.4.1 to 30.5.1
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.5.1/packages/jest-environment-jsdom)

Updates `jest-environment-node` from 30.4.1 to 30.5.1
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.5.1/packages/jest-environment-node)

Updates `jest-util` from 30.4.1 to 30.5.1
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.5.1/packages/jest-util)

Updates `ng-mocks` from 14.17.1 to 14.17.3
- [Release notes](https://github.com/help-me-mom/ng-mocks/releases)
- [Changelog](https://github.com/help-me-mom/ng-mocks/blob/main/CHANGELOG.md)
- [Commits](https://github.com/help-me-mom/ng-mocks/compare/v14.17.1...v14.17.3)

Updates `sharp` from 0.35.3 to 0.35.4
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.35.3...v0.35.4)

Updates `tsx` from 4.23.12 to 4.23.13
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.12...v4.23.13)

Updates `typescript-eslint` from 8.67.0 to 8.69.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/typescript-eslint)

Updates `zod` from 4.3.6 to 4.5.4
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.3.6...v4.5.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: hls.js
  dependency-version: 1.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: marked
  dependency-version: 18.0.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@astrojs/sitemap"
  dependency-version: 3.7.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@eslint/eslintrc"
  dependency-version: 3.3.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@faker-js/faker"
  dependency-version: 10.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@swc/core"
  dependency-version: 1.16.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.69.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.69.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/utils"
  dependency-version: 8.69.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: jest
  dependency-version: 30.5.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: jest-environment-jsdom
  dependency-version: 30.5.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: jest-environment-node
  dependency-version: 30.5.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: jest-util
  dependency-version: 30.5.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: ng-mocks
  dependency-version: 14.17.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: sharp
  dependency-version: 0.35.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: tsx
  dependency-version: 4.23.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: typescript-eslint
  dependency-version: 8.69.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: zod
  dependency-version: 4.5.4
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-05 10:23:42 +02:00
4grayandClaude Opus 5 302afb237a chore(deps): close transitive CVE alerts via pnpm overrides (#1527)
Four open Dependabot alerts, all on transitive npm dependencies, so no
direct dependency changes:

- browserslist 4.28.1 -> 4.28.8 (GHSA-73wf-gq98-2v4g, high)
- @xmldom/xmldom 0.8.13 -> 0.8.15 (GHSA-6gmq-8vp8-gcm6)
- @humanfs/node 0.16.7 -> 0.16.8 (GHSA-p498-v437-472g)
- postcss-selector-parser 6.1.2 -> 6.1.4 (GHSA-w9m9-85wc-3x92)

@xmldom/xmldom already had an override, but its pinned target 0.8.13 had
itself fallen into the widened advisory range (<= 0.8.14), so that entry
is bumped rather than added.

browserslist is pinned to 4.28.8 rather than the advisory's 4.28.7 because
4.28.8 was already resolved elsewhere in the tree; collapsing onto it takes
browserslist from three copies to one and drops the duplicate
caniuse-lite/electron-to-chromium/update-browserslist-db trees with it, so
the lockfile is a net reduction. postcss-selector-parser 6.0.10 is left
alone: it sits below the advisory's >= 6.1.0 lower bound.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 19:59:51 +02:00
4grayandClaude Fable 5.1 52b33fe5a3 fix(release): pass the keychain password to set-key-partition-list on macOS
`Build on macos arm64` started failing on master with

    security set-key-partition-list -S apple-tool:,apple: -s -k *** <tmp>.keychain
    SecKeychainUnlock: The user name or passphrase you entered is not correct.

Every failure ran the `macos-26-arm64` runner image 20260831.0337; the same
job on image 20260728.0273 kept passing, as did `macos-15-intel`. The image
exposed an electron-builder bug: `app-builder-lib` hands the certificate's
`.p12` import password to `set-key-partition-list -k`, which authenticates
against the temporary keychain and therefore needs the keychain's own
generated password. Older macOS builds accepted the wrong password once the
keychain was unlocked; the new one verifies it.

Upstream fixed this in electron-userland/electron-builder#10101 (master,
v27 alpha) and backported it in #10172 to release/v26 on 2026-09-03, but no
26.x containing it is published (26.16.0 predates the backport, #10167).
Apply the backport to the installed 26.15.7 as a pnpm patch, guarded by
`tools/dependencies/app-builder-lib-keychain-password.test.mjs`: it checks
that the patched version is the installed one, that the compiled source
passes `keychainPassword`, and — with `security` recorded — that
`createKeychain` unlocks the partition list with the password it gave
`create-keychain`, not the import password. The test fails 2/3 on the
unpatched code. CI runs it next to the Vite patch guard; CLAUDE.md and
AGENTS.md document when to retire the patch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 17:07:27 +02:00
4gray f04f67728e ci(embedded-mpv): keep Windows runtime pin available (#1495) 2026-08-29 21:24:06 +02:00
4gray 29ca94aa43 feat(release): announcement formats, highlight cards, and draft verification (#1480) 2026-08-29 10:16:07 +02:00
4gray d6a9c23148 chore(deps): coordinated security sweep for open Dependabot alerts (#1475) 2026-08-23 13:56:00 +02:00
242640e8c6 chore(deps): bump ngx-indexed-db from 21.0.0 to 22.0.0 (#1472)
Coordinated replacement for the Dependabot branch: the bot updated only the
root package.json, leaving the ^21 specifier in libs/shared/interfaces, which
failed the @nx/dependency-checks lint rule.

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 02:26:02 +02:00
dependabot[bot] 96e235cd90 chore(deps-dev): bump @angular/cli from 21.2.19 to 21.2.21 (#1462)
Bumps [@angular/cli](https://github.com/angular/angular-cli) from 21.2.19 to 21.2.21.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v21.2.19...v21.2.21)

---
updated-dependencies:
- dependency-name: "@angular/cli"
  dependency-version: 21.2.21
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-23 01:56:31 +02:00
dependabot[bot] c1ad4672c7 chore(deps-dev): bump @angular-devkit/schematics from 21.2.19 to 21.2.21 (#1460)
Bumps [@angular-devkit/schematics](https://github.com/angular/angular-cli) from 21.2.19 to 21.2.21.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v21.2.19...v21.2.21)

---
updated-dependencies:
- dependency-name: "@angular-devkit/schematics"
  dependency-version: 21.2.21
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-23 01:15:43 +02:00
dependabot[bot] ca301d5399 chore(deps): bump the npm-minor-patch group with 8 updates (#1459)
Bumps the npm-minor-patch group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [hls.js](https://github.com/video-dev/hls.js) | `1.6.17` | `1.7.0` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.66.0` | `8.67.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.66.0` | `8.67.0` |
| [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils) | `8.66.0` | `8.67.0` |
| [esbuild](https://github.com/evanw/esbuild) | `0.28.1` | `0.28.2` |
| [ng-mocks](https://github.com/help-me-mom/ng-mocks) | `14.16.1` | `14.17.1` |
| [tsx](https://github.com/privatenumber/tsx) | `4.23.11` | `4.23.12` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.66.0` | `8.67.0` |


Updates `hls.js` from 1.6.17 to 1.7.0
- [Release notes](https://github.com/video-dev/hls.js/releases)
- [Changelog](https://github.com/video-dev/hls.js/blob/master/docs/release-process.md)
- [Commits](https://github.com/video-dev/hls.js/compare/v1.6.17...v1.7.0)

Updates `@typescript-eslint/eslint-plugin` from 8.66.0 to 8.67.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.67.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.66.0 to 8.67.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.67.0/packages/parser)

Updates `@typescript-eslint/utils` from 8.66.0 to 8.67.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.67.0/packages/utils)

Updates `esbuild` from 0.28.1 to 0.28.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.28.1...v0.28.2)

Updates `ng-mocks` from 14.16.1 to 14.17.1
- [Release notes](https://github.com/help-me-mom/ng-mocks/releases)
- [Changelog](https://github.com/help-me-mom/ng-mocks/blob/main/CHANGELOG.md)
- [Commits](https://github.com/help-me-mom/ng-mocks/compare/v14.16.1...v14.17.1)

Updates `tsx` from 4.23.11 to 4.23.12
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.11...v4.23.12)

Updates `typescript-eslint` from 8.66.0 to 8.67.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.67.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: hls.js
  dependency-version: 1.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.67.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.67.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/utils"
  dependency-version: 8.67.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: esbuild
  dependency-version: 0.28.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: ng-mocks
  dependency-version: 14.17.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: tsx
  dependency-version: 4.23.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: typescript-eslint
  dependency-version: 8.67.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-22 23:28:57 +02:00
dependabot[bot]and4gray 6041233f41 chore(deps-dev): bump electron from 41.10.3 to 43.3.0 (#1414)
* chore(deps-dev): bump electron from 41.10.3 to 43.3.0

Bumps [electron](https://github.com/electron/electron) from 41.10.3 to 43.3.0.
- [Release notes](https://github.com/electron/electron/releases)
- [Commits](https://github.com/electron/electron/compare/v41.10.3...v43.3.0)

---
updated-dependencies:
- dependency-name: electron
  dependency-version: 43.3.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(deps): prepare Electron 43 runtime policy

---------

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>
2026-08-11 12:38:11 +02:00
dependabot[bot]and4gray 1e038657d6 chore(deps): bump better-sqlite3 from 12.9.0 to 13.0.3 (#1415)
* chore(deps): bump better-sqlite3 from 12.9.0 to 13.0.3

Bumps [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) from 12.9.0 to 13.0.3.
- [Release notes](https://github.com/WiseLibs/better-sqlite3/releases)
- [Commits](https://github.com/WiseLibs/better-sqlite3/compare/v12.9.0...v13.0.3)

---
updated-dependencies:
- dependency-name: better-sqlite3
  dependency-version: 13.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(deps): use better-sqlite3 prebuilt binaries

* docs(deps): note SQLite worker stability fix

---------

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>
2026-08-11 12:05:40 +02:00
dependabot[bot] 5c9411869a chore(deps): bump the npm-minor-patch group across 1 directory with 11 updates (#1416)
Bumps the npm-minor-patch group with 11 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [hls.js](https://github.com/video-dev/hls.js) | `1.6.16` | `1.6.17` |
| [marked](https://github.com/markedjs/marked) | `18.0.7` | `18.0.9` |
| [video.js](https://github.com/videojs/video.js) | `8.23.9` | `8.24.0` |
| [@playwright/test](https://github.com/microsoft/playwright) | `1.62.0` | `1.62.1` |
| [@swc/core](https://github.com/swc-project/swc/tree/HEAD/packages/core) | `1.15.46` | `1.15.47` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.65.0` | `8.66.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.65.0` | `8.66.0` |
| [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils) | `8.65.0` | `8.66.0` |
| [ng-mocks](https://github.com/help-me-mom/ng-mocks) | `14.15.3` | `14.16.1` |
| [tsx](https://github.com/privatenumber/tsx) | `4.23.1` | `4.23.11` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.65.0` | `8.66.0` |



Updates `hls.js` from 1.6.16 to 1.6.17
- [Release notes](https://github.com/video-dev/hls.js/releases)
- [Changelog](https://github.com/video-dev/hls.js/blob/master/docs/release-process.md)
- [Commits](https://github.com/video-dev/hls.js/compare/v1.6.16...v1.6.17)

Updates `marked` from 18.0.7 to 18.0.9
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](https://github.com/markedjs/marked/compare/v18.0.7...v18.0.9)

Updates `video.js` from 8.23.9 to 8.24.0
- [Release notes](https://github.com/videojs/video.js/releases)
- [Changelog](https://github.com/videojs/video.js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/videojs/video.js/compare/v8.23.9...v8.24.0)

Updates `@playwright/test` from 1.62.0 to 1.62.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.62.0...v1.62.1)

Updates `@swc/core` from 1.15.46 to 1.15.47
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/commits/v1.15.47/packages/core)

Updates `@typescript-eslint/eslint-plugin` from 8.65.0 to 8.66.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.65.0 to 8.66.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/parser)

Updates `@typescript-eslint/utils` from 8.65.0 to 8.66.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/utils)

Updates `ng-mocks` from 14.15.3 to 14.16.1
- [Release notes](https://github.com/help-me-mom/ng-mocks/releases)
- [Changelog](https://github.com/help-me-mom/ng-mocks/blob/main/CHANGELOG.md)
- [Commits](https://github.com/help-me-mom/ng-mocks/compare/v14.15.3...v14.16.1)

Updates `tsx` from 4.23.1 to 4.23.11
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.1...v4.23.11)

Updates `typescript-eslint` from 8.65.0 to 8.66.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.62.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@swc/core"
  dependency-version: 1.15.47
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@typescript-eslint/utils"
  dependency-version: 8.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: hls.js
  dependency-version: 1.6.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: marked
  dependency-version: 18.0.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: ng-mocks
  dependency-version: 14.16.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: tsx
  dependency-version: 4.23.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: typescript-eslint
  dependency-version: 8.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: video.js
  dependency-version: 8.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-11 11:28:33 +02:00
4gray 10e8187b16 chore(deps): update epg-parser to 0.5.0 (#1413) 2026-08-11 03:29:05 +02:00
4gray de77c6d467 fix(playback): update mpegts.js to 1.8.1 (#1412) 2026-08-11 03:15:08 +02:00
4gray 77842b9d04 fix(playback): update Shaka Player to 5.2.4 (#1411) 2026-08-11 03:14:03 +02:00
4gray 728df1a68c fix(packaging): restore Snap desktop runtime (#1406)
* fix(packaging): restore Snap desktop runtime

* docs(packaging): publish Snap launch repair note

* fix(packaging): declare Node 22.12 floor

* docs(architecture): update SQLite pin rationale

* fix(tooling): align Node engine floor

* fix(tooling): constrain supported Node releases

* docs(architecture): correct node-abi consumer
2026-08-11 02:08:30 +02:00
dependabot[bot] 2a7d7c315e chore(deps-dev): bump the nx-version-updates group across 1 directory with 11 updates (#1401)
Bumps the nx-version-updates group with 11 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@nx/angular](https://github.com/nrwl/nx/tree/HEAD/packages/angular) | `22.7.7` | `22.7.8` |
| [@nx/devkit](https://github.com/nrwl/nx/tree/HEAD/packages/devkit) | `22.7.7` | `22.7.8` |
| [@nx/esbuild](https://github.com/nrwl/nx/tree/HEAD/packages/esbuild) | `22.7.7` | `22.7.8` |
| [@nx/eslint](https://github.com/nrwl/nx/tree/HEAD/packages/eslint) | `22.7.7` | `22.7.8` |
| [@nx/eslint-plugin](https://github.com/nrwl/nx/tree/HEAD/packages/eslint-plugin) | `22.7.7` | `22.7.8` |
| [@nx/jest](https://github.com/nrwl/nx/tree/HEAD/packages/jest) | `22.7.7` | `22.7.8` |
| [@nx/js](https://github.com/nrwl/nx/tree/HEAD/packages/js) | `22.7.7` | `22.7.8` |
| [@nx/playwright](https://github.com/nrwl/nx/tree/HEAD/packages/playwright) | `22.7.7` | `22.7.8` |
| [@nx/web](https://github.com/nrwl/nx/tree/HEAD/packages/web) | `22.7.7` | `22.7.8` |
| [@nx/workspace](https://github.com/nrwl/nx/tree/HEAD/packages/workspace) | `22.7.7` | `22.7.8` |
| [nx](https://github.com/nrwl/nx/tree/HEAD/packages/nx) | `22.7.7` | `22.7.8` |



Updates `@nx/angular` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/angular)

Updates `@nx/devkit` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/devkit)

Updates `@nx/esbuild` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/esbuild)

Updates `@nx/eslint` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/eslint)

Updates `@nx/eslint-plugin` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/eslint-plugin)

Updates `@nx/jest` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/jest)

Updates `@nx/js` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/js)

Updates `@nx/playwright` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/playwright)

Updates `@nx/web` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/web)

Updates `@nx/workspace` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/workspace)

Updates `nx` from 22.7.7 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/nx)

---
updated-dependencies:
- dependency-name: "@nx/angular"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/devkit"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/esbuild"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/eslint"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/eslint-plugin"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/jest"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/js"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/playwright"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/web"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: "@nx/workspace"
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
- dependency-name: nx
  dependency-version: 22.7.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nx-version-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 23:05:20 +02:00
4gray a9e07d696f chore(deps): align Nx packages on 22.7.7 (#1396) 2026-08-10 08:19:43 +02:00
4gray 87dc45957b chore(deps): align Angular packages on 21.2.19 (#1383)
* chore(deps): align Angular packages on 21.2.19

* docs(deps): align Vite patch references
2026-08-09 09:49:38 +02:00
dependabot[bot] fd29362d44 chore(deps-dev): bump electron from 41.7.2 to 41.10.3 (#1377)
Bumps [electron](https://github.com/electron/electron) from 41.7.2 to 41.10.3.
- [Release notes](https://github.com/electron/electron/releases)
- [Commits](https://github.com/electron/electron/compare/v41.7.2...v41.10.3)

---
updated-dependencies:
- dependency-name: electron
  dependency-version: 41.10.3
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-08 08:47:26 +02:00
4gray d9a763e77d fix(build): prevent Vite dev transform overflow (#1379)
* fix(build): prevent Vite dev transform overflow

* fix(build): preserve commented Vite URL imports
2026-08-08 08:03:09 +02:00
dependabot[bot] 53c318bac7 chore(deps): bump axios from 1.18.1 to 1.19.0 (#1367)
Bumps [axios](https://github.com/axios/axios) from 1.18.1 to 1.19.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.18.1...v1.19.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.19.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 20:43:34 +02:00
dependabot[bot] d8e3eb9219 chore(deps-dev): bump angular-eslint from 21.3.1 to 21.4.0 (#1350)
Bumps [angular-eslint](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/angular-eslint) from 21.3.1 to 21.4.0.
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/angular-eslint/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v21.4.0/packages/angular-eslint)

---
updated-dependencies:
- dependency-name: angular-eslint
  dependency-version: 21.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 20:42:29 +02:00
4gray 7111942509 chore(deps): update Nx to 22.7.2 (#1365)
* chore(deps): update Nx to 22.7.2

* fix(deps): keep Nx major updates manual
2026-08-04 16:07:45 +02:00
dependabot[bot] d44948f2fa chore(deps): bump angularx-qrcode from 21.0.4 to 21.0.5 (#1351)
Bumps [angularx-qrcode](https://github.com/Cordobo/angularx-qrcode) from 21.0.4 to 21.0.5.
- [Release notes](https://github.com/Cordobo/angularx-qrcode/releases)
- [Commits](https://github.com/Cordobo/angularx-qrcode/compare/v21.0.4...v21.0.5)

---
updated-dependencies:
- dependency-name: angularx-qrcode
  dependency-version: 21.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-03 16:50:48 +02:00
4grayandClaude Opus 5 c741815b97 fix(build): include shared UI stylesheets in Nx cache inputs (#1360)
* fix(build): include shared UI stylesheets in Nx cache inputs

`libs/ui/styles` held shared SCSS partials but had no `project.json`, so its
files belonged to no Nx project and were absent from every task hash. Editing
a partial and running `pnpm nx build web` reported 4 of 4 tasks cached and
shipped the previous CSS — a silent wrong build rather than a failure.

Nx derives its project graph from TypeScript imports only, so a relative Sass
`@use` that crosses a project root creates no edge. Verified directly: after
adding the project but before declaring anything, `ui-styles` still had zero
dependents in the graph.

Make it the `ui-styles` project (no targets — it exists to be hashed) and
declare `implicitDependencies` on the 8 consumers. Chosen over adding the path
to `sharedGlobals`, which would put shared styles into every project's hash and
make a one-line SCSS tweak mark the whole workspace affected. A styles edit now
marks 15 projects affected and leaves electron-backend, website, the mock
servers and the shared libs alone.

`libs/ui/styles` was the only projectless directory holding files under `libs/`
or `apps/`.

Add `pnpm run styles:inputs:validate` to keep it closed: it resolves every
relative stylesheet import against Nx's real project graph and fails when one
escapes the input closure of a build that compiles it, naming the project to
declare. It exits 1 with 21 diagnostics on the pre-fix tree. Imports of
`apps/web/src/nav-list.scss` are deliberately accepted — `web` already hashes
that file, and a lib -> app edge would make the graph cyclic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(build): spawn git without a shell in the stylesheet check

`execSync("git ls-files '*.scss'")` runs through `cmd.exe` on Windows, where
single quotes are literal characters rather than quoting. Git received the
pathspec with the quotes intact, matched nothing and exited 0, so
`styles:inputs:validate` reported success after checking zero stylesheets —
silently disabling the check for Windows developers while staying green.

Spawn with `execFileSync` so no shell is involved and git expands its own
pathspec; verified to return the identical 133 files.

Both this and the eslint glob trap next to it in the docs report success while
covering nothing, so also make an empty scan fail rather than pass: the
workspace always contains SCSS, and a listing that returns none means the scan
broke.

Reported by Codex review on #1360.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(styles): move nav-list partial into ui-styles (#1361)

* fix(build): count every target of a comma-separated Sass @import

`@import` is the only rule that takes a list, and the scan read just its
first target. A later entry crossing an Nx project boundary escaped the
cache key while the check still reported success — the same silent-pass
failure the tool exists to prevent.

Parse every target of an `@import` list. The obvious "read all quoted
strings" fix trades one silent gap for a phantom one, so the rule decides:
`@use`/`@forward` load exactly one module and a quoted string after it is
`with (...)` configuration, and `url(...)` stays a plain CSS import the
browser resolves at runtime. Neither is a module Sass compiles.

The workspace has no relative `@import` at all today, so the scan still
finds the same 42 imports across 133 files; this closes the gap before
someone writes one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 23:18:37 +02:00
4gray 011f322807 ci(nx): enforce synchronized dependency updates (#1343)
* ci(nx): enforce lockstep dependency versions

* ci(deps): group Nx updates explicitly

* docs(nx): document coordinated dependency updates

* fix(nx): validate peer dependency versions

* fix(nx): validate duplicate root declarations
2026-08-02 12:52:37 +02:00
4gray 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
2026-07-31 08:00:59 +02:00
4gray e55d55b47f feat(mock-data): add shared screenshot-safe poster catalog (#1271)
Moves the fictional movie catalog into `libs/shared/marketing-fixtures` so the
Xtream and Stalker mocks describe the same titles, and adds 20 rendered posters
plus the shared fixture types behind them.

Supporting changes made while getting it green:

- `shared-marketing-fixtures` is classified Tier B in the coverage policy. Not
  Tier A: it is fictional fixture data, so a statement percentage over it means
  nothing, and a Tier A entry would pull it into the merged coverage map and the
  ratchet. Tier B still runs its spec in CI. `stalker-mock-server` needs no entry
  of its own — it is already Tier C and the Tier B/C runner falls back to
  `pnpm nx test <project>`, so its new `marketing-poster-url.spec.ts` runs.
- Two release-capture defects the catalog reorder introduced, both fixed in
  `tools/release/capture-app-driver.ts`:
  - VOD stream ids are `MARKETING_VOD_STREAM_ID_BASE + index` and the generator
    now lists the showcase movies first, so 62000-62002 became Black Harbor, The
    Paper Astronaut and Summer Static while the dashboard seeding still mapped
    those ids to the previous titles' backdrops.
  - the raw `tsx` spawn of the Xtream mock lacked `--tsconfig
    tsconfig.base.json`, so the mock could not resolve
    `@iptvnator/shared/marketing-fixtures` and the capture never started. Both
    mock projects' own serve targets already passed the flag.
2026-07-27 08:10:57 +02:00
4gray 08b868d6c1 test(electron): harden runtime boundary coverage (#1267)
Adds contract-focused regression coverage for the Electron HTTP server,
remote-control events, settings events, and managed download paths, and makes
Tier A coverage fail closed when instrumentation fails or a runtime-owning
production file disappears from a project or from the merged Istanbul report.

The old `coverage:ci` exited 0 despite a `Failed to collect coverage`
diagnostic: libs/m3u-state/src/lib/effects.ts was simply absent from the merged
map. All 30 Tier A reports are now required, the merged map covers 710 files,
and effects.ts is reported as 0/159 instead of silently disappearing.

Also fixes remote static-file path containment for encoded, malformed, NUL,
POSIX and Win32-style traversal inputs, with behavior-preserving testability
seams.

Statements 69.27% -> 69.54%; http-server.ts 0% -> 90.21%,
remote-control.events.ts 0% -> 96.55%, settings.events.ts 59.25% -> 96.29%.
2026-07-26 22:27:50 +02:00
4gray 1ab82b04a1 refactor(deps): drop uuid for a shared crypto-based id helper (#1266)
Supersedes #1252 and #872. uuid 14 is ESM-only, apps/web/jest.config.ts only
kept v9 working by mapping `^uuid$` at a `wrapper.mjs` that v14 no longer
ships, and the specifier also has to be synced in
libs/shared/m3u-utils/package.json or @nx/dependency-checks fails lint.

All four call sites only used `v4()`, so the dependency goes away instead.
`createRandomId()` prefers `crypto.randomUUID()` and falls back to building the
same v4 shape from `crypto.getRandomValues()` — that fallback is load-bearing,
because randomUUID is only exposed in secure contexts and the self-hosted PWA
is regularly served over plain http on a LAN address. getRandomValues stays
available there, and it is what uuid's own v4 used.

`@types/uuid` goes too; it only existed for the untyped v9 package.
2026-07-26 22:27:31 +02:00
4gray d5f5beab38 chore(deps): bump the npm minor/patch group across 43 packages (#1270)
Rebuilt from #1251 so the group could merge, on top of the transitive-CVE
overrides from #1258. Supersedes #1230 and #1251.

Carries axios 1.16.0 -> 1.18.1, closing seven runtime-scope advisories
including the proxy-credential leak on redirects, and sharp 0.34.5 -> 0.35.3
for the libvips CVEs.

`esModuleInterop` moves to tsconfig.base.json. artplayer 5.4.0 switched from a
Parcel build exposing `module.exports.default` to UMD assigning
`module.exports` directly; the flag was only set in apps/web, so every lib
compiled `import Artplayer from 'artplayer'` to `.default` and got undefined.
Production was never affected — esbuild resolves the ESM entry.

Two packages are deliberately held back, each for its own PR:

- epg-parser ^0.5.0 — grouped as a minor, but 0.x minors are breaking and this
  one reshapes the parse output (`channel.name` -> `displayName`, icons/urls
  become objects, `credits` becomes role-keyed, dates switch to ISO). Its only
  consumer is the uncovered web-backend `/parse-xml` endpoint.
- electron-builder ^26.15.3 — rewrote the snap target, and the resulting snap
  cannot start (`command.sh` execs a `desktop-init.sh` that never lands at the
  snap root under our core22 strict config). Its two required fixes go with it:
  the `engines` node floor from @electron/rebuild 4, and resolving upstream
  node-gyp instead of the dropped `@electron/node-gyp` fork.
2026-07-26 19:43:35 +02:00
4grayandClaude Opus 5 e91a7cde7a fix(deps): patch transitive runtime CVEs via pnpm overrides (#1258)
Closes 13 runtime-scope Dependabot advisories that Dependabot cannot fix itself:
every vulnerable package here is transitive, so the bot has no lever until each
parent publishes a release widening its own pin.

Overrides added (pinned-source form, matching existing convention):

- @xmldom/xmldom 0.8.11 -> 0.8.13  (5 high) via video.js -> mpd-parser
- fast-uri       3.1.0  -> 3.1.4   (4 high) via electron-conf -> ajv
- js-yaml        4.1.1  -> 4.3.0   (2)      via electron-updater
- form-data      4.0.5  -> 4.0.6   (1 high) via axios
- ajv            8.17.1 -> 8.18.0  (1)      via electron-conf

Every target stays inside its parent's declared semver range. For xmldom,
fast-uri and js-yaml the newest published version is outside that range
(0.9.x / 4.x / 5.x), so "latest" would have broken them; the new doc
records that constraint.

Deliberately excluded: axios and uuid are direct deps already covered by open
Dependabot PRs (#1251, #1252). undici is labelled runtime scope but every path
to it is build tooling (electron -> @electron/get, @angular/build,
@module-federation/dts-plugin) and it is not in the packaged app.

Reachability: xmldom arrives via video.js -> VHS -> mpd-parser, but the app
routes every .mpd to Shaka, which uses its own DASH parser, so that one is
defence in depth. The genuinely reachable one is js-yaml, which
electron-updater uses to parse latest.yml from releases.

Adds docs/architecture/dependency-security-overrides.md and a .changes note.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 02:21:11 +02:00
4grayandClaude Opus 5 b4ec68c1fa feat(release): manifest-driven screenshot capture with fail-closed mock-data guards (#1261)
Third slice of the release-notes pipeline (#1256 format+generator, #1257 CI
gate): release screenshots become reproducible and provably mock-only.

The v0.20 capture script was single-use (hard-coded slugs, paths, hero) and
fail-open: a lost IPTVNATOR_E2E_DATA_DIR silently fell back to the user's
real ~/.iptvnator database, `...process.env` leaked ambient TMDB keys and
proxies, nothing gated network access, and no frame content was ever
validated. Each hole leaks real playlists, credentials, or copyrighted
artwork into published screenshots without a single signal.

New pipeline:

- tools/release/screenshots.manifest.json — declarative shots (slug, title,
  named setup steps, themes). Adding a feature shot = one manifest entry.
- capture-release-screenshots.ts — orchestrator; output goes to
  apps/website/public/blog/<release>/screenshots/<slug>-<theme>.png, release
  slug derived from package.json (or --release), --only/--theme filters.
- capture-app-driver.ts / capture-navigation.ts — launch, seeding, theme,
  and the named-action vocabulary; actions are order-independent (every
  portal action starts from the dashboard).
- screenshot-guards.mjs — the fail-closed policy, pure and unit-tested:
  G1 the real database is snapshotted (sha256+mtime) before launch and must
     be byte-identical after; the isolated DB must actually exist
  G2 the app receives an allowlisted environment, never ...process.env
  G3 deny-by-default network gate; known app-level calls (GitHub update
     check) are answered by local stubs; any other blocked request fails
     the run — a silently-blocked TMDB call would leave a frame that looks
     broken rather than unsafe
  G4 every frame is scanned before capture: external img/background URLs,
     credential-shaped text, MAC addresses, non-localhost m3u8 references
  G5 TMDB enrichment asserted disabled via the renderer's IndexedDB
  Any violation deletes every frame captured in the run and exits non-zero.

The guards paid for themselves on the first live run: G3 caught the mock
server redirecting stream endpoints to a public demo HLS
(test-streams.mux.dev) — meaning earlier hand-run captures could embed
third-party video frames. The M3U shot now deliberately captures the groups
layout without starting playback.

`.changes` validation now cross-checks `screenshot:` slugs against the
manifest, so a note cannot reference an image the capture run never
produces.

Verified end-to-end: 10/10 shots (5 slugs × dark/light) captured against
dist build + xtream-mock-server, frames visually inspected (fictional
titles/artwork only), guard-violation paths exercised live. 67 unit tests
in release-tools, lint green, script files within the repo size limit.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 00:19:02 +02:00
4grayandClaude Opus 5 270350c2e1 chore(release): author release notes in .changes instead of reconstructing them (#1256)
* chore(release): author release notes in .changes instead of reconstructing them

CHANGELOG.md has been frozen at 0.12.0 since 2023 while the app shipped
0.23.0, semantic-release sat in devDependencies with no config, and the real
user-facing notes were a 280-line MDX post written from memory at release
time. The gap was never version math — it was authored notes captured while
the context is still fresh.

Add a `.changes/*.md` note format (type, area, issues, screenshot; no version
field, since the release version is chosen deliberately) plus a generator that
composes the GitHub release body, the CHANGELOG.md section and a blog-post
scaffold from the accumulated notes.

Changesets was considered and rejected: it versions multiple published
packages, and this repo has exactly one private package. Its `version` step
would also rewrite CHANGELOG.md into a flatter format than the blog post and
fight the deliberate, updater-constrained version choice.

- hand-rolled frontmatter parser over a YAML engine: the schema is closed, so
  it can reject unknown keys, which is what catches typos
- PR numbers are resolved from the commit that added the note, never written
  by the author
- MDX-significant characters in note bodies are escaped so a stray `<` cannot
  break the website build
- blog scaffold ships `draft: true` with explicit TODO headings; the prose is
  editorial work, only the inventory is mechanical
- revive CHANGELOG.md with an honest pointer for 0.13.0-0.23.0 rather than
  fabricating the missing history
- drop the five unused semantic-release/conventional-changelog packages

Docs: `.changes/README.md`, plus a "Release Notes For User-Visible Changes"
section mirrored in CLAUDE.md and AGENTS.md, and a PR template checkbox for
contributors who never read either.

Tests: 26 unit tests in tools/release/release-notes.test.mjs covering parsing,
validation, grouping and all three renderers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(release): default the notes version to package.json and harden alt escaping

Review follow-ups on the release-notes generator.

- `--version` now defaults to the root package.json version, so the
  `release🎶*` package scripts run bare instead of failing on a missing
  argument. Bumping package.json is the deliberate act that starts a release,
  which makes it the right single source of truth; `--version` remains as an
  override for dry runs before the bump. The notice goes to stderr so
  `--format github` keeps a pipeable stdout.
- Escape backslashes before apostrophes when building the MDX `alt` string
  literal. A note body ending in a backslash previously produced an
  unterminated string and would have broken the website build.
- Document that release posts are one per minor version, in the slug helper,
  the overwrite error, and `.changes/README.md` — a patch release edits the
  existing post rather than creating a second one.

Tests: +1 regression test for the alt escaping, verified to fail without the
fix (27 total, all passing).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(ci): put authored notes into the tag release body, fail-closed

Wires the .changes pipeline into the release workflow (Codex review P1 on
#1256). Calling the generator from the tag build cannot work — --consume
deletes .changes/ before the tag exists — so the tag build reads what the
generator already wrote: release-meta now fills BODY from the CHANGELOG.md
section matching the tag's version via tools/release/extract-changelog-section.mjs.

generate_release_notes stays on, so GitHub's commit list renders below the
authored notes; the existing draft-metadata repair step already concatenates
RELEASE_BODY with the generated notes, so the rare duplicate-draft path keeps
the same layering unchanged.

The extractor exits non-zero when the section is missing or empty, failing
the release instead of silently shipping PR-title-only notes. A hotfix tag
cut without running release:notes:changelog therefore fails at create-release
by design; the error message names the exact commands to run.

Tests: 5 new extractor tests (32 total in release-tools, all passing);
packaging suite (247) re-run green since build-and-make.yaml is one of its
inputs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(release): escape all regex metacharacters in the changelog extractor

CodeQL flagged the version-to-RegExp interpolation in
extract-changelog-section.mjs (regex injection + incomplete escaping): only
dots were escaped, and while the CLI validates its argument as bare semver
before calling, the exported extractSection() carries no such guarantee on
its own. Escape the full metacharacter set so no caller can inject pattern
syntax, with tests covering wildcard dots, alternation, `.*` and backslashes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(release): make changelog generation idempotent per version

Codex review P2 on #1256: rerunning `release:notes:changelog` for the same
version — the normal move after correcting a note before --consume —
prepended a second section instead of replacing the first, leaving duplicate
release entries.

Extract the marker insertion into upsertChangelogSection(): it removes any
existing section for the version, then rebuilds around the marker rather than
string-replacing into it, so the blank-line count on both sides stays exact
on both the fresh-insert and replace paths. The CLI reports when a section
was replaced.

Tests: 4 new cases (insert, replace-not-duplicate, neighbours untouched,
missing marker); 37 total passing. End-to-end rerun verified: one heading,
latest date wins, extractor output unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 18:22:43 +02:00
4grayandClaude Fable 5 0ee73f2d0f feat(m3u): support #KODIPROP lines placed before #EXTINF (#1234)
* feat(m3u): support #KODIPROP lines placed before #EXTINF

Bumps the iptv-playlist-parser fork pin to v0.15.2-iptvnator.2: Kodi
property lines apply to the next list entry, so #KODIPROP lines placed
above the #EXTINF are now preserved in item.raw (previously the parser
dropped them and ClearKey config in that layout was lost). The DASH +
ClearKey feature (#1225) extracts license config from item.raw, so both
KODIPROP layouts now work on every import path.

Covered by a parser contract case and an extended web-backend /parse
regression (before-EXTINF + between-EXTINF-and-URL + plain channel).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: reflect before-#EXTINF KODIPROP support in the M3U architecture doc

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: list the KODIPROP delta in the parser-fork inventory

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 11:43:58 +02:00
4grayandClaude Fable 5 bd07e17857 feat(m3u): DASH + ClearKey playback via Shaka Player (#1225)
* feat(m3u): extract ClearKey DRM from #KODIPROP playlist lines

Adds the typed ChannelDrm model (shared interfaces) and a KODIPROP
post-processing step in createPlaylistObject() — the single funnel for all
four playlist import paths. Parses inputstream.adaptive.license_type,
license_key and drm_legacy; ClearKey keys accepted as kid:key hex pairs,
W3C ClearKey license JSON, or a plain kid→key JSON map. Unsupported license
types (Widevine/PlayReady/license URLs) are preserved with supported=false
so playback can surface a DRM diagnostic instead of failing silently.
Also adds isDashStreamUrl/isDashChannel helpers for DASH routing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(playback): add Shaka DASH source engine with ClearKey support

Introduces ShakaVideoSession (libs/ui/playback/src/lib/shaka-engine/): a
lazily imported shaka-player engine (separate lazy chunk, ~217 KB transfer)
owning attach/configure/load with an operation queue and generation guard
against channel-switch races. Channel ClearKey config maps to
drm.clearKeys; channels with an unsupported license type emit a
DrmOrEncryption diagnostic without starting an engine. Shaka errors are
classified into the existing playback diagnostics
(PlaybackDiagnosticSource.Shaka).

Wires the engine into both built-in players like hls.js/mpegts.js:
- HTML5: extension === 'mpd' branch in playChannel(); hls/mpegts/native
  glue extracted to helpers to keep the component within the size budget
- ArtPlayer: customType 'mpd' in ArtPlayerSourceSession (+ getDrm seam)
- Shared controls: WebVideoControlsSource kind 'shaka' +
  WebVideoShakaControls using the Shaka 5 text model (selectTextTrack(null)
  hides subtitles; Player.setTextTrackVisibility no longer exists)

Adds a CJS shaka-player jest stub (video.js precedent) for web specs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(m3u): route DASH channels to the inline Shaka-capable player

DASH (.mpd) channels always play in a built-in web engine (radio
precedent): external MPV/VLC cannot receive KODIPROP ClearKey
configuration (VLC upstream #29465) and Video.js has no DASH bridge yet.

- shouldShowInlinePlayer() bypasses the external-player setting for DASH
- new shouldAutoLaunchExternalPlayer() guard consolidates the MPV/VLC
  auto-launch conditions in the m3u-state effects (incl. catch-up path)
- the M3U page overrides the player for DASH channels: ArtPlayer stays
  ArtPlayer, everything else falls back to the HTML5 player
- ChannelDrm is passed through ResolvedPortalPlayback into the synthetic
  player-view channel

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(e2e): add offline DASH ClearKey fixtures and e2e coverage

Fixtures (apps/web-e2e/src/fixtures/dash/): ~4s VP9+Opus DASH, clear and
CENC-encrypted variants with fixed synthetic ClearKey credentials.
Content synthesized by ffmpeg; encryption done by Shaka Packager because
ffmpeg's mp4 muxer writes senc-only metadata (Chromium needs saiz/saio)
and cannot produce the subsample encryption the VP9 CENC binding
requires. Generation script + README document regeneration.

web-e2e (Chromium): import an M3U with KODIPROP ClearKey via raw text,
verify encrypted and clear DASH actually play (currentTime advances, no
diagnostic banner) and that an unsupported license type (Widevine)
surfaces the DRM diagnostic. Fixtures are served through Playwright route
interception with HTTP Range support; the Angular service worker is
blocked since SW-routed requests bypass interception.

electron-backend-e2e: the same happy path + negative against a local
Range-aware fixture server — the automated proof that ClearKey EME works
in the real Electron runtime (file:// secure context).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: document DASH + ClearKey playback architecture

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(pwa): extract KODIPROP DRM on the web-backend /parse import path

The web-backend keeps its own playlist builder for the PWA URL-import
path, so the shared createPlaylistObject() DRM hook never ran there and
encrypted DASH channels imported by URL reached Shaka without keys.
Apply extractDrmFromRaw() in that builder too and cover the path with a
regression test.

Addresses Codex review on PR #1225.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): interrupt stalled Shaka loads and destroy failed engines

Two review findings on the ShakaVideoSession lifecycle:

- stop()/start() now tear the current player down immediately instead of
  queueing the destroy behind the in-flight operation. Shaka's destroy()
  interrupts a pending load() (LOAD_INTERRUPTED), so a stalled manifest
  fetch can no longer wedge the operation chain and block the next
  channel start (Codex P1).
- A rejected attach()/load() now destroys the failed player after
  emitting the diagnostic, so a non-functional engine never stays
  attached to the media element or exposed to the shared-controls
  bridge (Greptile P1).

Regression tests cover both paths. The Shaka fakes are consolidated into
a shared jest-free test double that mirrors the destroy-interrupts-load
semantic, and the ArtPlayer source-session spec is split (fixtures +
DASH cases) to stay within the max-lines lint budget.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(m3u): unify DASH URL detection with playback extension normalization

isDashStreamUrl() used the simpler getStreamExtensionFromUrl(), so URLs
the player engines classify as DASH (stream.MPD, ?ext=mpd, ?format=mpd)
were not routed to the Shaka-capable inline player and lost their
ClearKey metadata with Video.js or external players configured
(Codex P2). The normalized getPlaybackMediaExtensionFromUrl() now lives
in @iptvnator/shared/m3u-utils (re-exported unchanged from the playback
lib) and both routing and engine selection share it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(lint): satisfy CI lint and CodeQL in DASH support files

- replace shell-built tar/npm commands with execFileSync arg arrays in
  the fixture generator (CodeQL: uncontrolled shell command)
- give jest stub methods explicit bodies (no-empty-function)
- compact the diagnostic label switches in WebPlayerViewComponent to
  stay under the max-lines budget

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): tear down the Shaka engine on critical error events too

A non-recoverable Shaka error emitted after a successful load left the
dead engine attached to the media element and exposed to the
shared-controls bridge (Greptile P1, round 2). Critical error events now
destroy the player right after the diagnostic is emitted, matching the
load-failure path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(m3u): honor DASH catch-up URLs and drop unusable DRM fallbacks

Two Codex round-2 findings:

- The inline-playback DASH gate only examined the channel URL, while the
  external-player guard checks the resolved catch-up URL — a replay that
  resolves to an .mpd manifest with MPV/VLC configured ended up with no
  player at all. The gate now uses the effective playback URL
  (activePlaybackUrl ?? channel.url).
- The unsupported-DRM diagnostic advertised MPV/VLC fallback actions,
  but external players cannot receive the KODIPROP license config either
  — the diagnostic no longer recommends them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): suppress unusable external fallback for ClearKey DRM failures

Runtime DRM errors on channels that carry KODIPROP ClearKey config (wrong
or rotated keys) advertised MPV/VLC fallback actions, but external
players never receive the license config — the fallback could only fail
differently. DRM-classified diagnostics from such channels no longer
recommend external players; clear channels keep the hint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(m3u): symmetric DASH inline gate and lazy DRM for pre-upgrade playlists

- The inline DASH gate is now true when either the channel or the
  resolved catch-up URL is DASH, mirroring the external-player guard —
  a .mpd channel whose catch-up resolves to .m3u8 no longer ends up
  with no player at all.
- Playlists imported before the DRM feature carry no drm field, but the
  raw KODIPROP block survived in the stored items; the M3U page now
  falls back to extractDrmFromRaw(channel.raw) at playback time, so
  encrypted channels work without a re-import (Channel gains raw?).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: sync the DASH/Shaka contract across agent docs

Mirrors the DASH/Shaka source-engine contract into AGENTS.md and adds
Shaka to the shared web-video bridge descriptions in CLAUDE.md and the
player-controls contract; documents the lazy raw-KODIPROP DRM fallback
for pre-upgrade playlists in the M3U architecture doc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): reset the media element for rejected DRM and widen ClearKey fallback suppression

- Switching from a playing stream to an unsupported-DRM DASH channel
  loads no new source, but play() still ran and the un-loaded element
  could resume the previous stream underneath the diagnostic banner.
  The HTML5 player now resets the element instead of playing.
- Any inline failure on a KODIPROP ClearKey channel (manifest, codec,
  media, network — not just DRM-category errors) is unsolvable in
  MPV/VLC, which never receive the license config; the external
  fallback hint is now suppressed for all diagnostics of such channels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): restore suppressed DASH captions when the preference re-enables

The Shaka bridge dropped the auto-selected text track with
selectTextTrack(null) when showCaptions was off, but did not remember it
— re-enabling the preference mid-session left captions permanently off
(HLS/native bridges already restore). The session now remembers the
suppressed track id and reselects it via the bridge's caption-state pass;
suppression is also skipped when no track is active. Covered by session
and new WebVideoShakaControls specs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: retrigger CI

GitHub Actions created no check suites for the last three pushes to this
branch (third-party apps received the webhooks); an empty commit re-fires
the push and pull_request events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(playback): split oversized Shaka session and HTML5 spec files

CI lint enforces max-lines 400: extract ShakaTextTrackSuppression and the
shaka-error helpers out of ShakaVideoSession, and move the DASH-specific
HTML5 player test into its own spec. No behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: allow manual dispatch of the cross-platform E2E workflow

GitHub stopped delivering push/pull_request events for this branch;
workflow_dispatch provides a manual escape hatch (CI and build-and-make
already have one).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 20:31:18 +02:00
genrichh93-ui 188f5c4b56 feat(downloads): pause and resume support for the download manager (#1147)
Adds a paused state to the Electron download manager with a full partial-file lifecycle:

- Pause keeps the .part and byte progress; cancel discards them; every lifecycle stage (queued, active, mid-transfer) is pausable.
- Resume continues via HTTP Range with If-Range entity validation (strong ETag / Last-Modified persisted in the new resume_validator column, idempotent migration incl. legacy-table rebuild). Non-206 answers restart from zero over the same .part; the 206 Content-Range offset is verified; responses that end before the advertised size are retained for a Range retry instead of being committed as completed.
- Crash recovery converts interrupted transfers to paused, keeps queued-with-partial rows resumable, and commits finalizations that crashed before the DB update.
- Destination collisions are non-destructive (retained partials finalize to the next numbered name); locked .part files never lose their DB owner across cancel/remove/restart; resume claims rows atomically and the queue dedupes ids.
- Stored request headers are re-filtered through the User-Agent/Origin/Referer allowlist on read, URL-derived extensions are sanitized, resume appends never follow symlinks, and transfer errors are logged by message only.
- UI: pause/resume/cancel/retry/remove surface failures in a snackbar; paused items show an active Resume button in VOD/episode detail views; translations for all 18 locales.
- Runtime split into download-runtime/transfer/finalize/broadcast modules; +30 unit tests and an Electron E2E covering pause -> retained .part -> Range/If-Range resume -> byte-exact assembly.

Co-authored-by: genrichh93-ui <genrichh93@users.noreply.github.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-24 18:40:30 +02:00
4grayandClaude Fable 5 45b6d8a041 fix(m3u): parse playlists with URLs longer than 2084 characters (#1204)
* fix(m3u): parse playlists with URLs longer than 2084 characters

Pluto TV style playlists (issue #1189) embed a session JWT in every
stream URL (~2200 chars). validator.isURL inside iptv-playlist-parser
rejected anything over its IE-era 2084-char default, and the parser's
stalled item index then collapsed the whole playlist into a single
channel.

Sync the 4gray/iptv-playlist-parser fork with upstream v0.15.2, which
removes URL validation entirely and adds an explicit branch so '#'
comments and unknown directives are never treated as URLs. Two fork
deltas are preserved on top: the radio attribute (radio player
detection) and pipe stripping (item.url is cut at the first '|' while
|User-Agent=/|Referer= params still land in item.http). The now-dead
validator/is-valid-path dependencies are dropped from the fork.

- pin iptv-playlist-parser to the fork commit SHA
- add a parser contract spec guarding long URLs, comment handling,
  radio, pipe stripping, and header EPG attrs
- document the parser fork contract in the M3U architecture doc

Fixes #1189

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(m3u): bump parser to optimized fork build

Pulls the fork's optimized parse() rewrite (2.5-3x faster: 100k
channels ~780ms -> ~285ms, 10k ~79ms -> ~25ms) and the README
documenting fork deltas. Output is differential-verified byte-identical
to the previous build; all parser-contract, unit, and import E2E suites
rerun green against the new pin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(m3u): bump parser for input robustness and library hygiene

Pulls the fork's real-world input tolerance: UTF-8 BOM, blank lines and
whitespace before the header, and case-insensitive #EXTM3U no longer
reject the playlist (all VLC-accepted forms); Node Buffers are decoded
as UTF-8 and other non-string input throws a clear TypeError. Also
brings truthful types (url?: string), fork metadata, an enforced 100%
coverage gate, and the fork CHANGELOG.

Extends the contract spec with a BOM regression test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(m3u): pin parser to the tagged fork release v0.15.2-iptvnator.1

Same commit as before (33f5e9c) — the readable tag replaces the raw
SHA in package.json while pnpm-lock still records the immutable
codeload tarball by commit. Fork release:
https://github.com/4gray/iptv-playlist-parser/releases/tag/v0.15.2-iptvnator.1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(types): make ParsedPlaylistItem.url optional to match runtime

The parser fork's d.ts now truthfully declares url?: string (a trailing
#EXTINF without a stream URL yields url === undefined at runtime, and
always has). The local ParsedPlaylistItem mirrored the old type lie and
made the production typecheck reject the parser's Playlist type.
createChannel and createPlaylistObject already tolerate the absent url.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 08:56:26 +02:00