Skip to content

feat: add @absaoss-cps/ui-watchtower, the framework-independent core package - #34

Closed
fateeand wants to merge 21 commits into
mainfrom
43-create-ui-watchtower-framework-agnostic-core-engine-package
Closed

fateeand wants to merge 21 commits into
mainfrom
43-create-ui-watchtower-framework-agnostic-core-engine-package

Conversation

@fateeand

@fateeand fateeand commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

UI Watchtower now ships as two packages from this repository:

  • @absaoss-cps/ui-watchtower (new). The telemetry engine: scenarios, BI events, logging, redaction and the shell/fragment broadcast protocol. It needs no framework and is used directly from plain JavaScript or TypeScript or any framework's app.
  • @absaoss-cps/ngx-ui-watchtower. Now a thin Angular adapter over the core: providers, injectable services and Observables around the same engine. It installs the core as a regular dependency.

There is one implementation of the engine and the protocol, in the core. The Angular package delegates to it.

Changes

Core (projects/ui-watchtower)

  • uwtCreateTelemetry(options) creates an engine with scenarios, bi, logging, setUserId, flush and an idempotent dispose(). Every call fails open; a missing identity (application, environment and version as strings), destination or log store throws when the engine is created.
  • Browser features are opt-in through document (the global one by default, null for none). Nothing touches window or document on import, and there is no global engine state.
  • onSettled(listener) replaces the Observable inside the core, so the main entry point needs no RxJS.
  • dispose() stops watching the page, settles in-flight scenarios as abandoned, and flushes. That is all it changes: something may still run while an application shuts down, and the destination and log store are the caller's, so scenarios, BI events and logs started afterwards work as before.
  • The broadcast sink and log provider can share one connection. enabled: false wins over a shared connection, and so does dispose(): they send nothing, a disposed provider answers queries with [] at once, and the connection stays open for its other users.
  • A runOutside that hands nothing back no longer runs a scheduled callback twice (the core and the Angular helper).
  • Entry points: . (engine, contracts, no-op and broadcast destinations), /rum (the AWS RUM sink; aws-rum-web is an optional peer, loaded lazily) and /rxjs (traceScenario; rxjs is an optional peer).
  • Typed names: applications declare them on @absaoss-cps/ui-watchtower.

Angular adapter (projects/ngx-ui-watchtower)

  • The services each wrap the core class for their concern, built from DI. The sinks, the host, the log provider and /rum's sink are @Injectable subclasses of the core's.
  • settled$ listens to the core only while it has subscribers, as before the extraction: no record is copied when nobody reads it.
  • In 'broadcast' mode, the sink and the log provider share one channel per injector.

Release and CI

  • Each package has its own version and changelog. A change to the adapter releases only the adapter; a core release also releases the adapter as a patch, with its dependency moved to ^ the new core (Release Please's node-workspace plugin). One release PR holds every package with changes.
  • The core's first release is 0.1.0 (initial-version); the adapter continues from 0.4.0.
  • Tags become ui-watchtower-v… and ngx-ui-watchtower-v…; the old v… tags stay.
  • scripts/publish-packages.mjs publishes what was released, the core before the adapter. It skips versions npm already has, and stops before the adapter if the core fails. Rerunning the workflow on a release tag finishes a partial release.
  • New checks:
    • npm run api:check: public API snapshots;
    • npm run smoke: the packed packages installed in a plain Node app, a TypeScript app and an unchanged Angular app;
    • npm run release:check: Release Please replayed on the config: the first release, adapter-only and core changes, and the tags merging creates;
    • npm run test:coverage fails below 90% on any metric, in either package (the core is at 98% statements, 95% branches);
    • npm run test:release: the publish script and the workflow wiring.

Tests

  • Direct tests of the core (the RUM sink, the broadcast host and sink, UwtScenario, the engine) raise its coverage from 80% to 98% statements; the review fixes each have a test that fails without them.
  • A spec runs the /rum entry's private copies of the Angular helpers against the main entry's, so they cannot drift apart.

Docs

  • The root README presents both packages as equals; the core README leads with direct use, no adapter needed; docs/ADAPTERS.md guides writing an adapter for another framework.
  • DESIGN.md and the Angular README are updated, including a note not to install a core version the Angular package doesn't accept, or npm keeps two copies.

Logos

  • The repository and the core use "UI Watchtower", and the Angular package keeps "Angular UI Watchtower".

Compatibility

  • Angular apps need no change. Imports, providers, services, /rum, settled$, traceScenario and declare module '@absaoss-cps/ngx-ui-watchtower' name declarations all work as before.
  • The adapter's API snapshot changed in two places only:
    • four classes gained dispose();
    • UwtTelemetrySink is now the core's plain abstract class, so it lost its Angular factory metadata. It is still the destination token and the base class for custom sinks.
  • Known limitation, unchanged from 0.4.0: when an Angular app is destroyed while its page lives on (a fragment unmounted by its shell), the services' final messages, such as an in-flight scenario settled as abandoned and the last log flush, are dropped, because Angular destroys the sink and the channel first. Tracked in Fragment teardown drops in-flight scenario records and the last log flush: the broadcast channel closes before the services #36.
  • The broadcast protocol is unchanged. Every pairing of an Angular or core shell with an Angular or core fragment matches the message fixture recorded before the extraction, so separately deployed shells and fragments needn't upgrade together.

Before merging the release PR

Squash-merge this PR with its feat: title: the release PR then proposes the core at 0.1.0 and the adapter at 0.5.0.

npm sets up trusted publishing only for a package that exists. Before merging that release PR, an @absaoss-cps owner publishes the core at 0.1.0 once by hand, from the release PR's branch, then adds its trusted publisher (GitHub Actions, AbsaOSS/ui-watchtower, workflow release-please.yml). The workflow then finds 0.1.0 on npm, skips it, and publishes the adapter. The exact commands are in the README's "Releases" section. If the release PR is merged first, publishing stops at the core; publish it from the ui-watchtower-v0.1.0 tag, then rerun the workflow on the release tag.

Closes #35

@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Core coverage report

St.❔
Category Percentage Covered / Total
🟢 Statements 98.61% 1344/1363
🟢 Branches 94.95% 808/851
🟢 Functions 98.52% 332/337
🟢 Lines 98.87% 1318/1333

Test suite run success

419 tests passing in 22 suites.

Report generated by 🧪jest coverage report action from 380e134

@fateeand fateeand linked an issue Nov 10, 2026 that may be closed by this pull request
8 tasks
@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Ngx coverage report

St.❔
Category Percentage Covered / Total
🟢 Statements
99.23% (+2.09% 🔼)
258/260
🟢 Branches
96.67% (+1.23% 🔼)
58/60
🟢 Functions
98.61% (+1.52% 🔼)
71/72
🟢 Lines
99.57% (+2.1% 🔼)
234/235
Show files with reduced coverage 🔻
St.❔
File Statements Branches Functions Lines
🟢
... / uwt-telemetry-common.providers.ts
98.08% (-0.04% 🔻)
96% (-0.77% 🔻)
100%
98.04% (-0.04% 🔻)
🟢
... / uwt-rum-telemetry.sink.ts
100% (+3.5% 🔼)
50% (-43.37% 🔻)
100% (+4.76% 🔼)
100% (+2.51% 🔼)

Test suite run success

544 tests passing in 14 suites.

Report generated by 🧪jest coverage report action from 380e134

@AbsaOSS AbsaOSS deleted a comment from github-actions Bot Nov 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Disabled broadcast components still send traffic over shared connections, while missing validation for required identity fields allows incomplete configurations that cause silent log loss.

3 open issues
What changed in this PR

Extracts the shared telemetry engine into @absaoss-cps/ui-watchtower, enabling non-Angular applications and future framework adapters as requested in #35. The existing Angular package becomes an adapter over that core.

Changes:

  • Introduces the framework-independent core with lifecycle controls, broadcast support, and optional RUM and RxJS entry points.
  • Delegates Angular services and providers to the core while preserving existing imports and typed name declarations.
  • Adds two-package build and release support, API snapshots, consumer smoke checks, coverage thresholds, and adapter documentation.
File Description
tsconfig.json Adds core import mappings and project references.
scripts/​smoke/​core-ts/​nodom.ts Checks core usage without DOM typings.
scripts/​smoke/​core-node/​consumer.mjs Exercises installed core without frameworks or browser globals.
scripts/​smoke/​angular/​variants/​shell.app.config.ts Configures an Angular shell smoke consumer.
scripts/​smoke/​angular/​variants/​fragment.app.config.ts Configures an Angular broadcast fragment consumer.
scripts/​smoke/​angular/​tsconfig.json Configures strict Angular consumer compilation.
scripts/​smoke/​angular/​src/​telemetry.schema.ts Checks existing Angular name augmentation.
scripts/​smoke/​angular/​src/​main.ts Bootstraps the browser smoke application.
scripts/​smoke/​angular/​src/​main.server.ts Bootstraps the server smoke application.
scripts/​smoke/​angular/​src/​index.html Supplies the smoke application's HTML entry.
scripts/​smoke/​angular/​src/​app/​home.ts Exercises telemetry services during rendering.
scripts/​smoke/​angular/​src/​app/​app.ts Hosts routed smoke content.
scripts/​smoke/​angular/​src/​app/​app.routes.ts Routes to the telemetry smoke component.
scripts/​smoke/​angular/​src/​app/​app.routes.server.ts Enables prerendering for smoke routes.
scripts/​smoke/​angular/​src/​app/​app.config.server.ts Composes server-rendering providers.
scripts/​smoke/​angular/​angular.json Configures browser and prerendered consumer builds.
scripts/​publish-packages.mjs Publishes tagged versions in dependency order.
scripts/​api-snapshot.mjs Compares built public APIs with snapshots.
release-please-config.json Configures independent package releases and dependency updates.
projects/​ui-watchtower/​type-tests/​tsconfig.json Configures core compatibility and empty-registry checks.
projects/​ui-watchtower/​type-tests/​tsconfig.augmented.json Isolates augmented core registry checks.
projects/​ui-watchtower/​type-tests/​empty-registry.ts Checks unrestricted names before augmentation.
projects/​ui-watchtower/​type-tests/​augmented.ts Checks registered names across core APIs.
projects/​ui-watchtower/​tsconfig.spec.json Configures core and optional-entry-point tests.
projects/​ui-watchtower/​tsconfig.lib.prod.json Configures production package declarations.
projects/​ui-watchtower/​tsconfig.lib.json Configures core library compilation.
projects/​ui-watchtower/​src/​public-api.ts Exposes the framework-independent core API.
projects/​ui-watchtower/​src/​public-api.spec.ts Covers core operation without browser globals.
projects/​ui-watchtower/​src/​lib/​utils/​uwt-user-timings-internal.util/​uwt-user-timings-internal.util.ts Provides framework-independent timing helpers.
projects/​ui-watchtower/​src/​lib/​utils/​uwt-user-timings-internal.util/​uwt-user-timings-internal.util.spec.ts Opts timing tests into jsdom.
projects/​ui-watchtower/​src/​lib/​utils/​uwt-telemetry-event.util/​uwt-telemetry-event.util.ts Classifies telemetry payloads by event type.
projects/​ui-watchtower/​src/​lib/​utils/​uwt-telemetry-event.util/​uwt-telemetry-event.util.spec.ts Covers classification and namespace handling.
projects/​ui-watchtower/​src/​lib/​utils/​uwt-session-start.util/​uwt-session-start.util.ts Documents the retained storage key for compatibility.
projects/​ui-watchtower/​src/​lib/​utils/​uwt-session-start.util/​uwt-session-start.util.spec.ts Opts session-storage tests into jsdom.
projects/​ui-watchtower/​src/​lib/​utils/​uwt-debug-flag.util/​uwt-debug-flag.util.ts Passes reporters through debug-write suppression.
projects/​ui-watchtower/​src/​lib/​utils/​uwt-debug-flag.util/​uwt-debug-flag.util.spec.ts Opts debug-flag tests into jsdom.
projects/​ui-watchtower/​src/​lib/​sinks/​uwt-telemetry/​uwt-telemetry-abstract.sink/​uwt-telemetry-abstract.sink.ts Removes Angular metadata from the sink contract.
projects/​ui-watchtower/​src/​lib/​sinks/​uwt-telemetry/​uwt-noop-telemetry.sink/​uwt-noop-telemetry.sink.ts Provides the core no-op destination.
projects/​ui-watchtower/​src/​lib/​sinks/​uwt-telemetry/​uwt-noop-telemetry.sink/​uwt-noop-telemetry.sink.spec.ts Covers the no-op destination contract.
projects/​ui-watchtower/​src/​lib/​sinks/​uwt-broadcast/​uwt-broadcast-telemetry.sink.ts Provides broadcast forwarding and connection ownership.
projects/​ui-watchtower/​src/​lib/​scenario/​uwt-scenario/​uwt-scenario.spec.ts Opts scenario tests into jsdom.
projects/​ui-watchtower/​src/​lib/​providers/​uwt-noop-log-api.provider/​uwt-noop-log-api.provider.ts Provides a framework-independent no-op log store.
projects/​ui-watchtower/​src/​lib/​providers/​uwt-noop-log-api.provider/​uwt-noop-log-api.provider.spec.ts Covers discarded logs and empty query results.
projects/​ui-watchtower/​src/​lib/​providers/​uwt-log-api.provider/​uwt-log-api.provider.ts Defines shared log-store and query contracts.
projects/​ui-watchtower/​src/​lib/​providers/​uwt-broadcast-log-api.provider/​uwt-broadcast-log-api.provider.ts Forwards logs and queries over shared connections.
projects/​ui-watchtower/​src/​lib/​models/​uwt-telemetry-common.models/​uwt-telemetry-common.models.ts Defines shared telemetry types and event names.
projects/​ui-watchtower/​src/​lib/​models/​uwt-telemetry-common.models/​uwt-telemetry-common.models.spec.ts Covers event namespaces and defaults.
projects/​ui-watchtower/​src/​lib/​models/​uwt-scenario.models/​uwt-scenario.models.ts Documents core and Angular registry augmentation.
projects/​ui-watchtower/​src/​lib/​models/​uwt-log.models/​uwt-log.models.ts Updates logging documentation for the shared core.
projects/​ui-watchtower/​src/​lib/​models/​uwt-bi.models/​uwt-bi.models.ts Documents shared business-event registry augmentation.
projects/​ui-watchtower/​src/​lib/​engine/​uwt-telemetry/​uwt-telemetry.ts Creates and disposes the unified telemetry engine.
projects/​ui-watchtower/​src/​lib/​engine/​uwt-telemetry-page/​uwt-telemetry-page.ts Abstracts document access and page lifecycle listeners.
projects/​ui-watchtower/​src/​lib/​config/​uwt-telemetry-common.config/​uwt-telemetry-common.config.ts Centralizes identity, defaults, and configuration resolution.
projects/​ui-watchtower/​src/​lib/​config/​uwt-telemetry-common.config/​uwt-telemetry-common.config.spec.ts Covers shared configuration defaults.
projects/​ui-watchtower/​src/​lib/​config/​uwt-scenario-telemetry.config/​uwt-scenario-telemetry.config.ts Defines shared scenario settings.
projects/​ui-watchtower/​src/​lib/​config/​uwt-scenario-telemetry.config/​uwt-scenario-telemetry.config.spec.ts Covers scenario configuration defaults.
projects/​ui-watchtower/​src/​lib/​config/​uwt-log.config/​uwt-log.config.ts Defines shared logging settings.
projects/​ui-watchtower/​src/​lib/​config/​uwt-log.config/​uwt-log.config.spec.ts Covers logging configuration defaults.
projects/​ui-watchtower/​src/​lib/​config/​uwt-bi-telemetry.config/​uwt-bi-telemetry.config.ts Defines shared business-event settings.
projects/​ui-watchtower/​src/​lib/​config/​uwt-bi-telemetry.config/​uwt-bi-telemetry.config.spec.ts Covers business-event configuration defaults.
projects/​ui-watchtower/​rxjs/​tsconfig.lib.json Configures the optional RxJS entry point.
projects/​ui-watchtower/​rxjs/​src/​public-api.ts Exposes the RxJS integration separately.
projects/​ui-watchtower/​rxjs/​src/​lib/​uwt-scenario-operators/​uwt-scenario-operators.ts Implements shared observable scenario settlement.
projects/​ui-watchtower/​rxjs/​ng-package.json Packages the RxJS secondary entry point.
projects/​ui-watchtower/​rum/​tsconfig.lib.json Configures the optional RUM entry point.
projects/​ui-watchtower/​rum/​src/​public-api.ts Exposes the RUM sink and credential contracts.
projects/​ui-watchtower/​rum/​src/​lib/​uwt-rum-telemetry.sink/​uwt-rum-telemetry.sink.spec.ts Covers RUM startup, reporting, and disposal.
projects/​ui-watchtower/​rum/​ng-package.json Packages the RUM secondary entry point.
projects/​ui-watchtower/​package.json Declares the core package and optional peers.
projects/​ui-watchtower/​ng-package.json Defines core package output.
projects/​ui-watchtower/​jest.config.js Adds framework-free tests and coverage thresholds.
projects/​ngx-ui-watchtower/​type-tests/​tsconfig.json Configures Angular consumer compatibility checks.
projects/​ngx-ui-watchtower/​type-tests/​tsconfig.augmented.json Isolates augmented Angular registry checks.
projects/​ngx-ui-watchtower/​type-tests/​empty-registry.ts Checks Angular name fallbacks without augmentation.
projects/​ngx-ui-watchtower/​type-tests/​augmented.ts Checks existing Angular registry declarations.
projects/​ngx-ui-watchtower/​tsconfig.spec.json Allows adapter tests to include core sources.
projects/​ngx-ui-watchtower/​tsconfig.lib.json Resolves core imports against built declarations.
projects/​ngx-ui-watchtower/​src/​public-api.ts Re-exports shared contracts while preserving Angular exports.
projects/​ngx-ui-watchtower/​src/​lib/​utils/​uwt-telemetry-safe.util/​uwt-telemetry-safe.util.ts Supplies Angular platform, zone, and reporting helpers.
projects/​ngx-ui-watchtower/​src/​lib/​utils/​uwt-telemetry-safe.util/​uwt-telemetry-safe.util.spec.ts Retains adapter-specific suppression tests.
projects/​ngx-ui-watchtower/​src/​lib/​sinks/​uwt-telemetry/​uwt-noop-telemetry.sink/​uwt-noop-telemetry.sink.ts Makes the core no-op sink injectable.
projects/​ngx-ui-watchtower/​src/​lib/​sinks/​uwt-broadcast/​uwt-broadcast.wire.fixture.json Records the broadcast compatibility fixture.
projects/​ngx-ui-watchtower/​src/​lib/​sinks/​uwt-broadcast/​uwt-broadcast-telemetry.sink.ts Wraps the core broadcast sink with Angular injection.
projects/​ngx-ui-watchtower/​src/​lib/​services/​uwt-scenario-telemetry.service/​uwt-scenario-telemetry.service.spec.ts Checks teardown and subscriber-dependent record copying.
projects/​ngx-ui-watchtower/​src/​lib/​services/​uwt-logger.service/​uwt-logger.service.spec.ts Updates logging tests to target shared core logic.
projects/​ngx-ui-watchtower/​src/​lib/​services/​uwt-bi-telemetry.service/​uwt-bi-telemetry.service.ts Delegates business-event tracking to the core.
projects/​ngx-ui-watchtower/​src/​lib/​services/​uwt-bi-telemetry.service/​uwt-bi-telemetry.service.spec.ts Uses core contracts in adapter tracking tests.
projects/​ngx-ui-watchtower/​src/​lib/​scenario/​uwt-scenario-operators/​uwt-scenario-operators.ts Delegates RxJS integration with Angular reporting.
projects/​ngx-ui-watchtower/​src/​lib/​providers/​uwt-telemetry-common.providers/​uwt-telemetry-enabled.spec.ts Uses core contracts in disabled-mode tests.
projects/​ngx-ui-watchtower/​src/​lib/​providers/​uwt-telemetry-common.providers/​uwt-telemetry-common.providers.ts Uses shared defaults and provides one broadcast connection.
projects/​ngx-ui-watchtower/​src/​lib/​providers/​uwt-telemetry-common.providers/​uwt-telemetry-common.providers.spec.ts Updates provider tests to shared contracts.
projects/​ngx-ui-watchtower/​src/​lib/​providers/​uwt-noop-log-api.provider/​uwt-noop-log-api.provider.ts Makes the core no-op log provider injectable.
projects/​ngx-ui-watchtower/​src/​lib/​providers/​uwt-log-api.provider/​uwt-log-api.provider.ts Keeps the Angular token for the core log contract.
projects/​ngx-ui-watchtower/​src/​lib/​providers/​uwt-broadcast-log-api.provider/​uwt-broadcast-log-api.provider.ts Wraps core broadcast logging with Angular zone hooks.
projects/​ngx-ui-watchtower/​src/​lib/​providers/​uwt-broadcast-log-api.provider/​uwt-broadcast-log-api.provider.spec.ts Uses core log records in adapter tests.
projects/​ngx-ui-watchtower/​src/​lib/​config/​uwt-telemetry-common.config/​uwt-telemetry-common.config.ts Retains Angular tokens for shared configuration types.
projects/​ngx-ui-watchtower/​src/​lib/​config/​uwt-scenario-telemetry.config/​uwt-scenario-telemetry.config.ts Retains the scenario configuration injection token.
projects/​ngx-ui-watchtower/​src/​lib/​config/​uwt-log.config/​uwt-log.config.ts Retains the logging configuration injection token.
projects/​ngx-ui-watchtower/​src/​lib/​config/​uwt-bi-telemetry.config/​uwt-bi-telemetry.config.ts Retains the business-event configuration injection token.
projects/​ngx-ui-watchtower/​rum/​tsconfig.spec.json Allows RUM tests to include core sources.
projects/​ngx-ui-watchtower/​rum/​tsconfig.lib.json Expands RUM type-checking scope for core sources.
projects/​ngx-ui-watchtower/​rum/​src/​lib/​uwt-rum-telemetry.sink/​uwt-rum-telemetry.sink.spec.ts Uses the core session utility in RUM tests.
projects/​ngx-ui-watchtower/​rum/​src/​lib/​uwt-rum-internal.util/​uwt-rum-internal.util.ts Keeps only Angular-specific RUM helpers.
projects/​ngx-ui-watchtower/​README.md Explains the adapter relationship and compatibility.
projects/​ngx-ui-watchtower/​package.json Adds the core as a regular dependency.
projects/​ngx-ui-watchtower/​ng-package.json Allows the core dependency in adapter packaging.
package.json Coordinates two-package builds and verification commands.
package-lock.json Records the direct core test-transformer dependency.
jest.config.js Scopes Angular tests and enforces coverage thresholds.
docs/​ADAPTERS.md Documents adapter wiring, lifecycle, testing, and releases.
angular.json Registers the core library build.
.gitignore Ignores API snapshot mismatch output.
.github/​workflows/​release-please.yml Publishes both packages and supports release retries.
.github/​workflows/​checks.yml Adds core coverage, API, smoke, and release checks.
.eslintrc.js Enforces framework and optional-peer import boundaries.

Review effort: Max

@fateeand

Copy link
Copy Markdown
Collaborator Author

Split into a stack of 8 PRs under the epic #35, to be merged bottom first: #45, #46, #47, #48, #49, #50, #51, #52. The last one is identical to this PR's head.

@fateeand fateeand closed this Nov 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create ui-watchtower framework-agnostic core engine package

2 participants