perf(web): keep backup/restore and portal helpers off the initial path (#1734)

* perf(web): keep backup/restore and portal helpers off the initial path

#1601 (parental lock) put about 35 KB onto the renderer's initial path by
design (the lock service, lock store and enforcement gate the workspace
resolver and the catalog data sources) and was merged with the ratchet red:
renderer.initialBytes 1,655,428 against the 1,619,993 baseline.

Offset it without touching the lock gate. Code splitting puts a module in the
chunk shared by every entry that reaches it, so helpers only lazy routes use
landed in initial chunks because eager files reach them through barrels:

- PlaylistBackupService (only the lazy settings page) moves to
  @iptvnator/services/playlist-backup and out of the services barrel.
- The eager Xtream data layer and root shell import the portal logger and DI
  tokens through @iptvnator/portal/shared/util/logger and /tokens instead of
  the barrel, whose navigation, keyboard-shortcut and download helpers
  (about 45 KB) belong to the lazy portal routes.

renderer.initialBytes 1,655,428 -> 1,598,232 bytes (-57,196).

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

* chore(performance): lower the initial-bytes baseline to 1,598,232 bytes

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

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-09-27 17:57:44 +02:00
1 parent 4c1403f545
commit 9d02f90dfe
27 files changed
+61 -29

No files matched your search

+1 -1
View File
@@ -30,7 +30,7 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import {
PORTAL_EXTERNAL_PLAYBACK,
PORTAL_PLAYER,
} from '@iptvnator/portal/shared/util';
} from '@iptvnator/portal/shared/util/tokens';
import { STALKER_PLAYLIST_CONNECTION_EDITOR } from '@iptvnator/playlist/shared/ui/stalker-connection-editor';
import { provideXtreamDataSource } from '@iptvnator/portal/xtream/data-access';
import {
@@ -36,7 +36,7 @@ import {
createLogger,
createPortalDebugRequestContext,
logPortalDebugEvent,
} from '@iptvnator/portal/shared/util';
} from '@iptvnator/portal/shared/util/logger';
interface PlayerLaunchPayload {
readonly headers?: Record<string, string>;
@@ -2,7 +2,7 @@ import { Injectable, inject } from '@angular/core';
import {
PORTAL_PLAYBACK_POSITIONS,
PortalPlaybackPositions,
} from '@iptvnator/portal/shared/util';
} from '@iptvnator/portal/shared/util/tokens';
import {
PlaybackPositionRuntimeBridgeService,
RuntimeCapabilitiesService,
+1 -1
View File
@@ -34,7 +34,7 @@ import {
logPortalDebugEvent,
logPortalDebugRequest,
createLogger,
} from '@iptvnator/portal/shared/util';
} from '@iptvnator/portal/shared/util/logger';
import { getRuntimeBackendUrl } from './runtime-config';
/**
@@ -1,7 +1,7 @@
import { inject, Injectable, Provider } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { PORTAL_SHELL_ACTIONS } from '@iptvnator/portal/shared/util';
import { PORTAL_SHELL_ACTIONS } from '@iptvnator/portal/shared/util/tokens';
import {
WORKSPACE_SHELL_ACTIONS,
WorkspaceAccountInfoData,
@@ -5,7 +5,7 @@ import {
selectIsEpgAvailable,
} from '@iptvnator/m3u-state';
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
import { PlaylistBackupService } from '@iptvnator/services';
import { PlaylistBackupService } from '@iptvnator/services/playlist-backup';
import { provideMockStore } from '@ngrx/store/testing';
import { TranslateModule } from '@ngx-translate/core';
import { MockProvider } from 'ng-mocks';
@@ -3,11 +3,11 @@ import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { PlaylistActions } from '@iptvnator/m3u-state';
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
import { RuntimeCapabilitiesService } from '@iptvnator/services';
import {
PlaylistBackupImportSummary,
PlaylistBackupService,
RuntimeCapabilitiesService,
} from '@iptvnator/services';
} from '@iptvnator/services/playlist-backup';
import { SettingsSnackbarService } from './settings-snackbar.service';
@Injectable()
@@ -34,9 +34,9 @@ import {
import {
DatabaseService,
DataService,
PlaylistBackupService,
PlaylistsService,
} from '@iptvnator/services';
import { PlaylistBackupService } from '@iptvnator/services/playlist-backup';
import {
ELECTRON_BRIDGE_APP_UPDATE_STATUSES,
ElectronBridgeAppUpdateStatus,
+17 -2
View File
@@ -237,7 +237,10 @@ reason: the main `workspace-shell-util` barrel is imported eagerly, and the
settings search index must stay in the lazy settings and shell chunks.
`@iptvnator/workspace/shell/util/settings-context` keeps
`SettingsContextService`, which only the lazy settings page and settings
context panel use, out of that barrel for the same reason.
context panel use, out of that barrel for the same reason, and
`@iptvnator/services/playlist-backup` keeps `PlaylistBackupService` (about
22 KB, used only by the lazy settings page) out of the `@iptvnator/services`
barrel.
The web app's root shell (`app.component.ts`, `app.config.ts` and the services
they construct) is on the renderer's initial path, where a barrel costs its
@@ -260,7 +263,19 @@ PIN dialog and the Stalker step of the parental-lock enforcement, which runs
only while a Stalker route is open). A local file,
not the library alias, is the dynamic-import target because
`@nx/enforce-module-boundaries` forbids static imports of a library the same
project also loads dynamically. `renderer.initialBytes` in
project also loads dynamically.
A barrel the initial path imports also costs code it never runs itself:
code splitting places a module in the chunk shared by every entry point
that reaches it, so a helper that only lazy routes use, but that an eager
file can reach through a barrel's re-export, lands in an initial chunk even
though tree shaking would drop it from a single bundle. The eager Xtream
data layer and root shell therefore import `@iptvnator/portal/shared/util/logger`
and `@iptvnator/portal/shared/util/tokens` (the portal DI tokens), not the
`@iptvnator/portal/shared/util` barrel, whose navigation, keyboard-shortcut
and download helpers (about 45 KB) belong to the lazy portal routes. A
type-only import of a barrel uses `import type`, so it can never keep the
barrel reachable. `renderer.initialBytes` in
[performance journeys](performance-journeys.md) guards the result; to see why a
module is eager, build with `pnpm nx build web --stats-json` and follow the
static imports in `dist/apps/web/stats.json` from `apps/web/src/main.ts`.
+4 -2
View File
@@ -419,8 +419,10 @@ opens, so a relock there runs the step synchronously; if it is not loaded
yet (still fetching, or it cannot load — a stale PWA page after a
deployment) the step fails closed at once by navigating to
`/workspace/sources`: leaving the Stalker route clears its selection and
stops its playback, and the Xtream step still runs. The feature costs about 30 KB of
`renderer.initialBytes`.
stops its playback, and the Xtream step still runs. The feature costs about 35 KB of
`renderer.initialBytes`; the baseline was not raised for it, the growth was
offset by moving lazy-only barrel re-exports (backup/restore and the portal
helpers) off the initial path.
## Lock store lifetime
+7
View File
@@ -0,0 +1,7 @@
// Dependency-free entry for the DI tokens the web app's root shell and the
// eager Xtream data layer provide or inject. The main barrel would put every
// portal helper that lazy routes use onto the renderer's initial path.
export * from './lib/portal-external-playback';
export * from './lib/portal-playback-positions';
export * from './lib/portal-player';
export * from './lib/portal-shell-actions';
@@ -11,7 +11,7 @@ import {
XtreamSerieItem,
XtreamVodStream,
} from '@iptvnator/shared/interfaces';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import {
CategoryType,
StreamType,
@@ -8,7 +8,7 @@ import {
import { EpgSourceSettingsService, SettingsStore } from '@iptvnator/services';
import { XtreamApiService, XtreamCredentials } from './xtream-api.service';
import { XtreamXmltvFallbackService } from './xtream-xmltv-fallback.service';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
interface CacheEntry {
data: EpgItem[];
@@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core';
import { DataService } from '@iptvnator/services';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import {
EpgItem,
XtreamCategory,
@@ -4,7 +4,7 @@ import {
EpgProgram,
epgProviderClockMs,
} from '@iptvnator/shared/interfaces';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import { EpgSourceSettingsService, SettingsStore } from '@iptvnator/services';
type ElectronEpgBridge = {
@@ -16,7 +16,7 @@ import {
measureRendererPerformancePhase,
RENDERER_PERFORMANCE_PHASE,
} from '@iptvnator/shared/logging';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import {
DataService,
DatabaseService,
@@ -22,7 +22,7 @@ import {
XtreamCredentials,
} from '../../services/xtream-api.service';
import { XtreamXmltvFallbackService } from '../../services/xtream-xmltv-fallback.service';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
/**
* EPG state for managing Electronic Program Guide data
@@ -5,7 +5,7 @@ import {
withMethods,
withState,
} from '@ngrx/signals';
import { PORTAL_PLAYER } from '@iptvnator/portal/shared/util';
import { PORTAL_PLAYER } from '@iptvnator/portal/shared/util/tokens';
import {
XtreamSerieEpisode,
XtreamVodDetails,
@@ -14,7 +14,7 @@ import {
XtreamCredentials,
} from '../../services/xtream-api.service';
import { PortalStatusType } from '../../xtream-state';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import {
resolveXtreamPortalStatus,
resolveXtreamServerTimezone,
@@ -14,7 +14,7 @@ import {
XTREAM_DATA_SOURCE,
XtreamContentItem,
} from '../../data-sources/xtream-data-source.interface';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import { XtreamSearchResultItem } from '../../xtream-state';
/**
@@ -12,7 +12,7 @@ import { XTREAM_DATA_SOURCE } from '../data-sources/xtream-data-source.interface
import { XtreamApiService } from '../services/xtream-api.service';
import { TmdbEnrichmentService } from '@iptvnator/services';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import {
withContent,
withEpg,
@@ -5,7 +5,7 @@ import {
withMethods,
withState,
} from '@ngrx/signals';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import { XTREAM_DATA_SOURCE } from './data-sources/xtream-data-source.interface';
export const withFavorites = function () {
@@ -15,7 +15,7 @@ import {
Playlist,
PortalRecentItem,
} from '@iptvnator/shared/interfaces';
import { createLogger } from '@iptvnator/portal/shared/util';
import { createLogger } from '@iptvnator/portal/shared/util/logger';
import { XTREAM_DATA_SOURCE } from './data-sources/xtream-data-source.interface';
export interface RecentlyViewedItem extends PortalRecentItem {
-1
View File
@@ -10,7 +10,6 @@ export * from './lib/playback-position.service';
export * from './lib/playlist-delete-cleanup.token';
export * from './lib/playlist-delete-action.service';
export * from './lib/parental-lock';
export * from './lib/playlist-backup.service';
export * from './lib/playlist-cross-context-lock';
export * from './lib/playlist-refresh.service';
export * from './lib/playlists.service';
@@ -1,4 +1,4 @@
import {
import type {
PortalProvider,
PortalRailSection,
} from '@iptvnator/portal/shared/util';
+2 -2
View File
@@ -3,10 +3,10 @@
"journeys": {
"launch": {
"renderer.initialBytes": {
"value": 1619993,
"value": 1598232,
"unit": "bytes",
"updatedAt": "2026-09-27",
"evidencePr": 1729,
"evidencePr": 1734,
"measuredWith": "pnpm nx build web && pnpm run perf:initial-bytes"
}
}
+9
View File
@@ -32,6 +32,12 @@
"@iptvnator/portal/shared/util": [
"libs/portal/shared/util/src/index.ts"
],
"@iptvnator/portal/shared/util/logger": [
"libs/portal/shared/util/src/lib/logger.ts"
],
"@iptvnator/portal/shared/util/tokens": [
"libs/portal/shared/util/src/tokens.ts"
],
"@iptvnator/epg/data-access": ["libs/epg/data-access/src/index.ts"],
"@iptvnator/playback/util": ["libs/playback/util/src/index.ts"],
"@iptvnator/playlist/shared/ui": [
@@ -112,6 +118,9 @@
"libs/shared/host-health/src/index.ts"
],
"@iptvnator/services": ["libs/services/src/index.ts"],
"@iptvnator/services/playlist-backup": [
"libs/services/src/lib/playlist-backup.service.ts"
],
"@iptvnator/shared/interfaces": [
"libs/shared/interfaces/src/index.ts"
],