@parity/product-sdk-host 0.10.3 → 0.12.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.js CHANGED
@@ -1,9 +1,482 @@
1
1
  import { createLogger } from '@parity/product-sdk-logger';
2
- import { enumValue } from '@novasamatech/host-api';
3
- export { PushNotificationError, assertEnumVariant, enumValue, fromHex, isEnumVariant, resultErr, resultOk, toHex, unwrapResultOrThrow } from '@novasamatech/host-api';
2
+ import { isCorrectEnvironment, getClientSync } from '@parity/truapi/sandbox';
3
+ export { isCorrectEnvironment as isInsideContainerSync } from '@parity/truapi/sandbox';
4
+ import { scale } from '@parity/truapi';
5
+ import { unifyMetadata, decAnyMetadata } from '@polkadot-api/substrate-bindings';
6
+ import { AccountId } from 'polkadot-api';
7
+
8
+ // src/errors.ts
9
+ function isHostErrorPayload(error) {
10
+ if (error == null || typeof error !== "object") return false;
11
+ const obj = error;
12
+ return typeof obj.reason === "string" || typeof obj.tag === "string";
13
+ }
14
+ function formatHostError(error) {
15
+ if (error instanceof Error) return error.message;
16
+ if (typeof error === "string") return error;
17
+ if (isHostErrorPayload(error)) {
18
+ if ("tag" in error) {
19
+ if (error.value != null && typeof error.value.reason === "string") {
20
+ return `${error.tag}: ${error.value.reason}`;
21
+ }
22
+ return error.tag;
23
+ }
24
+ return error.reason;
25
+ }
26
+ if (error != null && typeof error === "object" && "message" in error) {
27
+ const message = error.message;
28
+ if (typeof message === "string") return message;
29
+ }
30
+ try {
31
+ return JSON.stringify(error);
32
+ } catch {
33
+ return String(error);
34
+ }
35
+ }
36
+ var HostError = class extends Error {
37
+ constructor(message, options) {
38
+ super(message, options);
39
+ this.name = "HostError";
40
+ }
41
+ };
42
+ var HostUnavailableError = class extends HostError {
43
+ constructor(message = "Host API is not available") {
44
+ super(message);
45
+ this.name = "HostUnavailableError";
46
+ }
47
+ };
48
+ var HostCallFailedError = class extends HostError {
49
+ payload;
50
+ constructor(label, payload) {
51
+ super(`${label}: ${formatHostError(payload)}`, { cause: payload });
52
+ this.name = "HostCallFailedError";
53
+ this.payload = payload;
54
+ }
55
+ };
56
+ function isHostError(error) {
57
+ return error instanceof HostError;
58
+ }
59
+ async function getClient() {
60
+ return getClientSync();
61
+ }
62
+ function subscribeWithInterrupt(observable, onNext) {
63
+ let interruptCallback;
64
+ const sub = observable.subscribe({
65
+ next: onNext,
66
+ error: (reason) => interruptCallback?.(reason),
67
+ complete: () => interruptCallback?.()
68
+ });
69
+ return {
70
+ unsubscribe: () => sub.unsubscribe(),
71
+ onInterrupt: (callback) => {
72
+ interruptCallback = callback;
73
+ return () => {
74
+ if (interruptCallback === callback) interruptCallback = void 0;
75
+ };
76
+ }
77
+ };
78
+ }
79
+
80
+ // src/papi-provider.ts
81
+ var log = createLogger("host:papi");
82
+ var JSON_RPC_INTERNAL_ERROR = -32603;
83
+ var JSON_RPC_METHOD_NOT_FOUND = -32601;
84
+ var STORAGE_TYPE_MAP = {
85
+ value: "Value",
86
+ hash: "Hash",
87
+ closestDescendantMerkleValue: "ClosestDescendantMerkleValue",
88
+ descendantsValues: "DescendantsValues",
89
+ descendantsHashes: "DescendantsHashes"
90
+ };
91
+ function convertRuntimeToJsonRpc(runtime) {
92
+ if (!runtime || typeof runtime !== "object") return null;
93
+ if (runtime.tag === "Valid") {
94
+ const spec = runtime.value;
95
+ const apis = {};
96
+ for (const api of spec.apis) {
97
+ apis[api.name] = api.version;
98
+ }
99
+ return {
100
+ type: "valid",
101
+ spec: {
102
+ specName: spec.specName,
103
+ implName: spec.implName,
104
+ specVersion: spec.specVersion,
105
+ implVersion: spec.implVersion,
106
+ transactionVersion: spec.transactionVersion,
107
+ apis
108
+ }
109
+ };
110
+ }
111
+ if (runtime.tag === "Invalid") {
112
+ return { type: "invalid", error: runtime.value.error };
113
+ }
114
+ return null;
115
+ }
116
+ function convertFollowEventToJsonRpc(item) {
117
+ switch (item.tag) {
118
+ case "Initialized":
119
+ return {
120
+ event: "initialized",
121
+ finalizedBlockHashes: item.value.finalizedBlockHashes,
122
+ finalizedBlockRuntime: convertRuntimeToJsonRpc(item.value.finalizedBlockRuntime)
123
+ };
124
+ case "NewBlock":
125
+ return {
126
+ event: "newBlock",
127
+ blockHash: item.value.blockHash,
128
+ parentBlockHash: item.value.parentBlockHash,
129
+ newRuntime: convertRuntimeToJsonRpc(item.value.newRuntime)
130
+ };
131
+ case "BestBlockChanged":
132
+ return { event: "bestBlockChanged", bestBlockHash: item.value.bestBlockHash };
133
+ case "Finalized":
134
+ return {
135
+ event: "finalized",
136
+ finalizedBlockHashes: item.value.finalizedBlockHashes,
137
+ prunedBlockHashes: item.value.prunedBlockHashes
138
+ };
139
+ case "OperationBodyDone":
140
+ return {
141
+ event: "operationBodyDone",
142
+ operationId: item.value.operationId,
143
+ value: item.value.value
144
+ };
145
+ case "OperationCallDone":
146
+ return {
147
+ event: "operationCallDone",
148
+ operationId: item.value.operationId,
149
+ output: item.value.output
150
+ };
151
+ case "OperationStorageItems":
152
+ return {
153
+ event: "operationStorageItems",
154
+ operationId: item.value.operationId,
155
+ items: item.value.items
156
+ };
157
+ case "OperationStorageDone":
158
+ return { event: "operationStorageDone", operationId: item.value.operationId };
159
+ case "OperationWaitingForContinue":
160
+ return { event: "operationWaitingForContinue", operationId: item.value.operationId };
161
+ case "OperationInaccessible":
162
+ return { event: "operationInaccessible", operationId: item.value.operationId };
163
+ case "OperationError":
164
+ return {
165
+ event: "operationError",
166
+ operationId: item.value.operationId,
167
+ error: item.value.error
168
+ };
169
+ case "Stop":
170
+ return { event: "stop" };
171
+ default: {
172
+ return { event: "stop" };
173
+ }
174
+ }
175
+ }
176
+ function convertStorageType(type) {
177
+ return STORAGE_TYPE_MAP[type] ?? "Value";
178
+ }
179
+ function convertOperationResultToJsonRpc(result) {
180
+ if (result.tag === "Started") {
181
+ return { result: "started", operationId: result.value.operationId };
182
+ }
183
+ return { result: "limitReached" };
184
+ }
185
+ function createHostPapiProvider(client, genesisHash) {
186
+ const chain = client.chain;
187
+ return (onMessage) => {
188
+ const activeFollows = /* @__PURE__ */ new Map();
189
+ const activeBroadcasts = /* @__PURE__ */ new Set();
190
+ let nextSubId = 0;
191
+ const getNextSubId = () => `follow_${nextSubId++}`;
192
+ function sendJsonRpcResponse(id, result) {
193
+ onMessage({ jsonrpc: "2.0", id, result });
194
+ }
195
+ function sendJsonRpcError(id, code, message) {
196
+ onMessage({ jsonrpc: "2.0", id, error: { code, message } });
197
+ }
198
+ function sendFollowEvent(subscription, event) {
199
+ onMessage({
200
+ jsonrpc: "2.0",
201
+ method: "chainHead_v1_followEvent",
202
+ params: { subscription, result: event }
203
+ });
204
+ }
205
+ const hostError = (id) => (error) => sendJsonRpcError(id, JSON_RPC_INTERNAL_ERROR, formatHostError(error));
206
+ function handleMessage(message) {
207
+ const { id, method } = message;
208
+ const params = message.params ?? [];
209
+ switch (method) {
210
+ case "chainHead_v1_follow": {
211
+ const [withRuntime] = params;
212
+ const syntheticSubId = getNextSubId();
213
+ const ref = {};
214
+ ref.handle = subscribeWithInterrupt(
215
+ chain.followHeadSubscribe({ request: { genesisHash, withRuntime } }),
216
+ (item) => {
217
+ if (item.tag === "Stop" && activeFollows.delete(syntheticSubId)) {
218
+ ref.handle?.unsubscribe();
219
+ }
220
+ sendFollowEvent(syntheticSubId, convertFollowEventToJsonRpc(item));
221
+ }
222
+ );
223
+ ref.handle.onInterrupt(() => {
224
+ if (activeFollows.delete(syntheticSubId)) {
225
+ sendFollowEvent(syntheticSubId, { event: "stop" });
226
+ }
227
+ });
228
+ activeFollows.set(syntheticSubId, ref.handle);
229
+ sendJsonRpcResponse(id, syntheticSubId);
230
+ break;
231
+ }
232
+ case "chainHead_v1_unfollow": {
233
+ const [followSubId] = params;
234
+ const follow = activeFollows.get(followSubId);
235
+ if (follow) {
236
+ follow.unsubscribe();
237
+ activeFollows.delete(followSubId);
238
+ }
239
+ sendJsonRpcResponse(id, null);
240
+ break;
241
+ }
242
+ case "chainHead_v1_header": {
243
+ const [followSubscriptionId, hash] = params;
244
+ chain.getHeadHeader({ genesisHash, followSubscriptionId, hash }).match(
245
+ (response) => sendJsonRpcResponse(id, response.header ?? null),
246
+ hostError(id)
247
+ );
248
+ break;
249
+ }
250
+ case "chainHead_v1_body": {
251
+ const [followSubscriptionId, hash] = params;
252
+ chain.getHeadBody({ genesisHash, followSubscriptionId, hash }).match(
253
+ (response) => sendJsonRpcResponse(
254
+ id,
255
+ convertOperationResultToJsonRpc(response.operation)
256
+ ),
257
+ hostError(id)
258
+ );
259
+ break;
260
+ }
261
+ case "chainHead_v1_storage": {
262
+ const [followSubscriptionId, hash, items, childTrie] = params;
263
+ const queryItems = items.map((item) => ({
264
+ key: item.key,
265
+ queryType: convertStorageType(item.type)
266
+ }));
267
+ chain.getHeadStorage({
268
+ genesisHash,
269
+ followSubscriptionId,
270
+ hash,
271
+ items: queryItems,
272
+ // PAPI passes `null` for an absent child trie, but the
273
+ // truapi codec encodes the optional `childTrie` field as
274
+ // `Option<Hex>` — it treats `undefined` as None yet runs
275
+ // the inner Hex codec on `null`, which throws
276
+ // (`null.startsWith`). Coerce `null` → `undefined`.
277
+ childTrie: childTrie ?? void 0
278
+ }).match(
279
+ (response) => sendJsonRpcResponse(
280
+ id,
281
+ convertOperationResultToJsonRpc(response.operation)
282
+ ),
283
+ hostError(id)
284
+ );
285
+ break;
286
+ }
287
+ case "chainHead_v1_call": {
288
+ const [followSubscriptionId, hash, fn, callParameters] = params;
289
+ chain.callHead({
290
+ genesisHash,
291
+ followSubscriptionId,
292
+ hash,
293
+ function: fn,
294
+ callParameters
295
+ }).match(
296
+ (response) => sendJsonRpcResponse(
297
+ id,
298
+ convertOperationResultToJsonRpc(response.operation)
299
+ ),
300
+ hostError(id)
301
+ );
302
+ break;
303
+ }
304
+ case "chainHead_v1_unpin": {
305
+ const [followSubscriptionId, hashOrHashes] = params;
306
+ const hashes = Array.isArray(hashOrHashes) ? hashOrHashes : [hashOrHashes];
307
+ chain.unpinHead({ genesisHash, followSubscriptionId, hashes }).match(() => sendJsonRpcResponse(id, null), hostError(id));
308
+ break;
309
+ }
310
+ case "chainHead_v1_continue": {
311
+ const [followSubscriptionId, operationId] = params;
312
+ chain.continueHead({ genesisHash, followSubscriptionId, operationId }).match(() => sendJsonRpcResponse(id, null), hostError(id));
313
+ break;
314
+ }
315
+ case "chainHead_v1_stopOperation": {
316
+ const [followSubscriptionId, operationId] = params;
317
+ chain.stopHeadOperation({ genesisHash, followSubscriptionId, operationId }).match(() => sendJsonRpcResponse(id, null), hostError(id));
318
+ break;
319
+ }
320
+ case "chainSpec_v1_genesisHash": {
321
+ chain.getSpecGenesisHash({ genesisHash }).match(
322
+ (response) => sendJsonRpcResponse(id, response.genesisHash),
323
+ hostError(id)
324
+ );
325
+ break;
326
+ }
327
+ case "chainSpec_v1_chainName": {
328
+ chain.getSpecChainName({ genesisHash }).match(
329
+ (response) => sendJsonRpcResponse(id, response.chainName),
330
+ hostError(id)
331
+ );
332
+ break;
333
+ }
334
+ case "chainSpec_v1_properties": {
335
+ chain.getSpecProperties({ genesisHash }).match((response) => {
336
+ try {
337
+ sendJsonRpcResponse(id, JSON.parse(response.properties));
338
+ } catch {
339
+ sendJsonRpcResponse(id, response.properties);
340
+ }
341
+ }, hostError(id));
342
+ break;
343
+ }
344
+ case "transaction_v1_broadcast": {
345
+ const [transaction] = params;
346
+ chain.broadcastTransaction({ genesisHash, transaction }).match((response) => {
347
+ const operationId = response.operationId ?? null;
348
+ if (operationId !== null) activeBroadcasts.add(operationId);
349
+ sendJsonRpcResponse(id, operationId);
350
+ }, hostError(id));
351
+ break;
352
+ }
353
+ case "transaction_v1_stop": {
354
+ const [operationId] = params;
355
+ activeBroadcasts.delete(operationId);
356
+ chain.stopTransaction({ genesisHash, operationId }).match(() => sendJsonRpcResponse(id, null), hostError(id));
357
+ break;
358
+ }
359
+ default:
360
+ sendJsonRpcError(
361
+ id,
362
+ JSON_RPC_METHOD_NOT_FOUND,
363
+ `Method "${method}" is not supported by the host`
364
+ );
365
+ break;
366
+ }
367
+ }
368
+ return {
369
+ send(message) {
370
+ try {
371
+ handleMessage(message);
372
+ } catch (error) {
373
+ log.warn("send: handler threw before settling the request", {
374
+ error: formatHostError(error)
375
+ });
376
+ sendJsonRpcError(message.id, JSON_RPC_INTERNAL_ERROR, formatHostError(error));
377
+ }
378
+ },
379
+ disconnect() {
380
+ for (const handle of activeFollows.values()) {
381
+ handle.unsubscribe();
382
+ }
383
+ activeFollows.clear();
384
+ for (const operationId of activeBroadcasts) {
385
+ chain.stopTransaction({ genesisHash, operationId }).match(
386
+ () => {
387
+ },
388
+ () => {
389
+ }
390
+ );
391
+ }
392
+ activeBroadcasts.clear();
393
+ }
394
+ };
395
+ };
396
+ }
397
+
398
+ // src/result.ts
399
+ function ok(value) {
400
+ return { ok: true, value };
401
+ }
402
+ function err(error) {
403
+ return { ok: false, error };
404
+ }
405
+
406
+ // src/truapi.ts
407
+ var log2 = createLogger("host");
408
+ function unwrapHostResult(result, label) {
409
+ return result.match(
410
+ (value) => value,
411
+ (error) => {
412
+ throw new Error(`${label}: ${formatHostError(error)}`, { cause: error });
413
+ }
414
+ );
415
+ }
416
+ function mapHostResult(result, map, label) {
417
+ return result.match(
418
+ (value) => ok(map(value)),
419
+ (error) => err(new HostCallFailedError(label, error))
420
+ );
421
+ }
422
+ function toHex(bytes) {
423
+ return scale.bytesToHex(bytes);
424
+ }
425
+ function fromHex(hex) {
426
+ return scale.hexToBytes(hex);
427
+ }
428
+ async function getTruApi() {
429
+ return getClient();
430
+ }
431
+ function adaptPreimageManager(client) {
432
+ const preimage = client.preimage;
433
+ return {
434
+ lookup(key, callback) {
435
+ return subscribeWithInterrupt(
436
+ preimage.lookupSubscribe({ request: { key } }),
437
+ (item) => callback(item.value !== void 0 ? fromHex(item.value) : null)
438
+ );
439
+ },
440
+ submit(value) {
441
+ return unwrapHostResult(preimage.submit(toHex(value)), "preimage submit failed");
442
+ }
443
+ };
444
+ }
445
+ async function getPreimageManager() {
446
+ const client = await getClient();
447
+ return client ? adaptPreimageManager(client) : null;
448
+ }
449
+ async function createHostPreimageManager() {
450
+ return getPreimageManager();
451
+ }
452
+ async function requestResourceAllocation(resources) {
453
+ const truApi = await getTruApi();
454
+ if (!truApi) {
455
+ return err(new HostUnavailableError("requestResourceAllocation: TruAPI unavailable"));
456
+ }
457
+ log2.debug("requestResourceAllocation", { resources: resources.map((r) => r.tag) });
458
+ return mapHostResult(
459
+ truApi.resourceAllocation.request({ resources }),
460
+ (response) => response.outcomes,
461
+ "requestResourceAllocation failed"
462
+ );
463
+ }
464
+ async function createProofAuthorized(statement) {
465
+ const truApi = await getTruApi();
466
+ if (!truApi) {
467
+ return err(new HostUnavailableError("createProofAuthorized: TruAPI unavailable"));
468
+ }
469
+ log2.debug("createProofAuthorized", { topics: statement.topics.length });
470
+ return mapHostResult(
471
+ truApi.statementStore.createProofAuthorized(statement),
472
+ (response) => response.proof,
473
+ "createProofAuthorized failed"
474
+ );
475
+ }
4
476
 
5
477
  // src/container.ts
6
- var log = createLogger("host:container");
478
+ var textEncoder = new TextEncoder();
479
+ var textDecoder = new TextDecoder();
7
480
  var ChainNotSupportedError = class extends Error {
8
481
  /** Genesis hash of the chain the host refused, for programmatic detection. */
9
482
  genesisHash;
@@ -15,93 +488,90 @@ var ChainNotSupportedError = class extends Error {
15
488
  this.genesisHash = genesisHash;
16
489
  }
17
490
  };
18
- async function isChainSupportedByHost(sdk, genesisHash) {
19
- const ready = await sdk.sandboxTransport.isReady();
20
- if (!ready) {
21
- throw new Error(
22
- `Host connection did not become ready; cannot verify support for chain ${genesisHash}.`
23
- );
24
- }
25
- const result = await sdk.hostApi.featureSupported(
26
- enumValue("v1", enumValue("Chain", genesisHash))
27
- );
28
- return result.match(
29
- (ok) => ok.value === true,
30
- (err) => {
31
- const value = err?.value;
32
- const reason = value?.payload?.reason ?? value?.reason ?? "unknown reason";
33
- throw new Error(`Host rejected the chain-support check for ${genesisHash}: ${reason}`);
491
+ async function isChainSupportedByHost(client, genesisHash) {
492
+ return client.system.featureSupported({ tag: "Chain", value: { genesisHash } }).match(
493
+ (response) => response.supported,
494
+ (error) => {
495
+ throw new Error(
496
+ `Host rejected the chain-support check for ${genesisHash}: ${formatHostError(error)}`
497
+ );
34
498
  }
35
499
  );
36
500
  }
37
501
  async function isInsideContainer() {
38
- if (typeof window === "undefined") return false;
39
- try {
40
- const sdk = await import('@novasamatech/host-api-wrapper');
41
- return sdk.sandboxProvider.isCorrectEnvironment();
42
- } catch {
43
- return isInsideContainerSync();
502
+ return isCorrectEnvironment();
503
+ }
504
+ function adaptLocalStorage(client) {
505
+ const ls = client.localStorage;
506
+ async function readBytes(key) {
507
+ const response = await unwrapHostResult(ls.read({ key }), "host localStorage read failed");
508
+ return response.value !== void 0 ? fromHex(response.value) : void 0;
44
509
  }
510
+ async function writeBytes(key, value) {
511
+ await unwrapHostResult(
512
+ ls.write({ key, value: toHex(value) }),
513
+ "host localStorage write failed"
514
+ );
515
+ }
516
+ async function readString(key) {
517
+ const bytes = await readBytes(key);
518
+ return bytes ? textDecoder.decode(bytes) : "";
519
+ }
520
+ async function writeString(key, value) {
521
+ return writeBytes(key, textEncoder.encode(value));
522
+ }
523
+ async function readJSON(key) {
524
+ const text = await readString(key);
525
+ return text ? JSON.parse(text) : null;
526
+ }
527
+ async function writeJSON(key, value) {
528
+ return writeString(key, JSON.stringify(value));
529
+ }
530
+ async function clear(key) {
531
+ await unwrapHostResult(ls.clear({ key }), "host localStorage clear failed");
532
+ }
533
+ return { readString, writeString, readJSON, writeJSON, readBytes, writeBytes, clear };
45
534
  }
46
535
  async function getHostLocalStorage() {
47
- if (!await isInsideContainer()) return null;
48
- try {
49
- const sdk = await import('@novasamatech/host-api-wrapper');
50
- return sdk.hostLocalStorage;
51
- } catch (err) {
52
- log.debug("getHostLocalStorage unavailable", err);
53
- return null;
54
- }
536
+ const client = await getClient();
537
+ return client ? adaptLocalStorage(client) : null;
55
538
  }
56
- async function createHostLocalStorage(transport) {
57
- if (!await isInsideContainer()) return null;
58
- try {
59
- const sdk = await import('@novasamatech/host-api-wrapper');
60
- return sdk.createLocalStorage(transport);
61
- } catch (err) {
62
- log.debug("createHostLocalStorage unavailable", err);
63
- return null;
64
- }
539
+ async function createHostLocalStorage() {
540
+ return getHostLocalStorage();
65
541
  }
66
542
  async function getHostProvider(genesisHash) {
67
- let sdk;
68
- try {
69
- sdk = await import('@novasamatech/host-api-wrapper');
70
- } catch (err) {
71
- log.debug("getHostProvider unavailable", err);
72
- return null;
73
- }
74
- return resolveHostProvider(sdk, genesisHash);
543
+ const client = await getClient();
544
+ if (!client) return null;
545
+ return resolveHostProvider(client, genesisHash);
75
546
  }
76
- async function resolveHostProvider(sdk, genesisHash) {
77
- if (!sdk.sandboxTransport.isCorrectEnvironment()) {
78
- return null;
79
- }
80
- if (!await isChainSupportedByHost(sdk, genesisHash)) {
547
+ async function resolveHostProvider(client, genesisHash) {
548
+ if (!await isChainSupportedByHost(client, genesisHash)) {
81
549
  throw new ChainNotSupportedError(genesisHash);
82
550
  }
83
- return sdk.createPapiProvider(genesisHash);
551
+ return createHostPapiProvider(client, genesisHash);
84
552
  }
85
- function isInsideContainerSync() {
86
- if (typeof window === "undefined") return false;
87
- const win = window;
88
- try {
89
- if (window !== window.top) return true;
90
- } catch {
91
- return true;
92
- }
93
- if (win.__HOST_WEBVIEW_MARK__ === true) return true;
94
- if (win.__HOST_API_PORT__ != null) return true;
95
- return false;
553
+ function adaptStatementStore(client) {
554
+ const ss = client.statementStore;
555
+ return {
556
+ subscribe(filter, callback) {
557
+ const request = "matchAll" in filter ? { tag: "MatchAll", value: filter.matchAll } : { tag: "MatchAny", value: filter.matchAny };
558
+ return subscribeWithInterrupt(ss.subscribe({ request }), callback);
559
+ },
560
+ async createProofAuthorized(statement) {
561
+ const response = await unwrapHostResult(
562
+ ss.createProofAuthorized(statement),
563
+ "createProofAuthorized failed"
564
+ );
565
+ return response.proof;
566
+ },
567
+ async submit(signedStatement) {
568
+ await unwrapHostResult(ss.submit(signedStatement), "statement submit failed");
569
+ }
570
+ };
96
571
  }
97
572
  async function getStatementStore() {
98
- try {
99
- const sdk = await import('@novasamatech/host-api-wrapper');
100
- return sdk.createStatementStore();
101
- } catch (err) {
102
- log.debug("getStatementStore unavailable", err);
103
- return null;
104
- }
573
+ const client = await getClient();
574
+ return client ? adaptStatementStore(client) : null;
105
575
  }
106
576
 
107
577
  // src/chains.ts
@@ -112,191 +582,392 @@ var BULLETIN_RPCS = {
112
582
  kusama: []
113
583
  };
114
584
  var DEFAULT_BULLETIN_ENDPOINT = BULLETIN_RPCS.paseo[0];
115
- var log2 = createLogger("host");
116
- function formatHostError(err) {
117
- const inner = isVersionedEnvelope(err) ? err.value : err;
118
- if (inner instanceof Error) return inner.message;
119
- if (typeof inner === "string") return inner;
120
- if (inner != null && typeof inner === "object" && "message" in inner && typeof inner.message === "string") {
121
- const named = inner;
122
- return typeof named.name === "string" ? `${named.name}: ${named.message}` : named.message;
123
- }
124
- try {
125
- return JSON.stringify(inner);
126
- } catch {
127
- return String(inner);
128
- }
129
- }
130
- function isVersionedEnvelope(value) {
131
- return value != null && typeof value === "object" && "tag" in value && "value" in value && typeof value.tag === "string";
132
- }
133
- var cachedTruApi = null;
134
- async function getTruApi() {
135
- if (cachedTruApi) return cachedTruApi;
136
- try {
137
- const sdk = await import('@novasamatech/host-api-wrapper');
138
- cachedTruApi = sdk.hostApi;
139
- log2.debug("TruAPI loaded");
140
- return cachedTruApi;
141
- } catch {
142
- log2.debug("TruAPI unavailable (not in container or SDK not installed)");
143
- return null;
585
+ function deriveTxExtVersion(metadata) {
586
+ const versions = unifyMetadata(decAnyMetadata(metadata)).extrinsic.version;
587
+ if (versions.length === 0) {
588
+ throw new Error("No extrinsic version found in metadata");
144
589
  }
590
+ const latestVersion = versions.reduce((acc, v) => Math.max(acc, v), 0);
591
+ return latestVersion === 4 ? 0 : latestVersion;
145
592
  }
146
- async function getPreimageManager() {
147
- try {
148
- const sdk = await import('@novasamatech/host-api-wrapper');
149
- return sdk.preimageManager;
150
- } catch (err) {
151
- log2.debug("getPreimageManager unavailable", err);
152
- return null;
153
- }
593
+ var deps = { deriveTxExtVersion };
594
+ function toHostExtensions(signedExtensions) {
595
+ return Object.values(signedExtensions).map((ext) => ({
596
+ id: ext.identifier,
597
+ extra: toHex(ext.value),
598
+ additionalSigned: toHex(ext.additionalSigned)
599
+ }));
154
600
  }
155
- async function createHostPreimageManager(transport) {
156
- if (!await isInsideContainer()) return null;
157
- try {
158
- const sdk = await import('@novasamatech/host-api-wrapper');
159
- return sdk.createPreimageManager(transport);
160
- } catch (err) {
161
- log2.debug("createHostPreimageManager unavailable", err);
162
- return null;
163
- }
164
- }
165
- async function getAccountsProvider() {
166
- try {
167
- const sdk = await import('@novasamatech/host-api-wrapper');
168
- return sdk.createAccountsProvider();
169
- } catch (err) {
170
- log2.debug("getAccountsProvider unavailable", err);
171
- return null;
172
- }
173
- }
174
- async function requestResourceAllocation(resources) {
175
- const truApi = await getTruApi();
176
- if (!truApi) {
177
- throw new Error("requestResourceAllocation: TruAPI unavailable");
178
- }
179
- log2.debug("requestResourceAllocation", { resources: resources.map((r) => r.tag) });
180
- return await truApi.requestResourceAllocation(enumValue("v1", resources)).match(
181
- (envelope) => envelope.value,
182
- (err) => {
183
- throw new Error(`requestResourceAllocation failed: ${formatHostError(err)}`, {
184
- cause: err
185
- });
601
+ function adaptAccountsProvider(client) {
602
+ const account = client.account;
603
+ const signing = client.signing;
604
+ return {
605
+ getUserId() {
606
+ return account.getUserId().map((response) => ({
607
+ primaryUsername: response.primaryUsername
608
+ }));
609
+ },
610
+ requestLogin(reason) {
611
+ return account.requestLogin({ reason });
612
+ },
613
+ getProductAccount(dotNsIdentifier, derivationIndex = 0) {
614
+ return account.getAccount({ productAccountId: { dotNsIdentifier, derivationIndex } }).map((response) => ({
615
+ publicKey: fromHex(response.account.publicKey),
616
+ dotNsIdentifier,
617
+ derivationIndex
618
+ }));
619
+ },
620
+ getProductAccountAlias(dotNsIdentifier, derivationIndex = 0) {
621
+ return account.getAccountAlias({ productAccountId: { dotNsIdentifier, derivationIndex } }).map((response) => ({
622
+ context: fromHex(response.context),
623
+ alias: fromHex(response.alias)
624
+ }));
625
+ },
626
+ getLegacyAccounts() {
627
+ return account.getLegacyAccounts().map(
628
+ (response) => response.accounts.map((a) => ({
629
+ publicKey: fromHex(a.publicKey),
630
+ name: a.name
631
+ }))
632
+ );
633
+ },
634
+ createRingVRFProof(dotNsIdentifier, derivationIndex, location, message) {
635
+ return account.createAccountProof({
636
+ productAccountId: { dotNsIdentifier, derivationIndex },
637
+ ringLocation: location,
638
+ context: toHex(message)
639
+ }).map((response) => fromHex(response.proof));
640
+ },
641
+ getProductAccountSigner(account_) {
642
+ const productAccountId = {
643
+ dotNsIdentifier: account_.dotNsIdentifier,
644
+ derivationIndex: account_.derivationIndex
645
+ };
646
+ return {
647
+ publicKey: account_.publicKey,
648
+ async signTx(callData, signedExtensions, metadata) {
649
+ const checkGenesis = signedExtensions.CheckGenesis;
650
+ if (!checkGenesis) {
651
+ throw new Error("Can't find genesis hash on transaction");
652
+ }
653
+ const response = await unwrapHostResult(
654
+ signing.createTransaction({
655
+ signer: productAccountId,
656
+ genesisHash: toHex(checkGenesis.additionalSigned),
657
+ callData: toHex(callData),
658
+ extensions: toHostExtensions(signedExtensions),
659
+ txExtVersion: deps.deriveTxExtVersion(metadata)
660
+ }),
661
+ "createTransaction failed"
662
+ );
663
+ return fromHex(response.transaction);
664
+ },
665
+ async signBytes(data) {
666
+ const response = await unwrapHostResult(
667
+ signing.signRaw({
668
+ account: productAccountId,
669
+ payload: { tag: "Bytes", value: { bytes: toHex(data) } }
670
+ }),
671
+ "signRaw failed"
672
+ );
673
+ return fromHex(response.signature);
674
+ }
675
+ };
676
+ },
677
+ getLegacyAccountSigner(account_) {
678
+ const signerHex = toHex(account_.publicKey);
679
+ const ss58Address = AccountId().dec(account_.publicKey);
680
+ return {
681
+ publicKey: account_.publicKey,
682
+ async signTx(callData, signedExtensions, metadata) {
683
+ const checkGenesis = signedExtensions.CheckGenesis;
684
+ if (!checkGenesis) {
685
+ throw new Error("Can't find genesis hash on transaction");
686
+ }
687
+ const response = await unwrapHostResult(
688
+ signing.createTransactionWithLegacyAccount({
689
+ signer: signerHex,
690
+ genesisHash: toHex(checkGenesis.additionalSigned),
691
+ callData: toHex(callData),
692
+ extensions: toHostExtensions(signedExtensions),
693
+ txExtVersion: deps.deriveTxExtVersion(metadata)
694
+ }),
695
+ "createTransactionWithLegacyAccount failed"
696
+ );
697
+ return fromHex(response.transaction);
698
+ },
699
+ async signBytes(data) {
700
+ const response = await unwrapHostResult(
701
+ signing.signRawWithLegacyAccount({
702
+ signer: ss58Address,
703
+ payload: { tag: "Bytes", value: { bytes: toHex(data) } }
704
+ }),
705
+ "signRawWithLegacyAccount failed"
706
+ );
707
+ return fromHex(response.signature);
708
+ }
709
+ };
710
+ },
711
+ subscribeAccountConnectionStatus(callback) {
712
+ return subscribeWithInterrupt(account.connectionStatusSubscribe(), callback);
186
713
  }
187
- );
714
+ };
188
715
  }
189
- async function createProofAuthorized(statement) {
190
- const truApi = await getTruApi();
191
- if (!truApi) {
192
- throw new Error("createProofAuthorized: TruAPI unavailable");
193
- }
194
- log2.debug("createProofAuthorized", {
195
- topics: statement.topics.length,
196
- dataLen: statement.data?.length ?? 0
197
- });
198
- return await truApi.statementStoreCreateProofAuthorized(enumValue("v1", statement)).match(
199
- (envelope) => envelope.value,
200
- (err) => {
201
- throw new Error(`createProofAuthorized failed: ${formatHostError(err)}`, {
202
- cause: err
203
- });
204
- }
205
- );
716
+ async function getAccountsProvider() {
717
+ const client = await getClient();
718
+ return client ? adaptAccountsProvider(client) : null;
206
719
  }
207
720
  var log3 = createLogger("host:permissions");
208
721
  async function requestPermission(permission) {
209
722
  const truApi = await getTruApi();
210
723
  if (!truApi) {
211
- throw new Error("requestPermission: TruAPI unavailable");
724
+ return err(new HostUnavailableError("requestPermission: TruAPI unavailable"));
212
725
  }
213
726
  log3.debug("requestPermission", { tag: permission.tag });
214
- return await truApi.permission(enumValue("v1", permission)).match(
215
- (envelope) => envelope.value,
216
- (err) => {
217
- throw new Error(`requestPermission failed: ${formatHostError(err)}`, { cause: err });
218
- }
727
+ return mapHostResult(
728
+ truApi.permissions.requestRemotePermission({ permission }),
729
+ (response) => response.granted,
730
+ "requestPermission failed"
219
731
  );
220
732
  }
221
733
  async function requestDevicePermission(permission) {
222
734
  const truApi = await getTruApi();
223
735
  if (!truApi) {
224
- throw new Error("requestDevicePermission: TruAPI unavailable");
736
+ return err(new HostUnavailableError("requestDevicePermission: TruAPI unavailable"));
225
737
  }
226
738
  log3.debug("requestDevicePermission", { permission });
227
- return await truApi.devicePermission(enumValue("v1", permission)).match(
228
- (envelope) => envelope.value,
229
- (err) => {
230
- throw new Error(`requestDevicePermission failed: ${formatHostError(err)}`, {
231
- cause: err
232
- });
233
- }
739
+ return mapHostResult(
740
+ truApi.permissions.requestDevicePermission(permission),
741
+ (response) => response.granted,
742
+ "requestDevicePermission failed"
234
743
  );
235
744
  }
236
- var log4 = createLogger("host:theme");
745
+
746
+ // src/theme.ts
747
+ function adaptThemeProvider(client) {
748
+ return {
749
+ subscribeTheme(callback) {
750
+ return subscribeWithInterrupt(client.theme.subscribe(), callback);
751
+ }
752
+ };
753
+ }
237
754
  async function getThemeProvider() {
238
- try {
239
- const sdk = await import('@novasamatech/host-api-wrapper');
240
- return sdk.createThemeProvider();
241
- } catch (err) {
242
- log4.debug("getThemeProvider unavailable", err);
243
- return null;
244
- }
755
+ const client = await getClient();
756
+ return client ? adaptThemeProvider(client) : null;
245
757
  }
246
- var log5 = createLogger("host:entropy");
758
+ var log4 = createLogger("host:entropy");
247
759
  async function deriveEntropy(key) {
248
760
  const truApi = await getTruApi();
249
761
  if (!truApi) {
250
- throw new Error("deriveEntropy: TruAPI unavailable");
762
+ return err(new HostUnavailableError("deriveEntropy: TruAPI unavailable"));
251
763
  }
252
- log5.debug("deriveEntropy", { keyLen: key.length });
253
- return await truApi.deriveEntropy(enumValue("v1", key)).match(
254
- (envelope) => envelope.value,
255
- (err) => {
256
- throw new Error(`deriveEntropy failed: ${formatHostError(err)}`, { cause: err });
257
- }
764
+ log4.debug("deriveEntropy", { keyLen: key.length });
765
+ return mapHostResult(
766
+ truApi.entropy.derive({ context: toHex(key) }),
767
+ (response) => fromHex(response.entropy),
768
+ "deriveEntropy failed"
258
769
  );
259
770
  }
260
- var log6 = createLogger("host:chat");
771
+
772
+ // src/chat.ts
773
+ function adaptChatManager(client) {
774
+ const chat = client.chat;
775
+ const roomStatus = /* @__PURE__ */ new Map();
776
+ const botStatus = /* @__PURE__ */ new Map();
777
+ return {
778
+ async registerRoom(request) {
779
+ const cached = roomStatus.get(request.roomId);
780
+ if (cached) return cached;
781
+ const response = await unwrapHostResult(
782
+ chat.createRoom(request),
783
+ "chat registerRoom failed"
784
+ );
785
+ roomStatus.set(request.roomId, response.status);
786
+ return response.status;
787
+ },
788
+ async registerBot(request) {
789
+ const cached = botStatus.get(request.botId);
790
+ if (cached) return cached;
791
+ const response = await unwrapHostResult(
792
+ chat.registerBot(request),
793
+ "chat registerBot failed"
794
+ );
795
+ botStatus.set(request.botId, response.status);
796
+ return response.status;
797
+ },
798
+ async sendMessage(roomId, payload) {
799
+ const response = await unwrapHostResult(
800
+ chat.postMessage({ roomId, payload }),
801
+ "chat sendMessage failed"
802
+ );
803
+ return { messageId: response.messageId };
804
+ },
805
+ subscribeChatList(callback) {
806
+ return subscribeWithInterrupt(chat.listSubscribe(), (item) => callback(item.rooms));
807
+ },
808
+ subscribeAction(callback) {
809
+ return subscribeWithInterrupt(chat.actionSubscribe(), callback);
810
+ }
811
+ };
812
+ }
261
813
  async function getChatManager() {
262
- try {
263
- const sdk = await import('@novasamatech/host-api-wrapper');
264
- return sdk.createProductChatManager();
265
- } catch (err) {
266
- log6.debug("getChatManager unavailable", err);
267
- return null;
268
- }
814
+ const client = await getClient();
815
+ return client ? adaptChatManager(client) : null;
269
816
  }
270
- function matchChatCustomRenderers(map) {
271
- return (params, render) => {
272
- const renderer = map[params.messageType];
273
- if (!renderer) {
274
- throw new Error(`Renderer for message type ${params.messageType} is not defined`);
817
+
818
+ // src/payments.ts
819
+ function adaptPaymentManager(client) {
820
+ const payment = client.payment;
821
+ return {
822
+ subscribeBalance(callback, purse) {
823
+ return subscribeWithInterrupt(
824
+ payment.balanceSubscribe({ request: { purse } }),
825
+ callback
826
+ );
827
+ },
828
+ topUp(amount, source, into) {
829
+ return unwrapHostResult(
830
+ payment.topUp({ into, amount, source }),
831
+ "payment topUp failed"
832
+ );
833
+ },
834
+ async requestPayment(amount, destination, from) {
835
+ const response = await unwrapHostResult(
836
+ payment.request({ from, amount, destination }),
837
+ "payment requestPayment failed"
838
+ );
839
+ return { id: response.id };
840
+ },
841
+ subscribePaymentStatus(paymentId, callback) {
842
+ return subscribeWithInterrupt(
843
+ payment.statusSubscribe({ request: { paymentId } }),
844
+ callback
845
+ );
275
846
  }
276
- return renderer(params, render);
277
847
  };
278
848
  }
279
- var log7 = createLogger("host:payments");
280
849
  async function getPaymentManager() {
281
- try {
282
- const sdk = await import('@novasamatech/host-api-wrapper');
283
- return sdk.paymentManager;
284
- } catch (err) {
285
- log7.debug("getPaymentManager unavailable", err);
286
- return null;
287
- }
850
+ const client = await getClient();
851
+ return client ? adaptPaymentManager(client) : null;
852
+ }
853
+
854
+ // src/notifications.ts
855
+ function adaptNotificationManager(client) {
856
+ const notifications = client.notifications;
857
+ return {
858
+ async push(input) {
859
+ const response = await unwrapHostResult(
860
+ notifications.sendPushNotification(input),
861
+ "notification push failed"
862
+ );
863
+ return response.id;
864
+ },
865
+ async cancel(id) {
866
+ await unwrapHostResult(
867
+ notifications.cancelPushNotification({ id }),
868
+ "notification cancel failed"
869
+ );
870
+ }
871
+ };
288
872
  }
289
- var log8 = createLogger("host:notifications");
290
873
  async function getNotificationManager() {
874
+ const client = await getClient();
875
+ return client ? adaptNotificationManager(client) : null;
876
+ }
877
+ var log5 = createLogger("host:navigation");
878
+ async function navigateTo(url) {
879
+ const truApi = await getTruApi();
880
+ if (!truApi) {
881
+ return err(new HostUnavailableError("navigateTo: TruAPI unavailable"));
882
+ }
883
+ log5.debug("navigateTo", { url });
884
+ return mapHostResult(truApi.system.navigateTo({ url }), () => void 0, "navigateTo failed");
885
+ }
886
+ var log6 = createLogger("host:features");
887
+ async function featureSupported(feature) {
888
+ const truApi = await getTruApi();
889
+ if (!truApi) {
890
+ return err(new HostUnavailableError("featureSupported: TruAPI unavailable"));
891
+ }
892
+ log6.debug("featureSupported", { tag: feature.tag });
893
+ return mapHostResult(
894
+ truApi.system.featureSupported({ tag: feature.tag, value: { genesisHash: feature.value } }),
895
+ (response) => response.supported,
896
+ "featureSupported failed"
897
+ );
898
+ }
899
+ async function isChainSupported(genesisHash) {
900
+ return featureSupported({ tag: "Chain", value: genesisHash });
901
+ }
902
+ var log7 = createLogger("host:chain-spec");
903
+ async function getChainSpec(genesisHash) {
904
+ const truApi = await getTruApi();
905
+ if (!truApi) {
906
+ log7.debug("getChainSpec: TruAPI unavailable");
907
+ return ok(null);
908
+ }
909
+ log7.debug("getChainSpec", { genesisHash });
910
+ const [genesisHashResult, nameResult, propertiesResult] = await Promise.all([
911
+ mapHostResult(
912
+ truApi.chain.getSpecGenesisHash({ genesisHash }),
913
+ (response) => response.genesisHash,
914
+ "getChainSpec (genesisHash) failed"
915
+ ),
916
+ mapHostResult(
917
+ truApi.chain.getSpecChainName({ genesisHash }),
918
+ (response) => response.chainName,
919
+ "getChainSpec (chainName) failed"
920
+ ),
921
+ mapHostResult(
922
+ truApi.chain.getSpecProperties({ genesisHash }),
923
+ (response) => response.properties,
924
+ "getChainSpec (properties) failed"
925
+ )
926
+ ]);
927
+ if (!genesisHashResult.ok) return genesisHashResult;
928
+ if (!nameResult.ok) return nameResult;
929
+ if (!propertiesResult.ok) return propertiesResult;
930
+ const propertiesRaw = propertiesResult.value;
931
+ let properties;
291
932
  try {
292
- const sdk = await import('@novasamatech/host-api-wrapper');
293
- return sdk.notificationManager;
294
- } catch (err) {
295
- log8.debug("getNotificationManager unavailable", err);
296
- return null;
933
+ properties = JSON.parse(propertiesRaw);
934
+ } catch (parseError) {
935
+ log7.debug("getChainSpec: properties JSON parse failed", parseError);
936
+ properties = null;
297
937
  }
938
+ return ok({
939
+ genesisHash: genesisHashResult.value,
940
+ name: nameResult.value,
941
+ properties,
942
+ propertiesRaw
943
+ });
944
+ }
945
+ var log8 = createLogger("host:chain-transaction");
946
+ async function broadcastTransaction(genesisHash, transaction) {
947
+ const truApi = await getTruApi();
948
+ if (!truApi) {
949
+ return err(new HostUnavailableError("broadcastTransaction: TruAPI unavailable"));
950
+ }
951
+ log8.debug("broadcastTransaction", { genesisHash });
952
+ return mapHostResult(
953
+ truApi.chain.broadcastTransaction({ genesisHash, transaction }),
954
+ (response) => response.operationId ?? null,
955
+ "broadcastTransaction failed"
956
+ );
957
+ }
958
+ async function stopTransaction(genesisHash, operationId) {
959
+ const truApi = await getTruApi();
960
+ if (!truApi) {
961
+ return err(new HostUnavailableError("stopTransaction: TruAPI unavailable"));
962
+ }
963
+ log8.debug("stopTransaction", { genesisHash, operationId });
964
+ return mapHostResult(
965
+ truApi.chain.stopTransaction({ genesisHash, operationId }),
966
+ () => void 0,
967
+ "stopTransaction failed"
968
+ );
298
969
  }
299
970
 
300
- export { BULLETIN_RPCS, ChainNotSupportedError, DEFAULT_BULLETIN_ENDPOINT, createHostLocalStorage, createHostPreimageManager, createProofAuthorized, deriveEntropy, formatHostError, getAccountsProvider, getChatManager, getHostLocalStorage, getHostProvider, getNotificationManager, getPaymentManager, getPreimageManager, getStatementStore, getThemeProvider, getTruApi, isInsideContainer, isInsideContainerSync, matchChatCustomRenderers, requestDevicePermission, requestPermission, requestResourceAllocation };
971
+ export { BULLETIN_RPCS, ChainNotSupportedError, DEFAULT_BULLETIN_ENDPOINT, HostCallFailedError, HostError, HostUnavailableError, broadcastTransaction, createHostLocalStorage, createHostPreimageManager, createProofAuthorized, deriveEntropy, err, featureSupported, formatHostError, fromHex, getAccountsProvider, getChainSpec, getChatManager, getHostLocalStorage, getHostProvider, getNotificationManager, getPaymentManager, getPreimageManager, getStatementStore, getThemeProvider, getTruApi, isChainSupported, isHostError, isInsideContainer, navigateTo, ok, requestDevicePermission, requestPermission, requestResourceAllocation, stopTransaction, toHex };
301
972
  //# sourceMappingURL=index.js.map
302
973
  //# sourceMappingURL=index.js.map