mirror of
https://github.com/valitydev/cross-origin-communicator.git
synced 2024-11-06 02:25:18 +00:00
FE-626: Added base logic. (#1)
This commit is contained in:
parent
d6946aa383
commit
1911820ad5
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.idea/
|
||||
/dist/
|
||||
/node_modules/
|
2
.npmignore
Normal file
2
.npmignore
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
/npm-debug.log
|
111
package-lock.json
generated
Normal file
111
package-lock.json
generated
Normal file
@ -0,0 +1,111 @@
|
||||
{
|
||||
"name": "cross-origin-communicator",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "3.0.4",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "1.4.0",
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.11"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
|
||||
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "7.1.2"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
|
||||
"integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
|
||||
"dev": true
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
24
package.json
Normal file
24
package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "cross-origin-communicator",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"prepublish": "npm run build",
|
||||
"prebuild": "npm run clean",
|
||||
"build": "tsc",
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"private": true,
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rbkmoney/cross-origin-communicator.git"
|
||||
},
|
||||
"author": "Ildar Galeev",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"rimraf": "^2.6.2",
|
||||
"typescript": "^2.9.2"
|
||||
}
|
||||
}
|
4
src/constants.ts
Normal file
4
src/constants.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum Constants {
|
||||
initializerHandName = 'communicator-initializer-hand',
|
||||
listenerHandName = 'communicator-listener-hand'
|
||||
}
|
3
src/index.ts
Normal file
3
src/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './listen';
|
||||
export * from './initialize';
|
||||
export * from './transport';
|
34
src/initialize.ts
Normal file
34
src/initialize.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { RealTransport } from './real-transport';
|
||||
import { Transport } from './transport';
|
||||
import { log } from './log';
|
||||
import { Constants } from './constants';
|
||||
|
||||
export const initialize = (target: Window, origin: string, transportName: string, isLog: boolean = false): Promise<Transport> => {
|
||||
let interval: number;
|
||||
return new Promise((resolve, reject) => {
|
||||
const reply = (e: any) => {
|
||||
if (e.data === Constants.listenerHandName) {
|
||||
if (isLog) {
|
||||
log('initializer receive listener hand');
|
||||
}
|
||||
window.clearInterval(interval);
|
||||
window.removeEventListener('message', reply, false);
|
||||
return resolve(new RealTransport(target, origin, transportName));
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', reply, false);
|
||||
let attempt = 0;
|
||||
const doSend = () => {
|
||||
attempt++;
|
||||
target.postMessage(Constants.initializerHandName, origin);
|
||||
if (isLog) {
|
||||
log('initializer send handshake attempt');
|
||||
}
|
||||
if (attempt === 10) {
|
||||
clearInterval(interval);
|
||||
return reject('communicator handshake failed');
|
||||
}
|
||||
};
|
||||
interval = window.setInterval(doSend, 500);
|
||||
});
|
||||
};
|
40
src/listen.ts
Normal file
40
src/listen.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { RealTransport } from './real-transport';
|
||||
import { Transport } from './transport';
|
||||
import { log } from './log';
|
||||
import { Constants } from './constants';
|
||||
|
||||
const timeout = (ms: number): Promise<Transport> =>
|
||||
new Promise((resolve, reject) => {
|
||||
const id = setTimeout(() => {
|
||||
clearTimeout(id);
|
||||
reject('listener handshake timed out in ' + ms + 'ms.');
|
||||
}, ms);
|
||||
});
|
||||
|
||||
const handleHandshake = (transportName: string, isLog: boolean = false): Promise<Transport> =>
|
||||
new Promise((resolve) => {
|
||||
const shake = (e: MessageEvent) => {
|
||||
if (e && e.data === Constants.initializerHandName) {
|
||||
const target = e.source;
|
||||
target.postMessage(Constants.listenerHandName, e.origin);
|
||||
if (isLog) {
|
||||
log('listener receive initializer hand');
|
||||
}
|
||||
window.removeEventListener('message', shake, false);
|
||||
return resolve(new RealTransport(target, e.origin, transportName));
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', shake, false);
|
||||
});
|
||||
|
||||
const handleHandshakeWithTimeout = (transportName: string, ms: number, isLog: boolean = false): Promise<Transport> =>
|
||||
Promise.race<Transport>([
|
||||
timeout(ms),
|
||||
handleHandshake(transportName, isLog)
|
||||
]);
|
||||
|
||||
export const listen = (transportName: string, ms: number = null, isLog: boolean = false): Promise<Transport> => {
|
||||
return ms === null || ms === undefined
|
||||
? handleHandshake(transportName, isLog)
|
||||
: handleHandshakeWithTimeout(transportName, ms, isLog);
|
||||
};
|
3
src/log.ts
Normal file
3
src/log.ts
Normal file
@ -0,0 +1,3 @@
|
||||
const logPrefix = '[communicator]';
|
||||
|
||||
export const log = (message: string) => console.info(`${logPrefix} ${message}`);
|
50
src/real-transport.ts
Normal file
50
src/real-transport.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { Transport } from './transport';
|
||||
|
||||
const parse = (data: any): any => {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(data);
|
||||
} catch (e) {} // tslint:disable-line:no-empty
|
||||
return parsed;
|
||||
};
|
||||
|
||||
const transportListener = (transportName: string, events: any, e: MessageEvent) => {
|
||||
const parsed = parse(e.data);
|
||||
if (parsed && (parsed.name in events)) {
|
||||
if (parsed.transport === transportName) {
|
||||
events[parsed.name].call(this, parsed.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class RealTransport implements Transport {
|
||||
|
||||
private readonly events: any = {};
|
||||
private readonly transportListener: any;
|
||||
|
||||
constructor(
|
||||
private readonly target: Window,
|
||||
private readonly origin: string,
|
||||
private readonly transportName = 'default-communicator-transport'
|
||||
) {
|
||||
this.transportListener = transportListener.bind(this, this.transportName, this.events);
|
||||
window.addEventListener('message', this.transportListener, false);
|
||||
}
|
||||
|
||||
emit(eventName: string, data?: object): void {
|
||||
const serialized = JSON.stringify({
|
||||
data,
|
||||
name: eventName,
|
||||
transport: this.transportName
|
||||
});
|
||||
this.target.postMessage(serialized, this.origin);
|
||||
}
|
||||
|
||||
on(eventName: string, callback: (data: object) => void): void {
|
||||
this.events[eventName] = callback;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
window.removeEventListener('message', this.transportListener, false);
|
||||
}
|
||||
}
|
7
src/transport.ts
Normal file
7
src/transport.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface Transport {
|
||||
emit(eventName: string, data?: object): void;
|
||||
|
||||
on(eventName: string, callback: (data: object) => void): void;
|
||||
|
||||
destroy(): void;
|
||||
}
|
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": false,
|
||||
"declaration": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"lib": ["es2015", "dom"]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user