@3dsource/angular-unreal-module 0.0.157 → 0.0.159

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,392 +1,185 @@
1
1
  # @3dsource/angular-unreal-module
2
2
 
3
- A set of standalone Angular components, services, and providers for integrating Unreal Engine (WebRTC) scenes into Angular applications. It facilitates communication between Angular and Unreal Engine and enables interactive 3D experiences.
3
+ Standalone Angular integration for Unreal Engine Pixel Streaming. The package
4
+ provides the scene component, WebRTC and signalling lifecycle, NgRx state,
5
+ command/callback APIs, reconnection, file transfer and telemetry.
4
6
 
5
- ## Overview
7
+ ## Requirements
6
8
 
7
- This package provides:
8
-
9
- - Standalone Unreal scene component to embed UE stream
10
- - Communication bridge (commands, UI interactions, input data)
11
- - Callback listener for Unreal events and command responses
12
- - NgRx state and effects for 3D stream lifecycle
13
- - Config and utilities for telemetry, errors, and regions ping
14
- - Auto-reconnection support for WebRTC/DataChannel failures
15
- - File receiving from Unreal Engine
16
- - Analytics, FPS monitoring, and stream status telemetry
17
- - Playwright testing mode with mock services
18
-
19
- ## Installation
20
-
21
- ### Prerequisites
22
-
23
- - Angular `>=19.0.0 <23.0.0`
24
- - NgRx store and effects `>=19.0.0 <23.0.0`
25
- - Angular CDK `>=19.0.0 <23.0.0` — used for dialog overlays
9
+ - Angular, Angular CDK and Angular Forms `>=19.0.0 <23.0.0`
10
+ - NgRx Store and Effects `>=19.0.0 <23.0.0`
26
11
  - RxJS `>=7.8.0 <8.0.0`
27
- - `provideHttpClient()` — required by internal services (telemetry, signalling, regions ping, error reporting)
28
-
29
- ### Peer Dependencies
30
-
31
- This library requires the following peer dependencies:
32
-
33
- ```json
34
- {
35
- "@3dsource/source-ui-native": ">=1.0.9",
36
- "@3dsource/types-unreal": ">=0.0.7",
37
- "@3dsource/utils": ">=1.0.21",
38
- "@angular/cdk": ">=19.0.0 <23.0.0",
39
- "@angular/common": ">=19.0.0 <23.0.0",
40
- "@angular/core": ">=19.0.0 <23.0.0",
41
- "@angular/forms": ">=19.0.0 <23.0.0",
42
- "@angular/platform-browser": ">=19.0.0 <23.0.0",
43
- "@ngrx/effects": ">=19.0.0 <23.0.0",
44
- "@ngrx/store": ">=19.0.0 <23.0.0",
45
- "rxjs": ">=7.8.0 <8.0.0"
46
- }
47
- ```
12
+ - `@3dsource/source-ui-native >=1.0.9`
13
+ - `@3dsource/types-unreal >=0.0.7`
14
+ - `@3dsource/utils >=1.0.21`
15
+ - `provideHttpClient()` in the host application
48
16
 
49
- ### Library Installation
17
+ ## Installation
50
18
 
51
19
  ```shell
52
- npm i @3dsource/angular-unreal-module
20
+ pnpm add @3dsource/angular-unreal-module @3dsource/source-ui-native @3dsource/types-unreal @3dsource/utils
53
21
  ```
54
22
 
55
- ## Usage
56
-
57
- The API is fully standalone (no NgModule). Use providers and components as shown below.
23
+ The package is standalone and does not expose an NgModule.
58
24
 
59
- ### 1) Provide the state and config at the application root
25
+ ## Setup
60
26
 
61
- Add providers in your application bootstrap (e.g., `app.config.ts`):
27
+ ### 1. Register state and configuration once
62
28
 
63
- > **⚠️ Important:** `UNREAL_CONFIG` is **required** — multiple internal services inject it without `{ optional: true }`. Omitting it will cause a `NullInjectorError` at runtime. You can provide it with an empty object `{}` as a minimum.
29
+ Add the Unreal feature state, HTTP client and configuration at the application
30
+ root. `UNREAL_CONFIG` is required, although all its fields are optional.
64
31
 
65
- ```ts
66
- import { ApplicationConfig } from '@angular/core';
67
- import { provideRouter } from '@angular/router';
32
+ <!-- prettier-ignore -->
33
+ ```typescript
68
34
  import { provideHttpClient } from '@angular/common/http';
35
+ import type { ApplicationConfig } from '@angular/core';
69
36
  import { provideStore } from '@ngrx/store';
70
- import { provideUnrealState, UNREAL_CONFIG } from '@3dsource/angular-unreal-module';
37
+ import {
38
+ provideUnrealState,
39
+ UNREAL_CONFIG,
40
+ type UnrealInitialConfig,
41
+ } from '@3dsource/angular-unreal-module';
42
+
43
+ const unrealConfig = {
44
+ regionsPingUrl: 'https://datacenter.3dsource.com/regions/',
45
+ dataChannelConnectionTimeout: 8000,
46
+ fpsMonitor: false,
47
+ autoHighResolution: false,
48
+ } satisfies UnrealInitialConfig;
71
49
 
72
50
  export const appConfig: ApplicationConfig = {
73
51
  providers: [
74
- provideRouter([]),
75
52
  provideHttpClient(),
76
-
77
- // Root NgRx (if not already added in your app)
78
53
  provideStore(),
79
54
  provideUnrealState(),
80
-
81
- // Required: Unreal initial configuration
82
- {
83
- provide: UNREAL_CONFIG,
84
- useValue: {
85
- customErrorsEndpoint: '', // Endpoint for custom error reporting
86
- commandTelemetryReceiver: '', // Endpoint for command telemetry
87
- regionsPingUrl: '', // URL prefix for regions latency ping
88
- screenLockerContainerId: '', // DOM container id for screen locker overlay
89
- dataChannelConnectionTimeout: 8000, // Timeout in ms for data channel connection (default: 8000)
90
- streamTelemetryV2Url: '', // Endpoint for stream-status telemetry
91
- fpsMonitor: false, // Enable FPS monitoring
92
- autoHighResolution: false, // Raise resolution after the scene becomes idle
93
- playwright: false, // Mirrors the provider flag for services/effects
94
- },
95
- },
55
+ { provide: UNREAL_CONFIG, useValue: unrealConfig },
96
56
  ],
97
57
  };
98
58
  ```
99
59
 
100
- #### Minimal configuration
60
+ Omit `provideStore()` when the root NgRx store is already configured.
101
61
 
102
- If you don't need custom endpoints, provide `UNREAL_CONFIG` with an empty object:
62
+ Available configuration fields:
103
63
 
104
- ```ts
105
- { provide: UNREAL_CONFIG, useValue: {} },
106
- provideUnrealState(),
107
- ```
64
+ | Field | Purpose |
65
+ | ------------------------------ | ---------------------------------------------- |
66
+ | `regionsPingUrl` | Region latency endpoint |
67
+ | `dataChannelConnectionTimeout` | DataChannel connection timeout in ms |
68
+ | `customErrorsEndpoint` | Custom error reporting endpoint |
69
+ | `commandTelemetryReceiver` | Command telemetry endpoint |
70
+ | `streamTelemetryV2Url` | Stream lifecycle telemetry endpoint |
71
+ | `screenLockerContainerId` | Container used by the screen-locker overlay |
72
+ | `fpsMonitor` | Enables FPS monitoring |
73
+ | `autoHighResolution` | Raises resolution after the scene becomes idle |
74
+ | `playwright` | Enables the test-specific service behaviour |
75
+
76
+ Use `{ provide: UNREAL_CONFIG, useValue: {} }` for the minimal configuration.
108
77
 
109
- Every `UnrealInitialConfig` field is optional, but the `UNREAL_CONFIG` provider
110
- itself is required. Available fields are `playwright`,
111
- `customErrorsEndpoint`, `commandTelemetryReceiver`, `regionsPingUrl`,
112
- `screenLockerContainerId`, `dataChannelConnectionTimeout`,
113
- `streamTelemetryV2Url`, `fpsMonitor` and `autoHighResolution`.
78
+ ### 2. Boot the engine on a lazy route
114
79
 
115
- ### 2) Boot the engine in each lazy 3D route
80
+ Lazy-load the route file from the application router:
81
+
82
+ <!-- prettier-ignore -->
83
+ ```typescript
84
+ import type { Routes } from '@angular/router';
85
+
86
+ export const APP_ROUTES: Routes = [
87
+ {
88
+ path: 'stream',
89
+ loadChildren: () =>
90
+ import('./stream/stream.routes').then((module) => module.STREAM_ROUTES),
91
+ },
92
+ ];
93
+ ```
116
94
 
117
- `provideUnrealModule()` registers the effects and initializes the streaming
118
- services. Put it on every lazy route that renders a 3D scene:
95
+ Register `provideUnrealModule()` inside that lazy route file:
119
96
 
120
- ```ts
121
- import { Routes } from '@angular/router';
97
+ <!-- prettier-ignore -->
98
+ ```typescript
99
+ import type { Routes } from '@angular/router';
122
100
  import { provideUnrealModule } from '@3dsource/angular-unreal-module';
101
+ import { StreamComponent } from './stream.component';
123
102
 
124
103
  export const STREAM_ROUTES: Routes = [
125
104
  {
126
- path: ':id',
127
- loadComponent: () => import('./stream.component').then((module) => module.StreamComponent),
105
+ path: '',
106
+ component: StreamComponent,
128
107
  providers: [provideUnrealModule()],
129
108
  },
130
109
  ];
131
110
  ```
132
111
 
133
- Keep `provideUnrealState()` and `UNREAL_CONFIG` at the root. Keeping
134
- `provideUnrealModule()` in lazy routes also keeps the streaming engine out of
135
- the initial application bundle.
112
+ Keep `provideUnrealState()` and `UNREAL_CONFIG` at the application root.
113
+ The `loadChildren()` boundary keeps the streaming engine out of the initial
114
+ bundle. Route-scoping `provideUnrealModule()` tears down its effects when the
115
+ route is left.
136
116
 
137
- ### 3) Use the Unreal scene component
117
+ ### 3. Render the scene
138
118
 
139
- Import the component into a standalone component and use it in the template.
140
-
141
- ```ts
142
- import { Component } from '@angular/core';
119
+ ```typescript
120
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
143
121
  import { UnrealSceneComponent } from '@3dsource/angular-unreal-module';
144
122
 
145
123
  @Component({
146
- selector: 'app-root',
147
- standalone: true,
124
+ selector: 'app-stream',
148
125
  imports: [UnrealSceneComponent],
149
- template: ` <app-unreal-scene [isStudio]="false" [useContainerAsSizeProvider]="true" [resolutionSize]="{ width: 1920, height: 1080 }" (changeMouseOverScene)="onHover($event)" /> `,
126
+ template: `<app-unreal-scene />`,
127
+ changeDetection: ChangeDetectionStrategy.OnPush,
150
128
  })
151
- export class AppComponent {
152
- onHover(isOver: boolean) {
153
- // handle mouse over scene
154
- }
155
- }
156
- ```
157
-
158
- Component selector: `<app-unreal-scene>`
159
-
160
- Inputs:
161
-
162
- | Input | Type | Default |
163
- | ---------------------------- | ----------------------------------- | ------------------------------- |
164
- | `isStudio` | `boolean` | `false` |
165
- | `useContainerAsSizeProvider` | `boolean` | `true` |
166
- | `resolutionSize` | `{ width: number; height: number }` | `{ width: 1920, height: 1080 }` |
167
-
168
- Outputs:
169
-
170
- | Output | Type |
171
- | ---------------------- | --------------------------- |
172
- | `changeMouseOverScene` | `OutputEmitterRef<boolean>` |
173
-
174
- ### 4) Send commands / interactions to Unreal
175
-
176
- Inject `UnrealCommunicatorService` to send commands or UI interactions. Types for command packets are provided by `@3dsource/types-unreal`.
177
-
178
- There are three sending methods — choose based on your needs:
179
-
180
- | Method | Description |
181
- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
182
- | `sendCommandToUnreal` | Full pipeline: adds `correlationId`, records telemetry, dispatches NgRx `commandStarted` action. **Recommended for application commands.** |
183
- | `emitUIInteraction` | Sends the packet as a raw `UIInteraction` message. No telemetry or store dispatch. |
184
- | `emitCommand` | Sends the packet as a raw `Command` message (for console commands, resolution changes, etc.). |
185
-
186
- ```ts
187
- import { Component, inject } from '@angular/core';
188
- import { UnrealCommunicatorService } from '@3dsource/angular-unreal-module';
189
- import { MetaBoxCommand } from '@3dsource/types-unreal';
190
- import type { MetaBoxCommandPacket } from '@3dsource/types-unreal';
191
-
192
- @Component({ standalone: true, template: '' })
193
- export class MyComponent {
194
- private unreal = inject(UnrealCommunicatorService);
195
-
196
- sendSomeCommand() {
197
- // Recommended: use sendCommandToUnreal for full telemetry + store tracking
198
- this.unreal.sendCommandToUnreal({
199
- command: MetaBoxCommand.FChangeResolutionCommand,
200
- payload: { resolution: { x: 1920, y: 1080 } },
201
- });
202
- }
203
-
204
- sendRawUIInteraction() {
205
- // Low-level: sends UIInteraction message without telemetry tracking
206
- const packet = {
207
- command: 'CustomCommand',
208
- payload: { key: 'value' },
209
- } as MetaBoxCommandPacket;
210
- this.unreal.emitUIInteraction(packet);
211
- }
212
- }
213
- ```
214
-
215
- ### 5) Store integration (actions & selectors)
216
-
217
- `provideUnrealState()` registers the NgRx feature state `unrealFeature`. You
218
- can dispatch actions and select state in your components:
219
-
220
- ```ts
221
- import { inject } from '@angular/core';
222
- import { Store } from '@ngrx/store';
223
- import { disconnectStream, selectShowLoader, selectTotalProgress, setConfig, setOrchestrationContext, startStream, unrealFeature } from '@3dsource/angular-unreal-module';
224
-
225
- // Dispatch actions
226
- const store = inject(Store);
227
- store.dispatch(startStream({ config: { autoStart: true, warnTimeout: 120 } }));
228
-
229
- // Select state
230
- const progress = store.selectSignal(selectTotalProgress);
231
- const isVideoPlaying = store.selectSignal(unrealFeature.selectIsVideoPlaying);
232
- const dataChannelConnected = store.selectSignal(unrealFeature.selectDataChannelConnected);
233
- ```
234
-
235
- **Key selectors:**
236
-
237
- - `selectTotalProgress` — Scene load progress (0–1 float)
238
- - `selectShowLoader` — Whether loader screen should be visible
239
- - `selectShowReconnectPopup` — Whether reconnect popup should be shown
240
- - `selectIsVideoPlayingAndDataChannelConnected` — Combined readiness check
241
- - `selectStreamConfig` — Current stream configuration
242
- - `unrealFeature.selectCirrusConnected` — Signalling server connection status
243
- - `unrealFeature.selectDataChannelConnected` — Data channel status
244
- - `unrealFeature.selectViewportReady` — Viewport readiness
245
-
246
- **Key actions:**
247
-
248
- - `startStream` — Start streaming with config
249
- - `setConfig` — Update stream configuration
250
- - `setOrchestrationContext` — Set orchestration URLs and environment
251
- - `disconnectStream` — Disconnect with reason
252
- - `destroyUnrealScene` — Full teardown
253
- - `reconnectPeer` — Trigger peer reconnection
254
-
255
- ### 6) Listen for Unreal callbacks
256
-
257
- Inject `UnrealCallbackService` to listen for callback events from Unreal Engine and to observe command responses.
258
-
259
- There are two methods:
260
-
261
- | Method | Description |
262
- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
263
- | `fromUnrealCallback` | Listens for callbacks matching a command or event key. Supports MetaBox commands and custom Unreal callback events. Returns `Observable<UnrealCallbackDescriptor[K]>`. |
264
- | `observeCommandResponse` | Sends a command and waits for its matching response by `correlationId`. Returns `Observable<MetaBoxCommandList[K]>`. Includes timeout and error handling. |
265
-
266
- **Custom Unreal callback events** (defined in `UnrealCallbackEventMap`):
267
-
268
- | Event | Payload Type |
269
- | -------------------------- | --------------------------------------------------- |
270
- | `onSceneState` | `FSceneState` |
271
- | `onFocusObject` | `FProductPayload` |
272
- | `cameraChanged` | `FCameraChangedPayload` |
273
- | `onObjectTransformChanged` | `{ objectName: string; transform: FTransformJson }` |
274
- | `onChangeSequence` | `unknown` |
275
- | `onFinishedSequence` | `unknown` |
276
-
277
- ```ts
278
- import { Component, inject } from '@angular/core';
279
- import { UnrealCallbackService } from '@3dsource/angular-unreal-module';
280
- import { UnrealCommunicatorService } from '@3dsource/angular-unreal-module';
281
- import { MetaBoxCommand } from '@3dsource/types-unreal';
282
-
283
- @Component({ standalone: true, template: '' })
284
- export class MyComponent {
285
- private callbackService = inject(UnrealCallbackService);
286
- private communicator = inject(UnrealCommunicatorService);
287
-
288
- listenForCallbacks() {
289
- // Listen for a MetaBox command callback
290
- this.callbackService.fromUnrealCallback(MetaBoxCommand.FLoadProductCommand).subscribe((data) => console.log('Product loaded:', data));
291
-
292
- // Listen for a custom Unreal event
293
- this.callbackService.fromUnrealCallback('cameraChanged').subscribe((data) => console.log('Camera changed:', data));
294
- }
295
-
296
- sendAndObserve() {
297
- // Send a command and observe its response (with correlationId matching)
298
- this.callbackService
299
- .observeCommandResponse(
300
- { command: MetaBoxCommand.FLoopBackCommand },
301
- (data) => this.communicator.sendCommandToUnreal(data),
302
- 60000, // timeout in ms (default: 60000)
303
- true, // emit on timeout (default: true)
304
- )
305
- .subscribe((response) => console.log('Response:', response));
306
- }
307
- }
129
+ export class StreamComponent {}
308
130
  ```
309
131
 
310
- ## Exported API
311
-
312
- ### Components
313
-
314
- | Component | Selector | Description |
315
- | ------------------------------- | ------------------ | -------------------------------------- |
316
- | `UnrealSceneComponent` | `app-unreal-scene` | Main scene container with video stream |
317
- | `AfkTimeoutModalComponent` | — | AFK timeout warning modal |
318
- | `FreezeFrameComponent` | — | Freeze frame overlay |
319
- | `LowBandwidthModalComponent` | — | Low bandwidth warning modal |
320
- | `LowBandwidthDetectorComponent` | — | Low bandwidth detector |
321
- | `ImageLoadingSrcComponent` | — | Loading image overlay |
322
- | `IntroSrcComponent` | — | Intro image/video overlay |
323
- | `VideoStatsComponent` | — | Video statistics display |
324
- | `StatGraphComponent` | — | Statistics graph |
325
- | `WebrtcErrorModalComponent` | — | WebRTC error modal |
326
-
327
- ### Services
328
-
329
- | Service | Description |
330
- | ------------------------------ | --------------------------------------------------------------- |
331
- | `UnrealCommunicatorService` | Send commands and UI interactions to Unreal |
332
- | `UnrealCallbackService` | Listen for Unreal callback events and observe command responses |
333
- | `AggregatorService` | Aggregates data channel messages from Unreal |
334
- | `SignallingService` | Manages WebSocket signalling connection |
335
- | `VideoService` | Manages video element and stats |
336
- | `WebRtcPlayerService` | Manages WebRTC peer connection |
337
- | `FreezeFrameService` | Handles freeze frame images |
338
- | `AFKService` | AFK (away from keyboard) detection and timeout |
339
- | `DevModeService` | Toggle dev mode for debugging |
340
- | `FileReceiverService` | Receives files from Unreal via data channel |
341
- | `FileHandlerService` | Processes received files |
342
- | `RegionsPingService` | Pings regions to determine latency |
343
- | `CommandTelemetryService` | Records command telemetry |
344
- | `StreamStatusTelemetryService` | Reports stream status telemetry |
345
- | `AnalyticsService` | Analytics event tracking |
346
- | `FpsMonitorService` | FPS monitoring |
347
-
348
- ### Pipes
349
-
350
- | Pipe | Description |
351
- | ---------- | ------------------------------------------------------------ |
352
- | `SafePipe` | Sanitizes a value for the requested Angular security context |
353
-
354
- ### Interfaces
355
-
356
- | Interface | Description |
357
- | -------------------------- | ---------------------------------------------------------------------------- |
358
- | `UnrealInitialConfig` | Shape for `UNREAL_CONFIG` injection token |
359
- | `StreamConfig` | Stream configuration (autoStart, warnTimeout) |
360
- | `StreamResolutionProps` | Stream resolution width/height |
361
- | `UnrealCallbackEventMap` | Custom Unreal callback events pushed from Unreal via data channel |
362
- | `UnrealCallbackDescriptor` | Combined map of MetaBoxCommandList and UnrealCallbackEventMap callback types |
363
-
364
- ## Features
365
-
366
- - Standalone Unreal Scene Component
367
- - Command and UI Interaction API via `UnrealCommunicatorService`
368
- - Callback listener and command response observer via `UnrealCallbackService`
369
- - Event-driven status UI (freeze frame, video stats, play overlay, AFK, low bandwidth)
370
- - NgRx-powered state management and effects
371
- - **Required** initial configuration via `UNREAL_CONFIG` injection token
372
- - Auto-reconnection on WebRTC/DataChannel failures (configurable)
373
- - File receiving from Unreal Engine via data channel
374
- - Analytics and FPS monitoring
375
- - Playwright testing mode with mock service substitution
376
-
377
- ## Examples
378
-
379
- Check the demo application for complete usage examples:
380
-
381
- ```shell
382
- pnpm demo:start
132
+ `UnrealSceneComponent` also accepts `isStudio`,
133
+ `useContainerAsSizeProvider` and `resolutionSize` inputs, and emits
134
+ `changeMouseOverScene`.
135
+
136
+ ## Main API
137
+
138
+ - `provideUnrealState()` — registers the `unrealFeature` NgRx state.
139
+ - `provideUnrealModule()` — registers effects and boots streaming services.
140
+ - `UnrealSceneComponent` — renders and manages the Pixel Streaming scene.
141
+ - `UnrealCommunicatorService` — sends commands and UI interactions.
142
+ - `UnrealCallbackService` — observes Unreal callbacks and command responses.
143
+ - `unrealFeature`, exported selectors and actions — expose connection and scene
144
+ lifecycle state.
145
+ - `FileReceiverService` and `FileHandlerService` — receive files over the
146
+ DataChannel.
147
+
148
+ Command packet types are provided by `@3dsource/types-unreal`.
149
+
150
+ Run `pnpm demo:start` from the repository root to see the scene component in
151
+ the demo application.
152
+
153
+ ## Optional prefetch scripts
154
+
155
+ The package publishes two dependency-free scripts for use in the document
156
+ `<head>` before Angular starts:
157
+
158
+ - `region-ping-prefetch.js` measures regions early and caches the closest one.
159
+ - `stream-prefetch.js` opens and parks an eligible WebRTC connection so Angular
160
+ can adopt it after bootstrap.
161
+
162
+ <!-- prettier-ignore -->
163
+ ```html
164
+ <script
165
+ src="https://cdn.jsdelivr.net/npm/@3dsource/angular-unreal-module/js/region-ping-prefetch.js"
166
+ async
167
+ ></script>
168
+ <script
169
+ src="https://cdn.jsdelivr.net/npm/@3dsource/angular-unreal-module/js/stream-prefetch.js"
170
+ async
171
+ ></script>
383
172
  ```
384
173
 
385
- See also: `projects/demo/src/app/demo-layout/info-pages/unreal-scene-demo/constants/unreal.routes.ts` for a real-world provider configuration example.
174
+ `stream-prefetch.js` runs by default only on
175
+ `metabox-configurator/{modular|basic}/...` routes. It reads the same-origin
176
+ `assets/config.json`; override that path with `data-config-url` when needed.
177
+ Pin an exact package version in production when deterministic CDN assets are
178
+ required.
386
179
 
387
180
  ## Repository development
388
181
 
389
- Run package commands from the repository root:
182
+ Run commands from the repository root:
390
183
 
391
184
  ```shell
392
185
  pnpm unreal:build
@@ -398,25 +191,22 @@ pnpm unreal:test:signalling
398
191
  pnpm unreal:test:signalling:leaks
399
192
  ```
400
193
 
401
- `unreal:build` builds `source-ui-native`, `types-unreal` and `utils` before this
402
- package. `unreal:build:watch` performs that initial build automatically and then
403
- watches `angular-unreal-module`.
194
+ `unreal:build` builds the local `source-ui-native`, `types-unreal` and `utils`
195
+ dependencies before this package.
404
196
 
405
- To version and publish:
197
+ Release commands publish only this package:
406
198
 
407
199
  ```shell
408
200
  pnpm unreal:release:patch
409
201
  pnpm unreal:release:dev
410
202
  ```
411
203
 
412
- The release command publishes to npm. After verifying the npm release, purge
413
- the matching jsDelivr tag explicitly:
204
+ After a successful publish, the release automatically purges the matching
205
+ jsDelivr tag. Retry a failed purge without rerunning the release:
414
206
 
415
207
  ```shell
416
208
  pnpm unreal:purge-cdn -- latest
209
+ pnpm unreal:purge-cdn -- dev
417
210
  ```
418
211
 
419
- ## Engine requirements
420
-
421
- - Node.js: >=24.16.0
422
- - pnpm: 11.17.0 (activated from the root `packageManager` field via Corepack)
212
+ Repository tooling requires Node.js 24.16.0 or newer and pnpm 11.17.0.