@captello/ulc-webview-sdk 0.1.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/LICENSE +21 -0
- package/README.md +424 -0
- package/dist/chunk-ETF52K7K.js +91 -0
- package/dist/chunk-ETF52K7K.js.map +1 -0
- package/dist/chunk-ZMYMZK2A.js +328 -0
- package/dist/chunk-ZMYMZK2A.js.map +1 -0
- package/dist/client-3IBxKbIE.d.cts +561 -0
- package/dist/client-3IBxKbIE.d.ts +561 -0
- package/dist/index.cjs +594 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +121 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/dist/promises.cjs +366 -0
- package/dist/promises.cjs.map +1 -0
- package/dist/promises.d.cts +72 -0
- package/dist/promises.d.ts +72 -0
- package/dist/promises.js +51 -0
- package/dist/promises.js.map +1 -0
- package/dist/react.cjs +476 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +108 -0
- package/dist/react.d.ts +108 -0
- package/dist/react.js +112 -0
- package/dist/react.js.map +1 -0
- package/package.json +82 -0
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { RefCallback } from 'react';
|
|
2
|
+
import { a as OutboundMessageMap, O as OutboundMessageType, d as OutboundMessage, C as CaptelloWebviewOptions, e as CaptelloWebview, V as ValidationTarget, P as PrefillInfoItem, f as SubmissionPrefill, S as SubmissionBody } from './client-3IBxKbIE.cjs';
|
|
3
|
+
export { b as SubmissionError, c as SubmissionTimeoutError, U as Unsubscribe } from './client-3IBxKbIE.cjs';
|
|
4
|
+
import { EmbedUrlOptions } from './index.cjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React adapter for the Captello webview SDK — `@captello/ulc-webview-sdk/react`.
|
|
8
|
+
*
|
|
9
|
+
* {@link useCaptelloWebview} owns a {@link CaptelloWebview} for the lifetime of an
|
|
10
|
+
* iframe: it creates the client once the iframe mounts, wires the outbound messages
|
|
11
|
+
* you care about to typed callbacks, tracks readiness, and destroys the client on
|
|
12
|
+
* unmount. You get back `iframeProps` to spread onto your `<iframe>` (or a bare `ref`),
|
|
13
|
+
* an `isReady` flag, and stable senders (`submit`, `reset`, `prefillInfo`, …).
|
|
14
|
+
*
|
|
15
|
+
* Sends made before the form loads are queued by the client and flushed on
|
|
16
|
+
* `form_load_complete`, so you can call `prefillInfo(...)` as soon as you have data —
|
|
17
|
+
* no need to gate on readiness yourself.
|
|
18
|
+
*
|
|
19
|
+
* Callbacks are held in a ref and always called fresh, so you do NOT need to memoize
|
|
20
|
+
* them — passing inline arrow functions will not re-subscribe or re-create the client.
|
|
21
|
+
*
|
|
22
|
+
* `react` is an optional peer dependency; importing this entry point requires React 18+.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* function UlcForm({ token, onSubmitted }: { token: string; onSubmitted: (b: SubmissionBody) => void }) {
|
|
26
|
+
* const { iframeProps, isReady, submit } = useCaptelloWebview({
|
|
27
|
+
* embedUrl: {
|
|
28
|
+
* baseUrl: "https://capture.captello.com/capture/submission",
|
|
29
|
+
* eventWebAccessToken: token,
|
|
30
|
+
* mode: FormMode.Submit,
|
|
31
|
+
* launcher: LauncherType.EventGenWeb,
|
|
32
|
+
* },
|
|
33
|
+
* onSubmissionBody: (m) => onSubmitted(m.data),
|
|
34
|
+
* });
|
|
35
|
+
* return (
|
|
36
|
+
* <>
|
|
37
|
+
* {!isReady && <Spinner />}
|
|
38
|
+
* <iframe {...iframeProps} title="UlcForm" allow="camera; microphone" />
|
|
39
|
+
* <button onClick={submit}>Submit</button>
|
|
40
|
+
* </>
|
|
41
|
+
* );
|
|
42
|
+
* }
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/** Per-message-type callback props accepted by {@link useCaptelloWebview}. */
|
|
46
|
+
interface CaptelloWebviewCallbacks {
|
|
47
|
+
onFormLoadComplete?: (message: OutboundMessageMap[OutboundMessageType.FormLoadComplete]) => void;
|
|
48
|
+
onFormErrorMessage?: (message: OutboundMessageMap[OutboundMessageType.FormErrorMessage]) => void;
|
|
49
|
+
onSubmissionBody?: (message: OutboundMessageMap[OutboundMessageType.SubmissionBody]) => void;
|
|
50
|
+
onFormSubmitSuccess?: (message: OutboundMessageMap[OutboundMessageType.FormSubmitSuccess]) => void;
|
|
51
|
+
onConnexionsProfileRedirect?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsProfileRedirect]) => void;
|
|
52
|
+
onConnexionsDownloadVcard?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsDownloadVcard]) => void;
|
|
53
|
+
/** Catch-all: called for every outbound message, after the specific handler above. */
|
|
54
|
+
onAnyMessage?: (message: OutboundMessage) => void;
|
|
55
|
+
}
|
|
56
|
+
/** Embed-URL config: a base URL plus {@link EmbedUrlOptions}. */
|
|
57
|
+
interface EmbedUrlConfig extends EmbedUrlOptions {
|
|
58
|
+
/** The capture base URL, e.g. `"https://capture.captello.com/capture/submission"`. */
|
|
59
|
+
baseUrl: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Options for {@link useCaptelloWebview}.
|
|
63
|
+
*
|
|
64
|
+
* Provide **either** `embedUrl` (the hook builds the URL and derives `targetOrigin`,
|
|
65
|
+
* returning `iframeProps.src`) **or** your own `targetOrigin` (you set the iframe `src`
|
|
66
|
+
* yourself). Plus message callbacks and the usual client options.
|
|
67
|
+
*/
|
|
68
|
+
interface UseCaptelloWebviewOptions extends CaptelloWebviewOptions, CaptelloWebviewCallbacks {
|
|
69
|
+
/** Build the iframe URL and derive `targetOrigin` from it. Sets `iframeProps.src`. */
|
|
70
|
+
embedUrl?: EmbedUrlConfig;
|
|
71
|
+
}
|
|
72
|
+
/** Readiness of the embedded form. */
|
|
73
|
+
type CaptelloWebviewStatus = "loading" | "ready" | "error";
|
|
74
|
+
/** Props to spread onto the `<iframe>`. `src` is present only when `embedUrl` is given. */
|
|
75
|
+
interface CaptelloIframeProps {
|
|
76
|
+
ref: RefCallback<HTMLIFrameElement | null>;
|
|
77
|
+
src?: string;
|
|
78
|
+
}
|
|
79
|
+
/** What {@link useCaptelloWebview} returns. */
|
|
80
|
+
interface UseCaptelloWebviewResult {
|
|
81
|
+
/** Spread onto your iframe: `<iframe {...iframeProps} />`. Includes `src` if `embedUrl` was given. */
|
|
82
|
+
iframeProps: CaptelloIframeProps;
|
|
83
|
+
/** The iframe ref callback (same as `iframeProps.ref`), if you'd rather set `src` yourself. */
|
|
84
|
+
ref: RefCallback<HTMLIFrameElement | null>;
|
|
85
|
+
/** `true` once the form has reported `form_load_complete`. */
|
|
86
|
+
isReady: boolean;
|
|
87
|
+
/** `"loading"` → `"ready"`; flips to `"error"` if a `form_error_message` arrives. */
|
|
88
|
+
status: CaptelloWebviewStatus;
|
|
89
|
+
/** The live client, or `null` before the iframe mounts. For escape-hatch use. */
|
|
90
|
+
getClient: () => CaptelloWebview | null;
|
|
91
|
+
submit: () => void;
|
|
92
|
+
reset: () => void;
|
|
93
|
+
updateDraft: () => void;
|
|
94
|
+
triggerValidation: (target: ValidationTarget) => void;
|
|
95
|
+
prefillInfo: (info: PrefillInfoItem[]) => void;
|
|
96
|
+
prefillSubmission: (submission: SubmissionPrefill) => void;
|
|
97
|
+
prefillSubmissionAndInfo: (data: {
|
|
98
|
+
submission?: SubmissionPrefill;
|
|
99
|
+
info?: PrefillInfoItem[];
|
|
100
|
+
}) => void;
|
|
101
|
+
submitAndWait: (timeoutMs?: number) => Promise<SubmissionBody>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Binds a {@link CaptelloWebview} to an iframe's lifecycle. See the module doc for usage.
|
|
105
|
+
*/
|
|
106
|
+
declare function useCaptelloWebview(options: UseCaptelloWebviewOptions): UseCaptelloWebviewResult;
|
|
107
|
+
|
|
108
|
+
export { type CaptelloIframeProps, CaptelloWebview, type CaptelloWebviewCallbacks, type CaptelloWebviewStatus, type EmbedUrlConfig, type UseCaptelloWebviewOptions, type UseCaptelloWebviewResult, useCaptelloWebview };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { RefCallback } from 'react';
|
|
2
|
+
import { a as OutboundMessageMap, O as OutboundMessageType, d as OutboundMessage, C as CaptelloWebviewOptions, e as CaptelloWebview, V as ValidationTarget, P as PrefillInfoItem, f as SubmissionPrefill, S as SubmissionBody } from './client-3IBxKbIE.js';
|
|
3
|
+
export { b as SubmissionError, c as SubmissionTimeoutError, U as Unsubscribe } from './client-3IBxKbIE.js';
|
|
4
|
+
import { EmbedUrlOptions } from './index.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React adapter for the Captello webview SDK — `@captello/ulc-webview-sdk/react`.
|
|
8
|
+
*
|
|
9
|
+
* {@link useCaptelloWebview} owns a {@link CaptelloWebview} for the lifetime of an
|
|
10
|
+
* iframe: it creates the client once the iframe mounts, wires the outbound messages
|
|
11
|
+
* you care about to typed callbacks, tracks readiness, and destroys the client on
|
|
12
|
+
* unmount. You get back `iframeProps` to spread onto your `<iframe>` (or a bare `ref`),
|
|
13
|
+
* an `isReady` flag, and stable senders (`submit`, `reset`, `prefillInfo`, …).
|
|
14
|
+
*
|
|
15
|
+
* Sends made before the form loads are queued by the client and flushed on
|
|
16
|
+
* `form_load_complete`, so you can call `prefillInfo(...)` as soon as you have data —
|
|
17
|
+
* no need to gate on readiness yourself.
|
|
18
|
+
*
|
|
19
|
+
* Callbacks are held in a ref and always called fresh, so you do NOT need to memoize
|
|
20
|
+
* them — passing inline arrow functions will not re-subscribe or re-create the client.
|
|
21
|
+
*
|
|
22
|
+
* `react` is an optional peer dependency; importing this entry point requires React 18+.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* function UlcForm({ token, onSubmitted }: { token: string; onSubmitted: (b: SubmissionBody) => void }) {
|
|
26
|
+
* const { iframeProps, isReady, submit } = useCaptelloWebview({
|
|
27
|
+
* embedUrl: {
|
|
28
|
+
* baseUrl: "https://capture.captello.com/capture/submission",
|
|
29
|
+
* eventWebAccessToken: token,
|
|
30
|
+
* mode: FormMode.Submit,
|
|
31
|
+
* launcher: LauncherType.EventGenWeb,
|
|
32
|
+
* },
|
|
33
|
+
* onSubmissionBody: (m) => onSubmitted(m.data),
|
|
34
|
+
* });
|
|
35
|
+
* return (
|
|
36
|
+
* <>
|
|
37
|
+
* {!isReady && <Spinner />}
|
|
38
|
+
* <iframe {...iframeProps} title="UlcForm" allow="camera; microphone" />
|
|
39
|
+
* <button onClick={submit}>Submit</button>
|
|
40
|
+
* </>
|
|
41
|
+
* );
|
|
42
|
+
* }
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/** Per-message-type callback props accepted by {@link useCaptelloWebview}. */
|
|
46
|
+
interface CaptelloWebviewCallbacks {
|
|
47
|
+
onFormLoadComplete?: (message: OutboundMessageMap[OutboundMessageType.FormLoadComplete]) => void;
|
|
48
|
+
onFormErrorMessage?: (message: OutboundMessageMap[OutboundMessageType.FormErrorMessage]) => void;
|
|
49
|
+
onSubmissionBody?: (message: OutboundMessageMap[OutboundMessageType.SubmissionBody]) => void;
|
|
50
|
+
onFormSubmitSuccess?: (message: OutboundMessageMap[OutboundMessageType.FormSubmitSuccess]) => void;
|
|
51
|
+
onConnexionsProfileRedirect?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsProfileRedirect]) => void;
|
|
52
|
+
onConnexionsDownloadVcard?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsDownloadVcard]) => void;
|
|
53
|
+
/** Catch-all: called for every outbound message, after the specific handler above. */
|
|
54
|
+
onAnyMessage?: (message: OutboundMessage) => void;
|
|
55
|
+
}
|
|
56
|
+
/** Embed-URL config: a base URL plus {@link EmbedUrlOptions}. */
|
|
57
|
+
interface EmbedUrlConfig extends EmbedUrlOptions {
|
|
58
|
+
/** The capture base URL, e.g. `"https://capture.captello.com/capture/submission"`. */
|
|
59
|
+
baseUrl: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Options for {@link useCaptelloWebview}.
|
|
63
|
+
*
|
|
64
|
+
* Provide **either** `embedUrl` (the hook builds the URL and derives `targetOrigin`,
|
|
65
|
+
* returning `iframeProps.src`) **or** your own `targetOrigin` (you set the iframe `src`
|
|
66
|
+
* yourself). Plus message callbacks and the usual client options.
|
|
67
|
+
*/
|
|
68
|
+
interface UseCaptelloWebviewOptions extends CaptelloWebviewOptions, CaptelloWebviewCallbacks {
|
|
69
|
+
/** Build the iframe URL and derive `targetOrigin` from it. Sets `iframeProps.src`. */
|
|
70
|
+
embedUrl?: EmbedUrlConfig;
|
|
71
|
+
}
|
|
72
|
+
/** Readiness of the embedded form. */
|
|
73
|
+
type CaptelloWebviewStatus = "loading" | "ready" | "error";
|
|
74
|
+
/** Props to spread onto the `<iframe>`. `src` is present only when `embedUrl` is given. */
|
|
75
|
+
interface CaptelloIframeProps {
|
|
76
|
+
ref: RefCallback<HTMLIFrameElement | null>;
|
|
77
|
+
src?: string;
|
|
78
|
+
}
|
|
79
|
+
/** What {@link useCaptelloWebview} returns. */
|
|
80
|
+
interface UseCaptelloWebviewResult {
|
|
81
|
+
/** Spread onto your iframe: `<iframe {...iframeProps} />`. Includes `src` if `embedUrl` was given. */
|
|
82
|
+
iframeProps: CaptelloIframeProps;
|
|
83
|
+
/** The iframe ref callback (same as `iframeProps.ref`), if you'd rather set `src` yourself. */
|
|
84
|
+
ref: RefCallback<HTMLIFrameElement | null>;
|
|
85
|
+
/** `true` once the form has reported `form_load_complete`. */
|
|
86
|
+
isReady: boolean;
|
|
87
|
+
/** `"loading"` → `"ready"`; flips to `"error"` if a `form_error_message` arrives. */
|
|
88
|
+
status: CaptelloWebviewStatus;
|
|
89
|
+
/** The live client, or `null` before the iframe mounts. For escape-hatch use. */
|
|
90
|
+
getClient: () => CaptelloWebview | null;
|
|
91
|
+
submit: () => void;
|
|
92
|
+
reset: () => void;
|
|
93
|
+
updateDraft: () => void;
|
|
94
|
+
triggerValidation: (target: ValidationTarget) => void;
|
|
95
|
+
prefillInfo: (info: PrefillInfoItem[]) => void;
|
|
96
|
+
prefillSubmission: (submission: SubmissionPrefill) => void;
|
|
97
|
+
prefillSubmissionAndInfo: (data: {
|
|
98
|
+
submission?: SubmissionPrefill;
|
|
99
|
+
info?: PrefillInfoItem[];
|
|
100
|
+
}) => void;
|
|
101
|
+
submitAndWait: (timeoutMs?: number) => Promise<SubmissionBody>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Binds a {@link CaptelloWebview} to an iframe's lifecycle. See the module doc for usage.
|
|
105
|
+
*/
|
|
106
|
+
declare function useCaptelloWebview(options: UseCaptelloWebviewOptions): UseCaptelloWebviewResult;
|
|
107
|
+
|
|
108
|
+
export { type CaptelloIframeProps, CaptelloWebview, type CaptelloWebviewCallbacks, type CaptelloWebviewStatus, type EmbedUrlConfig, type UseCaptelloWebviewOptions, type UseCaptelloWebviewResult, useCaptelloWebview };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { buildEmbedUrl, targetOriginFromUrl } from './chunk-ETF52K7K.js';
|
|
2
|
+
import { CaptelloWebview, OutboundMessageType } from './chunk-ZMYMZK2A.js';
|
|
3
|
+
export { CaptelloWebview, SubmissionError, SubmissionTimeoutError } from './chunk-ZMYMZK2A.js';
|
|
4
|
+
import { useRef, useState, useCallback, useEffect } from 'react';
|
|
5
|
+
|
|
6
|
+
var CALLBACK_BY_TYPE = {
|
|
7
|
+
["form_load_complete" /* FormLoadComplete */]: "onFormLoadComplete",
|
|
8
|
+
["form_error_message" /* FormErrorMessage */]: "onFormErrorMessage",
|
|
9
|
+
["submission_body" /* SubmissionBody */]: "onSubmissionBody",
|
|
10
|
+
["form_submit_success" /* FormSubmitSuccess */]: "onFormSubmitSuccess",
|
|
11
|
+
["connexions_profile_redirect" /* ConnexionsProfileRedirect */]: "onConnexionsProfileRedirect",
|
|
12
|
+
["connexions_download_vcard" /* ConnexionsDownloadVcard */]: "onConnexionsDownloadVcard"
|
|
13
|
+
};
|
|
14
|
+
function useCaptelloWebview(options) {
|
|
15
|
+
const { embedUrl, matchSource, queueUntilReady, hostWindow } = options;
|
|
16
|
+
const src = embedUrl ? buildEmbedUrl(embedUrl.baseUrl, embedUrl) : void 0;
|
|
17
|
+
const targetOrigin = src ? targetOriginFromUrl(src) : options.targetOrigin;
|
|
18
|
+
const optionsRef = useRef(options);
|
|
19
|
+
optionsRef.current = options;
|
|
20
|
+
const clientRef = useRef(null);
|
|
21
|
+
const frameRef = useRef(null);
|
|
22
|
+
const teardown = useRef(null);
|
|
23
|
+
const [status, setStatus] = useState("loading");
|
|
24
|
+
const attach = useCallback(
|
|
25
|
+
(frame) => {
|
|
26
|
+
teardown.current?.();
|
|
27
|
+
teardown.current = null;
|
|
28
|
+
clientRef.current = null;
|
|
29
|
+
frameRef.current = frame;
|
|
30
|
+
setStatus("loading");
|
|
31
|
+
if (!frame) return;
|
|
32
|
+
const client = new CaptelloWebview(frame, {
|
|
33
|
+
targetOrigin,
|
|
34
|
+
hostWindow: optionsRef.current.hostWindow,
|
|
35
|
+
matchSource: optionsRef.current.matchSource,
|
|
36
|
+
queueUntilReady: optionsRef.current.queueUntilReady
|
|
37
|
+
});
|
|
38
|
+
clientRef.current = client;
|
|
39
|
+
const offs = [];
|
|
40
|
+
for (const type of Object.values(OutboundMessageType)) {
|
|
41
|
+
offs.push(
|
|
42
|
+
client.on(type, (message) => {
|
|
43
|
+
if (type === "form_load_complete" /* FormLoadComplete */) setStatus("ready");
|
|
44
|
+
else if (type === "form_error_message" /* FormErrorMessage */) setStatus("error");
|
|
45
|
+
const handler = optionsRef.current[CALLBACK_BY_TYPE[type]];
|
|
46
|
+
handler?.(message);
|
|
47
|
+
optionsRef.current.onAnyMessage?.(message);
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
teardown.current = () => {
|
|
52
|
+
for (const off of offs) off();
|
|
53
|
+
client.destroy();
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
// Re-create the client only when connection-level inputs change.
|
|
57
|
+
// Callbacks are read via optionsRef, so they intentionally aren't deps.
|
|
58
|
+
[targetOrigin, matchSource, queueUntilReady, hostWindow]
|
|
59
|
+
);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (frameRef.current) attach(frameRef.current);
|
|
62
|
+
return () => {
|
|
63
|
+
teardown.current?.();
|
|
64
|
+
teardown.current = null;
|
|
65
|
+
clientRef.current = null;
|
|
66
|
+
};
|
|
67
|
+
}, [attach]);
|
|
68
|
+
const getClient = useCallback(() => clientRef.current, []);
|
|
69
|
+
const submit = useCallback(() => clientRef.current?.submit(), []);
|
|
70
|
+
const reset = useCallback(() => clientRef.current?.reset(), []);
|
|
71
|
+
const updateDraft = useCallback(() => clientRef.current?.updateDraft(), []);
|
|
72
|
+
const triggerValidation = useCallback(
|
|
73
|
+
(target) => clientRef.current?.triggerValidation(target),
|
|
74
|
+
[]
|
|
75
|
+
);
|
|
76
|
+
const prefillInfo = useCallback((info) => clientRef.current?.prefillInfo(info), []);
|
|
77
|
+
const prefillSubmission = useCallback(
|
|
78
|
+
(submission) => clientRef.current?.prefillSubmission(submission),
|
|
79
|
+
[]
|
|
80
|
+
);
|
|
81
|
+
const prefillSubmissionAndInfo = useCallback(
|
|
82
|
+
(data) => clientRef.current?.prefillSubmissionAndInfo(data),
|
|
83
|
+
[]
|
|
84
|
+
);
|
|
85
|
+
const submitAndWait = useCallback((timeoutMs) => {
|
|
86
|
+
const client = clientRef.current;
|
|
87
|
+
if (!client) {
|
|
88
|
+
return Promise.reject(new Error("CaptelloWebview: iframe is not mounted yet."));
|
|
89
|
+
}
|
|
90
|
+
return client.submitAndWait(timeoutMs);
|
|
91
|
+
}, []);
|
|
92
|
+
const iframeProps = src ? { ref: attach, src } : { ref: attach };
|
|
93
|
+
return {
|
|
94
|
+
iframeProps,
|
|
95
|
+
ref: attach,
|
|
96
|
+
isReady: status === "ready",
|
|
97
|
+
status,
|
|
98
|
+
getClient,
|
|
99
|
+
submit,
|
|
100
|
+
reset,
|
|
101
|
+
updateDraft,
|
|
102
|
+
triggerValidation,
|
|
103
|
+
prefillInfo,
|
|
104
|
+
prefillSubmission,
|
|
105
|
+
prefillSubmissionAndInfo,
|
|
106
|
+
submitAndWait
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export { useCaptelloWebview };
|
|
111
|
+
//# sourceMappingURL=react.js.map
|
|
112
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.ts"],"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 * {@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`, `prefillInfo`, …).\n *\n * Sends made before the form loads are queued by the client and flushed on\n * `form_load_complete`, so you can call `prefillInfo(...)` 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/capture/submission\",\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 { useCallback, useEffect, useRef, useState, type RefCallback } from \"react\";\n\nimport { CaptelloWebview } from \"./client\";\nimport type { CaptelloWebviewOptions, Unsubscribe } from \"./client\";\nimport { buildEmbedUrl, targetOriginFromUrl } 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 /** The capture base URL, e.g. `\"https://capture.captello.com/capture/submission\"`. */\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 prefillInfo: (info: PrefillInfoItem[]) => void;\n prefillSubmission: (submission: SubmissionPrefill) => void;\n prefillSubmissionAndInfo: (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 ? targetOriginFromUrl(src) : 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 prefillInfo = useCallback((info: PrefillInfoItem[]) => clientRef.current?.prefillInfo(info), []);\n const prefillSubmission = useCallback(\n (submission: SubmissionPrefill) => clientRef.current?.prefillSubmission(submission),\n [],\n );\n const prefillSubmissionAndInfo = useCallback(\n (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) =>\n clientRef.current?.prefillSubmissionAndInfo(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 prefillInfo,\n prefillSubmission,\n prefillSubmissionAndInfo,\n submitAndWait,\n };\n}\n\nexport { CaptelloWebview, SubmissionError, SubmissionTimeoutError } from \"./client\";\nexport type { Unsubscribe } from \"./client\";\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@captello/ulc-webview-sdk",
|
|
3
|
+
"version": "0.1.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
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"typesVersions": {
|
|
13
|
+
"*": {
|
|
14
|
+
"promises": [
|
|
15
|
+
"./dist/promises.d.ts"
|
|
16
|
+
],
|
|
17
|
+
"react": [
|
|
18
|
+
"./dist/react.d.ts"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"require": "./dist/index.cjs"
|
|
27
|
+
},
|
|
28
|
+
"./promises": {
|
|
29
|
+
"types": "./dist/promises.d.ts",
|
|
30
|
+
"import": "./dist/promises.js",
|
|
31
|
+
"require": "./dist/promises.cjs"
|
|
32
|
+
},
|
|
33
|
+
"./react": {
|
|
34
|
+
"types": "./dist/react.d.ts",
|
|
35
|
+
"import": "./dist/react.js",
|
|
36
|
+
"require": "./dist/react.cjs"
|
|
37
|
+
},
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup",
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"typecheck:test": "tsc -p tsconfig.test.json",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"clean": "rm -rf dist"
|
|
52
|
+
},
|
|
53
|
+
"keywords": [
|
|
54
|
+
"captello",
|
|
55
|
+
"webview",
|
|
56
|
+
"iframe",
|
|
57
|
+
"postmessage",
|
|
58
|
+
"embed",
|
|
59
|
+
"sdk"
|
|
60
|
+
],
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"react": ">=18"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"react": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@testing-library/react": "16.1.0",
|
|
71
|
+
"@types/react": "19.2.2",
|
|
72
|
+
"jsdom": "25.0.1",
|
|
73
|
+
"react": "19.2.2",
|
|
74
|
+
"react-dom": "19.2.2",
|
|
75
|
+
"tsup": "8.3.5",
|
|
76
|
+
"typescript": "5.9.3",
|
|
77
|
+
"vitest": "2.1.9"
|
|
78
|
+
},
|
|
79
|
+
"publishConfig": {
|
|
80
|
+
"access": "public"
|
|
81
|
+
}
|
|
82
|
+
}
|