@susilkumar006/widgets-test 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +445 -0
- package/dist/api/FacePeApiClient.d.cts +74 -0
- package/dist/api/FacePeApiClient.d.ts +74 -0
- package/dist/api/FacePeApiContext.d.cts +8 -0
- package/dist/api/FacePeApiContext.d.ts +8 -0
- package/dist/api/decoders.d.cts +48 -0
- package/dist/api/decoders.d.ts +48 -0
- package/dist/api/index.d.cts +8 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/json.d.cts +5 -0
- package/dist/api/json.d.ts +5 -0
- package/dist/api/useFacePeResource.d.cts +21 -0
- package/dist/api/useFacePeResource.d.ts +21 -0
- package/dist/components/Avatar/FacePeAvatar.d.cts +23 -0
- package/dist/components/Avatar/FacePeAvatar.d.ts +23 -0
- package/dist/components/Avatar/engines.d.cts +30 -0
- package/dist/components/Avatar/engines.d.ts +30 -0
- package/dist/components/Avatar/index.d.cts +1 -0
- package/dist/components/Avatar/index.d.ts +1 -0
- package/dist/components/Form/FacePeForm.d.cts +19 -0
- package/dist/components/Form/FacePeForm.d.ts +19 -0
- package/dist/components/Form/index.d.cts +1 -0
- package/dist/components/Form/index.d.ts +1 -0
- package/dist/components/Picker/FacePePicker.d.cts +22 -0
- package/dist/components/Picker/FacePePicker.d.ts +22 -0
- package/dist/components/Picker/index.d.cts +1 -0
- package/dist/components/Picker/index.d.ts +1 -0
- package/dist/components/Placement/FacePeOverlay.d.cts +13 -0
- package/dist/components/Placement/FacePeOverlay.d.ts +13 -0
- package/dist/components/Placement/FacePePage.d.cts +10 -0
- package/dist/components/Placement/FacePePage.d.ts +10 -0
- package/dist/components/Placement/index.d.cts +2 -0
- package/dist/components/Placement/index.d.ts +2 -0
- package/dist/components/Timeline/FacePeTimeline.d.cts +23 -0
- package/dist/components/Timeline/FacePeTimeline.d.ts +23 -0
- package/dist/components/Timeline/index.d.cts +1 -0
- package/dist/components/Timeline/index.d.ts +1 -0
- package/dist/components/shared/FacePeErrorBoundary.d.cts +28 -0
- package/dist/components/shared/FacePeErrorBoundary.d.ts +28 -0
- package/dist/components/shared/surface.d.cts +6 -0
- package/dist/components/shared/surface.d.ts +6 -0
- package/dist/context/FacePeContext.d.cts +22 -0
- package/dist/context/FacePeContext.d.ts +22 -0
- package/dist/context/avatar.d.cts +6 -0
- package/dist/context/avatar.d.ts +6 -0
- package/dist/context/index.d.cts +5 -0
- package/dist/context/index.d.ts +5 -0
- package/dist/context/navigation.d.cts +10 -0
- package/dist/context/navigation.d.ts +10 -0
- package/dist/events/FacePeEventEmitter.d.cts +59 -0
- package/dist/events/FacePeEventEmitter.d.ts +59 -0
- package/dist/events/createCorrelationId.d.cts +9 -0
- package/dist/events/createCorrelationId.d.ts +9 -0
- package/dist/events/index.d.cts +8 -0
- package/dist/events/index.d.ts +8 -0
- package/dist/events/telemetry.d.cts +11 -0
- package/dist/events/telemetry.d.ts +11 -0
- package/dist/events/useFacePeEmitter.d.cts +15 -0
- package/dist/events/useFacePeEmitter.d.ts +15 -0
- package/dist/index.cjs +1925 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +1903 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/FacePeCustomerPayload.schema.json +31 -0
- package/dist/schemas/FacePeError.schema.json +63 -0
- package/dist/schemas/FacePeEventMeta.schema.json +53 -0
- package/dist/schemas/FacePeFormValues.schema.json +27 -0
- package/dist/schemas/FacePeNavigationRequest.schema.json +33 -0
- package/dist/schemas/FacePePickerOption.schema.json +34 -0
- package/dist/schemas/FacePePickerOptionsPayload.schema.json +49 -0
- package/dist/schemas/FacePeTelemetryEvent.schema.json +66 -0
- package/dist/schemas/FacePeTimelineItem.schema.json +91 -0
- package/dist/schemas/FacePeTimelinePayload.schema.json +109 -0
- package/dist/styles.css +887 -0
- package/dist/types/api.d.cts +60 -0
- package/dist/types/api.d.ts +60 -0
- package/dist/types/avatar.d.cts +102 -0
- package/dist/types/avatar.d.ts +102 -0
- package/dist/types/common.d.cts +42 -0
- package/dist/types/common.d.ts +42 -0
- package/dist/types/components.d.cts +53 -0
- package/dist/types/components.d.ts +53 -0
- package/dist/types/config.d.cts +27 -0
- package/dist/types/config.d.ts +27 -0
- package/dist/types/events.d.cts +85 -0
- package/dist/types/events.d.ts +85 -0
- package/dist/types/form.d.cts +83 -0
- package/dist/types/form.d.ts +83 -0
- package/dist/types/index.d.cts +18 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/picker.d.cts +82 -0
- package/dist/types/picker.d.ts +82 -0
- package/dist/types/placement.d.cts +69 -0
- package/dist/types/placement.d.ts +69 -0
- package/dist/types/provider.d.cts +75 -0
- package/dist/types/provider.d.ts +75 -0
- package/dist/types/results.d.cts +28 -0
- package/dist/types/results.d.ts +28 -0
- package/dist/types/telemetry.d.cts +38 -0
- package/dist/types/telemetry.d.ts +38 -0
- package/dist/types/timeline.d.cts +103 -0
- package/dist/types/timeline.d.ts +103 -0
- package/dist/version.d.cts +2 -0
- package/dist/version.d.ts +2 -0
- package/package.json +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# @facepe/widgets
|
|
2
|
+
|
|
3
|
+
The FacePe SDK: a reusable React + TypeScript library that a host application
|
|
4
|
+
installs and compiles into its own bundle. It is **not** a standalone app — it
|
|
5
|
+
has no page, no dev server and nothing to run on its own.
|
|
6
|
+
|
|
7
|
+
FacePe components will be exported from this package. At this stage it contains
|
|
8
|
+
only the package foundation and a minimal public entry point
|
|
9
|
+
(`SDK_VERSION`).
|
|
10
|
+
|
|
11
|
+
## React comes from your application
|
|
12
|
+
|
|
13
|
+
`react` and `react-dom` are **peer dependencies** (React 18 or 19). The SDK does
|
|
14
|
+
not ship its own copy: the build leaves every `react` / `react-dom` import
|
|
15
|
+
external, so the host's React is the only one in the final bundle. A second copy
|
|
16
|
+
would break hooks.
|
|
17
|
+
|
|
18
|
+
## Install dependencies
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd facepe-sdk
|
|
22
|
+
npm install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This also installs React as a dev dependency, used only for type checking and
|
|
26
|
+
building the SDK itself.
|
|
27
|
+
|
|
28
|
+
## Build
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run build
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This runs these steps, and stops at the first failure:
|
|
35
|
+
|
|
36
|
+
1. `lint-css` checks the stylesheets against the styling contract.
|
|
37
|
+
2. `vite build` compiles `src/` in library mode to JavaScript.
|
|
38
|
+
3. `tsc` generates the TypeScript declaration files (and `emit-cjs-types` their CommonJS copies).
|
|
39
|
+
4. `emit-schemas` generates the JSON Schemas of the payload types.
|
|
40
|
+
|
|
41
|
+
Type check without building:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run typecheck
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Build output
|
|
48
|
+
|
|
49
|
+
Everything is written to `dist/`, which is the only folder that is published:
|
|
50
|
+
|
|
51
|
+
| File | Purpose |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `dist/index.js` | ES module build (`import`) |
|
|
54
|
+
| `dist/index.cjs` | CommonJS build (`require`) |
|
|
55
|
+
| `dist/index.d.ts` / `dist/index.d.cts` | TypeScript declarations (ESM / CommonJS) |
|
|
56
|
+
| `dist/styles.css` | Component styles, published as `@facepe/widgets/styles.css` |
|
|
57
|
+
| `dist/schemas/*.schema.json` | JSON Schemas of the payloads, published as `@facepe/widgets/schemas/…` |
|
|
58
|
+
| `*.map` | Source maps (with sources embedded) |
|
|
59
|
+
|
|
60
|
+
## Using it from a React app
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { FacePeProvider, FacePeForm } from '@facepe/widgets';
|
|
64
|
+
import '@facepe/widgets/styles.css'; // once, anywhere in the app
|
|
65
|
+
|
|
66
|
+
<FacePeProvider config={{ mode: 'edit', locale: 'en-IN' }}>
|
|
67
|
+
<FacePeForm onSubmit={(event) => save(event.payload)} />
|
|
68
|
+
</FacePeProvider>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Who can use FacePe: the access flow
|
|
72
|
+
|
|
73
|
+
Access is granted top-down, and every level is checked on every request:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
Super admin creates the company, enables "FacePe SDK" and/or "FacePe API" for it,
|
|
77
|
+
and grants it avatars
|
|
78
|
+
Company admin creates users, assigns each user avatar_ids, and gives a user
|
|
79
|
+
SDK/API access: Dashboard → Users → SDK / API → access key + secret key
|
|
80
|
+
User's app its SERVER exchanges the keys for a 5-minute token; the SDK (or the
|
|
81
|
+
app's own API calls) use that token
|
|
82
|
+
Every call company allowed? → user allowed? → this avatar_id assigned? → allowed
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Revoking or regenerating a user's keys, disabling the user, or switching the
|
|
86
|
+
company's access off takes effect on the next request, not when tokens expire.
|
|
87
|
+
|
|
88
|
+
### The avatar: `<FacePeAvatar />`
|
|
89
|
+
|
|
90
|
+
The same avatar experience as on the FacePe website: the same avatar, trained
|
|
91
|
+
prompt, voice and features, embedded in your page. The minimum setup:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
import '@facepe/widgets/styles.css';
|
|
95
|
+
import { FacePeProvider, FacePeAvatar } from '@facepe/widgets';
|
|
96
|
+
|
|
97
|
+
<FacePeProvider
|
|
98
|
+
config={{}}
|
|
99
|
+
apiBaseUrl="https://api.facepe.com/api/v1/sdk"
|
|
100
|
+
getToken={({ forceRefresh }) => // YOUR server, below
|
|
101
|
+
fetch(forceRefresh ? '/facepe-token?refresh=1' : '/facepe-token')
|
|
102
|
+
.then((r) => (r.ok ? r.text() : Promise.reject(new Error(`token ${r.status}`))))}
|
|
103
|
+
>
|
|
104
|
+
<FacePeAvatar />
|
|
105
|
+
</FacePeProvider>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
No avatar_id is needed: with only the user's keys (on your server), the
|
|
109
|
+
component loads the avatars assigned to that user and shows them to choose
|
|
110
|
+
from (the first is selected; with one avatar there is no choice to make). To
|
|
111
|
+
show one particular avatar instead, pass its `avatarId` to the provider or
|
|
112
|
+
the component.
|
|
113
|
+
|
|
114
|
+
`<FacePeAvatar>` shows the avatar's portrait and a **Start conversation**
|
|
115
|
+
button (browsers allow sound and the microphone only after a click). It then
|
|
116
|
+
streams the avatar's video and voice, listens to the user's microphone, and
|
|
117
|
+
shows captions; **Mute** and **End** control the session. Props: `avatarId`
|
|
118
|
+
(optional; overrides the provider's), `language` (`'en'`, `'hi'`, …), `autoStart`,
|
|
119
|
+
`captions`, plus the usual `emphasis`, `size`, `density`. Events:
|
|
120
|
+
`onSessionStart`, `onSessionEnd`, `onTranscript`, `onOrderUpdate`, `onError`.
|
|
121
|
+
Handle: `start()`, `end()`, `setMuted(muted)`, `focus()`. Slot: `controls`
|
|
122
|
+
replaces the built-in buttons.
|
|
123
|
+
|
|
124
|
+
The connection libraries are installed with the package and loaded in their
|
|
125
|
+
own chunks only when an avatar is shown, so they cost nothing on other pages.
|
|
126
|
+
An avatar is described only by its name, gender, portrait, description and
|
|
127
|
+
languages: where it comes from is never part of the API or the SDK.
|
|
128
|
+
|
|
129
|
+
### Connecting: keys on your server, a token in the browser
|
|
130
|
+
|
|
131
|
+
The secret key must never be in browser code. Your server exchanges the keys
|
|
132
|
+
and hands the browser only the short-lived token:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
// Your server (e.g. Express), with the keys from the FacePe dashboard in env vars.
|
|
136
|
+
// Reuse the token for its 5 minutes; fetch a new one only near expiry or on ?refresh=1.
|
|
137
|
+
let cached = { token: null, expiresAt: 0 };
|
|
138
|
+
app.get('/facepe-token', async (req, res) => {
|
|
139
|
+
if (req.query.refresh !== '1' && cached.token && cached.expiresAt - Date.now() > 30_000) {
|
|
140
|
+
return res.type('text').send(cached.token);
|
|
141
|
+
}
|
|
142
|
+
const r = await fetch('https://api.facepe.com/api/v1/sdk/token', {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
headers: { 'Content-Type': 'application/json' },
|
|
145
|
+
body: JSON.stringify({
|
|
146
|
+
accessKey: process.env.FACEPE_ACCESS_KEY,
|
|
147
|
+
secretKey: process.env.FACEPE_SECRET_KEY,
|
|
148
|
+
channel: 'sdk', // 'api' for direct API use
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
const body = await r.json();
|
|
152
|
+
if (!r.ok) return res.status(r.status).json(body.error); // e.g. access switched off
|
|
153
|
+
cached = { token: body.data.token, expiresAt: Date.now() + body.data.expiresIn * 1000 };
|
|
154
|
+
res.type('text').send(cached.token);
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- `apiBaseUrl` is the FacePe gateway, `…/api/v1/sdk`. The SDK calls nothing else.
|
|
159
|
+
- **Cache the token** for its lifetime (`expiresIn`, 5 minutes) in `getToken`
|
|
160
|
+
and fetch a new one only when it is about to expire or `forceRefresh` is
|
|
161
|
+
true; the SDK asks for a token on every request, and a fresh exchange each
|
|
162
|
+
time adds a round trip to every call.
|
|
163
|
+
- `getToken` receives `{ audience: 'app-b', forceRefresh }` and returns a string
|
|
164
|
+
or a Promise. After a 401 it is called again with `forceRefresh: true` and
|
|
165
|
+
the request is retried once. If the retry is refused too, `onSessionExpired` fires.
|
|
166
|
+
- Every request carries `X-Request-Id` (correlation) and `X-FacePe-SDK-Version`.
|
|
167
|
+
|
|
168
|
+
### Using the API directly (no SDK)
|
|
169
|
+
|
|
170
|
+
Exchange the keys with `"channel": "api"` (the company needs API access), then
|
|
171
|
+
call the gateway with `Authorization: Bearer <token>`:
|
|
172
|
+
|
|
173
|
+
| Request | Returns |
|
|
174
|
+
| --- | --- |
|
|
175
|
+
| `POST /api/v1/sdk/token` `{ accessKey, secretKey, channel }` | `{ token, expiresIn, channel }` |
|
|
176
|
+
| `GET /api/v1/sdk/session` | the company, the user, the user's `avatarIds` |
|
|
177
|
+
| `GET /api/v1/sdk/avatars` | the avatars assigned to the user |
|
|
178
|
+
| `GET /api/v1/sdk/avatars/:avatarId` | one avatar (403 if not assigned) |
|
|
179
|
+
| `POST /api/v1/sdk/avatars/:avatarId/sessions` `{ language? }` | how to connect: `connection` (`{ type: 'room', url, token }` or `{ type: 'stream', token, greeting }`), and a `usageId` |
|
|
180
|
+
| `POST /api/v1/sdk/avatar-sessions/:usageId/end` | closes the session (usage minutes) |
|
|
181
|
+
|
|
182
|
+
Refusals: `401` for missing, invalid, expired or revoked credentials; `403`
|
|
183
|
+
when the company's access is off, the user is disabled, or the avatar is not
|
|
184
|
+
assigned to the user; `429` when one access key exceeds its rate limit
|
|
185
|
+
(`SDK_RATE_LIMIT_PER_MIN`, default 120 per minute).
|
|
186
|
+
|
|
187
|
+
### Data: pass an id, or pass the data
|
|
188
|
+
|
|
189
|
+
Each component either shows data the host passes in, or, given only an
|
|
190
|
+
`entityId`, fetches it through the gateway (architecture §7.5):
|
|
191
|
+
|
|
192
|
+
| Component | Host passes | Or `entityId` = | Fetched from |
|
|
193
|
+
| --- | --- | --- | --- |
|
|
194
|
+
| `FacePeTimeline` | `items` | an order id | `GET /orders/:id/timeline`, the order's status history |
|
|
195
|
+
| `FacePePicker` | `options` | a location id | `GET /locations/:id/categories`, its menu categories |
|
|
196
|
+
| `FacePeForm` | `initialValues` | an order id | `GET /orders/:id/customer`; Submit saves with `PUT` |
|
|
197
|
+
|
|
198
|
+
Data passed by the host always wins; nothing is fetched then. While loading,
|
|
199
|
+
a component shows a loading state; a failed load raises `onError` with the
|
|
200
|
+
gateway's error code. Payloads are validated on arrival, and unknown fields
|
|
201
|
+
are ignored.
|
|
202
|
+
|
|
203
|
+
`entityId` can also be set once on the provider's `config`. It then applies
|
|
204
|
+
to every component, so set it per component when they show different
|
|
205
|
+
entities (an order and a location).
|
|
206
|
+
|
|
207
|
+
JSON Schemas of every payload ship in the package:
|
|
208
|
+
`@facepe/widgets/schemas/FacePeTimelinePayload.schema.json`, and likewise
|
|
209
|
+
`FacePePickerOptionsPayload`, `FacePeCustomerPayload`, `FacePeTimelineItem`,
|
|
210
|
+
`FacePePickerOption`, `FacePeFormValues`, `FacePeEventMeta`, `FacePeError`,
|
|
211
|
+
`FacePeNavigationRequest` and `FacePeTelemetryEvent`.
|
|
212
|
+
|
|
213
|
+
### Telemetry
|
|
214
|
+
|
|
215
|
+
Pass your own sink; the SDK makes no analytics calls of its own:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
<FacePeProvider config={config} telemetry={{ track: (e) => analytics.log(e) }}>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`track` receives one record per component event (`name` = the event type)
|
|
222
|
+
and one per API call (`name` = `'api.request'`, with `method`, `path`,
|
|
223
|
+
`status`, `outcome` and `durationMs`). Every record carries the `sdkVersion`
|
|
224
|
+
and the `correlationId` (the same as the event's, or the request's
|
|
225
|
+
`X-Request-Id`). Event payloads are never included, since they can hold what
|
|
226
|
+
the user typed; `error` events carry only the error code. A sink that throws
|
|
227
|
+
is reported and ignored.
|
|
228
|
+
|
|
229
|
+
### Placement: inline, page, overlay
|
|
230
|
+
|
|
231
|
+
The host decides where an experience appears; the component inside is the same.
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
<FacePeForm /> {/* inline: inside an existing page */}
|
|
235
|
+
<FacePePage><FacePeTimeline items={items} /></FacePePage> {/* full view */}
|
|
236
|
+
<FacePeOverlay open={open} label="Edit" onClose={() => setOpen(false)}>
|
|
237
|
+
<FacePeForm onCancel={() => setOpen(false)} />
|
|
238
|
+
</FacePeOverlay> {/* modal dialog */}
|
|
239
|
+
<FacePeOverlay variant="drawer" open={open} label="Details" onClose={close}>
|
|
240
|
+
…
|
|
241
|
+
</FacePeOverlay> {/* side drawer */}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Titles, breadcrumbs, routing and action bars stay with the host.
|
|
245
|
+
|
|
246
|
+
### Navigation
|
|
247
|
+
|
|
248
|
+
Components never change the URL. They raise `navigate` events with a
|
|
249
|
+
`{ to, params?, replace? }` request; wire them to your router once on the
|
|
250
|
+
provider (or per component):
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
<FacePeProvider config={config} onNavigate={(e) => router.navigate(e.payload.to)}>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Events
|
|
257
|
+
|
|
258
|
+
Every `on…` callback receives `{ type, payload, meta }`. `meta` carries
|
|
259
|
+
`schemaVersion` (1), `correlationId`, `timestamp`, `sdkVersion`, `source`
|
|
260
|
+
(the component) and, when the component has one, `entityId`. Events never
|
|
261
|
+
contain credentials.
|
|
262
|
+
|
|
263
|
+
### Errors
|
|
264
|
+
|
|
265
|
+
Each component has its own error boundary: a failure inside it does not take
|
|
266
|
+
your page down. It raises `onError` with code `RENDER_FAILED` and shows the
|
|
267
|
+
`fallback` slot (or a short message). Remount it (e.g. a new `key`) to retry.
|
|
268
|
+
|
|
269
|
+
### Imperative handles
|
|
270
|
+
|
|
271
|
+
Reach them with a `ref`. Every method returns a `Promise<Result>` and never throws.
|
|
272
|
+
|
|
273
|
+
| Component | Methods |
|
|
274
|
+
| --- | --- |
|
|
275
|
+
| `FacePeForm` | `focus`, `validate`, `reset` |
|
|
276
|
+
| `FacePePicker` | `focus`, `reset` |
|
|
277
|
+
| `FacePeTimeline` | `focus`, `reset`, `scrollToSection(id)` |
|
|
278
|
+
|
|
279
|
+
Submitting, selecting and cancelling are user actions; they reach you as
|
|
280
|
+
events (`onSubmit`, `onSelect`, `onCancel`), not as handle methods.
|
|
281
|
+
|
|
282
|
+
### Customization
|
|
283
|
+
|
|
284
|
+
The package hard-codes no colours and no fonts. With nothing set, a component
|
|
285
|
+
inherits the host page's text colour, font and size, has a transparent
|
|
286
|
+
surface, and derives borders and muted text from the text colour. You choose
|
|
287
|
+
the look, from the least to the most effort:
|
|
288
|
+
|
|
289
|
+
1. **Design tokens (L1)** on the provider: `theme={{ colorSurface,
|
|
290
|
+
colorSurfaceMuted, colorText, colorMuted, colorBorder, colorAccent,
|
|
291
|
+
colorOnAccent, colorDanger, colorSuccess, fontFamily, fontSize, spacing,
|
|
292
|
+
radius, shadow }}`. For dark mode pass `darkTheme` too, and set
|
|
293
|
+
`colorScheme="light" | "dark"` from your own app's setting: dark tokens
|
|
294
|
+
override the main ones per token. The SDK never reads the system preference
|
|
295
|
+
itself.
|
|
296
|
+
2. **Variants (L2)** on every component: `emphasis="subtle" | "default" |
|
|
297
|
+
"strong"`, `size="small" | "medium" | "large"`, `density`; per component,
|
|
298
|
+
`variant` — `FacePeForm`: `"stacked" | "inline"`, `FacePePicker`:
|
|
299
|
+
`"list" | "grid"`.
|
|
300
|
+
3. **Slots (L3)**: `FacePeForm` — `header`, `actions` (render function given
|
|
301
|
+
`{ submit, cancel, submitting, readOnly }`), `footer`; `FacePePicker` —
|
|
302
|
+
`header`, `option` (render function given `{ option, selected, disabled }`),
|
|
303
|
+
`emptyState`; `FacePeTimeline` — `item`, `emptyState`; all — `fallback`.
|
|
304
|
+
4. **CSS variables (L4)**, documented; set them on any ancestor:
|
|
305
|
+
|
|
306
|
+
| Variable | Default when unset |
|
|
307
|
+
| --- | --- |
|
|
308
|
+
| `--fp-color-surface` | transparent (the host's background) |
|
|
309
|
+
| `--fp-color-surface-muted` | a light tint of the text colour |
|
|
310
|
+
| `--fp-color-text` | inherited from the host |
|
|
311
|
+
| `--fp-color-muted` | the text colour, partly transparent |
|
|
312
|
+
| `--fp-color-border` | the text colour, mostly transparent |
|
|
313
|
+
| `--fp-color-accent` | the text colour (the primary action is outlined) |
|
|
314
|
+
| `--fp-color-on-accent` | inherited text colour |
|
|
315
|
+
| `--fp-color-danger` | the text colour |
|
|
316
|
+
| `--fp-color-success` | the text colour |
|
|
317
|
+
| `--fp-font-family` | inherited from the host |
|
|
318
|
+
| `--fp-font-size` | inherited from the host |
|
|
319
|
+
| `--fp-spacing` | `0.25rem` (gaps and padding are multiples) |
|
|
320
|
+
| `--fp-radius` | `0.375rem` (frames use 1.5×) |
|
|
321
|
+
| `--fp-shadow` | a soft shadow tinted from the text colour (`emphasis="strong"`) |
|
|
322
|
+
| `--fp-content-max-width` | `32rem` (form, picker; `none` in page/overlay) |
|
|
323
|
+
|
|
324
|
+
Components adapt to the width of their container (container queries), not
|
|
325
|
+
the viewport, so they fit a sidebar or a full page alike.
|
|
326
|
+
|
|
327
|
+
`npm run lint:css` (also run by `npm run build`) fails the build if a
|
|
328
|
+
stylesheet hard-codes a colour or font, sets a `--fp-*` token, or uses an
|
|
329
|
+
unscoped selector.
|
|
330
|
+
|
|
331
|
+
### Using the SDK in a Tailwind app
|
|
332
|
+
|
|
333
|
+
It works as is: the SDK's classes are scoped, so Tailwind and the SDK cannot
|
|
334
|
+
clash, and its buttons keep their look under Tailwind's Preflight reset.
|
|
335
|
+
Style it with Tailwind through the documented variables (arbitrary
|
|
336
|
+
properties) and through slots — not by targeting the SDK's internal class
|
|
337
|
+
names, which change with every build.
|
|
338
|
+
|
|
339
|
+
```jsx
|
|
340
|
+
<div className="[--fp-color-accent:theme(colors.violet.600)] [--fp-color-on-accent:white] [--fp-radius:theme(borderRadius.xl)]">
|
|
341
|
+
<FacePeForm slots={{ header: <h3 className="text-lg font-semibold">Contact us</h3> }} />
|
|
342
|
+
</div>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
With Tailwind v4, use its CSS variables instead of `theme()`:
|
|
346
|
+
`[--fp-color-accent:var(--color-violet-600)]`.
|
|
347
|
+
|
|
348
|
+
Component styles are scoped (CSS Modules) and ship as one stylesheet,
|
|
349
|
+
`@facepe/widgets/styles.css`. Import it once; without it the components render
|
|
350
|
+
unstyled.
|
|
351
|
+
|
|
352
|
+
Until the package is published to a registry, a local app can install it
|
|
353
|
+
straight from this folder:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
npm install ../facepe-sdk
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Everything public is imported from the package root. Deeper paths such as
|
|
360
|
+
`@facepe/widgets/dist/...` are blocked by the package `exports` map, so
|
|
361
|
+
internal files can change without breaking your app.
|
|
362
|
+
|
|
363
|
+
## Compatibility
|
|
364
|
+
|
|
365
|
+
| | Supported | Verified with |
|
|
366
|
+
| --- | --- | --- |
|
|
367
|
+
| React (peer dependency) | 18 and 19 | 18.3 (test suites) |
|
|
368
|
+
| TypeScript consumers | 5.x; `bundler`, `node16` ESM and `node16` CommonJS resolution | 5.9, all three resolution modes |
|
|
369
|
+
| Bundlers | any that reads `exports` and CSS imports | Vite 7 (SDK build), webpack 5 (`tests/webpack.test.mjs`) |
|
|
370
|
+
| Browsers | Chrome / Edge 111+, Safari 16.2+, Firefox 113+ (needs container queries, `<dialog>`, `color-mix()`) | Chrome (automated checks) |
|
|
371
|
+
|
|
372
|
+
Report an incompatibility rather than working around it in your app.
|
|
373
|
+
|
|
374
|
+
## Testing
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
npm test # build, then every suite in tests/
|
|
378
|
+
npm run test:only picker # suites whose file name contains "picker" (needs a build)
|
|
379
|
+
npm run api:check # public API unchanged vs etc/widgets.api.md
|
|
380
|
+
npm run size # bundle budget (150 KB gzip) and no bundled React
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The suites test the built package the way a host consumes it: behaviour of
|
|
384
|
+
each component in jsdom, the TypeScript contract in three module setups,
|
|
385
|
+
telemetry and gateway data against a fake gateway, and a webpack 5 host build.
|
|
386
|
+
|
|
387
|
+
When you change the public API on purpose, run `npm run api:update` and
|
|
388
|
+
commit `etc/widgets.api.md` with the change; reviewers see exactly what changed
|
|
389
|
+
in the contract, and the version bump (major for a breaking change) follows
|
|
390
|
+
from it. CI (`.github/workflows/sdk.yml`) runs all of the above on every pull
|
|
391
|
+
request, plus the gateway tests.
|
|
392
|
+
|
|
393
|
+
## Releasing
|
|
394
|
+
|
|
395
|
+
Only CI publishes (`npm publish` refuses anywhere else):
|
|
396
|
+
|
|
397
|
+
1. Bump `version` in `package.json` and move the `[Unreleased]` notes in
|
|
398
|
+
`CHANGELOG.md` under a `## [x.y.z]` heading.
|
|
399
|
+
2. Merge to `main`, then push the tag: `git tag widgets-vx.y.z && git push origin widgets-vx.y.z`.
|
|
400
|
+
3. The "SDK release" workflow re-runs every check, signs a build-provenance
|
|
401
|
+
attestation, writes an SBOM, publishes the tarball, and creates a GitHub
|
|
402
|
+
release with the tarball, SBOM and notes.
|
|
403
|
+
|
|
404
|
+
Support: the current and previous minor lines get security and critical
|
|
405
|
+
fixes (N-1). Deprecated exports, slots and tokens keep working, with a
|
|
406
|
+
warning in the changelog, for at least one minor release before removal.
|
|
407
|
+
|
|
408
|
+
## Upgrading host apps
|
|
409
|
+
|
|
410
|
+
Host apps get an upgrade pull request for each release from Renovate by
|
|
411
|
+
extending the preset in this folder:
|
|
412
|
+
|
|
413
|
+
```json
|
|
414
|
+
{ "extends": ["github>Dharun4242/Facepe//facepe-sdk/renovate-host-preset"] }
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Pin the version (the preset does), keep the lockfile committed, and let the
|
|
418
|
+
host's CI and visual regression run on the PR. To roll back, revert to the
|
|
419
|
+
previous pinned version.
|
|
420
|
+
|
|
421
|
+
## Adding to the public API
|
|
422
|
+
|
|
423
|
+
`src/index.ts` is the only public entry point and holds exports only. Add each
|
|
424
|
+
new public component, hook or type there by name (`export { X } from ...`,
|
|
425
|
+
`export type { Y } from ...`), never with `export *`. Anything not listed there
|
|
426
|
+
is internal.
|
|
427
|
+
|
|
428
|
+
## Project layout
|
|
429
|
+
|
|
430
|
+
```
|
|
431
|
+
facepe-sdk/
|
|
432
|
+
├── src/
|
|
433
|
+
│ ├── index.ts public entry point: the only file consumers import from
|
|
434
|
+
│ ├── components/ FacePe components (Form/, Picker/, Timeline/, Placement/)
|
|
435
|
+
│ ├── context/ FacePeProvider and useFacePe
|
|
436
|
+
│ ├── types/ public TypeScript contracts
|
|
437
|
+
│ ├── events/ event emitter shared by components (not public)
|
|
438
|
+
│ ├── api/ API client, one per provider (not public)
|
|
439
|
+
│ ├── version.ts SDK_VERSION
|
|
440
|
+
│ └── globals.d.ts build-time constants and CSS Module typings
|
|
441
|
+
├── scripts/ build helpers (CommonJS declarations)
|
|
442
|
+
├── package.json package metadata, exports, peer dependencies
|
|
443
|
+
├── tsconfig.json strict TypeScript, declaration output
|
|
444
|
+
└── vite.config.ts library-mode build, React kept external
|
|
445
|
+
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { FacePeTelemetryReporter } from '../events/telemetry.cjs';
|
|
2
|
+
import type { AsyncResult, FacePeSessionExpiredPayload, FacePeTokenProvider, JsonValue, Result } from '../types/index.cjs';
|
|
3
|
+
/** Request header carrying the correlation id (the gateway's agreed name). */
|
|
4
|
+
export declare const CORRELATION_HEADER = "X-Request-Id";
|
|
5
|
+
/** Request header carrying the SDK version, for the gateway's per-version metrics. */
|
|
6
|
+
export declare const SDK_VERSION_HEADER = "X-FacePe-SDK-Version";
|
|
7
|
+
/** The audience every token is requested for (architecture §7.4: the FacePe service). */
|
|
8
|
+
export declare const TOKEN_AUDIENCE = "app-b";
|
|
9
|
+
/** Per-attempt timeout when neither the provider nor the call sets one. */
|
|
10
|
+
export declare const DEFAULT_TIMEOUT_MS = 15000;
|
|
11
|
+
/** Provider settings, read afresh on every request. */
|
|
12
|
+
export type FacePeApiSettings = {
|
|
13
|
+
readonly baseUrl?: string;
|
|
14
|
+
readonly getToken?: FacePeTokenProvider;
|
|
15
|
+
readonly timeoutMs?: number;
|
|
16
|
+
};
|
|
17
|
+
export type FacePeApiClientOptions = {
|
|
18
|
+
readonly settings: () => FacePeApiSettings;
|
|
19
|
+
/** Called once a request is refused even after a fresh token. */
|
|
20
|
+
readonly onSessionExpired?: (payload: FacePeSessionExpiredPayload) => void;
|
|
21
|
+
/** Receives one `api.request` telemetry record per call. */
|
|
22
|
+
readonly onTelemetry?: FacePeTelemetryReporter;
|
|
23
|
+
};
|
|
24
|
+
/** Turns response data into the type a caller expects, or a Failure if it does not fit. */
|
|
25
|
+
export type FacePeDecoder<T> = (data: JsonValue) => Result<T>;
|
|
26
|
+
export type FacePeQuery = Readonly<Record<string, string | number | boolean | null | undefined>>;
|
|
27
|
+
export type FacePeRequestOptions = {
|
|
28
|
+
/** Query-string parameters; `null`/`undefined` ones are left out. */
|
|
29
|
+
readonly query?: FacePeQuery;
|
|
30
|
+
/** Overrides the provider's per-attempt timeout for this call. */
|
|
31
|
+
readonly timeoutMs?: number;
|
|
32
|
+
/** Lets the caller cancel (e.g. on unmount); resolves to Failure ABORTED. */
|
|
33
|
+
readonly signal?: AbortSignal;
|
|
34
|
+
/** Let the request outlive the page (e.g. ending a session as the tab closes). */
|
|
35
|
+
readonly keepalive?: boolean;
|
|
36
|
+
};
|
|
37
|
+
type Decoded<T> = FacePeRequestOptions & {
|
|
38
|
+
readonly decode: FacePeDecoder<T>;
|
|
39
|
+
};
|
|
40
|
+
export declare class FacePeApiClient {
|
|
41
|
+
private readonly options;
|
|
42
|
+
/** The last token getToken returned (memory only), for keepalive requests. */
|
|
43
|
+
private lastToken;
|
|
44
|
+
constructor(options: FacePeApiClientOptions);
|
|
45
|
+
/**
|
|
46
|
+
* A full address for an asset the API returned as a path (e.g. an avatar's
|
|
47
|
+
* picture, "/avatars/aria.jpg"): resolved against the API's address, not
|
|
48
|
+
* the host page's. Absolute URLs are returned unchanged.
|
|
49
|
+
*/
|
|
50
|
+
assetUrl(url: string | null | undefined): string | null;
|
|
51
|
+
get(path: string, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
52
|
+
get<T>(path: string, options: Decoded<T>): AsyncResult<T>;
|
|
53
|
+
post(path: string, body?: JsonValue, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
54
|
+
post<T>(path: string, body: JsonValue | undefined, options: Decoded<T>): AsyncResult<T>;
|
|
55
|
+
put(path: string, body?: JsonValue, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
56
|
+
put<T>(path: string, body: JsonValue | undefined, options: Decoded<T>): AsyncResult<T>;
|
|
57
|
+
patch(path: string, body?: JsonValue, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
58
|
+
patch<T>(path: string, body: JsonValue | undefined, options: Decoded<T>): AsyncResult<T>;
|
|
59
|
+
delete(path: string, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
60
|
+
delete<T>(path: string, options: Decoded<T>): AsyncResult<T>;
|
|
61
|
+
private send;
|
|
62
|
+
private perform;
|
|
63
|
+
/** The token to send, or `null` when the provider has no token provider. */
|
|
64
|
+
private token;
|
|
65
|
+
/** One HTTP attempt, response body included, under one timeout. */
|
|
66
|
+
private attempt;
|
|
67
|
+
/**
|
|
68
|
+
* Parses the body. The FacePe backend answers `{ success: true, data }` or
|
|
69
|
+
* `{ success: false, error: { code, message } }`; the envelope is unwrapped
|
|
70
|
+
* and its error code kept. Any other JSON body is taken as the data itself.
|
|
71
|
+
*/
|
|
72
|
+
private read;
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { FacePeTelemetryReporter } from '../events/telemetry.js';
|
|
2
|
+
import type { AsyncResult, FacePeSessionExpiredPayload, FacePeTokenProvider, JsonValue, Result } from '../types/index.js';
|
|
3
|
+
/** Request header carrying the correlation id (the gateway's agreed name). */
|
|
4
|
+
export declare const CORRELATION_HEADER = "X-Request-Id";
|
|
5
|
+
/** Request header carrying the SDK version, for the gateway's per-version metrics. */
|
|
6
|
+
export declare const SDK_VERSION_HEADER = "X-FacePe-SDK-Version";
|
|
7
|
+
/** The audience every token is requested for (architecture §7.4: the FacePe service). */
|
|
8
|
+
export declare const TOKEN_AUDIENCE = "app-b";
|
|
9
|
+
/** Per-attempt timeout when neither the provider nor the call sets one. */
|
|
10
|
+
export declare const DEFAULT_TIMEOUT_MS = 15000;
|
|
11
|
+
/** Provider settings, read afresh on every request. */
|
|
12
|
+
export type FacePeApiSettings = {
|
|
13
|
+
readonly baseUrl?: string;
|
|
14
|
+
readonly getToken?: FacePeTokenProvider;
|
|
15
|
+
readonly timeoutMs?: number;
|
|
16
|
+
};
|
|
17
|
+
export type FacePeApiClientOptions = {
|
|
18
|
+
readonly settings: () => FacePeApiSettings;
|
|
19
|
+
/** Called once a request is refused even after a fresh token. */
|
|
20
|
+
readonly onSessionExpired?: (payload: FacePeSessionExpiredPayload) => void;
|
|
21
|
+
/** Receives one `api.request` telemetry record per call. */
|
|
22
|
+
readonly onTelemetry?: FacePeTelemetryReporter;
|
|
23
|
+
};
|
|
24
|
+
/** Turns response data into the type a caller expects, or a Failure if it does not fit. */
|
|
25
|
+
export type FacePeDecoder<T> = (data: JsonValue) => Result<T>;
|
|
26
|
+
export type FacePeQuery = Readonly<Record<string, string | number | boolean | null | undefined>>;
|
|
27
|
+
export type FacePeRequestOptions = {
|
|
28
|
+
/** Query-string parameters; `null`/`undefined` ones are left out. */
|
|
29
|
+
readonly query?: FacePeQuery;
|
|
30
|
+
/** Overrides the provider's per-attempt timeout for this call. */
|
|
31
|
+
readonly timeoutMs?: number;
|
|
32
|
+
/** Lets the caller cancel (e.g. on unmount); resolves to Failure ABORTED. */
|
|
33
|
+
readonly signal?: AbortSignal;
|
|
34
|
+
/** Let the request outlive the page (e.g. ending a session as the tab closes). */
|
|
35
|
+
readonly keepalive?: boolean;
|
|
36
|
+
};
|
|
37
|
+
type Decoded<T> = FacePeRequestOptions & {
|
|
38
|
+
readonly decode: FacePeDecoder<T>;
|
|
39
|
+
};
|
|
40
|
+
export declare class FacePeApiClient {
|
|
41
|
+
private readonly options;
|
|
42
|
+
/** The last token getToken returned (memory only), for keepalive requests. */
|
|
43
|
+
private lastToken;
|
|
44
|
+
constructor(options: FacePeApiClientOptions);
|
|
45
|
+
/**
|
|
46
|
+
* A full address for an asset the API returned as a path (e.g. an avatar's
|
|
47
|
+
* picture, "/avatars/aria.jpg"): resolved against the API's address, not
|
|
48
|
+
* the host page's. Absolute URLs are returned unchanged.
|
|
49
|
+
*/
|
|
50
|
+
assetUrl(url: string | null | undefined): string | null;
|
|
51
|
+
get(path: string, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
52
|
+
get<T>(path: string, options: Decoded<T>): AsyncResult<T>;
|
|
53
|
+
post(path: string, body?: JsonValue, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
54
|
+
post<T>(path: string, body: JsonValue | undefined, options: Decoded<T>): AsyncResult<T>;
|
|
55
|
+
put(path: string, body?: JsonValue, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
56
|
+
put<T>(path: string, body: JsonValue | undefined, options: Decoded<T>): AsyncResult<T>;
|
|
57
|
+
patch(path: string, body?: JsonValue, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
58
|
+
patch<T>(path: string, body: JsonValue | undefined, options: Decoded<T>): AsyncResult<T>;
|
|
59
|
+
delete(path: string, options?: FacePeRequestOptions): AsyncResult<JsonValue>;
|
|
60
|
+
delete<T>(path: string, options: Decoded<T>): AsyncResult<T>;
|
|
61
|
+
private send;
|
|
62
|
+
private perform;
|
|
63
|
+
/** The token to send, or `null` when the provider has no token provider. */
|
|
64
|
+
private token;
|
|
65
|
+
/** One HTTP attempt, response body included, under one timeout. */
|
|
66
|
+
private attempt;
|
|
67
|
+
/**
|
|
68
|
+
* Parses the body. The FacePe backend answers `{ success: true, data }` or
|
|
69
|
+
* `{ success: false, error: { code, message } }`; the envelope is unwrapped
|
|
70
|
+
* and its error code kept. Any other JSON body is taken as the data itself.
|
|
71
|
+
*/
|
|
72
|
+
private read;
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FacePeApiClient } from './FacePeApiClient.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Internal: the API client of the nearest `<FacePeProvider>`. Kept apart from
|
|
4
|
+
* the public context so `useFacePe()` and `FacePeContextValue` are unchanged.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FacePeApiContext: import("react").Context<FacePeApiClient | null>;
|
|
7
|
+
/** The API client of the nearest `<FacePeProvider>`, for SDK components. */
|
|
8
|
+
export declare function useFacePeApi(): FacePeApiClient;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FacePeApiClient } from './FacePeApiClient.js';
|
|
2
|
+
/**
|
|
3
|
+
* Internal: the API client of the nearest `<FacePeProvider>`. Kept apart from
|
|
4
|
+
* the public context so `useFacePe()` and `FacePeContextValue` are unchanged.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FacePeApiContext: import("react").Context<FacePeApiClient | null>;
|
|
7
|
+
/** The API client of the nearest `<FacePeProvider>`, for SDK components. */
|
|
8
|
+
export declare function useFacePeApi(): FacePeApiClient;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal: runtime validation of gateway payloads — the trust boundary, the
|
|
3
|
+
* one place types are checked at runtime (architecture §7.5). Each decoder
|
|
4
|
+
* accepts the documented shape, ignores unknown fields (forward
|
|
5
|
+
* compatibility) and turns anything else into Failure INVAL_RESPONSE-style
|
|
6
|
+
* `INVALID_PAYLOAD`, never an exception.
|
|
7
|
+
*/
|
|
8
|
+
import type { FacePeAvatarInfo, FacePeFormValues, FacePePickerOption, FacePeTimelineItem } from '../types/index.cjs';
|
|
9
|
+
import type { FacePeDecoder } from './FacePeApiClient.cjs';
|
|
10
|
+
/** GET /orders/:id/timeline → `{ items: FacePeTimelineItem[] }`. */
|
|
11
|
+
export declare const decodeTimeline: FacePeDecoder<readonly FacePeTimelineItem[]>;
|
|
12
|
+
/** GET /locations/:id/categories → `{ options: FacePePickerOption[] }`. */
|
|
13
|
+
export declare const decodePickerOptions: FacePeDecoder<readonly FacePePickerOption[]>;
|
|
14
|
+
/** GET/PUT /orders/:id/customer → `{ name, email, notes }`. */
|
|
15
|
+
export declare const decodeFormValues: FacePeDecoder<FacePeFormValues>;
|
|
16
|
+
/** GET /avatars/:avatarId → `{ avatar }`. */
|
|
17
|
+
export declare const decodeAvatar: FacePeDecoder<FacePeAvatarInfo>;
|
|
18
|
+
/** GET /avatars → `{ avatars }`: the avatars the signed-in user may use. Entries that do not decode are skipped. */
|
|
19
|
+
export declare const decodeAvatarList: FacePeDecoder<readonly FacePeAvatarInfo[]>;
|
|
20
|
+
/**
|
|
21
|
+
* What POST /avatars/:avatarId/sessions returns: how to connect. The gateway
|
|
22
|
+
* never says where an avatar comes from, only the kind of connection:
|
|
23
|
+
* `room` (join a media room) or `stream` (a hosted stream).
|
|
24
|
+
*/
|
|
25
|
+
export type FacePeAvatarConnection = {
|
|
26
|
+
readonly type: 'stream';
|
|
27
|
+
readonly usageId: string | null;
|
|
28
|
+
readonly avatar: FacePeAvatarInfo;
|
|
29
|
+
readonly token: string;
|
|
30
|
+
readonly greeting: string | null;
|
|
31
|
+
} | {
|
|
32
|
+
readonly type: 'room';
|
|
33
|
+
readonly usageId: string | null;
|
|
34
|
+
readonly avatar: FacePeAvatarInfo;
|
|
35
|
+
readonly url: string;
|
|
36
|
+
readonly token: string;
|
|
37
|
+
};
|
|
38
|
+
export declare const decodeAvatarConnection: FacePeDecoder<FacePeAvatarConnection>;
|
|
39
|
+
/** Gateway paths, one per component (entity ids are path-encoded). */
|
|
40
|
+
export declare const gatewayPath: {
|
|
41
|
+
readonly avatars: "/avatars";
|
|
42
|
+
readonly avatar: (avatarId: string) => string;
|
|
43
|
+
readonly avatarSessions: (avatarId: string) => string;
|
|
44
|
+
readonly endAvatarSession: (usageId: string) => string;
|
|
45
|
+
readonly timeline: (orderId: string) => string;
|
|
46
|
+
readonly pickerOptions: (locationId: string) => string;
|
|
47
|
+
readonly customer: (orderId: string) => string;
|
|
48
|
+
};
|