Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
3fff549
feat(implementations): scaffold Angular Web SDK reference implementation
nalchevanidze Jun 8, 2026
00b4e71
chore(implementations): remove premature angular-web-sdk root shortcu…
nalchevanidze Jun 8, 2026
0992f18
docs(angular-web-sdk): remove project structure section from README
nalchevanidze Jun 8, 2026
3a57bc5
chore(implementations): remove angular-web-sdk launch script
nalchevanidze Jun 8, 2026
8d507f0
docs(angular-web-sdk): add REQUIREMENTS.md with acceptance criteria
nalchevanidze Jun 8, 2026
4952282
feat(angular-web-sdk): scaffold app shell and config token
nalchevanidze Jun 8, 2026
25e996a
chore(angular-web-sdk): drop Angular role suffixes from files and cla…
nalchevanidze Jun 8, 2026
76b7692
chore(angular-web-sdk): add global styles and active nav highlighting
nalchevanidze Jun 8, 2026
bbe66a2
feat(angular-web-sdk): SDK initialisation and page tracking
nalchevanidze Jun 8, 2026
8e1a08e
feat(angular-web-sdk): Contentful CDA client with locale consistency …
nalchevanidze Jun 8, 2026
dcd3312
feat(angular-web-sdk): entry resolution, auto/manual tracking, click …
nalchevanidze Jun 8, 2026
84d5937
chore(angular-web-sdk): improve page layout, spacing, and variant vis…
nalchevanidze Jun 8, 2026
8589f16
feat(angular-web-sdk): consent toggle UI and SDK wiring (feature 7)
nalchevanidze Jun 8, 2026
91b7fcf
feat(angular-web-sdk): identify/reset UI and SDK wiring (feature 8)
nalchevanidze Jun 8, 2026
4310b3a
feat(angular-web-sdk): live updates global toggle and per-entry overr…
nalchevanidze Jun 9, 2026
1a6b955
chore(angular-web-sdk): mark features 9-10 done in REQUIREMENTS.md
nalchevanidze Jun 9, 2026
a10139c
feat(angular-web-sdk): multi-route PageTwo and shared ControlPanel (f…
nalchevanidze Jun 9, 2026
1dfea8f
chore(angular-web-sdk): mark feature 17 done in REQUIREMENTS.md
nalchevanidze Jun 9, 2026
402e801
feat(angular-web-sdk): analytics event display in persistent sidebar …
nalchevanidze Jun 9, 2026
dfa684e
chore(angular-web-sdk): mark feature 15 done in REQUIREMENTS.md
nalchevanidze Jun 9, 2026
e15b5da
chore(angular-web-sdk): move analytics sidebar to left, fix grid layout
nalchevanidze Jun 9, 2026
a37664e
feat(angular-web-sdk): feature flag subscription with auto-tracked vi…
nalchevanidze Jun 9, 2026
df2cf6f
chore(angular-web-sdk): mark feature 19 done in REQUIREMENTS.md
nalchevanidze Jun 9, 2026
75ad649
docs(angular-web-sdk): add visual verification steps for feature 19 i…
nalchevanidze Jun 9, 2026
7aa34ad
docs(angular-web-sdk): simplify feature 19 verification steps
nalchevanidze Jun 9, 2026
88bd7d8
feat(angular-web-sdk): rich text rendering, merge tags, and card layo…
nalchevanidze Jun 9, 2026
716a2e8
feat(angular-web-sdk): nested entries recursive resolution (feature 12)
nalchevanidze Jun 9, 2026
fd3e53d
feat(angular-web-sdk): preview panel lazy-load and live-all-entries (…
nalchevanidze Jun 9, 2026
68bb906
docs(angular-web-sdk): strip progress table and env vars from REQUIRE…
nalchevanidze Jun 9, 2026
b5dc30d
docs(angular-web-sdk): add visual verification steps to all requirements
nalchevanidze Jun 9, 2026
bba7cab
feat(angular-web-sdk): toggle Identify/Reset button based on identifi…
nalchevanidze Jun 9, 2026
61bdb5b
docs(angular-web-sdk): fix requirements soundness issues from audit
nalchevanidze Jun 9, 2026
bc37e9a
feat(angular-web-sdk): remove redundant event type from analytics eve…
nalchevanidze Jun 9, 2026
82223b6
feat(angular-web-sdk): prefix entry IDs with 'id:' in analytics event…
nalchevanidze Jun 9, 2026
07412e6
feat(angular-web-sdk): prefix page URL with 'url:' in analytics event…
nalchevanidze Jun 9, 2026
6f084a2
chore(angular-web-sdk): remove blue left border from section headers
nalchevanidze Jun 9, 2026
2c54e7c
fix(angular-web-sdk): hide zero duration from analytics event labels
nalchevanidze Jun 9, 2026
1ad2a8f
fix(angular-web-sdk): always prefix componentId with 'id:' in event l…
nalchevanidze Jun 9, 2026
ba5a497
feat(angular-web-sdk): inline stat+button rows in control panel
nalchevanidze Jun 9, 2026
60b2cab
feat(angular-web-sdk): control panel table layout, port 4200, updated…
nalchevanidze Jun 9, 2026
49c66e5
feat(angular-web-sdk): control panel and UI polish
nalchevanidze Jun 9, 2026
190064e
chore(angular-web-sdk): rename Raw to Raw Events to match React imple…
nalchevanidze Jun 9, 2026
7d9ef00
chore(angular-web-sdk): rename Events to Unique Events in analytics d…
nalchevanidze Jun 9, 2026
4fbba75
chore(angular-web-sdk): rename event counters to Unique/Total
nalchevanidze Jun 9, 2026
32ecdf5
chore(angular-web-sdk): move Live updates section to first column on …
nalchevanidze Jun 9, 2026
5a42f44
chore(angular-web-sdk): increase spacing between SDK panel and conten…
nalchevanidze Jun 9, 2026
9a1eb00
chore(angular-web-sdk): fix section spacing via display:contents on p…
nalchevanidze Jun 9, 2026
cb5a8ac
refactor(angular-web-sdk): consolidate selectedOptimizations and shar…
nalchevanidze Jun 9, 2026
cb9bdcc
refactor(angular-web-sdk): extract Angular SDK integration layer to s…
nalchevanidze Jun 9, 2026
f321072
refactor(angular-web-sdk): make sdk/ fully generic with no app/ imports
nalchevanidze Jun 9, 2026
8d7108e
refactor(angular-web-sdk): rename SDK classes to Ng-prefixed convention
nalchevanidze Jun 9, 2026
2205886
refactor(angular-web-sdk): move ContentfulClient into sdk/ as NgConte…
nalchevanidze Jun 9, 2026
1ee547a
refactor(angular-web-sdk): organise sdk/ into services/ and utils/ su…
nalchevanidze Jun 9, 2026
9ae6a00
refactor(angular-web-sdk): extract resolveWithMeta and NgContentfulLi…
nalchevanidze Jun 9, 2026
694fda1
fix(angular-web-sdk): restore globalLiveUpdates property name on NgCo…
nalchevanidze Jun 9, 2026
57b1169
fix(angular-web-sdk): fix sed-mangled method name and meta nullabilit…
nalchevanidze Jun 9, 2026
3345c49
refactor(angular-web-sdk): replace individual computed signals with @…
nalchevanidze Jun 9, 2026
f6de14f
refactor(angular-web-sdk): replace individual computed signals with @…
nalchevanidze Jun 9, 2026
047b83e
refactor(angular-web-sdk): inline toggle call and privatize selectedO…
nalchevanidze Jun 9, 2026
aa0e6ee
refactor(angular-web-sdk): simplify control-panel signals by collapsi…
nalchevanidze Jun 9, 2026
c36d8d0
refactor(angular-web-sdk): move sections into components and remove u…
nalchevanidze Jun 9, 2026
5c0ee57
refactor(angular-web-sdk): collapse trackArrival wrapper and use arro…
nalchevanidze Jun 9, 2026
d155d31
refactor(angular-web-sdk): extract ContentCard, move config/utils, ap…
nalchevanidze Jun 9, 2026
11d5ce2
refactor(angular-web-sdk): inline type guards, import isRecord from S…
nalchevanidze Jun 9, 2026
eb892c3
refactor(angular-web-sdk): consolidate entry IDs into ENTRIES config …
nalchevanidze Jun 9, 2026
699b316
fix(angular-web-sdk): move sidebar before main so it renders on the left
nalchevanidze Jun 9, 2026
7a27667
chore(angular-web-sdk): remove redundant live-updates subtitle and ne…
nalchevanidze Jun 9, 2026
237287d
refactor(angular-web-sdk): unique/raw toggle buttons, precompute even…
nalchevanidze Jun 9, 2026
c27bdad
refactor(angular-web-sdk): inline NestedContentEntry into ContentCard…
nalchevanidze Jun 9, 2026
21e03b4
refactor(angular-web-sdk): consolidate content-card folder and add in…
nalchevanidze Jun 9, 2026
8f7ad43
refactor(angular-web-sdk): move rich-text-renderer into content-card …
nalchevanidze Jun 9, 2026
e7bbf7c
refactor(angular-web-sdk): rename components, consolidate content-car…
nalchevanidze Jun 9, 2026
8965c14
refactor(angular-web-sdk): expose selectedOptimizations signal on ser…
nalchevanidze Jun 9, 2026
fc2a321
refactor(angular-web-sdk): default resolveWithMeta to global selected…
nalchevanidze Jun 9, 2026
e37df22
refactor(angular-web-sdk): move all live-entry logic into NgContentfu…
nalchevanidze Jun 9, 2026
91709be
refactor(angular-web-sdk): make preview panel selectors configurable,…
nalchevanidze Jun 9, 2026
ea7b43f
refactor(angular-web-sdk): use _ convention for unused inject and rem…
nalchevanidze Jun 9, 2026
93a03f5
fix(angular-web-sdk): call inject in constructor to avoid TS6133 on u…
nalchevanidze Jun 9, 2026
b799ab1
refactor(angular-web-sdk): rename analytics-event-display to event-log
nalchevanidze Jun 9, 2026
dad44b8
refactor(angular-web-sdk): compute badges array in model, render with…
nalchevanidze Jun 9, 2026
d2fe271
style(angular-web-sdk): reduce badge size and gap
nalchevanidze Jun 9, 2026
e625eb8
refactor(angular-web-sdk): extract EntryBadge component with CSS tool…
nalchevanidze Jun 9, 2026
23e9e62
refactor(angular-web-sdk): consolidate Badge type and builders into b…
nalchevanidze Jun 9, 2026
7e8b426
refactor(angular-web-sdk): extract ObservationMode type from sdk
nalchevanidze Jun 9, 2026
82a7f09
refactor(angular-web-sdk): add resolveWith to resolver, collapse Nest…
nalchevanidze Jun 9, 2026
6a08cd6
refactor(angular-web-sdk): unify inject pattern with .with() on both …
nalchevanidze Jun 9, 2026
c217e2d
refactor(angular-web-sdk): merge NgContentfulLiveEntry and resolver i…
nalchevanidze Jun 9, 2026
c32a262
refactor(angular-web-sdk): consolidate isMergeTagEntry to utils, rena…
nalchevanidze Jun 9, 2026
b442a73
refactor(angular-web-sdk): type profile$ with Zod, expose profile sig…
nalchevanidze Jun 9, 2026
50bd5e2
refactor(angular-web-sdk): delete NgContentfulLiveEntry, move Observa…
nalchevanidze Jun 9, 2026
f0f90df
refactor(angular-web-sdk): flatten sdk/utils/ to utils.ts, trim SDK p…
nalchevanidze Jun 9, 2026
0827223
feat(angular-web-sdk): add provideContentfulOptimizationConfig helper
nalchevanidze Jun 9, 2026
7b53aa2
refactor(angular-web-sdk): provideContentfulOptimizationConfig takes …
nalchevanidze Jun 9, 2026
bfa3905
refactor(angular-web-sdk): remove NG_CONTENTFUL_OPTIMIZATION_CONFIG f…
nalchevanidze Jun 9, 2026
7cd1f9f
refactor(angular-web-sdk): remove MergeTagPipe and NgContentfulOptimi…
nalchevanidze Jun 9, 2026
2e37918
refactor(angular-web-sdk): remove CONFIG token, inline env vars into …
nalchevanidze Jun 9, 2026
aff6a8d
refactor(angular-web-sdk): rename config/entries.ts to fixtures.ts, E…
nalchevanidze Jun 9, 2026
c186c92
fix(angular-web-sdk): access import.meta.env directly instead of dest…
nalchevanidze Jun 9, 2026
6d78afd
refactor(angular-web-sdk): replace OnInit/OnDestroy subscription with…
nalchevanidze Jun 9, 2026
6c16e20
refactor(angular-web-sdk): hide NgContentfulOptimizationResolver, exp…
nalchevanidze Jun 9, 2026
7bea74d
refactor(angular-web-sdk): make sdk non-optional, remove all undefine…
nalchevanidze Jun 9, 2026
393af8e
refactor(angular-web-sdk): remove sdk proxy methods, tighten service …
nalchevanidze Jun 9, 2026
8591b4d
refactor(angular-web-sdk): replace OnInit+signals with resource() for…
nalchevanidze Jun 9, 2026
0af5c4c
fix(angular-web-sdk): expose clickScenarios as field for template access
nalchevanidze Jun 9, 2026
7761a43
refactor(angular-web-sdk): add FIXTURES.pageTwo.ids, use it in resour…
nalchevanidze Jun 9, 2026
44c741c
fix(angular-web-sdk): bind trackConversion as arrow field, remove loa…
nalchevanidze Jun 9, 2026
6bc98be
refactor(angular-web-sdk): delete unused MergeTagPipe
nalchevanidze Jun 9, 2026
31ad43d
refactor(angular-web-sdk): inline NgContentfulOptimizationResolver in…
nalchevanidze Jun 9, 2026
a459216
refactor(angular-web-sdk): move NgContentfulLiveUpdates to app layer
nalchevanidze Jun 9, 2026
8e98941
refactor(angular-web-sdk): move NgContentfulLiveUpdates into app/serv…
nalchevanidze Jun 9, 2026
271efe8
refactor(angular-web-sdk): remove NgContentfulLiveUpdates from NgCont…
nalchevanidze Jun 9, 2026
95a21a5
docs(angular-web-sdk): shorten and de-angularize REQUIREMENTS.md
nalchevanidze Jun 10, 2026
271afdd
Merge branch 'main' into NT-3348
nalchevanidze Jun 10, 2026
4bdfd3b
feat(angular-web-sdk): add descriptions to live-updates section cards
nalchevanidze Jun 10, 2026
229e28d
docs(angular-web-sdk): update REQUIREMENTS and add jira ticket
nalchevanidze Jun 10, 2026
65b79cf
feat(angular-web-sdk): implement SDK teardown in NgContentfulOptimiza…
nalchevanidze Jun 10, 2026
fa2bff7
docs(angular-web-sdk): resolve remaining REQUIREMENTS TODOs
nalchevanidze Jun 10, 2026
007f1d3
docs(angular-web-sdk): rewrite REQUIREMENTS as dense categorised sect…
nalchevanidze Jun 10, 2026
02c5a70
docs(angular-web-sdk): reformat REQUIREMENTS as bullet lists with inl…
nalchevanidze Jun 10, 2026
86cfdbd
docs(angular-web-sdk): move entry resolution under content, clarify p…
nalchevanidze Jun 10, 2026
047c92e
docs(angular-web-sdk): simplify entry resolution requirement
nalchevanidze Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions implementations/angular-web-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# dependencies
node_modules/

# build output
dist/

# Angular cache
.angular/

# env files (copy from .env.example to set up locally)
.env
.env*.local

# logs produced by launch-reference-app.sh
logs/

# debug
*.log
.pnpm-debug.log*

# misc
.DS_Store
*.tsbuildinfo
41 changes: 41 additions & 0 deletions implementations/angular-web-sdk/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# AGENTS.md

Angular SPA reference implementation of `@contentful/optimization-web`. Serves the app on
`http://localhost:4200` via the Angular CLI dev server alongside the shared mock API server.

## Rules

- Read [`REQUIREMENTS.md`](./REQUIREMENTS.md) before making any changes — it is the source of truth
for what this implementation must do and how it must behave.
- Do not add local adapter shims; import SDK behaviour directly from the published package surface
when it exists.
- This implementation uses Angular CLI (`@angular/build`) and PM2-managed mock server processes.
- Use standalone components (no NgModule).
- Use modern Angular patterns: signals and `computed()` for state, `inject()` for dependency
injection (not constructor injection), `input()` / `output()` for component I/O, `toSignal()` to
bridge RxJS observables to templates, and `@if` / `@for` control flow syntax.
- Name files and classes after the concept only — no Angular-role suffixes anywhere. File: `home.ts`
not `home.component.ts`. Class: `Home` not `HomeComponent`, `Optimization` not
`OptimizationService`, `MergeTag` not `MergeTagPipe`, etc.
- Avoid unsafe type assertions (`as SomeType`). Use `isRecord()` and typed type-guard functions
instead. The `no-unsafe-type-assertion` ESLint rule is enforced and blocks commits.
- Follow the Angular style guide class member ordering: inputs → injected dependencies → private
state → constructor (effects/setup) → protected state (template-facing computed/signals) →
lifecycle hooks → public methods → private methods.

## Commands

```sh
pnpm implementation:run -- angular-web-sdk implementation:install
pnpm implementation:run -- angular-web-sdk serve:mocks
pnpm implementation:run -- angular-web-sdk dev
pnpm implementation:run -- angular-web-sdk build
pnpm implementation:run -- angular-web-sdk typecheck
```

## Validate

- Run `typecheck` for TypeScript changes.
- Run `build` for production bundling changes.
- Run lint via `npx eslint <file>` from the implementation directory for source file changes.
- The pre-commit hook runs lint and Prettier automatically — fix any errors before committing.
96 changes: 96 additions & 0 deletions implementations/angular-web-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<p align="center">
<a href="https://www.contentful.com/developers/docs/personalization/">
<img alt="Contentful Logo" title="Contentful" src="../../contentful-icon.png" width="150">
</a>
</p>

<h1 align="center">Contentful Personalization & Analytics</h1>

<h3 align="center">Angular Web SDK Reference Implementation</h3>

<div align="center">

[Readme](./README.md) ·
[Guides](https://contentful.github.io/optimization/documents/Documentation.Guides.html) ·
[Reference](https://contentful.github.io/optimization) · [Contributing](../../CONTRIBUTING.md)

</div>

> [!WARNING]
>
> The Optimization SDK Suite is pre-release (alpha). Breaking changes can be published at any time.

Reference implementation of `@contentful/optimization-web` for Angular applications. Demonstrates
all SDK integration patterns — entry resolution, auto and manual tracking, consent, identify/reset,
live updates, nested entries, rich text with merge tags, feature flags, analytics event display, and
the preview panel.

## What this demonstrates

- SDK initialisation as a singleton Angular service
- Page tracking on every route change via the Angular router
- Entry resolution with variant/baseline display
- Auto-tracking via `data-ctfl-*` DOM attributes
- Manual tracking via `sdk.tracking.enableElement`
- Click scenarios: direct, descendant, ancestor
- Consent gating
- Identify and reset with session persistence
- Live updates: global toggle and per-entry override
- Preview panel forced-live mode
- Nested entries with recursive resolution
- Rich text rendering with inline merge tags
- Feature flag subscription with auto-emitted view events
- Analytics event display with heartbeat deduplication
- Multi-route navigation with conversion tracking

See [`REQUIREMENTS.md`](./REQUIREMENTS.md) for full feature specs and visual verification steps.

## Prerequisites

- Node.js >= 20.19.0 (24.13.0 recommended to match `.nvmrc`)
- pnpm 10.x

## Quick start

From the **repository root**:

```sh
pnpm build:pkgs
pnpm implementation:run -- angular-web-sdk implementation:install
pnpm implementation:run -- angular-web-sdk serve:mocks
pnpm implementation:run -- angular-web-sdk dev
```

The app is available at `http://localhost:4200`. The mock server must be running for entry data and
variant resolution to work.

## Running locally

From the **repository root**:

```sh
pnpm implementation:run -- angular-web-sdk dev
pnpm implementation:run -- angular-web-sdk build
pnpm implementation:run -- angular-web-sdk typecheck
```

Or from the **implementation directory**:

```sh
pnpm dev
pnpm build
pnpm typecheck
```

## Environment variables

Copy `.env.example` to `.env`:

```sh
cp .env.example .env
```

## Related

- [web-sdk_react](../web-sdk_react/README.md) — React Web SDK reference implementation
- [@contentful/optimization-web](../../packages/web/web-sdk/README.md) — Web SDK
170 changes: 170 additions & 0 deletions implementations/angular-web-sdk/REQUIREMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Web SDK Reference Implementation — Requirements

Implement and validate a client-side SPA reference implementation demonstrating the full Angular
adapter SDK integration path using public APIs only, against shared mocks and fixtures. All behavior
runs entirely on the client — no SSR, no server-side rendering, no hydration.

**Constraint (REFREQ-20):** No local shims, casts, or adapter logic may mask a missing SDK
capability. All SDK interaction must go through public package APIs.

---

## Core setup

### SDK initialisation

- Initialises once per page load from environment config; enforced by a module-level singleton.
- Falls back to baseline rendering on init failure — no crash or blank screen.
- On teardown: unsubscribes the router subscription, calls `sdk.destroy()` to flush queues and stop
entry interaction tracking, and resets the singleton so the SDK can be recreated cleanly.
- Confirm: load — entry cards render and control panel shows **Active optimizations: 0**; on
failure, entries show baseline with no crash; navigate away and confirm no subscription leaks in
DevTools.

### Multi-route navigation

- Two routes: **Home** (`/`) and **Page Two** (`/page-two`).
- SDK is not re-initialised on route changes; event history is preserved.
- Arriving on Page Two auto-fires `component — page-two-conversion`; the **Track conversion** button
fires an additional one.
- A `page` event fires on every SPA route change, regardless of consent.
- Confirm: navigate to Page Two — sidebar shows `page — /page-two` and
`component — page-two-conversion` automatically; navigating back and forth produces no repeated
SDK init entries.

### Consent

- App exposes a consent toggle; granting enables tracking, withdrawing disables it immediately.
- Page events are unaffected by consent state and always fire.
- Confirm: clean session — no `component` events regardless of scrolling; grant consent — view
events appear on scroll; withdraw — no new `component` events but `page` events still fire on
navigation.

### Identify and reset

- `identify()` associates the session with a fixed user ID; `reset()` returns to anonymous.
- Both states persist across page reloads via SDK-managed storage — no app-level code required.
- Confirm: clean session shows **Identified: No**; after **Identify** with live updates ON, variant
cards update; after **Reset**, cards revert to baseline; after reload, **Identified: Yes**
immediately.

### Preview panel

- Panel code is lazy-loaded on first open — no bundle loads when the feature is disabled (network
tab).
- Opening forces live re-resolution for all entries, overriding per-entry lock settings.
- Closing restores each entry's configured live-update behaviour.
- Supports audience activation/deactivation and variant override/reset — all handled by the panel
itself; no app code required.
- Confirm: global OFF, **Always locked** — identify does not change the entry; open panel then reset
— it updates; close and identify again — it does not change. Inside the panel: activate an
audience override — entry updates; deactivate — reverts; apply a variant override then reset —
entry returns to SDK-resolved variant.

### Locale consistency

- All CDA entry fetches use the SDK-resolved locale.
- Structural: Contentful client is wrapped with `sdk.withOptimizationLocale()` before entries are
fetched.

### Feature flags

- App subscribes to a `'boolean'` flag via the SDK flag state API; value shown in the control panel.
- Accessing the flag emits a flag-view event automatically — no explicit tracking call in app code.
- Returns `undefined` for anonymous sessions; resolves to configured value after `identify()`.
- Confirm: clean session — **Flag "boolean": undefined**; after **Identify**, resolves to `true` and
a `component` event appears in the sidebar automatically; after **Reset**, returns to `undefined`.

---

## Tracking

### Analytics

- App renders the SDK event stream in real time; every tracking event (page, view, click, hover)
appears in the sidebar as it occurs.
- View and hover rows update in place (duration); **Raw count** increases faster than the
deduplicated list.
- Events blocked by consent do not appear — the SDK's `eventStream` only emits delivered events;
blocked events are routed to `blockedEventStream` and never reach the log.
- History persists across route changes.
- Confirm: clean session — sidebar shows `page — /`; without consent, scroll produces no `component`
events; grant consent, hover an entry — `component_hover` row appears and duration updates in
place while Raw count rises; navigate routes — prior events remain and `page` events append.

### Page tracking

- A `page` event fires on first load and on every route change, regardless of consent.
- Confirm: clean session — `page — /` appears immediately; navigating between routes produces a
`page` event each time, with or without consent.

### Entry tracking _(which events are emitted)_

- **Attribute-based (auto)** — `data-ctfl-*` attributes on DOM elements are observed by the SDK;
view, click, and hover events fire automatically after consent; none before; all stop immediately
if consent is withdrawn.
- **Click scenarios** — all three must emit `component_click`:
- **Direct** — the tracked element itself is clicked.
- **Descendant** — a button inside the tracked element is clicked.
- **Ancestor** — click listener is on a wrapper element containing the tracked entry.
- Confirm: clean session — scroll **Auto-observed**, no `component` events; grant consent, scroll
— view events appear; click a card → `component_click`; hover → `component_hover`; click each of
`direct`, `descendant`, `ancestor` — a `component_click` appears for each.
- **Code-based (manual)** — entries registered via explicit `enableElement` calls instead of
`data-ctfl-*` attributes; emits view events only; no click or hover events.
- Confirm: grant consent, scroll **Manually-observed** — view events appear; click or hover a card
— no `component_click` or `component_hover`.

---

## Live updates

### Global toggle

- **OFF (default):** entry resolves once then freezes; `identify()`/`reset()` does not change what
is displayed.
- **ON:** entry re-resolves immediately on every profile change.
- Confirm: global OFF, **Identify** — Default card does not change; toggle ON — label updates;
**Reset** — Default card updates immediately.

### Per-entry overrides

- **Always live** — re-resolves on profile change even when global is OFF.
- **Always locked** — never re-resolves regardless of global toggle; does re-resolve when preview
panel is open.
- **Default** — no override; follows the global toggle.
- Confirm: global OFF, **Identify** — only **Always live** updates; **Default** and **Always
locked** stay frozen; toggle ON, **Reset** — **Default** and **Always live** update; **Always
locked** stays frozen.

---

## Content

### Entry resolution

- SDK resolves the correct variant for each entry; always falls back to baseline on invalid data; no
variant selection logic in app code.
- Confirm: load — all cards show `baseline` badge; live updates ON, **Identify** — active experience
cards switch to `variant` with `var`/`exp` IDs; **Reset** — cards revert to `baseline`.

### Nested entries

- Each level resolves to its own variant independently; arbitrary nesting depth is handled by the
SDK without app-level recursion.
- Confirm: first Auto-observed card shows `nested` badge with indented child entries, each with
their own ID rows; live updates ON, **Identify** — each nested level updates independently.

### Merge tags

- Profile-resolved values rendered inline; shows `[Merge Tag]` fallback when no profile is active.
- Updates on `identify()` and reverts on `reset()`.
- Confirm: clean session — merge tag cards (yellow badge) show `[Merge Tag]`; live updates ON,
**Identify** — replaced with resolved value; **Reset** — reverts to `[Merge Tag]`.

### Rich text

- Renders as formatted HTML (paragraphs, headings, lists, blockquotes, hyperlinks).
- Merge tag nodes embedded in rich text are resolved inline with the same fallback behaviour.
- Confirm: rich text cards (blue badge) render formatted HTML — not raw JSON; merge tag nodes show
resolved value or `[Merge Tag]` fallback inline.
62 changes: 62 additions & 0 deletions implementations/angular-web-sdk/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"angular-web-sdk": {
"projectType": "application",
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"outputPath": "dist/angular-web-sdk",
"index": "src/index.html",
"browser": "src/main.ts",
"tsConfig": "tsconfig.json",
"assets": [],
"styles": ["src/styles.css"]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "angular-web-sdk:build:production"
},
"development": {
"buildTarget": "angular-web-sdk:build:development"
}
},
"defaultConfiguration": "development",
"options": {
"port": 4200
}
}
}
}
},
"cli": {
"analytics": false,
"packageManager": "pnpm"
}
}
Loading
Loading