@peerpage/embed-angular
v0.5.0
Published
Angular wrapper for the Peerpage embed — a typed <peerpage> standalone component over the framework-agnostic <peerpage-embed> custom element. Self-contained (bundles @peerpage/embed-element); the only Peerpage code that runs in the host origin, and it exe
Maintainers
Readme
@peerpage/embed-angular
Trust model (read this first): this package is the only Peerpage code that runs in your
app's origin, and it executes nothing generated. It is a thin, typed Angular component over the
framework-agnostic <peerpage-embed> custom element (@peerpage/embed-element): it fetches a
short-lived (≤5-minute) identity token from your own backend — by default a same-origin request so
a cookie session applies, or, for Bearer-token apps (most SPAs), via a tokenProvider you
supply (see Inputs). Either way your own credential is never forwarded to Peerpage. It then mounts
Peerpage's runtime as an iframe on the platform origin (or, when you opt in per-tenant, inline). Every
AI-built page runs in that runtime, never in your page. This wrapper adds zero new network calls of its own.
Install
npm install @peerpage/embed-angular@angular/core and @angular/common (>= 18) are peer dependencies — it uses your app's Angular.
Use it like a normal Angular component
Import the standalone PeerpageComponent and drop the <peerpage> tag. No
CUSTOM_ELEMENTS_SCHEMA, no stringly-typed attributes — typed @Input()s with full template
type-checking:
import { Component } from "@angular/core";
import { PeerpageComponent } from "@peerpage/embed-angular";
@Component({
selector: "app-workflows",
standalone: true,
imports: [PeerpageComponent],
template: `<peerpage slug="flag-orders" tenantId="acme"></peerpage>`,
})
export class WorkflowsComponent {}platformUrl / apiUrl default to Peerpage's hosted platform, so only slug + tenantId
are required. Bind dynamic values the usual way ([slug]="slug", [tenantId]="tenant", …).
Inputs
| Input | Required | Description |
| -------------- | :------: | --------------------------------------------------------------------------------------------------- |
| slug | ✓ | The page to open, e.g. "flag-orders". |
| tenantId | ✓ | Your Peerpage tenant id, e.g. "acme". |
| platformUrl | | The Peerpage runtime origin. Default "https://run.getpeerpage.com"; override for self-host / dev. |
| apiUrl | | The Peerpage API origin (where /embed/config lives); drives inline/iframe. Default "https://api.getpeerpage.com". |
| tokenPath | | Path on your backend that mints the identity token. Default "/peerpage/token". |
| tokenProvider| | A () => Promise<string> your app supplies when it authenticates with a Bearer token held in JS (most SPAs — no same-origin cookie session). See below. |
| width | | Outer-box width, any CSS length. Default "100%". |
| height | | Sizing model: "fill" (default), "auto" (grow with content), or a CSS length ("720px", "80vh"). Iframe mode; inline sizes to the host flow. |
| minHeight / maxHeight | | Optional bounds on the box height (any CSS length) — handy with height="auto". |
| className | | Your CSS class on the embed box — border, radius, shadow, margin, background. |
The embed box is your own element in your origin, so styling it is safe — none of this reaches the runtime inside. Size it with the inputs, e.g. a fixed-height card:
<peerpage slug="flag-orders" tenantId="acme"
width="480px" height="auto" minHeight="240px" maxHeight="80vh"
className="card"></peerpage>For one-off inline styles you can also bind natively on the <peerpage> host element
([style.border]="…") — the embed fills the host, so host-level box styles wrap it.
Bearer-token apps (tokenProvider)
The default token flow does a same-origin POST /peerpage/token with credentials: "include" — that
only works when your app authenticates with a cookie session. Most SPAs instead hold a Bearer JWT
in JS (localStorage / an auth store) and attach it via an HTTP interceptor the embed's own fetch does
not go through — so /peerpage/token would receive no Authorization header and return 401. For those
apps, pass a tokenProvider that mints the token with your own credential attached:
@Component({ imports: [PeerpageComponent], template: `
<peerpage slug="flag-orders" tenantId="acme" [tokenProvider]="mintToken"></peerpage>
` })
export class WorkflowsComponent {
mintToken = async () => {
const res = await fetch("/peerpage/token", { method: "POST", headers: { Authorization: `Bearer ${this.auth.accessToken}` } });
if (!res.ok) throw new Error(`token ${res.status}`);
return (await res.json()).token;
};
}Your credential goes only to your own /peerpage/token — it is never forwarded to Peerpage.
How it works
PeerpageComponent registers the <peerpage-embed> custom element (a side-effect import of
@peerpage/embed-element) and mounts it imperatively into the component's host with your inputs
mapped onto its attributes — so the element always boots with its required attributes present (no
Angular binding-timing race) and you never touch CUSTOM_ELEMENTS_SCHEMA. Under Angular SSR it
renders nothing on the server and mounts in the browser. Prefer the raw
<peerpage-embed> custom element for
non-Angular hosts.
