@nium/nium-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/dist/index.d.mts +163 -0
- package/dist/index.d.ts +163 -0
- package/dist/index.js +251 -0
- package/dist/index.mjs +222 -0
- package/package.json +25 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/** Environments supported by the SDK */
|
|
2
|
+
type NiumEnv = 'local' | 'sandbox' | 'qa' | 'production';
|
|
3
|
+
/** Module types that map to web-onboard routes */
|
|
4
|
+
type NiumElementType = 'payment' | 'tax' | 'identity' | 'full';
|
|
5
|
+
/** Configuration passed to `init()` */
|
|
6
|
+
interface NiumInitOptions {
|
|
7
|
+
/** Target environment */
|
|
8
|
+
env: NiumEnv;
|
|
9
|
+
/** Session code returned by marketplace-service /init */
|
|
10
|
+
sessionCode: string;
|
|
11
|
+
/** Optional locale (defaults to 'en') */
|
|
12
|
+
locale?: string;
|
|
13
|
+
/** Override the hosted form base URL (useful for local dev) */
|
|
14
|
+
hostedFormUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
/** Theme options for embedded components */
|
|
17
|
+
interface NiumTheme {
|
|
18
|
+
primaryColor?: string;
|
|
19
|
+
borderRadius?: string;
|
|
20
|
+
fontFamily?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Prefill data for the payment (beneficiary) form */
|
|
23
|
+
interface NiumPaymentPrefill {
|
|
24
|
+
name?: {
|
|
25
|
+
first?: string;
|
|
26
|
+
last?: string;
|
|
27
|
+
};
|
|
28
|
+
email?: string;
|
|
29
|
+
entityType?: 'individual' | 'business';
|
|
30
|
+
}
|
|
31
|
+
/** Options passed to `createElement()` */
|
|
32
|
+
interface NiumElementOptions {
|
|
33
|
+
/** Pre-fill form fields */
|
|
34
|
+
prefill?: NiumPaymentPrefill;
|
|
35
|
+
/** Custom theme overrides */
|
|
36
|
+
theme?: NiumTheme;
|
|
37
|
+
/** Custom minimum height for the iframe (default: 400) */
|
|
38
|
+
customizations?: {
|
|
39
|
+
minHeight?: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
interface NiumReadyEvent {
|
|
43
|
+
type: 'ready';
|
|
44
|
+
}
|
|
45
|
+
interface NiumResizeEvent {
|
|
46
|
+
type: 'resize';
|
|
47
|
+
height: number;
|
|
48
|
+
}
|
|
49
|
+
interface NiumChangeEvent {
|
|
50
|
+
type: 'change';
|
|
51
|
+
data: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
interface NiumSubmitResult {
|
|
54
|
+
beneficiaryId?: string;
|
|
55
|
+
filerHashId?: string;
|
|
56
|
+
status: 'success';
|
|
57
|
+
}
|
|
58
|
+
interface NiumSubmitEvent {
|
|
59
|
+
type: 'submit';
|
|
60
|
+
result: NiumSubmitResult;
|
|
61
|
+
}
|
|
62
|
+
interface NiumErrorEvent {
|
|
63
|
+
type: 'error';
|
|
64
|
+
error: {
|
|
65
|
+
message: string;
|
|
66
|
+
code?: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
interface NiumCancelEvent {
|
|
70
|
+
type: 'cancel';
|
|
71
|
+
}
|
|
72
|
+
type NiumEvent = NiumReadyEvent | NiumResizeEvent | NiumChangeEvent | NiumSubmitEvent | NiumErrorEvent | NiumCancelEvent;
|
|
73
|
+
/** Map of event type string -> payload */
|
|
74
|
+
interface NiumEventMap {
|
|
75
|
+
ready: NiumReadyEvent;
|
|
76
|
+
resize: NiumResizeEvent;
|
|
77
|
+
change: NiumChangeEvent;
|
|
78
|
+
submit: NiumSubmitEvent;
|
|
79
|
+
error: NiumErrorEvent;
|
|
80
|
+
cancel: NiumCancelEvent;
|
|
81
|
+
}
|
|
82
|
+
/** Callback signature for a given event type */
|
|
83
|
+
type NiumEventCallback<T extends keyof NiumEventMap> = (event: NiumEventMap[T]) => void;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* NiumElement wraps a hidden iframe pointing at the web-onboard hosted forms.
|
|
87
|
+
* It translates internal postMessage events into typed callbacks and exposes
|
|
88
|
+
* a programmatic `submit()` method (Airwallex-style).
|
|
89
|
+
*/
|
|
90
|
+
declare class NiumElement {
|
|
91
|
+
private readonly iframeSrc;
|
|
92
|
+
private readonly elementType;
|
|
93
|
+
private readonly options;
|
|
94
|
+
private iframe;
|
|
95
|
+
private container;
|
|
96
|
+
private listeners;
|
|
97
|
+
private boundMessageHandler;
|
|
98
|
+
private destroyed;
|
|
99
|
+
private readonly origin;
|
|
100
|
+
private pendingSubmit;
|
|
101
|
+
constructor(iframeSrc: string, elementType: NiumElementType, options: NiumElementOptions, origin: string);
|
|
102
|
+
/** Mount the iframe into a DOM element (selector string or HTMLElement). */
|
|
103
|
+
mount(target: string | HTMLElement): void;
|
|
104
|
+
/** Remove the iframe from the DOM. The element can be re-mounted later. */
|
|
105
|
+
unmount(): void;
|
|
106
|
+
/** Permanently destroy the element. Removes from DOM and clears all listeners. */
|
|
107
|
+
destroy(): void;
|
|
108
|
+
/**
|
|
109
|
+
* Programmatically trigger form submission from the parent app.
|
|
110
|
+
*
|
|
111
|
+
* Sends a `nium:submit-request` message to the iframe. The hosted form
|
|
112
|
+
* validates and submits, then responds with `nium:submit` (success) or
|
|
113
|
+
* `nium:error` (failure).
|
|
114
|
+
*
|
|
115
|
+
* @returns Promise that resolves with the submit result or rejects with an error.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* const result = await element.submit()
|
|
120
|
+
* console.log(result.beneficiaryId)
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
submit(): Promise<NiumSubmitResult>;
|
|
124
|
+
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
125
|
+
on<K extends keyof NiumEventMap>(event: K, callback: NiumEventCallback<K>): () => void;
|
|
126
|
+
/** Remove a specific listener. */
|
|
127
|
+
off<K extends keyof NiumEventMap>(event: K, callback: NiumEventCallback<K>): void;
|
|
128
|
+
private handleMessage;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Initialize the SDK. Must be called once before `createElement`.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* import { init, createElement } from '@nium/nium-sdk'
|
|
137
|
+
*
|
|
138
|
+
* await init({
|
|
139
|
+
* env: 'sandbox',
|
|
140
|
+
* sessionCode: '<code-from-server>',
|
|
141
|
+
* })
|
|
142
|
+
*
|
|
143
|
+
* const element = await createElement('payment')
|
|
144
|
+
* element.mount('#container')
|
|
145
|
+
*
|
|
146
|
+
* // Option A: listen for submit events
|
|
147
|
+
* element.on('submit', (e) => console.log(e.result))
|
|
148
|
+
*
|
|
149
|
+
* // Option B: programmatic submit from parent
|
|
150
|
+
* const result = await element.submit()
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare const init: (options: NiumInitOptions) => Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Create an embeddable element for a specific onboarding module.
|
|
156
|
+
*
|
|
157
|
+
* @param type - The module to render: 'payment' | 'tax' | 'identity' | 'full'
|
|
158
|
+
* @param options - Optional configuration (prefill, theme, customizations)
|
|
159
|
+
* @returns A NiumElement that can be mounted, listened to, submitted, and destroyed
|
|
160
|
+
*/
|
|
161
|
+
declare const createElement: (type: NiumElementType, options?: NiumElementOptions) => Promise<NiumElement>;
|
|
162
|
+
|
|
163
|
+
export { type NiumCancelEvent, type NiumChangeEvent, NiumElement, type NiumElementOptions, type NiumElementType, type NiumEnv, type NiumErrorEvent, type NiumEvent, type NiumEventCallback, type NiumEventMap, type NiumInitOptions, type NiumPaymentPrefill, type NiumReadyEvent, type NiumResizeEvent, type NiumSubmitEvent, type NiumSubmitResult, type NiumTheme, createElement, init };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/** Environments supported by the SDK */
|
|
2
|
+
type NiumEnv = 'local' | 'sandbox' | 'qa' | 'production';
|
|
3
|
+
/** Module types that map to web-onboard routes */
|
|
4
|
+
type NiumElementType = 'payment' | 'tax' | 'identity' | 'full';
|
|
5
|
+
/** Configuration passed to `init()` */
|
|
6
|
+
interface NiumInitOptions {
|
|
7
|
+
/** Target environment */
|
|
8
|
+
env: NiumEnv;
|
|
9
|
+
/** Session code returned by marketplace-service /init */
|
|
10
|
+
sessionCode: string;
|
|
11
|
+
/** Optional locale (defaults to 'en') */
|
|
12
|
+
locale?: string;
|
|
13
|
+
/** Override the hosted form base URL (useful for local dev) */
|
|
14
|
+
hostedFormUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
/** Theme options for embedded components */
|
|
17
|
+
interface NiumTheme {
|
|
18
|
+
primaryColor?: string;
|
|
19
|
+
borderRadius?: string;
|
|
20
|
+
fontFamily?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Prefill data for the payment (beneficiary) form */
|
|
23
|
+
interface NiumPaymentPrefill {
|
|
24
|
+
name?: {
|
|
25
|
+
first?: string;
|
|
26
|
+
last?: string;
|
|
27
|
+
};
|
|
28
|
+
email?: string;
|
|
29
|
+
entityType?: 'individual' | 'business';
|
|
30
|
+
}
|
|
31
|
+
/** Options passed to `createElement()` */
|
|
32
|
+
interface NiumElementOptions {
|
|
33
|
+
/** Pre-fill form fields */
|
|
34
|
+
prefill?: NiumPaymentPrefill;
|
|
35
|
+
/** Custom theme overrides */
|
|
36
|
+
theme?: NiumTheme;
|
|
37
|
+
/** Custom minimum height for the iframe (default: 400) */
|
|
38
|
+
customizations?: {
|
|
39
|
+
minHeight?: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
interface NiumReadyEvent {
|
|
43
|
+
type: 'ready';
|
|
44
|
+
}
|
|
45
|
+
interface NiumResizeEvent {
|
|
46
|
+
type: 'resize';
|
|
47
|
+
height: number;
|
|
48
|
+
}
|
|
49
|
+
interface NiumChangeEvent {
|
|
50
|
+
type: 'change';
|
|
51
|
+
data: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
interface NiumSubmitResult {
|
|
54
|
+
beneficiaryId?: string;
|
|
55
|
+
filerHashId?: string;
|
|
56
|
+
status: 'success';
|
|
57
|
+
}
|
|
58
|
+
interface NiumSubmitEvent {
|
|
59
|
+
type: 'submit';
|
|
60
|
+
result: NiumSubmitResult;
|
|
61
|
+
}
|
|
62
|
+
interface NiumErrorEvent {
|
|
63
|
+
type: 'error';
|
|
64
|
+
error: {
|
|
65
|
+
message: string;
|
|
66
|
+
code?: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
interface NiumCancelEvent {
|
|
70
|
+
type: 'cancel';
|
|
71
|
+
}
|
|
72
|
+
type NiumEvent = NiumReadyEvent | NiumResizeEvent | NiumChangeEvent | NiumSubmitEvent | NiumErrorEvent | NiumCancelEvent;
|
|
73
|
+
/** Map of event type string -> payload */
|
|
74
|
+
interface NiumEventMap {
|
|
75
|
+
ready: NiumReadyEvent;
|
|
76
|
+
resize: NiumResizeEvent;
|
|
77
|
+
change: NiumChangeEvent;
|
|
78
|
+
submit: NiumSubmitEvent;
|
|
79
|
+
error: NiumErrorEvent;
|
|
80
|
+
cancel: NiumCancelEvent;
|
|
81
|
+
}
|
|
82
|
+
/** Callback signature for a given event type */
|
|
83
|
+
type NiumEventCallback<T extends keyof NiumEventMap> = (event: NiumEventMap[T]) => void;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* NiumElement wraps a hidden iframe pointing at the web-onboard hosted forms.
|
|
87
|
+
* It translates internal postMessage events into typed callbacks and exposes
|
|
88
|
+
* a programmatic `submit()` method (Airwallex-style).
|
|
89
|
+
*/
|
|
90
|
+
declare class NiumElement {
|
|
91
|
+
private readonly iframeSrc;
|
|
92
|
+
private readonly elementType;
|
|
93
|
+
private readonly options;
|
|
94
|
+
private iframe;
|
|
95
|
+
private container;
|
|
96
|
+
private listeners;
|
|
97
|
+
private boundMessageHandler;
|
|
98
|
+
private destroyed;
|
|
99
|
+
private readonly origin;
|
|
100
|
+
private pendingSubmit;
|
|
101
|
+
constructor(iframeSrc: string, elementType: NiumElementType, options: NiumElementOptions, origin: string);
|
|
102
|
+
/** Mount the iframe into a DOM element (selector string or HTMLElement). */
|
|
103
|
+
mount(target: string | HTMLElement): void;
|
|
104
|
+
/** Remove the iframe from the DOM. The element can be re-mounted later. */
|
|
105
|
+
unmount(): void;
|
|
106
|
+
/** Permanently destroy the element. Removes from DOM and clears all listeners. */
|
|
107
|
+
destroy(): void;
|
|
108
|
+
/**
|
|
109
|
+
* Programmatically trigger form submission from the parent app.
|
|
110
|
+
*
|
|
111
|
+
* Sends a `nium:submit-request` message to the iframe. The hosted form
|
|
112
|
+
* validates and submits, then responds with `nium:submit` (success) or
|
|
113
|
+
* `nium:error` (failure).
|
|
114
|
+
*
|
|
115
|
+
* @returns Promise that resolves with the submit result or rejects with an error.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* const result = await element.submit()
|
|
120
|
+
* console.log(result.beneficiaryId)
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
submit(): Promise<NiumSubmitResult>;
|
|
124
|
+
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
125
|
+
on<K extends keyof NiumEventMap>(event: K, callback: NiumEventCallback<K>): () => void;
|
|
126
|
+
/** Remove a specific listener. */
|
|
127
|
+
off<K extends keyof NiumEventMap>(event: K, callback: NiumEventCallback<K>): void;
|
|
128
|
+
private handleMessage;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Initialize the SDK. Must be called once before `createElement`.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* import { init, createElement } from '@nium/nium-sdk'
|
|
137
|
+
*
|
|
138
|
+
* await init({
|
|
139
|
+
* env: 'sandbox',
|
|
140
|
+
* sessionCode: '<code-from-server>',
|
|
141
|
+
* })
|
|
142
|
+
*
|
|
143
|
+
* const element = await createElement('payment')
|
|
144
|
+
* element.mount('#container')
|
|
145
|
+
*
|
|
146
|
+
* // Option A: listen for submit events
|
|
147
|
+
* element.on('submit', (e) => console.log(e.result))
|
|
148
|
+
*
|
|
149
|
+
* // Option B: programmatic submit from parent
|
|
150
|
+
* const result = await element.submit()
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare const init: (options: NiumInitOptions) => Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Create an embeddable element for a specific onboarding module.
|
|
156
|
+
*
|
|
157
|
+
* @param type - The module to render: 'payment' | 'tax' | 'identity' | 'full'
|
|
158
|
+
* @param options - Optional configuration (prefill, theme, customizations)
|
|
159
|
+
* @returns A NiumElement that can be mounted, listened to, submitted, and destroyed
|
|
160
|
+
*/
|
|
161
|
+
declare const createElement: (type: NiumElementType, options?: NiumElementOptions) => Promise<NiumElement>;
|
|
162
|
+
|
|
163
|
+
export { type NiumCancelEvent, type NiumChangeEvent, NiumElement, type NiumElementOptions, type NiumElementType, type NiumEnv, type NiumErrorEvent, type NiumEvent, type NiumEventCallback, type NiumEventMap, type NiumInitOptions, type NiumPaymentPrefill, type NiumReadyEvent, type NiumResizeEvent, type NiumSubmitEvent, type NiumSubmitResult, type NiumTheme, createElement, init };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
NiumElement: () => NiumElement,
|
|
24
|
+
createElement: () => createElement,
|
|
25
|
+
init: () => init
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/element.ts
|
|
30
|
+
var NiumElement = class {
|
|
31
|
+
constructor(iframeSrc, elementType, options, origin) {
|
|
32
|
+
this.iframe = null;
|
|
33
|
+
this.container = null;
|
|
34
|
+
this.listeners = {};
|
|
35
|
+
this.boundMessageHandler = null;
|
|
36
|
+
this.destroyed = false;
|
|
37
|
+
// Pending submit() promise — resolved/rejected when iframe responds
|
|
38
|
+
this.pendingSubmit = null;
|
|
39
|
+
this.iframeSrc = iframeSrc;
|
|
40
|
+
this.elementType = elementType;
|
|
41
|
+
this.options = options;
|
|
42
|
+
this.origin = origin;
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Lifecycle
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/** Mount the iframe into a DOM element (selector string or HTMLElement). */
|
|
48
|
+
mount(target) {
|
|
49
|
+
if (this.destroyed) {
|
|
50
|
+
throw new Error("@nium/nium-sdk: Cannot mount a destroyed element");
|
|
51
|
+
}
|
|
52
|
+
const el = typeof target === "string" ? document.querySelector(target) : target;
|
|
53
|
+
if (!el) {
|
|
54
|
+
throw new Error(`@nium/nium-sdk: Mount target not found: ${target}`);
|
|
55
|
+
}
|
|
56
|
+
if (this.iframe && this.container === el) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (this.iframe) {
|
|
60
|
+
this.unmount();
|
|
61
|
+
}
|
|
62
|
+
this.container = el;
|
|
63
|
+
const minHeight = this.options.customizations?.minHeight ?? 400;
|
|
64
|
+
const iframe = document.createElement("iframe");
|
|
65
|
+
iframe.src = this.iframeSrc;
|
|
66
|
+
iframe.style.width = "100%";
|
|
67
|
+
iframe.style.border = "none";
|
|
68
|
+
iframe.style.overflow = "hidden";
|
|
69
|
+
iframe.style.minHeight = `${minHeight}px`;
|
|
70
|
+
iframe.setAttribute("allow", "clipboard-write");
|
|
71
|
+
iframe.setAttribute("title", `NIUM ${this.elementType} onboarding`);
|
|
72
|
+
this.iframe = iframe;
|
|
73
|
+
el.appendChild(iframe);
|
|
74
|
+
this.boundMessageHandler = this.handleMessage.bind(this);
|
|
75
|
+
window.addEventListener("message", this.boundMessageHandler);
|
|
76
|
+
}
|
|
77
|
+
/** Remove the iframe from the DOM. The element can be re-mounted later. */
|
|
78
|
+
unmount() {
|
|
79
|
+
if (this.iframe && this.iframe.parentNode) {
|
|
80
|
+
this.iframe.parentNode.removeChild(this.iframe);
|
|
81
|
+
}
|
|
82
|
+
this.iframe = null;
|
|
83
|
+
this.container = null;
|
|
84
|
+
if (this.boundMessageHandler) {
|
|
85
|
+
window.removeEventListener("message", this.boundMessageHandler);
|
|
86
|
+
this.boundMessageHandler = null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Permanently destroy the element. Removes from DOM and clears all listeners. */
|
|
90
|
+
destroy() {
|
|
91
|
+
this.unmount();
|
|
92
|
+
this.listeners = {};
|
|
93
|
+
this.pendingSubmit = null;
|
|
94
|
+
this.destroyed = true;
|
|
95
|
+
}
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Programmatic submit (Airwallex-style)
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
/**
|
|
100
|
+
* Programmatically trigger form submission from the parent app.
|
|
101
|
+
*
|
|
102
|
+
* Sends a `nium:submit-request` message to the iframe. The hosted form
|
|
103
|
+
* validates and submits, then responds with `nium:submit` (success) or
|
|
104
|
+
* `nium:error` (failure).
|
|
105
|
+
*
|
|
106
|
+
* @returns Promise that resolves with the submit result or rejects with an error.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* const result = await element.submit()
|
|
111
|
+
* console.log(result.beneficiaryId)
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
async submit() {
|
|
115
|
+
if (!this.iframe?.contentWindow) {
|
|
116
|
+
throw new Error("@nium/nium-sdk: Element is not mounted");
|
|
117
|
+
}
|
|
118
|
+
if (this.pendingSubmit) {
|
|
119
|
+
this.pendingSubmit.reject(new Error("Superseded by a new submit() call"));
|
|
120
|
+
}
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
this.pendingSubmit = { resolve, reject };
|
|
123
|
+
this.iframe.contentWindow.postMessage(
|
|
124
|
+
{ type: "nium:submit-request", payload: {} },
|
|
125
|
+
this.origin
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Events
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
133
|
+
on(event, callback) {
|
|
134
|
+
if (!this.listeners[event]) {
|
|
135
|
+
this.listeners[event] = /* @__PURE__ */ new Set();
|
|
136
|
+
}
|
|
137
|
+
const set = this.listeners[event];
|
|
138
|
+
set.add(callback);
|
|
139
|
+
return () => {
|
|
140
|
+
set.delete(callback);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/** Remove a specific listener. */
|
|
144
|
+
off(event, callback) {
|
|
145
|
+
const set = this.listeners[event];
|
|
146
|
+
if (set) {
|
|
147
|
+
set.delete(callback);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Internal
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
handleMessage(event) {
|
|
154
|
+
if (event.origin !== this.origin) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const data = event.data;
|
|
158
|
+
if (!data?.type?.startsWith("nium:")) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const eventName = data.type.slice(5);
|
|
162
|
+
const payload = data.payload ?? {};
|
|
163
|
+
if (eventName === "resize" && this.iframe && "height" in payload) {
|
|
164
|
+
this.iframe.style.height = `${payload.height}px`;
|
|
165
|
+
}
|
|
166
|
+
if (eventName === "submit" && this.pendingSubmit) {
|
|
167
|
+
const result = payload.result;
|
|
168
|
+
if (result) {
|
|
169
|
+
this.pendingSubmit.resolve(result);
|
|
170
|
+
}
|
|
171
|
+
this.pendingSubmit = null;
|
|
172
|
+
}
|
|
173
|
+
if (eventName === "error" && this.pendingSubmit) {
|
|
174
|
+
const err = payload.error;
|
|
175
|
+
this.pendingSubmit.reject(new Error(err?.message ?? "Submit failed"));
|
|
176
|
+
this.pendingSubmit = null;
|
|
177
|
+
}
|
|
178
|
+
if (eventName in { ready: 1, resize: 1, change: 1, submit: 1, error: 1, cancel: 1 }) {
|
|
179
|
+
const typedEvent = { type: eventName, ...payload };
|
|
180
|
+
const set = this.listeners[eventName];
|
|
181
|
+
if (set) {
|
|
182
|
+
for (const cb of set) {
|
|
183
|
+
try {
|
|
184
|
+
cb(typedEvent);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
console.error(`@nium/nium-sdk: Error in "${eventName}" listener:`, err);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// src/index.ts
|
|
195
|
+
var initConfig = null;
|
|
196
|
+
var ENV_URLS = {
|
|
197
|
+
local: "http://localhost:3000",
|
|
198
|
+
sandbox: "https://onboard.sandbox.niumops.com",
|
|
199
|
+
qa: "https://onboard-qa.nium.com",
|
|
200
|
+
production: "https://onboard.niumops.com"
|
|
201
|
+
};
|
|
202
|
+
var ELEMENT_ROUTES = {
|
|
203
|
+
payment: "/sdk/beneficiary-form",
|
|
204
|
+
tax: "/sdk/tax-form",
|
|
205
|
+
identity: "/sdk/identity-form",
|
|
206
|
+
full: "/sdk/full-form"
|
|
207
|
+
};
|
|
208
|
+
var init = async (options) => {
|
|
209
|
+
if (!options.sessionCode) {
|
|
210
|
+
throw new Error("@nium/nium-sdk: sessionCode is required");
|
|
211
|
+
}
|
|
212
|
+
if (!options.env) {
|
|
213
|
+
throw new Error("@nium/nium-sdk: env is required");
|
|
214
|
+
}
|
|
215
|
+
initConfig = options;
|
|
216
|
+
};
|
|
217
|
+
var createElement = async (type, options = {}) => {
|
|
218
|
+
if (!initConfig) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
"@nium/nium-sdk: Must call init() before createElement()"
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const rawBaseUrl = initConfig.hostedFormUrl ?? ENV_URLS[initConfig.env];
|
|
224
|
+
const baseUrl = rawBaseUrl.replace(/\/+$/, "");
|
|
225
|
+
const origin = new URL(baseUrl).origin;
|
|
226
|
+
const route = ELEMENT_ROUTES[type];
|
|
227
|
+
const url = new URL(`${baseUrl}${route}`);
|
|
228
|
+
url.searchParams.set("code", initConfig.sessionCode);
|
|
229
|
+
if (initConfig.locale) {
|
|
230
|
+
url.searchParams.set("locale", initConfig.locale);
|
|
231
|
+
}
|
|
232
|
+
if (options.prefill) {
|
|
233
|
+
url.searchParams.set(
|
|
234
|
+
"prefill",
|
|
235
|
+
btoa(JSON.stringify(options.prefill))
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (options.theme) {
|
|
239
|
+
url.searchParams.set(
|
|
240
|
+
"theme",
|
|
241
|
+
btoa(JSON.stringify(options.theme))
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
return new NiumElement(url.toString(), type, options, origin);
|
|
245
|
+
};
|
|
246
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
247
|
+
0 && (module.exports = {
|
|
248
|
+
NiumElement,
|
|
249
|
+
createElement,
|
|
250
|
+
init
|
|
251
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// src/element.ts
|
|
2
|
+
var NiumElement = class {
|
|
3
|
+
constructor(iframeSrc, elementType, options, origin) {
|
|
4
|
+
this.iframe = null;
|
|
5
|
+
this.container = null;
|
|
6
|
+
this.listeners = {};
|
|
7
|
+
this.boundMessageHandler = null;
|
|
8
|
+
this.destroyed = false;
|
|
9
|
+
// Pending submit() promise — resolved/rejected when iframe responds
|
|
10
|
+
this.pendingSubmit = null;
|
|
11
|
+
this.iframeSrc = iframeSrc;
|
|
12
|
+
this.elementType = elementType;
|
|
13
|
+
this.options = options;
|
|
14
|
+
this.origin = origin;
|
|
15
|
+
}
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Lifecycle
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/** Mount the iframe into a DOM element (selector string or HTMLElement). */
|
|
20
|
+
mount(target) {
|
|
21
|
+
if (this.destroyed) {
|
|
22
|
+
throw new Error("@nium/nium-sdk: Cannot mount a destroyed element");
|
|
23
|
+
}
|
|
24
|
+
const el = typeof target === "string" ? document.querySelector(target) : target;
|
|
25
|
+
if (!el) {
|
|
26
|
+
throw new Error(`@nium/nium-sdk: Mount target not found: ${target}`);
|
|
27
|
+
}
|
|
28
|
+
if (this.iframe && this.container === el) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (this.iframe) {
|
|
32
|
+
this.unmount();
|
|
33
|
+
}
|
|
34
|
+
this.container = el;
|
|
35
|
+
const minHeight = this.options.customizations?.minHeight ?? 400;
|
|
36
|
+
const iframe = document.createElement("iframe");
|
|
37
|
+
iframe.src = this.iframeSrc;
|
|
38
|
+
iframe.style.width = "100%";
|
|
39
|
+
iframe.style.border = "none";
|
|
40
|
+
iframe.style.overflow = "hidden";
|
|
41
|
+
iframe.style.minHeight = `${minHeight}px`;
|
|
42
|
+
iframe.setAttribute("allow", "clipboard-write");
|
|
43
|
+
iframe.setAttribute("title", `NIUM ${this.elementType} onboarding`);
|
|
44
|
+
this.iframe = iframe;
|
|
45
|
+
el.appendChild(iframe);
|
|
46
|
+
this.boundMessageHandler = this.handleMessage.bind(this);
|
|
47
|
+
window.addEventListener("message", this.boundMessageHandler);
|
|
48
|
+
}
|
|
49
|
+
/** Remove the iframe from the DOM. The element can be re-mounted later. */
|
|
50
|
+
unmount() {
|
|
51
|
+
if (this.iframe && this.iframe.parentNode) {
|
|
52
|
+
this.iframe.parentNode.removeChild(this.iframe);
|
|
53
|
+
}
|
|
54
|
+
this.iframe = null;
|
|
55
|
+
this.container = null;
|
|
56
|
+
if (this.boundMessageHandler) {
|
|
57
|
+
window.removeEventListener("message", this.boundMessageHandler);
|
|
58
|
+
this.boundMessageHandler = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Permanently destroy the element. Removes from DOM and clears all listeners. */
|
|
62
|
+
destroy() {
|
|
63
|
+
this.unmount();
|
|
64
|
+
this.listeners = {};
|
|
65
|
+
this.pendingSubmit = null;
|
|
66
|
+
this.destroyed = true;
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Programmatic submit (Airwallex-style)
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* Programmatically trigger form submission from the parent app.
|
|
73
|
+
*
|
|
74
|
+
* Sends a `nium:submit-request` message to the iframe. The hosted form
|
|
75
|
+
* validates and submits, then responds with `nium:submit` (success) or
|
|
76
|
+
* `nium:error` (failure).
|
|
77
|
+
*
|
|
78
|
+
* @returns Promise that resolves with the submit result or rejects with an error.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const result = await element.submit()
|
|
83
|
+
* console.log(result.beneficiaryId)
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
async submit() {
|
|
87
|
+
if (!this.iframe?.contentWindow) {
|
|
88
|
+
throw new Error("@nium/nium-sdk: Element is not mounted");
|
|
89
|
+
}
|
|
90
|
+
if (this.pendingSubmit) {
|
|
91
|
+
this.pendingSubmit.reject(new Error("Superseded by a new submit() call"));
|
|
92
|
+
}
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
this.pendingSubmit = { resolve, reject };
|
|
95
|
+
this.iframe.contentWindow.postMessage(
|
|
96
|
+
{ type: "nium:submit-request", payload: {} },
|
|
97
|
+
this.origin
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Events
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
105
|
+
on(event, callback) {
|
|
106
|
+
if (!this.listeners[event]) {
|
|
107
|
+
this.listeners[event] = /* @__PURE__ */ new Set();
|
|
108
|
+
}
|
|
109
|
+
const set = this.listeners[event];
|
|
110
|
+
set.add(callback);
|
|
111
|
+
return () => {
|
|
112
|
+
set.delete(callback);
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** Remove a specific listener. */
|
|
116
|
+
off(event, callback) {
|
|
117
|
+
const set = this.listeners[event];
|
|
118
|
+
if (set) {
|
|
119
|
+
set.delete(callback);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Internal
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
handleMessage(event) {
|
|
126
|
+
if (event.origin !== this.origin) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const data = event.data;
|
|
130
|
+
if (!data?.type?.startsWith("nium:")) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const eventName = data.type.slice(5);
|
|
134
|
+
const payload = data.payload ?? {};
|
|
135
|
+
if (eventName === "resize" && this.iframe && "height" in payload) {
|
|
136
|
+
this.iframe.style.height = `${payload.height}px`;
|
|
137
|
+
}
|
|
138
|
+
if (eventName === "submit" && this.pendingSubmit) {
|
|
139
|
+
const result = payload.result;
|
|
140
|
+
if (result) {
|
|
141
|
+
this.pendingSubmit.resolve(result);
|
|
142
|
+
}
|
|
143
|
+
this.pendingSubmit = null;
|
|
144
|
+
}
|
|
145
|
+
if (eventName === "error" && this.pendingSubmit) {
|
|
146
|
+
const err = payload.error;
|
|
147
|
+
this.pendingSubmit.reject(new Error(err?.message ?? "Submit failed"));
|
|
148
|
+
this.pendingSubmit = null;
|
|
149
|
+
}
|
|
150
|
+
if (eventName in { ready: 1, resize: 1, change: 1, submit: 1, error: 1, cancel: 1 }) {
|
|
151
|
+
const typedEvent = { type: eventName, ...payload };
|
|
152
|
+
const set = this.listeners[eventName];
|
|
153
|
+
if (set) {
|
|
154
|
+
for (const cb of set) {
|
|
155
|
+
try {
|
|
156
|
+
cb(typedEvent);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.error(`@nium/nium-sdk: Error in "${eventName}" listener:`, err);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/index.ts
|
|
167
|
+
var initConfig = null;
|
|
168
|
+
var ENV_URLS = {
|
|
169
|
+
local: "http://localhost:3000",
|
|
170
|
+
sandbox: "https://onboard.sandbox.niumops.com",
|
|
171
|
+
qa: "https://onboard-qa.nium.com",
|
|
172
|
+
production: "https://onboard.niumops.com"
|
|
173
|
+
};
|
|
174
|
+
var ELEMENT_ROUTES = {
|
|
175
|
+
payment: "/sdk/beneficiary-form",
|
|
176
|
+
tax: "/sdk/tax-form",
|
|
177
|
+
identity: "/sdk/identity-form",
|
|
178
|
+
full: "/sdk/full-form"
|
|
179
|
+
};
|
|
180
|
+
var init = async (options) => {
|
|
181
|
+
if (!options.sessionCode) {
|
|
182
|
+
throw new Error("@nium/nium-sdk: sessionCode is required");
|
|
183
|
+
}
|
|
184
|
+
if (!options.env) {
|
|
185
|
+
throw new Error("@nium/nium-sdk: env is required");
|
|
186
|
+
}
|
|
187
|
+
initConfig = options;
|
|
188
|
+
};
|
|
189
|
+
var createElement = async (type, options = {}) => {
|
|
190
|
+
if (!initConfig) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
"@nium/nium-sdk: Must call init() before createElement()"
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
const rawBaseUrl = initConfig.hostedFormUrl ?? ENV_URLS[initConfig.env];
|
|
196
|
+
const baseUrl = rawBaseUrl.replace(/\/+$/, "");
|
|
197
|
+
const origin = new URL(baseUrl).origin;
|
|
198
|
+
const route = ELEMENT_ROUTES[type];
|
|
199
|
+
const url = new URL(`${baseUrl}${route}`);
|
|
200
|
+
url.searchParams.set("code", initConfig.sessionCode);
|
|
201
|
+
if (initConfig.locale) {
|
|
202
|
+
url.searchParams.set("locale", initConfig.locale);
|
|
203
|
+
}
|
|
204
|
+
if (options.prefill) {
|
|
205
|
+
url.searchParams.set(
|
|
206
|
+
"prefill",
|
|
207
|
+
btoa(JSON.stringify(options.prefill))
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
if (options.theme) {
|
|
211
|
+
url.searchParams.set(
|
|
212
|
+
"theme",
|
|
213
|
+
btoa(JSON.stringify(options.theme))
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
return new NiumElement(url.toString(), type, options, origin);
|
|
217
|
+
};
|
|
218
|
+
export {
|
|
219
|
+
NiumElement,
|
|
220
|
+
createElement,
|
|
221
|
+
init
|
|
222
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nium/nium-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "NIUM Onboarding SDK - Embed hosted onboarding forms with a clean JavaScript API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
13
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
14
|
+
"type": "tsc --noEmit",
|
|
15
|
+
"lint": "eslint src/"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"tsup": "^8.0.0",
|
|
19
|
+
"typescript": "^5.0.0"
|
|
20
|
+
},
|
|
21
|
+
"license": "UNLICENSED",
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
}
|
|
25
|
+
}
|