@parity/product-sdk-host 0.1.0 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,8 +1,13 @@
1
- import { JsonRpcProvider } from 'polkadot-api/ws-provider/web';
2
1
  import * as polkadot_api from 'polkadot-api';
2
+ import { JsonRpcProvider } from 'polkadot-api';
3
3
  export { HexString, assertEnumVariant, enumValue, fromHex, isEnumVariant, resultErr, resultOk, toHex, unwrapResultOrThrow } from '@novasamatech/host-api';
4
4
 
5
- /** Subset of product-sdk's hostLocalStorage that the KV store uses. */
5
+ /**
6
+ * Persistent string and JSON storage exposed by the host container. Most
7
+ * apps reach it indirectly through the Storage package's `KvStore`; reach for
8
+ * it directly via {@link getHostLocalStorage} when you need raw host storage
9
+ * without the KV abstraction.
10
+ */
6
11
  interface HostLocalStorage {
7
12
  readString(key: string): Promise<string | null>;
8
13
  writeString(key: string, value: string): Promise<void>;
@@ -14,7 +19,14 @@ interface HostLocalStorage {
14
19
  */
15
20
  clear(key: string): Promise<void>;
16
21
  }
17
- /** Proof types returned from createProof (matches product-sdk SCALE-decoded types). */
22
+ /**
23
+ * Cryptographic proof attached to a statement before submission, returned by
24
+ * {@link HostStatementStore.createProof}. Variants cover the supported
25
+ * signature schemes — `Sr25519`, `Ed25519`, `Ecdsa`, and `OnChain` (chain-
26
+ * attestation-based proofs).
27
+ *
28
+ * Mirrors `@novasamatech/product-sdk@0.7`'s proof shape.
29
+ */
18
30
  type StatementProof = {
19
31
  tag: "Sr25519";
20
32
  value: {
@@ -28,38 +40,77 @@ type StatementProof = {
28
40
  signer: Uint8Array;
29
41
  };
30
42
  } | {
31
- tag: "Secp256k1Ecdsa";
43
+ tag: "Ecdsa";
32
44
  value: {
33
45
  signature: Uint8Array;
34
46
  signer: Uint8Array;
35
47
  };
36
48
  } | {
37
- tag: "EcdsaRecoverable";
49
+ tag: "OnChain";
38
50
  value: {
39
- signature: Uint8Array;
51
+ who: Uint8Array;
52
+ blockHash: Uint8Array;
53
+ event: bigint;
40
54
  };
41
55
  };
42
- /** The statement store interface provided by the host API via product-sdk. */
56
+ /**
57
+ * Topic-based subscription filter. Mirrors `StatementTopicFilter` from
58
+ * `@novasamatech/product-sdk` — the host delivers statements that match
59
+ * either *all* of the listed topics (`matchAll`) or *any* of them
60
+ * (`matchAny`).
61
+ */
62
+ type StatementTopicFilter = {
63
+ matchAll: Uint8Array[];
64
+ } | {
65
+ matchAny: Uint8Array[];
66
+ };
67
+ /**
68
+ * A page of signed statements delivered by {@link HostStatementStore.subscribe}.
69
+ *
70
+ * Pages arrive sequentially. `isComplete` is `true` on the final page of a
71
+ * subscription's initial backfill; subsequent pages contain new statements
72
+ * as they appear on chain.
73
+ */
74
+ interface StatementsPage {
75
+ statements: unknown[];
76
+ isComplete: boolean;
77
+ }
78
+ /** Subscription handle returned by the host. */
79
+ interface HostSubscription {
80
+ unsubscribe: () => void;
81
+ }
82
+ /**
83
+ * Statement Store handle exposed by the host container. Provides
84
+ * `subscribe`, `createProof`, and `submit` operations that go through the
85
+ * host's native binary protocol; the `statement-store` package layers a
86
+ * higher-level client on top.
87
+ *
88
+ * Shape matches `@novasamatech/product-sdk@0.7`'s `createStatementStore()`.
89
+ */
43
90
  interface HostStatementStore {
44
91
  /**
45
- * Subscribe to statements matching the given topics.
46
- * @param topics - Topic filters as Uint8Array[]
47
- * @param callback - Called with batches of SignedStatement[]
48
- * @returns Subscription object with unsubscribe method
92
+ * Subscribe to statements matching the given topic filter.
93
+ *
94
+ * The callback is invoked once per page of statements. After the initial
95
+ * backfill completes (signaled by `page.isComplete === true`), subsequent
96
+ * pages contain new statements as they're produced.
97
+ *
98
+ * @param filter - Topic match filter (`matchAll` or `matchAny`).
99
+ * @param callback - Called with each `StatementsPage` from the host.
100
+ * @returns Subscription handle with `unsubscribe`.
49
101
  */
50
- subscribe(topics: Uint8Array[], callback: (statements: unknown[]) => void): {
51
- unsubscribe: () => void;
52
- };
102
+ subscribe(filter: StatementTopicFilter, callback: (page: StatementsPage) => void): HostSubscription;
53
103
  /**
54
104
  * Create a proof for a statement using the given account.
55
- * @param accountId - The account ID tuple [ss58Address, chainPrefix] from product-sdk
56
- * @param statement - The unsigned statement
57
- * @returns The proof (signature + signer info)
105
+ *
106
+ * @param accountId - The account ID tuple `[ss58Address, chainPrefix]` from product-sdk.
107
+ * @param statement - The unsigned statement.
108
+ * @returns The proof (signature + signer info, or chain-attestation reference).
58
109
  */
59
110
  createProof(accountId: [string, number], statement: unknown): Promise<StatementProof>;
60
111
  /**
61
112
  * Submit a signed statement to the bulletin chain.
62
- * @param signedStatement - Statement with attached proof
113
+ * @param signedStatement - Statement with attached proof.
63
114
  */
64
115
  submit(signedStatement: unknown): Promise<void>;
65
116
  }
@@ -113,13 +164,17 @@ declare function getStatementStore(): Promise<HostStatementStore | null>;
113
164
  * Shared chain network configuration — single source of truth for
114
165
  * chain-specific endpoints used by multiple packages.
115
166
  */
116
- /** Bulletin chain RPC endpoints by environment. */
167
+ /**
168
+ * Bulletin Chain RPC endpoints per network environment. Only `paseo` is
169
+ * populated today; `polkadot` and `kusama` are reserved for when those
170
+ * Bulletin deployments go live.
171
+ */
117
172
  declare const BULLETIN_RPCS: {
118
173
  readonly paseo: readonly ["wss://paseo-bulletin-rpc.polkadot.io"];
119
174
  readonly polkadot: string[];
120
175
  readonly kusama: string[];
121
176
  };
122
- /** Default bulletin endpoint (first paseo endpoint). */
177
+ /** Default Bulletin Chain endpoint — the first entry under {@link BULLETIN_RPCS}.paseo. */
123
178
  declare const DEFAULT_BULLETIN_ENDPOINT: string;
124
179
 
125
180
  /**
@@ -221,7 +276,10 @@ interface PreimageManager {
221
276
  */
222
277
  declare function getAccountsProvider(): Promise<AccountsProvider | null>;
223
278
  /**
224
- * Account from the host wallet.
279
+ * One of the user's existing wallet accounts, surfaced through the host and
280
+ * identified by its public key and an optional name. Contrast with
281
+ * {@link ProductAccount}, which is also user-controlled but derived by the
282
+ * host for a specific app rather than picked from the user's existing keys.
225
283
  */
226
284
  interface HostAccount {
227
285
  publicKey: Uint8Array;
@@ -268,18 +326,22 @@ interface ResultAsync<T, E> {
268
326
  */
269
327
  interface AccountsProvider {
270
328
  /**
271
- * Get non-product accounts (user's external wallets connected to the host).
329
+ * Get legacy accounts (user's external wallets connected to the host).
330
+ *
331
+ * Renamed from `getNonProductAccounts` in @novasamatech/product-sdk 0.7.
272
332
  *
273
333
  * @returns ResultAsync resolving to array of accounts.
274
334
  */
275
- getNonProductAccounts: () => ResultAsync<HostAccount[], unknown>;
335
+ getLegacyAccounts: () => ResultAsync<HostAccount[], unknown>;
276
336
  /**
277
- * Get a signer for a non-product account.
337
+ * Get a signer for a legacy account.
338
+ *
339
+ * Renamed from `getNonProductAccountSigner` in @novasamatech/product-sdk 0.7.
278
340
  *
279
341
  * @param account - The product account (used for public key lookup).
280
342
  * @returns A PolkadotSigner for signing transactions.
281
343
  */
282
- getNonProductAccountSigner: (account: ProductAccount) => polkadot_api.PolkadotSigner;
344
+ getLegacyAccountSigner: (account: ProductAccount) => polkadot_api.PolkadotSigner;
283
345
  /**
284
346
  * Get an app-scoped product account from the host.
285
347
  *
@@ -334,4 +396,4 @@ interface AccountsProvider {
334
396
  } | (() => void);
335
397
  }
336
398
 
337
- export { type AccountsProvider, BULLETIN_RPCS, type ContextualAlias, DEFAULT_BULLETIN_ENDPOINT, type HostAccount, type HostLocalStorage, type HostStatementStore, type PreimageManager, type ProductAccount, type ResultAsync, type StatementProof, type TruApi, getAccountsProvider, getHostLocalStorage, getHostProvider, getPreimageManager, getStatementStore, getTruApi, isInsideContainer, isInsideContainerSync };
399
+ export { type AccountsProvider, BULLETIN_RPCS, type ContextualAlias, DEFAULT_BULLETIN_ENDPOINT, type HostAccount, type HostLocalStorage, type HostStatementStore, type HostSubscription, type PreimageManager, type ProductAccount, type ResultAsync, type StatementProof, type StatementTopicFilter, type StatementsPage, type TruApi, getAccountsProvider, getHostLocalStorage, getHostProvider, getPreimageManager, getStatementStore, getTruApi, isInsideContainer, isInsideContainerSync };
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
+ import { createLogger } from '@parity/product-sdk-logger';
2
+ export { assertEnumVariant, enumValue, fromHex, isEnumVariant, resultErr, resultOk, toHex, unwrapResultOrThrow } from '@novasamatech/host-api';
3
+
1
4
  // src/container.ts
2
5
  async function isInsideContainer() {
3
6
  if (typeof window === "undefined") return false;
4
7
  try {
5
- const sdk = await import("@novasamatech/product-sdk");
8
+ const sdk = await import('@novasamatech/product-sdk');
6
9
  return sdk.sandboxProvider.isCorrectEnvironment();
7
10
  } catch {
8
11
  return isInsideContainerSync();
@@ -11,7 +14,7 @@ async function isInsideContainer() {
11
14
  async function getHostLocalStorage() {
12
15
  if (!await isInsideContainer()) return null;
13
16
  try {
14
- const sdk = await import("@novasamatech/product-sdk");
17
+ const sdk = await import('@novasamatech/product-sdk');
15
18
  return sdk.hostLocalStorage;
16
19
  } catch {
17
20
  return null;
@@ -19,7 +22,7 @@ async function getHostLocalStorage() {
19
22
  }
20
23
  async function getHostProvider(genesisHash) {
21
24
  try {
22
- const sdk = await import("@novasamatech/product-sdk");
25
+ const sdk = await import('@novasamatech/product-sdk');
23
26
  return sdk.createPapiProvider(genesisHash);
24
27
  } catch {
25
28
  return null;
@@ -39,76 +42,12 @@ function isInsideContainerSync() {
39
42
  }
40
43
  async function getStatementStore() {
41
44
  try {
42
- const sdk = await import("@novasamatech/product-sdk");
45
+ const sdk = await import('@novasamatech/product-sdk');
43
46
  return sdk.createStatementStore();
44
47
  } catch {
45
48
  return null;
46
49
  }
47
50
  }
48
- if (void 0) {
49
- const { test, expect, vi } = void 0;
50
- test("returns false in Node environment (no window)", async () => {
51
- expect(await isInsideContainer()).toBe(false);
52
- });
53
- test("manualDetection returns true for __HOST_WEBVIEW_MARK__", async () => {
54
- const fakeWindow = {
55
- top: null,
56
- __HOST_WEBVIEW_MARK__: true
57
- };
58
- vi.stubGlobal("window", fakeWindow);
59
- const result = await isInsideContainer();
60
- expect(result).toBe(true);
61
- vi.unstubAllGlobals();
62
- });
63
- test("manualDetection returns true for __HOST_API_PORT__", async () => {
64
- const fakeWindow = {
65
- top: null,
66
- __HOST_API_PORT__: 12345
67
- };
68
- vi.stubGlobal("window", fakeWindow);
69
- const result = await isInsideContainer();
70
- expect(result).toBe(true);
71
- vi.unstubAllGlobals();
72
- });
73
- test("manualDetection returns false when no signals present", async () => {
74
- const fakeWindow = { top: null };
75
- Object.defineProperty(fakeWindow, "top", { get: () => fakeWindow });
76
- vi.stubGlobal("window", fakeWindow);
77
- const result = await isInsideContainer();
78
- expect(result).toBe(false);
79
- vi.unstubAllGlobals();
80
- });
81
- test("manualDetection returns true for cross-origin iframe", async () => {
82
- const fakeWindow = {};
83
- Object.defineProperty(fakeWindow, "top", {
84
- get: () => {
85
- throw new DOMException("cross-origin");
86
- }
87
- });
88
- vi.stubGlobal("window", fakeWindow);
89
- const result = await isInsideContainer();
90
- expect(result).toBe(true);
91
- vi.unstubAllGlobals();
92
- });
93
- test("manualDetection returns true when window !== window.top (iframe)", async () => {
94
- const fakeWindow = { top: {} };
95
- vi.stubGlobal("window", fakeWindow);
96
- const result = await isInsideContainer();
97
- expect(result).toBe(true);
98
- vi.unstubAllGlobals();
99
- });
100
- test("getHostLocalStorage returns null outside container", async () => {
101
- expect(await getHostLocalStorage()).toBeNull();
102
- });
103
- test("getHostProvider returns null when product-sdk unavailable", async () => {
104
- const result = await getHostProvider("0xabc");
105
- expect(result).toBeNull();
106
- });
107
- test("getStatementStore returns null when product-sdk unavailable", async () => {
108
- const result = await getStatementStore();
109
- expect(result).toBeNull();
110
- });
111
- }
112
51
 
113
52
  // src/chains.ts
114
53
  var BULLETIN_RPCS = {
@@ -117,41 +56,12 @@ var BULLETIN_RPCS = {
117
56
  kusama: []
118
57
  };
119
58
  var DEFAULT_BULLETIN_ENDPOINT = BULLETIN_RPCS.paseo[0];
120
- if (void 0) {
121
- const { describe, test, expect } = void 0;
122
- describe("chains config", () => {
123
- test("BULLETIN_RPCS has paseo endpoint", () => {
124
- expect(BULLETIN_RPCS.paseo.length).toBeGreaterThan(0);
125
- expect(BULLETIN_RPCS.paseo[0]).toMatch(/^wss:\/\//);
126
- });
127
- test("BULLETIN_RPCS polkadot and kusama are empty until live", () => {
128
- expect(BULLETIN_RPCS.polkadot).toEqual([]);
129
- expect(BULLETIN_RPCS.kusama).toEqual([]);
130
- });
131
- test("DEFAULT_BULLETIN_ENDPOINT matches first paseo endpoint", () => {
132
- expect(DEFAULT_BULLETIN_ENDPOINT).toBe(BULLETIN_RPCS.paseo[0]);
133
- });
134
- });
135
- }
136
-
137
- // src/truapi.ts
138
- import { createLogger } from "@parity/product-sdk-logger";
139
- import {
140
- enumValue,
141
- isEnumVariant,
142
- assertEnumVariant,
143
- unwrapResultOrThrow,
144
- resultOk,
145
- resultErr,
146
- toHex,
147
- fromHex
148
- } from "@novasamatech/host-api";
149
59
  var log = createLogger("host");
150
60
  var cachedTruApi = null;
151
61
  async function getTruApi() {
152
62
  if (cachedTruApi) return cachedTruApi;
153
63
  try {
154
- const sdk = await import("@novasamatech/product-sdk");
64
+ const sdk = await import('@novasamatech/product-sdk');
155
65
  cachedTruApi = sdk.hostApi;
156
66
  log.debug("TruAPI loaded");
157
67
  return cachedTruApi;
@@ -162,7 +72,7 @@ async function getTruApi() {
162
72
  }
163
73
  async function getPreimageManager() {
164
74
  try {
165
- const sdk = await import("@novasamatech/product-sdk");
75
+ const sdk = await import('@novasamatech/product-sdk');
166
76
  return sdk.preimageManager;
167
77
  } catch {
168
78
  return null;
@@ -170,50 +80,13 @@ async function getPreimageManager() {
170
80
  }
171
81
  async function getAccountsProvider() {
172
82
  try {
173
- const sdk = await import("@novasamatech/product-sdk");
83
+ const sdk = await import('@novasamatech/product-sdk');
174
84
  return sdk.createAccountsProvider();
175
85
  } catch {
176
86
  return null;
177
87
  }
178
88
  }
179
- if (void 0) {
180
- const { test, expect } = void 0;
181
- test("getTruApi returns TruApi when SDK is available", async () => {
182
- cachedTruApi = null;
183
- const api = await getTruApi();
184
- expect(api === null || typeof api === "object").toBe(true);
185
- });
186
- test("getPreimageManager returns manager when SDK is available", async () => {
187
- const manager = await getPreimageManager();
188
- expect(manager === null || typeof manager === "object").toBe(true);
189
- });
190
- test("getAccountsProvider returns provider when SDK is available", async () => {
191
- const provider = await getAccountsProvider();
192
- expect(provider === null || typeof provider === "object").toBe(true);
193
- });
194
- test("enumValue is exported", async () => {
195
- const { enumValue: enumValue2 } = await null;
196
- expect(typeof enumValue2).toBe("function");
197
- });
198
- }
199
- export {
200
- BULLETIN_RPCS,
201
- DEFAULT_BULLETIN_ENDPOINT,
202
- assertEnumVariant,
203
- enumValue,
204
- fromHex,
205
- getAccountsProvider,
206
- getHostLocalStorage,
207
- getHostProvider,
208
- getPreimageManager,
209
- getStatementStore,
210
- getTruApi,
211
- isEnumVariant,
212
- isInsideContainer,
213
- isInsideContainerSync,
214
- resultErr,
215
- resultOk,
216
- toHex,
217
- unwrapResultOrThrow
218
- };
89
+
90
+ export { BULLETIN_RPCS, DEFAULT_BULLETIN_ENDPOINT, getAccountsProvider, getHostLocalStorage, getHostProvider, getPreimageManager, getStatementStore, getTruApi, isInsideContainer, isInsideContainerSync };
91
+ //# sourceMappingURL=index.js.map
219
92
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/container.ts","../src/chains.ts","../src/truapi.ts"],"sourcesContent":["import type { JsonRpcProvider } from \"polkadot-api/ws-provider/web\";\n\nimport type { HostLocalStorage, HostStatementStore } from \"./types.js\";\n\n/**\n * Detect if running inside a Host container (Polkadot Browser / Polkadot Desktop).\n *\n * The SDK is designed to run exclusively inside a host container. This function\n * is primarily useful for early validation or informational purposes.\n *\n * Uses product-sdk's sandboxProvider as primary detection.\n * Falls back to manual signal checks when product-sdk is not installed.\n */\nexport async function isInsideContainer(): Promise<boolean> {\n if (typeof window === \"undefined\") return false;\n\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.sandboxProvider.isCorrectEnvironment();\n } catch {\n return isInsideContainerSync();\n }\n}\n\n/**\n * Get the Host API localStorage instance when running inside a container.\n * Returns null outside a container or when product-sdk is unavailable.\n */\nexport async function getHostLocalStorage(): Promise<HostLocalStorage | null> {\n if (!(await isInsideContainer())) return null;\n\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.hostLocalStorage as HostLocalStorage;\n } catch {\n return null;\n }\n}\n\n/**\n * Get a PAPI-compatible JSON-RPC provider that routes through the host connection.\n *\n * When running inside a Polkadot container, this wraps the chain connection via the\n * host's `createPapiProvider`, enabling shared connections and efficient routing.\n * Returns `null` when `@novasamatech/product-sdk` is unavailable.\n *\n * @param genesisHash - Genesis hash of the target chain (`0x`-prefixed hex string).\n * @returns A host-routed `JsonRpcProvider`, or `null` if unavailable.\n */\nexport async function getHostProvider(genesisHash: `0x${string}`): Promise<JsonRpcProvider | null> {\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.createPapiProvider(genesisHash);\n } catch {\n return null;\n }\n}\n\n/**\n * Synchronous container detection — fast heuristic check without product-sdk.\n *\n * Checks for iframe, webview marker, and host message port signals.\n * Use this when you need a quick sync check (e.g., in hot code paths).\n * For full detection including product-sdk, use {@link isInsideContainer} (async).\n */\nexport function isInsideContainerSync(): boolean {\n if (typeof window === \"undefined\") return false;\n\n const win = window as unknown as Record<string, unknown>;\n\n // Iframe detection (polkadot.com browser)\n try {\n if (window !== window.top) return true;\n } catch {\n // Cross-origin iframe — likely inside a container\n return true;\n }\n\n // Webview detection (Polkadot Desktop)\n if (win.__HOST_WEBVIEW_MARK__ === true) return true;\n\n // Desktop message-passing API\n if (win.__HOST_API_PORT__ != null) return true;\n\n return false;\n}\n\n/**\n * Get the host API statement store when running inside a container.\n *\n * Returns a statement store with `subscribe`, `createProof`, and `submit` methods\n * that communicate through the host's native binary protocol — bypassing JSON-RPC\n * entirely. Returns `null` when `@novasamatech/product-sdk` is unavailable.\n *\n * @returns The host statement store, or `null` if unavailable.\n */\nexport async function getStatementStore(): Promise<HostStatementStore | null> {\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.createStatementStore() as HostStatementStore;\n } catch {\n return null;\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect, vi } = import.meta.vitest;\n\n test(\"returns false in Node environment (no window)\", async () => {\n expect(await isInsideContainer()).toBe(false);\n });\n\n test(\"manualDetection returns true for __HOST_WEBVIEW_MARK__\", async () => {\n const fakeWindow = {\n top: null,\n __HOST_WEBVIEW_MARK__: true,\n };\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true for __HOST_API_PORT__\", async () => {\n const fakeWindow = {\n top: null,\n __HOST_API_PORT__: 12345,\n };\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns false when no signals present\", async () => {\n const fakeWindow = { top: null };\n Object.defineProperty(fakeWindow, \"top\", { get: () => fakeWindow });\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(false);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true for cross-origin iframe\", async () => {\n const fakeWindow = {};\n Object.defineProperty(fakeWindow, \"top\", {\n get: () => {\n throw new DOMException(\"cross-origin\");\n },\n });\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true when window !== window.top (iframe)\", async () => {\n const fakeWindow = { top: {} }; // top is a different object\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"getHostLocalStorage returns null outside container\", async () => {\n expect(await getHostLocalStorage()).toBeNull();\n });\n\n test(\"getHostProvider returns null when product-sdk unavailable\", async () => {\n const result = await getHostProvider(\"0xabc\");\n expect(result).toBeNull();\n });\n\n test(\"getStatementStore returns null when product-sdk unavailable\", async () => {\n const result = await getStatementStore();\n expect(result).toBeNull();\n });\n}\n","/**\n * Shared chain network configuration — single source of truth for\n * chain-specific endpoints used by multiple packages.\n */\n\n/** Bulletin chain RPC endpoints by environment. */\nexport const BULLETIN_RPCS = {\n paseo: [\"wss://paseo-bulletin-rpc.polkadot.io\"],\n polkadot: [] as string[],\n kusama: [] as string[],\n} as const;\n\n/** Default bulletin endpoint (first paseo endpoint). */\nexport const DEFAULT_BULLETIN_ENDPOINT: string = BULLETIN_RPCS.paseo[0];\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe(\"chains config\", () => {\n test(\"BULLETIN_RPCS has paseo endpoint\", () => {\n expect(BULLETIN_RPCS.paseo.length).toBeGreaterThan(0);\n expect(BULLETIN_RPCS.paseo[0]).toMatch(/^wss:\\/\\//);\n });\n\n test(\"BULLETIN_RPCS polkadot and kusama are empty until live\", () => {\n expect(BULLETIN_RPCS.polkadot).toEqual([]);\n expect(BULLETIN_RPCS.kusama).toEqual([]);\n });\n\n test(\"DEFAULT_BULLETIN_ENDPOINT matches first paseo endpoint\", () => {\n expect(DEFAULT_BULLETIN_ENDPOINT).toBe(BULLETIN_RPCS.paseo[0]);\n });\n });\n}\n","/**\n * TruAPI - the protocol for communicating between apps and the Polkadot host container.\n *\n * This module centralizes access to @novasamatech/product-sdk and @novasamatech/host-api,\n * allowing other @parity/product-sdk-* packages to import from here rather than depending\n * directly on novasama packages.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nconst log = createLogger(\"host\");\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers from @novasamatech/host-api (re-exported from @novasamatech/scale)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport {\n /**\n * Construct an enum variant for TruAPI calls.\n *\n * @example\n * ```ts\n * import { enumValue, getTruApi } from \"@parity/product-sdk-host\";\n *\n * const truApi = await getTruApi();\n * if (truApi) {\n * await truApi.permission([enumValue(\"ChainSubmit\")]);\n * }\n * ```\n */\n enumValue,\n /**\n * Check if a value is a specific enum variant.\n */\n isEnumVariant,\n /**\n * Assert that a value is a specific enum variant, throwing if not.\n */\n assertEnumVariant,\n /**\n * Unwrap a Result, throwing on error.\n */\n unwrapResultOrThrow,\n /**\n * Create an Ok result.\n */\n resultOk,\n /**\n * Create an Err result.\n */\n resultErr,\n /**\n * Convert bytes to hex string.\n */\n toHex,\n /**\n * Convert hex string to bytes.\n */\n fromHex,\n} from \"@novasamatech/host-api\";\n\nexport type { HexString } from \"@novasamatech/host-api\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TruAPI accessor\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The TruApi type - provides low-level methods for communicating with the host.\n *\n * Methods include:\n * - `navigateTo(url)` — Navigate to a URL within the host\n * - `permission(permissions)` — Request permissions from the host\n * - `localStorageRead/Write/Clear` — Host-backed storage\n * - `sign(payload)` — Request transaction signing\n * - `deriveEntropy(context)` — Derive deterministic entropy\n * - `themeSubscribe()` — Subscribe to host theme changes\n * - And many more...\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type TruApi = any;\n\n/** Cached TruApi instance */\nlet cachedTruApi: TruApi | null = null;\n\n/**\n * Get the TruAPI instance for direct low-level access.\n *\n * Returns the `hostApi` object from `@novasamatech/product-sdk` which provides\n * methods for communicating directly with the host container. Returns `null`\n * when running outside a container or when the SDK is unavailable.\n *\n * For most use cases, prefer the higher-level functions like `getHostLocalStorage()`,\n * `getHostProvider()`, etc. Use this when you need direct access to host methods\n * like `navigateTo()`, `permission()`, or `deriveEntropy()`.\n *\n * @example\n * ```ts\n * import { getTruApi, enumValue } from \"@parity/product-sdk-host\";\n *\n * const truApi = await getTruApi();\n * if (truApi) {\n * // Request permission\n * const result = await truApi.permission([enumValue(\"ChainSubmit\")]);\n *\n * // Navigate to a URL\n * await truApi.navigateTo(\"polkadot://settings\");\n *\n * // Subscribe to theme changes\n * const sub = truApi.themeSubscribe(undefined, (theme) => {\n * console.log(\"Theme changed:\", theme);\n * });\n * }\n * ```\n *\n * @returns The TruAPI instance, or `null` if unavailable.\n */\nexport async function getTruApi(): Promise<TruApi | null> {\n if (cachedTruApi) return cachedTruApi;\n\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n cachedTruApi = sdk.hostApi;\n log.debug(\"TruAPI loaded\");\n return cachedTruApi;\n } catch {\n log.debug(\"TruAPI unavailable (not in container or SDK not installed)\");\n return null;\n }\n}\n\n/**\n * Get the preimage manager for bulletin chain operations.\n *\n * The preimage manager handles uploading and looking up preimages (arbitrary data)\n * on the bulletin chain through the host's optimized path.\n *\n * @returns The preimage manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getPreimageManager } from \"@parity/product-sdk-host\";\n *\n * const manager = await getPreimageManager();\n * if (manager) {\n * // Submit a preimage\n * const key = await manager.submit(new Uint8Array([1, 2, 3]));\n *\n * // Look up a preimage\n * const sub = manager.lookup(key, (data) => {\n * if (data) console.log(\"Found:\", data);\n * });\n * }\n * ```\n */\nexport async function getPreimageManager(): Promise<PreimageManager | null> {\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.preimageManager;\n } catch {\n return null;\n }\n}\n\n/**\n * Preimage manager interface for bulletin chain operations.\n */\nexport interface PreimageManager {\n /**\n * Submit a preimage to the bulletin chain.\n * @param data - The data to submit.\n * @returns The preimage key (hex string).\n */\n submit(data: Uint8Array): Promise<string>;\n\n /**\n * Look up a preimage by key.\n * @param key - The preimage key (hex string).\n * @param callback - Called with the data when found, or null if not yet available.\n * @returns Subscription handle with unsubscribe method.\n */\n lookup(\n key: string,\n callback: (preimage: Uint8Array | null) => void,\n ): { unsubscribe: () => void; onInterrupt: (cb: () => void) => () => void };\n}\n\n/**\n * Get the accounts provider for managing host accounts.\n *\n * @returns The accounts provider, or `null` if unavailable.\n */\nexport async function getAccountsProvider(): Promise<AccountsProvider | null> {\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.createAccountsProvider() as unknown as AccountsProvider;\n } catch {\n return null;\n }\n}\n\n/**\n * Account from the host wallet.\n */\nexport interface HostAccount {\n publicKey: Uint8Array;\n name?: string;\n}\n\n/**\n * A product account — an app-scoped derived account managed by the host wallet.\n *\n * The host derives a unique keypair for each app (identified by `dotNsIdentifier`)\n * so apps get their own account that the user controls but is scoped to the app.\n */\nexport interface ProductAccount {\n /** App identifier (e.g., \"mark3t.dot\"). */\n dotNsIdentifier: string;\n /** Derivation index within the app scope. Default: 0 */\n derivationIndex: number;\n /** Raw public key (32 bytes). */\n publicKey: Uint8Array;\n}\n\n/**\n * A contextual alias obtained from Ring VRF.\n *\n * Proves account membership in a ring without revealing which account.\n */\nexport interface ContextualAlias {\n /** Ring context (32 bytes). */\n context: Uint8Array;\n /** The Ring VRF alias bytes. */\n alias: Uint8Array;\n}\n\n/**\n * Neverthrow-style ResultAsync returned by product-sdk methods.\n *\n * Use `.match(onOk, onErr)` to handle success/error cases.\n */\nexport interface ResultAsync<T, E> {\n match: <A, B = A>(ok: (t: T) => A, err: (e: E) => B) => Promise<A | B>;\n}\n\n/**\n * Accounts provider interface from @novasamatech/product-sdk.\n *\n * Provides methods for accessing host wallet accounts, product accounts,\n * and Ring VRF operations.\n */\nexport interface AccountsProvider {\n /**\n * Get non-product accounts (user's external wallets connected to the host).\n *\n * @returns ResultAsync resolving to array of accounts.\n */\n getNonProductAccounts: () => ResultAsync<HostAccount[], unknown>;\n\n /**\n * Get a signer for a non-product account.\n *\n * @param account - The product account (used for public key lookup).\n * @returns A PolkadotSigner for signing transactions.\n */\n getNonProductAccountSigner: (account: ProductAccount) => import(\"polkadot-api\").PolkadotSigner;\n\n /**\n * Get an app-scoped product account from the host.\n *\n * Product accounts are derived by the host wallet for each app, identified\n * by `dotNsIdentifier` (e.g., \"mark3t.dot\"). The user controls these accounts\n * but they are scoped to the requesting app.\n *\n * @param dotNsIdentifier - App identifier (e.g., \"mark3t.dot\").\n * @param derivationIndex - Derivation index within the app scope. Default: 0\n * @returns ResultAsync resolving to the account.\n */\n getProductAccount: (\n dotNsIdentifier: string,\n derivationIndex?: number,\n ) => ResultAsync<HostAccount, unknown>;\n\n /**\n * Get a signer for a product account.\n *\n * @param account - The product account.\n * @returns A PolkadotSigner for signing transactions.\n */\n getProductAccountSigner: (account: ProductAccount) => import(\"polkadot-api\").PolkadotSigner;\n\n /**\n * Get a contextual alias for a product account via Ring VRF.\n *\n * Aliases prove account membership in a ring without revealing which\n * account produced the alias.\n *\n * @param dotNsIdentifier - App identifier.\n * @param derivationIndex - Derivation index. Default: 0\n * @returns ResultAsync resolving to the contextual alias.\n */\n getProductAccountAlias: (\n dotNsIdentifier: string,\n derivationIndex?: number,\n ) => ResultAsync<ContextualAlias, unknown>;\n\n /**\n * Create a Ring VRF proof for anonymous operations.\n *\n * Proves that the signer is a member of the ring at the given location\n * without revealing which member.\n *\n * @param dotNsIdentifier - App identifier.\n * @param derivationIndex - Derivation index.\n * @param location - Ring location on-chain.\n * @param message - Message to sign.\n * @returns ResultAsync resolving to the proof bytes.\n */\n createRingVRFProof: (\n dotNsIdentifier: string,\n derivationIndex: number,\n location: unknown,\n message: Uint8Array,\n ) => ResultAsync<Uint8Array, unknown>;\n\n /**\n * Subscribe to account connection status changes.\n *\n * @param callback - Called with status string (\"connected\" | \"disconnected\").\n * @returns Unsubscribe handle.\n */\n subscribeAccountConnectionStatus: (\n callback: (status: string) => void,\n ) => { unsubscribe: () => void } | (() => void);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Tests\n// ─────────────────────────────────────────────────────────────────────────────\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getTruApi returns TruApi when SDK is available\", async () => {\n // Reset cache for test\n cachedTruApi = null;\n const api = await getTruApi();\n // In dev/test mode, product-sdk is installed\n expect(api === null || typeof api === \"object\").toBe(true);\n });\n\n test(\"getPreimageManager returns manager when SDK is available\", async () => {\n const manager = await getPreimageManager();\n // In dev/test mode, product-sdk is installed\n expect(manager === null || typeof manager === \"object\").toBe(true);\n });\n\n test(\"getAccountsProvider returns provider when SDK is available\", async () => {\n // In dev/test mode, product-sdk is installed, so this returns a provider\n const provider = await getAccountsProvider();\n // Just verify it returns something (null when SDK unavailable, provider when available)\n expect(provider === null || typeof provider === \"object\").toBe(true);\n });\n\n test(\"enumValue is exported\", async () => {\n const { enumValue } = await import(\"./truapi.js\");\n expect(typeof enumValue).toBe(\"function\");\n });\n}\n"],"mappings":";AAaA,eAAsB,oBAAsC;AACxD,MAAI,OAAO,WAAW,YAAa,QAAO;AAE1C,MAAI;AACA,UAAM,MAAM,MAAM,OAAO,2BAA2B;AACpD,WAAO,IAAI,gBAAgB,qBAAqB;AAAA,EACpD,QAAQ;AACJ,WAAO,sBAAsB;AAAA,EACjC;AACJ;AAMA,eAAsB,sBAAwD;AAC1E,MAAI,CAAE,MAAM,kBAAkB,EAAI,QAAO;AAEzC,MAAI;AACA,UAAM,MAAM,MAAM,OAAO,2BAA2B;AACpD,WAAO,IAAI;AAAA,EACf,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAYA,eAAsB,gBAAgB,aAA6D;AAC/F,MAAI;AACA,UAAM,MAAM,MAAM,OAAO,2BAA2B;AACpD,WAAO,IAAI,mBAAmB,WAAW;AAAA,EAC7C,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AASO,SAAS,wBAAiC;AAC7C,MAAI,OAAO,WAAW,YAAa,QAAO;AAE1C,QAAM,MAAM;AAGZ,MAAI;AACA,QAAI,WAAW,OAAO,IAAK,QAAO;AAAA,EACtC,QAAQ;AAEJ,WAAO;AAAA,EACX;AAGA,MAAI,IAAI,0BAA0B,KAAM,QAAO;AAG/C,MAAI,IAAI,qBAAqB,KAAM,QAAO;AAE1C,SAAO;AACX;AAWA,eAAsB,oBAAwD;AAC1E,MAAI;AACA,UAAM,MAAM,MAAM,OAAO,2BAA2B;AACpD,WAAO,IAAI,qBAAqB;AAAA,EACpC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEA,IAAI,QAAoB;AACpB,QAAM,EAAE,MAAM,QAAQ,GAAG,IAAI;AAE7B,OAAK,iDAAiD,YAAY;AAC9D,WAAO,MAAM,kBAAkB,CAAC,EAAE,KAAK,KAAK;AAAA,EAChD,CAAC;AAED,OAAK,0DAA0D,YAAY;AACvE,UAAM,aAAa;AAAA,MACf,KAAK;AAAA,MACL,uBAAuB;AAAA,IAC3B;AACA,OAAG,WAAW,UAAU,UAAU;AAClC,UAAM,SAAS,MAAM,kBAAkB;AACvC,WAAO,MAAM,EAAE,KAAK,IAAI;AACxB,OAAG,iBAAiB;AAAA,EACxB,CAAC;AAED,OAAK,sDAAsD,YAAY;AACnE,UAAM,aAAa;AAAA,MACf,KAAK;AAAA,MACL,mBAAmB;AAAA,IACvB;AACA,OAAG,WAAW,UAAU,UAAU;AAClC,UAAM,SAAS,MAAM,kBAAkB;AACvC,WAAO,MAAM,EAAE,KAAK,IAAI;AACxB,OAAG,iBAAiB;AAAA,EACxB,CAAC;AAED,OAAK,yDAAyD,YAAY;AACtE,UAAM,aAAa,EAAE,KAAK,KAAK;AAC/B,WAAO,eAAe,YAAY,OAAO,EAAE,KAAK,MAAM,WAAW,CAAC;AAClE,OAAG,WAAW,UAAU,UAAU;AAClC,UAAM,SAAS,MAAM,kBAAkB;AACvC,WAAO,MAAM,EAAE,KAAK,KAAK;AACzB,OAAG,iBAAiB;AAAA,EACxB,CAAC;AAED,OAAK,wDAAwD,YAAY;AACrE,UAAM,aAAa,CAAC;AACpB,WAAO,eAAe,YAAY,OAAO;AAAA,MACrC,KAAK,MAAM;AACP,cAAM,IAAI,aAAa,cAAc;AAAA,MACzC;AAAA,IACJ,CAAC;AACD,OAAG,WAAW,UAAU,UAAU;AAClC,UAAM,SAAS,MAAM,kBAAkB;AACvC,WAAO,MAAM,EAAE,KAAK,IAAI;AACxB,OAAG,iBAAiB;AAAA,EACxB,CAAC;AAED,OAAK,oEAAoE,YAAY;AACjF,UAAM,aAAa,EAAE,KAAK,CAAC,EAAE;AAC7B,OAAG,WAAW,UAAU,UAAU;AAClC,UAAM,SAAS,MAAM,kBAAkB;AACvC,WAAO,MAAM,EAAE,KAAK,IAAI;AACxB,OAAG,iBAAiB;AAAA,EACxB,CAAC;AAED,OAAK,sDAAsD,YAAY;AACnE,WAAO,MAAM,oBAAoB,CAAC,EAAE,SAAS;AAAA,EACjD,CAAC;AAED,OAAK,6DAA6D,YAAY;AAC1E,UAAM,SAAS,MAAM,gBAAgB,OAAO;AAC5C,WAAO,MAAM,EAAE,SAAS;AAAA,EAC5B,CAAC;AAED,OAAK,+DAA+D,YAAY;AAC5E,UAAM,SAAS,MAAM,kBAAkB;AACvC,WAAO,MAAM,EAAE,SAAS;AAAA,EAC5B,CAAC;AACL;;;AC3KO,IAAM,gBAAgB;AAAA,EACzB,OAAO,CAAC,sCAAsC;AAAA,EAC9C,UAAU,CAAC;AAAA,EACX,QAAQ,CAAC;AACb;AAGO,IAAM,4BAAoC,cAAc,MAAM,CAAC;AAEtE,IAAI,QAAoB;AACpB,QAAM,EAAE,UAAU,MAAM,OAAO,IAAI;AAEnC,WAAS,iBAAiB,MAAM;AAC5B,SAAK,oCAAoC,MAAM;AAC3C,aAAO,cAAc,MAAM,MAAM,EAAE,gBAAgB,CAAC;AACpD,aAAO,cAAc,MAAM,CAAC,CAAC,EAAE,QAAQ,WAAW;AAAA,IACtD,CAAC;AAED,SAAK,0DAA0D,MAAM;AACjE,aAAO,cAAc,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACzC,aAAO,cAAc,MAAM,EAAE,QAAQ,CAAC,CAAC;AAAA,IAC3C,CAAC;AAED,SAAK,0DAA0D,MAAM;AACjE,aAAO,yBAAyB,EAAE,KAAK,cAAc,MAAM,CAAC,CAAC;AAAA,IACjE,CAAC;AAAA,EACL,CAAC;AACL;;;ACvBA,SAAS,oBAAoB;AAQ7B;AAAA,EAcI;AAAA,EAIA;AAAA,EAIA;AAAA,EAIA;AAAA,EAIA;AAAA,EAIA;AAAA,EAIA;AAAA,EAIA;AAAA,OACG;AAjDP,IAAM,MAAM,aAAa,MAAM;AAyE/B,IAAI,eAA8B;AAkClC,eAAsB,YAAoC;AACtD,MAAI,aAAc,QAAO;AAEzB,MAAI;AACA,UAAM,MAAM,MAAM,OAAO,2BAA2B;AACpD,mBAAe,IAAI;AACnB,QAAI,MAAM,eAAe;AACzB,WAAO;AAAA,EACX,QAAQ;AACJ,QAAI,MAAM,4DAA4D;AACtE,WAAO;AAAA,EACX;AACJ;AA0BA,eAAsB,qBAAsD;AACxE,MAAI;AACA,UAAM,MAAM,MAAM,OAAO,2BAA2B;AACpD,WAAO,IAAI;AAAA,EACf,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AA8BA,eAAsB,sBAAwD;AAC1E,MAAI;AACA,UAAM,MAAM,MAAM,OAAO,2BAA2B;AACpD,WAAO,IAAI,uBAAuB;AAAA,EACtC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AA6IA,IAAI,QAAoB;AACpB,QAAM,EAAE,MAAM,OAAO,IAAI;AAEzB,OAAK,kDAAkD,YAAY;AAE/D,mBAAe;AACf,UAAM,MAAM,MAAM,UAAU;AAE5B,WAAO,QAAQ,QAAQ,OAAO,QAAQ,QAAQ,EAAE,KAAK,IAAI;AAAA,EAC7D,CAAC;AAED,OAAK,4DAA4D,YAAY;AACzE,UAAM,UAAU,MAAM,mBAAmB;AAEzC,WAAO,YAAY,QAAQ,OAAO,YAAY,QAAQ,EAAE,KAAK,IAAI;AAAA,EACrE,CAAC;AAED,OAAK,8DAA8D,YAAY;AAE3E,UAAM,WAAW,MAAM,oBAAoB;AAE3C,WAAO,aAAa,QAAQ,OAAO,aAAa,QAAQ,EAAE,KAAK,IAAI;AAAA,EACvE,CAAC;AAED,OAAK,yBAAyB,YAAY;AACtC,UAAM,EAAE,WAAAA,WAAU,IAAI,MAAa;AACnC,WAAO,OAAOA,UAAS,EAAE,KAAK,UAAU;AAAA,EAC5C,CAAC;AACL;","names":["enumValue"]}
1
+ {"version":3,"sources":["../src/container.ts","../src/chains.ts","../src/truapi.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { JsonRpcProvider } from \"polkadot-api\";\n\nimport type { HostLocalStorage, HostStatementStore } from \"./types.js\";\n\n/**\n * Detect if running inside a Host container (Polkadot Browser / Polkadot Desktop).\n *\n * The SDK is designed to run exclusively inside a host container. This function\n * is primarily useful for early validation or informational purposes.\n *\n * Uses product-sdk's sandboxProvider as primary detection.\n * Falls back to manual signal checks when product-sdk is not installed.\n */\nexport async function isInsideContainer(): Promise<boolean> {\n if (typeof window === \"undefined\") return false;\n\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.sandboxProvider.isCorrectEnvironment();\n } catch {\n return isInsideContainerSync();\n }\n}\n\n/**\n * Get the Host API localStorage instance when running inside a container.\n * Returns null outside a container or when product-sdk is unavailable.\n */\nexport async function getHostLocalStorage(): Promise<HostLocalStorage | null> {\n if (!(await isInsideContainer())) return null;\n\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.hostLocalStorage as HostLocalStorage;\n } catch {\n return null;\n }\n}\n\n/**\n * Get a PAPI-compatible JSON-RPC provider that routes through the host connection.\n *\n * When running inside a Polkadot container, this wraps the chain connection via the\n * host's `createPapiProvider`, enabling shared connections and efficient routing.\n * Returns `null` when `@novasamatech/product-sdk` is unavailable.\n *\n * @param genesisHash - Genesis hash of the target chain (`0x`-prefixed hex string).\n * @returns A host-routed `JsonRpcProvider`, or `null` if unavailable.\n */\nexport async function getHostProvider(genesisHash: `0x${string}`): Promise<JsonRpcProvider | null> {\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.createPapiProvider(genesisHash);\n } catch {\n return null;\n }\n}\n\n/**\n * Synchronous container detection — fast heuristic check without product-sdk.\n *\n * Checks for iframe, webview marker, and host message port signals.\n * Use this when you need a quick sync check (e.g., in hot code paths).\n * For full detection including product-sdk, use {@link isInsideContainer} (async).\n */\nexport function isInsideContainerSync(): boolean {\n if (typeof window === \"undefined\") return false;\n\n const win = window as unknown as Record<string, unknown>;\n\n // Iframe detection (polkadot.com browser)\n try {\n if (window !== window.top) return true;\n } catch {\n // Cross-origin iframe — likely inside a container\n return true;\n }\n\n // Webview detection (Polkadot Desktop)\n if (win.__HOST_WEBVIEW_MARK__ === true) return true;\n\n // Desktop message-passing API\n if (win.__HOST_API_PORT__ != null) return true;\n\n return false;\n}\n\n/**\n * Get the host API statement store when running inside a container.\n *\n * Returns a statement store with `subscribe`, `createProof`, and `submit` methods\n * that communicate through the host's native binary protocol — bypassing JSON-RPC\n * entirely. Returns `null` when `@novasamatech/product-sdk` is unavailable.\n *\n * @returns The host statement store, or `null` if unavailable.\n */\nexport async function getStatementStore(): Promise<HostStatementStore | null> {\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.createStatementStore() as HostStatementStore;\n } catch {\n return null;\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect, vi } = import.meta.vitest;\n\n test(\"returns false in Node environment (no window)\", async () => {\n expect(await isInsideContainer()).toBe(false);\n });\n\n test(\"manualDetection returns true for __HOST_WEBVIEW_MARK__\", async () => {\n const fakeWindow = {\n top: null,\n __HOST_WEBVIEW_MARK__: true,\n };\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true for __HOST_API_PORT__\", async () => {\n const fakeWindow = {\n top: null,\n __HOST_API_PORT__: 12345,\n };\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns false when no signals present\", async () => {\n const fakeWindow = { top: null };\n Object.defineProperty(fakeWindow, \"top\", { get: () => fakeWindow });\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(false);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true for cross-origin iframe\", async () => {\n const fakeWindow = {};\n Object.defineProperty(fakeWindow, \"top\", {\n get: () => {\n throw new DOMException(\"cross-origin\");\n },\n });\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true when window !== window.top (iframe)\", async () => {\n const fakeWindow = { top: {} }; // top is a different object\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"getHostLocalStorage returns null outside container\", async () => {\n expect(await getHostLocalStorage()).toBeNull();\n });\n\n test(\"getHostProvider returns null when product-sdk unavailable\", async () => {\n const result = await getHostProvider(\"0xabc\");\n expect(result).toBeNull();\n });\n\n test(\"getStatementStore returns null when product-sdk unavailable\", async () => {\n const result = await getStatementStore();\n expect(result).toBeNull();\n });\n}\n","/**\n * Shared chain network configuration — single source of truth for\n * chain-specific endpoints used by multiple packages.\n */\n\n/**\n * Bulletin Chain RPC endpoints per network environment. Only `paseo` is\n * populated today; `polkadot` and `kusama` are reserved for when those\n * Bulletin deployments go live.\n */\nexport const BULLETIN_RPCS = {\n paseo: [\"wss://paseo-bulletin-rpc.polkadot.io\"],\n polkadot: [] as string[],\n kusama: [] as string[],\n} as const;\n\n/** Default Bulletin Chain endpoint — the first entry under {@link BULLETIN_RPCS}.paseo. */\nexport const DEFAULT_BULLETIN_ENDPOINT: string = BULLETIN_RPCS.paseo[0];\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe(\"chains config\", () => {\n test(\"BULLETIN_RPCS has paseo endpoint\", () => {\n expect(BULLETIN_RPCS.paseo.length).toBeGreaterThan(0);\n expect(BULLETIN_RPCS.paseo[0]).toMatch(/^wss:\\/\\//);\n });\n\n test(\"BULLETIN_RPCS polkadot and kusama are empty until live\", () => {\n expect(BULLETIN_RPCS.polkadot).toEqual([]);\n expect(BULLETIN_RPCS.kusama).toEqual([]);\n });\n\n test(\"DEFAULT_BULLETIN_ENDPOINT matches first paseo endpoint\", () => {\n expect(DEFAULT_BULLETIN_ENDPOINT).toBe(BULLETIN_RPCS.paseo[0]);\n });\n });\n}\n","/**\n * TruAPI - the protocol for communicating between apps and the Polkadot host container.\n *\n * This module centralizes access to @novasamatech/product-sdk and @novasamatech/host-api,\n * allowing other @parity/product-sdk-* packages to import from here rather than depending\n * directly on novasama packages.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nconst log = createLogger(\"host\");\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers from @novasamatech/host-api (re-exported from @novasamatech/scale)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport {\n /**\n * Construct an enum variant for TruAPI calls.\n *\n * @example\n * ```ts\n * import { enumValue, getTruApi } from \"@parity/product-sdk-host\";\n *\n * const truApi = await getTruApi();\n * if (truApi) {\n * await truApi.permission([enumValue(\"ChainSubmit\")]);\n * }\n * ```\n */\n enumValue,\n /**\n * Check if a value is a specific enum variant.\n */\n isEnumVariant,\n /**\n * Assert that a value is a specific enum variant, throwing if not.\n */\n assertEnumVariant,\n /**\n * Unwrap a Result, throwing on error.\n */\n unwrapResultOrThrow,\n /**\n * Create an Ok result.\n */\n resultOk,\n /**\n * Create an Err result.\n */\n resultErr,\n /**\n * Convert bytes to hex string.\n */\n toHex,\n /**\n * Convert hex string to bytes.\n */\n fromHex,\n} from \"@novasamatech/host-api\";\n\n/** A `0x`-prefixed hex string (the template literal type ``\\`0x${string}\\``) used by the host API surface for raw byte payloads. Re-exported from `@novasamatech/host-api` so consumers bridging between host APIs and SDK code can reach the host-side type without an additional dependency. */\nexport type { HexString } from \"@novasamatech/host-api\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TruAPI accessor\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The TruApi type - provides low-level methods for communicating with the host.\n *\n * Methods include:\n * - `navigateTo(url)` — Navigate to a URL within the host\n * - `permission(permissions)` — Request permissions from the host\n * - `localStorageRead/Write/Clear` — Host-backed storage\n * - `sign(payload)` — Request transaction signing\n * - `deriveEntropy(context)` — Derive deterministic entropy\n * - `themeSubscribe()` — Subscribe to host theme changes\n * - And many more...\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type TruApi = any;\n\n/** Cached TruApi instance */\nlet cachedTruApi: TruApi | null = null;\n\n/**\n * Get the TruAPI instance for direct low-level access.\n *\n * Returns the `hostApi` object from `@novasamatech/product-sdk` which provides\n * methods for communicating directly with the host container. Returns `null`\n * when running outside a container or when the SDK is unavailable.\n *\n * For most use cases, prefer the higher-level functions like `getHostLocalStorage()`,\n * `getHostProvider()`, etc. Use this when you need direct access to host methods\n * like `navigateTo()`, `permission()`, or `deriveEntropy()`.\n *\n * @example\n * ```ts\n * import { getTruApi, enumValue } from \"@parity/product-sdk-host\";\n *\n * const truApi = await getTruApi();\n * if (truApi) {\n * // Request permission\n * const result = await truApi.permission([enumValue(\"ChainSubmit\")]);\n *\n * // Navigate to a URL\n * await truApi.navigateTo(\"polkadot://settings\");\n *\n * // Subscribe to theme changes\n * const sub = truApi.themeSubscribe(undefined, (theme) => {\n * console.log(\"Theme changed:\", theme);\n * });\n * }\n * ```\n *\n * @returns The TruAPI instance, or `null` if unavailable.\n */\nexport async function getTruApi(): Promise<TruApi | null> {\n if (cachedTruApi) return cachedTruApi;\n\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n cachedTruApi = sdk.hostApi;\n log.debug(\"TruAPI loaded\");\n return cachedTruApi;\n } catch {\n log.debug(\"TruAPI unavailable (not in container or SDK not installed)\");\n return null;\n }\n}\n\n/**\n * Get the preimage manager for bulletin chain operations.\n *\n * The preimage manager handles uploading and looking up preimages (arbitrary data)\n * on the bulletin chain through the host's optimized path.\n *\n * @returns The preimage manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getPreimageManager } from \"@parity/product-sdk-host\";\n *\n * const manager = await getPreimageManager();\n * if (manager) {\n * // Submit a preimage\n * const key = await manager.submit(new Uint8Array([1, 2, 3]));\n *\n * // Look up a preimage\n * const sub = manager.lookup(key, (data) => {\n * if (data) console.log(\"Found:\", data);\n * });\n * }\n * ```\n */\nexport async function getPreimageManager(): Promise<PreimageManager | null> {\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.preimageManager;\n } catch {\n return null;\n }\n}\n\n/**\n * Preimage manager interface for bulletin chain operations.\n */\nexport interface PreimageManager {\n /**\n * Submit a preimage to the bulletin chain.\n * @param data - The data to submit.\n * @returns The preimage key (hex string).\n */\n submit(data: Uint8Array): Promise<string>;\n\n /**\n * Look up a preimage by key.\n * @param key - The preimage key (hex string).\n * @param callback - Called with the data when found, or null if not yet available.\n * @returns Subscription handle with unsubscribe method.\n */\n lookup(\n key: string,\n callback: (preimage: Uint8Array | null) => void,\n ): { unsubscribe: () => void; onInterrupt: (cb: () => void) => () => void };\n}\n\n/**\n * Get the accounts provider for managing host accounts.\n *\n * @returns The accounts provider, or `null` if unavailable.\n */\nexport async function getAccountsProvider(): Promise<AccountsProvider | null> {\n try {\n const sdk = await import(\"@novasamatech/product-sdk\");\n return sdk.createAccountsProvider() as unknown as AccountsProvider;\n } catch {\n return null;\n }\n}\n\n/**\n * One of the user's existing wallet accounts, surfaced through the host and\n * identified by its public key and an optional name. Contrast with\n * {@link ProductAccount}, which is also user-controlled but derived by the\n * host for a specific app rather than picked from the user's existing keys.\n */\nexport interface HostAccount {\n publicKey: Uint8Array;\n name?: string;\n}\n\n/**\n * A product account — an app-scoped derived account managed by the host wallet.\n *\n * The host derives a unique keypair for each app (identified by `dotNsIdentifier`)\n * so apps get their own account that the user controls but is scoped to the app.\n */\nexport interface ProductAccount {\n /** App identifier (e.g., \"mark3t.dot\"). */\n dotNsIdentifier: string;\n /** Derivation index within the app scope. Default: 0 */\n derivationIndex: number;\n /** Raw public key (32 bytes). */\n publicKey: Uint8Array;\n}\n\n/**\n * A contextual alias obtained from Ring VRF.\n *\n * Proves account membership in a ring without revealing which account.\n */\nexport interface ContextualAlias {\n /** Ring context (32 bytes). */\n context: Uint8Array;\n /** The Ring VRF alias bytes. */\n alias: Uint8Array;\n}\n\n/**\n * Neverthrow-style ResultAsync returned by product-sdk methods.\n *\n * Use `.match(onOk, onErr)` to handle success/error cases.\n */\nexport interface ResultAsync<T, E> {\n match: <A, B = A>(ok: (t: T) => A, err: (e: E) => B) => Promise<A | B>;\n}\n\n/**\n * Accounts provider interface from @novasamatech/product-sdk.\n *\n * Provides methods for accessing host wallet accounts, product accounts,\n * and Ring VRF operations.\n */\nexport interface AccountsProvider {\n /**\n * Get legacy accounts (user's external wallets connected to the host).\n *\n * Renamed from `getNonProductAccounts` in @novasamatech/product-sdk 0.7.\n *\n * @returns ResultAsync resolving to array of accounts.\n */\n getLegacyAccounts: () => ResultAsync<HostAccount[], unknown>;\n\n /**\n * Get a signer for a legacy account.\n *\n * Renamed from `getNonProductAccountSigner` in @novasamatech/product-sdk 0.7.\n *\n * @param account - The product account (used for public key lookup).\n * @returns A PolkadotSigner for signing transactions.\n */\n getLegacyAccountSigner: (account: ProductAccount) => import(\"polkadot-api\").PolkadotSigner;\n\n /**\n * Get an app-scoped product account from the host.\n *\n * Product accounts are derived by the host wallet for each app, identified\n * by `dotNsIdentifier` (e.g., \"mark3t.dot\"). The user controls these accounts\n * but they are scoped to the requesting app.\n *\n * @param dotNsIdentifier - App identifier (e.g., \"mark3t.dot\").\n * @param derivationIndex - Derivation index within the app scope. Default: 0\n * @returns ResultAsync resolving to the account.\n */\n getProductAccount: (\n dotNsIdentifier: string,\n derivationIndex?: number,\n ) => ResultAsync<HostAccount, unknown>;\n\n /**\n * Get a signer for a product account.\n *\n * @param account - The product account.\n * @returns A PolkadotSigner for signing transactions.\n */\n getProductAccountSigner: (account: ProductAccount) => import(\"polkadot-api\").PolkadotSigner;\n\n /**\n * Get a contextual alias for a product account via Ring VRF.\n *\n * Aliases prove account membership in a ring without revealing which\n * account produced the alias.\n *\n * @param dotNsIdentifier - App identifier.\n * @param derivationIndex - Derivation index. Default: 0\n * @returns ResultAsync resolving to the contextual alias.\n */\n getProductAccountAlias: (\n dotNsIdentifier: string,\n derivationIndex?: number,\n ) => ResultAsync<ContextualAlias, unknown>;\n\n /**\n * Create a Ring VRF proof for anonymous operations.\n *\n * Proves that the signer is a member of the ring at the given location\n * without revealing which member.\n *\n * @param dotNsIdentifier - App identifier.\n * @param derivationIndex - Derivation index.\n * @param location - Ring location on-chain.\n * @param message - Message to sign.\n * @returns ResultAsync resolving to the proof bytes.\n */\n createRingVRFProof: (\n dotNsIdentifier: string,\n derivationIndex: number,\n location: unknown,\n message: Uint8Array,\n ) => ResultAsync<Uint8Array, unknown>;\n\n /**\n * Subscribe to account connection status changes.\n *\n * @param callback - Called with status string (\"connected\" | \"disconnected\").\n * @returns Unsubscribe handle.\n */\n subscribeAccountConnectionStatus: (\n callback: (status: string) => void,\n ) => { unsubscribe: () => void } | (() => void);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Tests\n// ─────────────────────────────────────────────────────────────────────────────\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getTruApi returns TruApi when SDK is available\", async () => {\n // Reset cache for test\n cachedTruApi = null;\n const api = await getTruApi();\n // In dev/test mode, product-sdk is installed\n expect(api === null || typeof api === \"object\").toBe(true);\n });\n\n test(\"getPreimageManager returns manager when SDK is available\", async () => {\n const manager = await getPreimageManager();\n // In dev/test mode, product-sdk is installed\n expect(manager === null || typeof manager === \"object\").toBe(true);\n });\n\n test(\"getAccountsProvider returns provider when SDK is available\", async () => {\n // In dev/test mode, product-sdk is installed, so this returns a provider\n const provider = await getAccountsProvider();\n // Just verify it returns something (null when SDK unavailable, provider when available)\n expect(provider === null || typeof provider === \"object\").toBe(true);\n });\n\n test(\"enumValue is exported\", async () => {\n const { enumValue } = await import(\"./truapi.js\");\n expect(typeof enumValue).toBe(\"function\");\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parity/product-sdk-host",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Host container detection and storage access for Polkadot Desktop and Mobile environments",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -18,8 +18,8 @@
18
18
  "src"
19
19
  ],
20
20
  "dependencies": {
21
- "polkadot-api": "^1.9.0",
22
- "@parity/product-sdk-logger": "0.1.0"
21
+ "polkadot-api": "^2.1.2",
22
+ "@parity/product-sdk-logger": "0.1.1"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "@novasamatech/product-sdk": ">=0.1.0",
@@ -34,8 +34,8 @@
34
34
  }
35
35
  },
36
36
  "devDependencies": {
37
- "@novasamatech/product-sdk": "^0.6.17",
38
- "@novasamatech/host-api": "^0.7.0",
37
+ "@novasamatech/product-sdk": "^0.7.7",
38
+ "@novasamatech/host-api": "^0.7.7",
39
39
  "typescript": "^5.7.0",
40
40
  "vitest": "^3.0.0"
41
41
  },
package/src/chains.ts CHANGED
@@ -3,14 +3,18 @@
3
3
  * chain-specific endpoints used by multiple packages.
4
4
  */
5
5
 
6
- /** Bulletin chain RPC endpoints by environment. */
6
+ /**
7
+ * Bulletin Chain RPC endpoints per network environment. Only `paseo` is
8
+ * populated today; `polkadot` and `kusama` are reserved for when those
9
+ * Bulletin deployments go live.
10
+ */
7
11
  export const BULLETIN_RPCS = {
8
12
  paseo: ["wss://paseo-bulletin-rpc.polkadot.io"],
9
13
  polkadot: [] as string[],
10
14
  kusama: [] as string[],
11
15
  } as const;
12
16
 
13
- /** Default bulletin endpoint (first paseo endpoint). */
17
+ /** Default Bulletin Chain endpoint — the first entry under {@link BULLETIN_RPCS}.paseo. */
14
18
  export const DEFAULT_BULLETIN_ENDPOINT: string = BULLETIN_RPCS.paseo[0];
15
19
 
16
20
  if (import.meta.vitest) {
package/src/container.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { JsonRpcProvider } from "polkadot-api/ws-provider/web";
1
+ import type { JsonRpcProvider } from "polkadot-api";
2
2
 
3
3
  import type { HostLocalStorage, HostStatementStore } from "./types.js";
4
4
 
package/src/index.ts CHANGED
@@ -1,3 +1,12 @@
1
+ /**
2
+ * @parity/product-sdk-host — Detect and talk to the Polkadot Desktop/Mobile host container.
3
+ *
4
+ * Use `isInsideContainer` to branch behavior when running embedded vs. standalone,
5
+ * and `getHostLocalStorage`, `getHostProvider`, and `getStatementStore` to reach
6
+ * the storage, signer, and statement-store APIs the host injects.
7
+ *
8
+ * @packageDocumentation
9
+ */
1
10
  export {
2
11
  isInsideContainer,
3
12
  isInsideContainerSync,
@@ -5,7 +14,14 @@ export {
5
14
  getHostProvider,
6
15
  getStatementStore,
7
16
  } from "./container.js";
8
- export type { HostLocalStorage, HostStatementStore, StatementProof } from "./types.js";
17
+ export type {
18
+ HostLocalStorage,
19
+ HostStatementStore,
20
+ HostSubscription,
21
+ StatementProof,
22
+ StatementTopicFilter,
23
+ StatementsPage,
24
+ } from "./types.js";
9
25
  export { BULLETIN_RPCS, DEFAULT_BULLETIN_ENDPOINT } from "./chains.js";
10
26
 
11
27
  // TruAPI - re-exports from @novasamatech/product-sdk and @novasamatech/host-api
package/src/truapi.ts CHANGED
@@ -61,6 +61,7 @@ export {
61
61
  fromHex,
62
62
  } from "@novasamatech/host-api";
63
63
 
64
+ /** A `0x`-prefixed hex string (the template literal type ``\`0x${string}\``) used by the host API surface for raw byte payloads. Re-exported from `@novasamatech/host-api` so consumers bridging between host APIs and SDK code can reach the host-side type without an additional dependency. */
64
65
  export type { HexString } from "@novasamatech/host-api";
65
66
 
66
67
  // ─────────────────────────────────────────────────────────────────────────────
@@ -202,7 +203,10 @@ export async function getAccountsProvider(): Promise<AccountsProvider | null> {
202
203
  }
203
204
 
204
205
  /**
205
- * Account from the host wallet.
206
+ * One of the user's existing wallet accounts, surfaced through the host and
207
+ * identified by its public key and an optional name. Contrast with
208
+ * {@link ProductAccount}, which is also user-controlled but derived by the
209
+ * host for a specific app rather than picked from the user's existing keys.
206
210
  */
207
211
  export interface HostAccount {
208
212
  publicKey: Uint8Array;
@@ -253,19 +257,23 @@ export interface ResultAsync<T, E> {
253
257
  */
254
258
  export interface AccountsProvider {
255
259
  /**
256
- * Get non-product accounts (user's external wallets connected to the host).
260
+ * Get legacy accounts (user's external wallets connected to the host).
261
+ *
262
+ * Renamed from `getNonProductAccounts` in @novasamatech/product-sdk 0.7.
257
263
  *
258
264
  * @returns ResultAsync resolving to array of accounts.
259
265
  */
260
- getNonProductAccounts: () => ResultAsync<HostAccount[], unknown>;
266
+ getLegacyAccounts: () => ResultAsync<HostAccount[], unknown>;
261
267
 
262
268
  /**
263
- * Get a signer for a non-product account.
269
+ * Get a signer for a legacy account.
270
+ *
271
+ * Renamed from `getNonProductAccountSigner` in @novasamatech/product-sdk 0.7.
264
272
  *
265
273
  * @param account - The product account (used for public key lookup).
266
274
  * @returns A PolkadotSigner for signing transactions.
267
275
  */
268
- getNonProductAccountSigner: (account: ProductAccount) => import("polkadot-api").PolkadotSigner;
276
+ getLegacyAccountSigner: (account: ProductAccount) => import("polkadot-api").PolkadotSigner;
269
277
 
270
278
  /**
271
279
  * Get an app-scoped product account from the host.
package/src/types.ts CHANGED
@@ -1,4 +1,9 @@
1
- /** Subset of product-sdk's hostLocalStorage that the KV store uses. */
1
+ /**
2
+ * Persistent string and JSON storage exposed by the host container. Most
3
+ * apps reach it indirectly through the Storage package's `KvStore`; reach for
4
+ * it directly via {@link getHostLocalStorage} when you need raw host storage
5
+ * without the KV abstraction.
6
+ */
2
7
  export interface HostLocalStorage {
3
8
  readString(key: string): Promise<string | null>;
4
9
  writeString(key: string, value: string): Promise<void>;
@@ -11,37 +16,85 @@ export interface HostLocalStorage {
11
16
  clear(key: string): Promise<void>;
12
17
  }
13
18
 
14
- /** Proof types returned from createProof (matches product-sdk SCALE-decoded types). */
19
+ /**
20
+ * Cryptographic proof attached to a statement before submission, returned by
21
+ * {@link HostStatementStore.createProof}. Variants cover the supported
22
+ * signature schemes — `Sr25519`, `Ed25519`, `Ecdsa`, and `OnChain` (chain-
23
+ * attestation-based proofs).
24
+ *
25
+ * Mirrors `@novasamatech/product-sdk@0.7`'s proof shape.
26
+ */
15
27
  export type StatementProof =
16
28
  | { tag: "Sr25519"; value: { signature: Uint8Array; signer: Uint8Array } }
17
29
  | { tag: "Ed25519"; value: { signature: Uint8Array; signer: Uint8Array } }
18
- | { tag: "Secp256k1Ecdsa"; value: { signature: Uint8Array; signer: Uint8Array } }
19
- | { tag: "EcdsaRecoverable"; value: { signature: Uint8Array } };
30
+ | { tag: "Ecdsa"; value: { signature: Uint8Array; signer: Uint8Array } }
31
+ | {
32
+ tag: "OnChain";
33
+ value: { who: Uint8Array; blockHash: Uint8Array; event: bigint };
34
+ };
20
35
 
21
- /** The statement store interface provided by the host API via product-sdk. */
36
+ /**
37
+ * Topic-based subscription filter. Mirrors `StatementTopicFilter` from
38
+ * `@novasamatech/product-sdk` — the host delivers statements that match
39
+ * either *all* of the listed topics (`matchAll`) or *any* of them
40
+ * (`matchAny`).
41
+ */
42
+ export type StatementTopicFilter = { matchAll: Uint8Array[] } | { matchAny: Uint8Array[] };
43
+
44
+ /**
45
+ * A page of signed statements delivered by {@link HostStatementStore.subscribe}.
46
+ *
47
+ * Pages arrive sequentially. `isComplete` is `true` on the final page of a
48
+ * subscription's initial backfill; subsequent pages contain new statements
49
+ * as they appear on chain.
50
+ */
51
+ export interface StatementsPage {
52
+ statements: unknown[];
53
+ isComplete: boolean;
54
+ }
55
+
56
+ /** Subscription handle returned by the host. */
57
+ export interface HostSubscription {
58
+ unsubscribe: () => void;
59
+ }
60
+
61
+ /**
62
+ * Statement Store handle exposed by the host container. Provides
63
+ * `subscribe`, `createProof`, and `submit` operations that go through the
64
+ * host's native binary protocol; the `statement-store` package layers a
65
+ * higher-level client on top.
66
+ *
67
+ * Shape matches `@novasamatech/product-sdk@0.7`'s `createStatementStore()`.
68
+ */
22
69
  export interface HostStatementStore {
23
70
  /**
24
- * Subscribe to statements matching the given topics.
25
- * @param topics - Topic filters as Uint8Array[]
26
- * @param callback - Called with batches of SignedStatement[]
27
- * @returns Subscription object with unsubscribe method
71
+ * Subscribe to statements matching the given topic filter.
72
+ *
73
+ * The callback is invoked once per page of statements. After the initial
74
+ * backfill completes (signaled by `page.isComplete === true`), subsequent
75
+ * pages contain new statements as they're produced.
76
+ *
77
+ * @param filter - Topic match filter (`matchAll` or `matchAny`).
78
+ * @param callback - Called with each `StatementsPage` from the host.
79
+ * @returns Subscription handle with `unsubscribe`.
28
80
  */
29
81
  subscribe(
30
- topics: Uint8Array[],
31
- callback: (statements: unknown[]) => void,
32
- ): { unsubscribe: () => void };
82
+ filter: StatementTopicFilter,
83
+ callback: (page: StatementsPage) => void,
84
+ ): HostSubscription;
33
85
 
34
86
  /**
35
87
  * Create a proof for a statement using the given account.
36
- * @param accountId - The account ID tuple [ss58Address, chainPrefix] from product-sdk
37
- * @param statement - The unsigned statement
38
- * @returns The proof (signature + signer info)
88
+ *
89
+ * @param accountId - The account ID tuple `[ss58Address, chainPrefix]` from product-sdk.
90
+ * @param statement - The unsigned statement.
91
+ * @returns The proof (signature + signer info, or chain-attestation reference).
39
92
  */
40
93
  createProof(accountId: [string, number], statement: unknown): Promise<StatementProof>;
41
94
 
42
95
  /**
43
96
  * Submit a signed statement to the bulletin chain.
44
- * @param signedStatement - Statement with attached proof
97
+ * @param signedStatement - Statement with attached proof.
45
98
  */
46
99
  submit(signedStatement: unknown): Promise<void>;
47
100
  }