Skip to content

feat: add ExO event builder to the Experiences SDK - #203

Merged
Felipe Mamud (fmamud) merged 6 commits into
mainfrom
nt-3534-event-builder
Sep 24, 2026
Merged

Felipe Mamud (fmamud) merged 6 commits into
mainfrom
nt-3534-event-builder

Conversation

@fmamud

@fmamud Felipe Mamud (fmamud) commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR adds event-builder functionality to the Experiences SDK, including support for ExO view, click, and hover events.

It also introduces @contentful/optimization-api-client, Zod Mini, and es-toolkit as dependencies of the core package.

Related Jira ticket: NT-3534

Description

The new EventBuilder lives in @contentful/experiences-sdk-core and is available through the React, Svelte, and Angular SDKs. It provides buildExoView, buildExoClick, and buildExoHover, while retaining support for flag-view, identify, page-view, and custom track events.

The builder centralizes universal event metadata, context enrichment, timestamps, consent, campaign attribution, validation, and API payload construction. Tests verify ExO schema compatibility, runtime validation, context providers, campaign extraction, and the retained event methods.

This change introduces @contentful/optimization-api-client as a runtime dependency of the Experiences SDK core package. The API client provides the authoritative event schemas, event types, friendly validation errors, and shared logging behavior. Reusing these contracts prevents the Experiences SDK from maintaining copies that could drift from the Optimization APIs.

The API client does not provide the Experiences event builder itself. EventBuilder remains an Experiences SDK integration layer that translates runtime Experiences data into API-compatible event structures. Because the framework SDKs depend on core, the API client becomes a transitive dependency of the React, Svelte, and Angular packages.

Zod Mini is important because event inputs can be assembled from arbitrary runtime data, where TypeScript types provide no protection. Validation at the builder boundary prevents malformed events from reaching the API and provides useful errors to consumers.

es-toolkit is used to merge default page context with event-specific properties. A tested structured merge avoids losing nested runtime context and removes the need to maintain custom merge behavior inside the SDK.

Motivation and Context

The Experiences SDK needs a shared integration layer for constructing events emitted from rendered ExO entities. Without runtime validation, malformed or incomplete event data could be emitted silently, especially for JavaScript consumers or values derived from application and CMS state.

This implementation keeps event construction environment-neutral while leaving room for future web-specific context collection.

Related Jira ticket: NT-3534

PR Checklist

  • I have read the CONTRIBUTING.md file
  • All commits follow conventional commits
  • Documentation is updated (not required for this internal API addition)
  • PR doesn't contain any sensitive information
  • There are no breaking changes

Comment thread packages/core/src/event-builder.ts Outdated
Comment thread packages/adapter-angular/src/index.ts
Comment thread packages/core/src/index.ts
Comment thread packages/core/src/event-builder.ts Outdated

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.

Why does this live in the core package direclty and not something optimization-scoped? Would make it easier to define granular ownership later on and ensure clearer boundaries between concerns.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We've discussed about this before if should we organize the structure/modules first, then introduce optimization features, or we make it progress now and organize later on.

We came up with an agreement to have it in core scope from now, but in a near future we'll may have optimization-core or something.

@phobetron Charles Hudson (phobetron) Sep 23, 2026 •

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.

A builder instance would be added to the SDK runtime instance (when it exists) so if we end up adding a shared runtime class to Core then we would continue to colocate this here. The runtime would need to be in Core (or Node/Web, depending) and not in a separate Optimization package because it would not only affect personalization functionality but also improve general Experiences SDK DX.

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.

That does not make sense to me. Why would code need to be co-located in the same physical package to use it?
That being said, I'm fine to defer this if that was the agreed mode of operation for now.

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.

Why would you not want to colocate it? I'd rather not go digging for code far from where I intend to use it, especially if it won't be used directly elsewhere. It's also a bit small for its own package.

@fmamud
Felipe Mamud (fmamud) merged commit d0978af into main Sep 24, 2026
13 checks passed
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.

4 participants