@novasamatech/host-container 0.4.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 +133 -0
- package/dist/createComplexSubscriber.d.ts +12 -0
- package/dist/createComplexSubscriber.js +31 -0
- package/dist/createContainer.d.ts +30 -0
- package/dist/createContainer.js +188 -0
- package/dist/createIframeProvider.d.ts +8 -0
- package/dist/createIframeProvider.js +87 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# @novasamatech/host-container
|
|
2
|
+
|
|
3
|
+
A robust solution for hosting and managing decentralized applications (dapps) within the Polkadot ecosystem.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Host container provides the infrastructure layer for securely embedding and communicating with third-party dapps.
|
|
8
|
+
It handles the isolation boundary, message routing, lifecycle management, and security concerns inherent in hosting untrusted web content.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```shell
|
|
13
|
+
npm install @novasamatech/host-container --save -E
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Basic Container Setup
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { createContainer, createIframeProvider } from '@novasamatech/host-container';
|
|
20
|
+
|
|
21
|
+
const iframe = document.createElement('iframe');
|
|
22
|
+
|
|
23
|
+
const provider = createIframeProvider({
|
|
24
|
+
iframe,
|
|
25
|
+
url: 'https://dapp.example.com'
|
|
26
|
+
});
|
|
27
|
+
const container = createContainer(provider);
|
|
28
|
+
|
|
29
|
+
document.body.appendChild(iframe);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Accounts handling
|
|
33
|
+
|
|
34
|
+
To handle account requests and subscriptions you can call `handleAccounts` method:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
container.handleAccounts({
|
|
38
|
+
async get() {
|
|
39
|
+
// return injected accounts
|
|
40
|
+
return [];
|
|
41
|
+
},
|
|
42
|
+
subscribe(callback) {
|
|
43
|
+
// subscribe to account changes
|
|
44
|
+
|
|
45
|
+
callback([]);
|
|
46
|
+
return () => {
|
|
47
|
+
// unsubscribe
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
In this case you also should handle sign requests:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
container.handleSignRequest({
|
|
57
|
+
signRaw(raw) {
|
|
58
|
+
// return signed payload based on raw data
|
|
59
|
+
return {
|
|
60
|
+
id: 0,
|
|
61
|
+
signature: '0x...',
|
|
62
|
+
signedTransaction: '0x...',
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
signPayload(payload) {
|
|
66
|
+
// return signed payload based on sign payload
|
|
67
|
+
return {
|
|
68
|
+
id: 0,
|
|
69
|
+
signature: '0x...',
|
|
70
|
+
signedTransaction: '0x...',
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
createTransaction(payload) {
|
|
74
|
+
// return full signed transaction
|
|
75
|
+
return '0x0000'
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### PAPI provider support
|
|
81
|
+
|
|
82
|
+
Host container supports [PAPI](https://papi.how/) requests redirecting from product to host container.
|
|
83
|
+
It can be useful to deduplicate socket connections or light client instances between multiple dapps.
|
|
84
|
+
|
|
85
|
+
To support this feature you should add two additional handlers to container:
|
|
86
|
+
|
|
87
|
+
#### Chain support check
|
|
88
|
+
```ts
|
|
89
|
+
const polkadotGenesisHash = '0x...';
|
|
90
|
+
|
|
91
|
+
container.handleChainSupportCheck(async (genesisHash) => {
|
|
92
|
+
return genesisHash === polkadotGenesisHash;
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Provider implementation
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { getWsProvider } from 'polkadot-api/ws-provider';
|
|
100
|
+
|
|
101
|
+
const polkadotGenesisHash = '0x...';
|
|
102
|
+
const endpoint = 'wss://...';
|
|
103
|
+
const provider = getWsProvider(endpoint);
|
|
104
|
+
|
|
105
|
+
container.connectToPapiProvider(polkadotGenesisHash, provider);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Additional metadata sync
|
|
109
|
+
|
|
110
|
+
#### Receiving connection status
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
const unsubscribe = container.subscribeConnectionStatus((status) => {
|
|
114
|
+
console.log('connection status changed', status);
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Receiving dapp location change
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const unsubscribe = container.subscribeLocationChange((location) => {
|
|
122
|
+
console.log('dapp location changed:', location);
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Known pitfalls
|
|
127
|
+
|
|
128
|
+
#### CSP error on iframe loading
|
|
129
|
+
If dapp is hosted on different domain than container and uses https, you should add this meta tag to your host application html:
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
|
|
133
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MessageType, PickMessagePayload, Transport } from '@novasamatech/host-api';
|
|
2
|
+
type Subscriber<Response extends MessageType> = (callback: (response: PickMessagePayload<Response>) => void) => VoidFunction;
|
|
3
|
+
type Params<SubscribeRequest extends MessageType, Response extends MessageType> = {
|
|
4
|
+
transport: Transport;
|
|
5
|
+
subscribeRequest: SubscribeRequest;
|
|
6
|
+
unsubscribeRequest: MessageType;
|
|
7
|
+
getSubscriber(): Subscriber<Response>;
|
|
8
|
+
};
|
|
9
|
+
export declare function createComplexSubscriber<SubscribeRequest extends MessageType, Response extends MessageType>({ transport, subscribeRequest, unsubscribeRequest, getSubscriber, }: Params<SubscribeRequest, Response>): {
|
|
10
|
+
replaceSubscriber: () => void;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function createComplexSubscriber({ transport, subscribeRequest, unsubscribeRequest, getSubscriber, }) {
|
|
2
|
+
let unsubscribe = null;
|
|
3
|
+
let id = null;
|
|
4
|
+
const replaceSubscriber = () => {
|
|
5
|
+
if (unsubscribe) {
|
|
6
|
+
unsubscribe();
|
|
7
|
+
}
|
|
8
|
+
const subscriber = getSubscriber();
|
|
9
|
+
unsubscribe = subscriber(value => {
|
|
10
|
+
transport.postMessage(id ?? '_', value);
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
transport.subscribe(subscribeRequest, async (requestId) => {
|
|
14
|
+
id = requestId;
|
|
15
|
+
if (!unsubscribe) {
|
|
16
|
+
const subscriber = getSubscriber();
|
|
17
|
+
unsubscribe = subscriber(value => {
|
|
18
|
+
transport.postMessage(id ?? '_', value);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
transport.subscribe(unsubscribeRequest, async () => {
|
|
22
|
+
if (unsubscribe) {
|
|
23
|
+
unsubscribe();
|
|
24
|
+
unsubscribe = null;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
replaceSubscriber,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ConnectionStatus, HexString, InjectedAccountSchema, TransportProvider, TxPayloadV1 } from '@novasamatech/host-api';
|
|
2
|
+
import type { SignerPayloadJSON, SignerPayloadRaw, SignerResult } from '@polkadot/types/types';
|
|
3
|
+
import type { JsonRpcProvider } from '@polkadot-api/json-rpc-provider';
|
|
4
|
+
type ContainerHandlers = {
|
|
5
|
+
accounts: {
|
|
6
|
+
get(): Promise<InjectedAccountSchema[]>;
|
|
7
|
+
subscribe(callback: (accounts: InjectedAccountSchema[]) => void): VoidFunction;
|
|
8
|
+
};
|
|
9
|
+
sign: {
|
|
10
|
+
signRaw(raw: SignerPayloadRaw): Promise<SignerResult>;
|
|
11
|
+
signPayload(payload: SignerPayloadJSON): Promise<SignerResult>;
|
|
12
|
+
createTransaction(payload: TxPayloadV1): Promise<HexString>;
|
|
13
|
+
};
|
|
14
|
+
chainSupport(chainId: HexString): Promise<boolean>;
|
|
15
|
+
};
|
|
16
|
+
type Params = {
|
|
17
|
+
handshakeTimeout: number;
|
|
18
|
+
};
|
|
19
|
+
export type Container = ReturnType<typeof createContainer>;
|
|
20
|
+
export declare function createContainer(provider: TransportProvider, params?: Params): {
|
|
21
|
+
connectToPapiProvider(chainId: HexString, provider: JsonRpcProvider): VoidFunction;
|
|
22
|
+
handleAccounts(handler: ContainerHandlers["accounts"]): void;
|
|
23
|
+
handleSignRequest(handler: ContainerHandlers["sign"]): void;
|
|
24
|
+
handleChainSupportCheck(handler: ContainerHandlers["chainSupport"]): void;
|
|
25
|
+
isReady(): Promise<boolean>;
|
|
26
|
+
subscribeLocationChange(callback: (location: string) => void): VoidFunction;
|
|
27
|
+
subscribeConnectionStatus(callback: (connectionStatus: ConnectionStatus) => void): VoidFunction;
|
|
28
|
+
dispose(): void;
|
|
29
|
+
};
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { createTransport } from '@novasamatech/host-api';
|
|
2
|
+
import { createComplexSubscriber } from './createComplexSubscriber';
|
|
3
|
+
function formatError(e) {
|
|
4
|
+
const message = e instanceof Error ? e.message : typeof e === 'string' ? e : 'Unknown error';
|
|
5
|
+
return { tag: 'error', value: message };
|
|
6
|
+
}
|
|
7
|
+
const defaultHandlers = {
|
|
8
|
+
accounts: {
|
|
9
|
+
get: async () => [],
|
|
10
|
+
subscribe: () => () => {
|
|
11
|
+
/* empty */
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
sign: {
|
|
15
|
+
signRaw: () => Promise.reject(new Error('signRaw is not implemented')),
|
|
16
|
+
signPayload: () => Promise.reject(new Error('signPayload is not implemented')),
|
|
17
|
+
createTransaction: () => Promise.reject(new Error('createTransaction is not implemented')),
|
|
18
|
+
},
|
|
19
|
+
chainSupport: async () => false,
|
|
20
|
+
};
|
|
21
|
+
export function createContainer(provider, params) {
|
|
22
|
+
const transport = createTransport(provider, { handshakeTimeout: params?.handshakeTimeout });
|
|
23
|
+
if (!transport.isCorrectEnvironment()) {
|
|
24
|
+
throw new Error('Transport is not available: dapp provider has incorrect environment');
|
|
25
|
+
}
|
|
26
|
+
const externalHandlers = {};
|
|
27
|
+
// account subscription
|
|
28
|
+
transport.handleMessage('getAccountsRequestV1', async () => {
|
|
29
|
+
try {
|
|
30
|
+
const handler = externalHandlers.accounts ?? defaultHandlers.accounts;
|
|
31
|
+
const accounts = await handler.get();
|
|
32
|
+
return {
|
|
33
|
+
tag: 'getAccountsResponseV1',
|
|
34
|
+
value: { tag: 'success', value: accounts },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return {
|
|
39
|
+
tag: 'getAccountsResponseV1',
|
|
40
|
+
value: formatError(e),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const accountSubscriber = transport
|
|
45
|
+
? createComplexSubscriber({
|
|
46
|
+
transport,
|
|
47
|
+
subscribeRequest: 'accountSubscriptionV1',
|
|
48
|
+
unsubscribeRequest: 'accountUnsubscriptionV1',
|
|
49
|
+
getSubscriber() {
|
|
50
|
+
return callback => {
|
|
51
|
+
const subscriber = externalHandlers.accounts?.subscribe ?? defaultHandlers.accounts.subscribe;
|
|
52
|
+
return subscriber(accounts => {
|
|
53
|
+
callback({
|
|
54
|
+
tag: 'getAccountsResponseV1',
|
|
55
|
+
value: { tag: 'success', value: accounts },
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
: null;
|
|
62
|
+
// sign subscription
|
|
63
|
+
transport.handleMessage('signRawRequestV1', async (message) => {
|
|
64
|
+
try {
|
|
65
|
+
const signRaw = externalHandlers.sign?.signRaw ?? defaultHandlers.sign.signRaw;
|
|
66
|
+
const result = await signRaw(message);
|
|
67
|
+
return {
|
|
68
|
+
tag: 'signResponseV1',
|
|
69
|
+
value: { tag: 'success', value: result },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
return {
|
|
74
|
+
tag: 'signResponseV1',
|
|
75
|
+
value: formatError(e),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
transport.handleMessage('signPayloadRequestV1', async (message) => {
|
|
80
|
+
try {
|
|
81
|
+
const signPayload = externalHandlers.sign?.signPayload ?? defaultHandlers.sign.signPayload;
|
|
82
|
+
const result = await signPayload(message);
|
|
83
|
+
return {
|
|
84
|
+
tag: 'signResponseV1',
|
|
85
|
+
value: { tag: 'success', value: result },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
return {
|
|
90
|
+
tag: 'signResponseV1',
|
|
91
|
+
value: formatError(e),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
transport.handleMessage('createTransactionRequestV1', async (message) => {
|
|
96
|
+
try {
|
|
97
|
+
const createTransaction = externalHandlers.sign?.createTransaction ?? defaultHandlers.sign.createTransaction;
|
|
98
|
+
const result = await createTransaction(message);
|
|
99
|
+
return {
|
|
100
|
+
tag: 'createTransactionResponseV1',
|
|
101
|
+
value: { tag: 'success', value: result },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
return {
|
|
106
|
+
tag: 'createTransactionResponseV1',
|
|
107
|
+
value: formatError(e),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// chain support subscription
|
|
112
|
+
transport.handleMessage('supportFeatureRequestV1', async (message) => {
|
|
113
|
+
if (message.tag === 'chain') {
|
|
114
|
+
try {
|
|
115
|
+
const checkChainSupport = externalHandlers.chainSupport ?? defaultHandlers.chainSupport;
|
|
116
|
+
const result = await checkChainSupport(message.value.chainId);
|
|
117
|
+
return {
|
|
118
|
+
tag: 'supportFeatureResponseV1',
|
|
119
|
+
value: {
|
|
120
|
+
tag: 'success',
|
|
121
|
+
value: { tag: 'chain', value: { chainId: message.value.chainId, result } },
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
return {
|
|
127
|
+
tag: 'supportFeatureResponseV1',
|
|
128
|
+
value: formatError(e),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
function init() {
|
|
134
|
+
// init status subscription
|
|
135
|
+
transport.isReady();
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
connectToPapiProvider(chainId, provider) {
|
|
139
|
+
init();
|
|
140
|
+
let connection = null;
|
|
141
|
+
return transport.handleMessage('papiProviderSendMessageV1', async (message) => {
|
|
142
|
+
if (!connection) {
|
|
143
|
+
connection = provider(message => {
|
|
144
|
+
transport.postMessage('_', {
|
|
145
|
+
tag: 'papiProviderReceiveMessageV1',
|
|
146
|
+
value: { tag: 'success', value: { chainId, message } },
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (message.chainId === chainId) {
|
|
151
|
+
connection.send(message.message);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
handleAccounts(handler) {
|
|
156
|
+
init();
|
|
157
|
+
externalHandlers.accounts = handler;
|
|
158
|
+
accountSubscriber?.replaceSubscriber();
|
|
159
|
+
},
|
|
160
|
+
handleSignRequest(handler) {
|
|
161
|
+
init();
|
|
162
|
+
externalHandlers.sign = handler;
|
|
163
|
+
},
|
|
164
|
+
handleChainSupportCheck(handler) {
|
|
165
|
+
init();
|
|
166
|
+
externalHandlers.chainSupport = handler;
|
|
167
|
+
},
|
|
168
|
+
isReady() {
|
|
169
|
+
return transport.isReady();
|
|
170
|
+
},
|
|
171
|
+
subscribeLocationChange(callback) {
|
|
172
|
+
init();
|
|
173
|
+
return transport.subscribe('locationChangedV1', async (location) => {
|
|
174
|
+
callback(location);
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
subscribeConnectionStatus(callback) {
|
|
178
|
+
// this specific order exists because container should report all connection statuses including "disconnected",
|
|
179
|
+
// which immediately got changed to "connecting" after init() call.
|
|
180
|
+
const unsubscribe = transport.onConnectionStatusChange(callback);
|
|
181
|
+
init();
|
|
182
|
+
return unsubscribe;
|
|
183
|
+
},
|
|
184
|
+
dispose() {
|
|
185
|
+
transport.dispose();
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Logger, TransportProvider } from '@novasamatech/host-api';
|
|
2
|
+
type Params = {
|
|
3
|
+
iframe: HTMLIFrameElement;
|
|
4
|
+
url: string;
|
|
5
|
+
logger?: Logger;
|
|
6
|
+
};
|
|
7
|
+
export declare function createIframeProvider({ iframe, url, logger }: Params): TransportProvider;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createDefaultLogger } from '@novasamatech/host-api';
|
|
2
|
+
function hasWindow() {
|
|
3
|
+
try {
|
|
4
|
+
return typeof window !== 'undefined';
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function isValidMessage(event, sourceEnv, currentEnv) {
|
|
11
|
+
return (event.source !== currentEnv &&
|
|
12
|
+
event.source === sourceEnv &&
|
|
13
|
+
event.data &&
|
|
14
|
+
event.data.constructor.name === 'Uint8Array');
|
|
15
|
+
}
|
|
16
|
+
export function createIframeProvider({ iframe, url, logger }) {
|
|
17
|
+
iframe.src = url;
|
|
18
|
+
let disposed = false;
|
|
19
|
+
let iframePromise = null;
|
|
20
|
+
const subscribers = new Set();
|
|
21
|
+
function waitForIframe(callback) {
|
|
22
|
+
if (iframe.contentWindow) {
|
|
23
|
+
return callback(iframe.contentWindow);
|
|
24
|
+
}
|
|
25
|
+
if (iframePromise) {
|
|
26
|
+
iframePromise.then(callback);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
iframePromise = new Promise(resolve => {
|
|
30
|
+
iframe.addEventListener('load', () => {
|
|
31
|
+
resolve(iframe.contentWindow ?? null);
|
|
32
|
+
callback(iframe.contentWindow ?? null);
|
|
33
|
+
iframePromise = null;
|
|
34
|
+
}, { once: true });
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const messageHandler = (event) => {
|
|
38
|
+
if (disposed)
|
|
39
|
+
return;
|
|
40
|
+
waitForIframe(iframe => {
|
|
41
|
+
if (disposed)
|
|
42
|
+
return;
|
|
43
|
+
if (!iframe)
|
|
44
|
+
return;
|
|
45
|
+
if (!isValidMessage(event, iframe, window))
|
|
46
|
+
return;
|
|
47
|
+
for (const subscriber of subscribers) {
|
|
48
|
+
subscriber(event.data);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
if (hasWindow()) {
|
|
53
|
+
window.addEventListener('message', messageHandler);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
logger: logger ?? createDefaultLogger(),
|
|
57
|
+
isCorrectEnvironment() {
|
|
58
|
+
return hasWindow();
|
|
59
|
+
},
|
|
60
|
+
postMessage(message) {
|
|
61
|
+
if (disposed)
|
|
62
|
+
return;
|
|
63
|
+
waitForIframe(iframe => {
|
|
64
|
+
if (!iframe)
|
|
65
|
+
return;
|
|
66
|
+
if (disposed)
|
|
67
|
+
return;
|
|
68
|
+
iframe.postMessage(message, '*', [message.buffer]);
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
subscribe(callback) {
|
|
72
|
+
subscribers.add(callback);
|
|
73
|
+
return () => {
|
|
74
|
+
subscribers.delete(callback);
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
dispose() {
|
|
78
|
+
disposed = true;
|
|
79
|
+
iframe.src = '';
|
|
80
|
+
iframePromise = null;
|
|
81
|
+
subscribers.clear();
|
|
82
|
+
if (hasWindow()) {
|
|
83
|
+
window.removeEventListener('message', messageHandler);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@novasamatech/host-container",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"description": "Host container for hosting and managing products within the Polkadot ecosystem.",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/novasamatech/spektr-sdk.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"polkadot",
|
|
13
|
+
"spektr",
|
|
14
|
+
"dapp"
|
|
15
|
+
],
|
|
16
|
+
"main": "dist/index.js",
|
|
17
|
+
"exports": {
|
|
18
|
+
"./package.json": "./package.json",
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@polkadot-api/json-rpc-provider": "0.0.4",
|
|
30
|
+
"@novasamatech/host-api": "0.4.0"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
}
|
|
35
|
+
}
|