@ozura/elements 1.2.0-next.26 → 1.2.0-next.28
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/frame/element-frame.js +35 -1
- package/dist/frame/element-frame.js.map +1 -1
- package/dist/frame/tokenizer-frame.js +111 -50
- package/dist/frame/tokenizer-frame.js.map +1 -1
- package/dist/oz-elements.esm.js +101 -68
- package/dist/oz-elements.esm.js.map +1 -1
- package/dist/oz-elements.umd.js +101 -68
- package/dist/oz-elements.umd.js.map +1 -1
- package/dist/react/frame/elementFrame.d.ts +7 -0
- package/dist/react/frame/tokenizerFrame.d.ts +4 -1
- package/dist/react/index.cjs.js +101 -68
- package/dist/react/index.cjs.js.map +1 -1
- package/dist/react/index.esm.js +101 -68
- package/dist/react/index.esm.js.map +1 -1
- package/dist/react/sdk/OzElement.d.ts +3 -1
- package/dist/react/sdk/OzVault.d.ts +3 -9
- package/dist/react/server/index.d.ts +11 -0
- package/dist/react/types/index.d.ts +29 -10
- package/dist/server/frame/elementFrame.d.ts +7 -0
- package/dist/server/frame/tokenizerFrame.d.ts +4 -1
- package/dist/server/index.cjs.js +14 -11
- package/dist/server/index.cjs.js.map +1 -1
- package/dist/server/index.esm.js +14 -11
- package/dist/server/index.esm.js.map +1 -1
- package/dist/server/sdk/OzElement.d.ts +3 -1
- package/dist/server/sdk/OzVault.d.ts +3 -9
- package/dist/server/server/index.d.ts +11 -0
- package/dist/server/types/index.d.ts +29 -10
- package/dist/types/frame/elementFrame.d.ts +7 -0
- package/dist/types/frame/tokenizerFrame.d.ts +4 -1
- package/dist/types/sdk/OzElement.d.ts +3 -1
- package/dist/types/sdk/OzVault.d.ts +3 -9
- package/dist/types/server/index.d.ts +11 -0
- package/dist/types/types/index.d.ts +29 -10
- package/package.json +1 -1
package/dist/react/index.cjs.js
CHANGED
|
@@ -326,7 +326,7 @@ function sanitizeOptions(options) {
|
|
|
326
326
|
* it never holds raw card data — all sensitive values live in the iframe.
|
|
327
327
|
*/
|
|
328
328
|
class OzElement {
|
|
329
|
-
constructor(elementType, options, vaultId, frameBaseUrl, fonts = [], appearanceStyle) {
|
|
329
|
+
constructor(elementType, options, vaultId, frameBaseUrl, fonts = [], appearanceStyle, onDestroy) {
|
|
330
330
|
this.iframe = null;
|
|
331
331
|
this._frameWindow = null;
|
|
332
332
|
this._ready = false;
|
|
@@ -342,6 +342,7 @@ class OzElement {
|
|
|
342
342
|
this.fonts = fonts;
|
|
343
343
|
this.appearanceStyle = appearanceStyle;
|
|
344
344
|
this.frameId = `oz-${elementType}-${uuid()}`;
|
|
345
|
+
this._onDestroy = onDestroy;
|
|
345
346
|
}
|
|
346
347
|
/** The element type this proxy represents. */
|
|
347
348
|
get type() {
|
|
@@ -497,9 +498,14 @@ class OzElement {
|
|
|
497
498
|
* and prevents future use. Distinct from `unmount()` which allows re-mounting.
|
|
498
499
|
*/
|
|
499
500
|
destroy() {
|
|
501
|
+
var _a;
|
|
500
502
|
this.unmount();
|
|
501
503
|
this.handlers.clear();
|
|
502
504
|
this._destroyed = true;
|
|
505
|
+
// Notify OzVault so it can prune the stale frameId entry from its elements
|
|
506
|
+
// and completionState maps. Without this, manually calling el.destroy() leaks
|
|
507
|
+
// map entries that grow unboundedly in SPA scenarios with repeated mount/unmount.
|
|
508
|
+
(_a = this._onDestroy) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
503
509
|
}
|
|
504
510
|
// ─── Called by OzVault ───────────────────────────────────────────────────
|
|
505
511
|
/**
|
|
@@ -902,7 +908,22 @@ function createSessionFetcher(url) {
|
|
|
902
908
|
throw new OzError(`Could not reach session endpoint (${url}): ${msg}`, undefined, 'network');
|
|
903
909
|
}
|
|
904
910
|
}
|
|
905
|
-
|
|
911
|
+
// Parse JSON separately from the ok-check so that a non-JSON error body
|
|
912
|
+
// (HTML error page, WAF block, CDN 503) produces the right error code.
|
|
913
|
+
// Previously res.json() was attempted before res.ok was checked; a parse
|
|
914
|
+
// failure on a 5xx HTML body would fall through as {} and produce a
|
|
915
|
+
// misleading 'validation' code when the real cause is a server/network issue.
|
|
916
|
+
let data = {};
|
|
917
|
+
try {
|
|
918
|
+
data = await res.json();
|
|
919
|
+
}
|
|
920
|
+
catch (_a) {
|
|
921
|
+
if (!res.ok) {
|
|
922
|
+
throw new OzError(`Session endpoint returned HTTP ${res.status} with a non-JSON body`, undefined, res.status >= 500 ? 'server' : res.status === 401 || res.status === 403 ? 'auth' : 'validation');
|
|
923
|
+
}
|
|
924
|
+
// HTTP 200 but body isn't JSON — this is a misconfigured session endpoint.
|
|
925
|
+
throw new OzError('Session endpoint returned HTTP 200 but the response body is not valid JSON. Check your /api/oz-session implementation.', undefined, 'validation');
|
|
926
|
+
}
|
|
906
927
|
if (!res.ok) {
|
|
907
928
|
throw new OzError(typeof data.error === 'string' && data.error
|
|
908
929
|
? data.error
|
|
@@ -920,10 +941,19 @@ function createSessionFetcher(url) {
|
|
|
920
941
|
}
|
|
921
942
|
|
|
922
943
|
function isCardMetadata(v) {
|
|
923
|
-
|
|
944
|
+
if (!v || typeof v !== 'object')
|
|
945
|
+
return false;
|
|
946
|
+
const r = v;
|
|
947
|
+
return (typeof r.last4 === 'string' &&
|
|
948
|
+
typeof r.brand === 'string' &&
|
|
949
|
+
typeof r.expMonth === 'string' &&
|
|
950
|
+
typeof r.expYear === 'string');
|
|
924
951
|
}
|
|
925
952
|
function isBankAccountMetadata(v) {
|
|
926
|
-
|
|
953
|
+
if (!v || typeof v !== 'object')
|
|
954
|
+
return false;
|
|
955
|
+
const r = v;
|
|
956
|
+
return typeof r.last4 === 'string' && typeof r.routingNumberLast4 === 'string';
|
|
927
957
|
}
|
|
928
958
|
const DEFAULT_FRAME_BASE_URL = "https://lively-hill-097170c0f.4.azurestaticapps.net";
|
|
929
959
|
/**
|
|
@@ -933,16 +963,10 @@ const DEFAULT_FRAME_BASE_URL = "https://lively-hill-097170c0f.4.azurestaticapps.
|
|
|
933
963
|
* Use the static `OzVault.create()` factory — do not call `new OzVault()` directly.
|
|
934
964
|
*
|
|
935
965
|
* @example
|
|
966
|
+
* // Recommended — pass sessionUrl and let the SDK call your backend automatically
|
|
936
967
|
* const vault = await OzVault.create({
|
|
937
|
-
* pubKey: '
|
|
938
|
-
*
|
|
939
|
-
* // Call your backend — which calls ozura.mintWaxKey() from @ozura/elements/server
|
|
940
|
-
* const { waxKey } = await fetch('/api/mint-wax', {
|
|
941
|
-
* method: 'POST',
|
|
942
|
-
* body: JSON.stringify({ sessionId }),
|
|
943
|
-
* }).then(r => r.json());
|
|
944
|
-
* return waxKey;
|
|
945
|
-
* },
|
|
968
|
+
* pubKey: 'pk_prod_...', // or 'pk_test_...' for test mode
|
|
969
|
+
* sessionUrl: '/api/oz-session', // backend endpoint that calls ozura.createSession()
|
|
946
970
|
* });
|
|
947
971
|
* const cardNum = vault.createElement('cardNumber');
|
|
948
972
|
* cardNum.mount('#card-number');
|
|
@@ -1178,7 +1202,12 @@ class OzVault {
|
|
|
1178
1202
|
this.completionState.delete(existing.frameId);
|
|
1179
1203
|
existing.destroy();
|
|
1180
1204
|
}
|
|
1181
|
-
const el = new OzElement(type, options, this.vaultId, this.frameBaseUrl, this.fonts, this.resolvedAppearance)
|
|
1205
|
+
const el = new OzElement(type, options, this.vaultId, this.frameBaseUrl, this.fonts, this.resolvedAppearance, () => {
|
|
1206
|
+
// Prune vault-level maps when the element is manually destroyed so they
|
|
1207
|
+
// don't grow unboundedly in SPA scenarios with repeated mount/unmount cycles.
|
|
1208
|
+
this.elements.delete(el.frameId);
|
|
1209
|
+
this.completionState.delete(el.frameId);
|
|
1210
|
+
});
|
|
1182
1211
|
this.elements.set(el.frameId, el);
|
|
1183
1212
|
typeMap.set(type, el);
|
|
1184
1213
|
return el;
|
|
@@ -1835,61 +1864,65 @@ class OzVault {
|
|
|
1835
1864
|
}
|
|
1836
1865
|
pending.reject(new OzError(normalizeVaultError(raw), raw, errorCode));
|
|
1837
1866
|
}
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
const
|
|
1859
|
-
this.
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1867
|
+
else {
|
|
1868
|
+
// Also check bank resolvers — both card and bank errors use OZ_TOKEN_ERROR.
|
|
1869
|
+
// Use else-if rather than sequential checks so a UUID collision (however
|
|
1870
|
+
// improbable) can never trigger double-rejection of two unrelated resolvers.
|
|
1871
|
+
const bankPending = this.bankTokenizeResolvers.get(msg.requestId);
|
|
1872
|
+
if (bankPending) {
|
|
1873
|
+
this.bankTokenizeResolvers.delete(msg.requestId);
|
|
1874
|
+
if (bankPending.timeoutId != null)
|
|
1875
|
+
clearTimeout(bankPending.timeoutId);
|
|
1876
|
+
if (this.isRefreshableAuthError(errorCode, raw) && !bankPending.retried && this._storedFetchWaxKey) {
|
|
1877
|
+
const resetCountAtRetry = this._resetCount;
|
|
1878
|
+
this.refreshWaxKey().then(() => {
|
|
1879
|
+
if (this._destroyed) {
|
|
1880
|
+
bankPending.reject(new OzError('Vault destroyed during wax key refresh.'));
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
if (this._resetCount !== resetCountAtRetry) {
|
|
1884
|
+
bankPending.reject(new OzError('Vault was reset while tokenization was in progress.'));
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
const newRequestId = `req-${uuid()}`;
|
|
1888
|
+
this.bankTokenizeResolvers.set(newRequestId, Object.assign(Object.assign({}, bankPending), { retried: true }));
|
|
1889
|
+
try {
|
|
1890
|
+
const retryBankChannels = bankPending.readyElements.map(() => new MessageChannel());
|
|
1891
|
+
this.sendToTokenizer({
|
|
1892
|
+
type: 'OZ_BANK_TOKENIZE',
|
|
1893
|
+
requestId: newRequestId,
|
|
1894
|
+
waxKey: this.waxKey,
|
|
1895
|
+
tokenizationSessionId: this.tokenizationSessionId,
|
|
1896
|
+
pubKey: this.pubKey,
|
|
1897
|
+
firstName: bankPending.firstName,
|
|
1898
|
+
lastName: bankPending.lastName,
|
|
1899
|
+
fieldCount: bankPending.fieldCount,
|
|
1900
|
+
}, retryBankChannels.map(ch => ch.port1));
|
|
1901
|
+
bankPending.readyElements.forEach((el, i) => el.beginCollect(newRequestId, retryBankChannels[i].port2));
|
|
1902
|
+
const retryBankTimeoutId = setTimeout(() => {
|
|
1903
|
+
if (this.bankTokenizeResolvers.has(newRequestId)) {
|
|
1904
|
+
this.bankTokenizeResolvers.delete(newRequestId);
|
|
1905
|
+
this.sendToTokenizer({ type: 'OZ_TOKENIZE_CANCEL', requestId: newRequestId });
|
|
1906
|
+
bankPending.reject(new OzError('Bank tokenization timed out after wax key refresh.', undefined, 'timeout'));
|
|
1907
|
+
}
|
|
1908
|
+
}, 30000);
|
|
1909
|
+
const retryBankEntry = this.bankTokenizeResolvers.get(newRequestId);
|
|
1910
|
+
if (retryBankEntry)
|
|
1911
|
+
retryBankEntry.timeoutId = retryBankTimeoutId;
|
|
1912
|
+
}
|
|
1913
|
+
catch (setupErr) {
|
|
1914
|
+
this.bankTokenizeResolvers.delete(newRequestId);
|
|
1915
|
+
bankPending.reject(setupErr instanceof OzError ? setupErr : new OzError('Retry bank tokenization failed to start'));
|
|
1916
|
+
}
|
|
1917
|
+
}).catch((refreshErr) => {
|
|
1918
|
+
const msg = refreshErr instanceof Error ? refreshErr.message : 'Wax key refresh failed';
|
|
1919
|
+
bankPending.reject(new OzError(msg, undefined, 'auth'));
|
|
1920
|
+
});
|
|
1921
|
+
break;
|
|
1922
|
+
}
|
|
1923
|
+
bankPending.reject(new OzError(normalizeBankVaultError(raw), raw, errorCode));
|
|
1890
1924
|
}
|
|
1891
|
-
|
|
1892
|
-
}
|
|
1925
|
+
} // end else (bank path)
|
|
1893
1926
|
break;
|
|
1894
1927
|
}
|
|
1895
1928
|
case 'OZ_BANK_TOKEN_RESULT': {
|