@gabrielmorais-dev-id/web-sdk
v0.1.8
Published
Oreus Web SDK: framework-agnostic Web Components backed by the Oreus HTTP facade.
Downloads
1,543
Readme
@oreus/web-sdk
- Install
- Quick start
- Provider configuration
- Authentication
- Scopes
- Components
- Events
- Styling
- Localization
- State
- Integrations
- Development
Install (temporary)
npm i @gabrielmorais-dev-id/web-sdk@latest Quick start
import { registerOreusComponents } from "@oreus/web-sdk";
import "@oreus/web-sdk/styles.css";
registerOreusComponents();<oreus-provider
issuer="https://sso.example.com/oidc"
client-id="your-app-id"
api-base-url="https://api.example.com"
resource="https://api.example.com"
redirect-uri="https://your-app.example.com/oauth/callback"
scopes="read:vox write:vox"
>
<oreus-auth></oreus-auth>
<oreus-vox-chat></oreus-vox-chat>
</oreus-provider>Every other component must be a descendant of <oreus-provider>: the provider owns the session, the API client and the state, and hands them to its children.
Provider configuration
| Attribute | Required | Description |
| ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| issuer | yes | OIDC issuer of your Oreus tenant (usually ends with/oidc). |
| client-id | yes | Id of your third-party application. |
| api-base-url | yes | Origin of the Oreus HTTP facade. Read once, before the runtime starts, and immutable afterwards, so production, staging and local use the same artifact with a different value. |
| resource | yes | OAuth resource identifier of the API. |
| redirect-uri | yes | Must match a redirect URI registered for your application exactly. |
| scopes | yes | Space-separated scopes to request. SeeScopes. |
| locale | no | en or fr. See Localization. |
| theme | no | light (default) or dark. |
Changing issuer, client-id, api-base-url, resource, redirect-uri or scopes after mount reconfigures the provider.
Authentication
<oreus-auth> renders the sign-in / sign-out control. The SDK signs the user in with OIDC (authorization code + PKCE) against issuer, then obtains an organization token for the active organization. All API calls use that token. The active organization is the one the user authorized on the Logto consent screen; the SDK shows no organization picker.
Besides the scopes you list in scopes, the provider always requests openid profile email offline_access urn:logto:scope:organizations.
No scope is needed to sign in. Without any of the read:* / write:* scopes below the user can sign in, but there is nothing to show.
Identity provider setup: register your application with the redirect URI used in redirect-uri. After sign-out the user returns to the current page URL (without query or hash).
Scopes
How scopes work
- You request scopes with the
scopesattribute. - The SDK reads the scopes that the organization token actually grants, from its
scopeclaim. Requesting a scope the organization does not grant does not enable it. - Each component checks those effective scopes.
A missing scope hides a feature, it never blocks the app.
- A component whose main read scope is missing renders a "forbidden" state in place of its content. The rest of the page keeps working.
- A missing write or delete scope removes only the related action: the menu entry, button or drop zone is not rendered.
- Optional capabilities degrade quietly. For example, without
read:sessionsthe chat input has no@session mentions and no history, but it still sends messages.
Request only what the screens you use need.
Scope reference by component
| Component | Needs | Without it |
| -------------------------------- | ----------------------------------------------------------------- | ------------------------------------- |
| oreus-auth | none (sign-in scopes only) | n/a |
| oreus-vox-chat | write:vox to send messages (write:voxies in a voxie chat) | "Forbidden" state, chat is not usable |
| oreus-chat-history | read:sessions | "Forbidden" state |
| oreus-agent-library | read:stores | "Forbidden" state |
| oreus-voxie-list | read:voxies | "Forbidden" state |
| oreus-describe-voxie | write:voxies | "Forbidden" state |
| oreus-collections | read:folders | "Forbidden" state |
| oreus-files-table | read:files | "Forbidden" state |
| oreus-or-organization-card | read:orbs | "Forbidden" state |
| oreus-or-usage-alert | read:orbs | Renders nothing |
Sub-scopes (features inside a component)
| Scope | Feature it enables | Where |
| ---------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------- |
| read:vox | Vox data (conversation context) | oreus-vox-chat |
| read:sessions | Past conversations,@ session mentions, saving chats | oreus-vox-chat, oreus-chat-history |
| write:sessions | Rename conversations; save a conversation that is not temporary | oreus-chat-history, oreus-vox-chat |
| read:voxies | Load the selected voxie into the chat | oreus-vox-chat |
| write:voxies | Create a voxie, edit it, start a chat from a voxie card | oreus-voxie-list, oreus-describe-voxie |
| delete:voxies | Delete a voxie | oreus-voxie-list |
| read:agents | Agents in the@ mention menu and agent details | oreus-vox-chat, oreus-agent-library |
| write:stores | Publish / manage store items from the agent details dialog | oreus-agent-library |
| read:reviews | See agent reviews | oreus-agent-library |
| write:reviews | Write a review | oreus-agent-library |
| write:attachments | Attach files from the computer in the chat input | oreus-vox-chat |
| read:folders | Attach from collections; folder lists and the move dialog | oreus-vox-chat, oreus-collections, oreus-files-table |
| write:folders | Rename a collection | oreus-collections |
| delete:folders | Delete a collection | oreus-collections |
| read:files | Open and preview files | oreus-files-table, oreus-vox-chat |
| write:files | Upload (button and drop zone), rename, move | oreus-files-table |
| delete:files | Delete a file | oreus-files-table |
| read:organizations | Active organization type: the Business Drive shows only when the active organization is PROFESSIONAL | oreus-vox-chat |
Components
Eleven elements are public; every other oreus-* element is internal and may change without notice. Each public element comes with all its features (dialogs, menus, uploads, empty and error states).
Attributes are written in kebab-case in HTML. Components in the same <oreus-provider> share state: selecting a conversation in oreus-chat-history opens it in the oreus-vox-chat of the same provider, wherever it is on the page.
oreus-provider
Session, API client and state for every component below it. Attributes: see Provider configuration. Emits no event.
oreus-auth
Sign-in / sign-out button. No attribute, no event.
oreus-vox-chat
The chat. Vox and Voxie conversations share this one element.
| Attribute | Type | Default | Description |
| ------------------------ | ------- | --------- | ---------------------------------------------------------------- |
| session-id | string | none | Conversation to open, for example restored from your URL. |
| voxie-id | string | none | Voxie to chat with, for example restored from your URL. |
| Event | detail | Fired when |
| --------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| oreus-chat-change | { sessionId?: string \| null, voxieId?: string } | The open conversation changes from inside the chat (a new conversation is created, a voxie is picked, …). Not fired when you changesession-id / voxie-id yourself. |
oreus-chat-history
List of past conversations, with search, rename and "New chat". No attribute.
| Event | detail | Fired when | What the SDK already does |
| ------------------------ | ------------------------------------------------------------------- | ------------------------- | ----------------------------------------- |
| oreus-new-chat | none | "New chat" is clicked | Resets the chat of the same provider |
| oreus-session-select | SessionSummary: { id, name?, updatedAt?, voxie?, voxieId? } | A conversation is clicked | Opens it in the chat of the same provider |
oreus-agent-library
Agent store: Discover, Most popular and My agents, with "See all" pages and an agent details dialog.
| Attribute | Type | Default | Description |
| ------------ | ------ | ------- | ------------------------------------------------------------------------------- |
| sections | string | all | Comma-separated sections to show:discover, most_popular, my_agents. |
| Event | detail | Fired when | What the SDK already does |
| ----------------- | ---------------------------------------------------------- | ---------------------------------------- | ------------------------------ |
| oreus-select | { storeItemId: string, item: StoreItem } | An agent card is clicked | Opens the agent details dialog |
| oreus-see-all | { section: "discover" \| "most_popular" \| "my_agents" } | "See all" of a section is clicked | Expands that section |
| oreus-back | none | "Back" is clicked in an expanded section | Returns to the overview |
oreus-voxie-list
List of voxies with edit and delete. No attribute.
| Event | detail | Fired when | What the SDK already does |
| ---------------- | -------------------------------------------- | ----------------------- | -------------------------------------------------------------------------- |
| oreus-select | { voxieId: string, voxie: VoxieSummary } | A voxie card is clicked | Withwrite:voxies, switches the chat of the same provider to that voxie |
oreus-describe-voxie
Prompt box that generates a new voxie from a description, then opens the voxie editor. No attribute, no event.
oreus-collections
Collections (folders) with rename and delete.
| Attribute | Type | Default | Description |
| ------------- | ------ | ------- | --------------------- |
| page-size | number | 12 | Collections per page. |
| Event | detail | Fired when | What the SDK already does |
| ---------------- | -------------------------------------- | ----------------------- | -------------------------------------------------- |
| oreus-select | { folderId: string, name: string } | A collection is clicked | Nothing. Show its files withoreus-files-table. |
oreus-files-table
Files of one folder, with upload button, drop zone, upload progress, preview, rename, move and delete. Emits no event.
| Attribute | Type | Default | Description |
| ------------- | ------ | -------- | ------------------------------------------------------------------------------ |
| folder-id | string | required | Folder to list, usually thefolderId of an oreus-collections selection. |
oreus-or-organization-card
OR balance and usage of the active organization. No attribute, no event.
oreus-or-usage-alert
Alert for low (10% or less left) or used-up included OR, with the time until reset once they run out. Renders nothing while the balance is fine. oreus-vox-chat already shows it above its input. Emits no event.
| Attribute | Type | Default | Description |
| --------- | ------------------------------------- | -------- | ------------------------------------------------------------------------ |
| variant | "auto" | "warning" | "depleted" | "auto" | auto follows the balance; warning or depleted always shows that alert. |
Events
The SDK never navigates. Events tell your app what the user clicked and carry the data you need; what happens next is up to you: change the URL, open a page or a modal, send analytics, or ignore the event. The table "What the SDK already does" lists what happens inside the components anyway.
- Every event is a
CustomEvent: read the payload fromevent.detail. - Events bubble and cross shadow DOM (
composed), so you can listen on the element or on any ancestor. - Only the events listed in Components are part of the public contract. Other
oreus-*events may escape internal elements; do not rely on them. - The SDK does not emit domain or status events. Errors are rendered by the related component (inline state or toast).
const collections = document.querySelector("oreus-collections");
collections.addEventListener("oreus-select", (event) => {
const { folderId, name } = event.detail;
document.querySelector("oreus-files-table").setAttribute("folder-id", folderId);
});Keeping the chat in the URL: store sessionId / voxieId from oreus-chat-change (and from oreus-session-select / oreus-select if you route on those), then pass them back through session-id / voxie-id after a reload.
Styling
Components live in shadow DOM and are isolated from your page CSS: inherited properties such as text-align, font or color from the host page do not leak in. Customize through the CSS custom properties below, set on <oreus-provider> or any ancestor.
oreus-provider {
--oreus-primary: #1a5cff;
--oreus-font-family: "Inter", sans-serif;
}| Property | Effect |
| ----------------------- | ---------------------------------------------------- |
| --oreus-primary | Primary button color, including the gradient variant, and the focus ring |
| --oreus-font-family | Font of every component |
theme="dark" on the provider switches to the dark palette. Everything else keeps the Oreus look.
Localization
Set locale on the provider: en or fr. Any other value falls back to en, and a value starting with fr (such as fr-CA) selects fr. Without the attribute the SDK uses document.documentElement.lang, then en. Changing locale at runtime re-renders texts, dates and numbers.
State
Every <oreus-provider> is isolated from the others. The provider owns a
provider-scoped Nano Stores Query context, so query caches, the active
organization, and local chat state are never shared between provider trees.
Remote data lives in domain query stores (runtime.data.agents, sessions,
drive, orbs, voxies, vox, organizations), which expose loading, error, empty, and content
states. Components subscribe through StoreController and render through
<oreus-content-state>; a revalidation keeps already-cached data visible instead of
replacing it with a spinner. Mutations expose their own loading and error state and
revalidate only the related query.
The active organization is the one the user authorized for your app on the Logto
consent screen. The organizations claim lists all the user's organizations, so the SDK
asks the token endpoint for each in turn until one is granted (the others answer
access_denied). The granted organization ID is then kept in localStorage, per user,
and tried first on the next loads; signing out clears it. The organization token and the
query caches are never persisted.
Integrations
Call registerOreusComponents() once before using the elements. The SDK has no dependency on a host UI library.
Template types need no setup: importing the package types the elements' props and events in React, Preact, Solid, Qwik, Stencil, Hono, Vue and Svelte. Typings for a framework that is not installed are ignored. Other hosts can extend their own JSX namespace with CustomElements from the /jsx entry.
| Host | Runtime setup |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| React 19+ / Next.js 15+ | Register from client code ("use client"); custom events use onoreus-select style props. React 18 is not supported: it drops event handlers on custom elements. |
| Vue / Nuxt | ConfigureisCustomElement: tag => tag.startsWith("oreus-"); in Nuxt render under <ClientOnly>. |
| Solid | Useon:oreus-select for custom events. |
| Angular | AddCUSTOM_ELEMENTS_SCHEMA. |
| Preact, Svelte, Lit, plain JavaScript | None. |
Editor data: the package publishes custom-elements.json through its customElements field. For VS Code HTML autocomplete, set "html.customData": ["./node_modules/@oreus/web-sdk/dist/vscode.html-custom-data.json"].
document.querySelector("oreus-vox-chat") is typed with the element class. Public event detail types (SessionSummary, StoreItem, VoxieSummary, LibrarySectionId) are exported from the main entry.
Development
pnpm --dir packages/web-sdk typecheck
pnpm --dir packages/web-sdk build
pnpm --dir packages/web-sdk test
pnpm --dir packages/web-sdk dev # demo in examples/, reads .env (copy .env.example)