@parity/product-sdk-host 0.0.0-dev.312.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 ADDED
@@ -0,0 +1,1163 @@
1
+ import { getClient, isCorrectEnvironment, subscribeWithInterrupt } from './chunk-GDXSV7JV.js';
2
+ export { isCorrectEnvironment as isInsideContainerSync } from './chunk-GDXSV7JV.js';
3
+ import { createLogger } from '@parity/product-sdk-logger';
4
+ import { scale } from '@parity/truapi';
5
+ import { err, ok } from '@parity/result';
6
+ export { err, ok } from '@parity/result';
7
+ export { isSdkError } from '@parity/product-sdk-errors';
8
+ import { unifyMetadata, decAnyMetadata } from '@polkadot-api/substrate-bindings';
9
+ import { AccountId } from 'polkadot-api';
10
+
11
+ // src/errors.ts
12
+ function isTagged(value) {
13
+ return value != null && typeof value === "object" && typeof value.tag === "string";
14
+ }
15
+ function hasReason(value) {
16
+ return value != null && typeof value === "object" && typeof value.reason === "string";
17
+ }
18
+ function formatHostError(error) {
19
+ if (error instanceof Error) return error.message;
20
+ if (typeof error === "string") return error;
21
+ if (isTagged(error)) {
22
+ if (error.tag === "Domain" && isTagged(error.value) && error.value.value !== void 0) {
23
+ return formatHostError(error.value.value);
24
+ }
25
+ if (hasReason(error.value)) {
26
+ return `${error.tag}: ${error.value.reason}`;
27
+ }
28
+ return error.tag;
29
+ }
30
+ if (hasReason(error)) {
31
+ return error.reason;
32
+ }
33
+ if (error != null && typeof error === "object" && "message" in error) {
34
+ const message = error.message;
35
+ if (typeof message === "string") return message;
36
+ }
37
+ try {
38
+ return JSON.stringify(error);
39
+ } catch {
40
+ return String(error);
41
+ }
42
+ }
43
+ var HostError = class extends Error {
44
+ isSdkError = true;
45
+ source = "host";
46
+ constructor(message, options) {
47
+ super(message, options);
48
+ this.name = "HostError";
49
+ }
50
+ };
51
+ var HostUnavailableError = class extends HostError {
52
+ constructor(message = "Host API is not available") {
53
+ super(message);
54
+ this.name = "HostUnavailableError";
55
+ }
56
+ };
57
+ var HostCallFailedError = class extends HostError {
58
+ payload;
59
+ constructor(label, payload) {
60
+ super(`${label}: ${formatHostError(payload)}`, { cause: payload });
61
+ this.name = "HostCallFailedError";
62
+ this.payload = payload;
63
+ }
64
+ };
65
+ function isHostError(error) {
66
+ return error instanceof HostError;
67
+ }
68
+ var log = createLogger("host:papi");
69
+ var JSON_RPC_INTERNAL_ERROR = -32603;
70
+ var JSON_RPC_METHOD_NOT_FOUND = -32601;
71
+ var STORAGE_TYPE_MAP = {
72
+ value: "Value",
73
+ hash: "Hash",
74
+ closestDescendantMerkleValue: "ClosestDescendantMerkleValue",
75
+ descendantsValues: "DescendantsValues",
76
+ descendantsHashes: "DescendantsHashes"
77
+ };
78
+ function convertRuntimeToJsonRpc(runtime) {
79
+ if (!runtime || typeof runtime !== "object") return null;
80
+ if (runtime.tag === "Valid") {
81
+ const spec = runtime.value;
82
+ const apis = {};
83
+ for (const api of spec.apis) {
84
+ apis[api.name] = api.version;
85
+ }
86
+ return {
87
+ type: "valid",
88
+ spec: {
89
+ specName: spec.specName,
90
+ implName: spec.implName,
91
+ specVersion: spec.specVersion,
92
+ implVersion: spec.implVersion,
93
+ transactionVersion: spec.transactionVersion,
94
+ apis
95
+ }
96
+ };
97
+ }
98
+ if (runtime.tag === "Invalid") {
99
+ return { type: "invalid", error: runtime.value.error };
100
+ }
101
+ return null;
102
+ }
103
+ function convertFollowEventToJsonRpc(item) {
104
+ switch (item.tag) {
105
+ case "Initialized":
106
+ return {
107
+ event: "initialized",
108
+ finalizedBlockHashes: item.value.finalizedBlockHashes,
109
+ finalizedBlockRuntime: convertRuntimeToJsonRpc(item.value.finalizedBlockRuntime)
110
+ };
111
+ case "NewBlock":
112
+ return {
113
+ event: "newBlock",
114
+ blockHash: item.value.blockHash,
115
+ parentBlockHash: item.value.parentBlockHash,
116
+ newRuntime: convertRuntimeToJsonRpc(item.value.newRuntime)
117
+ };
118
+ case "BestBlockChanged":
119
+ return { event: "bestBlockChanged", bestBlockHash: item.value.bestBlockHash };
120
+ case "Finalized":
121
+ return {
122
+ event: "finalized",
123
+ finalizedBlockHashes: item.value.finalizedBlockHashes,
124
+ prunedBlockHashes: item.value.prunedBlockHashes
125
+ };
126
+ case "OperationBodyDone":
127
+ return {
128
+ event: "operationBodyDone",
129
+ operationId: item.value.operationId,
130
+ value: item.value.value
131
+ };
132
+ case "OperationCallDone":
133
+ return {
134
+ event: "operationCallDone",
135
+ operationId: item.value.operationId,
136
+ output: item.value.output
137
+ };
138
+ case "OperationStorageItems":
139
+ return {
140
+ event: "operationStorageItems",
141
+ operationId: item.value.operationId,
142
+ items: item.value.items
143
+ };
144
+ case "OperationStorageDone":
145
+ return { event: "operationStorageDone", operationId: item.value.operationId };
146
+ case "OperationWaitingForContinue":
147
+ return { event: "operationWaitingForContinue", operationId: item.value.operationId };
148
+ case "OperationInaccessible":
149
+ return { event: "operationInaccessible", operationId: item.value.operationId };
150
+ case "OperationError":
151
+ return {
152
+ event: "operationError",
153
+ operationId: item.value.operationId,
154
+ error: item.value.error
155
+ };
156
+ case "Stop":
157
+ return { event: "stop" };
158
+ default: {
159
+ return { event: "stop" };
160
+ }
161
+ }
162
+ }
163
+ function convertStorageType(type) {
164
+ return STORAGE_TYPE_MAP[type] ?? "Value";
165
+ }
166
+ function convertOperationResultToJsonRpc(result) {
167
+ if (result.tag === "Started") {
168
+ return { result: "started", operationId: result.value.operationId };
169
+ }
170
+ return { result: "limitReached" };
171
+ }
172
+ function createHostPapiProvider(client, genesisHash) {
173
+ const chain = client.chain;
174
+ return (onMessage) => {
175
+ const activeFollows = /* @__PURE__ */ new Map();
176
+ const activeBroadcasts = /* @__PURE__ */ new Set();
177
+ function sendJsonRpcResponse(id, result) {
178
+ onMessage({ jsonrpc: "2.0", id, result });
179
+ }
180
+ function sendJsonRpcError(id, code, message) {
181
+ onMessage({ jsonrpc: "2.0", id, error: { code, message } });
182
+ }
183
+ function sendFollowEvent(subscription, event) {
184
+ onMessage({
185
+ jsonrpc: "2.0",
186
+ method: "chainHead_v1_followEvent",
187
+ params: { subscription, result: event }
188
+ });
189
+ }
190
+ const hostError = (id) => (error) => sendJsonRpcError(id, JSON_RPC_INTERNAL_ERROR, formatHostError(error));
191
+ function handleMessage(message) {
192
+ const { id, method } = message;
193
+ const params = message.params ?? [];
194
+ switch (method) {
195
+ case "chainHead_v1_follow": {
196
+ const [withRuntime] = params;
197
+ const ref = {};
198
+ const pendingItems = [];
199
+ const forwardItem = (followSubscriptionId2, item) => {
200
+ if (item.tag === "Stop" && activeFollows.delete(followSubscriptionId2)) {
201
+ ref.handle?.unsubscribe();
202
+ }
203
+ sendFollowEvent(followSubscriptionId2, convertFollowEventToJsonRpc(item));
204
+ };
205
+ ref.handle = subscribeWithInterrupt(
206
+ chain.followHeadSubscribe({ request: { genesisHash, withRuntime } }),
207
+ (item) => {
208
+ const followSubscriptionId2 = ref.handle?.subscriptionId;
209
+ if (!followSubscriptionId2) {
210
+ pendingItems.push(item);
211
+ return;
212
+ }
213
+ forwardItem(followSubscriptionId2, item);
214
+ }
215
+ );
216
+ const followSubscriptionId = ref.handle.subscriptionId;
217
+ if (!followSubscriptionId) {
218
+ ref.handle.unsubscribe();
219
+ sendJsonRpcError(
220
+ id,
221
+ JSON_RPC_INTERNAL_ERROR,
222
+ "Host follow subscription did not start"
223
+ );
224
+ break;
225
+ }
226
+ ref.handle.onInterrupt(() => {
227
+ if (activeFollows.delete(followSubscriptionId)) {
228
+ sendFollowEvent(followSubscriptionId, { event: "stop" });
229
+ }
230
+ });
231
+ activeFollows.set(followSubscriptionId, ref.handle);
232
+ sendJsonRpcResponse(id, followSubscriptionId);
233
+ for (const item of pendingItems) {
234
+ forwardItem(followSubscriptionId, item);
235
+ }
236
+ break;
237
+ }
238
+ case "chainHead_v1_unfollow": {
239
+ const [followSubId] = params;
240
+ const follow = activeFollows.get(followSubId);
241
+ if (follow) {
242
+ follow.unsubscribe();
243
+ activeFollows.delete(followSubId);
244
+ }
245
+ sendJsonRpcResponse(id, null);
246
+ break;
247
+ }
248
+ case "chainHead_v1_header": {
249
+ const [followSubscriptionId, hash] = params;
250
+ chain.getHeadHeader({ genesisHash, followSubscriptionId, hash }).match(
251
+ (response) => sendJsonRpcResponse(id, response.header ?? null),
252
+ hostError(id)
253
+ );
254
+ break;
255
+ }
256
+ case "chainHead_v1_body": {
257
+ const [followSubscriptionId, hash] = params;
258
+ chain.getHeadBody({ genesisHash, followSubscriptionId, hash }).match(
259
+ (response) => sendJsonRpcResponse(
260
+ id,
261
+ convertOperationResultToJsonRpc(response.operation)
262
+ ),
263
+ hostError(id)
264
+ );
265
+ break;
266
+ }
267
+ case "chainHead_v1_storage": {
268
+ const [followSubscriptionId, hash, items, childTrie] = params;
269
+ const queryItems = items.map((item) => ({
270
+ key: item.key,
271
+ queryType: convertStorageType(item.type)
272
+ }));
273
+ chain.getHeadStorage({
274
+ genesisHash,
275
+ followSubscriptionId,
276
+ hash,
277
+ items: queryItems,
278
+ // PAPI passes `null` for an absent child trie, but the
279
+ // truapi codec encodes the optional `childTrie` field as
280
+ // `Option<Hex>` — it treats `undefined` as None yet runs
281
+ // the inner Hex codec on `null`, which throws
282
+ // (`null.startsWith`). Coerce `null` → `undefined`.
283
+ childTrie: childTrie ?? void 0
284
+ }).match(
285
+ (response) => sendJsonRpcResponse(
286
+ id,
287
+ convertOperationResultToJsonRpc(response.operation)
288
+ ),
289
+ hostError(id)
290
+ );
291
+ break;
292
+ }
293
+ case "chainHead_v1_call": {
294
+ const [followSubscriptionId, hash, fn, callParameters] = params;
295
+ chain.callHead({
296
+ genesisHash,
297
+ followSubscriptionId,
298
+ hash,
299
+ function: fn,
300
+ callParameters
301
+ }).match(
302
+ (response) => sendJsonRpcResponse(
303
+ id,
304
+ convertOperationResultToJsonRpc(response.operation)
305
+ ),
306
+ hostError(id)
307
+ );
308
+ break;
309
+ }
310
+ case "chainHead_v1_unpin": {
311
+ const [followSubscriptionId, hashOrHashes] = params;
312
+ const hashes = Array.isArray(hashOrHashes) ? hashOrHashes : [hashOrHashes];
313
+ chain.unpinHead({ genesisHash, followSubscriptionId, hashes }).match(() => sendJsonRpcResponse(id, null), hostError(id));
314
+ break;
315
+ }
316
+ case "chainHead_v1_continue": {
317
+ const [followSubscriptionId, operationId] = params;
318
+ chain.continueHead({ genesisHash, followSubscriptionId, operationId }).match(() => sendJsonRpcResponse(id, null), hostError(id));
319
+ break;
320
+ }
321
+ case "chainHead_v1_stopOperation": {
322
+ const [followSubscriptionId, operationId] = params;
323
+ chain.stopHeadOperation({ genesisHash, followSubscriptionId, operationId }).match(() => sendJsonRpcResponse(id, null), hostError(id));
324
+ break;
325
+ }
326
+ case "chainSpec_v1_genesisHash": {
327
+ chain.getSpecGenesisHash({ genesisHash }).match(
328
+ (response) => sendJsonRpcResponse(id, response.genesisHash),
329
+ hostError(id)
330
+ );
331
+ break;
332
+ }
333
+ case "chainSpec_v1_chainName": {
334
+ chain.getSpecChainName({ genesisHash }).match(
335
+ (response) => sendJsonRpcResponse(id, response.chainName),
336
+ hostError(id)
337
+ );
338
+ break;
339
+ }
340
+ case "chainSpec_v1_properties": {
341
+ chain.getSpecProperties({ genesisHash }).match((response) => {
342
+ try {
343
+ sendJsonRpcResponse(id, JSON.parse(response.properties));
344
+ } catch {
345
+ sendJsonRpcResponse(id, response.properties);
346
+ }
347
+ }, hostError(id));
348
+ break;
349
+ }
350
+ case "transaction_v1_broadcast": {
351
+ const [transaction] = params;
352
+ chain.broadcastTransaction({ genesisHash, transaction }).match((response) => {
353
+ const operationId = response.operationId ?? null;
354
+ if (operationId !== null) activeBroadcasts.add(operationId);
355
+ sendJsonRpcResponse(id, operationId);
356
+ }, hostError(id));
357
+ break;
358
+ }
359
+ case "transaction_v1_stop": {
360
+ const [operationId] = params;
361
+ activeBroadcasts.delete(operationId);
362
+ chain.stopTransaction({ genesisHash, operationId }).match(() => sendJsonRpcResponse(id, null), hostError(id));
363
+ break;
364
+ }
365
+ default:
366
+ sendJsonRpcError(
367
+ id,
368
+ JSON_RPC_METHOD_NOT_FOUND,
369
+ `Method "${method}" is not supported by the host`
370
+ );
371
+ break;
372
+ }
373
+ }
374
+ return {
375
+ send(message) {
376
+ try {
377
+ handleMessage(message);
378
+ } catch (error) {
379
+ log.warn("send: handler threw before settling the request", {
380
+ error: formatHostError(error)
381
+ });
382
+ sendJsonRpcError(message.id, JSON_RPC_INTERNAL_ERROR, formatHostError(error));
383
+ }
384
+ },
385
+ disconnect() {
386
+ for (const handle of activeFollows.values()) {
387
+ handle.unsubscribe();
388
+ }
389
+ activeFollows.clear();
390
+ for (const operationId of activeBroadcasts) {
391
+ chain.stopTransaction({ genesisHash, operationId }).match(
392
+ () => {
393
+ },
394
+ () => {
395
+ }
396
+ );
397
+ }
398
+ activeBroadcasts.clear();
399
+ }
400
+ };
401
+ };
402
+ }
403
+
404
+ // src/truapi.ts
405
+ var log2 = createLogger("host");
406
+ function unwrapHostResult(result, label) {
407
+ return result.match(
408
+ (value) => value,
409
+ (error) => {
410
+ throw new Error(`${label}: ${formatHostError(error)}`, { cause: error });
411
+ }
412
+ );
413
+ }
414
+ function mapHostResult(result, map, label) {
415
+ return result.match(
416
+ (value) => ok(map(value)),
417
+ (error) => err(new HostCallFailedError(label, error))
418
+ );
419
+ }
420
+ function toHex(bytes) {
421
+ return scale.bytesToHex(bytes);
422
+ }
423
+ function fromHex(hex) {
424
+ return scale.hexToBytes(hex);
425
+ }
426
+ async function getTruApi() {
427
+ return getClient();
428
+ }
429
+ function adaptPreimageManager(client) {
430
+ const preimage = client.preimage;
431
+ return {
432
+ lookup(key, callback) {
433
+ return subscribeWithInterrupt(
434
+ preimage.lookupSubscribe({ request: { key } }),
435
+ (item) => callback(item.value !== void 0 ? fromHex(item.value) : null)
436
+ );
437
+ },
438
+ submit(value) {
439
+ return unwrapHostResult(preimage.submit(toHex(value)), "preimage submit failed");
440
+ }
441
+ };
442
+ }
443
+ async function getPreimageManager() {
444
+ const client = await getClient();
445
+ return client ? adaptPreimageManager(client) : null;
446
+ }
447
+ async function createHostPreimageManager() {
448
+ return getPreimageManager();
449
+ }
450
+ async function requestResourceAllocation(resources) {
451
+ const truApi = await getTruApi();
452
+ if (!truApi) {
453
+ return err(new HostUnavailableError("requestResourceAllocation: TruAPI unavailable"));
454
+ }
455
+ log2.debug("requestResourceAllocation", { resources: resources.map((r) => r.tag) });
456
+ return mapHostResult(
457
+ truApi.resourceAllocation.request({ resources }),
458
+ (response) => response.outcomes,
459
+ "requestResourceAllocation failed"
460
+ );
461
+ }
462
+ async function createProofAuthorized(statement) {
463
+ const truApi = await getTruApi();
464
+ if (!truApi) {
465
+ return err(new HostUnavailableError("createProofAuthorized: TruAPI unavailable"));
466
+ }
467
+ log2.debug("createProofAuthorized", { topics: statement.topics.length });
468
+ return mapHostResult(
469
+ truApi.statementStore.createProofAuthorized(statement),
470
+ (response) => response.proof,
471
+ "createProofAuthorized failed"
472
+ );
473
+ }
474
+
475
+ // src/container.ts
476
+ var textEncoder = new TextEncoder();
477
+ var textDecoder = new TextDecoder();
478
+ var ChainNotSupportedError = class extends Error {
479
+ /** Genesis hash of the chain the host refused, for programmatic detection. */
480
+ genesisHash;
481
+ constructor(genesisHash) {
482
+ super(
483
+ `Chain ${genesisHash} is not supported by the current host. It may not be enabled in this host build, or its genesis hash may have drifted after a network reset.`
484
+ );
485
+ this.name = "ChainNotSupportedError";
486
+ this.genesisHash = genesisHash;
487
+ }
488
+ };
489
+ async function isChainSupportedByHost(client, genesisHash) {
490
+ return client.system.featureSupported({ tag: "Chain", value: { genesisHash } }).match(
491
+ (response) => response.supported,
492
+ (error) => {
493
+ throw new Error(
494
+ `Host rejected the chain-support check for ${genesisHash}: ${formatHostError(error)}`
495
+ );
496
+ }
497
+ );
498
+ }
499
+ async function isInsideContainer() {
500
+ return isCorrectEnvironment();
501
+ }
502
+ function adaptLocalStorage(client) {
503
+ const ls = client.localStorage;
504
+ async function readBytes(key) {
505
+ const response = await unwrapHostResult(ls.read({ key }), "host localStorage read failed");
506
+ return response.value !== void 0 ? fromHex(response.value) : void 0;
507
+ }
508
+ async function writeBytes(key, value) {
509
+ await unwrapHostResult(
510
+ ls.write({ key, value: toHex(value) }),
511
+ "host localStorage write failed"
512
+ );
513
+ }
514
+ async function readString(key) {
515
+ const bytes = await readBytes(key);
516
+ return bytes ? textDecoder.decode(bytes) : "";
517
+ }
518
+ async function writeString(key, value) {
519
+ return writeBytes(key, textEncoder.encode(value));
520
+ }
521
+ async function readJSON(key) {
522
+ const text = await readString(key);
523
+ return text ? JSON.parse(text) : null;
524
+ }
525
+ async function writeJSON(key, value) {
526
+ return writeString(key, JSON.stringify(value));
527
+ }
528
+ async function clear(key) {
529
+ await unwrapHostResult(ls.clear({ key }), "host localStorage clear failed");
530
+ }
531
+ return { readString, writeString, readJSON, writeJSON, readBytes, writeBytes, clear };
532
+ }
533
+ async function getHostLocalStorage() {
534
+ const client = await getClient();
535
+ return client ? adaptLocalStorage(client) : null;
536
+ }
537
+ async function createHostLocalStorage() {
538
+ return getHostLocalStorage();
539
+ }
540
+ async function getHostProvider(genesisHash) {
541
+ const client = await getClient();
542
+ if (!client) return null;
543
+ return resolveHostProvider(client, genesisHash);
544
+ }
545
+ async function resolveHostProvider(client, genesisHash) {
546
+ if (!await isChainSupportedByHost(client, genesisHash)) {
547
+ throw new ChainNotSupportedError(genesisHash);
548
+ }
549
+ return createHostPapiProvider(client, genesisHash);
550
+ }
551
+ function adaptStatementStore(client) {
552
+ const ss = client.statementStore;
553
+ return {
554
+ subscribe(filter, callback) {
555
+ const request = "matchAll" in filter ? { tag: "MatchAll", value: filter.matchAll } : { tag: "MatchAny", value: filter.matchAny };
556
+ return subscribeWithInterrupt(ss.subscribe({ request }), callback);
557
+ },
558
+ async createProofAuthorized(statement) {
559
+ const response = await unwrapHostResult(
560
+ ss.createProofAuthorized(statement),
561
+ "createProofAuthorized failed"
562
+ );
563
+ return response.proof;
564
+ },
565
+ async submit(signedStatement) {
566
+ await unwrapHostResult(ss.submit(signedStatement), "statement submit failed");
567
+ }
568
+ };
569
+ }
570
+ async function getStatementStore() {
571
+ const client = await getClient();
572
+ return client ? adaptStatementStore(client) : null;
573
+ }
574
+
575
+ // src/chains.ts
576
+ var BULLETIN_RPCS = {
577
+ paseo: ["wss://paseo-bulletin-next-rpc.polkadot.io"],
578
+ devnet: ["wss://bulletin-paseo.tservices.es:8443"],
579
+ polkadot: [],
580
+ kusama: []
581
+ };
582
+ var DEFAULT_BULLETIN_ENDPOINT = BULLETIN_RPCS.paseo[0];
583
+
584
+ // src/worker.ts
585
+ var WorkerCallError = class extends HostError {
586
+ /** Which of the frozen failure modes this was. */
587
+ tag;
588
+ constructor(tag, reason) {
589
+ super(reason ? `worker call failed: ${tag} (${reason})` : `worker call failed: ${tag}`);
590
+ this.name = "WorkerCallError";
591
+ this.tag = tag;
592
+ }
593
+ };
594
+ var ERROR_TAGS = [
595
+ "unavailable",
596
+ "denied",
597
+ "invalid",
598
+ "timeout",
599
+ "crashed",
600
+ "version"
601
+ ];
602
+ function readBridge() {
603
+ const host = globalThis.__polkadotHost;
604
+ const bridge = host?.workerCall;
605
+ return typeof bridge === "function" ? bridge : null;
606
+ }
607
+ function isErrorTag(value) {
608
+ return typeof value === "string" && ERROR_TAGS.includes(value);
609
+ }
610
+ function parseAnswer(raw) {
611
+ let answer;
612
+ try {
613
+ answer = JSON.parse(raw);
614
+ } catch {
615
+ throw new WorkerCallError("invalid", "host answer was not JSON");
616
+ }
617
+ if (answer && typeof answer === "object" && "error" in answer) {
618
+ const { error, reason } = answer;
619
+ throw new WorkerCallError(
620
+ isErrorTag(error) ? error : "unavailable",
621
+ typeof reason === "string" ? reason : void 0
622
+ );
623
+ }
624
+ return answer;
625
+ }
626
+ function getWorkerManager() {
627
+ return {
628
+ isAvailable() {
629
+ return readBridge() !== null;
630
+ },
631
+ async call(apiName, payload, options) {
632
+ const bridge = readBridge();
633
+ if (!bridge) {
634
+ throw new HostUnavailableError("no host worker bridge on this page");
635
+ }
636
+ const raw = await bridge(apiName, JSON.stringify(payload ?? {}), options?.deadlineMs);
637
+ return parseAnswer(raw);
638
+ }
639
+ };
640
+ }
641
+ var log3 = createLogger("host");
642
+ var TRANSIENT_FAILURE = /* @__PURE__ */ Symbol("transient-failure");
643
+ var PROBE_TIMEOUT_MS = 3e3;
644
+ var discoveryCache = /* @__PURE__ */ new WeakMap();
645
+ async function getHostChainInfo(identifiers) {
646
+ const client = await getClient();
647
+ if (!client) return null;
648
+ let bySet = discoveryCache.get(client);
649
+ if (!bySet) {
650
+ bySet = /* @__PURE__ */ new Map();
651
+ discoveryCache.set(client, bySet);
652
+ }
653
+ const key = [...identifiers].sort().join(",");
654
+ let cached = bySet.get(key);
655
+ if (!cached) {
656
+ cached = fetchChainInfo(client, identifiers).then((result) => {
657
+ if (result === TRANSIENT_FAILURE) {
658
+ bySet.delete(key);
659
+ return null;
660
+ }
661
+ return result;
662
+ });
663
+ bySet.set(key, cached);
664
+ }
665
+ return cached;
666
+ }
667
+ async function fetchChainInfo(client, identifiers) {
668
+ try {
669
+ let timer;
670
+ const probe = Promise.all(
671
+ identifiers.map(
672
+ (id) => client.chain.getChainInfo({ chain: id }).match(
673
+ (value) => ({ id, ok: value }),
674
+ (error) => ({ id, err: error })
675
+ )
676
+ )
677
+ );
678
+ const outcomes = await Promise.race([
679
+ probe,
680
+ new Promise((resolve) => {
681
+ timer = setTimeout(() => resolve("timeout"), PROBE_TIMEOUT_MS);
682
+ })
683
+ ]).finally(() => clearTimeout(timer));
684
+ if (outcomes === "timeout") {
685
+ log3.warn("getChainInfo probe timed out, treating the host as pre-discovery for now");
686
+ return TRANSIENT_FAILURE;
687
+ }
688
+ let network;
689
+ const chains = {};
690
+ for (const outcome of outcomes) {
691
+ if ("ok" in outcome) {
692
+ network = outcome.ok.network;
693
+ chains[outcome.id] = outcome.ok.genesisHash;
694
+ continue;
695
+ }
696
+ if (outcome.err.tag === "Unsupported") return null;
697
+ if (isNotSupported(outcome.err)) continue;
698
+ log3.warn(`getChainInfo failed: ${formatHostError(outcome.err)}`);
699
+ return TRANSIENT_FAILURE;
700
+ }
701
+ if (network === void 0) return null;
702
+ return { network, chains };
703
+ } catch (error) {
704
+ log3.warn(`getChainInfo failed: ${formatHostError(error)}`);
705
+ return TRANSIENT_FAILURE;
706
+ }
707
+ }
708
+ function isNotSupported(error) {
709
+ return error.tag === "Domain" && error.value.value.tag === "NotSupported";
710
+ }
711
+ function sameRingLocation(a, b) {
712
+ if (a.chainId.toLowerCase() !== b.chainId.toLowerCase() || a.junctions.length !== b.junctions.length) {
713
+ return false;
714
+ }
715
+ return a.junctions.every((junction, index) => {
716
+ const candidate = b.junctions[index];
717
+ if (junction.tag === "PalletInstance") {
718
+ return candidate.tag === "PalletInstance" && junction.value === candidate.value;
719
+ }
720
+ return candidate.tag === "CollectionId" && junction.value.toLowerCase() === candidate.value.toLowerCase();
721
+ });
722
+ }
723
+ function findRingVrfKeyHandle(keys, ring) {
724
+ return keys.find((key) => key.rings.some((candidate) => sameRingLocation(candidate, ring)))?.handle;
725
+ }
726
+ function deriveTxExtVersion(metadata) {
727
+ const versions = unifyMetadata(decAnyMetadata(metadata)).extrinsic.version;
728
+ if (versions.length === 0) {
729
+ throw new Error("No extrinsic version found in metadata");
730
+ }
731
+ const latestVersion = versions.reduce((acc, v) => Math.max(acc, v), 0);
732
+ return latestVersion === 4 ? 0 : latestVersion;
733
+ }
734
+ var deps = { deriveTxExtVersion };
735
+ function toHostExtensions(signedExtensions) {
736
+ return Object.values(signedExtensions).map((ext) => ({
737
+ id: ext.identifier,
738
+ extra: toHex(ext.value),
739
+ additionalSigned: toHex(ext.additionalSigned)
740
+ }));
741
+ }
742
+ function toWireProductAccountId({
743
+ dotNsIdentifier,
744
+ derivationIndex = 0
745
+ }) {
746
+ return { dotNsIdentifier, derivationIndex: { tag: "Index", value: derivationIndex } };
747
+ }
748
+ function adaptAccountsProvider(client) {
749
+ const account = client.account;
750
+ const signing = client.signing;
751
+ return {
752
+ getUserId() {
753
+ return account.getUserId().map((response) => ({
754
+ primaryUsername: response.primaryUsername
755
+ }));
756
+ },
757
+ requestLogin(reason) {
758
+ return account.requestLogin({ reason });
759
+ },
760
+ getProductAccount(dotNsIdentifier, derivationIndex = 0) {
761
+ return account.getAccount({
762
+ productAccountId: toWireProductAccountId({ dotNsIdentifier, derivationIndex })
763
+ }).map((response) => ({
764
+ publicKey: fromHex(response.account.publicKey),
765
+ dotNsIdentifier,
766
+ derivationIndex
767
+ }));
768
+ },
769
+ registerRingVrfKey(index, ring) {
770
+ return account.registerRingVrfKey({ index: { tag: "Index", value: index }, ring }).map(fromHex);
771
+ },
772
+ listRingVrfKeys(owner, disclosure = "Anonymized") {
773
+ return account.listRingVrfKeys({ owner, disclosure }).map(
774
+ (keys) => keys.map((key) => ({
775
+ ...key,
776
+ handle: key.handle,
777
+ publicKey: key.publicKey === void 0 ? void 0 : fromHex(key.publicKey)
778
+ }))
779
+ );
780
+ },
781
+ getProductAccountAlias(keyHandle, context, location) {
782
+ return account.getAccountAlias({
783
+ keyHandle,
784
+ context,
785
+ ringLocation: location
786
+ }).map((response) => ({
787
+ context: fromHex(response.context),
788
+ alias: fromHex(response.alias)
789
+ }));
790
+ },
791
+ getLegacyAccounts() {
792
+ return account.getLegacyAccounts().map(
793
+ (response) => response.accounts.map((a) => ({
794
+ publicKey: fromHex(a.publicKey),
795
+ name: a.name
796
+ }))
797
+ );
798
+ },
799
+ createRingVRFProof(keyHandle, context, location, message) {
800
+ return account.createAccountProof({
801
+ keyHandle,
802
+ context,
803
+ ringLocation: location,
804
+ message: toHex(message)
805
+ }).map((response) => ({
806
+ proof: fromHex(response.proof),
807
+ contextualAlias: {
808
+ context: fromHex(response.contextualAlias.context),
809
+ alias: fromHex(response.contextualAlias.alias)
810
+ },
811
+ ringIndex: response.ringIndex,
812
+ ringRevision: response.ringRevision
813
+ }));
814
+ },
815
+ ringVrfSign(keyHandle, message) {
816
+ return account.ringVrfSign({
817
+ keyHandle,
818
+ message: toHex(message)
819
+ }).map(fromHex);
820
+ },
821
+ signVrf(account_, transcriptLabel, items) {
822
+ return account.signVrf({
823
+ account: toWireProductAccountId(account_),
824
+ transcriptLabel: toHex(transcriptLabel),
825
+ items: items.map(({ label, value }) => ({
826
+ label: toHex(label),
827
+ value: toHex(value)
828
+ }))
829
+ }).map((response) => ({
830
+ preOutput: fromHex(response.preOutput),
831
+ proof: fromHex(response.proof)
832
+ }));
833
+ },
834
+ getProductAccountSigner(account_) {
835
+ const productAccountId = toWireProductAccountId(account_);
836
+ return {
837
+ publicKey: account_.publicKey,
838
+ async signTx(callData, signedExtensions, metadata) {
839
+ const checkGenesis = signedExtensions.CheckGenesis;
840
+ if (!checkGenesis) {
841
+ throw new Error("Can't find genesis hash on transaction");
842
+ }
843
+ const response = await unwrapHostResult(
844
+ signing.createTransaction({
845
+ signer: productAccountId,
846
+ genesisHash: toHex(checkGenesis.additionalSigned),
847
+ callData: toHex(callData),
848
+ extensions: toHostExtensions(signedExtensions),
849
+ txExtVersion: deps.deriveTxExtVersion(metadata)
850
+ }),
851
+ "createTransaction failed"
852
+ );
853
+ return fromHex(response.transaction);
854
+ },
855
+ async signBytes(data) {
856
+ const response = await unwrapHostResult(
857
+ signing.signRaw({
858
+ account: productAccountId,
859
+ payload: { tag: "Bytes", value: { bytes: toHex(data) } }
860
+ }),
861
+ "signRaw failed"
862
+ );
863
+ return fromHex(response.signature);
864
+ }
865
+ };
866
+ },
867
+ getLegacyAccountSigner(account_) {
868
+ const signerHex = toHex(account_.publicKey);
869
+ const ss58Address = AccountId().dec(account_.publicKey);
870
+ return {
871
+ publicKey: account_.publicKey,
872
+ async signTx(callData, signedExtensions, metadata) {
873
+ const checkGenesis = signedExtensions.CheckGenesis;
874
+ if (!checkGenesis) {
875
+ throw new Error("Can't find genesis hash on transaction");
876
+ }
877
+ const response = await unwrapHostResult(
878
+ signing.createTransactionWithLegacyAccount({
879
+ signer: signerHex,
880
+ genesisHash: toHex(checkGenesis.additionalSigned),
881
+ callData: toHex(callData),
882
+ extensions: toHostExtensions(signedExtensions),
883
+ txExtVersion: deps.deriveTxExtVersion(metadata)
884
+ }),
885
+ "createTransactionWithLegacyAccount failed"
886
+ );
887
+ return fromHex(response.transaction);
888
+ },
889
+ async signBytes(data) {
890
+ const response = await unwrapHostResult(
891
+ signing.signRawWithLegacyAccount({
892
+ signer: ss58Address,
893
+ payload: { tag: "Bytes", value: { bytes: toHex(data) } }
894
+ }),
895
+ "signRawWithLegacyAccount failed"
896
+ );
897
+ return fromHex(response.signature);
898
+ }
899
+ };
900
+ },
901
+ subscribeAccountConnectionStatus(callback) {
902
+ return subscribeWithInterrupt(account.connectionStatusSubscribe(), callback);
903
+ }
904
+ };
905
+ }
906
+ async function getAccountsProvider() {
907
+ const client = await getClient();
908
+ return client ? adaptAccountsProvider(client) : null;
909
+ }
910
+ var log4 = createLogger("host:permissions");
911
+ async function requestPermission(permission) {
912
+ const truApi = await getTruApi();
913
+ if (!truApi) {
914
+ return err(new HostUnavailableError("requestPermission: TruAPI unavailable"));
915
+ }
916
+ log4.debug("requestPermission", { tag: permission.tag });
917
+ return mapHostResult(
918
+ truApi.permissions.requestRemotePermission({ permission }),
919
+ (response) => response.granted,
920
+ "requestPermission failed"
921
+ );
922
+ }
923
+ async function requestDevicePermission(permission) {
924
+ const truApi = await getTruApi();
925
+ if (!truApi) {
926
+ return err(new HostUnavailableError("requestDevicePermission: TruAPI unavailable"));
927
+ }
928
+ log4.debug("requestDevicePermission", { permission });
929
+ return mapHostResult(
930
+ truApi.permissions.requestDevicePermission(permission),
931
+ (response) => response.granted,
932
+ "requestDevicePermission failed"
933
+ );
934
+ }
935
+
936
+ // src/theme.ts
937
+ function adaptThemeProvider(client) {
938
+ return {
939
+ subscribeTheme(callback) {
940
+ return subscribeWithInterrupt(client.theme.subscribe(), callback);
941
+ }
942
+ };
943
+ }
944
+ async function getThemeProvider() {
945
+ const client = await getClient();
946
+ return client ? adaptThemeProvider(client) : null;
947
+ }
948
+ var log5 = createLogger("host:entropy");
949
+ async function deriveEntropy(key) {
950
+ const truApi = await getTruApi();
951
+ if (!truApi) {
952
+ return err(new HostUnavailableError("deriveEntropy: TruAPI unavailable"));
953
+ }
954
+ log5.debug("deriveEntropy", { keyLen: key.length });
955
+ return mapHostResult(
956
+ truApi.entropy.derive({ context: toHex(key) }),
957
+ (response) => fromHex(response.entropy),
958
+ "deriveEntropy failed"
959
+ );
960
+ }
961
+
962
+ // src/chat.ts
963
+ function adaptChatManager(client) {
964
+ const chat = client.chat;
965
+ const roomStatus = /* @__PURE__ */ new Map();
966
+ const botStatus = /* @__PURE__ */ new Map();
967
+ return {
968
+ async registerRoom(request) {
969
+ const cached = roomStatus.get(request.roomId);
970
+ if (cached) return cached;
971
+ const response = await unwrapHostResult(
972
+ chat.createRoom(request),
973
+ "chat registerRoom failed"
974
+ );
975
+ roomStatus.set(request.roomId, response.status);
976
+ return response.status;
977
+ },
978
+ async registerBot(request) {
979
+ const cached = botStatus.get(request.botId);
980
+ if (cached) return cached;
981
+ const response = await unwrapHostResult(
982
+ chat.registerBot(request),
983
+ "chat registerBot failed"
984
+ );
985
+ botStatus.set(request.botId, response.status);
986
+ return response.status;
987
+ },
988
+ async sendMessage(roomId, payload) {
989
+ const response = await unwrapHostResult(
990
+ chat.postMessage({ roomId, payload }),
991
+ "chat sendMessage failed"
992
+ );
993
+ return { messageId: response.messageId };
994
+ },
995
+ subscribeChatList(callback) {
996
+ return subscribeWithInterrupt(chat.listSubscribe(), (item) => callback(item.rooms));
997
+ },
998
+ subscribeAction(callback) {
999
+ return subscribeWithInterrupt(chat.actionSubscribe(), callback);
1000
+ }
1001
+ };
1002
+ }
1003
+ async function getChatManager() {
1004
+ const client = await getClient();
1005
+ return client ? adaptChatManager(client) : null;
1006
+ }
1007
+
1008
+ // src/payments.ts
1009
+ function adaptPaymentManager(client) {
1010
+ const payment = client.payment;
1011
+ return {
1012
+ subscribeBalance(callback, purse) {
1013
+ return subscribeWithInterrupt(
1014
+ payment.balanceSubscribe({ request: { purse } }),
1015
+ callback
1016
+ );
1017
+ },
1018
+ topUp(amount, source, into) {
1019
+ return unwrapHostResult(
1020
+ payment.topUp({ into, amount, source }),
1021
+ "payment topUp failed"
1022
+ );
1023
+ },
1024
+ async requestPayment(amount, destination, from) {
1025
+ const response = await unwrapHostResult(
1026
+ payment.request({ from, amount, destination }),
1027
+ "payment requestPayment failed"
1028
+ );
1029
+ return { id: response.id };
1030
+ },
1031
+ subscribePaymentStatus(paymentId, callback) {
1032
+ return subscribeWithInterrupt(
1033
+ payment.statusSubscribe({ request: { paymentId } }),
1034
+ callback
1035
+ );
1036
+ }
1037
+ };
1038
+ }
1039
+ async function getPaymentManager() {
1040
+ const client = await getClient();
1041
+ return client ? adaptPaymentManager(client) : null;
1042
+ }
1043
+
1044
+ // src/notifications.ts
1045
+ function adaptNotificationManager(client) {
1046
+ const notifications = client.notifications;
1047
+ return {
1048
+ async push(input) {
1049
+ const response = await unwrapHostResult(
1050
+ notifications.sendPushNotification(input),
1051
+ "notification push failed"
1052
+ );
1053
+ return response.id;
1054
+ },
1055
+ async cancel(id) {
1056
+ await unwrapHostResult(
1057
+ notifications.cancelPushNotification({ id }),
1058
+ "notification cancel failed"
1059
+ );
1060
+ }
1061
+ };
1062
+ }
1063
+ async function getNotificationManager() {
1064
+ const client = await getClient();
1065
+ return client ? adaptNotificationManager(client) : null;
1066
+ }
1067
+ var log6 = createLogger("host:navigation");
1068
+ async function navigateTo(url) {
1069
+ const truApi = await getTruApi();
1070
+ if (!truApi) {
1071
+ return err(new HostUnavailableError("navigateTo: TruAPI unavailable"));
1072
+ }
1073
+ log6.debug("navigateTo", { url });
1074
+ return mapHostResult(truApi.system.navigateTo({ url }), () => void 0, "navigateTo failed");
1075
+ }
1076
+ var log7 = createLogger("host:features");
1077
+ async function featureSupported(feature) {
1078
+ const truApi = await getTruApi();
1079
+ if (!truApi) {
1080
+ return err(new HostUnavailableError("featureSupported: TruAPI unavailable"));
1081
+ }
1082
+ log7.debug("featureSupported", { tag: feature.tag });
1083
+ return mapHostResult(
1084
+ truApi.system.featureSupported({ tag: feature.tag, value: { genesisHash: feature.value } }),
1085
+ (response) => response.supported,
1086
+ "featureSupported failed"
1087
+ );
1088
+ }
1089
+ async function isChainSupported(genesisHash) {
1090
+ return featureSupported({ tag: "Chain", value: genesisHash });
1091
+ }
1092
+ var log8 = createLogger("host:chain-spec");
1093
+ async function getChainSpec(genesisHash) {
1094
+ const truApi = await getTruApi();
1095
+ if (!truApi) {
1096
+ log8.debug("getChainSpec: TruAPI unavailable");
1097
+ return ok(null);
1098
+ }
1099
+ log8.debug("getChainSpec", { genesisHash });
1100
+ const [genesisHashResult, nameResult, propertiesResult] = await Promise.all([
1101
+ mapHostResult(
1102
+ truApi.chain.getSpecGenesisHash({ genesisHash }),
1103
+ (response) => response.genesisHash,
1104
+ "getChainSpec (genesisHash) failed"
1105
+ ),
1106
+ mapHostResult(
1107
+ truApi.chain.getSpecChainName({ genesisHash }),
1108
+ (response) => response.chainName,
1109
+ "getChainSpec (chainName) failed"
1110
+ ),
1111
+ mapHostResult(
1112
+ truApi.chain.getSpecProperties({ genesisHash }),
1113
+ (response) => response.properties,
1114
+ "getChainSpec (properties) failed"
1115
+ )
1116
+ ]);
1117
+ if (!genesisHashResult.ok) return genesisHashResult;
1118
+ if (!nameResult.ok) return nameResult;
1119
+ if (!propertiesResult.ok) return propertiesResult;
1120
+ const propertiesRaw = propertiesResult.value;
1121
+ let properties;
1122
+ try {
1123
+ properties = JSON.parse(propertiesRaw);
1124
+ } catch (parseError) {
1125
+ log8.debug("getChainSpec: properties JSON parse failed", parseError);
1126
+ properties = null;
1127
+ }
1128
+ return ok({
1129
+ genesisHash: genesisHashResult.value,
1130
+ name: nameResult.value,
1131
+ properties,
1132
+ propertiesRaw
1133
+ });
1134
+ }
1135
+ var log9 = createLogger("host:chain-transaction");
1136
+ async function broadcastTransaction(genesisHash, transaction) {
1137
+ const truApi = await getTruApi();
1138
+ if (!truApi) {
1139
+ return err(new HostUnavailableError("broadcastTransaction: TruAPI unavailable"));
1140
+ }
1141
+ log9.debug("broadcastTransaction", { genesisHash });
1142
+ return mapHostResult(
1143
+ truApi.chain.broadcastTransaction({ genesisHash, transaction }),
1144
+ (response) => response.operationId ?? null,
1145
+ "broadcastTransaction failed"
1146
+ );
1147
+ }
1148
+ async function stopTransaction(genesisHash, operationId) {
1149
+ const truApi = await getTruApi();
1150
+ if (!truApi) {
1151
+ return err(new HostUnavailableError("stopTransaction: TruAPI unavailable"));
1152
+ }
1153
+ log9.debug("stopTransaction", { genesisHash, operationId });
1154
+ return mapHostResult(
1155
+ truApi.chain.stopTransaction({ genesisHash, operationId }),
1156
+ () => void 0,
1157
+ "stopTransaction failed"
1158
+ );
1159
+ }
1160
+
1161
+ export { BULLETIN_RPCS, ChainNotSupportedError, DEFAULT_BULLETIN_ENDPOINT, HostCallFailedError, HostError, HostUnavailableError, WorkerCallError, broadcastTransaction, createHostLocalStorage, createHostPreimageManager, createProofAuthorized, deriveEntropy, featureSupported, findRingVrfKeyHandle, formatHostError, fromHex, getAccountsProvider, getChainSpec, getChatManager, getHostChainInfo, getHostLocalStorage, getHostProvider, getNotificationManager, getPaymentManager, getPreimageManager, getStatementStore, getThemeProvider, getTruApi, getWorkerManager, isChainSupported, isHostError, isInsideContainer, navigateTo, requestDevicePermission, requestPermission, requestResourceAllocation, stopTransaction, toHex };
1162
+ //# sourceMappingURL=index.js.map
1163
+ //# sourceMappingURL=index.js.map