@coinflowlabs/angular 1.7.2 → 1.8.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/README.md
CHANGED
|
@@ -4,6 +4,12 @@ This library was generated with [Angular CLI](https://github.com/angular/angular
|
|
|
4
4
|
|
|
5
5
|
# Changelog
|
|
6
6
|
|
|
7
|
+
## 1.8.0
|
|
8
|
+
|
|
9
|
+
- New `CoinflowCardForm` component — simple card input with a single `tokenize()` call and full theme support
|
|
10
|
+
- Added theme options for `fontSize`and placeholder text
|
|
11
|
+
- Improved security for iframe communication
|
|
12
|
+
|
|
7
13
|
## 1.7.2
|
|
8
14
|
|
|
9
15
|
- Fixed typing for sessionKey being not present when passing the wallet object
|
|
@@ -67,8 +67,8 @@ var PaymentMethods;
|
|
|
67
67
|
PaymentMethods["applePay"] = "applePay";
|
|
68
68
|
PaymentMethods["credits"] = "credits";
|
|
69
69
|
PaymentMethods["crypto"] = "crypto";
|
|
70
|
-
PaymentMethods["instantBankTransfer"] = "instantBankTransfer";
|
|
71
70
|
PaymentMethods["wire"] = "wire";
|
|
71
|
+
PaymentMethods["cashApp"] = "cashApp";
|
|
72
72
|
})(PaymentMethods || (PaymentMethods = {}));
|
|
73
73
|
const paymentMethodLabels = {
|
|
74
74
|
[PaymentMethods.card]: 'Card',
|
|
@@ -81,8 +81,8 @@ const paymentMethodLabels = {
|
|
|
81
81
|
[PaymentMethods.applePay]: 'Apple Pay',
|
|
82
82
|
[PaymentMethods.credits]: 'Credits',
|
|
83
83
|
[PaymentMethods.crypto]: 'Crypto',
|
|
84
|
-
[PaymentMethods.instantBankTransfer]: 'Instant Bank Transfer',
|
|
85
84
|
[PaymentMethods.wire]: 'Wire Transfer',
|
|
85
|
+
[PaymentMethods.cashApp]: 'CashApp',
|
|
86
86
|
};
|
|
87
87
|
var CardType;
|
|
88
88
|
(function (CardType) {
|
|
@@ -483,8 +483,20 @@ function getCurrencyDecimals(currency) {
|
|
|
483
483
|
function isTypedCurrencyCents(cents, currency) {
|
|
484
484
|
return cents.currency === currency;
|
|
485
485
|
}
|
|
486
|
+
function invertRate(ex) {
|
|
487
|
+
return {
|
|
488
|
+
base: ex.to,
|
|
489
|
+
rate: 1 / ex.rate,
|
|
490
|
+
to: ex.base,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
486
493
|
|
|
487
|
-
function
|
|
494
|
+
function getNSureDeviceId() {
|
|
495
|
+
if (typeof window !== 'undefined') {
|
|
496
|
+
const urlDeviceId = new URLSearchParams(window.location.search).get('deviceId');
|
|
497
|
+
if (urlDeviceId)
|
|
498
|
+
return urlDeviceId;
|
|
499
|
+
}
|
|
488
500
|
if (nsureSDK)
|
|
489
501
|
return nsureSDK.getDeviceId();
|
|
490
502
|
return null;
|
|
@@ -604,7 +616,7 @@ class CoinflowUtils {
|
|
|
604
616
|
url.searchParams.append('deviceId', deviceId);
|
|
605
617
|
}
|
|
606
618
|
else {
|
|
607
|
-
const deviceId =
|
|
619
|
+
const deviceId = getNSureDeviceId();
|
|
608
620
|
if (deviceId)
|
|
609
621
|
url.searchParams.append('deviceId', deviceId);
|
|
610
622
|
}
|
|
@@ -719,6 +731,12 @@ class CoinflowUtils {
|
|
|
719
731
|
const { transaction } = props;
|
|
720
732
|
return LZString.compressToEncodedURIComponent(JSON.stringify(transaction));
|
|
721
733
|
},
|
|
734
|
+
tempo: () => {
|
|
735
|
+
if (!('transaction' in props))
|
|
736
|
+
return undefined;
|
|
737
|
+
const { transaction } = props;
|
|
738
|
+
return LZString.compressToEncodedURIComponent(JSON.stringify(transaction));
|
|
739
|
+
},
|
|
722
740
|
user: () => {
|
|
723
741
|
return undefined;
|
|
724
742
|
},
|
|
@@ -740,6 +758,8 @@ class CoinflowUtils {
|
|
|
740
758
|
return args.stellar;
|
|
741
759
|
case 'monad':
|
|
742
760
|
return args.monad;
|
|
761
|
+
case 'tempo':
|
|
762
|
+
return args.tempo;
|
|
743
763
|
case 'user':
|
|
744
764
|
return args.user;
|
|
745
765
|
default:
|
|
@@ -766,6 +786,18 @@ function getCustomerName(info) {
|
|
|
766
786
|
};
|
|
767
787
|
return undefined;
|
|
768
788
|
}
|
|
789
|
+
function recordFrontendError({ event, error, env, merchantId, }) {
|
|
790
|
+
const isError = error instanceof Error;
|
|
791
|
+
const message = isError ? error.message : error;
|
|
792
|
+
const stackTrace = isError ? error.stack : '';
|
|
793
|
+
fetch(`${CoinflowUtils.getCoinflowApiUrl(env)}/api/telemetry/frontend-error`, {
|
|
794
|
+
method: 'POST',
|
|
795
|
+
body: JSON.stringify({ message, stackTrace, merchantId, event }),
|
|
796
|
+
headers: {
|
|
797
|
+
'Content-Type': 'application/json',
|
|
798
|
+
},
|
|
799
|
+
}).catch(() => { });
|
|
800
|
+
}
|
|
769
801
|
|
|
770
802
|
var IFrameMessageMethods;
|
|
771
803
|
(function (IFrameMessageMethods) {
|
|
@@ -777,6 +809,7 @@ var IFrameMessageMethods;
|
|
|
777
809
|
IFrameMessageMethods["AuthDeclined"] = "authDeclined";
|
|
778
810
|
IFrameMessageMethods["Loaded"] = "loaded";
|
|
779
811
|
IFrameMessageMethods["AccountLinked"] = "accountLinked";
|
|
812
|
+
IFrameMessageMethods["Redirect"] = "redirect";
|
|
780
813
|
})(IFrameMessageMethods || (IFrameMessageMethods = {}));
|
|
781
814
|
function getWalletPubkey(input) {
|
|
782
815
|
let wallet;
|
|
@@ -840,6 +873,9 @@ function handleIFrameMessage(rawMessage, handlers, handleHeightChangeId) {
|
|
|
840
873
|
return;
|
|
841
874
|
case IFrameMessageMethods.AccountLinked:
|
|
842
875
|
return;
|
|
876
|
+
case IFrameMessageMethods.Redirect:
|
|
877
|
+
window.open(data, '_blank');
|
|
878
|
+
return;
|
|
843
879
|
}
|
|
844
880
|
console.warn(`Didn't expect to get here, handleIFrameMessage method:${method} is not one of ${Object.values(IFrameMessageMethods)}`);
|
|
845
881
|
}
|
|
@@ -909,6 +945,11 @@ function getHandlers(props) {
|
|
|
909
945
|
onSuccess: props.onSuccess,
|
|
910
946
|
onAuthDeclined: props.onAuthDeclined,
|
|
911
947
|
}),
|
|
948
|
+
tempo: () => getEvmWalletHandlers({
|
|
949
|
+
wallet: wallet,
|
|
950
|
+
onSuccess: props.onSuccess,
|
|
951
|
+
onAuthDeclined: props.onAuthDeclined,
|
|
952
|
+
}),
|
|
912
953
|
user: () => getSessionKeyHandlers(props),
|
|
913
954
|
})();
|
|
914
955
|
}
|
|
@@ -1228,6 +1269,96 @@ var nftCartItem;
|
|
|
1228
1269
|
})(productType = nftCartItem.productType || (nftCartItem.productType = {}));
|
|
1229
1270
|
})(nftCartItem || (nftCartItem = {}));
|
|
1230
1271
|
|
|
1272
|
+
let cachedSessionId = null;
|
|
1273
|
+
async function initProtectionSession({ merchantId, env, }) {
|
|
1274
|
+
if (cachedSessionId)
|
|
1275
|
+
return;
|
|
1276
|
+
if (typeof window !== 'undefined') {
|
|
1277
|
+
try {
|
|
1278
|
+
const result = await window.Verisoul?.session();
|
|
1279
|
+
if (result?.session_id)
|
|
1280
|
+
cachedSessionId = result.session_id;
|
|
1281
|
+
}
|
|
1282
|
+
catch (e) {
|
|
1283
|
+
console.error(e);
|
|
1284
|
+
recordFrontendError({ event: 'ProtectionInit', env, merchantId, error: e });
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
function getProtectionSessionId() {
|
|
1289
|
+
return cachedSessionId;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
const DEVICE_ID_HEADER = 'x-device-id';
|
|
1293
|
+
const SESSION_ID_HEADER = 'x-session-id';
|
|
1294
|
+
function getCoinflowProtectionHeaders() {
|
|
1295
|
+
return {
|
|
1296
|
+
[DEVICE_ID_HEADER]: getNSureDeviceId(),
|
|
1297
|
+
[SESSION_ID_HEADER]: getProtectionSessionId(),
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const NSURE_APP_IDS = {
|
|
1302
|
+
prod: '9JBW2RHC7JNJN8ZQ',
|
|
1303
|
+
sandbox: 'SANDBOX_CTCE4XK53ZW0R7V1',
|
|
1304
|
+
};
|
|
1305
|
+
const PROTECTION_SESSION_PROJECT_IDS = {
|
|
1306
|
+
prod: '315da543-c486-435a-aa21-53844c469822',
|
|
1307
|
+
sandbox: 'e9f629c4-80ee-4c6d-967e-62af47d8679e',
|
|
1308
|
+
};
|
|
1309
|
+
async function initCoinflowProtection({ coinflowEnv, merchantId, }) {
|
|
1310
|
+
const env = coinflowEnv === 'prod' ? 'prod' : 'sandbox';
|
|
1311
|
+
const hasUrlDeviceId = typeof window !== 'undefined' &&
|
|
1312
|
+
new URLSearchParams(window.location.search).has('deviceId');
|
|
1313
|
+
if (!hasUrlDeviceId) {
|
|
1314
|
+
const partnerId = await new CoinflowUtils(coinflowEnv).getNSurePartnerId(merchantId);
|
|
1315
|
+
if (partnerId) {
|
|
1316
|
+
const appId = NSURE_APP_IDS[env] ?? NSURE_APP_IDS['sandbox'];
|
|
1317
|
+
try {
|
|
1318
|
+
nsureSDK.init(appId, partnerId);
|
|
1319
|
+
}
|
|
1320
|
+
catch (e) {
|
|
1321
|
+
console.error('Failed to initialize nSure SDK:', e);
|
|
1322
|
+
recordFrontendError({
|
|
1323
|
+
event: 'NSureInit',
|
|
1324
|
+
error: e,
|
|
1325
|
+
env: coinflowEnv,
|
|
1326
|
+
merchantId,
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
// Initialize protection session (Verisoul)
|
|
1332
|
+
if (typeof document !== 'undefined') {
|
|
1333
|
+
const existing = document.querySelector('script[verisoul-project-id]');
|
|
1334
|
+
if (!existing) {
|
|
1335
|
+
const projectId = PROTECTION_SESSION_PROJECT_IDS[env] ??
|
|
1336
|
+
PROTECTION_SESSION_PROJECT_IDS['sandbox'];
|
|
1337
|
+
if (projectId) {
|
|
1338
|
+
await new Promise(resolve => {
|
|
1339
|
+
const script = document.createElement('script');
|
|
1340
|
+
script.src = `https://js.v.coinflow.sh/${env}/bundle.js`;
|
|
1341
|
+
script.async = true;
|
|
1342
|
+
script.setAttribute('verisoul-project-id', projectId);
|
|
1343
|
+
script.onload = () => {
|
|
1344
|
+
initProtectionSession({ merchantId, env: coinflowEnv }).then(resolve);
|
|
1345
|
+
};
|
|
1346
|
+
script.onerror = e => {
|
|
1347
|
+
recordFrontendError({
|
|
1348
|
+
event: 'ProtectionError',
|
|
1349
|
+
error: e,
|
|
1350
|
+
env: coinflowEnv,
|
|
1351
|
+
merchantId,
|
|
1352
|
+
});
|
|
1353
|
+
resolve();
|
|
1354
|
+
};
|
|
1355
|
+
document.head.appendChild(script);
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1231
1362
|
class CoinflowIFrameComponent {
|
|
1232
1363
|
constructor(sanitizer) {
|
|
1233
1364
|
this.sanitizer = sanitizer;
|
|
@@ -1670,6 +1801,125 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
1670
1801
|
type: Input
|
|
1671
1802
|
}] } });
|
|
1672
1803
|
|
|
1804
|
+
class CoinflowCardForm {
|
|
1805
|
+
constructor() {
|
|
1806
|
+
this.url = '';
|
|
1807
|
+
this.loaded = false;
|
|
1808
|
+
}
|
|
1809
|
+
ngOnInit() {
|
|
1810
|
+
this.buildUrl();
|
|
1811
|
+
this.messageHandler = (event) => this.handleMessage(event.data, event.origin);
|
|
1812
|
+
window.addEventListener('message', this.messageHandler);
|
|
1813
|
+
}
|
|
1814
|
+
ngOnDestroy() {
|
|
1815
|
+
if (this.messageHandler) {
|
|
1816
|
+
window.removeEventListener('message', this.messageHandler);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
buildUrl() {
|
|
1820
|
+
const baseUrl = CoinflowUtils.getCoinflowBaseUrl(this.args.env);
|
|
1821
|
+
const iframeUrl = new URL(`/form/v2/${this.args.variant}`, baseUrl);
|
|
1822
|
+
iframeUrl.searchParams.append('merchantId', this.args.merchantId);
|
|
1823
|
+
if (this.args.theme) {
|
|
1824
|
+
iframeUrl.searchParams.append('theme', LZString.compressToEncodedURIComponent(JSON.stringify(this.args.theme)));
|
|
1825
|
+
}
|
|
1826
|
+
if (this.args.token) {
|
|
1827
|
+
iframeUrl.searchParams.append('token', this.args.token);
|
|
1828
|
+
}
|
|
1829
|
+
this.url = iframeUrl.toString();
|
|
1830
|
+
}
|
|
1831
|
+
handleMessage(data, origin) {
|
|
1832
|
+
const expectedOrigin = new URL(CoinflowUtils.getCoinflowBaseUrl(this.args.env)).origin;
|
|
1833
|
+
if (origin !== expectedOrigin)
|
|
1834
|
+
return;
|
|
1835
|
+
try {
|
|
1836
|
+
const parsed = JSON.parse(data);
|
|
1837
|
+
if (parsed.method === IFrameMessageMethods.Loaded) {
|
|
1838
|
+
this.loaded = true;
|
|
1839
|
+
this.args.onLoad?.();
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
catch {
|
|
1843
|
+
// not JSON
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
tokenize() {
|
|
1847
|
+
return new Promise((resolve, reject) => {
|
|
1848
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
1849
|
+
if (!iframe?.contentWindow) {
|
|
1850
|
+
reject(new Error('Card form iframe not loaded'));
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
const handler = (event) => {
|
|
1854
|
+
const { data, origin } = event;
|
|
1855
|
+
const expectedOrigin = new URL(CoinflowUtils.getCoinflowBaseUrl(this.args.env)).origin;
|
|
1856
|
+
if (origin !== expectedOrigin)
|
|
1857
|
+
return;
|
|
1858
|
+
try {
|
|
1859
|
+
const parsed = JSON.parse(data);
|
|
1860
|
+
if (parsed.method !== 'tokenize')
|
|
1861
|
+
return;
|
|
1862
|
+
window.removeEventListener('message', handler);
|
|
1863
|
+
if (typeof parsed.data === 'string' &&
|
|
1864
|
+
parsed.data.startsWith('ERROR')) {
|
|
1865
|
+
reject(new Error(parsed.data.replace('ERROR ', '')));
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
const responseData = typeof parsed.data === 'string'
|
|
1869
|
+
? JSON.parse(parsed.data)
|
|
1870
|
+
: parsed.data;
|
|
1871
|
+
resolve(responseData);
|
|
1872
|
+
}
|
|
1873
|
+
catch {
|
|
1874
|
+
// not relevant
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1877
|
+
window.addEventListener('message', handler);
|
|
1878
|
+
iframe.contentWindow.postMessage('tokenize', '*');
|
|
1879
|
+
});
|
|
1880
|
+
}
|
|
1881
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CoinflowCardForm, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1882
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: CoinflowCardForm, isStandalone: true, selector: "lib-coinflow-card-form", inputs: { args: "args" }, viewQueries: [{ propertyName: "iframeRef", first: true, predicate: ["cardFormIframe"], descendants: true, static: true }], ngImport: i0, template: `<iframe
|
|
1883
|
+
#cardFormIframe
|
|
1884
|
+
[src]="url"
|
|
1885
|
+
title="Card Form"
|
|
1886
|
+
frameBorder="0"
|
|
1887
|
+
allow="payment"
|
|
1888
|
+
[style.width]="'100%'"
|
|
1889
|
+
[style.height]="'60px'"
|
|
1890
|
+
[style.border]="'none'"
|
|
1891
|
+
[style.opacity]="loaded ? 1 : 0"
|
|
1892
|
+
[style.transition]="'opacity 300ms linear'"
|
|
1893
|
+
></iframe>`, isInline: true }); }
|
|
1894
|
+
}
|
|
1895
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CoinflowCardForm, decorators: [{
|
|
1896
|
+
type: Component,
|
|
1897
|
+
args: [{
|
|
1898
|
+
selector: 'lib-coinflow-card-form',
|
|
1899
|
+
standalone: true,
|
|
1900
|
+
imports: [],
|
|
1901
|
+
template: `<iframe
|
|
1902
|
+
#cardFormIframe
|
|
1903
|
+
[src]="url"
|
|
1904
|
+
title="Card Form"
|
|
1905
|
+
frameBorder="0"
|
|
1906
|
+
allow="payment"
|
|
1907
|
+
[style.width]="'100%'"
|
|
1908
|
+
[style.height]="'60px'"
|
|
1909
|
+
[style.border]="'none'"
|
|
1910
|
+
[style.opacity]="loaded ? 1 : 0"
|
|
1911
|
+
[style.transition]="'opacity 300ms linear'"
|
|
1912
|
+
></iframe>`,
|
|
1913
|
+
}]
|
|
1914
|
+
}], propDecorators: { args: [{
|
|
1915
|
+
type: Input
|
|
1916
|
+
}], iframeRef: [{
|
|
1917
|
+
type: ViewChild,
|
|
1918
|
+
args: ['cardFormIframe', { static: true }]
|
|
1919
|
+
}] } });
|
|
1920
|
+
/** @deprecated Use CoinflowCardForm instead */
|
|
1921
|
+
const CoinflowCardFormV2 = CoinflowCardForm;
|
|
1922
|
+
|
|
1673
1923
|
class CoinflowMobileWalletButtonComponent {
|
|
1674
1924
|
constructor() {
|
|
1675
1925
|
this.opacity = 0.8;
|
|
@@ -2036,5 +2286,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
2036
2286
|
* Generated bundle index. Do not edit.
|
|
2037
2287
|
*/
|
|
2038
2288
|
|
|
2039
|
-
export { BankingCurrencies, CARD_TYPE_MAPPING, CardType, ChargebackProtectionAccountType, CoinflowApplePayButtonComponent, CoinflowCardNumberInput, CoinflowCardNumberOnlyInput, CoinflowCvvInputComponent, CoinflowCvvOnlyInputComponent, CoinflowGooglePayButtonComponent, CoinflowIFrameComponent, CoinflowPurchaseComponent, CoinflowPurchaseHistoryComponent, CoinflowPurchaseProtectionComponent, CoinflowUtils, CoinflowWithdrawComponent, CoinflowWithdrawHistoryComponent, Currency, CurrencyToISO4217, EventBus, IFrameMessageMethods, MerchantStyle, PaymentMethods, RN_REDIRECT_MESSAGE_NAME, SettlementType, ThreeDsChallengePreference, TokenExCardNumberIframeId, TokenExCvvContainerID, WithdrawCategory, WithdrawCurrencies, WithdrawSpeed, doInitializeCvvOnlyTokenExIframe, doInitializeTokenExCardOnlyIframe, doInitializeTokenExIframe, getCurrencyDecimals, getCustomerName, getHandlers, getIframeConfig, getWalletPubkey, handleIFrameMessage, isBankingCurrency, isTypedCurrencyCents, isWithdrawCurrency, isZeroAuthSavedPaymentMethods, isZeroAuthVerifyCard, nftCartItem, paymentMethodLabels, setTokenExScriptTag };
|
|
2289
|
+
export { BankingCurrencies, CARD_TYPE_MAPPING, CardType, ChargebackProtectionAccountType, CoinflowApplePayButtonComponent, CoinflowCardForm, CoinflowCardFormV2, CoinflowCardNumberInput, CoinflowCardNumberOnlyInput, CoinflowCvvInputComponent, CoinflowCvvOnlyInputComponent, CoinflowGooglePayButtonComponent, CoinflowIFrameComponent, CoinflowPurchaseComponent, CoinflowPurchaseHistoryComponent, CoinflowPurchaseProtectionComponent, CoinflowUtils, CoinflowWithdrawComponent, CoinflowWithdrawHistoryComponent, Currency, CurrencyToISO4217, DEVICE_ID_HEADER, EventBus, IFrameMessageMethods, MerchantStyle, PaymentMethods, RN_REDIRECT_MESSAGE_NAME, SESSION_ID_HEADER, SettlementType, ThreeDsChallengePreference, TokenExCardNumberIframeId, TokenExCvvContainerID, WithdrawCategory, WithdrawCurrencies, WithdrawSpeed, doInitializeCvvOnlyTokenExIframe, doInitializeTokenExCardOnlyIframe, doInitializeTokenExIframe, getCoinflowProtectionHeaders, getCurrencyDecimals, getCustomerName, getHandlers, getIframeConfig, getWalletPubkey, handleIFrameMessage, initCoinflowProtection, invertRate, isBankingCurrency, isTypedCurrencyCents, isWithdrawCurrency, isZeroAuthSavedPaymentMethods, isZeroAuthVerifyCard, nftCartItem, paymentMethodLabels, recordFrontendError, setTokenExScriptTag };
|
|
2040
2290
|
//# sourceMappingURL=coinflowlabs-angular.mjs.map
|