Files
iptvnator/package.json
T
4grayandClaude Opus 4.8 23ead63b1f fix(packaging): ship ms so the updater doesn't crash the app (#1103) (#1113)
* fix(packaging): ship `ms` so the updater doesn't crash the app (#1103)

The 0.22 AppImage crashed on launch with "Cannot find module 'ms'" after
the desktop updater landed (b1119189). electron-updater requires
`debug` -> `ms` unguarded at startup, but the packaged app.asar shipped
`debug` without `ms`.

Root cause: with pnpm's isolated node-linker, electron-builder 26 uses its
PnpmNodeModulesCollector, which builds the bundle from `pnpm list --json`.
pnpm deduplicates repeated packages there, so all but one `debug@4.4.3`
occurrence report empty `dependencies` — and the collector (unlike the npm
collector) has no implicit-dependency recovery, so it drops `ms` entirely.
It stayed latent because the only prior `debug` consumer (follow-redirects
via axios) guards its require in try/catch; electron-updater is the first
packaged module to hit it unguarded.

Fix: declare `ms` as a direct dependency so it becomes a top-level,
fully-expanded node in the collector's tree and is bundled. This keeps the
isolated pnpm layout intact — `node-linker=hoisted` was rejected because it
removes `node_modules/.pnpm`, which apps/electron-backend/build-embedded-mpv.js
scans to resolve @electron/node-gyp, breaking the native build on every
platform.

Also add a packaged-asar dependency-closure guard to
verify-electron-package-layout.mjs: it audits every package shipped in the
archive and fails if any non-optional dependency is missing, so this class
of regression is caught in CI. Logic is extracted to a unit-tested module.

Verified locally: repackaged app.asar now ships `ms`, the embedded-mpv
native build succeeds, and the closure guard reports 0 missing.

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

* fix(packaging): harden asar dependency-closure guard from review findings

Addresses review feedback on #1113 plus an adversarial-review finding:

- Walk every ancestor directory in resolvePackagedDependency (not just
  node_modules boundaries) so a dependency hoisted to the archive root
  resolves for packages under app subdirectories, matching Node's real
  resolution (Codex review).
- Skip dependencies also declared in peerDependencies: host-provided
  peers (e.g. electron) listed in both fields are not packaging defects
  (Greptile review).
- Fix a silent no-op on Windows: @electron/asar lists entries and
  resolves extractFile paths with the host separator, so the posix-only
  matching audited zero packages on the Windows CI leg. Listings are now
  normalized to posix and lookup paths converted back to the host
  separator (pathSep is injectable for tests).
- Reject vacuous passes structurally: inspectPackagedDependencyClosure
  now reports packageCount and manifestReadFailures, and the verifier
  errors when the audit saw no packages or failed to read manifests,
  so the guard can never silently audit nothing again.

Verified: 27 packaging tests pass; the real app.asar audits 235 packages
with 0 missing under both posix and simulated win32 IO; hiding `ms` from
a win32-shaped listing correctly flags it.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:58:21 +02:00

250 lines
11 KiB
JSON

{
"name": "iptvnator",
"version": "0.22.0",
"license": "MIT",
"description": "IPTV player application.",
"homepage": "https://github.com/4gray/iptvnator",
"author": {
"name": "4gray",
"email": "fourgray@proton.me"
},
"keywords": [
"angular",
"electron",
"typescript",
"m3u",
"m3u8",
"player",
"iptv",
"video",
"tv"
],
"scripts": {
"postinstall": "electron-builder install-app-deps",
"build:frontend": "nx build web",
"build:frontend:pwa": "nx build web --configuration=pwa",
"build:backend": "nx build electron-backend --configuration=production",
"serve:frontend": "nx serve web --no-tui",
"serve:frontend:pwa": "nx serve web --configuration=pwa --no-tui",
"serve:backend": "nx serve electron-backend",
"serve:marketing-demo": "nx run-many --target=serve --projects=xtream-mock-server,electron-backend --parallel=2",
"serve:marketing-demo:web": "nx run-many --target=serve --projects=xtream-mock-server,web --parallel=2",
"test:frontend": "nx test web",
"test:backend": "nx test electron-backend",
"test:unit:all": "nx run-many --target=test --all --parallel=3",
"test:unit:ci": "nx run-many --target=test --projects=electron-backend,web,services,m3u-state,portal-stalker-data-access,portal-shared-util,playlist-shared-ui,portal-xtream-data-access,portal-xtream-feature,workspace-dashboard-data-access,components,@iptvnator/pipes,packaging,epg-data-access,workspace-shell-util,database --parallel=3 --output-style=static",
"coverage:unit:ci": "node tools/coverage/run-tier-a-coverage.mjs",
"coverage:merge": "node tools/coverage/merge-coverage.mjs",
"coverage:health": "node tools/coverage/coverage-health.mjs",
"coverage:ci": "pnpm run coverage:unit:ci && pnpm run coverage:merge && node tools/coverage/coverage-health.mjs --require-report",
"coverage:e2e:summary": "node tools/coverage/e2e-semantic-summary.mjs",
"coverage:e2e:v8:web": "node tools/coverage/e2e-v8-web.mjs",
"typecheck:web": "tsc -p apps/web/tsconfig.app.json --noEmit",
"typecheck:backend": "tsc -p apps/electron-backend/tsconfig.app.json --noEmit",
"typecheck:ci": "pnpm run typecheck:web && pnpm run typecheck:backend",
"verify:package-layout": "node tools/packaging/verify-electron-package-layout.mjs",
"embedded-mpv:build-native:homebrew": "IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW=1 nx run electron-backend:build-embedded-mpv",
"embedded-mpv:build-runtime": "node tools/embedded-mpv/build-macos-runtime.mjs",
"embedded-mpv:stage-runtime": "node tools/embedded-mpv/stage-runtime.mjs",
"embedded-mpv:stage-runtime:macos": "node tools/embedded-mpv/stage-macos-runtime.mjs",
"embedded-mpv:stage-runtime:windows-archive": "node tools/embedded-mpv/stage-windows-runtime-archive.mjs",
"serve:backend:embedded-mpv": "pnpm embedded-mpv:build-native:homebrew && IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW=1 IPTVNATOR_ENABLE_EMBEDDED_MPV_EXPERIMENT=1 pnpm serve:backend",
"package:app": "nx run electron-backend:make --prepackageOnly",
"make:app": "nx run electron-backend:make",
"smoke:packaged": "node tools/testing/launch-packaged-electron.mjs",
"serve:website": "nx serve website",
"build:website": "nx build website",
"i18n:check": "node tools/i18n/check-drift.mjs",
"release:artwork:dry-run": "tsx tools/release/generate-marketing-artwork.ts --dry-run",
"release:artwork:manifest": "tsx tools/release/generate-marketing-artwork.ts --manifest",
"release:artwork:generate": "tsx tools/release/generate-marketing-artwork.ts --generate",
"release:artwork:validate": "tsx tools/release/generate-marketing-artwork.ts --validate",
"lint": "nx lint electron-backend",
"build": "nx build electron-backend",
"wiki:export": "node tools/wiki/export-wiki-context.mjs",
"test:wiki-export": "node --test tools/wiki/export-wiki-context.test.mjs"
},
"private": true,
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319",
"dependencies": {
"@angular/animations": "21.2.9",
"@angular/cdk": "21.2.9",
"@angular/common": "21.2.9",
"@angular/compiler": "21.2.9",
"@angular/core": "21.2.9",
"@angular/forms": "21.2.9",
"@angular/material": "21.2.9",
"@angular/platform-browser": "21.2.9",
"@angular/platform-browser-dynamic": "21.2.9",
"@angular/router": "21.2.9",
"@ngrx/component-store": "21.0.1",
"@ngrx/effects": "21.0.1",
"@ngrx/entity": "21.0.1",
"@ngrx/router-store": "21.0.1",
"@ngrx/signals": "21.0.1",
"@ngrx/store": "21.0.1",
"@ngrx/store-devtools": "21.0.1",
"@ngx-pwa/local-storage": "21.0.0",
"@videojs/http-streaming": "2.15.0",
"@yangkghjh/videojs-aspect-ratio-panel": "0.0.1",
"angularx-qrcode": "21.0.4",
"artplayer": "5.3.0",
"axios": "1.15.2",
"better-sqlite3": "12.9.0",
"date-fns": "4.1.0",
"drizzle-orm": "0.45.2",
"electron-conf": "1.3.0",
"electron-dl": "4.0.0",
"electron-updater": "6.8.9",
"epg-parser": "^0.1.6",
"fix-path": "5.0.0",
"hls.js": "1.6.13",
"iptv-playlist-parser": "github:4gray/iptv-playlist-parser",
"lodash": "4.18.1",
"marked": "18.0.5",
"mpegts.js": "1.8.0",
"ms": "2.1.3",
"ngx-indexed-db": "21.0.0",
"ngx-skeleton-loader": "11.3.0",
"rxjs": "7.8.2",
"saxes": "6.0.0",
"semver": "7.7.3",
"uuid": "9.0.0",
"video.js": "8.23.4",
"videojs-contrib-quality-levels": "4.1.0",
"videojs-quality-selector-hls": "1.1.1",
"zone.js": "~0.15.1"
},
"devDependencies": {
"@angular-devkit/core": "21.2.9",
"@angular-devkit/schematics": "21.2.9",
"@angular-eslint/builder": "21.3.1",
"@angular-eslint/eslint-plugin": "21.3.1",
"@angular-eslint/eslint-plugin-template": "21.3.1",
"@angular-eslint/schematics": "21.3.1",
"@angular-eslint/template-parser": "21.3.1",
"@angular/build": "21.2.9",
"@angular/cli": "21.2.10",
"@angular/common": "21.2.9",
"@angular/compiler": "21.2.9",
"@angular/compiler-cli": "21.2.9",
"@angular/core": "21.2.9",
"@angular/forms": "21.2.9",
"@angular/language-service": "21.2.9",
"@angular/platform-browser": "21.2.9",
"@angular/platform-browser-dynamic": "21.2.9",
"@angular/router": "21.2.9",
"@angular/service-worker": "21.2.9",
"@astrojs/mdx": "4.3.13",
"@astrojs/sitemap": "3.7.0",
"@astrojs/tailwind": "6.0.2",
"@electron/asar": "3.4.1",
"@eslint/eslintrc": "3.3.1",
"@eslint/js": "^9.38.0",
"@faker-js/faker": "10.3.0",
"@fontsource/crimson-pro": "5.2.8",
"@fontsource/dm-sans": "5.2.8",
"@fontsource/jetbrains-mono": "5.2.8",
"@fontsource/roboto": "5.2.10",
"@ngrx/eslint-plugin": "^21.0.1",
"@ngx-translate/core": "16.0.4",
"@ngx-translate/http-loader": "16.0.1",
"@nx/angular": "22.7.1",
"@nx/devkit": "22.7.1",
"@nx/esbuild": "22.7.1",
"@nx/eslint": "22.7.1",
"@nx/eslint-plugin": "22.7.1",
"@nx/jest": "22.7.1",
"@nx/js": "22.7.1",
"@nx/playwright": "22.7.1",
"@nx/web": "22.7.1",
"@nx/workspace": "22.7.1",
"@playwright/test": "^1.36.0",
"@schematics/angular": "21.2.9",
"@semantic-release/changelog": "6.0.3",
"@semantic-release/git": "10.0.1",
"@semantic-release/npm": "12.0.1",
"@swc-node/register": "1.11.1",
"@swc/core": "1.15.8",
"@swc/helpers": "0.5.18",
"@tailwindcss/typography": "0.5.19",
"@types/better-sqlite3": "^7.6.12",
"@types/cors": "2.8.19",
"@types/express": "5.0.6",
"@types/jest": "^30.0.0",
"@types/lodash": "4.17.20",
"@types/mocha": "9.0.0",
"@types/node": "20.19.9",
"@types/uuid": "^10.0.0",
"@types/video.js": "7.3.29",
"@typescript-eslint/eslint-plugin": "^8.46.2",
"@typescript-eslint/parser": "^8.46.2",
"@typescript-eslint/utils": "^8.46.2",
"angular-eslint": "21.3.1",
"astro": "5.18.1",
"conventional-changelog-cli": "5.0.0",
"cors": "2.8.6",
"drizzle-kit": "0.31.5",
"electron": "^41.7.2",
"electron-builder": "^26.0.12",
"electron-playwright-helpers": "1.8.2",
"esbuild": "0.27.0",
"eslint": "^9.8.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-playwright": "^1.6.2",
"express": "5.2.1",
"globals": "15.9.0",
"istanbul-lib-coverage": "3.2.2",
"istanbul-lib-report": "3.0.1",
"istanbul-reports": "3.2.0",
"jest": "^30.0.2",
"jest-environment-jsdom": "^30.0.2",
"jest-environment-node": "^30.0.2",
"jest-preset-angular": "~15.0.0",
"jest-util": "^30.0.2",
"jsonc-eslint-parser": "^2.1.0",
"material-design-icons-iconfont": "6.7.0",
"ng-mocks": "14.15.1",
"nx": "22.7.1",
"nx-electron": "22.0.0",
"prettier": "^3.8.1",
"semantic-release": "24.2.9",
"sharp": "0.34.5",
"tailwindcss": "^3.4.19",
"ts-jest": "^29.4.5",
"ts-node": "10.9.2",
"tslib": "^2.8.1",
"tsx": "4.21.0",
"typescript": "5.9.3",
"typescript-eslint": "^8.46.2"
},
"pnpm": {
"overrides": {
"@hono/node-server@1.19.9": "1.19.14",
"ajv@6.12.6": "6.14.0",
"brace-expansion@1.1.12": "1.1.13",
"defu@6.1.4": "6.1.6",
"devalue@5.6.2": "5.6.4",
"express-rate-limit@8.2.1": "8.3.0",
"flatted@3.3.3": "3.4.2",
"follow-redirects@1.15.11": "1.16.0",
"h3@1.15.5": "1.15.10",
"hono@4.12.0": "4.12.14",
"immutable@5.1.4": "5.1.5",
"lodash-es@4.17.22": "4.18.1",
"node-abi@3.85.0": "3.92.0",
"node-forge@1.3.3": "1.4.0",
"path-to-regexp@0.1.12": "0.1.13",
"picomatch@2.3.1": "2.3.2",
"rollup@4.52.3": "4.59.0",
"smol-toml@1.6.0": "1.6.1",
"svgo@3.3.2": "3.3.3",
"yaml@1.10.2": "1.10.3"
},
"patchedDependencies": {
"nx-electron@22.0.0": "patches/nx-electron@22.0.0.patch"
}
}
}