@captello/ulc-webview-sdk 0.5.0 → 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/CHANGELOG.md ADDED
@@ -0,0 +1,131 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - First stable release. Adds `defaultFormValues`, makes `embedUrl` required, and simplifies the
8
+ React callback signatures.
9
+
10
+ **`defaultFormValues`** — a new option on `<CaptelloForm>` and `useCaptelloWebview` that
11
+ populates the form as soon as it reports `form_load_complete`:
12
+
13
+ ```tsx
14
+ <CaptelloForm
15
+ embedUrl={{ baseUrl: "https://capture.captello.com", eventWebAccessToken }}
16
+ defaultFormValues={{
17
+ info: [{ ll_field_unique_identifier: "Email", value: user.email }],
18
+ submission: { data: { "1042": "Acme Inc." } },
19
+ }}
20
+ />
21
+ ```
22
+
23
+ It's the declarative form of `prefill(...)` — same wire message, same shapes — so seeding a
24
+ form no longer needs a `ref`, an effect, or a readiness check. It is sent as the _first_
25
+ outbound message (a later explicit `prefill(...)` still wins) and read once at mount, so
26
+ changing the prop afterwards does not re-populate the form. No memoization required.
27
+
28
+ **Breaking — callbacks receive payloads, not message envelopes.** The React callback props
29
+ are named after their message type, so the `type` discriminator they carried was redundant.
30
+ Each now receives just the payload, and payload-free messages pass no argument:
31
+
32
+ ```diff
33
+ -onSubmissionBody={(m) => save(m.data)}
34
+ +onSubmissionBody={(body) => save(body)} // or simply: onSubmissionBody={save}
35
+
36
+ -onFormErrorMessage={(m) => showToast(m.data)}
37
+ +onFormErrorMessage={(message) => showToast(message)}
38
+
39
+ -onFormLoadComplete={(m) => track(m.type)}
40
+ +onFormLoadComplete={() => track("form_load_complete")}
41
+
42
+ -onFormSubmitSuccess={(m) => log(m.action)}
43
+ +onFormSubmitSuccess={(action) => log(action)}
44
+ ```
45
+
46
+ | Callback | Now receives |
47
+ | ----------------------------- | ---------------------- |
48
+ | `onFormLoadComplete` | — (no payload) |
49
+ | `onFormErrorMessage` | `string` |
50
+ | `onSubmissionBody` | `SubmissionBody` |
51
+ | `onFormSubmitSuccess` | `"create" \| "update"` |
52
+ | `onConnexionsProfileRedirect` | — (no payload) |
53
+ | `onConnexionsDownloadVcard` | — (no payload) |
54
+
55
+ `onAnyMessage` is unchanged — it fires for every type, so it still gets the whole message
56
+ including `type`. The low-level `CaptelloWebview.on(type, listener)` is also unchanged.
57
+
58
+ **Breaking — `embedUrl` is now required** on both `<CaptelloForm>` and
59
+ `useCaptelloWebview`, and `targetOrigin` has been dropped from their options: the origin is
60
+ always derived from `embedUrl`. Consequently `iframeProps.src` is now always a `string`, and
61
+ a `src` passed via `<CaptelloForm iframeProps={{ src }}>` is ignored.
62
+
63
+ To migrate, move your URL into `embedUrl`:
64
+
65
+ ```diff
66
+ -useCaptelloWebview({ targetOrigin: "https://capture.captello.com", ... })
67
+ +useCaptelloWebview({ embedUrl: { baseUrl: "https://capture.captello.com", eventWebAccessToken }, ... })
68
+ ```
69
+
70
+ If you need to own both the URL and the origin, use the `CaptelloWebview` class directly —
71
+ it still accepts `targetOrigin`.
72
+
73
+ ## 0.6.0
74
+
75
+ ### Minor Changes
76
+
77
+ - **Breaking:** `visible_submissions_data` now reports `simple_name` and `address` values as
78
+ objects keyed by their transcription identifier (`ll_field_unique_identifier`) instead of by
79
+ opaque sub-element id.
80
+
81
+ - `simple_name` → `NameSubmissionValue` (`{ FirstName?, LastName? }`)
82
+ - `address` → `AddressSubmissionValue` (`{ StreetAddress?, StreetAddress2?, City?, State?, Zipcode?, Country? }`)
83
+
84
+ Hidden sub-fields are omitted, so all keys are optional. The shared `CompositeSubmissionValue`
85
+ type (sub-element-id → string) is removed; narrow on `element_type` and read the named fields.
86
+
87
+ ```ts
88
+ // before
89
+ case FormElementType.simple_name:
90
+ Object.values(item.element_value).join(" "); // { "el_1_1": "Jane", "el_1_2": "Doe" }
91
+
92
+ // after
93
+ case FormElementType.simple_name:
94
+ [item.element_value.FirstName, item.element_value.LastName].filter(Boolean).join(" ");
95
+ ```
96
+
97
+ ## 0.5.0
98
+
99
+ ### Minor Changes
100
+
101
+ - 15aef07: Add `<CaptelloForm>` to the React adapter (`@captello/ulc-webview-sdk/react`) — a turnkey
102
+ component built on `useCaptelloWebview`. It renders the `<iframe>`, wires the embed URL and
103
+ message callbacks, shows centered `loading` / `error` overlays, accepts render-prop
104
+ `children` that receive the live api, and forwards a `CaptelloFormHandle` on `ref`
105
+ (`submit`, `reset`, `updateDraft`, `triggerValidation`, `prefill`, `submitAndWait`, plus
106
+ `status` / `isReady`, `getIframe()`, and `getClient()`) so a parent can drive the form
107
+ without lifting state. The hook remains available for callers that prefer to own the markup.
108
+
109
+ ## 0.4.0
110
+
111
+ ### Breaking Changes
112
+
113
+ - **Unified prefill API:** `prefillSubmission()`, `prefillInfo()`, and `prefillSubmissionAndInfo()` have been replaced by a single `prefill()` method.
114
+
115
+ ```ts
116
+ // Before
117
+ client.prefillSubmission(submission);
118
+ client.prefillInfo(info);
119
+ client.prefillSubmissionAndInfo({ submission, info });
120
+
121
+ // After
122
+ client.prefill({ submission });
123
+ client.prefill({ info });
124
+ client.prefill({ submission, info });
125
+ ```
126
+
127
+ - **Removed `PrefillDataType` export:** The enum is now an internal implementation detail. Consumers no longer need to specify a data type discriminator — the SDK resolves it automatically.
128
+
129
+ ### React Hook
130
+
131
+ - `useCaptelloWebview` now returns `prefill` instead of the three separate methods.
package/README.md CHANGED
@@ -145,7 +145,13 @@ function UlcForm({
145
145
  mode: FormMode.Submit,
146
146
  launcher: LauncherType.EventGenWeb,
147
147
  }}
148
- onSubmissionBody={(m) => onSubmitted(m.data)}
148
+ defaultFormValues={{
149
+ info: [
150
+ { ll_field_unique_identifier: "FirstName", value: "Ada" },
151
+ { ll_field_unique_identifier: "Email", value: "ada@example.com" },
152
+ ],
153
+ }}
154
+ onSubmissionBody={onSubmitted}
149
155
  loading={<Spinner />}
150
156
  error={(message) => <ErrorBanner>{message}</ErrorBanner>}
151
157
  >
@@ -159,14 +165,14 @@ function UlcForm({
159
165
  }
160
166
  ```
161
167
 
162
- Props are the hook options (`embedUrl` or `targetOrigin`, every message callback, and the
163
- client options) plus a few rendering conveniences:
168
+ Props are the hook options (the required `embedUrl`, `defaultFormValues`, every message
169
+ callback, and the client options) plus a few rendering conveniences:
164
170
 
165
171
  - **`className` / `style` / `id`** — applied to the wrapper element. Size the form here; the
166
172
  iframe fills it.
167
173
  - **`iframeProps`** — attributes spread onto the `<iframe>` (`title`, `allow`, `sandbox`,
168
174
  `name`, …). Defaults: `title="Captello form"`, `allow="camera; microphone; geolocation"`.
169
- When you drive the URL yourself (no `embedUrl`), set `src` here.
175
+ `src` is ignored the URL comes from `embedUrl`.
170
176
  - **`loading`** — a node shown, centered over the iframe, until `form_load_complete`. The
171
177
  iframe stays mounted underneath so it keeps loading.
172
178
  - **`error`** — a node (or `(message) => node`) shown when the form reports
@@ -201,8 +207,8 @@ function UlcForm({
201
207
  mode: FormMode.Submit,
202
208
  launcher: LauncherType.EventGenWeb,
203
209
  },
204
- onSubmissionBody: (m) => onSubmitted(m.data),
205
- onFormErrorMessage: (m) => showToast(m.data),
210
+ onSubmissionBody: onSubmitted,
211
+ onFormErrorMessage: showToast,
206
212
  });
207
213
 
208
214
  return (
@@ -217,30 +223,77 @@ function UlcForm({
217
223
 
218
224
  What the hook (and the component built on it) handle for you:
219
225
 
220
- - **URL + origin.** Pass `embedUrl: { baseUrl, ...EmbedUrlOptions }` and the hook builds
226
+ - **URL + origin.** `embedUrl: { baseUrl, ...EmbedUrlOptions }` is required; the hook builds
221
227
  the URL, derives `targetOrigin`, and returns it as `iframeProps.src` — no separate
222
- `buildEmbedUrl` / manual `src` wiring to keep in sync. (Prefer
223
- to own the `src`? Omit `embedUrl`, pass `targetOrigin`, and use the returned `ref`.)
228
+ `buildEmbedUrl` / manual `src` wiring to keep in sync. (Need to own both the URL and the
229
+ origin? Use the `CaptelloWebview` class directly.)
224
230
  - **Readiness.** `isReady` and `status` (`"loading" | "ready" | "error"`) — no manual
225
231
  `useState` + `onFormLoadComplete` for a spinner.
226
232
  - **Prefill timing.** Sends made before the form loads are queued and flushed on
227
233
  `form_load_complete`, so you can `prefill(...)` as soon as you have data — no
228
234
  gating on readiness, and no silently-dropped messages.
235
+ - **Default values.** `defaultFormValues: { submission?, info? }` populates the form as soon
236
+ as it's ready, so seeding a known email or a previous submission needs no `ref`, no effect,
237
+ and no readiness check. See below.
229
238
  - **No memoization.** Callbacks are read fresh via a ref, so inline arrow functions
230
- won't re-subscribe or re-create the client. The client is recreated only when
231
- `targetOrigin` (or `embedUrl`) / `hostWindow` / `matchSource` / `queueUntilReady` change.
239
+ won't re-subscribe or re-create the client. The client is recreated only when the
240
+ `embedUrl`-derived origin / `hostWindow` / `matchSource` / `queueUntilReady` change.
232
241
  - **Stable senders** (`submit`, `reset`, `updateDraft`, `triggerValidation`, `prefill`,
233
242
  `submitAndWait`) — safe in deps or passed to children. `getClient()` returns the live
234
243
  client for escape hatches.
235
244
 
245
+ ### `defaultFormValues` — populate the form on load
246
+
247
+ Both `<CaptelloForm>` and `useCaptelloWebview` take a `defaultFormValues` option that seeds
248
+ the form the moment it reports `form_load_complete`:
249
+
250
+ ```tsx
251
+ <CaptelloForm
252
+ embedUrl={{ baseUrl: "https://capture.captello.com", eventWebAccessToken }}
253
+ defaultFormValues={{
254
+ // matched by ll_field_unique_identifier
255
+ info: [{ ll_field_unique_identifier: "Email", value: user.email }],
256
+ // and/or keyed by element id, same shape prefill() takes
257
+ submission: { data: { "1042": "Acme Inc." } },
258
+ }}
259
+ />
260
+ ```
261
+
262
+ It's the declarative form of `prefill(...)` — same wire message, same shapes — so you don't
263
+ need a `ref`, an effect, or a readiness check just to seed a form. Semantics:
264
+
265
+ - **Sent first.** It goes out ahead of everything else, so an explicit `prefill(...)` you
266
+ make later overwrites it.
267
+ - **Read once, at mount.** These are _defaults_, not controlled values: changing the prop
268
+ afterwards does **not** re-populate the form. Call `prefill(...)` for that.
269
+ - **No memoization needed.** An inline object literal won't re-fire it or re-create the client.
270
+ - **Omit or leave empty** (`{}`) and no message is sent at all.
271
+
272
+ `submission` accepts a partial, and a whole `SubmissionBody` handed to you by
273
+ `onSubmissionBody` also fits — handy for re-opening a captured lead.
274
+
236
275
  ### Callback props
237
276
 
238
- `useCaptelloWebview` accepts a callback per outbound message `onFormLoadComplete`,
239
- `onFormErrorMessage`, `onSubmissionBody`, `onFormSubmitSuccess`,
240
- `onConnexionsProfileRedirect`, `onConnexionsDownloadVcard` plus `onAnyMessage` (fires
241
- for every message, after the specific handler). All are optional. Connection options
242
- (`embedUrl` or `targetOrigin`, `hostWindow`, `matchSource`, `queueUntilReady`) go in the
243
- same object.
277
+ `useCaptelloWebview` accepts one optional callback per outbound message. Each receives the
278
+ message's **payload**, not the `{ type, … }` envelope — the callback name already tells you
279
+ the type, so there's nothing to discriminate on:
280
+
281
+ | Callback | Receives |
282
+ | ----------------------------- | --------------------------- |
283
+ | `onFormLoadComplete` | — (no payload) |
284
+ | `onFormErrorMessage` | `string` (translated text) |
285
+ | `onSubmissionBody` | `SubmissionBody` |
286
+ | `onFormSubmitSuccess` | `"create" \| "update"` |
287
+ | `onConnexionsProfileRedirect` | — (no payload) |
288
+ | `onConnexionsDownloadVcard` | — (no payload) |
289
+ | `onAnyMessage` | the whole `OutboundMessage` |
290
+
291
+ `onAnyMessage` is the exception: it fires for every type (after the specific handler), so it
292
+ gets the full message including `type`. So does the low-level `CaptelloWebview.on(type, …)`,
293
+ which is unchanged — envelopes there, payloads here.
294
+
295
+ `embedUrl` (required), `defaultFormValues`, and the client options (`hostWindow`,
296
+ `matchSource`, `queueUntilReady`) go in the same object.
244
297
 
245
298
  ### Without the adapter
246
299
 
@@ -295,7 +348,7 @@ have.
295
348
  A `submission_body` payload includes `visible_submissions_data` — one entry per filled,
296
349
  visible element, typed as `VisibleSubmissionDataItem[]` and discriminated by
297
350
  `element_type`. Narrow on `element_type` and `element_value` is precisely typed (string,
298
- string array, composite name/address, order quantities, etc.), so you render it exactly
351
+ string array, name/address objects, order quantities, etc.), so you render it exactly
299
352
  how your UI needs — no flattening helper to fight:
300
353
 
301
354
  ```ts
@@ -312,8 +365,18 @@ webview.on(OutboundMessageType.SubmissionBody, (msg) => {
312
365
  addRow(item.element_title, renderChoices(item.element_value));
313
366
  break;
314
367
  case FormElementType.simple_name:
315
- // element_value: CompositeSubmissionValue (sub-field id string)
316
- addRow(item.element_title, Object.values(item.element_value).join(" "));
368
+ // element_value: NameSubmissionValue ({ FirstName?, LastName? })
369
+ addRow(
370
+ item.element_title,
371
+ [item.element_value.FirstName, item.element_value.LastName].filter(Boolean).join(" "),
372
+ );
373
+ break;
374
+ case FormElementType.address:
375
+ // element_value: AddressSubmissionValue ({ StreetAddress?, City?, State?, Zipcode?, Country?, … })
376
+ addRow(
377
+ item.element_title,
378
+ [item.element_value.StreetAddress, item.element_value.City].filter(Boolean).join(", "),
379
+ );
317
380
  break;
318
381
  // …other element types
319
382
  }
@@ -64,9 +64,26 @@ declare enum FormElementType {
64
64
  star_rating = "star_survey",
65
65
  attachments = "attachments"
66
66
  }
67
- /** Composite element value (simple_name / address): sub-field values keyed by sub-element id. */
68
- type CompositeSubmissionValue = {
69
- [subElementId: string]: string;
67
+ /**
68
+ * `simple_name` value: the visible sub-fields keyed by their transcription identifier
69
+ * (`ll_field_unique_identifier`), the same identifiers used by prefill. Hidden sub-fields are
70
+ * omitted, so keys are optional.
71
+ */
72
+ type NameSubmissionValue = {
73
+ FirstName?: string;
74
+ LastName?: string;
75
+ };
76
+ /**
77
+ * `address` value: the visible sub-fields keyed by their transcription identifier
78
+ * (`ll_field_unique_identifier`). Hidden sub-fields are omitted, so keys are optional.
79
+ */
80
+ type AddressSubmissionValue = {
81
+ StreetAddress?: string;
82
+ StreetAddress2?: string;
83
+ City?: string;
84
+ State?: string;
85
+ Zipcode?: string;
86
+ Country?: string;
70
87
  };
71
88
  /** Checkbox value when the element is an "order" checkbox (quantities + note). */
72
89
  type OrderCheckboxSubmissionData = {
@@ -134,8 +151,8 @@ type VisibleSubmissionElementValueMap = {
134
151
  [FormElementType.business_card]: Partial<BusinessCardValue>;
135
152
  [FormElementType.boolean]: boolean;
136
153
  [FormElementType.attachments]: AttachmentValue[];
137
- [FormElementType.simple_name]: CompositeSubmissionValue;
138
- [FormElementType.address]: CompositeSubmissionValue;
154
+ [FormElementType.simple_name]: NameSubmissionValue;
155
+ [FormElementType.address]: AddressSubmissionValue;
139
156
  };
140
157
  /** Element types that carry a user-facing value (i.e. can appear in visible data). */
141
158
  type VisibleSubmissionElementType = keyof VisibleSubmissionElementValueMap;
@@ -497,4 +514,4 @@ declare class CaptelloWebview {
497
514
  private handleMessage;
498
515
  }
499
516
 
500
- export { type AnyOutboundListener as A, type BusinessCardValue as B, type CaptelloWebviewOptions as C, FormElementType as F, type InboundMessage as I, OutboundMessageType as O, type PrefillInfoItem as P, type SubmissionBody as S, type Unsubscribe as U, type ValidationTarget as V, type OutboundMessageMap as a, SubmissionError as b, SubmissionTimeoutError as c, type OutboundMessage as d, CaptelloWebview as e, type SubmissionPrefill as f, type AttachmentValue as g, type CompositeSubmissionValue as h, InboundMessageType as i, type OrderCheckboxSubmissionData as j, type OrderRadioSubmissionData as k, type OutboundListener as l, type SubmissionQuestionData as m, type VisibleSubmissionDataItem as n, type VisibleSubmissionElementType as o, type VisibleSubmissionElementValueMap as p, parseOutboundMessage as q };
517
+ export { type AddressSubmissionValue as A, type BusinessCardValue as B, type CaptelloWebviewOptions as C, FormElementType as F, type InboundMessage as I, type NameSubmissionValue as N, OutboundMessageType as O, type PrefillInfoItem as P, type SubmissionBody as S, type Unsubscribe as U, type ValidationTarget as V, type OutboundMessageMap as a, SubmissionError as b, SubmissionTimeoutError as c, type OutboundMessage as d, type SubmissionPrefill as e, CaptelloWebview as f, type AnyOutboundListener as g, type AttachmentValue as h, InboundMessageType as i, type OrderCheckboxSubmissionData as j, type OrderRadioSubmissionData as k, type OutboundListener as l, type SubmissionQuestionData as m, type VisibleSubmissionDataItem as n, type VisibleSubmissionElementType as o, type VisibleSubmissionElementValueMap as p, parseOutboundMessage as q };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AnyOutboundListener, g as AttachmentValue, B as BusinessCardValue, e as CaptelloWebview, C as CaptelloWebviewOptions, h as CompositeSubmissionValue, F as FormElementType, I as InboundMessage, i as InboundMessageType, j as OrderCheckboxSubmissionData, k as OrderRadioSubmissionData, l as OutboundListener, d as OutboundMessage, a as OutboundMessageMap, O as OutboundMessageType, P as PrefillInfoItem, S as SubmissionBody, b as SubmissionError, f as SubmissionPrefill, m as SubmissionQuestionData, c as SubmissionTimeoutError, U as Unsubscribe, V as ValidationTarget, n as VisibleSubmissionDataItem, o as VisibleSubmissionElementType, p as VisibleSubmissionElementValueMap, q as parseOutboundMessage } from './client-Dik-Mjid.js';
1
+ export { A as AddressSubmissionValue, g as AnyOutboundListener, h as AttachmentValue, B as BusinessCardValue, f as CaptelloWebview, C as CaptelloWebviewOptions, F as FormElementType, I as InboundMessage, i as InboundMessageType, N as NameSubmissionValue, j as OrderCheckboxSubmissionData, k as OrderRadioSubmissionData, l as OutboundListener, d as OutboundMessage, a as OutboundMessageMap, O as OutboundMessageType, P as PrefillInfoItem, S as SubmissionBody, b as SubmissionError, e as SubmissionPrefill, m as SubmissionQuestionData, c as SubmissionTimeoutError, U as Unsubscribe, V as ValidationTarget, n as VisibleSubmissionDataItem, o as VisibleSubmissionElementType, p as VisibleSubmissionElementValueMap, q as parseOutboundMessage } from './client-CalIoKT6.js';
2
2
 
3
3
  /**
4
4
  * Builder for the Captello capture webview embed URL.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/submission-data.ts"],"names":["FormElementType"],"mappings":";;;;AAiCO,IAAK,eAAA,qBAAAA,gBAAAA,KAAL;AACH,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,SAAA,CAAA,GAAU,eAAA;AACV,EAAAA,iBAAA,YAAA,CAAA,GAAa,SAAA;AACb,EAAAA,iBAAA,KAAA,CAAA,GAAM,KAAA;AACN,EAAAA,iBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,iBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,iBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,iBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,iBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,iBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,iBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,eAAA,CAAA,GAAgB,eAAA;AAChB,EAAAA,iBAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,iBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,iBAAA,WAAA,CAAA,GAAY,kBAAA;AACZ,EAAAA,iBAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,iBAAA,UAAA,CAAA,GAAW,WAAA;AACX,EAAAA,iBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,iBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,iBAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,iBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,iBAAA,gBAAA,CAAA,GAAiB,gBAAA;AACjB,EAAAA,iBAAA,iBAAA,CAAA,GAAkB,uBAAA;AAClB,EAAAA,iBAAA,iBAAA,CAAA,GAAkB,uBAAA;AAClB,EAAAA,iBAAA,mBAAA,CAAA,GAAoB,mBAAA;AACpB,EAAAA,iBAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,iBAAA,aAAA,CAAA,GAAc,aAAA;AApCN,EAAA,OAAAA,gBAAAA;AAAA,CAAA,EAAA,eAAA,IAAA,EAAA","file":"index.js","sourcesContent":["/**\n * Types for a submission's **visible** data — the list a host renders as key/value\n * pairs (one row per filled, visible form element).\n *\n * The webview emits this as `visible_submissions_data` inside the `submission_body`\n * payload: an array of {@link VisibleSubmissionDataItem}, discriminated by\n * `element_type`, where `element_value`'s shape depends on the type. Narrow on\n * `element_type` to get a precisely-typed `element_value` and render it however you\n * like:\n *\n * @example\n * for (const item of submission.visible_submissions_data ?? []) {\n * switch (item.element_type) {\n * case FormElementType.email:\n * row(item.element_title, item.element_value); // element_value: string\n * break;\n * case FormElementType.checkbox:\n * // element_value: string[] | OrderCheckboxSubmissionData\n * break;\n * // …\n * }\n * }\n */\n\n/* ------------------------------------------------------------------ *\n * Element types\n * ------------------------------------------------------------------ */\n\n/**\n * Every form element type, mirroring the webview's `FormElementType`. The string\n * values are the wire values. Structural types (sections, separators, …) never carry\n * a value and never appear in visible submission data.\n */\nexport enum FormElementType {\n email = \"email\",\n section = \"section_block\",\n html_block = \"section\",\n url = \"url\",\n text = \"text\",\n select = \"select\",\n radio = \"radio\",\n simple_name = \"simple_name\",\n textarea = \"textarea\",\n time = \"time\",\n address = \"address\",\n money = \"money\",\n number = \"number\",\n date = \"date\",\n phone = \"phone\",\n simple_phone = \"simple_phone\",\n checkbox = \"checkbox\",\n image = \"image\",\n business_card = \"business_card\",\n signature = \"signature\",\n barcode = \"barcode\",\n separator = \"column_separator\",\n activation = \"activation\",\n document = \"documents\",\n datetime = \"datetime\",\n meeting = \"meeting\",\n audio = \"audio\",\n rating = \"rating\",\n assign_owner = \"assign_owner\",\n boolean = \"boolean\",\n engagem_feeder = \"engagem_feeder\",\n speaker_section = \"speaker_section_block\",\n session_section = \"session_section_block\",\n image_placeholder = \"image_placeholder\",\n star_rating = \"star_survey\",\n attachments = \"attachments\",\n}\n\n/* ------------------------------------------------------------------ *\n * Element value shapes\n * ------------------------------------------------------------------ */\n\n/** Composite element value (simple_name / address): sub-field values keyed by sub-element id. */\nexport type CompositeSubmissionValue = { [subElementId: string]: string };\n\n/** Checkbox value when the element is an \"order\" checkbox (quantities + note). */\nexport type OrderCheckboxSubmissionData = {\n values: { value: string; quantity: number }[];\n note: string;\n};\n\n/** Radio/select value when the element is an \"order\" radio/select (quantity + note). */\nexport type OrderRadioSubmissionData = { value: string; quantity: number; note: string };\n\n/** An uploaded attachment. `blob` is not present on the wire (postMessage JSON drops it). */\nexport type AttachmentValue = { token: string; url: string; name: string; size: number };\n\n/** A business-card capture: front/back image references (either may be absent). */\nexport type BusinessCardValue = { front: string; back: string };\n\n/** One engagement-feeder question/answer entry. */\nexport type SubmissionQuestionData = {\n question: string;\n answers: string[];\n /** `\"\"` when the element is hidden. */\n correct_answer: number | \"\";\n};\n\n/**\n * Maps each value-carrying element type to its `element_value` shape. Structural\n * types are intentionally absent, so they can never appear in a visible item.\n */\nexport type VisibleSubmissionElementValueMap = {\n [FormElementType.email]: string;\n [FormElementType.url]: string;\n [FormElementType.text]: string;\n [FormElementType.textarea]: string;\n [FormElementType.money]: string;\n [FormElementType.number]: string;\n [FormElementType.phone]: string;\n [FormElementType.simple_phone]: string;\n [FormElementType.date]: string;\n [FormElementType.time]: string;\n [FormElementType.datetime]: string;\n [FormElementType.audio]: string;\n [FormElementType.rating]: string;\n [FormElementType.star_rating]: string;\n [FormElementType.assign_owner]: string;\n [FormElementType.signature]: string;\n [FormElementType.barcode]: string;\n [FormElementType.meeting]: string;\n [FormElementType.activation]: string;\n [FormElementType.checkbox]: string[] | OrderCheckboxSubmissionData;\n [FormElementType.radio]: string | OrderRadioSubmissionData;\n [FormElementType.select]: string | OrderRadioSubmissionData;\n [FormElementType.image]: string[];\n [FormElementType.document]: number[];\n [FormElementType.engagem_feeder]: SubmissionQuestionData[];\n [FormElementType.business_card]: Partial<BusinessCardValue>;\n [FormElementType.boolean]: boolean;\n [FormElementType.attachments]: AttachmentValue[];\n [FormElementType.simple_name]: CompositeSubmissionValue;\n [FormElementType.address]: CompositeSubmissionValue;\n};\n\n/** Element types that carry a user-facing value (i.e. can appear in visible data). */\nexport type VisibleSubmissionElementType = keyof VisibleSubmissionElementValueMap;\n\n/**\n * One entry in `visible_submissions_data`, discriminated by `element_type`. Narrowing\n * on `element_type` gives a precisely-typed `element_value`.\n */\nexport type VisibleSubmissionDataItem = {\n [T in VisibleSubmissionElementType]: {\n element_id: string;\n element_title: string;\n element_type: T;\n element_value: VisibleSubmissionElementValueMap[T];\n };\n}[VisibleSubmissionElementType];\n"]}
1
+ {"version":3,"sources":["../src/submission-data.ts"],"names":["FormElementType"],"mappings":";;;;AAiCO,IAAK,eAAA,qBAAAA,gBAAAA,KAAL;AACH,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,SAAA,CAAA,GAAU,eAAA;AACV,EAAAA,iBAAA,YAAA,CAAA,GAAa,SAAA;AACb,EAAAA,iBAAA,KAAA,CAAA,GAAM,KAAA;AACN,EAAAA,iBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,iBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,iBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,iBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,iBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,iBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,iBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,eAAA,CAAA,GAAgB,eAAA;AAChB,EAAAA,iBAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,iBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,iBAAA,WAAA,CAAA,GAAY,kBAAA;AACZ,EAAAA,iBAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,iBAAA,UAAA,CAAA,GAAW,WAAA;AACX,EAAAA,iBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,iBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,iBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,iBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,iBAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,iBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,iBAAA,gBAAA,CAAA,GAAiB,gBAAA;AACjB,EAAAA,iBAAA,iBAAA,CAAA,GAAkB,uBAAA;AAClB,EAAAA,iBAAA,iBAAA,CAAA,GAAkB,uBAAA;AAClB,EAAAA,iBAAA,mBAAA,CAAA,GAAoB,mBAAA;AACpB,EAAAA,iBAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,iBAAA,aAAA,CAAA,GAAc,aAAA;AApCN,EAAA,OAAAA,gBAAAA;AAAA,CAAA,EAAA,eAAA,IAAA,EAAA","file":"index.js","sourcesContent":["/**\n * Types for a submission's **visible** data — the list a host renders as key/value\n * pairs (one row per filled, visible form element).\n *\n * The webview emits this as `visible_submissions_data` inside the `submission_body`\n * payload: an array of {@link VisibleSubmissionDataItem}, discriminated by\n * `element_type`, where `element_value`'s shape depends on the type. Narrow on\n * `element_type` to get a precisely-typed `element_value` and render it however you\n * like:\n *\n * @example\n * for (const item of submission.visible_submissions_data ?? []) {\n * switch (item.element_type) {\n * case FormElementType.email:\n * row(item.element_title, item.element_value); // element_value: string\n * break;\n * case FormElementType.checkbox:\n * // element_value: string[] | OrderCheckboxSubmissionData\n * break;\n * // …\n * }\n * }\n */\n\n/* ------------------------------------------------------------------ *\n * Element types\n * ------------------------------------------------------------------ */\n\n/**\n * Every form element type, mirroring the webview's `FormElementType`. The string\n * values are the wire values. Structural types (sections, separators, …) never carry\n * a value and never appear in visible submission data.\n */\nexport enum FormElementType {\n email = \"email\",\n section = \"section_block\",\n html_block = \"section\",\n url = \"url\",\n text = \"text\",\n select = \"select\",\n radio = \"radio\",\n simple_name = \"simple_name\",\n textarea = \"textarea\",\n time = \"time\",\n address = \"address\",\n money = \"money\",\n number = \"number\",\n date = \"date\",\n phone = \"phone\",\n simple_phone = \"simple_phone\",\n checkbox = \"checkbox\",\n image = \"image\",\n business_card = \"business_card\",\n signature = \"signature\",\n barcode = \"barcode\",\n separator = \"column_separator\",\n activation = \"activation\",\n document = \"documents\",\n datetime = \"datetime\",\n meeting = \"meeting\",\n audio = \"audio\",\n rating = \"rating\",\n assign_owner = \"assign_owner\",\n boolean = \"boolean\",\n engagem_feeder = \"engagem_feeder\",\n speaker_section = \"speaker_section_block\",\n session_section = \"session_section_block\",\n image_placeholder = \"image_placeholder\",\n star_rating = \"star_survey\",\n attachments = \"attachments\",\n}\n\n/* ------------------------------------------------------------------ *\n * Element value shapes\n * ------------------------------------------------------------------ */\n\n/**\n * `simple_name` value: the visible sub-fields keyed by their transcription identifier\n * (`ll_field_unique_identifier`), the same identifiers used by prefill. Hidden sub-fields are\n * omitted, so keys are optional.\n */\nexport type NameSubmissionValue = {\n FirstName?: string;\n LastName?: string;\n};\n\n/**\n * `address` value: the visible sub-fields keyed by their transcription identifier\n * (`ll_field_unique_identifier`). Hidden sub-fields are omitted, so keys are optional.\n */\nexport type AddressSubmissionValue = {\n StreetAddress?: string;\n StreetAddress2?: string;\n City?: string;\n State?: string;\n Zipcode?: string;\n Country?: string;\n};\n\n/** Checkbox value when the element is an \"order\" checkbox (quantities + note). */\nexport type OrderCheckboxSubmissionData = {\n values: { value: string; quantity: number }[];\n note: string;\n};\n\n/** Radio/select value when the element is an \"order\" radio/select (quantity + note). */\nexport type OrderRadioSubmissionData = { value: string; quantity: number; note: string };\n\n/** An uploaded attachment. `blob` is not present on the wire (postMessage JSON drops it). */\nexport type AttachmentValue = { token: string; url: string; name: string; size: number };\n\n/** A business-card capture: front/back image references (either may be absent). */\nexport type BusinessCardValue = { front: string; back: string };\n\n/** One engagement-feeder question/answer entry. */\nexport type SubmissionQuestionData = {\n question: string;\n answers: string[];\n /** `\"\"` when the element is hidden. */\n correct_answer: number | \"\";\n};\n\n/**\n * Maps each value-carrying element type to its `element_value` shape. Structural\n * types are intentionally absent, so they can never appear in a visible item.\n */\nexport type VisibleSubmissionElementValueMap = {\n [FormElementType.email]: string;\n [FormElementType.url]: string;\n [FormElementType.text]: string;\n [FormElementType.textarea]: string;\n [FormElementType.money]: string;\n [FormElementType.number]: string;\n [FormElementType.phone]: string;\n [FormElementType.simple_phone]: string;\n [FormElementType.date]: string;\n [FormElementType.time]: string;\n [FormElementType.datetime]: string;\n [FormElementType.audio]: string;\n [FormElementType.rating]: string;\n [FormElementType.star_rating]: string;\n [FormElementType.assign_owner]: string;\n [FormElementType.signature]: string;\n [FormElementType.barcode]: string;\n [FormElementType.meeting]: string;\n [FormElementType.activation]: string;\n [FormElementType.checkbox]: string[] | OrderCheckboxSubmissionData;\n [FormElementType.radio]: string | OrderRadioSubmissionData;\n [FormElementType.select]: string | OrderRadioSubmissionData;\n [FormElementType.image]: string[];\n [FormElementType.document]: number[];\n [FormElementType.engagem_feeder]: SubmissionQuestionData[];\n [FormElementType.business_card]: Partial<BusinessCardValue>;\n [FormElementType.boolean]: boolean;\n [FormElementType.attachments]: AttachmentValue[];\n [FormElementType.simple_name]: NameSubmissionValue;\n [FormElementType.address]: AddressSubmissionValue;\n};\n\n/** Element types that carry a user-facing value (i.e. can appear in visible data). */\nexport type VisibleSubmissionElementType = keyof VisibleSubmissionElementValueMap;\n\n/**\n * One entry in `visible_submissions_data`, discriminated by `element_type`. Narrowing\n * on `element_type` gives a precisely-typed `element_value`.\n */\nexport type VisibleSubmissionDataItem = {\n [T in VisibleSubmissionElementType]: {\n element_id: string;\n element_title: string;\n element_type: T;\n element_value: VisibleSubmissionElementValueMap[T];\n };\n}[VisibleSubmissionElementType];\n"]}
@@ -1,5 +1,5 @@
1
- import { C as CaptelloWebviewOptions, S as SubmissionBody, O as OutboundMessageType, a as OutboundMessageMap } from './client-Dik-Mjid.js';
2
- export { b as SubmissionError, c as SubmissionTimeoutError } from './client-Dik-Mjid.js';
1
+ import { C as CaptelloWebviewOptions, S as SubmissionBody, O as OutboundMessageType, a as OutboundMessageMap } from './client-CalIoKT6.js';
2
+ export { b as SubmissionError, c as SubmissionTimeoutError } from './client-CalIoKT6.js';
3
3
 
4
4
  /**
5
5
  * Promise-based, one-shot helpers for imperative flows — `@captello/ulc-webview-sdk/promises`.
package/dist/react.d.ts CHANGED
@@ -1,18 +1,33 @@
1
1
  import * as react from 'react';
2
2
  import { CSSProperties, IframeHTMLAttributes, ReactNode, RefCallback } from 'react';
3
- import { C as CaptelloWebviewOptions, a as OutboundMessageMap, O as OutboundMessageType, d as OutboundMessage, e as CaptelloWebview, V as ValidationTarget, f as SubmissionPrefill, P as PrefillInfoItem, S as SubmissionBody } from './client-Dik-Mjid.js';
4
- export { b as SubmissionError, c as SubmissionTimeoutError, U as Unsubscribe } from './client-Dik-Mjid.js';
3
+ import { C as CaptelloWebviewOptions, S as SubmissionBody, d as OutboundMessage, e as SubmissionPrefill, P as PrefillInfoItem, f as CaptelloWebview, V as ValidationTarget } from './client-CalIoKT6.js';
4
+ export { b as SubmissionError, c as SubmissionTimeoutError, U as Unsubscribe } from './client-CalIoKT6.js';
5
5
  import { EmbedUrlOptions } from './index.js';
6
6
 
7
- /** Per-message-type callback props accepted by {@link useCaptelloWebview}. */
7
+ /**
8
+ * Per-message-type callback props accepted by {@link useCaptelloWebview}.
9
+ *
10
+ * Each callback receives the message's **payload**, not the message envelope — the
11
+ * callback name already carries the `type`, so there is nothing to discriminate on.
12
+ * Messages that carry no payload take no argument.
13
+ *
14
+ * {@link CaptelloWebviewCallbacks.onAnyMessage} is the exception: it fires for every
15
+ * type, so it gets the whole message including the `type` discriminator.
16
+ */
8
17
  interface CaptelloWebviewCallbacks {
9
- onFormLoadComplete?: (message: OutboundMessageMap[OutboundMessageType.FormLoadComplete]) => void;
10
- onFormErrorMessage?: (message: OutboundMessageMap[OutboundMessageType.FormErrorMessage]) => void;
11
- onSubmissionBody?: (message: OutboundMessageMap[OutboundMessageType.SubmissionBody]) => void;
12
- onFormSubmitSuccess?: (message: OutboundMessageMap[OutboundMessageType.FormSubmitSuccess]) => void;
13
- onConnexionsProfileRedirect?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsProfileRedirect]) => void;
14
- onConnexionsDownloadVcard?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsDownloadVcard]) => void;
15
- /** Catch-all: called for every outbound message, after the specific handler above. */
18
+ /** The form finished loading and rendering. Safe to interact with it after this. */
19
+ onFormLoadComplete?: () => void;
20
+ /** A user-facing error occurred. Receives the translated, display-ready text. */
21
+ onFormErrorMessage?: (message: string) => void;
22
+ /** Receives the full submission body, for the host to persist / forward. */
23
+ onSubmissionBody?: (body: SubmissionBody) => void;
24
+ /** The form was submitted successfully. Receives whether it created or updated. */
25
+ onFormSubmitSuccess?: (action: "create" | "update") => void;
26
+ /** Connexions: the host should perform the profile redirect (embed mode). */
27
+ onConnexionsProfileRedirect?: () => void;
28
+ /** Connexions: the host should trigger the vCard download (embed mode). */
29
+ onConnexionsDownloadVcard?: () => void;
30
+ /** Catch-all: the full message, including `type`. Called after the specific handler above. */
16
31
  onAnyMessage?: (message: OutboundMessage) => void;
17
32
  }
18
33
  /** Embed-URL config: a base URL plus {@link EmbedUrlOptions}. */
@@ -25,28 +40,54 @@ interface EmbedUrlConfig extends EmbedUrlOptions {
25
40
  baseUrl: string;
26
41
  }
27
42
  /**
28
- * Options for {@link useCaptelloWebview}.
43
+ * Values to seed a form with on load — see {@link UseCaptelloWebviewOptions.defaultFormValues}.
44
+ *
45
+ * `submission` is typed as {@link SubmissionPrefill} (every field optional) so a partial
46
+ * is valid; a whole {@link SubmissionBody} echoed back from `onSubmissionBody` also fits.
47
+ */
48
+ interface DefaultFormValues {
49
+ /** Values keyed by element id / sub-element id, under `data`. */
50
+ submission?: SubmissionPrefill;
51
+ /** Field values matched by `ll_field_unique_identifier` (e.g. `"Email"`). */
52
+ info?: PrefillInfoItem[];
53
+ }
54
+ /**
55
+ * Options for {@link useCaptelloWebview}: the embed config, message callbacks, and the
56
+ * usual client options.
29
57
  *
30
- * Provide **either** `embedUrl` (the hook builds the URL and derives `targetOrigin`,
31
- * returning `iframeProps.src`) **or** your own `targetOrigin` (you set the iframe `src`
32
- * yourself). Plus message callbacks and the usual client options.
58
+ * `embedUrl` is required — the hook builds the URL from it, derives `targetOrigin`, and
59
+ * returns it as `iframeProps.src`. Any `targetOrigin` you pass is ignored; drop to
60
+ * {@link CaptelloWebview} directly if you need to own both the URL and the origin.
33
61
  */
34
- interface UseCaptelloWebviewOptions extends CaptelloWebviewOptions, CaptelloWebviewCallbacks {
62
+ interface UseCaptelloWebviewOptions extends Omit<CaptelloWebviewOptions, "targetOrigin">, CaptelloWebviewCallbacks {
35
63
  /** Build the iframe URL and derive `targetOrigin` from it. Sets `iframeProps.src`. */
36
- embedUrl?: EmbedUrlConfig;
64
+ embedUrl: EmbedUrlConfig;
65
+ /**
66
+ * Values to populate the form with as soon as it is ready. Saves you from wiring a
67
+ * `ref` and calling `prefill(...)` from an effect just to seed the form.
68
+ *
69
+ * Sent as the *first* outbound message, so a later explicit `prefill(...)` wins.
70
+ * Read once when the client attaches — changing the value afterwards does **not**
71
+ * re-populate the form (these are defaults, not controlled values); call `prefill(...)`
72
+ * for that. No memoization needed: an inline object literal is fine.
73
+ *
74
+ * @example
75
+ * defaultFormValues={{ info: [{ ll_field_unique_identifier: "Email", value: user.email }] }}
76
+ */
77
+ defaultFormValues?: DefaultFormValues;
37
78
  }
38
79
  /** Readiness of the embedded form. */
39
80
  type CaptelloWebviewStatus = "loading" | "ready" | "error";
40
- /** Props to spread onto the `<iframe>`. `src` is present only when `embedUrl` is given. */
81
+ /** Props to spread onto the `<iframe>` the ref plus the `embedUrl`-derived `src`. */
41
82
  interface CaptelloIframeProps {
42
83
  ref: RefCallback<HTMLIFrameElement | null>;
43
- src?: string;
84
+ src: string;
44
85
  }
45
86
  /** What {@link useCaptelloWebview} returns. */
46
87
  interface UseCaptelloWebviewResult {
47
- /** Spread onto your iframe: `<iframe {...iframeProps} />`. Includes `src` if `embedUrl` was given. */
88
+ /** Spread onto your iframe: `<iframe {...iframeProps} />`. Carries the `embedUrl`-derived `src`. */
48
89
  iframeProps: CaptelloIframeProps;
49
- /** The iframe ref callback (same as `iframeProps.ref`), if you'd rather set `src` yourself. */
90
+ /** The iframe ref callback (same as `iframeProps.ref`), if you'd rather wire `src` yourself. */
50
91
  ref: RefCallback<HTMLIFrameElement | null>;
51
92
  /** `true` once the form has reported `form_load_complete`. */
52
93
  isReady: boolean;
@@ -82,7 +123,7 @@ interface CaptelloFormHandle extends Pick<UseCaptelloWebviewResult, "submit" | "
82
123
  getIframe: () => HTMLIFrameElement | null;
83
124
  }
84
125
  /**
85
- * Props for {@link CaptelloForm}: every {@link useCaptelloWebviewOptions} option (embed
126
+ * Props for {@link CaptelloForm}: every {@link UseCaptelloWebviewOptions} option (embed
86
127
  * config + message callbacks + client options) plus rendering conveniences.
87
128
  */
88
129
  interface CaptelloFormProps extends UseCaptelloWebviewOptions {
@@ -94,8 +135,8 @@ interface CaptelloFormProps extends UseCaptelloWebviewOptions {
94
135
  id?: string;
95
136
  /**
96
137
  * Attributes spread onto the `<iframe>` — `title`, `allow`, `sandbox`, `name`, etc.
97
- * Defaults: `title="Captello form"`, `allow="camera; microphone; geolocation"`. When you
98
- * drive the URL yourself (no `embedUrl`), set `src` here.
138
+ * Defaults: `title="Captello form"`, `allow="camera; microphone; geolocation"`.
139
+ * `src` is ignored: it comes from `embedUrl`.
99
140
  */
100
141
  iframeProps?: Omit<IframeHTMLAttributes<HTMLIFrameElement>, "ref">;
101
142
  /** Rendered, centered over the iframe, while it is loading. The iframe stays mounted underneath. */
@@ -117,12 +158,12 @@ interface CaptelloFormProps extends UseCaptelloWebviewOptions {
117
158
  * shows your `loading` / `error` overlays, and forwards a {@link CaptelloFormHandle} on
118
159
  * `ref` so a parent can `submit()` / `prefill()` without lifting state.
119
160
  *
120
- * Pass `embedUrl` and the form fills its wrapper — size the form via `className` / `style`
121
- * (an iframe has no intrinsic height). Reach for {@link useCaptelloWebview} instead when
122
- * you need to own the markup.
161
+ * `embedUrl` is required and the form fills its wrapper — size the form via `className` /
162
+ * `style` (an iframe has no intrinsic height). Reach for {@link useCaptelloWebview} instead
163
+ * when you need to own the markup.
123
164
  *
124
165
  * @example
125
- * function UlcForm({ token }: { token: string }) {
166
+ * function UlcForm({ token, email }: { token: string; email: string }) {
126
167
  * const ref = useRef<CaptelloFormHandle>(null);
127
168
  * return (
128
169
  * <CaptelloForm
@@ -134,7 +175,8 @@ interface CaptelloFormProps extends UseCaptelloWebviewOptions {
134
175
  * mode: FormMode.Submit,
135
176
  * launcher: LauncherType.EventGenWeb,
136
177
  * }}
137
- * onSubmissionBody={(m) => save(m.data)}
178
+ * defaultFormValues={{ info: [{ ll_field_unique_identifier: "Email", value: email }] }}
179
+ * onSubmissionBody={save}
138
180
  * loading={<Spinner />}
139
181
  * error={(msg) => <ErrorBanner>{msg}</ErrorBanner>}
140
182
  * >
@@ -145,4 +187,4 @@ interface CaptelloFormProps extends UseCaptelloWebviewOptions {
145
187
  */
146
188
  declare const CaptelloForm: react.ForwardRefExoticComponent<CaptelloFormProps & react.RefAttributes<CaptelloFormHandle>>;
147
189
 
148
- export { CaptelloForm, type CaptelloFormHandle, type CaptelloFormProps, type CaptelloIframeProps, CaptelloWebview, type CaptelloWebviewCallbacks, type CaptelloWebviewStatus, type EmbedUrlConfig, type UseCaptelloWebviewOptions, type UseCaptelloWebviewResult, useCaptelloWebview };
190
+ export { CaptelloForm, type CaptelloFormHandle, type CaptelloFormProps, type CaptelloIframeProps, CaptelloWebview, type CaptelloWebviewCallbacks, type CaptelloWebviewStatus, type DefaultFormValues, type EmbedUrlConfig, type UseCaptelloWebviewOptions, type UseCaptelloWebviewResult, useCaptelloWebview };
package/dist/react.js CHANGED
@@ -4,18 +4,32 @@ export { CaptelloWebview, SubmissionError, SubmissionTimeoutError } from './chun
4
4
  import { forwardRef, useState, useRef, useCallback, useImperativeHandle, useEffect } from 'react';
5
5
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
6
 
7
- var CALLBACK_BY_TYPE = {
8
- ["form_load_complete" /* FormLoadComplete */]: "onFormLoadComplete",
9
- ["form_error_message" /* FormErrorMessage */]: "onFormErrorMessage",
10
- ["submission_body" /* SubmissionBody */]: "onSubmissionBody",
11
- ["form_submit_success" /* FormSubmitSuccess */]: "onFormSubmitSuccess",
12
- ["connexions_profile_redirect" /* ConnexionsProfileRedirect */]: "onConnexionsProfileRedirect",
13
- ["connexions_download_vcard" /* ConnexionsDownloadVcard */]: "onConnexionsDownloadVcard"
14
- };
7
+ function dispatchToCallback(callbacks, message) {
8
+ switch (message.type) {
9
+ case "form_load_complete" /* FormLoadComplete */:
10
+ callbacks.onFormLoadComplete?.();
11
+ break;
12
+ case "form_error_message" /* FormErrorMessage */:
13
+ callbacks.onFormErrorMessage?.(message.data);
14
+ break;
15
+ case "submission_body" /* SubmissionBody */:
16
+ callbacks.onSubmissionBody?.(message.data);
17
+ break;
18
+ case "form_submit_success" /* FormSubmitSuccess */:
19
+ callbacks.onFormSubmitSuccess?.(message.action);
20
+ break;
21
+ case "connexions_profile_redirect" /* ConnexionsProfileRedirect */:
22
+ callbacks.onConnexionsProfileRedirect?.();
23
+ break;
24
+ case "connexions_download_vcard" /* ConnexionsDownloadVcard */:
25
+ callbacks.onConnexionsDownloadVcard?.();
26
+ break;
27
+ }
28
+ }
15
29
  function useCaptelloWebview(options) {
16
30
  const { embedUrl, matchSource, queueUntilReady, hostWindow } = options;
17
- const src = embedUrl ? buildEmbedUrl(embedUrl.baseUrl, embedUrl) : void 0;
18
- const targetOrigin = src ? new URL(src).origin : options.targetOrigin;
31
+ const src = buildEmbedUrl(embedUrl.baseUrl, embedUrl);
32
+ const targetOrigin = new URL(src).origin;
19
33
  const optionsRef = useRef(options);
20
34
  optionsRef.current = options;
21
35
  const clientRef = useRef(null);
@@ -38,13 +52,27 @@ function useCaptelloWebview(options) {
38
52
  });
39
53
  clientRef.current = client;
40
54
  const offs = [];
55
+ const defaults = optionsRef.current.defaultFormValues;
56
+ if (defaults && (defaults.submission != null || defaults.info != null)) {
57
+ if (optionsRef.current.queueUntilReady === false) {
58
+ offs.push(
59
+ client.once("form_load_complete" /* FormLoadComplete */, () => {
60
+ try {
61
+ client.prefill(defaults);
62
+ } catch {
63
+ }
64
+ })
65
+ );
66
+ } else {
67
+ client.prefill(defaults);
68
+ }
69
+ }
41
70
  for (const type of Object.values(OutboundMessageType)) {
42
71
  offs.push(
43
72
  client.on(type, (message) => {
44
73
  if (type === "form_load_complete" /* FormLoadComplete */) setStatus("ready");
45
74
  else if (type === "form_error_message" /* FormErrorMessage */) setStatus("error");
46
- const handler = optionsRef.current[CALLBACK_BY_TYPE[type]];
47
- handler?.(message);
75
+ dispatchToCallback(optionsRef.current, message);
48
76
  optionsRef.current.onAnyMessage?.(message);
49
77
  })
50
78
  );
@@ -85,7 +113,7 @@ function useCaptelloWebview(options) {
85
113
  }
86
114
  return client.submitAndWait(timeoutMs);
87
115
  }, []);
88
- const iframeProps = src ? { ref: attach, src } : { ref: attach };
116
+ const iframeProps = { ref: attach, src };
89
117
  return {
90
118
  iframeProps,
91
119
  ref: attach,
@@ -129,12 +157,12 @@ function CaptelloFormImpl(props, ref) {
129
157
  // Wrap the two status-bearing callbacks to track the error text, then forward to
130
158
  // the caller's handler. The hook reads callbacks fresh, so these inline wrappers
131
159
  // don't re-subscribe or re-create the client.
132
- onFormLoadComplete: (message) => {
160
+ onFormLoadComplete: () => {
133
161
  setErrorMessage(void 0);
134
- onFormLoadComplete?.(message);
162
+ onFormLoadComplete?.();
135
163
  },
136
164
  onFormErrorMessage: (message) => {
137
- setErrorMessage(message.data);
165
+ setErrorMessage(message);
138
166
  onFormErrorMessage?.(message);
139
167
  }
140
168
  });
@@ -163,7 +191,6 @@ function CaptelloFormImpl(props, ref) {
163
191
  }),
164
192
  [api]
165
193
  );
166
- const src = api.iframeProps.src ?? iframeProps?.src;
167
194
  const showLoading = api.status === "loading" && loading != null;
168
195
  const showError = api.status === "error" && error != null;
169
196
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -175,7 +202,7 @@ function CaptelloFormImpl(props, ref) {
175
202
  allow: DEFAULT_ALLOW,
176
203
  ...iframeProps,
177
204
  ref: setIframe,
178
- src,
205
+ src: api.iframeProps.src,
179
206
  style: { ...IFRAME_STYLE, ...iframeProps?.style }
180
207
  }
181
208
  ),
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["/**\n * React adapter for the Captello webview SDK — `@captello/ulc-webview-sdk/react`.\n *\n * Two entry points, same engine:\n * - {@link CaptelloForm} — a turnkey `<iframe>` component. Drop it in with an `embedUrl`\n * and message callbacks; it renders the frame, shows your `loading` / `error` overlays,\n * and exposes the senders via an imperative `ref`. This is the shortest path.\n * - {@link useCaptelloWebview} — the underlying hook, for when you want to own the markup.\n *\n * {@link useCaptelloWebview} owns a {@link CaptelloWebview} for the lifetime of an\n * iframe: it creates the client once the iframe mounts, wires the outbound messages\n * you care about to typed callbacks, tracks readiness, and destroys the client on\n * unmount. You get back `iframeProps` to spread onto your `<iframe>` (or a bare `ref`),\n * an `isReady` flag, and stable senders (`submit`, `reset`, `prefill`, …).\n *\n * Sends made before the form loads are queued by the client and flushed on\n * `form_load_complete`, so you can call `prefill(...)` as soon as you have data —\n * no need to gate on readiness yourself.\n *\n * Callbacks are held in a ref and always called fresh, so you do NOT need to memoize\n * them — passing inline arrow functions will not re-subscribe or re-create the client.\n *\n * `react` is an optional peer dependency; importing this entry point requires React 18+.\n *\n * @example\n * function UlcForm({ token, onSubmitted }: { token: string; onSubmitted: (b: SubmissionBody) => void }) {\n * const { iframeProps, isReady, submit } = useCaptelloWebview({\n * embedUrl: {\n * baseUrl: \"https://capture.captello.com\",\n * eventWebAccessToken: token,\n * mode: FormMode.Submit,\n * launcher: LauncherType.EventGenWeb,\n * },\n * onSubmissionBody: (m) => onSubmitted(m.data),\n * });\n * return (\n * <>\n * {!isReady && <Spinner />}\n * <iframe {...iframeProps} title=\"UlcForm\" allow=\"camera; microphone\" />\n * <button onClick={submit}>Submit</button>\n * </>\n * );\n * }\n */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type IframeHTMLAttributes,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefCallback,\n} from \"react\";\n\nimport { CaptelloWebview } from \"./client\";\nimport type { CaptelloWebviewOptions, Unsubscribe } from \"./client\";\nimport { buildEmbedUrl } from \"./embed-url\";\nimport type { EmbedUrlOptions } from \"./embed-url\";\nimport { OutboundMessageType } from \"./messages\";\nimport type {\n OutboundMessage,\n OutboundMessageMap,\n PrefillInfoItem,\n SubmissionBody,\n SubmissionPrefill,\n ValidationTarget,\n} from \"./messages\";\n\n/** Per-message-type callback props accepted by {@link useCaptelloWebview}. */\nexport interface CaptelloWebviewCallbacks {\n onFormLoadComplete?: (message: OutboundMessageMap[OutboundMessageType.FormLoadComplete]) => void;\n onFormErrorMessage?: (message: OutboundMessageMap[OutboundMessageType.FormErrorMessage]) => void;\n onSubmissionBody?: (message: OutboundMessageMap[OutboundMessageType.SubmissionBody]) => void;\n onFormSubmitSuccess?: (message: OutboundMessageMap[OutboundMessageType.FormSubmitSuccess]) => void;\n onConnexionsProfileRedirect?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsProfileRedirect]) => void;\n onConnexionsDownloadVcard?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsDownloadVcard]) => void;\n /** Catch-all: called for every outbound message, after the specific handler above. */\n onAnyMessage?: (message: OutboundMessage) => void;\n}\n\n/** Embed-URL config: a base URL plus {@link EmbedUrlOptions}. */\nexport interface EmbedUrlConfig extends EmbedUrlOptions {\n /**\n * The capture **origin**, e.g. `\"https://capture.captello.com\"`. The SDK appends the\n * capture path for you, so the origin, a trailing slash, or the full\n * `…/capture/submission` URL all work — see {@link buildEmbedUrl}.\n */\n baseUrl: string;\n}\n\n/**\n * Options for {@link useCaptelloWebview}.\n *\n * Provide **either** `embedUrl` (the hook builds the URL and derives `targetOrigin`,\n * returning `iframeProps.src`) **or** your own `targetOrigin` (you set the iframe `src`\n * yourself). Plus message callbacks and the usual client options.\n */\nexport interface UseCaptelloWebviewOptions extends CaptelloWebviewOptions, CaptelloWebviewCallbacks {\n /** Build the iframe URL and derive `targetOrigin` from it. Sets `iframeProps.src`. */\n embedUrl?: EmbedUrlConfig;\n}\n\n/** Readiness of the embedded form. */\nexport type CaptelloWebviewStatus = \"loading\" | \"ready\" | \"error\";\n\n/** Props to spread onto the `<iframe>`. `src` is present only when `embedUrl` is given. */\nexport interface CaptelloIframeProps {\n ref: RefCallback<HTMLIFrameElement | null>;\n src?: string;\n}\n\n/** What {@link useCaptelloWebview} returns. */\nexport interface UseCaptelloWebviewResult {\n /** Spread onto your iframe: `<iframe {...iframeProps} />`. Includes `src` if `embedUrl` was given. */\n iframeProps: CaptelloIframeProps;\n /** The iframe ref callback (same as `iframeProps.ref`), if you'd rather set `src` yourself. */\n ref: RefCallback<HTMLIFrameElement | null>;\n /** `true` once the form has reported `form_load_complete`. */\n isReady: boolean;\n /** `\"loading\"` → `\"ready\"`; flips to `\"error\"` if a `form_error_message` arrives. */\n status: CaptelloWebviewStatus;\n /** The live client, or `null` before the iframe mounts. For escape-hatch use. */\n getClient: () => CaptelloWebview | null;\n submit: () => void;\n reset: () => void;\n updateDraft: () => void;\n triggerValidation: (target: ValidationTarget) => void;\n prefill: (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) => void;\n submitAndWait: (timeoutMs?: number) => Promise<SubmissionBody>;\n}\n\nconst CALLBACK_BY_TYPE: Record<OutboundMessageType, keyof CaptelloWebviewCallbacks> = {\n [OutboundMessageType.FormLoadComplete]: \"onFormLoadComplete\",\n [OutboundMessageType.FormErrorMessage]: \"onFormErrorMessage\",\n [OutboundMessageType.SubmissionBody]: \"onSubmissionBody\",\n [OutboundMessageType.FormSubmitSuccess]: \"onFormSubmitSuccess\",\n [OutboundMessageType.ConnexionsProfileRedirect]: \"onConnexionsProfileRedirect\",\n [OutboundMessageType.ConnexionsDownloadVcard]: \"onConnexionsDownloadVcard\",\n};\n\n/**\n * Binds a {@link CaptelloWebview} to an iframe's lifecycle. See the module doc for usage.\n */\nexport function useCaptelloWebview(options: UseCaptelloWebviewOptions): UseCaptelloWebviewResult {\n const { embedUrl, matchSource, queueUntilReady, hostWindow } = options;\n\n // Resolve the URL + the targetOrigin to use. embedUrl wins; otherwise use the\n // explicit targetOrigin. Recompute only when the URL-affecting inputs change.\n const src = embedUrl ? buildEmbedUrl(embedUrl.baseUrl, embedUrl) : undefined;\n const targetOrigin = src ? new URL(src).origin : options.targetOrigin;\n\n // Latest options/callbacks, read fresh inside listeners so callers needn't memoize.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const clientRef = useRef<CaptelloWebview | null>(null);\n const frameRef = useRef<HTMLIFrameElement | null>(null);\n const teardown = useRef<(() => void) | null>(null);\n\n const [status, setStatus] = useState<CaptelloWebviewStatus>(\"loading\");\n\n const attach = useCallback(\n (frame: HTMLIFrameElement | null) => {\n // Tear down any previous client (ref changed or unmounting).\n teardown.current?.();\n teardown.current = null;\n clientRef.current = null;\n frameRef.current = frame;\n setStatus(\"loading\");\n\n if (!frame) return;\n\n const client = new CaptelloWebview(frame, {\n targetOrigin,\n hostWindow: optionsRef.current.hostWindow,\n matchSource: optionsRef.current.matchSource,\n queueUntilReady: optionsRef.current.queueUntilReady,\n });\n clientRef.current = client;\n\n const offs: Unsubscribe[] = [];\n for (const type of Object.values(OutboundMessageType)) {\n offs.push(\n client.on(type, (message) => {\n if (type === OutboundMessageType.FormLoadComplete) setStatus(\"ready\");\n else if (type === OutboundMessageType.FormErrorMessage) setStatus(\"error\");\n\n const handler = optionsRef.current[CALLBACK_BY_TYPE[type]] as\n | ((m: typeof message) => void)\n | undefined;\n handler?.(message);\n optionsRef.current.onAnyMessage?.(message);\n }),\n );\n }\n\n teardown.current = () => {\n for (const off of offs) off();\n client.destroy();\n };\n },\n // Re-create the client only when connection-level inputs change.\n // Callbacks are read via optionsRef, so they intentionally aren't deps.\n [targetOrigin, matchSource, queueUntilReady, hostWindow],\n );\n\n useEffect(() => {\n if (frameRef.current) attach(frameRef.current);\n return () => {\n teardown.current?.();\n teardown.current = null;\n clientRef.current = null;\n };\n }, [attach]);\n\n const getClient = useCallback(() => clientRef.current, []);\n\n const submit = useCallback(() => clientRef.current?.submit(), []);\n const reset = useCallback(() => clientRef.current?.reset(), []);\n const updateDraft = useCallback(() => clientRef.current?.updateDraft(), []);\n const triggerValidation = useCallback(\n (target: ValidationTarget) => clientRef.current?.triggerValidation(target),\n [],\n );\n const prefill = useCallback(\n (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) => clientRef.current?.prefill(data),\n [],\n );\n const submitAndWait = useCallback((timeoutMs?: number) => {\n const client = clientRef.current;\n if (!client) {\n return Promise.reject(new Error(\"CaptelloWebview: iframe is not mounted yet.\"));\n }\n return client.submitAndWait(timeoutMs);\n }, []);\n\n const iframeProps: CaptelloIframeProps = src ? { ref: attach, src } : { ref: attach };\n\n return {\n iframeProps,\n ref: attach,\n isReady: status === \"ready\",\n status,\n getClient,\n submit,\n reset,\n updateDraft,\n triggerValidation,\n prefill,\n submitAndWait,\n };\n}\n\n/* ------------------------------------------------------------------ *\n * <CaptelloForm /> — the turnkey component\n * ------------------------------------------------------------------ */\n\n/** Default iframe permissions for a capture form (business-card camera scan, mic, geo). */\nconst DEFAULT_ALLOW = \"camera; microphone; geolocation\";\n\n/** Wrapper is the positioning context for the loading / error overlays. */\nconst WRAPPER_STYLE: CSSProperties = { position: \"relative\" };\n\n/** The iframe fills the wrapper; size the component, not this. */\nconst IFRAME_STYLE: CSSProperties = { display: \"block\", width: \"100%\", height: \"100%\", border: 0 };\n\n/** Centers the `loading` / `error` node over the iframe. */\nconst OVERLAY_STYLE: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\n/**\n * Imperative handle exposed on {@link CaptelloForm}'s `ref` — the same senders the hook\n * returns, plus the current status and the underlying `<iframe>` node. Lets a parent\n * drive the form (e.g. an external submit button) without lifting state.\n */\nexport interface CaptelloFormHandle\n extends Pick<\n UseCaptelloWebviewResult,\n \"submit\" | \"reset\" | \"updateDraft\" | \"triggerValidation\" | \"prefill\" | \"submitAndWait\" | \"getClient\"\n > {\n /** Current readiness: `\"loading\" | \"ready\" | \"error\"`. */\n readonly status: CaptelloWebviewStatus;\n /** `true` once the form has reported `form_load_complete`. */\n readonly isReady: boolean;\n /** The underlying `<iframe>` DOM node, or `null` before it mounts. */\n getIframe: () => HTMLIFrameElement | null;\n}\n\n/**\n * Props for {@link CaptelloForm}: every {@link useCaptelloWebviewOptions} option (embed\n * config + message callbacks + client options) plus rendering conveniences.\n */\nexport interface CaptelloFormProps extends UseCaptelloWebviewOptions {\n /** `className` for the wrapper element. */\n className?: string;\n /** `style` for the wrapper element — size the form here. The component adds `position: relative`; your values win. */\n style?: CSSProperties;\n /** `id` for the wrapper element. */\n id?: string;\n /**\n * Attributes spread onto the `<iframe>` — `title`, `allow`, `sandbox`, `name`, etc.\n * Defaults: `title=\"Captello form\"`, `allow=\"camera; microphone; geolocation\"`. When you\n * drive the URL yourself (no `embedUrl`), set `src` here.\n */\n iframeProps?: Omit<IframeHTMLAttributes<HTMLIFrameElement>, \"ref\">;\n /** Rendered, centered over the iframe, while it is loading. The iframe stays mounted underneath. */\n loading?: ReactNode;\n /**\n * Rendered, centered over the iframe, when the form reports `form_error_message`.\n * Pass a function to receive the translated, display-ready error text.\n */\n error?: ReactNode | ((message: string | undefined) => ReactNode);\n /**\n * Inline controls rendered after the form. A function receives the live api\n * (status + senders), so you can wire a submit button without a `ref`.\n */\n children?: ReactNode | ((api: UseCaptelloWebviewResult) => ReactNode);\n}\n\nfunction CaptelloFormImpl(props: CaptelloFormProps, ref: Ref<CaptelloFormHandle>): ReactElement {\n const {\n className,\n style,\n id,\n iframeProps,\n loading,\n error,\n children,\n onFormLoadComplete,\n onFormErrorMessage,\n ...options\n } = props;\n\n // The translated error text from the last form_error_message, for the `error` render.\n const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);\n\n const api = useCaptelloWebview({\n ...options,\n // Wrap the two status-bearing callbacks to track the error text, then forward to\n // the caller's handler. The hook reads callbacks fresh, so these inline wrappers\n // don't re-subscribe or re-create the client.\n onFormLoadComplete: (message) => {\n setErrorMessage(undefined);\n onFormLoadComplete?.(message);\n },\n onFormErrorMessage: (message) => {\n setErrorMessage(message.data);\n onFormErrorMessage?.(message);\n },\n });\n\n // Merge the hook's iframe ref with our own node ref so getIframe() can return the DOM node.\n const nodeRef = useRef<HTMLIFrameElement | null>(null);\n const hookRef = api.iframeProps.ref;\n const setIframe = useCallback<RefCallback<HTMLIFrameElement | null>>(\n (node) => {\n nodeRef.current = node;\n hookRef(node);\n },\n [hookRef],\n );\n\n useImperativeHandle(\n ref,\n () => ({\n submit: api.submit,\n reset: api.reset,\n updateDraft: api.updateDraft,\n triggerValidation: api.triggerValidation,\n prefill: api.prefill,\n submitAndWait: api.submitAndWait,\n getClient: api.getClient,\n getIframe: () => nodeRef.current,\n status: api.status,\n isReady: api.isReady,\n }),\n [api],\n );\n\n // embedUrl-derived src wins; otherwise use a src the caller passed via iframeProps.\n const src = api.iframeProps.src ?? iframeProps?.src;\n const showLoading = api.status === \"loading\" && loading != null;\n const showError = api.status === \"error\" && error != null;\n\n return (\n <>\n <div className={className} id={id} style={{ ...WRAPPER_STYLE, ...style }}>\n <iframe\n title=\"Captello form\"\n allow={DEFAULT_ALLOW}\n {...iframeProps}\n ref={setIframe}\n src={src}\n style={{ ...IFRAME_STYLE, ...iframeProps?.style }}\n />\n {showLoading ? <div style={OVERLAY_STYLE}>{loading}</div> : null}\n {showError ? (\n <div style={OVERLAY_STYLE}>{typeof error === \"function\" ? error(errorMessage) : error}</div>\n ) : null}\n </div>\n {typeof children === \"function\" ? children(api) : children}\n </>\n );\n}\n\n/**\n * Turnkey component for embedding a Captello capture form — the shortest path to a\n * working integration. Renders the `<iframe>`, wires {@link useCaptelloWebview} to it,\n * shows your `loading` / `error` overlays, and forwards a {@link CaptelloFormHandle} on\n * `ref` so a parent can `submit()` / `prefill()` without lifting state.\n *\n * Pass `embedUrl` and the form fills its wrapper — size the form via `className` / `style`\n * (an iframe has no intrinsic height). Reach for {@link useCaptelloWebview} instead when\n * you need to own the markup.\n *\n * @example\n * function UlcForm({ token }: { token: string }) {\n * const ref = useRef<CaptelloFormHandle>(null);\n * return (\n * <CaptelloForm\n * ref={ref}\n * style={{ height: 600 }}\n * embedUrl={{\n * baseUrl: \"https://capture.captello.com\",\n * eventWebAccessToken: token,\n * mode: FormMode.Submit,\n * launcher: LauncherType.EventGenWeb,\n * }}\n * onSubmissionBody={(m) => save(m.data)}\n * loading={<Spinner />}\n * error={(msg) => <ErrorBanner>{msg}</ErrorBanner>}\n * >\n * {({ isReady }) => <button disabled={!isReady} onClick={() => ref.current?.submit()}>Submit</button>}\n * </CaptelloForm>\n * );\n * }\n */\nexport const CaptelloForm = forwardRef(CaptelloFormImpl);\nCaptelloForm.displayName = \"CaptelloForm\";\n\nexport { CaptelloWebview, SubmissionError, SubmissionTimeoutError } from \"./client\";\nexport type { Unsubscribe } from \"./client\";\n"]}
1
+ {"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["/**\n * React adapter for the Captello webview SDK — `@captello/ulc-webview-sdk/react`.\n *\n * Two entry points, same engine:\n * - {@link CaptelloForm} — a turnkey `<iframe>` component. Drop it in with an `embedUrl`\n * and message callbacks; it renders the frame, shows your `loading` / `error` overlays,\n * and exposes the senders via an imperative `ref`. This is the shortest path.\n * - {@link useCaptelloWebview} — the underlying hook, for when you want to own the markup.\n *\n * {@link useCaptelloWebview} owns a {@link CaptelloWebview} for the lifetime of an\n * iframe: it creates the client once the iframe mounts, wires the outbound messages\n * you care about to typed callbacks, tracks readiness, and destroys the client on\n * unmount. You get back `iframeProps` to spread onto your `<iframe>` (or a bare `ref`),\n * an `isReady` flag, and stable senders (`submit`, `reset`, `prefill`, …).\n *\n * Sends made before the form loads are queued by the client and flushed on\n * `form_load_complete`, so you can call `prefill(...)` as soon as you have data —\n * no need to gate on readiness yourself. To seed a form declaratively, pass\n * `defaultFormValues` instead and skip the `prefill(...)` wiring entirely.\n *\n * Callbacks are held in a ref and always called fresh, so you do NOT need to memoize\n * them — passing inline arrow functions will not re-subscribe or re-create the client.\n *\n * `react` is an optional peer dependency; importing this entry point requires React 18+.\n *\n * @example\n * function UlcForm({ token, onSubmitted }: { token: string; onSubmitted: (b: SubmissionBody) => void }) {\n * const { iframeProps, isReady, submit } = useCaptelloWebview({\n * embedUrl: {\n * baseUrl: \"https://capture.captello.com\",\n * eventWebAccessToken: token,\n * mode: FormMode.Submit,\n * launcher: LauncherType.EventGenWeb,\n * },\n * onSubmissionBody: onSubmitted,\n * });\n * return (\n * <>\n * {!isReady && <Spinner />}\n * <iframe {...iframeProps} title=\"UlcForm\" allow=\"camera; microphone\" />\n * <button onClick={submit}>Submit</button>\n * </>\n * );\n * }\n */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type IframeHTMLAttributes,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefCallback,\n} from \"react\";\n\nimport { CaptelloWebview } from \"./client\";\nimport type { CaptelloWebviewOptions, Unsubscribe } from \"./client\";\nimport { buildEmbedUrl } from \"./embed-url\";\nimport type { EmbedUrlOptions } from \"./embed-url\";\nimport { OutboundMessageType } from \"./messages\";\nimport type {\n OutboundMessage,\n PrefillInfoItem,\n SubmissionBody,\n SubmissionPrefill,\n ValidationTarget,\n} from \"./messages\";\n\n/**\n * Per-message-type callback props accepted by {@link useCaptelloWebview}.\n *\n * Each callback receives the message's **payload**, not the message envelope — the\n * callback name already carries the `type`, so there is nothing to discriminate on.\n * Messages that carry no payload take no argument.\n *\n * {@link CaptelloWebviewCallbacks.onAnyMessage} is the exception: it fires for every\n * type, so it gets the whole message including the `type` discriminator.\n */\nexport interface CaptelloWebviewCallbacks {\n /** The form finished loading and rendering. Safe to interact with it after this. */\n onFormLoadComplete?: () => void;\n /** A user-facing error occurred. Receives the translated, display-ready text. */\n onFormErrorMessage?: (message: string) => void;\n /** Receives the full submission body, for the host to persist / forward. */\n onSubmissionBody?: (body: SubmissionBody) => void;\n /** The form was submitted successfully. Receives whether it created or updated. */\n onFormSubmitSuccess?: (action: \"create\" | \"update\") => void;\n /** Connexions: the host should perform the profile redirect (embed mode). */\n onConnexionsProfileRedirect?: () => void;\n /** Connexions: the host should trigger the vCard download (embed mode). */\n onConnexionsDownloadVcard?: () => void;\n /** Catch-all: the full message, including `type`. Called after the specific handler above. */\n onAnyMessage?: (message: OutboundMessage) => void;\n}\n\n/** Embed-URL config: a base URL plus {@link EmbedUrlOptions}. */\nexport interface EmbedUrlConfig extends EmbedUrlOptions {\n /**\n * The capture **origin**, e.g. `\"https://capture.captello.com\"`. The SDK appends the\n * capture path for you, so the origin, a trailing slash, or the full\n * `…/capture/submission` URL all work — see {@link buildEmbedUrl}.\n */\n baseUrl: string;\n}\n\n/**\n * Values to seed a form with on load — see {@link UseCaptelloWebviewOptions.defaultFormValues}.\n *\n * `submission` is typed as {@link SubmissionPrefill} (every field optional) so a partial\n * is valid; a whole {@link SubmissionBody} echoed back from `onSubmissionBody` also fits.\n */\nexport interface DefaultFormValues {\n /** Values keyed by element id / sub-element id, under `data`. */\n submission?: SubmissionPrefill;\n /** Field values matched by `ll_field_unique_identifier` (e.g. `\"Email\"`). */\n info?: PrefillInfoItem[];\n}\n\n/**\n * Options for {@link useCaptelloWebview}: the embed config, message callbacks, and the\n * usual client options.\n *\n * `embedUrl` is required — the hook builds the URL from it, derives `targetOrigin`, and\n * returns it as `iframeProps.src`. Any `targetOrigin` you pass is ignored; drop to\n * {@link CaptelloWebview} directly if you need to own both the URL and the origin.\n */\nexport interface UseCaptelloWebviewOptions extends Omit<CaptelloWebviewOptions, \"targetOrigin\">, CaptelloWebviewCallbacks {\n /** Build the iframe URL and derive `targetOrigin` from it. Sets `iframeProps.src`. */\n embedUrl: EmbedUrlConfig;\n /**\n * Values to populate the form with as soon as it is ready. Saves you from wiring a\n * `ref` and calling `prefill(...)` from an effect just to seed the form.\n *\n * Sent as the *first* outbound message, so a later explicit `prefill(...)` wins.\n * Read once when the client attaches — changing the value afterwards does **not**\n * re-populate the form (these are defaults, not controlled values); call `prefill(...)`\n * for that. No memoization needed: an inline object literal is fine.\n *\n * @example\n * defaultFormValues={{ info: [{ ll_field_unique_identifier: \"Email\", value: user.email }] }}\n */\n defaultFormValues?: DefaultFormValues;\n}\n\n/** Readiness of the embedded form. */\nexport type CaptelloWebviewStatus = \"loading\" | \"ready\" | \"error\";\n\n/** Props to spread onto the `<iframe>` — the ref plus the `embedUrl`-derived `src`. */\nexport interface CaptelloIframeProps {\n ref: RefCallback<HTMLIFrameElement | null>;\n src: string;\n}\n\n/** What {@link useCaptelloWebview} returns. */\nexport interface UseCaptelloWebviewResult {\n /** Spread onto your iframe: `<iframe {...iframeProps} />`. Carries the `embedUrl`-derived `src`. */\n iframeProps: CaptelloIframeProps;\n /** The iframe ref callback (same as `iframeProps.ref`), if you'd rather wire `src` yourself. */\n ref: RefCallback<HTMLIFrameElement | null>;\n /** `true` once the form has reported `form_load_complete`. */\n isReady: boolean;\n /** `\"loading\"` → `\"ready\"`; flips to `\"error\"` if a `form_error_message` arrives. */\n status: CaptelloWebviewStatus;\n /** The live client, or `null` before the iframe mounts. For escape-hatch use. */\n getClient: () => CaptelloWebview | null;\n submit: () => void;\n reset: () => void;\n updateDraft: () => void;\n triggerValidation: (target: ValidationTarget) => void;\n prefill: (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) => void;\n submitAndWait: (timeoutMs?: number) => Promise<SubmissionBody>;\n}\n\n/**\n * Unwraps `message` to its payload and calls the matching callback.\n *\n * Exhaustive over {@link OutboundMessageType}: adding a message type without handling it\n * here is a compile error, so a new type can't silently go undelivered.\n */\nfunction dispatchToCallback(callbacks: CaptelloWebviewCallbacks, message: OutboundMessage): void {\n switch (message.type) {\n case OutboundMessageType.FormLoadComplete:\n callbacks.onFormLoadComplete?.();\n break;\n case OutboundMessageType.FormErrorMessage:\n callbacks.onFormErrorMessage?.(message.data);\n break;\n case OutboundMessageType.SubmissionBody:\n callbacks.onSubmissionBody?.(message.data);\n break;\n case OutboundMessageType.FormSubmitSuccess:\n callbacks.onFormSubmitSuccess?.(message.action);\n break;\n case OutboundMessageType.ConnexionsProfileRedirect:\n callbacks.onConnexionsProfileRedirect?.();\n break;\n case OutboundMessageType.ConnexionsDownloadVcard:\n callbacks.onConnexionsDownloadVcard?.();\n break;\n default: {\n const exhaustive: never = message;\n void exhaustive;\n }\n }\n}\n\n/**\n * Binds a {@link CaptelloWebview} to an iframe's lifecycle. See the module doc for usage.\n */\nexport function useCaptelloWebview(options: UseCaptelloWebviewOptions): UseCaptelloWebviewResult {\n const { embedUrl, matchSource, queueUntilReady, hostWindow } = options;\n\n // Build the iframe URL and scope messaging to its origin. Recomputed on every render\n // (cheap), but only the derived origin feeds `attach`'s deps, so a same-origin URL\n // change doesn't tear the client down.\n const src = buildEmbedUrl(embedUrl.baseUrl, embedUrl);\n const targetOrigin = new URL(src).origin;\n\n // Latest options/callbacks, read fresh inside listeners so callers needn't memoize.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const clientRef = useRef<CaptelloWebview | null>(null);\n const frameRef = useRef<HTMLIFrameElement | null>(null);\n const teardown = useRef<(() => void) | null>(null);\n\n const [status, setStatus] = useState<CaptelloWebviewStatus>(\"loading\");\n\n const attach = useCallback(\n (frame: HTMLIFrameElement | null) => {\n // Tear down any previous client (ref changed or unmounting).\n teardown.current?.();\n teardown.current = null;\n clientRef.current = null;\n frameRef.current = frame;\n setStatus(\"loading\");\n\n if (!frame) return;\n\n const client = new CaptelloWebview(frame, {\n targetOrigin,\n hostWindow: optionsRef.current.hostWindow,\n matchSource: optionsRef.current.matchSource,\n queueUntilReady: optionsRef.current.queueUntilReady,\n });\n clientRef.current = client;\n\n const offs: Unsubscribe[] = [];\n\n // Seed the form with `defaultFormValues`. With `queueUntilReady` (the default)\n // this lands first in the outbox and flushes on load, so an explicit prefill()\n // made later still wins. With queueing off there's no outbox to ride in on, so\n // wait for the form to report in — subscribed before the callback loop below,\n // to seed before the caller's onFormLoadComplete runs.\n const defaults = optionsRef.current.defaultFormValues;\n if (defaults && (defaults.submission != null || defaults.info != null)) {\n if (optionsRef.current.queueUntilReady === false) {\n offs.push(\n client.once(OutboundMessageType.FormLoadComplete, () => {\n try {\n client.prefill(defaults);\n } catch {\n /* iframe detached between load and seed — drop silently */\n }\n }),\n );\n } else {\n client.prefill(defaults);\n }\n }\n\n for (const type of Object.values(OutboundMessageType)) {\n offs.push(\n client.on(type, (message) => {\n if (type === OutboundMessageType.FormLoadComplete) setStatus(\"ready\");\n else if (type === OutboundMessageType.FormErrorMessage) setStatus(\"error\");\n\n dispatchToCallback(optionsRef.current, message);\n optionsRef.current.onAnyMessage?.(message);\n }),\n );\n }\n\n teardown.current = () => {\n for (const off of offs) off();\n client.destroy();\n };\n },\n // Re-create the client only when connection-level inputs change.\n // Callbacks are read via optionsRef, so they intentionally aren't deps.\n [targetOrigin, matchSource, queueUntilReady, hostWindow],\n );\n\n useEffect(() => {\n if (frameRef.current) attach(frameRef.current);\n return () => {\n teardown.current?.();\n teardown.current = null;\n clientRef.current = null;\n };\n }, [attach]);\n\n const getClient = useCallback(() => clientRef.current, []);\n\n const submit = useCallback(() => clientRef.current?.submit(), []);\n const reset = useCallback(() => clientRef.current?.reset(), []);\n const updateDraft = useCallback(() => clientRef.current?.updateDraft(), []);\n const triggerValidation = useCallback(\n (target: ValidationTarget) => clientRef.current?.triggerValidation(target),\n [],\n );\n const prefill = useCallback(\n (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) => clientRef.current?.prefill(data),\n [],\n );\n const submitAndWait = useCallback((timeoutMs?: number) => {\n const client = clientRef.current;\n if (!client) {\n return Promise.reject(new Error(\"CaptelloWebview: iframe is not mounted yet.\"));\n }\n return client.submitAndWait(timeoutMs);\n }, []);\n\n const iframeProps: CaptelloIframeProps = { ref: attach, src };\n\n return {\n iframeProps,\n ref: attach,\n isReady: status === \"ready\",\n status,\n getClient,\n submit,\n reset,\n updateDraft,\n triggerValidation,\n prefill,\n submitAndWait,\n };\n}\n\n/* ------------------------------------------------------------------ *\n * <CaptelloForm /> — the turnkey component\n * ------------------------------------------------------------------ */\n\n/** Default iframe permissions for a capture form (business-card camera scan, mic, geo). */\nconst DEFAULT_ALLOW = \"camera; microphone; geolocation\";\n\n/** Wrapper is the positioning context for the loading / error overlays. */\nconst WRAPPER_STYLE: CSSProperties = { position: \"relative\" };\n\n/** The iframe fills the wrapper; size the component, not this. */\nconst IFRAME_STYLE: CSSProperties = { display: \"block\", width: \"100%\", height: \"100%\", border: 0 };\n\n/** Centers the `loading` / `error` node over the iframe. */\nconst OVERLAY_STYLE: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\n/**\n * Imperative handle exposed on {@link CaptelloForm}'s `ref` — the same senders the hook\n * returns, plus the current status and the underlying `<iframe>` node. Lets a parent\n * drive the form (e.g. an external submit button) without lifting state.\n */\nexport interface CaptelloFormHandle\n extends Pick<\n UseCaptelloWebviewResult,\n \"submit\" | \"reset\" | \"updateDraft\" | \"triggerValidation\" | \"prefill\" | \"submitAndWait\" | \"getClient\"\n > {\n /** Current readiness: `\"loading\" | \"ready\" | \"error\"`. */\n readonly status: CaptelloWebviewStatus;\n /** `true` once the form has reported `form_load_complete`. */\n readonly isReady: boolean;\n /** The underlying `<iframe>` DOM node, or `null` before it mounts. */\n getIframe: () => HTMLIFrameElement | null;\n}\n\n/**\n * Props for {@link CaptelloForm}: every {@link UseCaptelloWebviewOptions} option (embed\n * config + message callbacks + client options) plus rendering conveniences.\n */\nexport interface CaptelloFormProps extends UseCaptelloWebviewOptions {\n /** `className` for the wrapper element. */\n className?: string;\n /** `style` for the wrapper element — size the form here. The component adds `position: relative`; your values win. */\n style?: CSSProperties;\n /** `id` for the wrapper element. */\n id?: string;\n /**\n * Attributes spread onto the `<iframe>` — `title`, `allow`, `sandbox`, `name`, etc.\n * Defaults: `title=\"Captello form\"`, `allow=\"camera; microphone; geolocation\"`.\n * `src` is ignored: it comes from `embedUrl`.\n */\n iframeProps?: Omit<IframeHTMLAttributes<HTMLIFrameElement>, \"ref\">;\n /** Rendered, centered over the iframe, while it is loading. The iframe stays mounted underneath. */\n loading?: ReactNode;\n /**\n * Rendered, centered over the iframe, when the form reports `form_error_message`.\n * Pass a function to receive the translated, display-ready error text.\n */\n error?: ReactNode | ((message: string | undefined) => ReactNode);\n /**\n * Inline controls rendered after the form. A function receives the live api\n * (status + senders), so you can wire a submit button without a `ref`.\n */\n children?: ReactNode | ((api: UseCaptelloWebviewResult) => ReactNode);\n}\n\nfunction CaptelloFormImpl(props: CaptelloFormProps, ref: Ref<CaptelloFormHandle>): ReactElement {\n const {\n className,\n style,\n id,\n iframeProps,\n loading,\n error,\n children,\n onFormLoadComplete,\n onFormErrorMessage,\n ...options\n } = props;\n\n // The translated error text from the last form_error_message, for the `error` render.\n const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);\n\n const api = useCaptelloWebview({\n ...options,\n // Wrap the two status-bearing callbacks to track the error text, then forward to\n // the caller's handler. The hook reads callbacks fresh, so these inline wrappers\n // don't re-subscribe or re-create the client.\n onFormLoadComplete: () => {\n setErrorMessage(undefined);\n onFormLoadComplete?.();\n },\n onFormErrorMessage: (message) => {\n setErrorMessage(message);\n onFormErrorMessage?.(message);\n },\n });\n\n // Merge the hook's iframe ref with our own node ref so getIframe() can return the DOM node.\n const nodeRef = useRef<HTMLIFrameElement | null>(null);\n const hookRef = api.iframeProps.ref;\n const setIframe = useCallback<RefCallback<HTMLIFrameElement | null>>(\n (node) => {\n nodeRef.current = node;\n hookRef(node);\n },\n [hookRef],\n );\n\n useImperativeHandle(\n ref,\n () => ({\n submit: api.submit,\n reset: api.reset,\n updateDraft: api.updateDraft,\n triggerValidation: api.triggerValidation,\n prefill: api.prefill,\n submitAndWait: api.submitAndWait,\n getClient: api.getClient,\n getIframe: () => nodeRef.current,\n status: api.status,\n isReady: api.isReady,\n }),\n [api],\n );\n\n const showLoading = api.status === \"loading\" && loading != null;\n const showError = api.status === \"error\" && error != null;\n\n return (\n <>\n <div className={className} id={id} style={{ ...WRAPPER_STYLE, ...style }}>\n <iframe\n title=\"Captello form\"\n allow={DEFAULT_ALLOW}\n {...iframeProps}\n ref={setIframe}\n src={api.iframeProps.src}\n style={{ ...IFRAME_STYLE, ...iframeProps?.style }}\n />\n {showLoading ? <div style={OVERLAY_STYLE}>{loading}</div> : null}\n {showError ? (\n <div style={OVERLAY_STYLE}>{typeof error === \"function\" ? error(errorMessage) : error}</div>\n ) : null}\n </div>\n {typeof children === \"function\" ? children(api) : children}\n </>\n );\n}\n\n/**\n * Turnkey component for embedding a Captello capture form — the shortest path to a\n * working integration. Renders the `<iframe>`, wires {@link useCaptelloWebview} to it,\n * shows your `loading` / `error` overlays, and forwards a {@link CaptelloFormHandle} on\n * `ref` so a parent can `submit()` / `prefill()` without lifting state.\n *\n * `embedUrl` is required and the form fills its wrapper — size the form via `className` /\n * `style` (an iframe has no intrinsic height). Reach for {@link useCaptelloWebview} instead\n * when you need to own the markup.\n *\n * @example\n * function UlcForm({ token, email }: { token: string; email: string }) {\n * const ref = useRef<CaptelloFormHandle>(null);\n * return (\n * <CaptelloForm\n * ref={ref}\n * style={{ height: 600 }}\n * embedUrl={{\n * baseUrl: \"https://capture.captello.com\",\n * eventWebAccessToken: token,\n * mode: FormMode.Submit,\n * launcher: LauncherType.EventGenWeb,\n * }}\n * defaultFormValues={{ info: [{ ll_field_unique_identifier: \"Email\", value: email }] }}\n * onSubmissionBody={save}\n * loading={<Spinner />}\n * error={(msg) => <ErrorBanner>{msg}</ErrorBanner>}\n * >\n * {({ isReady }) => <button disabled={!isReady} onClick={() => ref.current?.submit()}>Submit</button>}\n * </CaptelloForm>\n * );\n * }\n */\nexport const CaptelloForm = forwardRef(CaptelloFormImpl);\nCaptelloForm.displayName = \"CaptelloForm\";\n\nexport { CaptelloWebview, SubmissionError, SubmissionTimeoutError } from \"./client\";\nexport type { Unsubscribe } from \"./client\";\n"]}
package/package.json CHANGED
@@ -1,78 +1,79 @@
1
1
  {
2
- "name": "@captello/ulc-webview-sdk",
3
- "version": "0.5.0",
4
- "description": "Typed SDK for embedding the Captello capture webview: message protocol, host client, and embed-URL builder.",
5
- "author": "Lead Liaison",
6
- "license": "MIT",
7
- "type": "module",
8
- "sideEffects": false,
9
- "module": "./dist/index.js",
10
- "types": "./dist/index.d.ts",
11
- "typesVersions": {
12
- "*": {
13
- "promises": [
14
- "./dist/promises.d.ts"
15
- ],
16
- "react": [
17
- "./dist/react.d.ts"
18
- ]
19
- }
20
- },
21
- "exports": {
22
- ".": {
23
- "types": "./dist/index.d.ts",
24
- "import": "./dist/index.js"
2
+ "name": "@captello/ulc-webview-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Typed SDK for embedding the Captello capture webview: message protocol, host client, and embed-URL builder.",
5
+ "author": "Lead Liaison",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "sideEffects": false,
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "typesVersions": {
12
+ "*": {
13
+ "promises": [
14
+ "./dist/promises.d.ts"
15
+ ],
16
+ "react": [
17
+ "./dist/react.d.ts"
18
+ ]
19
+ }
20
+ },
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ },
26
+ "./promises": {
27
+ "types": "./dist/promises.d.ts",
28
+ "import": "./dist/promises.js"
29
+ },
30
+ "./react": {
31
+ "types": "./dist/react.d.ts",
32
+ "import": "./dist/react.js"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "LICENSE",
39
+ "CHANGELOG.md"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "dev": "tsup --watch",
44
+ "typecheck": "tsc --noEmit",
45
+ "typecheck:test": "tsc -p tsconfig.test.json",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "clean": "rm -rf dist"
49
+ },
50
+ "keywords": [
51
+ "captello",
52
+ "webview",
53
+ "iframe",
54
+ "postmessage",
55
+ "embed",
56
+ "sdk"
57
+ ],
58
+ "peerDependencies": {
59
+ "react": ">=18"
25
60
  },
26
- "./promises": {
27
- "types": "./dist/promises.d.ts",
28
- "import": "./dist/promises.js"
61
+ "peerDependenciesMeta": {
62
+ "react": {
63
+ "optional": true
64
+ }
29
65
  },
30
- "./react": {
31
- "types": "./dist/react.d.ts",
32
- "import": "./dist/react.js"
66
+ "devDependencies": {
67
+ "@testing-library/react": "16.1.0",
68
+ "@types/react": "19.2.2",
69
+ "jsdom": "25.0.1",
70
+ "react": "19.2.2",
71
+ "react-dom": "19.2.2",
72
+ "tsup": "8.3.5",
73
+ "typescript": "5.9.3",
74
+ "vitest": "2.1.9"
33
75
  },
34
- "./package.json": "./package.json"
35
- },
36
- "files": [
37
- "dist",
38
- "LICENSE"
39
- ],
40
- "keywords": [
41
- "captello",
42
- "webview",
43
- "iframe",
44
- "postmessage",
45
- "embed",
46
- "sdk"
47
- ],
48
- "peerDependencies": {
49
- "react": ">=18"
50
- },
51
- "peerDependenciesMeta": {
52
- "react": {
53
- "optional": true
76
+ "publishConfig": {
77
+ "access": "public"
54
78
  }
55
- },
56
- "devDependencies": {
57
- "@testing-library/react": "16.1.0",
58
- "@types/react": "19.2.2",
59
- "jsdom": "25.0.1",
60
- "react": "19.2.2",
61
- "react-dom": "19.2.2",
62
- "tsup": "8.3.5",
63
- "typescript": "5.9.3",
64
- "vitest": "2.1.9"
65
- },
66
- "publishConfig": {
67
- "access": "public"
68
- },
69
- "scripts": {
70
- "build": "tsup",
71
- "dev": "tsup --watch",
72
- "typecheck": "tsc --noEmit",
73
- "typecheck:test": "tsc -p tsconfig.test.json",
74
- "test": "vitest run",
75
- "test:watch": "vitest",
76
- "clean": "rm -rf dist"
77
- }
78
- }
79
+ }