@captello/ulc-webview-sdk 1.0.0 → 1.2.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 +123 -0
- package/README.md +352 -5
- package/dist/app-host.d.ts +228 -0
- package/dist/app-host.js +170 -0
- package/dist/app-host.js.map +1 -0
- package/dist/{chunk-PFFBCSJ2.js → chunk-ZPVXZW2B.js} +235 -19
- package/dist/chunk-ZPVXZW2B.js.map +1 -0
- package/dist/{chunk-4E7OW4RJ.js → chunk-ZX4AKPWF.js} +5 -3
- package/dist/chunk-ZX4AKPWF.js.map +1 -0
- package/dist/client-CAMlFA8s.d.ts +898 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +2 -2
- package/dist/promises.d.ts +3 -5
- package/dist/promises.js +2 -2
- package/dist/promises.js.map +1 -1
- package/dist/react.d.ts +19 -4
- package/dist/react.js +14 -3
- package/dist/react.js.map +1 -1
- package/package.json +83 -73
- package/dist/chunk-4E7OW4RJ.js.map +0 -1
- package/dist/chunk-PFFBCSJ2.js.map +0 -1
- package/dist/client-CalIoKT6.d.ts +0 -517
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for a submission's **visible** data — the list a host renders as key/value
|
|
3
|
+
* pairs (one row per filled, visible form element).
|
|
4
|
+
*
|
|
5
|
+
* The webview emits this as `visible_submissions_data` inside the `submission_body`
|
|
6
|
+
* payload: an array of {@link VisibleSubmissionDataItem}, discriminated by
|
|
7
|
+
* `element_type`, where `element_value`'s shape depends on the type. Narrow on
|
|
8
|
+
* `element_type` to get a precisely-typed `element_value` and render it however you
|
|
9
|
+
* like:
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* for (const item of submission.visible_submissions_data ?? []) {
|
|
13
|
+
* switch (item.element_type) {
|
|
14
|
+
* case FormElementType.email:
|
|
15
|
+
* row(item.element_title, item.element_value); // element_value: string
|
|
16
|
+
* break;
|
|
17
|
+
* case FormElementType.checkbox:
|
|
18
|
+
* // element_value: string[] | OrderCheckboxSubmissionData
|
|
19
|
+
* break;
|
|
20
|
+
* // …
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Every form element type, mirroring the webview's `FormElementType`. The string
|
|
26
|
+
* values are the wire values. Structural types (sections, separators, …) never carry
|
|
27
|
+
* a value and never appear in visible submission data.
|
|
28
|
+
*/
|
|
29
|
+
declare enum FormElementType {
|
|
30
|
+
email = "email",
|
|
31
|
+
section = "section_block",
|
|
32
|
+
html_block = "section",
|
|
33
|
+
url = "url",
|
|
34
|
+
text = "text",
|
|
35
|
+
select = "select",
|
|
36
|
+
radio = "radio",
|
|
37
|
+
simple_name = "simple_name",
|
|
38
|
+
textarea = "textarea",
|
|
39
|
+
time = "time",
|
|
40
|
+
address = "address",
|
|
41
|
+
money = "money",
|
|
42
|
+
number = "number",
|
|
43
|
+
date = "date",
|
|
44
|
+
phone = "phone",
|
|
45
|
+
simple_phone = "simple_phone",
|
|
46
|
+
checkbox = "checkbox",
|
|
47
|
+
image = "image",
|
|
48
|
+
business_card = "business_card",
|
|
49
|
+
signature = "signature",
|
|
50
|
+
barcode = "barcode",
|
|
51
|
+
separator = "column_separator",
|
|
52
|
+
activation = "activation",
|
|
53
|
+
document = "documents",
|
|
54
|
+
datetime = "datetime",
|
|
55
|
+
meeting = "meeting",
|
|
56
|
+
audio = "audio",
|
|
57
|
+
rating = "rating",
|
|
58
|
+
assign_owner = "assign_owner",
|
|
59
|
+
boolean = "boolean",
|
|
60
|
+
engagem_feeder = "engagem_feeder",
|
|
61
|
+
speaker_section = "speaker_section_block",
|
|
62
|
+
session_section = "session_section_block",
|
|
63
|
+
image_placeholder = "image_placeholder",
|
|
64
|
+
star_rating = "star_survey",
|
|
65
|
+
attachments = "attachments"
|
|
66
|
+
}
|
|
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;
|
|
87
|
+
};
|
|
88
|
+
/** Checkbox value when the element is an "order" checkbox (quantities + note). */
|
|
89
|
+
type OrderCheckboxSubmissionData = {
|
|
90
|
+
values: {
|
|
91
|
+
value: string;
|
|
92
|
+
quantity: number;
|
|
93
|
+
}[];
|
|
94
|
+
note: string;
|
|
95
|
+
};
|
|
96
|
+
/** Radio/select value when the element is an "order" radio/select (quantity + note). */
|
|
97
|
+
type OrderRadioSubmissionData = {
|
|
98
|
+
value: string;
|
|
99
|
+
quantity: number;
|
|
100
|
+
note: string;
|
|
101
|
+
};
|
|
102
|
+
/** An uploaded attachment. `blob` is not present on the wire (postMessage JSON drops it). */
|
|
103
|
+
type AttachmentValue = {
|
|
104
|
+
token: string;
|
|
105
|
+
url: string;
|
|
106
|
+
name: string;
|
|
107
|
+
size: number;
|
|
108
|
+
};
|
|
109
|
+
/** A business-card capture: front/back image references (either may be absent). */
|
|
110
|
+
type BusinessCardValue = {
|
|
111
|
+
front: string;
|
|
112
|
+
back: string;
|
|
113
|
+
};
|
|
114
|
+
/** One engagement-feeder question/answer entry. */
|
|
115
|
+
type SubmissionQuestionData = {
|
|
116
|
+
question: string;
|
|
117
|
+
answers: string[];
|
|
118
|
+
/** `""` when the element is hidden. */
|
|
119
|
+
correct_answer: number | "";
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Maps each value-carrying element type to its `element_value` shape. Structural
|
|
123
|
+
* types are intentionally absent, so they can never appear in a visible item.
|
|
124
|
+
*/
|
|
125
|
+
type VisibleSubmissionElementValueMap = {
|
|
126
|
+
[FormElementType.email]: string;
|
|
127
|
+
[FormElementType.url]: string;
|
|
128
|
+
[FormElementType.text]: string;
|
|
129
|
+
[FormElementType.textarea]: string;
|
|
130
|
+
[FormElementType.money]: string;
|
|
131
|
+
[FormElementType.number]: string;
|
|
132
|
+
[FormElementType.phone]: string;
|
|
133
|
+
[FormElementType.simple_phone]: string;
|
|
134
|
+
[FormElementType.date]: string;
|
|
135
|
+
[FormElementType.time]: string;
|
|
136
|
+
[FormElementType.datetime]: string;
|
|
137
|
+
[FormElementType.audio]: string;
|
|
138
|
+
[FormElementType.rating]: string;
|
|
139
|
+
[FormElementType.star_rating]: string;
|
|
140
|
+
[FormElementType.assign_owner]: string;
|
|
141
|
+
[FormElementType.signature]: string;
|
|
142
|
+
[FormElementType.barcode]: string;
|
|
143
|
+
[FormElementType.meeting]: string;
|
|
144
|
+
[FormElementType.activation]: string;
|
|
145
|
+
[FormElementType.checkbox]: string[] | OrderCheckboxSubmissionData;
|
|
146
|
+
[FormElementType.radio]: string | OrderRadioSubmissionData;
|
|
147
|
+
[FormElementType.select]: string | OrderRadioSubmissionData;
|
|
148
|
+
[FormElementType.image]: string[];
|
|
149
|
+
[FormElementType.document]: number[];
|
|
150
|
+
[FormElementType.engagem_feeder]: SubmissionQuestionData[];
|
|
151
|
+
[FormElementType.business_card]: Partial<BusinessCardValue>;
|
|
152
|
+
[FormElementType.boolean]: boolean;
|
|
153
|
+
[FormElementType.attachments]: AttachmentValue[];
|
|
154
|
+
[FormElementType.simple_name]: NameSubmissionValue;
|
|
155
|
+
[FormElementType.address]: AddressSubmissionValue;
|
|
156
|
+
};
|
|
157
|
+
/** Element types that carry a user-facing value (i.e. can appear in visible data). */
|
|
158
|
+
type VisibleSubmissionElementType = keyof VisibleSubmissionElementValueMap;
|
|
159
|
+
/**
|
|
160
|
+
* One entry in `visible_submissions_data`, discriminated by `element_type`. Narrowing
|
|
161
|
+
* on `element_type` gives a precisely-typed `element_value`.
|
|
162
|
+
*/
|
|
163
|
+
type VisibleSubmissionDataItem = {
|
|
164
|
+
[T in VisibleSubmissionElementType]: {
|
|
165
|
+
element_id: string;
|
|
166
|
+
element_title: string;
|
|
167
|
+
element_type: T;
|
|
168
|
+
element_value: VisibleSubmissionElementValueMap[T];
|
|
169
|
+
};
|
|
170
|
+
}[VisibleSubmissionElementType];
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The message protocol exchanged between the Captello capture webview (the iframe)
|
|
174
|
+
* and its host page.
|
|
175
|
+
*
|
|
176
|
+
* Wire format (this is the contract — match it exactly):
|
|
177
|
+
* - Every message is a JSON **string**. The webview sends outbound messages with
|
|
178
|
+
* `JSON.stringify(message)` and reads inbound messages with `JSON.parse(event.data)`.
|
|
179
|
+
* A host that posts a raw object instead of a string will be ignored, because the
|
|
180
|
+
* webview's parser produces a non-object and bails.
|
|
181
|
+
* - Every message is an object with a `type` discriminator. Inbound and outbound
|
|
182
|
+
* types are disjoint string enums.
|
|
183
|
+
*
|
|
184
|
+
* Direction is named from the **webview's** point of view:
|
|
185
|
+
* - {@link OutboundMessageType}: webview → host (the host listens for these).
|
|
186
|
+
* - {@link InboundMessageType}: host → webview (the host sends these).
|
|
187
|
+
*/
|
|
188
|
+
|
|
189
|
+
/** Message `type` values the webview emits to its host. */
|
|
190
|
+
declare enum OutboundMessageType {
|
|
191
|
+
/** The form finished loading and rendering. Safe to interact with it after this. */
|
|
192
|
+
FormLoadComplete = "form_load_complete",
|
|
193
|
+
/** A user-facing error occurred; `data` is the translated, display-ready message. */
|
|
194
|
+
FormErrorMessage = "form_error_message",
|
|
195
|
+
/**
|
|
196
|
+
* Emitted for embedded forms instead of submitting directly: `data` is the full
|
|
197
|
+
* submission body for the host to persist/forward.
|
|
198
|
+
*/
|
|
199
|
+
SubmissionBody = "submission_body",
|
|
200
|
+
/** The form was submitted successfully. `action` indicates whether it was a new submission or an update. */
|
|
201
|
+
FormSubmitSuccess = "form_submit_success",
|
|
202
|
+
/** Connexions: the host should perform the profile redirect (embed mode). */
|
|
203
|
+
ConnexionsProfileRedirect = "connexions_profile_redirect",
|
|
204
|
+
/** Connexions: the host should trigger the vCard download (embed mode). */
|
|
205
|
+
ConnexionsDownloadVcard = "connexions_download_vcard",
|
|
206
|
+
/**
|
|
207
|
+
* The user pressed the transcribe button (shown only when the embed URL enables
|
|
208
|
+
* it via `show_transcribe_button`): the host should transcribe the request's
|
|
209
|
+
* scan images and answer with an inbound
|
|
210
|
+
* {@link InboundMessageType.TranscribeScannerResult}.
|
|
211
|
+
*/
|
|
212
|
+
TranscribeScannerRequest = "transcribe_scanner_request"
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Opaque submission payload carried by {@link OutboundMessageType.SubmissionBody}.
|
|
216
|
+
*
|
|
217
|
+
* This mirrors the webview's internal `FormSubmission` model. It is intentionally
|
|
218
|
+
* typed as an open record here so the SDK stays decoupled from the app's full model
|
|
219
|
+
* graph; the documented fields below are stable, the rest are passed through as-is.
|
|
220
|
+
* Host code that needs the deep element-value types should treat `data` as untyped
|
|
221
|
+
* and key it by element id (e.g. `"element_12"`, `"element_12_3"`).
|
|
222
|
+
*/
|
|
223
|
+
interface SubmissionBody {
|
|
224
|
+
id: number;
|
|
225
|
+
form_id: number;
|
|
226
|
+
prospect_id: number;
|
|
227
|
+
email: string;
|
|
228
|
+
first_name: string;
|
|
229
|
+
last_name: string;
|
|
230
|
+
full_name: string;
|
|
231
|
+
company: string;
|
|
232
|
+
phone: string;
|
|
233
|
+
/** Submitted values keyed by element id / sub-element id. */
|
|
234
|
+
data: Record<string, unknown>;
|
|
235
|
+
/**
|
|
236
|
+
* Visible, filled elements ready to render as key/value rows — one item per
|
|
237
|
+
* element, discriminated by `element_type` (narrow on it for a precisely-typed
|
|
238
|
+
* `element_value`). See {@link VisibleSubmissionDataItem}. May be absent on older
|
|
239
|
+
* webview builds.
|
|
240
|
+
*/
|
|
241
|
+
visible_submissions_data?: VisibleSubmissionDataItem[];
|
|
242
|
+
submission_date: string;
|
|
243
|
+
/** Query-string params the webview was loaded with, echoed back on submit. */
|
|
244
|
+
query_parameters?: Record<string, string>;
|
|
245
|
+
/** Additional fields from the webview's submission model are passed through verbatim. */
|
|
246
|
+
[key: string]: unknown;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Submitted values flat-keyed by element / sub-element id (e.g. `"element_12"`, `"element_12_3"`).
|
|
250
|
+
*
|
|
251
|
+
* This is the webview's own internal shape rather than the submissions API's: it is what a
|
|
252
|
+
* received {@link SubmissionBody} carries, and what the backend stores verbatim for a draft and
|
|
253
|
+
* hands back unchanged — hence the name. Either can be passed to
|
|
254
|
+
* {@link SubmissionPrefill.data} as-is, so an `onSubmissionBody` payload round-trips.
|
|
255
|
+
*
|
|
256
|
+
* Contrast {@link SubmissionPrefillDataItem}, the array shape the submissions API returns.
|
|
257
|
+
*/
|
|
258
|
+
type DraftSubmissionData = Record<string, unknown>;
|
|
259
|
+
/**
|
|
260
|
+
* One submitted value in {@link SubmissionPrefill.data}. Mirrors the submissions API's
|
|
261
|
+
* `SubmissionDataResponse` shape, so a `submission.data` array fetched from that API can
|
|
262
|
+
* be passed straight through as-is.
|
|
263
|
+
*/
|
|
264
|
+
interface SubmissionPrefillDataItem {
|
|
265
|
+
element_id: string;
|
|
266
|
+
element_title: string;
|
|
267
|
+
value: string;
|
|
268
|
+
/** Present only when the element has sub-elements (e.g. a simple name or address). */
|
|
269
|
+
value_splitted?: Record<string, string>;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Loose submission shape accepted when **pre-filling** the form (host → webview).
|
|
273
|
+
*
|
|
274
|
+
* Distinct from {@link SubmissionBody} in that every field is optional — assemble a partial
|
|
275
|
+
* object from your own data rather than populating a whole body.
|
|
276
|
+
*
|
|
277
|
+
* `data` accepts either shape a host is likely to be holding, and the webview normalizes
|
|
278
|
+
* whichever it receives:
|
|
279
|
+
* - {@link SubmissionPrefillDataItem}`[]` — one entry per element, as the submissions API
|
|
280
|
+
* returns it for a submitted submission. Pass a fetched `submission.data` array straight
|
|
281
|
+
* through.
|
|
282
|
+
* - {@link DraftSubmissionData} — values flat-keyed by element / sub-element id. This is the
|
|
283
|
+
* shape a received {@link SubmissionBody} carries, and the shape a draft is stored and
|
|
284
|
+
* returned in, so `onSubmissionBody` payloads round-trip directly.
|
|
285
|
+
*/
|
|
286
|
+
interface SubmissionPrefill {
|
|
287
|
+
/**
|
|
288
|
+
* Submitted values, as either the submissions API's array (see
|
|
289
|
+
* {@link SubmissionPrefillDataItem}) or a flat {@link DraftSubmissionData} record.
|
|
290
|
+
*/
|
|
291
|
+
data?: SubmissionPrefillDataItem[] | DraftSubmissionData;
|
|
292
|
+
[key: string]: unknown;
|
|
293
|
+
}
|
|
294
|
+
interface FormLoadCompleteMessage {
|
|
295
|
+
type: OutboundMessageType.FormLoadComplete;
|
|
296
|
+
}
|
|
297
|
+
interface FormSubmitSuccessMessage {
|
|
298
|
+
type: OutboundMessageType.FormSubmitSuccess;
|
|
299
|
+
action: "create" | "update";
|
|
300
|
+
}
|
|
301
|
+
interface FormErrorMessageMessage {
|
|
302
|
+
type: OutboundMessageType.FormErrorMessage;
|
|
303
|
+
/** Translated, display-ready error text. */
|
|
304
|
+
data: string;
|
|
305
|
+
}
|
|
306
|
+
interface SubmissionBodyMessage {
|
|
307
|
+
type: OutboundMessageType.SubmissionBody;
|
|
308
|
+
data: SubmissionBody;
|
|
309
|
+
}
|
|
310
|
+
interface ConnexionsProfileRedirectMessage {
|
|
311
|
+
type: OutboundMessageType.ConnexionsProfileRedirect;
|
|
312
|
+
}
|
|
313
|
+
interface ConnexionsDownloadVcardMessage {
|
|
314
|
+
type: OutboundMessageType.ConnexionsDownloadVcard;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Webview → host: the user pressed the transcribe button. Answer with a
|
|
318
|
+
* {@link TranscribeScannerResultMessage} (see
|
|
319
|
+
* {@link CaptelloWebview.onTranscribeScannerRequest} for the handler that does this
|
|
320
|
+
* for you). The webview shows a pending state on the button and gives up with a
|
|
321
|
+
* user-facing error if no result arrives in time.
|
|
322
|
+
*
|
|
323
|
+
* Host processing is a convention, not a one-off: future operations get their own
|
|
324
|
+
* `*_request` / `*_result` message pairs shaped like this one.
|
|
325
|
+
*/
|
|
326
|
+
interface TranscribeScannerRequestMessage {
|
|
327
|
+
type: OutboundMessageType.TranscribeScannerRequest;
|
|
328
|
+
/** Correlation id — echo it back verbatim in the result. */
|
|
329
|
+
request_id: string;
|
|
330
|
+
/** The scanner (badge/barcode) element whose scan should be transcribed (e.g. `"element_6"`). */
|
|
331
|
+
element_id: string;
|
|
332
|
+
/** Public URLs of the uploaded scan images (the scanner element's image value). */
|
|
333
|
+
image_urls: string[];
|
|
334
|
+
/** The draft submission token the webview was loaded with, when it has one. */
|
|
335
|
+
draft_submission_token?: string;
|
|
336
|
+
}
|
|
337
|
+
/** Discriminated union of every message the webview can emit to its host. */
|
|
338
|
+
type OutboundMessage = FormLoadCompleteMessage | FormSubmitSuccessMessage | FormErrorMessageMessage | SubmissionBodyMessage | ConnexionsProfileRedirectMessage | ConnexionsDownloadVcardMessage | TranscribeScannerRequestMessage;
|
|
339
|
+
/** Maps each outbound `type` to its full message shape (used by the client's `.on`). */
|
|
340
|
+
type OutboundMessageMap = {
|
|
341
|
+
[M in OutboundMessage as M["type"]]: M;
|
|
342
|
+
};
|
|
343
|
+
/** Message `type` values the host sends into the webview. */
|
|
344
|
+
declare enum InboundMessageType {
|
|
345
|
+
/** Programmatically trigger form submission (as if the user pressed submit). */
|
|
346
|
+
Submit = "submit_form",
|
|
347
|
+
/** Reset the form, clearing all entered values. */
|
|
348
|
+
Reset = "reset_form",
|
|
349
|
+
/** Pre-fill the form with existing data. */
|
|
350
|
+
FormPrefill = "form_prefill",
|
|
351
|
+
/** Switch the current submission into draft-update mode. */
|
|
352
|
+
UpdateDraft = "update_draft",
|
|
353
|
+
/** Run validation against a target field (or the whole form). */
|
|
354
|
+
TriggerValidation = "trigger_validation",
|
|
355
|
+
/** Answer to an outbound {@link OutboundMessageType.TranscribeScannerRequest}. */
|
|
356
|
+
TranscribeScannerResult = "transcribe_scanner_result"
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Shape selector for {@link InboundMessageType.FormPrefill} payloads.
|
|
360
|
+
*
|
|
361
|
+
* Vestigial: the webview keys only off the message `type` and reads `data` directly —
|
|
362
|
+
* it never inspects `data_type`. Confirmed by mutation-testing the integration spec (a
|
|
363
|
+
* deliberately wrong value changed nothing). Kept because it is part of the shipped wire
|
|
364
|
+
* shape and older webview builds may still read it; `prefill()` sets it for you.
|
|
365
|
+
*/
|
|
366
|
+
declare enum PrefillDataType {
|
|
367
|
+
UlcSubmissionAndInfo = "ulc_submission_and_info"
|
|
368
|
+
}
|
|
369
|
+
/** Targets for {@link InboundMessageType.TriggerValidation}. */
|
|
370
|
+
type ValidationTarget = "invitation_code" | "email" | "all";
|
|
371
|
+
/**
|
|
372
|
+
* A single transcription field/value item for the `info` prefill array.
|
|
373
|
+
*
|
|
374
|
+
* The webview matches each item to a form element by `ll_field_unique_identifier`
|
|
375
|
+
* alone (e.g. `"FirstName"`, `"Email"`); `ll_field_id` is catalog metadata and is not
|
|
376
|
+
* used for matching, so it is accepted as either a number or a string. `value` is
|
|
377
|
+
* typically a string but may be a boolean (e.g. the PII opt-out field).
|
|
378
|
+
*/
|
|
379
|
+
interface PrefillInfoItem {
|
|
380
|
+
ll_field_unique_identifier: string;
|
|
381
|
+
ll_field_id?: string | number;
|
|
382
|
+
value: string | boolean;
|
|
383
|
+
}
|
|
384
|
+
interface SubmitMessage {
|
|
385
|
+
type: InboundMessageType.Submit;
|
|
386
|
+
}
|
|
387
|
+
interface ResetMessage {
|
|
388
|
+
type: InboundMessageType.Reset;
|
|
389
|
+
}
|
|
390
|
+
interface UpdateDraftMessage {
|
|
391
|
+
type: InboundMessageType.UpdateDraft;
|
|
392
|
+
}
|
|
393
|
+
interface TriggerValidationMessage {
|
|
394
|
+
type: InboundMessageType.TriggerValidation;
|
|
395
|
+
target: ValidationTarget;
|
|
396
|
+
}
|
|
397
|
+
interface PrefillMessage {
|
|
398
|
+
type: InboundMessageType.FormPrefill;
|
|
399
|
+
data_type: PrefillDataType.UlcSubmissionAndInfo;
|
|
400
|
+
data: {
|
|
401
|
+
submission?: SubmissionPrefill;
|
|
402
|
+
info?: PrefillInfoItem[];
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* One transcribed field in {@link TranscribeScannerResultData.fields}.
|
|
407
|
+
*
|
|
408
|
+
* Semantically the same thing as a {@link PrefillInfoItem} but in the transcription
|
|
409
|
+
* service's camelCase wire casing — `llFieldIdentifier` carries the same values as
|
|
410
|
+
* `ll_field_unique_identifier` (e.g. `"FirstName"`, `"Email"`), and it is what the
|
|
411
|
+
* webview matches form elements by. The webview ignores fields with an empty `value`
|
|
412
|
+
* rather than blanking already-filled inputs.
|
|
413
|
+
*/
|
|
414
|
+
interface TranscribedScannerField {
|
|
415
|
+
/** Catalog field identifier the webview matches on, e.g. `"FirstName"`. */
|
|
416
|
+
llFieldIdentifier: string;
|
|
417
|
+
/** Catalog field id — metadata, not used for matching. */
|
|
418
|
+
llFieldId?: number;
|
|
419
|
+
/** Display name as the transcription service reports it. */
|
|
420
|
+
llFieldNameName?: string;
|
|
421
|
+
value: string;
|
|
422
|
+
}
|
|
423
|
+
/** Success payload of a {@link TranscribeScannerResultMessage}. */
|
|
424
|
+
interface TranscribeScannerResultData {
|
|
425
|
+
fields: TranscribedScannerField[];
|
|
426
|
+
/** How the transcription was produced, e.g. `"ocr_transcription"`. */
|
|
427
|
+
submissionType?: string;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Host → webview: the answer to a {@link TranscribeScannerRequestMessage}. Send
|
|
431
|
+
* `data` with the transcribed fields on success, or `error` (display-ready text) on
|
|
432
|
+
* failure. `request_id` should echo the request's; the webview also accepts a result
|
|
433
|
+
* without one while exactly one request is pending, for hosts that answer strictly
|
|
434
|
+
* one at a time.
|
|
435
|
+
*/
|
|
436
|
+
interface TranscribeScannerResultMessage {
|
|
437
|
+
type: InboundMessageType.TranscribeScannerResult;
|
|
438
|
+
/** The `request_id` of the request being answered, verbatim. */
|
|
439
|
+
request_id?: string;
|
|
440
|
+
/** Transcribed fields (success). */
|
|
441
|
+
data?: TranscribeScannerResultData;
|
|
442
|
+
/** Display-ready error text (failure) — leave `data` unset. */
|
|
443
|
+
error?: string;
|
|
444
|
+
}
|
|
445
|
+
/** Discriminated union of every message the host can send into the webview. */
|
|
446
|
+
type InboundMessage = SubmitMessage | ResetMessage | UpdateDraftMessage | TriggerValidationMessage | PrefillMessage | TranscribeScannerResultMessage;
|
|
447
|
+
/**
|
|
448
|
+
* Parses a raw `MessageEvent.data` value into a typed {@link OutboundMessage}, or
|
|
449
|
+
* returns `null` if it is not a recognized Captello webview message.
|
|
450
|
+
*
|
|
451
|
+
* Accepts either a JSON string (the webview always sends strings) or an
|
|
452
|
+
* already-parsed object, so it is robust to hosts/proxies that pre-parse.
|
|
453
|
+
*/
|
|
454
|
+
declare function parseOutboundMessage(data: unknown): OutboundMessage | null;
|
|
455
|
+
|
|
456
|
+
/** Listener for a specific outbound message type. */
|
|
457
|
+
type OutboundListener<T extends OutboundMessageType> = (message: OutboundMessageMap[T]) => void;
|
|
458
|
+
/** Listener for every outbound message (used by {@link CaptelloWebview.onAny}). */
|
|
459
|
+
type AnyOutboundListener = (message: OutboundMessage) => void;
|
|
460
|
+
/**
|
|
461
|
+
* Handler for {@link CaptelloWebview.onTranscribeScannerRequest}. Receives the full
|
|
462
|
+
* request (`element_id`, `image_urls`, `draft_submission_token`) plus a
|
|
463
|
+
* {@link TranscribeScannerReply}, and answers in either style:
|
|
464
|
+
*
|
|
465
|
+
* - **return** the transcribed fields (a promise is fine) — the promise-based style, or
|
|
466
|
+
* - **call `reply.resolve(...)` / `reply.reject(...)`** whenever the work finishes —
|
|
467
|
+
* for callback-style code that can't hand back a promise.
|
|
468
|
+
*
|
|
469
|
+
* Returning nothing hands ownership to `reply`, leaving the request open until it
|
|
470
|
+
* answers. A thrown error / rejection becomes an error result; if it's an `Error`, its
|
|
471
|
+
* `message` is shown to the user by the webview, so throw display-ready messages.
|
|
472
|
+
*/
|
|
473
|
+
type TranscribeScannerRequestHandler = (request: TranscribeScannerRequestMessage, reply: TranscribeScannerReply) => TranscribeScannerResultData | void | Promise<TranscribeScannerResultData | void>;
|
|
474
|
+
/**
|
|
475
|
+
* The second argument handed to a {@link TranscribeScannerRequestHandler} — everything
|
|
476
|
+
* needed to answer one request, so the handler never has to reach back out to the
|
|
477
|
+
* client or touch `postMessage` itself.
|
|
478
|
+
*
|
|
479
|
+
* Use it when the transcription can't hand back a promise: a callback-style AJAX
|
|
480
|
+
* wrapper, an event bus, a method that returns `void` and finishes whenever it
|
|
481
|
+
* finishes. Pass it along to whatever does the work and let that code answer:
|
|
482
|
+
*
|
|
483
|
+
* ```js
|
|
484
|
+
* webview.onTranscribeScannerRequest((request, reply) => {
|
|
485
|
+
* transcribeService(request.image_urls,
|
|
486
|
+
* (fields) => reply.resolve({ fields, submissionType: "ocr_transcription" }),
|
|
487
|
+
* () => reply.reject("Transcription failed."),
|
|
488
|
+
* );
|
|
489
|
+
* });
|
|
490
|
+
* ```
|
|
491
|
+
*
|
|
492
|
+
* A handler that returns a value (or a promise of one) is answered from that return
|
|
493
|
+
* value and can ignore this entirely — the promise style is unchanged.
|
|
494
|
+
*
|
|
495
|
+
* The first answer wins: once `resolve`, `reject`, or the handler's own return value
|
|
496
|
+
* has answered, later calls are ignored rather than posting a second result.
|
|
497
|
+
*/
|
|
498
|
+
interface TranscribeScannerReply {
|
|
499
|
+
/** Answer with the transcribed fields. */
|
|
500
|
+
resolve(data: TranscribeScannerResultData): void;
|
|
501
|
+
/**
|
|
502
|
+
* Fail the request. `error` is shown to the user by the webview verbatim, so pass
|
|
503
|
+
* display-ready text.
|
|
504
|
+
*/
|
|
505
|
+
reject(error: string): void;
|
|
506
|
+
/** `true` once this request has been answered, by either path. */
|
|
507
|
+
readonly answered: boolean;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Identifies the transcribe request being answered by
|
|
511
|
+
* {@link CaptelloWebview.resolveTranscribeScannerRequest} /
|
|
512
|
+
* {@link CaptelloWebview.rejectTranscribeScannerRequest} — the request message itself,
|
|
513
|
+
* or its bare `request_id`.
|
|
514
|
+
*
|
|
515
|
+
* `null`/`undefined` sends an un-correlated result, which the webview accepts only
|
|
516
|
+
* while exactly one request is pending. Pass the request whenever you have it.
|
|
517
|
+
*/
|
|
518
|
+
type TranscribeScannerRequestRef = TranscribeScannerRequestMessage | string | null | undefined;
|
|
519
|
+
/** The `unsubscribe()` side of a subscription handle. */
|
|
520
|
+
interface Subscription {
|
|
521
|
+
/** Remove the listener. Safe to call more than once. */
|
|
522
|
+
unsubscribe(): void;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Handle returned by every `on*` method. It is an object carrying
|
|
526
|
+
* {@link Subscription.unsubscribe}, and is also directly callable:
|
|
527
|
+
*
|
|
528
|
+
* ```ts
|
|
529
|
+
* const sub = webview.on(OutboundMessageType.SubmissionBody, save);
|
|
530
|
+
* sub.unsubscribe(); // preferred — reads clearly at the call site
|
|
531
|
+
* sub(); // equivalent
|
|
532
|
+
* ```
|
|
533
|
+
*
|
|
534
|
+
* Both do the same thing; the callable form keeps older `const off = ...; off()`
|
|
535
|
+
* code working.
|
|
536
|
+
*/
|
|
537
|
+
type Unsubscribe = (() => void) & Subscription;
|
|
538
|
+
/**
|
|
539
|
+
* Rejection reason from {@link CaptelloWebview.submitAndWait} when the webview reports
|
|
540
|
+
* a `form_error_message`. `message` is the translated, display-ready text.
|
|
541
|
+
*/
|
|
542
|
+
declare class SubmissionError extends Error {
|
|
543
|
+
constructor(message: string);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Rejection reason from {@link CaptelloWebview.submitAndWait} when no `submission_body`
|
|
547
|
+
* or `form_error_message` arrives within the timeout.
|
|
548
|
+
*/
|
|
549
|
+
declare class SubmissionTimeoutError extends Error {
|
|
550
|
+
readonly timeoutMs: number;
|
|
551
|
+
constructor(timeoutMs: number);
|
|
552
|
+
}
|
|
553
|
+
interface CaptelloWebviewOptions {
|
|
554
|
+
/**
|
|
555
|
+
* Origin to validate incoming messages against and to target outgoing messages.
|
|
556
|
+
* Strongly recommended — set it to the webview's origin (e.g.
|
|
557
|
+
* `"https://capture.captello.com"`).
|
|
558
|
+
*
|
|
559
|
+
* A full URL is accepted and reduced to its origin, so you can pass the embed URL
|
|
560
|
+
* or the webview base URL you already have on hand rather than deriving the origin
|
|
561
|
+
* yourself.
|
|
562
|
+
*
|
|
563
|
+
* Defaults to `"*"`, which accepts messages from any origin and posts without an
|
|
564
|
+
* origin check. Only acceptable for trusted/local development.
|
|
565
|
+
*/
|
|
566
|
+
targetOrigin?: string;
|
|
567
|
+
/**
|
|
568
|
+
* The window to attach the `message` listener to. Defaults to the global `window`.
|
|
569
|
+
* Override for testing or non-standard host environments.
|
|
570
|
+
*/
|
|
571
|
+
hostWindow?: Window;
|
|
572
|
+
/**
|
|
573
|
+
* If `true` (default), incoming messages are accepted only when they originate
|
|
574
|
+
* from the bound iframe's `contentWindow`. Set `false` only if the webview relays
|
|
575
|
+
* messages through an intermediate window and source matching is impossible.
|
|
576
|
+
*/
|
|
577
|
+
matchSource?: boolean;
|
|
578
|
+
/**
|
|
579
|
+
* If `true` (default), messages sent before the webview reports
|
|
580
|
+
* `form_load_complete` are buffered and flushed, in order, once it's ready. This
|
|
581
|
+
* removes a common footgun: calling `prefill(...)` right after mount would
|
|
582
|
+
* otherwise post to a form that isn't listening yet and be silently dropped.
|
|
583
|
+
*
|
|
584
|
+
* Set `false` to send immediately (the legacy behavior). Note: a client that
|
|
585
|
+
* attaches *after* the form already loaded will not have seen `form_load_complete`,
|
|
586
|
+
* so its queued messages won't flush — create the client with the iframe.
|
|
587
|
+
*/
|
|
588
|
+
queueUntilReady?: boolean;
|
|
589
|
+
/**
|
|
590
|
+
* If `true` (default), the client calls {@link CaptelloWebview.destroy} on itself
|
|
591
|
+
* once the bound iframe is removed from the document — so a host that tears down a
|
|
592
|
+
* panel, modal, or route doesn't leak the `message` listener or have to remember a
|
|
593
|
+
* matching `destroy()`.
|
|
594
|
+
*
|
|
595
|
+
* Removal is detected with a `MutationObserver`, and an ancestor being removed
|
|
596
|
+
* counts: what matters is that the iframe is no longer connected to the document.
|
|
597
|
+
* Only ever fires after the iframe has been seen connected, so constructing against
|
|
598
|
+
* a not-yet-inserted element is not mistaken for a teardown.
|
|
599
|
+
*
|
|
600
|
+
* Requires a real DOM element and `MutationObserver`; with a `{ contentWindow }`
|
|
601
|
+
* stand-in, or in a non-DOM environment, there is nothing to observe and this is
|
|
602
|
+
* inert. Set `false` to manage the lifetime entirely yourself — e.g. when you
|
|
603
|
+
* deliberately detach and re-insert the same iframe element.
|
|
604
|
+
*/
|
|
605
|
+
autoDestroy?: boolean;
|
|
606
|
+
/**
|
|
607
|
+
* If `true`, the client may learn its webview window from the **first** inbound
|
|
608
|
+
* message instead of being handed an iframe — see {@link attach}, which is the
|
|
609
|
+
* supported way to switch this on.
|
|
610
|
+
*
|
|
611
|
+
* Only ever adopts a sender that has already cleared the origin check and parsed as
|
|
612
|
+
* a well-formed Captello message, so with `targetOrigin` set to the webview's origin
|
|
613
|
+
* only the webview can be adopted. Every later message is source-matched against
|
|
614
|
+
* whatever was adopted, so this is as strict as an explicit iframe after the first
|
|
615
|
+
* message. Under the default `targetOrigin: "*"` that gate is absent — one more
|
|
616
|
+
* reason to set it in production.
|
|
617
|
+
*
|
|
618
|
+
* An explicitly passed iframe always wins; nothing is adopted while one resolves.
|
|
619
|
+
*
|
|
620
|
+
* @default false
|
|
621
|
+
*/
|
|
622
|
+
adoptSource?: boolean;
|
|
623
|
+
}
|
|
624
|
+
/** Options for {@link attach}. Same as the client's, minus the ones it manages itself. */
|
|
625
|
+
type AttachOptions = Omit<CaptelloWebviewOptions, "adoptSource">;
|
|
626
|
+
/**
|
|
627
|
+
* Attaches to the embedded Captello webview **without an iframe reference**.
|
|
628
|
+
*
|
|
629
|
+
* This is the drop-in replacement for a hand-rolled global
|
|
630
|
+
* `window.addEventListener("message", …)` dispatcher. Call it once, register your
|
|
631
|
+
* handlers, and you are done — there is nothing to query from the DOM, no ordering to
|
|
632
|
+
* get right relative to when the iframe is created, and no teardown to remember.
|
|
633
|
+
*
|
|
634
|
+
* ```js
|
|
635
|
+
* const webview = CaptelloSdk.attach({ targetOrigin: CAPTURE_PORTAL_WEB_VIEW_BASE });
|
|
636
|
+
*
|
|
637
|
+
* webview.on(CaptelloSdk.OutboundMessageType.FormErrorMessage, (m) => showError(m.data));
|
|
638
|
+
* webview.on(CaptelloSdk.OutboundMessageType.SubmissionBody, (m) => save(m.data));
|
|
639
|
+
* webview.onTranscribeScannerRequest((request, reply) => transcribe(request, reply));
|
|
640
|
+
* ```
|
|
641
|
+
*
|
|
642
|
+
* How it finds the form: the first inbound message that clears the origin check **and**
|
|
643
|
+
* parses as a well-formed Captello message identifies the webview. That sender becomes
|
|
644
|
+
* the target for sends and the window every later message is source-matched against —
|
|
645
|
+
* so after the first message this is as strict as passing the iframe. Set
|
|
646
|
+
* `targetOrigin` to the webview's origin and only the webview can ever be adopted.
|
|
647
|
+
*
|
|
648
|
+
* Teardown is still automatic: a removed iframe's window reports `closed`, which is
|
|
649
|
+
* readable cross-origin, so the client notices its form going away and destroys itself.
|
|
650
|
+
* Pass `autoDestroy: false` to own the lifetime yourself.
|
|
651
|
+
*
|
|
652
|
+
* The trade-off versus {@link CaptelloWebview}: until the form speaks once there is no
|
|
653
|
+
* window to post to, so an unprompted `submit()` / `prefill()` before then has nowhere
|
|
654
|
+
* to go. With the default `queueUntilReady` those sends are buffered and flushed on
|
|
655
|
+
* `form_load_complete`, which covers the normal case. If you drive the form
|
|
656
|
+
* unprompted from page load and can hold the element, prefer
|
|
657
|
+
* `new CaptelloWebview(iframe, …)`.
|
|
658
|
+
*/
|
|
659
|
+
declare function attach(options?: AttachOptions): CaptelloWebview;
|
|
660
|
+
/**
|
|
661
|
+
* The webview's iframe — the element itself, or any object exposing its
|
|
662
|
+
* `contentWindow` (useful for test doubles).
|
|
663
|
+
*
|
|
664
|
+
* The caller is responsible for the iframe being mounted before constructing the
|
|
665
|
+
* client: messages are source-matched against its `contentWindow` from the very first
|
|
666
|
+
* one, and sends post to it directly.
|
|
667
|
+
*
|
|
668
|
+
* When a real DOM element is passed, the client also tears itself down automatically
|
|
669
|
+
* once that element leaves the document — see
|
|
670
|
+
* {@link CaptelloWebviewOptions.autoDestroy}.
|
|
671
|
+
*/
|
|
672
|
+
type FrameLike = HTMLIFrameElement | {
|
|
673
|
+
contentWindow: Window | null;
|
|
674
|
+
};
|
|
675
|
+
/**
|
|
676
|
+
* Host-side controller for an embedded Captello capture webview.
|
|
677
|
+
*
|
|
678
|
+
* Wraps a single `<iframe>` and encodes the full message protocol:
|
|
679
|
+
* - **Receiving** (webview → host): subscribe with {@link on} / {@link onAny}.
|
|
680
|
+
* - **Sending** (host → webview): use {@link submit}, {@link reset}, {@link prefill},
|
|
681
|
+
* {@link triggerValidation}, {@link updateDraft}, or the lower-level {@link send}.
|
|
682
|
+
*
|
|
683
|
+
* Wire details handled for you: outgoing messages are `JSON.stringify`'d (the webview
|
|
684
|
+
* parses inbound data with `JSON.parse`, so a raw object would be ignored), and
|
|
685
|
+
* incoming messages are validated by origin + source before being parsed.
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```ts
|
|
689
|
+
* const iframe = document.querySelector("iframe")!;
|
|
690
|
+
* const webview = new CaptelloWebview(iframe, {
|
|
691
|
+
* targetOrigin: "https://capture.captello.com",
|
|
692
|
+
* });
|
|
693
|
+
*
|
|
694
|
+
* webview.on(OutboundMessageType.FormLoadComplete, () => console.log("ready"));
|
|
695
|
+
* webview.on(OutboundMessageType.SubmissionBody, (msg) => save(msg.data));
|
|
696
|
+
*
|
|
697
|
+
* // later, drive the form:
|
|
698
|
+
* webview.submit();
|
|
699
|
+
*
|
|
700
|
+
* // on teardown:
|
|
701
|
+
* webview.destroy();
|
|
702
|
+
* ```
|
|
703
|
+
*/
|
|
704
|
+
declare class CaptelloWebview {
|
|
705
|
+
private readonly frame;
|
|
706
|
+
/** Watches for the iframe leaving the document — see `autoDestroy`. */
|
|
707
|
+
private frameObserver?;
|
|
708
|
+
/** The webview window learned from an inbound message — see `adoptSource`. */
|
|
709
|
+
private adoptedWindow;
|
|
710
|
+
private readonly adoptSource;
|
|
711
|
+
private readonly autoDestroy;
|
|
712
|
+
/** Watches an adopted window for `closed` — see `watchAdoptedWindow`. */
|
|
713
|
+
private adoptedPoll?;
|
|
714
|
+
private readonly targetOrigin;
|
|
715
|
+
private readonly hostWindow;
|
|
716
|
+
private readonly matchSource;
|
|
717
|
+
private readonly listeners;
|
|
718
|
+
private readonly anyListeners;
|
|
719
|
+
private readonly boundHandler;
|
|
720
|
+
private destroyed;
|
|
721
|
+
private readonly queueUntilReady;
|
|
722
|
+
/** True once `form_load_complete` has been observed. */
|
|
723
|
+
private ready;
|
|
724
|
+
/** Messages sent before ready, flushed in order on load. */
|
|
725
|
+
private readonly outbox;
|
|
726
|
+
constructor(frame: FrameLike, options?: CaptelloWebviewOptions);
|
|
727
|
+
/**
|
|
728
|
+
* Tears the client down once the bound iframe leaves the document.
|
|
729
|
+
*
|
|
730
|
+
* Observes the whole document subtree because the iframe usually goes away with an
|
|
731
|
+
* ancestor (a panel or modal being emptied), which produces no mutation on the
|
|
732
|
+
* iframe's own parent. `isConnected` is the actual test, so removal at any depth
|
|
733
|
+
* counts; the observer only bothers checking when a mutation actually removed
|
|
734
|
+
* something.
|
|
735
|
+
*/
|
|
736
|
+
/**
|
|
737
|
+
* Tears the client down once an adopted webview window goes away.
|
|
738
|
+
*
|
|
739
|
+
* With no iframe element there is nothing to observe in the DOM, but a removed
|
|
740
|
+
* iframe's `contentWindow` reports `closed === true` — and `closed` is readable
|
|
741
|
+
* cross-origin — so a cheap periodic check is enough. Verified in Chromium against a
|
|
742
|
+
* genuinely cross-origin iframe.
|
|
743
|
+
*/
|
|
744
|
+
private watchAdoptedWindow;
|
|
745
|
+
private watchForFrameRemoval;
|
|
746
|
+
/** `true` once the webview has reported `form_load_complete`. */
|
|
747
|
+
get isReady(): boolean;
|
|
748
|
+
/**
|
|
749
|
+
* Subscribe to a single outbound message type. Returns an unsubscribe function.
|
|
750
|
+
*
|
|
751
|
+
* @example webview.on(OutboundMessageType.FormErrorMessage, (m) => toast(m.data));
|
|
752
|
+
*/
|
|
753
|
+
on<T extends OutboundMessageType>(type: T, listener: OutboundListener<T>): Unsubscribe;
|
|
754
|
+
/**
|
|
755
|
+
* Subscribe once: the listener is removed automatically after it fires the first
|
|
756
|
+
* time for `type`. Returns an unsubscribe function for cancelling early.
|
|
757
|
+
*/
|
|
758
|
+
once<T extends OutboundMessageType>(type: T, listener: OutboundListener<T>): Unsubscribe;
|
|
759
|
+
/** Subscribe to every outbound message regardless of type. Returns an unsubscribe function. */
|
|
760
|
+
onAny(listener: AnyOutboundListener): Unsubscribe;
|
|
761
|
+
/**
|
|
762
|
+
* Low-level send: posts any inbound message to the webview as a JSON string.
|
|
763
|
+
* Prefer the typed helpers below; use this only for forward-compatibility.
|
|
764
|
+
*
|
|
765
|
+
* When `queueUntilReady` is enabled (the default) and the form hasn't reported
|
|
766
|
+
* `form_load_complete` yet, the message is buffered and flushed on load instead of
|
|
767
|
+
* posted immediately.
|
|
768
|
+
*
|
|
769
|
+
* @throws if the iframe's `contentWindow` is not available (not yet loaded /
|
|
770
|
+
* detached) and the message can't be queued.
|
|
771
|
+
*/
|
|
772
|
+
send(message: InboundMessage): void;
|
|
773
|
+
/** Posts a message immediately, bypassing the ready-queue. */
|
|
774
|
+
private postNow;
|
|
775
|
+
/** Marks the client ready and flushes any queued messages, in order. */
|
|
776
|
+
private markReadyAndFlush;
|
|
777
|
+
/** Programmatically submit the form (fire-and-forget). */
|
|
778
|
+
submit(): void;
|
|
779
|
+
/**
|
|
780
|
+
* Submit the form and await the outcome.
|
|
781
|
+
*
|
|
782
|
+
* Sends `submit_form`, then resolves with the {@link SubmissionBody} when the
|
|
783
|
+
* webview emits `submission_body`, or rejects with a {@link SubmissionError}
|
|
784
|
+
* (carrying the translated message) when it emits `form_error_message`. Rejects
|
|
785
|
+
* with a {@link SubmissionTimeoutError} if neither arrives within `timeoutMs`.
|
|
786
|
+
*
|
|
787
|
+
* This is the typed, leak-free version of the common "click submit, wait for the
|
|
788
|
+
* result" flow — listeners are always cleaned up, including on timeout.
|
|
789
|
+
*
|
|
790
|
+
* @param timeoutMs how long to wait before giving up. Defaults to 60_000.
|
|
791
|
+
* @example
|
|
792
|
+
* try {
|
|
793
|
+
* const body = await webview.submitAndWait();
|
|
794
|
+
* await persist(body);
|
|
795
|
+
* } catch (err) {
|
|
796
|
+
* if (err instanceof SubmissionError) showToast(err.message);
|
|
797
|
+
* }
|
|
798
|
+
*/
|
|
799
|
+
submitAndWait(timeoutMs?: number): Promise<SubmissionBody>;
|
|
800
|
+
/** Reset the form, clearing all entered values. */
|
|
801
|
+
reset(): void;
|
|
802
|
+
/** Switch the current submission into draft-update mode. */
|
|
803
|
+
updateDraft(): void;
|
|
804
|
+
/** Run validation against a target field, or `"all"` for the whole form. */
|
|
805
|
+
triggerValidation(target: ValidationTarget): void;
|
|
806
|
+
/** Pre-fill form fields from a submission body, transcription items, or both. */
|
|
807
|
+
prefill(data: {
|
|
808
|
+
submission?: SubmissionPrefill;
|
|
809
|
+
info?: PrefillInfoItem[];
|
|
810
|
+
}): void;
|
|
811
|
+
/**
|
|
812
|
+
* Answer the webview's transcribe requests (its transcribe button — shown when the
|
|
813
|
+
* embed URL sets `showTranscribeButton` — sends `transcribe_scanner_request` when
|
|
814
|
+
* pressed).
|
|
815
|
+
*
|
|
816
|
+
* The handler receives the request (`element_id`, `image_urls`,
|
|
817
|
+
* `draft_submission_token`) and a {@link TranscribeScannerReply}, and answers in
|
|
818
|
+
* whichever style suits the transcription code. Either way the result is posted for
|
|
819
|
+
* you — the host never builds or stringifies a `transcribe_scanner_result`.
|
|
820
|
+
*
|
|
821
|
+
* **Return the fields** when the work is promise-based:
|
|
822
|
+
*
|
|
823
|
+
* ```js
|
|
824
|
+
* webview.onTranscribeScannerRequest(async ({ image_urls }) => {
|
|
825
|
+
* const fields = await transcribe(image_urls);
|
|
826
|
+
* return { fields, submissionType: "ocr_transcription" };
|
|
827
|
+
* });
|
|
828
|
+
* ```
|
|
829
|
+
*
|
|
830
|
+
* **Or answer through `reply`** when it isn't — a callback-style AJAX wrapper, an
|
|
831
|
+
* event bus, a method that returns `void`. Hand `reply` to whatever does the work:
|
|
832
|
+
*
|
|
833
|
+
* ```js
|
|
834
|
+
* webview.onTranscribeScannerRequest((request, reply) => {
|
|
835
|
+
* transcribeService(request.image_urls,
|
|
836
|
+
* (fields) => reply.resolve({ fields, submissionType: "ocr_transcription" }),
|
|
837
|
+
* () => reply.reject("Transcription failed."),
|
|
838
|
+
* );
|
|
839
|
+
* });
|
|
840
|
+
* ```
|
|
841
|
+
*
|
|
842
|
+
* A handler that returns nothing is taken to own the reply, so the request stays
|
|
843
|
+
* open until `reply` answers it. A thrown error / rejected promise is sent as an
|
|
844
|
+
* error result; an `Error`'s `message` is shown to the user by the webview, so throw
|
|
845
|
+
* display-ready messages. The first answer wins — a second is ignored.
|
|
846
|
+
*
|
|
847
|
+
* Returns an unsubscribe function.
|
|
848
|
+
*/
|
|
849
|
+
onTranscribeScannerRequest(handler: TranscribeScannerRequestHandler): Unsubscribe;
|
|
850
|
+
/**
|
|
851
|
+
* Answer a transcribe request **later**, from code that can't hand back a promise
|
|
852
|
+
* — a callback-style AJAX wrapper, an event bus, a method that returns `void`.
|
|
853
|
+
*
|
|
854
|
+
* Pair it with a plain `on(OutboundMessageType.TranscribeScannerRequest, ...)`
|
|
855
|
+
* subscription: hold on to the request (or just its `request_id`), and call this
|
|
856
|
+
* once the transcription lands. Prefer {@link onTranscribeScannerRequest} when
|
|
857
|
+
* your transcription code is already promise-based — it does this for you.
|
|
858
|
+
*
|
|
859
|
+
* Accepts the request message itself or its bare `request_id`. The reply is
|
|
860
|
+
* `JSON.stringify`'d and posted immediately, bypassing the ready-queue: a received
|
|
861
|
+
* request already proves the webview is live and listening. Replying after
|
|
862
|
+
* {@link destroy}, or after the iframe is detached, is a silent no-op — by then
|
|
863
|
+
* there is nobody left to answer, and the webview times its own button out.
|
|
864
|
+
*
|
|
865
|
+
* @example
|
|
866
|
+
* webview.on(OutboundMessageType.TranscribeScannerRequest, (request) => {
|
|
867
|
+
* ajax.send("Scanner", "transcribe", { image_urls: request.image_urls },
|
|
868
|
+
* (res) => webview.resolveTranscribeScannerRequest(request, {
|
|
869
|
+
* fields: res.fields,
|
|
870
|
+
* submissionType: "ocr_transcription",
|
|
871
|
+
* }),
|
|
872
|
+
* () => webview.rejectTranscribeScannerRequest(request, "Transcription failed."),
|
|
873
|
+
* );
|
|
874
|
+
* });
|
|
875
|
+
*/
|
|
876
|
+
resolveTranscribeScannerRequest(request: TranscribeScannerRequestRef, data: TranscribeScannerResultData): void;
|
|
877
|
+
/**
|
|
878
|
+
* Fail a transcribe request answered via
|
|
879
|
+
* {@link resolveTranscribeScannerRequest}'s deferred flow. `error` is shown to the
|
|
880
|
+
* user verbatim by the webview, so pass display-ready text.
|
|
881
|
+
*
|
|
882
|
+
* Same delivery semantics as {@link resolveTranscribeScannerRequest}: posted
|
|
883
|
+
* immediately, and a no-op once the client is destroyed or the iframe detached.
|
|
884
|
+
*/
|
|
885
|
+
rejectTranscribeScannerRequest(request: TranscribeScannerRequestRef, error: string): void;
|
|
886
|
+
/**
|
|
887
|
+
* Posts a `transcribe_scanner_result`, bypassing the ready-queue. A received
|
|
888
|
+
* request proves the webview is live and listening, even if this client attached
|
|
889
|
+
* after form load and never saw `form_load_complete`; queuing here could stall the
|
|
890
|
+
* reply forever while the webview's button sits in its pending state until timeout.
|
|
891
|
+
*/
|
|
892
|
+
private sendTranscribeScannerResult;
|
|
893
|
+
/** Remove the `message` listener and drop all subscriptions. Idempotent. */
|
|
894
|
+
destroy(): void;
|
|
895
|
+
private handleMessage;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
export { type AddressSubmissionValue as A, type BusinessCardValue as B, type CaptelloWebviewOptions as C, type DraftSubmissionData as D, parseOutboundMessage as E, type FrameLike as F, type InboundMessage as I, type NameSubmissionValue as N, OutboundMessageType as O, type PrefillInfoItem as P, type SubmissionBody as S, type TranscribeScannerRequestHandler as T, 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 AttachOptions as h, type AttachmentValue as i, FormElementType as j, InboundMessageType as k, type OrderCheckboxSubmissionData as l, type OrderRadioSubmissionData as m, type OutboundListener as n, type SubmissionPrefillDataItem as o, type SubmissionQuestionData as p, type TranscribeScannerReply as q, type TranscribeScannerRequestMessage as r, type TranscribeScannerRequestRef as s, type TranscribeScannerResultData as t, type TranscribeScannerResultMessage as u, type TranscribedScannerField as v, type VisibleSubmissionDataItem as w, type VisibleSubmissionElementType as x, type VisibleSubmissionElementValueMap as y, attach as z };
|