@novasamatech/host-api-wrapper 0.7.9-5

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,35 @@
1
+ import type { CodecType, PaymentBalanceErr, Subscription, Transport } from '@novasamatech/host-api';
2
+ export type PaymentBalance = {
3
+ available: bigint;
4
+ };
5
+ export type PaymentStatus = {
6
+ type: 'processing';
7
+ } | {
8
+ type: 'completed';
9
+ } | {
10
+ type: 'failed';
11
+ reason: string;
12
+ };
13
+ export type TopUpSource = {
14
+ type: 'productAccount';
15
+ derivationIndex: number;
16
+ } | {
17
+ type: 'privateKey';
18
+ key: Uint8Array;
19
+ };
20
+ export declare const createPaymentManager: (transport?: Transport) => {
21
+ subscribeBalance(callback: (balance: PaymentBalance) => void): Subscription<CodecType<typeof PaymentBalanceErr>>;
22
+ topUp(amount: bigint, source: TopUpSource): Promise<void>;
23
+ requestPayment(amount: bigint, destination: Uint8Array): Promise<{
24
+ id: string;
25
+ }>;
26
+ subscribePaymentStatus(id: string, callback: (status: PaymentStatus) => void): Subscription;
27
+ };
28
+ export declare const paymentManager: {
29
+ subscribeBalance(callback: (balance: PaymentBalance) => void): Subscription<CodecType<typeof PaymentBalanceErr>>;
30
+ topUp(amount: bigint, source: TopUpSource): Promise<void>;
31
+ requestPayment(amount: bigint, destination: Uint8Array): Promise<{
32
+ id: string;
33
+ }>;
34
+ subscribePaymentStatus(id: string, callback: (status: PaymentStatus) => void): Subscription;
35
+ };
@@ -0,0 +1,49 @@
1
+ import { createHostApi, enumValue } from '@novasamatech/host-api';
2
+ import { resultToPromise, unwrapVersionedResult } from './helpers.js';
3
+ import { sandboxTransport } from './sandboxTransport.js';
4
+ export const createPaymentManager = (transport = sandboxTransport) => {
5
+ const hostApi = createHostApi(transport);
6
+ const version = 'v1';
7
+ return {
8
+ subscribeBalance(callback) {
9
+ const subscriber = hostApi.paymentBalanceSubscribe(enumValue(version, undefined), payload => {
10
+ if (payload.tag === version) {
11
+ callback(payload.value);
12
+ }
13
+ });
14
+ return {
15
+ unsubscribe: subscriber.unsubscribe,
16
+ onInterrupt: cb => subscriber.onInterrupt(v => cb(v.value)),
17
+ };
18
+ },
19
+ topUp(amount, source) {
20
+ const sourceCodec = source.type === 'productAccount'
21
+ ? {
22
+ tag: 'ProductAccount',
23
+ value: source.derivationIndex,
24
+ }
25
+ : { tag: 'PrivateKey', value: source.key };
26
+ return resultToPromise(unwrapVersionedResult(version, hostApi.paymentTopUp(enumValue(version, { amount, source: sourceCodec }))));
27
+ },
28
+ requestPayment(amount, destination) {
29
+ return resultToPromise(unwrapVersionedResult(version, hostApi.paymentRequest(enumValue(version, { amount, destination }))));
30
+ },
31
+ subscribePaymentStatus(id, callback) {
32
+ return hostApi.paymentStatusSubscribe(enumValue(version, id), payload => {
33
+ if (payload.tag === version) {
34
+ const raw = payload.value;
35
+ if (raw.tag === 'Processing') {
36
+ callback({ type: 'processing' });
37
+ }
38
+ else if (raw.tag === 'Completed') {
39
+ callback({ type: 'completed' });
40
+ }
41
+ else if (raw.tag === 'Failed') {
42
+ callback({ type: 'failed', reason: raw.value });
43
+ }
44
+ }
45
+ });
46
+ },
47
+ };
48
+ };
49
+ export const paymentManager = createPaymentManager();
@@ -0,0 +1,24 @@
1
+ import { DevicePermission, RemotePermission } from '@novasamatech/host-api';
2
+ import type { CodecType } from 'scale-ts';
3
+ export type DevicePermissionKind = CodecType<typeof DevicePermission>;
4
+ export type RemotePermissionItem = CodecType<typeof RemotePermission>;
5
+ /**
6
+ * Request a single device permission from the host.
7
+ * Returns ResultAsync<boolean, GenericError>:
8
+ * - ok(true) — permission granted
9
+ * - ok(false) — permission denied by the user
10
+ * - err(...) — transport or encoding error
11
+ */
12
+ export declare function requestDevicePermission(permission: DevicePermissionKind): import("neverthrow").ResultAsync<boolean, import("@novasamatech/scale").CodecError<{
13
+ reason: string;
14
+ }, "GenericError">>;
15
+ /**
16
+ * Request remote permission from the host.
17
+ * Returns ResultAsync<boolean, GenericError>:
18
+ * - ok(true) — permission granted
19
+ * - ok(false) — permission denied by the user
20
+ * - err(...) — transport or encoding error
21
+ */
22
+ export declare function requestPermission(permission: RemotePermissionItem): import("neverthrow").ResultAsync<boolean, import("@novasamatech/scale").CodecError<{
23
+ reason: string;
24
+ }, "GenericError">>;
@@ -0,0 +1,28 @@
1
+ import { DevicePermission, RemotePermission, enumValue } from '@novasamatech/host-api';
2
+ import { hostApi } from './hostApi.js';
3
+ /**
4
+ * Request a single device permission from the host.
5
+ * Returns ResultAsync<boolean, GenericError>:
6
+ * - ok(true) — permission granted
7
+ * - ok(false) — permission denied by the user
8
+ * - err(...) — transport or encoding error
9
+ */
10
+ export function requestDevicePermission(permission) {
11
+ return hostApi
12
+ .devicePermission(enumValue('v1', permission))
13
+ .map(r => r.value)
14
+ .mapErr(e => e.value);
15
+ }
16
+ /**
17
+ * Request remote permission from the host.
18
+ * Returns ResultAsync<boolean, GenericError>:
19
+ * - ok(true) — permission granted
20
+ * - ok(false) — permission denied by the user
21
+ * - err(...) — transport or encoding error
22
+ */
23
+ export function requestPermission(permission) {
24
+ return hostApi
25
+ .permission(enumValue('v1', permission))
26
+ .map(r => r.value)
27
+ .mapErr(e => e.value);
28
+ }
@@ -0,0 +1,9 @@
1
+ import type { HexString, Subscription } from '@novasamatech/host-api';
2
+ export declare const createPreimageManager: (transport?: import("@novasamatech/host-api").Transport) => {
3
+ lookup(key: HexString, callback: (preimage: Uint8Array | null) => void): Subscription<void>;
4
+ submit(value: Uint8Array): Promise<`0x${string}`>;
5
+ };
6
+ export declare const preimageManager: {
7
+ lookup(key: HexString, callback: (preimage: Uint8Array | null) => void): Subscription<void>;
8
+ submit(value: Uint8Array): Promise<`0x${string}`>;
9
+ };
@@ -0,0 +1,24 @@
1
+ import { createHostApi, enumValue } from '@novasamatech/host-api';
2
+ import { resultToPromise, unwrapVersionedResult } from './helpers.js';
3
+ import { sandboxTransport } from './sandboxTransport.js';
4
+ export const createPreimageManager = (transport = sandboxTransport) => {
5
+ const supportedVersion = 'v1';
6
+ const hostApi = createHostApi(transport);
7
+ return {
8
+ lookup(key, callback) {
9
+ const subscriber = hostApi.preimageLookupSubscribe(enumValue(supportedVersion, key), payload => {
10
+ if (payload.tag === supportedVersion) {
11
+ callback(payload.value);
12
+ }
13
+ });
14
+ return {
15
+ unsubscribe: subscriber.unsubscribe,
16
+ onInterrupt: cb => subscriber.onInterrupt(v => cb(v.value)),
17
+ };
18
+ },
19
+ submit(value) {
20
+ return resultToPromise(unwrapVersionedResult(supportedVersion, hostApi.preimageSubmit(enumValue(supportedVersion, value))));
21
+ },
22
+ };
23
+ };
24
+ export const preimageManager = createPreimageManager();
@@ -0,0 +1,9 @@
1
+ import type { Provider } from '@novasamatech/host-api';
2
+ declare global {
3
+ interface Window {
4
+ __HOST_API_PORT__?: MessagePort;
5
+ __HOST_WEBVIEW_MARK__?: boolean;
6
+ }
7
+ }
8
+ export declare const sandboxProvider: Provider;
9
+ export declare const sandboxTransport: import("@novasamatech/host-api").Transport;
@@ -0,0 +1,109 @@
1
+ import { createDefaultLogger, createTransport } from '@novasamatech/host-api';
2
+ function delay(ttl) {
3
+ return new Promise(resolve => setTimeout(resolve, ttl));
4
+ }
5
+ function getParentWindow() {
6
+ if (window.top) {
7
+ return window.top;
8
+ }
9
+ throw new Error('No parent window found');
10
+ }
11
+ function isIframe() {
12
+ try {
13
+ return window !== window.top;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ function isWebview() {
20
+ try {
21
+ return window['__HOST_WEBVIEW_MARK__'] === true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ async function getWebviewPort(iteration = 200) {
28
+ if (iteration === 0) {
29
+ throw new Error('No webview port found');
30
+ }
31
+ if (window['__HOST_API_PORT__']) {
32
+ return window['__HOST_API_PORT__'];
33
+ }
34
+ await delay(100);
35
+ return getWebviewPort(iteration - 1);
36
+ }
37
+ function isValidIframeMessage(event, sourceEnv, currentEnv) {
38
+ return (event.source !== currentEnv &&
39
+ event.source === sourceEnv &&
40
+ event.data &&
41
+ event.data.constructor.name === 'Uint8Array');
42
+ }
43
+ function isValidWebviewMessage(event) {
44
+ return event.data && event.data.constructor.name === 'Uint8Array';
45
+ }
46
+ // Copy into a tight-fitting ArrayBuffer. Required before handing the array across
47
+ // an Electron IPC / structured-clone boundary: structured clone serializes the full
48
+ // underlying ArrayBuffer.
49
+ function detach(view) {
50
+ const copy = new Uint8Array(view.byteLength);
51
+ copy.set(view);
52
+ return copy;
53
+ }
54
+ function createDefaultSdkProvider() {
55
+ const subscribers = new Set();
56
+ const handleIframeMessage = (event) => {
57
+ if (!isValidIframeMessage(event, getParentWindow(), window))
58
+ return;
59
+ for (const subscriber of subscribers) {
60
+ subscriber(event.data);
61
+ }
62
+ };
63
+ const handleWebviewMessage = (event) => {
64
+ if (!isValidWebviewMessage(event))
65
+ return;
66
+ const detached = detach(event.data);
67
+ for (const subscriber of subscribers) {
68
+ subscriber(detached);
69
+ }
70
+ };
71
+ if (isIframe()) {
72
+ window.addEventListener('message', handleIframeMessage);
73
+ }
74
+ else if (isWebview()) {
75
+ getWebviewPort().then(port => (port.onmessage = handleWebviewMessage));
76
+ }
77
+ return {
78
+ logger: createDefaultLogger(),
79
+ isCorrectEnvironment() {
80
+ return isIframe() || isWebview();
81
+ },
82
+ postMessage(message) {
83
+ if (isIframe()) {
84
+ getParentWindow().postMessage(message, '*', [message.buffer]);
85
+ }
86
+ else if (isWebview()) {
87
+ const detached = detach(message);
88
+ getWebviewPort().then(port => port.postMessage(detached, [detached.buffer]));
89
+ }
90
+ },
91
+ subscribe(callback) {
92
+ subscribers.add(callback);
93
+ return () => {
94
+ subscribers.delete(callback);
95
+ };
96
+ },
97
+ dispose() {
98
+ subscribers.clear();
99
+ if (isIframe()) {
100
+ window.removeEventListener('message', handleIframeMessage);
101
+ }
102
+ if (isWebview()) {
103
+ getWebviewPort().then(port => (port.onmessage = null));
104
+ }
105
+ },
106
+ };
107
+ }
108
+ export const sandboxProvider = createDefaultSdkProvider();
109
+ export const sandboxTransport = createTransport(sandboxProvider);
@@ -0,0 +1,44 @@
1
+ import type { CodecType, ProductAccountId as ProductAccountIdCodec, SignedStatement as SignedStatementCodec, Statement as StatementCodec, Subscription, Topic as TopicCodec, Transport } from '@novasamatech/host-api';
2
+ export type Statement = CodecType<typeof StatementCodec>;
3
+ export type SignedStatement = CodecType<typeof SignedStatementCodec>;
4
+ export type Topic = CodecType<typeof TopicCodec>;
5
+ export type ProductAccountId = CodecType<typeof ProductAccountIdCodec>;
6
+ export type StatementTopicFilter = {
7
+ matchAll: Topic[];
8
+ } | {
9
+ matchAny: Topic[];
10
+ };
11
+ export type StatementsPage = {
12
+ statements: SignedStatement[];
13
+ isComplete: boolean;
14
+ };
15
+ export declare const createStatementStore: (transport?: Transport) => {
16
+ subscribe(filter: StatementTopicFilter, callback: (page: StatementsPage) => void): Subscription<void>;
17
+ createProof(accountId: ProductAccountId, statement: Statement): Promise<{
18
+ tag: "Sr25519";
19
+ value: {
20
+ signature: Uint8Array<ArrayBufferLike>;
21
+ signer: Uint8Array<ArrayBufferLike>;
22
+ };
23
+ } | {
24
+ tag: "Ed25519";
25
+ value: {
26
+ signature: Uint8Array<ArrayBufferLike>;
27
+ signer: Uint8Array<ArrayBufferLike>;
28
+ };
29
+ } | {
30
+ tag: "Ecdsa";
31
+ value: {
32
+ signature: Uint8Array<ArrayBufferLike>;
33
+ signer: Uint8Array<ArrayBufferLike>;
34
+ };
35
+ } | {
36
+ tag: "OnChain";
37
+ value: {
38
+ who: Uint8Array<ArrayBufferLike>;
39
+ blockHash: Uint8Array<ArrayBufferLike>;
40
+ event: bigint;
41
+ };
42
+ }>;
43
+ submit(signedStatement: SignedStatement): Promise<void>;
44
+ };
@@ -0,0 +1,41 @@
1
+ import { createHostApi, enumValue } from '@novasamatech/host-api';
2
+ import { sandboxTransport } from './sandboxTransport.js';
3
+ export const createStatementStore = (transport = sandboxTransport) => {
4
+ const hostApi = createHostApi(transport);
5
+ return {
6
+ subscribe(filter, callback) {
7
+ const scaleFilter = 'matchAll' in filter ? enumValue('MatchAll', filter.matchAll) : enumValue('MatchAny', filter.matchAny);
8
+ const subscriber = hostApi.statementStoreSubscribe(enumValue('v1', scaleFilter), payload => {
9
+ if (payload.tag === 'v1') {
10
+ callback(payload.value);
11
+ }
12
+ });
13
+ return {
14
+ unsubscribe: subscriber.unsubscribe,
15
+ onInterrupt: cb => subscriber.onInterrupt(v => cb(v.value)),
16
+ };
17
+ },
18
+ async createProof(accountId, statement) {
19
+ const result = await hostApi.statementStoreCreateProof(enumValue('v1', [accountId, statement]));
20
+ return result.match(payload => {
21
+ if (payload.tag === 'v1') {
22
+ return payload.value;
23
+ }
24
+ throw new Error(`Unknown response version ${payload.tag}`);
25
+ }, err => {
26
+ throw err.value;
27
+ });
28
+ },
29
+ async submit(signedStatement) {
30
+ const result = await hostApi.statementStoreSubmit(enumValue('v1', signedStatement));
31
+ return result.match(payload => {
32
+ if (payload.tag === 'v1') {
33
+ return;
34
+ }
35
+ throw new Error(`Unknown response version ${payload.tag}`);
36
+ }, err => {
37
+ throw err.value;
38
+ });
39
+ },
40
+ };
41
+ };
@@ -0,0 +1,6 @@
1
+ import type { CodecType, Subscription, Transport } from '@novasamatech/host-api';
2
+ import { Theme } from '@novasamatech/host-api';
3
+ export type ThemeMode = CodecType<typeof Theme>;
4
+ export declare function createThemeProvider(transport?: Transport): {
5
+ subscribeTheme(callback: (theme: ThemeMode) => void): Subscription<void>;
6
+ };
package/dist/theme.js ADDED
@@ -0,0 +1,18 @@
1
+ import { Theme, createHostApi, enumValue } from '@novasamatech/host-api';
2
+ import { sandboxTransport } from './sandboxTransport.js';
3
+ export function createThemeProvider(transport = sandboxTransport) {
4
+ const hostApi = createHostApi(transport);
5
+ return {
6
+ subscribeTheme(callback) {
7
+ const subscriber = hostApi.themeSubscribe(enumValue('v1', undefined), value => {
8
+ if (value.tag === 'v1') {
9
+ callback(value.value);
10
+ }
11
+ });
12
+ return {
13
+ unsubscribe: subscriber.unsubscribe,
14
+ onInterrupt: cb => subscriber.onInterrupt(v => cb(v.value)),
15
+ };
16
+ },
17
+ };
18
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@novasamatech/host-api-wrapper",
3
+ "type": "module",
4
+ "version": "0.7.9-5",
5
+ "description": "Host API wrapper: integrate and run your product inside Polkadot browser.",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/paritytech/triangle-js-sdks.git"
10
+ },
11
+ "keywords": [
12
+ "polkadot"
13
+ ],
14
+ "main": "dist/index.js",
15
+ "exports": {
16
+ "./package.json": "./package.json",
17
+ ".": {
18
+ "#/source": "./src/index.ts",
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md"
26
+ ],
27
+ "dependencies": {
28
+ "@polkadot/extension-inject": "^0.63.1",
29
+ "@polkadot-api/json-rpc-provider-proxy": "^0.4.0",
30
+ "@polkadot-api/substrate-bindings": "^0.20.2",
31
+ "@novasamatech/host-api": "0.7.9-5",
32
+ "polkadot-api": ">=2",
33
+ "neverthrow": "^8.2.0"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ }
38
+ }