@novasamatech/product-sdk 0.5.0-6 → 0.5.0-8

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.
@@ -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,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,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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/product-sdk",
3
3
  "type": "module",
4
- "version": "0.5.0-6",
4
+ "version": "0.5.0-8",
5
5
  "description": "Polkadot product SDK: integrate and run your product inside Polkadot browser.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "@polkadot/extension-inject": "0.62.5",
30
30
  "@polkadot-api/json-rpc-provider": "0.0.4",
31
31
  "@polkadot-api/json-rpc-provider-proxy": "0.2.7",
32
- "@novasamatech/host-api": "0.5.0-6"
32
+ "@novasamatech/host-api": "0.5.0-8"
33
33
  },
34
34
  "publishConfig": {
35
35
  "access": "public"