This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the Adobe UIX (UI Extensibility) SDK monorepo for Experience Cloud Apps. It enables:
- Host applications to define extensible areas in their UI
- Guest applications (extensions) to run in isolated contexts (iframes) and interact with hosts via RPC
The SDK abstracts iframe communication into a clean async API, using message passing underneath.
The monorepo contains four SDK packages in packages/:
-
@adobe/uix-core: Internal utilities shared by all packages
- RPC layer (
rpc/): call-sender, call-receiver for bidirectional method calls - Tunneling (
tunnel/): message passing infrastructure between host/guest - Event emitters, promise wrappers, cross-realm object handling
- Type definitions used across the SDK
- RPC layer (
-
@adobe/uix-guest: Extension-side library
GuestServer: Background frame that registers extension capabilitiesGuestUI: UI frames for rendering extension content- Entry points:
register()for GuestServer,attach()for GuestUI - Extensions must have exactly one GuestServer; may have multiple GuestUI frames
-
@adobe/uix-host: Host-side library (framework-agnostic)
Hostclass: manages extension loading, connection, lifecycle- Extension registry integration (fetches available extensions)
- Port abstraction for host-guest communication
- DOM utilities for iframe management
-
@adobe/uix-host-react: React bindings for uix-host
<Extensible>provider componentuseExtensions()hook for accessing extensions in React components<ExtensibleComponentBoundary>for scoping extensions to subtrees<GuestUIFrame>for rendering extension UI
- Host-Guest Architecture: Hosts provide extension points; guests implement them via method registration
- RPC Communication: Bidirectional async method calls abstracted from underlying postMessage
- Shared Context: Hosts can provide context (auth, theme, locale) that all guests can access
- Extension Points: Named interfaces that guests implement (e.g., "cf-editor", "aem")
- Namespaced APIs: All guest methods must be organized in namespaces (e.g.,
methods.myNamespace.myMethod())
All commands run from repository root:
npm run build # Development build with source maps
npm run build:production # Production build with minification
npm run clean # Remove all build artifacts and node_modulesnpm run dev # Start development server with hot reload
# - Incremental SDK compilation
# - Live example servers
# - Mock registry for host-guest connections
npm run demo # Production build + demo servernpm test # Run all tests (lint + unit tests in all packages)
npm run test:unit # Run Jest unit tests only
npm run test:unit:watch # Watch mode for unit tests
npm run lint # Check formatting and linting
npm run format # Auto-fix formatting issuesIndividual packages support npm run test and npm run test:watch (run with -w packages/<name>).
npm run test:e2e # Full cycle: rebuild SDK, set up apps, run tests
npm run test:e2e:skip-setup # Skip build + setup (use only if apps are already
# installed and generated from the current build)Important: Never use --skip-setup after modifying SDK source — the setup step both
copies the fresh dist into e2e app node_modules AND clears the webpack/babel cache, ensuring
the new build is actually served. Using --skip-setup with a stale build will silently run
against old code.
# Link local SDK to another project (uses yalc, not npm link)
node scripts/publish-local-to.mjs ../path/to/other-project
# Must re-run after each rebuild (doesn't auto-update)
# Use --dry-run to preview commandsnpm run declarations:build # Build TypeScript declarations
npm run declarations:watch # Watch mode for declarationsnpm run docs # Generate API documentation
npm run docs:watch # Watch mode for docs
npm run report:api # Generate API report without markdown- Unit tests: Jest with ts-jest, located alongside source files (
.test.ts) - Test environment: jsdom (simulates browser environment)
- Configuration:
jest.config.tsin root defines projects for core, host, host-react - Running specific tests: Use
npm testwithin a package workspace or-wflag from root
Example test file locations:
packages/uix-core/src/rpc/call-sender.test.tspackages/uix-host/src/extensions-provider/extension-registry.test.tspackages/uix-host-react/src/components/ExtensibleWrapper/ExtensionManagerProvider.test.ts
E2E tests live in e2e/ and are orchestrated by scripts/e2e-run.mjs. They use TestCafe
running against real webpack-dev-server instances of the host and guest apps.
How the e2e pipeline works:
e2e-setup.mjs local— generatespackage.jsonfrom templates, runsnpm install, copies local SDKdist/into each app'snode_modules, then clears the webpack/babel cache so the fresh build is always picked upe2e-run.mjs— kills any stale processes on ports 3000/3002/3003, starts host and guest app dev servers, waits for them to be ready, runs TestCafe, then tears down
Adding a new e2e scenario:
- Add a host component in
e2e/host-app/src/and a route ine2e/host-app/src/App.js - Add a test file in
e2e/tests/tests/— picked up automatically by the**/*.jsglob - Guest app (
e2e/guest-app/) uses?id=<ext-id>in the URL hash to set its extension ID
Known pitfalls:
- The setup script copies each SDK package's
dist/folder intonode_modules/@adobe/<pkg>/dist/so that the"main": "dist/index.js"and"types": "dist/index.d.ts"entrypoints in each package.json resolve correctly. - webpack and babel-loader caches in
node_modules/.cache/survive between runs. The setup script clears this cache whenever local SDK packages are injected. If you bypass setup, you may get stale SDK code silently served by the dev server.
Releases are automated via GitHub Actions workflows:
- Manual Release (
.github/workflows/npm-publish-manual.yml): Trigger via workflow_dispatch in GitHub Actions UI to create major/minor/patch/prerelease versions - Nightly Prerelease (
.github/workflows/npm-prerelease-nightly.yml): Automated nightly builds at 11 PM UTC, published to NPM under thenightlytag
Both workflows use GitHub secrets (ADOBE_BOT_NPM_TOKEN, GITHUB_TOKEN) and call scripts/release.mjs internally in the CI/CD environment.
For local testing or special cases, the release script can be run manually:
# Standard release (updates versions, commits, tags, pushes, publishes)
node scripts/release.mjs <major|minor|patch|prerelease>
# Options:
# --no-version Skip version bump
# --no-git Skip Git commit/tag/push
# --no-publish Skip NPM publish
# --registry=<url> Override default NPM registry
# --dry-run Preview commands without executingRelease script validates:
- On
mainbranch with clean working directory - All packages have matching version strings
- Updates versions across all packages and interdependencies
- Target: ES2022
- Project references configured for all packages in root
tsconfig.json - Each package has its own
tsconfig.jsonextendingtsconfig-base.json
- SDK packages: Built with
tsup(faster esbuild-based bundler) - Examples: Use Vite or Parcel depending on the example
- Output: Both ESM and CJS formats (
npm run build:esm)
- All exports use
.jsextensions in import paths (TypeScript requirement for ESM) - Package entry points defined in
package.json:main,types,browserfields
- Source files:
kebab-case.ts - React components:
PascalCase.tsxorPascalCase.tsfor logic-only components - Test files:
*.test.tsalongside source
- Add core functionality to appropriate package (uix-core, uix-host, uix-guest)
- If React-specific, add to uix-host-react
- Update TypeScript types (exported from each package's
index.ts) - Add unit tests alongside implementation
- Update examples if demonstrating new capability
- uix-core has no dependencies (except Penpal)
- uix-host depends on uix-core
- uix-host-react depends on uix-host (and React as peer dependency)
- uix-guest depends on uix-core
- Host creates
Hostinstance with extension list provider - Host listens for
loadallguestsevent - Host queries loaded guests with
getLoadedGuests({ namespace: ['method'] }) - GuestServer registers methods via
register({ methods: {...} }) - Host calls guest methods via
guest.apis.namespace.method()
- Development builds include extra logging (set
debug: truein host/guest config) - Use
UIX_SDK_BUILDMODEglobal to check build mode - Debug flags in
debug-host.tsanddebug-guest.ts
Located in examples/, each demonstrates different use cases:
host-vite-react-*: React host applicationsguest-vite-react-*: React guest extensionsguest-parcel-*,guest-vite-*: Various build tool examples- Each example has its own
package.jsonand can run independently - Examples serve as both documentation and acceptance tests
Run all examples together: npm run dev (starts multi-server)
Custom scripts in scripts/*.mjs:
bundler.mjs: Builds packages in dependency ordere2e-run.mjs: Orchestrates e2e tests (kill stale servers, setup, start apps, run TestCafe)e2e-setup.mjs: Sets up e2e app directories with correct SDK version and clears bundler cachemulti-server.mjs: Runs dev/demo servers for examples + mock registrypublish-local-to.mjs: Exports local builds to other projects via yalcrelease.mjs: Automated versioning, Git tagging, NPM publishingmock-registry.mjs: Local extension registry for development
- Runtime: Penpal (iframe communication) is the only SDK dependency
- Development: TypeScript, Jest, tsup, Parcel, Vite, React (for examples)
- Tooling: Prettier, ESLint, Husky (Git hooks), fixpack (package.json formatting)
- The SDK targets modern browsers (last 2 versions); doesn't support Internet Explorer
- Uses native browser features: EventTarget, fetch, Proxy, Reflect, WeakMap
- For older browser support, consumers must transpile the SDK
- All packages are marked
sideEffects: falsefor optimal tree-shaking