platform-access-di
Pattern of rule platform-access. Load when the window, the document or an environment check enters the code — ready-made token injects, casting to Window & typeof globalThis, the window as a parameter
Файлы1
Открыть в GitHubSKILL.md.claude/skills/platform-access-di/SKILL.md
GitHub<!-- rt-kit v0.35.0 · patterns/platform-access-di.md · 1483daf56f8c · правится надстройкой, не здесь -->
When
The window, the document and the environment check
Pattern of the rule platform-access. What must be true — the law
docs/constitution/frontend-application.md.
When to use
window,documentor an environment check enters a component, a service or a directive.- DOM work appears that must not happen before the first render.
- A pure function needs access to the window.
Injects
import { DOCUMENT } from '@angular/common';
import { inject, Injectable } from '@angular/core';
import { PlatformService, WINDOW } from '@rt-tools/core';
@Injectable({ providedIn: 'root' })
export class SomeService {
readonly #document: Document = inject(DOCUMENT);
readonly #platform: PlatformService = inject(PlatformService);
readonly #window: Window = inject(WINDOW);
}
When Window & typeof globalThis is needed
The Window interface does not describe the global constructors and namespaces —
IntersectionObserver, ResizeObserver, google from @types/google.maps. The token returns
the very same object, so the type is narrowed by a cast, and a comment with the reason is put
next to it:
// Constructors like IntersectionObserver are declared on globalThis, not on the
// Window interface — the token returns the same object, only the type is narrowed.
readonly #window: Window & typeof globalThis = inject(WINDOW) as Window & typeof globalThis;
A pure function takes the window as a parameter
There is no DI in *.logic.ts and *.util.ts, and the global is not pulled inside:
export function mapsReady(windowRef: Window & typeof globalThis): boolean {
return typeof windowRef.google?.maps?.importLibrary === 'function';
}
The calling component injects it.
The environment check and the first render
if (!this.#platform.isPlatformBrowser) {
return;
}
afterNextRender((): void => {
// DOM work that must not happen before the first render
});
typeof window !== 'undefined' is no good: a check by the presence of a global is right by
accident.
Common misses
isPlatformBrowser(inject(PLATFORM_ID))instead ofPlatformService.- An environment check around a read and a write to storage:
StorageServicefalls back to memory outside the browser anyway, and such anifis dead code. WINDOWas a class field in a service that must work without a DOM at all: there the window is taken inside a method under an environment check.- An edit added
WINDOWto a service created at startup, and it was checked by the build alone: the failure shows only on a running page-rendering server. - A cast without a comment: in mappers a cast is forbidden, and the line reads as a violation.