@novasamatech/product-sdk 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 ADDED
@@ -0,0 +1,82 @@
1
+ # @novasamatech/product-sdk
2
+
3
+ Easy way to embed Polkadot host functionality into your dapp.
4
+
5
+ ## Overview
6
+
7
+ Spektr SDK provides a set of tools to integrate your application with Spektr dapp browser.
8
+ Core features:
9
+ - Generic account provider similar to [polkadot-js extension](https://polkadot.js.org/extension/).
10
+ - Redirect [PAPI](https://papi.how/) requests to host application
11
+ - Receive additional information from host application - supported chains, theme, etc.
12
+
13
+ ## Installation
14
+
15
+ ```shell
16
+ npm install @novasamatech/product-sdk --save -E
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Injecting account provider into `injectedWeb3` interface
22
+
23
+ Spektr SDK can provide account information and signers with same interface as any other polkadot compatible wallet.
24
+
25
+ ```ts
26
+ import { injectSpektrExtension, SpektrExtensionName } from '@novasamatech/product-sdk';
27
+ import { connectInjectedExtension, type InjectedPolkadotAccount } from '@polkadot-api/pjs-signer';
28
+
29
+ async function getSpektrExtension() {
30
+ const ready = await injectSpektrExtension();
31
+
32
+ if (ready) {
33
+ return connectInjectedExtension(SpektrExtensionName)
34
+ }
35
+
36
+ return null;
37
+ }
38
+
39
+ async function getAccounts(): Promise<InjectedPolkadotAccount[]> {
40
+ const extension = await getSpektrExtension();
41
+
42
+ if (extension) {
43
+ return extension.getAccounts()
44
+ }
45
+
46
+ // fallback to other providers
47
+ return [];
48
+ }
49
+ ```
50
+
51
+ ### Redirecting PAPI requests to host application
52
+
53
+ You can wrap your PAPI provider with spektr provider to support redirecting requests to host application.
54
+
55
+ ```diff
56
+ import { createClient, type PolkadotClient } from 'polkadot-api';
57
+ import { getWsProvider } from 'polkadot-api/ws-provider';
58
+ import { createSpektrPapiProvider, WellKnownChain } from '@novasamatech/product-sdk';
59
+
60
+ function createPapiClient(): PolkadotClient {
61
+ const polkadotEndpoint = 'wss://...';
62
+
63
+ - const provider = getWsProvider(polkadotEndpoint);
64
+ + const provider = createSpektrPapiProvider({
65
+ + chainId: WellKnownChain.polkadotRelay,
66
+ + fallback: getWsProvider(polkadotEndpoint),
67
+ + });
68
+
69
+ return createClient(provider);
70
+ }
71
+ ```
72
+
73
+ ### Subscribing metadata and statuses
74
+
75
+ ```ts
76
+ import { spektrMetaProvider } from '@novasamatech/product-sdk';
77
+
78
+ const unsubscribe = spektrMetaProvider.subscribeConnectionStatus((status) => {
79
+ console.log('connection status changed', status);
80
+ });
81
+ ```
82
+
@@ -0,0 +1,11 @@
1
+ export declare const Version: string;
2
+ export declare const SpektrExtensionName = "spektr";
3
+ export declare const WellKnownChain: {
4
+ readonly polkadotRelay: "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3";
5
+ readonly polkadotAssetHub: "0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f";
6
+ readonly kusamaRelay: "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe";
7
+ readonly kusamaAssetHub: "0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a";
8
+ readonly westendRelay: "0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e";
9
+ readonly westendAssetHub: "0x67f9723393ef76214df0118c34bbbd3dbebc8ed46a10973a8c969d48fe7598c9";
10
+ readonly rococo: "0x6408de7737c59c238890533af25896a2c20608d8b380bb01029acb392781063e";
11
+ };
@@ -0,0 +1,12 @@
1
+ import pkg from '../package.json';
2
+ export const Version = pkg.version;
3
+ export const SpektrExtensionName = 'spektr';
4
+ export const WellKnownChain = {
5
+ polkadotRelay: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
6
+ polkadotAssetHub: '0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f',
7
+ kusamaRelay: '0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe',
8
+ kusamaAssetHub: '0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a',
9
+ westendRelay: '0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e',
10
+ westendAssetHub: '0x67f9723393ef76214df0118c34bbbd3dbebc8ed46a10973a8c969d48fe7598c9',
11
+ rococo: '0x6408de7737c59c238890533af25896a2c20608d8b380bb01029acb392781063e',
12
+ };
@@ -0,0 +1,7 @@
1
+ import type { ConnectionStatus, Transport } from '@novasamatech/host-api';
2
+ export declare function createSpektrMetaProvider(transport?: Transport): {
3
+ subscribeConnectionStatus(callback: (connectionStatus: ConnectionStatus) => void): VoidFunction;
4
+ };
5
+ export declare const spektrMetaProvider: {
6
+ subscribeConnectionStatus(callback: (connectionStatus: ConnectionStatus) => void): VoidFunction;
7
+ };
@@ -0,0 +1,21 @@
1
+ import { defaultTransport } from './defaultTransport';
2
+ export function createSpektrMetaProvider(transport = defaultTransport) {
3
+ if (transport.isCorrectEnvironment() && typeof window !== 'undefined') {
4
+ const getUrl = () => {
5
+ return window.location.pathname + window.location.hash + window.location.search;
6
+ };
7
+ window.addEventListener('hashchange', () => {
8
+ transport.postMessage('_', { tag: 'locationChangedV1', value: getUrl() });
9
+ });
10
+ window.addEventListener('popstate', () => {
11
+ transport.postMessage('_', { tag: 'locationChangedV1', value: getUrl() });
12
+ });
13
+ transport.postMessage('_', { tag: 'locationChangedV1', value: getUrl() });
14
+ }
15
+ return {
16
+ subscribeConnectionStatus(callback) {
17
+ return transport.onConnectionStatusChange(callback);
18
+ },
19
+ };
20
+ }
21
+ export const spektrMetaProvider = createSpektrMetaProvider();
@@ -0,0 +1,11 @@
1
+ import type { HexString, Transport } from '@novasamatech/host-api';
2
+ import type { JsonRpcProvider } from '@polkadot-api/json-rpc-provider';
3
+ type Params = {
4
+ chainId: HexString;
5
+ fallback: JsonRpcProvider;
6
+ };
7
+ type InternalParams = {
8
+ transport?: Transport;
9
+ };
10
+ export declare function createSpektrPapiProvider({ chainId, fallback }: Params, internal?: InternalParams): JsonRpcProvider;
11
+ export {};
@@ -0,0 +1,43 @@
1
+ import { unwrapResponseOrThrow } from '@novasamatech/host-api';
2
+ import { getSyncProvider } from '@polkadot-api/json-rpc-provider-proxy';
3
+ import { defaultTransport } from './defaultTransport';
4
+ export function createSpektrPapiProvider({ chainId, fallback }, internal) {
5
+ const transport = internal?.transport ?? defaultTransport;
6
+ if (!transport.isCorrectEnvironment())
7
+ return fallback;
8
+ const spektrProvider = onMessage => {
9
+ const unsubscribe = transport.subscribe('papiProviderReceiveMessageV1', (_, payload) => {
10
+ const unwrapped = unwrapResponseOrThrow(payload);
11
+ if (unwrapped.chainId === chainId) {
12
+ onMessage(unwrapped.message);
13
+ }
14
+ });
15
+ return {
16
+ send(message) {
17
+ transport.postMessage('_', { tag: 'papiProviderSendMessageV1', value: { chainId, message } });
18
+ },
19
+ disconnect() {
20
+ unsubscribe();
21
+ },
22
+ };
23
+ };
24
+ function checkIfReady() {
25
+ return transport.isReady().then(ready => {
26
+ if (!ready)
27
+ return false;
28
+ return transport
29
+ .request({ tag: 'supportFeatureRequestV1', value: { tag: 'chain', value: { chainId } } }, 'supportFeatureResponseV1')
30
+ .then(payload => {
31
+ const result = unwrapResponseOrThrow(payload);
32
+ if (result.tag === 'chain' && result.value.chainId === chainId) {
33
+ return result.value.result;
34
+ }
35
+ })
36
+ .catch(e => {
37
+ transport.provider.logger.error('Error checking chain support', e);
38
+ return false;
39
+ });
40
+ });
41
+ }
42
+ return getSyncProvider(() => checkIfReady().then(ready => (ready ? spektrProvider : fallback)));
43
+ }
@@ -0,0 +1,21 @@
1
+ import { type MessagePayloadSchema, type MessageType, type PickMessagePayload } from '@novasamatech/host-api';
2
+ export type Provider = {
3
+ send(message: Uint8Array): void;
4
+ on(callback: (message: Uint8Array) => void): () => void;
5
+ };
6
+ export declare const defaultProvider: Provider;
7
+ export type Transport = NonNullable<ReturnType<typeof createTransport>>;
8
+ export declare function createTransport(provider: Provider): {
9
+ isSpektrReady(): Promise<boolean>;
10
+ subscribeAny(callback: (id: string, payload: MessagePayloadSchema) => void): () => void;
11
+ subscribe<const Type extends MessageType>(type: Type, callback: (id: string, payload: PickMessagePayload<Type>) => void): () => void;
12
+ send(id: string, payload: MessagePayloadSchema): string;
13
+ request(payload: MessagePayloadSchema): Promise<MessagePayloadSchema>;
14
+ } | null;
15
+ export declare const defaultTransport: {
16
+ isSpektrReady(): Promise<boolean>;
17
+ subscribeAny(callback: (id: string, payload: MessagePayloadSchema) => void): () => void;
18
+ subscribe<const Type extends MessageType>(type: Type, callback: (id: string, payload: PickMessagePayload<Type>) => void): () => void;
19
+ send(id: string, payload: MessagePayloadSchema): string;
20
+ request(payload: MessagePayloadSchema): Promise<MessagePayloadSchema>;
21
+ } | null;
@@ -0,0 +1,111 @@
1
+ import { HANDSHAKE_INTERVAL, isValidMessage, promiseWithResolvers } from '@novasamatech/product-sdk-shared';
2
+ import { messageEncoder, } from '@novasamatech/host-api';
3
+ import { nanoid } from 'nanoid';
4
+ import { getParentWindow, inIframe } from './utils';
5
+ export const defaultProvider = {
6
+ send(message) {
7
+ getParentWindow().postMessage(message, '*', [message.buffer]);
8
+ },
9
+ on(callback) {
10
+ const handle = (event) => {
11
+ if (!isValidMessage(event, getParentWindow(), window))
12
+ return;
13
+ callback(event.data);
14
+ };
15
+ window.addEventListener('message', handle);
16
+ return () => {
17
+ window.removeEventListener('message', handle);
18
+ };
19
+ },
20
+ };
21
+ export function createTransport(provider) {
22
+ if (!inIframe()) {
23
+ return null;
24
+ }
25
+ const readyAbortController = new AbortController();
26
+ let connected = null;
27
+ const api = {
28
+ isSpektrReady() {
29
+ if (connected !== null) {
30
+ return Promise.resolve(connected);
31
+ }
32
+ let resolved = false;
33
+ const request = new Promise(resolve => {
34
+ const interval = setInterval(() => {
35
+ if (readyAbortController.signal.aborted) {
36
+ clearInterval(interval);
37
+ resolve(false);
38
+ return;
39
+ }
40
+ const id = nanoid();
41
+ const encoded = messageEncoder.enc({ id, payload: { tag: 'handshakeRequestV1', value: undefined } });
42
+ const unsubscribe = api.subscribe('handshakeResponseV1', receivedId => {
43
+ if (receivedId !== id)
44
+ return;
45
+ clearInterval(interval);
46
+ unsubscribe();
47
+ resolved = true;
48
+ resolve(true);
49
+ });
50
+ provider.send(encoded);
51
+ }, HANDSHAKE_INTERVAL);
52
+ });
53
+ return Promise.race([
54
+ request,
55
+ new Promise(resolve => {
56
+ setTimeout(() => {
57
+ if (!resolved) {
58
+ readyAbortController.abort();
59
+ resolve(false);
60
+ }
61
+ }, 1_000);
62
+ }).then(result => {
63
+ connected = result;
64
+ return result;
65
+ }),
66
+ ]);
67
+ },
68
+ subscribeAny(callback) {
69
+ return provider.on(message => {
70
+ let result;
71
+ try {
72
+ result = messageEncoder.dec(message);
73
+ }
74
+ catch {
75
+ return;
76
+ }
77
+ callback(result.id, result.payload);
78
+ });
79
+ },
80
+ subscribe(type, callback) {
81
+ return api.subscribeAny((id, message) => {
82
+ if (message.tag == type) {
83
+ callback(id, message);
84
+ }
85
+ });
86
+ },
87
+ send(id, payload) {
88
+ const encoded = messageEncoder.enc({ id, payload });
89
+ provider.send(encoded);
90
+ return id;
91
+ },
92
+ async request(payload) {
93
+ const ready = await api.isSpektrReady();
94
+ if (!ready) {
95
+ throw new Error('Spektr is not ready');
96
+ }
97
+ const id = nanoid();
98
+ const { resolve, promise } = promiseWithResolvers();
99
+ const unsubscribe = api.subscribeAny((receivedId, message) => {
100
+ if (receivedId === id) {
101
+ unsubscribe();
102
+ resolve(message);
103
+ }
104
+ });
105
+ api.send(id, payload);
106
+ return promise;
107
+ },
108
+ };
109
+ return api;
110
+ }
111
+ export const defaultTransport = createTransport(defaultProvider);
@@ -0,0 +1,3 @@
1
+ import type { TransportProvider } from '@novasamatech/host-api';
2
+ export declare const defaultProvider: TransportProvider;
3
+ export declare const defaultTransport: import("@novasamatech/host-api").Transport;
@@ -0,0 +1,57 @@
1
+ import { createDefaultLogger, createTransport } from '@novasamatech/host-api';
2
+ function getParentWindow() {
3
+ if (window.top) {
4
+ return window.top;
5
+ }
6
+ throw new Error('No parent window found');
7
+ }
8
+ function isIframe() {
9
+ try {
10
+ return window !== window.top;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ function isValidMessage(event, sourceEnv, currentEnv) {
17
+ return (event.source !== currentEnv &&
18
+ event.source === sourceEnv &&
19
+ event.data &&
20
+ event.data.constructor.name === 'Uint8Array');
21
+ }
22
+ function createDefaultSdkProvider() {
23
+ const subscribers = new Set();
24
+ const handleMessage = (event) => {
25
+ if (!isValidMessage(event, getParentWindow(), window))
26
+ return;
27
+ for (const subscriber of subscribers) {
28
+ subscriber(event.data);
29
+ }
30
+ };
31
+ if (isIframe()) {
32
+ window.addEventListener('message', handleMessage);
33
+ }
34
+ return {
35
+ logger: createDefaultLogger(),
36
+ isCorrectEnvironment() {
37
+ return isIframe();
38
+ },
39
+ postMessage(message) {
40
+ getParentWindow().postMessage(message, '*', [message.buffer]);
41
+ },
42
+ subscribe(callback) {
43
+ subscribers.add(callback);
44
+ return () => {
45
+ subscribers.delete(callback);
46
+ };
47
+ },
48
+ dispose() {
49
+ subscribers.clear();
50
+ if (isIframe()) {
51
+ window.removeEventListener('message', handleMessage);
52
+ }
53
+ },
54
+ };
55
+ }
56
+ export const defaultProvider = createDefaultSdkProvider();
57
+ export const defaultTransport = createTransport(defaultProvider, { handshakeTimeout: 1_000 });
@@ -0,0 +1,6 @@
1
+ export { SpektrExtensionName, WellKnownChain } from './constants';
2
+ export { defaultProvider, defaultTransport } from './defaultTransport';
3
+ export { spektrMetaProvider } from './createSpektrMetaProvider';
4
+ export { createSpektrMetaProvider } from './createSpektrMetaProvider';
5
+ export { createExtensionEnableFactory, injectSpektrExtension } from './injectSpektrExtension';
6
+ export { createSpektrPapiProvider } from './createSpektrPapiProvider';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { SpektrExtensionName, WellKnownChain } from './constants';
2
+ export { defaultProvider, defaultTransport } from './defaultTransport';
3
+ export { spektrMetaProvider } from './createSpektrMetaProvider';
4
+ export { createSpektrMetaProvider } from './createSpektrMetaProvider';
5
+ export { createExtensionEnableFactory, injectSpektrExtension } from './injectSpektrExtension';
6
+ export { createSpektrPapiProvider } from './createSpektrPapiProvider';
@@ -0,0 +1,2 @@
1
+ import { type Provider } from './createTransport';
2
+ export declare function inject(provider?: Provider): Promise<boolean>;
package/dist/inject.js ADDED
@@ -0,0 +1,60 @@
1
+ import { injectExtension } from '@polkadot/extension-inject';
2
+ import { unwrapResponseOrThrow } from '@spektr/sdk-transport';
3
+ import { SpektrExtensionName, Version } from './constants';
4
+ import { createTransport, defaultProvider } from './createTransport';
5
+ function injectPolkadotExtension(transport) {
6
+ async function enable() {
7
+ return {
8
+ accounts: {
9
+ get() {
10
+ return transport.request({ tag: 'getAccountsRequestV1', value: undefined }).then(e => {
11
+ if (e.tag === 'getAccountsResponseV1') {
12
+ return unwrapResponseOrThrow(e.value);
13
+ }
14
+ throw new Error(`Invalid response, got ${e.tag} message`);
15
+ });
16
+ },
17
+ subscribe(callback) {
18
+ return transport.subscribe('getAccountsResponseV1', (_, message) => {
19
+ try {
20
+ const accounts = unwrapResponseOrThrow(message.value);
21
+ callback(accounts);
22
+ }
23
+ catch {
24
+ console.error('Invalid account response, got', message.value.value);
25
+ }
26
+ });
27
+ },
28
+ },
29
+ signer: {
30
+ signRaw(raw) {
31
+ return transport.request({ tag: 'signRawRequestV1', value: raw }).then(response => {
32
+ if (response.tag === 'signPayloadResponseV1') {
33
+ return unwrapResponseOrThrow(response.value);
34
+ }
35
+ throw new Error(`Invalid response, got ${response.tag} message`);
36
+ });
37
+ },
38
+ signPayload(payload) {
39
+ return transport.request({ tag: 'signPayloadRequestV1', value: payload }).then(response => {
40
+ if (response.tag === 'signPayloadResponseV1') {
41
+ return unwrapResponseOrThrow(response.value);
42
+ }
43
+ throw new Error(`Invalid response, got ${response.tag} message`);
44
+ });
45
+ },
46
+ },
47
+ };
48
+ }
49
+ injectExtension(enable, { name: SpektrExtensionName, version: Version });
50
+ }
51
+ export async function inject(provider = defaultProvider) {
52
+ const transport = createTransport(provider);
53
+ if (!transport)
54
+ return false;
55
+ const ready = await transport.isSpektrReady();
56
+ if (!ready)
57
+ return false;
58
+ injectPolkadotExtension(transport);
59
+ return true;
60
+ }
@@ -0,0 +1,24 @@
1
+ import type { HexString, Transport, TxPayloadV1 } from '@novasamatech/host-api';
2
+ import type { InjectedAccounts } from '@polkadot/extension-inject/types';
3
+ import type { SignerPayloadJSON, SignerPayloadRaw, SignerResult } from '@polkadot/types/types/extrinsic';
4
+ interface Signer {
5
+ /**
6
+ * @description signs an extrinsic payload from a serialized form
7
+ */
8
+ signPayload?: (payload: SignerPayloadJSON) => Promise<SignerResult>;
9
+ /**
10
+ * @description signs a raw payload, only the bytes data as supplied
11
+ */
12
+ signRaw?: (raw: SignerPayloadRaw) => Promise<SignerResult>;
13
+ /**
14
+ * @description signs a transaction according to https://github.com/polkadot-js/api/issues/6213
15
+ */
16
+ createTransaction?: (payload: TxPayloadV1) => Promise<HexString>;
17
+ }
18
+ interface Injected {
19
+ accounts: InjectedAccounts;
20
+ signer: Signer;
21
+ }
22
+ export declare function createExtensionEnableFactory(transport: Transport): Promise<(() => Promise<Injected>) | null>;
23
+ export declare function injectSpektrExtension(transport?: Transport | null): Promise<boolean>;
24
+ export {};
@@ -0,0 +1,72 @@
1
+ import { unwrapResponseOrThrow } from '@novasamatech/host-api';
2
+ import { injectExtension } from '@polkadot/extension-inject';
3
+ import { SpektrExtensionName, Version } from './constants';
4
+ import { defaultTransport } from './defaultTransport';
5
+ export async function createExtensionEnableFactory(transport) {
6
+ const ready = await transport.isReady();
7
+ if (!ready)
8
+ return null;
9
+ async function enable() {
10
+ return {
11
+ accounts: {
12
+ get() {
13
+ return transport
14
+ .request({ tag: 'getAccountsRequestV1', value: undefined }, 'getAccountsResponseV1')
15
+ .then(unwrapResponseOrThrow);
16
+ },
17
+ subscribe(callback) {
18
+ const unsubscribe = transport.subscribe('getAccountsResponseV1', (_, payload) => {
19
+ try {
20
+ const accounts = unwrapResponseOrThrow(payload);
21
+ callback(accounts);
22
+ }
23
+ catch {
24
+ transport.provider.logger.error('Failed response on account subscription', payload.value);
25
+ }
26
+ });
27
+ transport.postMessage('_', { tag: 'accountSubscriptionV1', value: undefined });
28
+ return () => {
29
+ transport.postMessage('_', { tag: 'accountUnsubscriptionV1', value: undefined });
30
+ unsubscribe();
31
+ };
32
+ },
33
+ },
34
+ signer: {
35
+ signRaw(raw) {
36
+ return transport
37
+ .request({ tag: 'signRawRequestV1', value: raw }, 'signResponseV1')
38
+ .then(unwrapResponseOrThrow);
39
+ },
40
+ signPayload(payload) {
41
+ return transport
42
+ .request({ tag: 'signPayloadRequestV1', value: payload }, 'signResponseV1')
43
+ .then(unwrapResponseOrThrow);
44
+ },
45
+ createTransaction(payload) {
46
+ return transport
47
+ .request({ tag: 'createTransactionRequestV1', value: payload }, 'createTransactionResponseV1')
48
+ .then(unwrapResponseOrThrow);
49
+ },
50
+ },
51
+ };
52
+ }
53
+ return enable;
54
+ }
55
+ export async function injectSpektrExtension(transport = defaultTransport) {
56
+ if (!transport)
57
+ return false;
58
+ try {
59
+ const enable = await createExtensionEnableFactory(transport);
60
+ if (enable) {
61
+ injectExtension(enable, { name: SpektrExtensionName, version: Version });
62
+ return true;
63
+ }
64
+ else {
65
+ return false;
66
+ }
67
+ }
68
+ catch (e) {
69
+ transport.provider.logger.error('Error injecting extension', e);
70
+ return false;
71
+ }
72
+ }
@@ -0,0 +1,3 @@
1
+ import type { TransportProvider } from '@novasamatech/host-api';
2
+ export declare const defaultProvider: TransportProvider;
3
+ export declare const defaultTransport: import("@novasamatech/host-api").Transport;
@@ -0,0 +1,51 @@
1
+ import { isValidMessage } from '@novasamatech/product-sdk-shared';
2
+ import { createTransport } from '@novasamatech/host-api';
3
+ function getParentWindow() {
4
+ if (window.top) {
5
+ return window.top;
6
+ }
7
+ throw new Error('No parent window found');
8
+ }
9
+ function isIframe() {
10
+ try {
11
+ return window !== window.top;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ function createDefaultSdkProvider() {
18
+ const subscribers = new Set();
19
+ const handleMessage = (event) => {
20
+ if (!isValidMessage(event, getParentWindow(), window))
21
+ return;
22
+ for (const subscriber of subscribers) {
23
+ subscriber(event.data);
24
+ }
25
+ };
26
+ if (isIframe()) {
27
+ window.addEventListener('message', handleMessage);
28
+ }
29
+ return {
30
+ isCorrectEnvironment() {
31
+ return isIframe();
32
+ },
33
+ postMessage(message) {
34
+ getParentWindow().postMessage(message, '*', [message.buffer]);
35
+ },
36
+ subscribe(callback) {
37
+ subscribers.add(callback);
38
+ return () => {
39
+ subscribers.delete(callback);
40
+ };
41
+ },
42
+ dispose() {
43
+ subscribers.clear();
44
+ if (isIframe()) {
45
+ window.removeEventListener('message', handleMessage);
46
+ }
47
+ },
48
+ };
49
+ }
50
+ export const defaultProvider = createDefaultSdkProvider();
51
+ export const defaultTransport = createTransport(defaultProvider, { handshakeTimeout: 1_000 });
@@ -0,0 +1,8 @@
1
+ import type { Injected } from '@polkadot/extension-inject/types';
2
+ export interface InjectedWindowProvider {
3
+ enable: () => Promise<Injected>;
4
+ version: string;
5
+ }
6
+ export interface InjectedWindow {
7
+ injectedWeb3: Record<string, InjectedWindowProvider>;
8
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare function inIframe(): boolean;
2
+ export declare function getParentWindow(): Window;
package/dist/utils.js ADDED
@@ -0,0 +1,14 @@
1
+ export function inIframe() {
2
+ try {
3
+ return window !== window.top;
4
+ }
5
+ catch {
6
+ return false;
7
+ }
8
+ }
9
+ export function getParentWindow() {
10
+ if (window.top) {
11
+ return window.top;
12
+ }
13
+ throw new Error('No parent window found');
14
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@novasamatech/product-sdk",
3
+ "type": "module",
4
+ "version": "0.4.0",
5
+ "description": "Polkadot product SDK: integrate and run your product inside Polkadot browser.",
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/extension-inject": "0.62.5",
30
+ "@polkadot-api/json-rpc-provider": "0.0.4",
31
+ "@polkadot-api/json-rpc-provider-proxy": "0.2.7",
32
+ "@novasamatech/host-api": "0.4.0"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ }
37
+ }