@parity/product-sdk-host 0.16.0 → 0.17.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/{chain-discovery-nLrPzb3d.d.ts → chain-discovery-BEvu7HaV.d.ts} +42 -5
- package/dist/chunk-PAKEUP2Q.js +89 -0
- package/dist/chunk-PAKEUP2Q.js.map +1 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +118 -29
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +9 -2
- package/dist/testing.js +3 -2
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
- package/src/accounts.ts +34 -10
- package/src/chains.ts +10 -3
- package/src/index.ts +5 -0
- package/src/papi-provider.ts +370 -37
- package/src/testing.ts +9 -2
- package/src/transport.ts +228 -4
- package/dist/chunk-GDXSV7JV.js +0 -50
- package/dist/chunk-GDXSV7JV.js.map +0 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { TrUApiClient, ChainIdentifier, HexString } from '@parity/truapi';
|
|
2
|
+
import { ConnectionStatus } from '@parity/truapi/sandbox';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Access to the in-house TruAPI client (`@parity/truapi`) for the host package.
|
|
5
6
|
*
|
|
6
|
-
* Environment detection
|
|
7
|
-
* `@parity/truapi/sandbox`; this module layers the
|
|
8
|
-
* top — an async {@link getClient} accessor
|
|
9
|
-
*
|
|
7
|
+
* Environment detection, the lazily-built cached client, and the connection-status
|
|
8
|
+
* signal come from `@parity/truapi/sandbox`; this module layers the
|
|
9
|
+
* product-sdk-specific glue on top — an async {@link getClient} accessor,
|
|
10
|
+
* {@link subscribeConnectionStatus}, and {@link subscribeWithInterrupt}, which
|
|
11
|
+
* adapts a truapi stream into the host's {@link HostSubscription} shape.
|
|
10
12
|
*
|
|
11
13
|
* @module
|
|
12
14
|
*/
|
|
@@ -20,6 +22,9 @@ import { TrUApiClient, ChainIdentifier, HexString } from '@parity/truapi';
|
|
|
20
22
|
* Calling this in a production build silently reroutes every host accessor to
|
|
21
23
|
* the injected client, so we warn — it almost always means a `/testing` import
|
|
22
24
|
* leaked into a production path.
|
|
25
|
+
*
|
|
26
|
+
* Injecting or clearing notifies {@link subscribeConnectionStatus} subscribers,
|
|
27
|
+
* so a product's "host lost" path can be exercised by disposing the fake host.
|
|
23
28
|
*/
|
|
24
29
|
declare function setTruApiClient(client: TrUApiClient | null): void;
|
|
25
30
|
/**
|
|
@@ -27,6 +32,38 @@ declare function setTruApiClient(client: TrUApiClient | null): void;
|
|
|
27
32
|
* sandbox heuristic (iframe / webview marker / injected message port).
|
|
28
33
|
*/
|
|
29
34
|
declare function isCorrectEnvironment(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Connection lifecycle of the host channel: `"connecting"` while the client waits
|
|
37
|
+
* for the host, `"connected"` once the channel is established, `"disconnected"`
|
|
38
|
+
* outside a host container or after the channel closes.
|
|
39
|
+
*
|
|
40
|
+
* Not the same concept as `@parity/product-sdk-signer`'s identically-shaped
|
|
41
|
+
* `ConnectionStatus`, which tracks a signer provider rather than the transport.
|
|
42
|
+
*/
|
|
43
|
+
type HostConnectionStatus = ConnectionStatus;
|
|
44
|
+
/**
|
|
45
|
+
* Test-only: push `status` to every {@link subscribeConnectionStatus} subscriber,
|
|
46
|
+
* so a product can exercise its reconnecting / offline UI. The host-side
|
|
47
|
+
* counterpart of `@parity/product-sdk-signer`'s `FakeSignerProvider.emitStatus`.
|
|
48
|
+
* Exposed through `@parity/product-sdk-host/testing`, not the main entry.
|
|
49
|
+
*/
|
|
50
|
+
declare function emitConnectionStatus(status: HostConnectionStatus): void;
|
|
51
|
+
/**
|
|
52
|
+
* Subscribe to host-channel connection status. The callback fires synchronously
|
|
53
|
+
* with the current status and again on every change; the returned function
|
|
54
|
+
* unsubscribes. Repeats of the status you already have are suppressed.
|
|
55
|
+
*
|
|
56
|
+
* This is the **transport** channel. For the host's account-level connection —
|
|
57
|
+
* what drives `@parity/product-sdk-signer`'s `ConnectionStatus` — use
|
|
58
|
+
* `AccountsProvider.subscribeAccountConnectionStatus` instead.
|
|
59
|
+
*
|
|
60
|
+
* Subscribing is not passive: outside an established channel the first subscribe
|
|
61
|
+
* builds the client and provider, so this can be what constructs the transport.
|
|
62
|
+
*
|
|
63
|
+
* Honours the `setTruApiClient` seam — an injected client is connected by
|
|
64
|
+
* definition, and injecting or clearing one notifies live subscribers.
|
|
65
|
+
*/
|
|
66
|
+
declare function subscribeConnectionStatus(callback: (status: HostConnectionStatus) => void): () => void;
|
|
30
67
|
|
|
31
68
|
/**
|
|
32
69
|
* Host chain discovery. Resolves chain roles to genesis hashes against the
|
|
@@ -67,4 +104,4 @@ interface HostChainDiscovery {
|
|
|
67
104
|
*/
|
|
68
105
|
declare function getHostChainInfo(identifiers: readonly HostChainIdentifier[]): Promise<HostChainDiscovery | null>;
|
|
69
106
|
|
|
70
|
-
export { type HostChainDiscovery as H, type HostChainIdentifier as a, getHostChainInfo as g, isCorrectEnvironment as i,
|
|
107
|
+
export { type HostChainDiscovery as H, type HostChainIdentifier as a, type HostConnectionStatus as b, setTruApiClient as c, emitConnectionStatus as e, getHostChainInfo as g, isCorrectEnvironment as i, subscribeConnectionStatus as s };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { isCorrectEnvironment as isCorrectEnvironment$1, subscribeConnectionStatus as subscribeConnectionStatus$1, getClientSync as getClientSync$1 } from '@parity/truapi/sandbox';
|
|
2
|
+
|
|
3
|
+
// src/transport.ts
|
|
4
|
+
var clientOverride = null;
|
|
5
|
+
var localStatusListeners = /* @__PURE__ */ new Set();
|
|
6
|
+
function isProductionBuild() {
|
|
7
|
+
try {
|
|
8
|
+
return process.env.NODE_ENV === "production";
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function setTruApiClient(client) {
|
|
14
|
+
if (client !== null && isProductionBuild()) {
|
|
15
|
+
console.warn(
|
|
16
|
+
"[product-sdk] setTruApiClient() was called in a production build. This is a test-only seam from @parity/product-sdk-host/testing; a leaked import will silently reroute all host access to the injected client."
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const wasOverridden = clientOverride !== null;
|
|
20
|
+
clientOverride = client;
|
|
21
|
+
if (wasOverridden !== (client !== null)) {
|
|
22
|
+
notifyLocalStatusListeners(client !== null ? "connected" : "disconnected");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getClientSync() {
|
|
26
|
+
return clientOverride ?? getClientSync$1();
|
|
27
|
+
}
|
|
28
|
+
function isCorrectEnvironment() {
|
|
29
|
+
return clientOverride !== null || isCorrectEnvironment$1();
|
|
30
|
+
}
|
|
31
|
+
async function getClient() {
|
|
32
|
+
return getClientSync();
|
|
33
|
+
}
|
|
34
|
+
function latchDisconnected(previous, next) {
|
|
35
|
+
return next === "connecting" && previous === "disconnected" ? "disconnected" : next;
|
|
36
|
+
}
|
|
37
|
+
function notifyLocalStatusListeners(status) {
|
|
38
|
+
for (const listener of [...localStatusListeners]) listener(status);
|
|
39
|
+
}
|
|
40
|
+
function emitConnectionStatus(status) {
|
|
41
|
+
if (isProductionBuild()) {
|
|
42
|
+
console.warn(
|
|
43
|
+
"[product-sdk] emitConnectionStatus() was called in a production build. This is a test-only seam from @parity/product-sdk-host/testing; a leaked import will report a fabricated connection status to real subscribers."
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
notifyLocalStatusListeners(status);
|
|
47
|
+
}
|
|
48
|
+
function subscribeConnectionStatus(callback) {
|
|
49
|
+
let last = null;
|
|
50
|
+
const deliver = (status, fromSandbox) => {
|
|
51
|
+
const next = fromSandbox ? latchDisconnected(last, status) : status;
|
|
52
|
+
if (next === last) return;
|
|
53
|
+
last = next;
|
|
54
|
+
callback(next);
|
|
55
|
+
};
|
|
56
|
+
const onLocal = (status) => deliver(status, false);
|
|
57
|
+
localStatusListeners.add(onLocal);
|
|
58
|
+
if (clientOverride !== null) {
|
|
59
|
+
onLocal("connected");
|
|
60
|
+
return () => void localStatusListeners.delete(onLocal);
|
|
61
|
+
}
|
|
62
|
+
const unsubscribeSandbox = subscribeConnectionStatus$1((status) => deliver(status, true));
|
|
63
|
+
return () => {
|
|
64
|
+
localStatusListeners.delete(onLocal);
|
|
65
|
+
unsubscribeSandbox();
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function subscribeWithInterrupt(observable, onNext) {
|
|
69
|
+
let interruptCallback;
|
|
70
|
+
const sub = observable.subscribe({
|
|
71
|
+
next: onNext,
|
|
72
|
+
error: (reason) => interruptCallback?.(reason),
|
|
73
|
+
complete: () => interruptCallback?.()
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
subscriptionId: sub.subscriptionId,
|
|
77
|
+
unsubscribe: () => sub.unsubscribe(),
|
|
78
|
+
onInterrupt: (callback) => {
|
|
79
|
+
interruptCallback = callback;
|
|
80
|
+
return () => {
|
|
81
|
+
if (interruptCallback === callback) interruptCallback = void 0;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { emitConnectionStatus, getClient, isCorrectEnvironment, setTruApiClient, subscribeConnectionStatus, subscribeWithInterrupt };
|
|
88
|
+
//# sourceMappingURL=chunk-PAKEUP2Q.js.map
|
|
89
|
+
//# sourceMappingURL=chunk-PAKEUP2Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transport.ts"],"names":["sandboxGetClientSync","sandboxIsCorrectEnvironment","sandboxSubscribeConnectionStatus"],"mappings":"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","file":"chunk-PAKEUP2Q.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Access to the in-house TruAPI client (`@parity/truapi`) for the host package.\n *\n * Environment detection, the lazily-built cached client, and the connection-status\n * signal come from `@parity/truapi/sandbox`; this module layers the\n * product-sdk-specific glue on top — an async {@link getClient} accessor,\n * {@link subscribeConnectionStatus}, and {@link subscribeWithInterrupt}, which\n * adapts a truapi stream into the host's {@link HostSubscription} shape.\n *\n * @module\n */\n\nimport type { ObservableLike, TrUApiClient } from \"@parity/truapi\";\nimport {\n type ConnectionStatus,\n getClientSync as sandboxGetClientSync,\n isCorrectEnvironment as sandboxIsCorrectEnvironment,\n subscribeConnectionStatus as sandboxSubscribeConnectionStatus,\n} from \"@parity/truapi/sandbox\";\n\nimport type { HostSubscription } from \"./types.js\";\n\n/** A {@link HostSubscription} carrying the transport-assigned subscription id. */\nexport interface TransportSubscription extends HostSubscription {\n readonly subscriptionId: string;\n}\n\n// Test-only override. When set — via `setTruApiClient`, exposed through\n// `@parity/product-sdk-host/testing` — every host accessor resolves this client\n// instead of the sandbox one. `null` in production, so the branches below are\n// no-ops there.\nlet clientOverride: TrUApiClient | null = null;\n\n// Status subscribers registered here rather than only in the sandbox, so that\n// flipping the test seam is an *event*. The sandbox tracks only the client it\n// built itself, so it cannot know an injected client appeared or went away.\nconst localStatusListeners = new Set<(status: HostConnectionStatus) => void>();\n\nfunction isProductionBuild(): boolean {\n try {\n // Must stay a plain `process.env.NODE_ENV` member expression: bundlers\n // (Vite, esbuild, webpack) substitute it textually, which is how this\n // check works in browser builds where `process` doesn't exist.\n return process.env.NODE_ENV === \"production\";\n } catch {\n // No `process` and no bundler define — can't tell, stay quiet.\n return false;\n }\n}\n\n/**\n * Test-only seam: force {@link getClient} / {@link getClientSync} to return\n * `client`, and {@link isCorrectEnvironment} to report `true`. Pass `null` to\n * restore normal detection. Exposed through `@parity/product-sdk-host/testing`,\n * not the package's main entry.\n *\n * Calling this in a production build silently reroutes every host accessor to\n * the injected client, so we warn — it almost always means a `/testing` import\n * leaked into a production path.\n *\n * Injecting or clearing notifies {@link subscribeConnectionStatus} subscribers,\n * so a product's \"host lost\" path can be exercised by disposing the fake host.\n */\nexport function setTruApiClient(client: TrUApiClient | null): void {\n if (client !== null && isProductionBuild()) {\n console.warn(\n \"[product-sdk] setTruApiClient() was called in a production build. This is a test-only seam from @parity/product-sdk-host/testing; a leaked import will silently reroute all host access to the injected client.\",\n );\n }\n const wasOverridden = clientOverride !== null;\n clientOverride = client;\n if (wasOverridden !== (client !== null)) {\n notifyLocalStatusListeners(client !== null ? \"connected\" : \"disconnected\");\n }\n}\n\n/**\n * Synchronous TruAPI client accessor. Returns the injected test client when one\n * is set, otherwise the sandbox client (`null` outside a host container).\n */\nexport function getClientSync(): TrUApiClient | null {\n return clientOverride ?? sandboxGetClientSync();\n}\n\n/**\n * Host-container detection. `true` when a test client is injected, otherwise the\n * sandbox heuristic (iframe / webview marker / injected message port).\n */\nexport function isCorrectEnvironment(): boolean {\n return clientOverride !== null || sandboxIsCorrectEnvironment();\n}\n\n/**\n * Get the TruAPI client. Returns `null` outside a host container. Async wrapper\n * over {@link getClientSync} for the host wrappers that already `await` it.\n */\nexport async function getClient(): Promise<TrUApiClient | null> {\n return getClientSync();\n}\n\n/**\n * Connection lifecycle of the host channel: `\"connecting\"` while the client waits\n * for the host, `\"connected\"` once the channel is established, `\"disconnected\"`\n * outside a host container or after the channel closes.\n *\n * Not the same concept as `@parity/product-sdk-signer`'s identically-shaped\n * `ConnectionStatus`, which tracks a signer provider rather than the transport.\n */\nexport type HostConnectionStatus = ConnectionStatus;\n\n/**\n * Correct one defect in the sandbox's status signal: `@parity/truapi` never clears\n * its cached client when the pipe closes, so a subscriber arriving after a\n * disconnect re-derives `\"connecting\"` from the dead client — and because the\n * sandbox fans every change out to all listeners, that rewrites everyone's state\n * with no way back. Hold `\"disconnected\"` until a real `\"connected\"` arrives.\n *\n * Applies to sandbox-sourced statuses only. A status pushed by the test seam is\n * deliberate and passes through, so a fake host can still drive a reconnect.\n *\n * Outstanding upstream, not tied to the version we happen to be on: `sandbox.js`\n * is byte-identical from 0.7.0 through 0.9.0 (npm latest) and still unfixed on\n * `paritytech/host-rust-core` main, the repo formerly named truapi. Remove once\n * it clears the cached client on close.\n */\nfunction latchDisconnected(\n previous: HostConnectionStatus | null,\n next: HostConnectionStatus,\n): HostConnectionStatus {\n return next === \"connecting\" && previous === \"disconnected\" ? \"disconnected\" : next;\n}\n\nfunction notifyLocalStatusListeners(status: HostConnectionStatus): void {\n // Iterate a snapshot: a listener that unsubscribes itself, or re-enters\n // `setTruApiClient`, must not mutate the set mid-loop.\n for (const listener of [...localStatusListeners]) listener(status);\n}\n\n/**\n * Test-only: push `status` to every {@link subscribeConnectionStatus} subscriber,\n * so a product can exercise its reconnecting / offline UI. The host-side\n * counterpart of `@parity/product-sdk-signer`'s `FakeSignerProvider.emitStatus`.\n * Exposed through `@parity/product-sdk-host/testing`, not the main entry.\n */\nexport function emitConnectionStatus(status: HostConnectionStatus): void {\n if (isProductionBuild()) {\n console.warn(\n \"[product-sdk] emitConnectionStatus() was called in a production build. This is a test-only seam from @parity/product-sdk-host/testing; a leaked import will report a fabricated connection status to real subscribers.\",\n );\n }\n notifyLocalStatusListeners(status);\n}\n\n/**\n * Subscribe to host-channel connection status. The callback fires synchronously\n * with the current status and again on every change; the returned function\n * unsubscribes. Repeats of the status you already have are suppressed.\n *\n * This is the **transport** channel. For the host's account-level connection —\n * what drives `@parity/product-sdk-signer`'s `ConnectionStatus` — use\n * `AccountsProvider.subscribeAccountConnectionStatus` instead.\n *\n * Subscribing is not passive: outside an established channel the first subscribe\n * builds the client and provider, so this can be what constructs the transport.\n *\n * Honours the `setTruApiClient` seam — an injected client is connected by\n * definition, and injecting or clearing one notifies live subscribers.\n */\nexport function subscribeConnectionStatus(\n callback: (status: HostConnectionStatus) => void,\n): () => void {\n let last: HostConnectionStatus | null = null;\n\n // One wrapped callback for both sources, so `last` stays coherent: the seam\n // and the sandbox must not each keep their own idea of what was delivered.\n const deliver = (status: HostConnectionStatus, fromSandbox: boolean): void => {\n const next = fromSandbox ? latchDisconnected(last, status) : status;\n if (next === last) return;\n last = next;\n callback(next);\n };\n\n const onLocal = (status: HostConnectionStatus) => deliver(status, false);\n localStatusListeners.add(onLocal);\n\n if (clientOverride !== null) {\n onLocal(\"connected\");\n return () => void localStatusListeners.delete(onLocal);\n }\n\n const unsubscribeSandbox = sandboxSubscribeConnectionStatus((status) => deliver(status, true));\n return () => {\n localStatusListeners.delete(onLocal);\n unsubscribeSandbox();\n };\n}\n\n/**\n * Adapt a truapi `ObservableLike` stream into the host's callback-style\n * {@link HostSubscription} (`unsubscribe` + `onInterrupt`). `onNext` fires for\n * each item; the registered `onInterrupt` callback fires when the host ends the\n * subscription server-side — which the generated client surfaces as either\n * `complete` (a host interrupt frame) or `error` (transport close). Shared by\n * the statement-store and preimage adapters, which both expose this shape.\n */\nexport function subscribeWithInterrupt<Item, Reason = never>(\n observable: ObservableLike<Item, Reason>,\n onNext: (item: Item) => void,\n): TransportSubscription {\n let interruptCallback: ((reason?: unknown) => void) | undefined;\n const sub = observable.subscribe({\n next: onNext,\n error: (reason) => interruptCallback?.(reason),\n complete: () => interruptCallback?.(),\n });\n return {\n subscriptionId: sub.subscriptionId,\n unsubscribe: () => sub.unsubscribe(),\n onInterrupt: (callback) => {\n interruptCallback = callback;\n return () => {\n if (interruptCallback === callback) interruptCallback = undefined;\n };\n },\n };\n}\n\nif (import.meta.vitest) {\n const { test, expect, afterEach } = import.meta.vitest;\n\n afterEach(() => setTruApiClient(null));\n\n // Environment detection and client building are covered by `@parity/truapi`'s\n // own sandbox tests; here we only assert the local glue degrades outside a\n // host container.\n test(\"getClientSync returns null outside a container\", () => {\n expect(getClientSync()).toBeNull();\n });\n\n test(\"getClient resolves null outside a container\", async () => {\n expect(await getClient()).toBeNull();\n });\n\n test(\"setTruApiClient overrides the client and container detection\", async () => {\n const fake = {} as TrUApiClient;\n setTruApiClient(fake);\n expect(getClientSync()).toBe(fake);\n expect(await getClient()).toBe(fake);\n expect(isCorrectEnvironment()).toBe(true);\n\n setTruApiClient(null);\n expect(getClientSync()).toBeNull();\n expect(isCorrectEnvironment()).toBe(false);\n });\n\n test(\"setTruApiClient warns when injecting in a production build\", () => {\n const original = process.env.NODE_ENV;\n const warnings: string[] = [];\n const realWarn = console.warn;\n console.warn = (...args: unknown[]) => void warnings.push(String(args[0]));\n try {\n process.env.NODE_ENV = \"production\";\n setTruApiClient({} as TrUApiClient);\n expect(warnings).toHaveLength(1);\n expect(warnings[0]).toContain(\"production build\");\n\n // Clearing the override must not warn.\n setTruApiClient(null);\n expect(warnings).toHaveLength(1);\n } finally {\n console.warn = realWarn;\n process.env.NODE_ENV = original;\n }\n });\n\n test(\"subscribeConnectionStatus reports disconnected outside a container\", () => {\n const statuses: HostConnectionStatus[] = [];\n\n const unsubscribe = subscribeConnectionStatus((status) => statuses.push(status));\n\n expect(statuses).toEqual([\"disconnected\"]);\n unsubscribe();\n });\n\n test(\"subscribeConnectionStatus reports connected for an injected client\", () => {\n setTruApiClient({} as TrUApiClient);\n const statuses: HostConnectionStatus[] = [];\n\n const unsubscribe = subscribeConnectionStatus((status) => statuses.push(status));\n\n // The sandbox only tracks the client it built itself, so without the\n // override branch this would report \"disconnected\" while every other\n // accessor resolved the injected client.\n expect(statuses).toEqual([\"connected\"]);\n unsubscribe();\n });\n\n test(\"disposing the injected client notifies live subscribers\", () => {\n setTruApiClient({} as TrUApiClient);\n const statuses: HostConnectionStatus[] = [];\n const unsubscribe = subscribeConnectionStatus((status) => statuses.push(status));\n\n setTruApiClient(null);\n\n expect(statuses).toEqual([\"connected\", \"disconnected\"]);\n unsubscribe();\n });\n\n test(\"injecting a client notifies subscribers that started without one\", () => {\n const statuses: HostConnectionStatus[] = [];\n const unsubscribe = subscribeConnectionStatus((status) => statuses.push(status));\n\n setTruApiClient({} as TrUApiClient);\n\n expect(statuses).toEqual([\"disconnected\", \"connected\"]);\n unsubscribe();\n });\n\n test(\"unsubscribe stops seam notifications\", () => {\n setTruApiClient({} as TrUApiClient);\n const statuses: HostConnectionStatus[] = [];\n const unsubscribe = subscribeConnectionStatus((status) => statuses.push(status));\n\n unsubscribe();\n setTruApiClient(null);\n\n expect(statuses).toEqual([\"connected\"]);\n expect(localStatusListeners.size).toBe(0);\n });\n\n test(\"emitConnectionStatus drives transitions, including a reconnect\", () => {\n setTruApiClient({} as TrUApiClient);\n const statuses: HostConnectionStatus[] = [];\n const unsubscribe = subscribeConnectionStatus((status) => statuses.push(status));\n\n emitConnectionStatus(\"disconnected\");\n // A seam-pushed \"connecting\" after \"disconnected\" is deliberate, so it must\n // survive the sandbox latch — otherwise no test could drive a reconnect.\n emitConnectionStatus(\"connecting\");\n emitConnectionStatus(\"connected\");\n\n expect(statuses).toEqual([\"connected\", \"disconnected\", \"connecting\", \"connected\"]);\n unsubscribe();\n });\n\n test(\"repeats of the current status are suppressed\", () => {\n setTruApiClient({} as TrUApiClient);\n const statuses: HostConnectionStatus[] = [];\n const unsubscribe = subscribeConnectionStatus((status) => statuses.push(status));\n\n emitConnectionStatus(\"connected\");\n emitConnectionStatus(\"connected\");\n\n expect(statuses).toEqual([\"connected\"]);\n unsubscribe();\n });\n\n test(\"a listener that unsubscribes itself mid-notification is safe\", () => {\n setTruApiClient({} as TrUApiClient);\n const statuses: HostConnectionStatus[] = [];\n const handle: { unsubscribe?: () => void } = {};\n handle.unsubscribe = subscribeConnectionStatus((status) => {\n statuses.push(status);\n handle.unsubscribe?.();\n });\n\n setTruApiClient(null);\n\n expect(statuses).toEqual([\"connected\", \"disconnected\"]);\n expect(localStatusListeners.size).toBe(0);\n });\n\n // The sandbox latch can't be driven through the public surface — it needs a\n // real provider close — so the correction is pinned as a pure function.\n test(\"latchDisconnected holds disconnected through the stale-cache connecting\", () => {\n expect(latchDisconnected(\"disconnected\", \"connecting\")).toBe(\"disconnected\");\n });\n\n test(\"latchDisconnected passes every other transition through\", () => {\n expect(latchDisconnected(null, \"disconnected\")).toBe(\"disconnected\");\n expect(latchDisconnected(null, \"connecting\")).toBe(\"connecting\");\n expect(latchDisconnected(\"connecting\", \"connected\")).toBe(\"connected\");\n expect(latchDisconnected(\"connected\", \"disconnected\")).toBe(\"disconnected\");\n // A genuine reconnect still gets through once the channel re-establishes.\n expect(latchDisconnected(\"connected\", \"connecting\")).toBe(\"connecting\");\n });\n\n test(\"subscribeWithInterrupt preserves the transport subscription id\", () => {\n const observable = {\n subscribe: () => ({\n subscriptionId: \"p:17\",\n unsubscribe: () => {},\n }),\n [Symbol.observable]() {\n return this;\n },\n } as ObservableLike<never>;\n\n const subscription = subscribeWithInterrupt(observable, () => {});\n\n expect(subscription.subscriptionId).toBe(\"p:17\");\n });\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ import { SdkError } from '@parity/product-sdk-errors';
|
|
|
5
5
|
export { SdkError, isSdkError } from '@parity/product-sdk-errors';
|
|
6
6
|
import { Result } from '@parity/result';
|
|
7
7
|
export { Result, err, ok } from '@parity/result';
|
|
8
|
-
export { H as HostChainDiscovery, a as HostChainIdentifier, g as getHostChainInfo, i as isInsideContainerSync } from './chain-discovery-
|
|
8
|
+
export { H as HostChainDiscovery, a as HostChainIdentifier, b as HostConnectionStatus, g as getHostChainInfo, i as isInsideContainerSync, s as subscribeConnectionStatus } from './chain-discovery-BEvu7HaV.js';
|
|
9
9
|
import { ResultAsync as ResultAsync$1 } from 'neverthrow';
|
|
10
|
+
import '@parity/truapi/sandbox';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Public types for the host wrappers.
|
|
@@ -156,12 +157,14 @@ declare function getStatementStore(): Promise<HostStatementStore | null>;
|
|
|
156
157
|
* chain-specific endpoints used by multiple packages.
|
|
157
158
|
*/
|
|
158
159
|
/**
|
|
159
|
-
* Bulletin Chain RPC endpoints per network environment. `paseo` (Paseo Next v2)
|
|
160
|
-
*
|
|
161
|
-
* `kusama` are reserved for when
|
|
160
|
+
* Bulletin Chain RPC endpoints per network environment. `paseo` (Paseo Next v2),
|
|
161
|
+
* `previewnet` (zombienet, a step ahead of paseo), and `devnet` (public Paseo
|
|
162
|
+
* testnet) are populated today; `polkadot` and `kusama` are reserved for when
|
|
163
|
+
* those Bulletin deployments go live.
|
|
162
164
|
*/
|
|
163
165
|
declare const BULLETIN_RPCS: {
|
|
164
166
|
readonly paseo: readonly ["wss://paseo-bulletin-next-rpc.polkadot.io"];
|
|
167
|
+
readonly previewnet: readonly ["wss://previewnet.substrate.dev/bulletin"];
|
|
165
168
|
readonly devnet: readonly ["wss://bulletin-paseo.tservices.es:8443"];
|
|
166
169
|
readonly polkadot: string[];
|
|
167
170
|
readonly kusama: string[];
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getClient, isCorrectEnvironment, subscribeWithInterrupt } from './chunk-
|
|
2
|
-
export { isCorrectEnvironment as isInsideContainerSync } from './chunk-
|
|
1
|
+
import { getClient, isCorrectEnvironment, subscribeWithInterrupt } from './chunk-PAKEUP2Q.js';
|
|
2
|
+
export { isCorrectEnvironment as isInsideContainerSync, subscribeConnectionStatus } from './chunk-PAKEUP2Q.js';
|
|
3
3
|
import { createLogger } from '@parity/product-sdk-logger';
|
|
4
4
|
import { scale } from '@parity/truapi';
|
|
5
5
|
import { err, ok } from '@parity/result';
|
|
@@ -68,6 +68,23 @@ function isHostError(error) {
|
|
|
68
68
|
var log = createLogger("host:papi");
|
|
69
69
|
var JSON_RPC_INTERNAL_ERROR = -32603;
|
|
70
70
|
var JSON_RPC_METHOD_NOT_FOUND = -32601;
|
|
71
|
+
function followOperationId(item) {
|
|
72
|
+
switch (item.tag) {
|
|
73
|
+
case "OperationBodyDone":
|
|
74
|
+
case "OperationCallDone":
|
|
75
|
+
case "OperationStorageItems":
|
|
76
|
+
case "OperationStorageDone":
|
|
77
|
+
case "OperationWaitingForContinue":
|
|
78
|
+
case "OperationInaccessible":
|
|
79
|
+
case "OperationError":
|
|
80
|
+
return item.value.operationId;
|
|
81
|
+
default:
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function isTerminalOperationItem(item) {
|
|
86
|
+
return item.tag === "OperationBodyDone" || item.tag === "OperationCallDone" || item.tag === "OperationStorageDone" || item.tag === "OperationInaccessible" || item.tag === "OperationError";
|
|
87
|
+
}
|
|
71
88
|
var STORAGE_TYPE_MAP = {
|
|
72
89
|
value: "Value",
|
|
73
90
|
hash: "Hash",
|
|
@@ -174,6 +191,8 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
174
191
|
return (onMessage) => {
|
|
175
192
|
const activeFollows = /* @__PURE__ */ new Map();
|
|
176
193
|
const activeBroadcasts = /* @__PURE__ */ new Set();
|
|
194
|
+
const followOperations = /* @__PURE__ */ new Map();
|
|
195
|
+
const pendingOperationStarts = /* @__PURE__ */ new Map();
|
|
177
196
|
function sendJsonRpcResponse(id, result) {
|
|
178
197
|
onMessage({ jsonrpc: "2.0", id, result });
|
|
179
198
|
}
|
|
@@ -187,6 +206,72 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
187
206
|
params: { subscription, result: event }
|
|
188
207
|
});
|
|
189
208
|
}
|
|
209
|
+
function forwardFollowItem(followSubscriptionId, item) {
|
|
210
|
+
const operationId = followOperationId(item);
|
|
211
|
+
if (operationId === void 0) {
|
|
212
|
+
sendFollowEvent(followSubscriptionId, convertFollowEventToJsonRpc(item));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const operations = followOperations.get(followSubscriptionId);
|
|
216
|
+
if (!operations) return;
|
|
217
|
+
let operation = operations.get(operationId);
|
|
218
|
+
if (!operation) {
|
|
219
|
+
if ((pendingOperationStarts.get(followSubscriptionId) ?? 0) === 0) {
|
|
220
|
+
sendFollowEvent(followSubscriptionId, convertFollowEventToJsonRpc(item));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
operation = { announced: false, items: [] };
|
|
224
|
+
operations.set(operationId, operation);
|
|
225
|
+
}
|
|
226
|
+
if (!operation.announced) {
|
|
227
|
+
operation.items.push(item);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
sendFollowEvent(followSubscriptionId, convertFollowEventToJsonRpc(item));
|
|
231
|
+
if (isTerminalOperationItem(item)) {
|
|
232
|
+
operations.delete(operationId);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function sendOperationStartedResponse(id, followSubscriptionId, result) {
|
|
236
|
+
sendJsonRpcResponse(id, convertOperationResultToJsonRpc(result));
|
|
237
|
+
if (result.tag !== "Started") return;
|
|
238
|
+
const operations = followOperations.get(followSubscriptionId);
|
|
239
|
+
if (!operations) return;
|
|
240
|
+
const operationId = result.value.operationId;
|
|
241
|
+
let operation = operations.get(operationId);
|
|
242
|
+
if (!operation) {
|
|
243
|
+
operation = { announced: true, items: [] };
|
|
244
|
+
operations.set(operationId, operation);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
operation.announced = true;
|
|
248
|
+
const pendingItems = operation.items;
|
|
249
|
+
operation.items = [];
|
|
250
|
+
for (const item of pendingItems) {
|
|
251
|
+
forwardFollowItem(followSubscriptionId, item);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function startOperationRequest(id, followSubscriptionId) {
|
|
255
|
+
const pending = pendingOperationStarts.get(followSubscriptionId);
|
|
256
|
+
if (pending !== void 0) {
|
|
257
|
+
pendingOperationStarts.set(followSubscriptionId, pending + 1);
|
|
258
|
+
}
|
|
259
|
+
const settle = () => {
|
|
260
|
+
const outstanding = pendingOperationStarts.get(followSubscriptionId);
|
|
261
|
+
if (outstanding === void 0) return;
|
|
262
|
+
pendingOperationStarts.set(followSubscriptionId, Math.max(0, outstanding - 1));
|
|
263
|
+
};
|
|
264
|
+
return {
|
|
265
|
+
ok: (response) => {
|
|
266
|
+
settle();
|
|
267
|
+
sendOperationStartedResponse(id, followSubscriptionId, response.operation);
|
|
268
|
+
},
|
|
269
|
+
err: (error) => {
|
|
270
|
+
settle();
|
|
271
|
+
hostError(id)(error);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
190
275
|
const hostError = (id) => (error) => sendJsonRpcError(id, JSON_RPC_INTERNAL_ERROR, formatHostError(error));
|
|
191
276
|
function handleMessage(message) {
|
|
192
277
|
const { id, method } = message;
|
|
@@ -199,8 +284,10 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
199
284
|
const forwardItem = (followSubscriptionId2, item) => {
|
|
200
285
|
if (item.tag === "Stop" && activeFollows.delete(followSubscriptionId2)) {
|
|
201
286
|
ref.handle?.unsubscribe();
|
|
287
|
+
followOperations.delete(followSubscriptionId2);
|
|
288
|
+
pendingOperationStarts.delete(followSubscriptionId2);
|
|
202
289
|
}
|
|
203
|
-
|
|
290
|
+
forwardFollowItem(followSubscriptionId2, item);
|
|
204
291
|
};
|
|
205
292
|
ref.handle = subscribeWithInterrupt(
|
|
206
293
|
chain.followHeadSubscribe({ request: { genesisHash, withRuntime } }),
|
|
@@ -224,11 +311,15 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
224
311
|
break;
|
|
225
312
|
}
|
|
226
313
|
ref.handle.onInterrupt(() => {
|
|
314
|
+
followOperations.delete(followSubscriptionId);
|
|
315
|
+
pendingOperationStarts.delete(followSubscriptionId);
|
|
227
316
|
if (activeFollows.delete(followSubscriptionId)) {
|
|
228
317
|
sendFollowEvent(followSubscriptionId, { event: "stop" });
|
|
229
318
|
}
|
|
230
319
|
});
|
|
231
320
|
activeFollows.set(followSubscriptionId, ref.handle);
|
|
321
|
+
followOperations.set(followSubscriptionId, /* @__PURE__ */ new Map());
|
|
322
|
+
pendingOperationStarts.set(followSubscriptionId, 0);
|
|
232
323
|
sendJsonRpcResponse(id, followSubscriptionId);
|
|
233
324
|
for (const item of pendingItems) {
|
|
234
325
|
forwardItem(followSubscriptionId, item);
|
|
@@ -242,6 +333,8 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
242
333
|
follow.unsubscribe();
|
|
243
334
|
activeFollows.delete(followSubId);
|
|
244
335
|
}
|
|
336
|
+
followOperations.delete(followSubId);
|
|
337
|
+
pendingOperationStarts.delete(followSubId);
|
|
245
338
|
sendJsonRpcResponse(id, null);
|
|
246
339
|
break;
|
|
247
340
|
}
|
|
@@ -255,13 +348,8 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
255
348
|
}
|
|
256
349
|
case "chainHead_v1_body": {
|
|
257
350
|
const [followSubscriptionId, hash] = params;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
id,
|
|
261
|
-
convertOperationResultToJsonRpc(response.operation)
|
|
262
|
-
),
|
|
263
|
-
hostError(id)
|
|
264
|
-
);
|
|
351
|
+
const bodyStart = startOperationRequest(id, followSubscriptionId);
|
|
352
|
+
chain.getHeadBody({ genesisHash, followSubscriptionId, hash }).match(bodyStart.ok, bodyStart.err);
|
|
265
353
|
break;
|
|
266
354
|
}
|
|
267
355
|
case "chainHead_v1_storage": {
|
|
@@ -270,6 +358,7 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
270
358
|
key: item.key,
|
|
271
359
|
queryType: convertStorageType(item.type)
|
|
272
360
|
}));
|
|
361
|
+
const storageStart = startOperationRequest(id, followSubscriptionId);
|
|
273
362
|
chain.getHeadStorage({
|
|
274
363
|
genesisHash,
|
|
275
364
|
followSubscriptionId,
|
|
@@ -281,30 +370,19 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
281
370
|
// the inner Hex codec on `null`, which throws
|
|
282
371
|
// (`null.startsWith`). Coerce `null` → `undefined`.
|
|
283
372
|
childTrie: childTrie ?? void 0
|
|
284
|
-
}).match(
|
|
285
|
-
(response) => sendJsonRpcResponse(
|
|
286
|
-
id,
|
|
287
|
-
convertOperationResultToJsonRpc(response.operation)
|
|
288
|
-
),
|
|
289
|
-
hostError(id)
|
|
290
|
-
);
|
|
373
|
+
}).match(storageStart.ok, storageStart.err);
|
|
291
374
|
break;
|
|
292
375
|
}
|
|
293
376
|
case "chainHead_v1_call": {
|
|
294
377
|
const [followSubscriptionId, hash, fn, callParameters] = params;
|
|
378
|
+
const callStart = startOperationRequest(id, followSubscriptionId);
|
|
295
379
|
chain.callHead({
|
|
296
380
|
genesisHash,
|
|
297
381
|
followSubscriptionId,
|
|
298
382
|
hash,
|
|
299
383
|
function: fn,
|
|
300
384
|
callParameters
|
|
301
|
-
}).match(
|
|
302
|
-
(response) => sendJsonRpcResponse(
|
|
303
|
-
id,
|
|
304
|
-
convertOperationResultToJsonRpc(response.operation)
|
|
305
|
-
),
|
|
306
|
-
hostError(id)
|
|
307
|
-
);
|
|
385
|
+
}).match(callStart.ok, callStart.err);
|
|
308
386
|
break;
|
|
309
387
|
}
|
|
310
388
|
case "chainHead_v1_unpin": {
|
|
@@ -320,7 +398,10 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
320
398
|
}
|
|
321
399
|
case "chainHead_v1_stopOperation": {
|
|
322
400
|
const [followSubscriptionId, operationId] = params;
|
|
323
|
-
chain.stopHeadOperation({ genesisHash, followSubscriptionId, operationId }).match(() =>
|
|
401
|
+
chain.stopHeadOperation({ genesisHash, followSubscriptionId, operationId }).match(() => {
|
|
402
|
+
followOperations.get(followSubscriptionId)?.delete(operationId);
|
|
403
|
+
sendJsonRpcResponse(id, null);
|
|
404
|
+
}, hostError(id));
|
|
324
405
|
break;
|
|
325
406
|
}
|
|
326
407
|
case "chainSpec_v1_genesisHash": {
|
|
@@ -387,6 +468,8 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
387
468
|
handle.unsubscribe();
|
|
388
469
|
}
|
|
389
470
|
activeFollows.clear();
|
|
471
|
+
followOperations.clear();
|
|
472
|
+
pendingOperationStarts.clear();
|
|
390
473
|
for (const operationId of activeBroadcasts) {
|
|
391
474
|
chain.stopTransaction({ genesisHash, operationId }).match(
|
|
392
475
|
() => {
|
|
@@ -575,6 +658,7 @@ async function getStatementStore() {
|
|
|
575
658
|
// src/chains.ts
|
|
576
659
|
var BULLETIN_RPCS = {
|
|
577
660
|
paseo: ["wss://paseo-bulletin-next-rpc.polkadot.io"],
|
|
661
|
+
previewnet: ["wss://previewnet.substrate.dev/bulletin"],
|
|
578
662
|
devnet: ["wss://bulletin-paseo.tservices.es:8443"],
|
|
579
663
|
polkadot: [],
|
|
580
664
|
kusama: []
|
|
@@ -665,13 +749,18 @@ function sameRingLocation(a, b) {
|
|
|
665
749
|
function findRingVrfKeyHandle(keys, ring) {
|
|
666
750
|
return keys.find((key) => key.rings.some((candidate) => sameRingLocation(candidate, ring)))?.handle;
|
|
667
751
|
}
|
|
668
|
-
function
|
|
669
|
-
const versions = unifyMetadata(decAnyMetadata(metadata)).extrinsic.version;
|
|
752
|
+
function selectHostTxExtVersion(versions) {
|
|
670
753
|
if (versions.length === 0) {
|
|
671
754
|
throw new Error("No extrinsic version found in metadata");
|
|
672
755
|
}
|
|
673
|
-
|
|
674
|
-
|
|
756
|
+
if (versions.includes(4)) {
|
|
757
|
+
return 0;
|
|
758
|
+
}
|
|
759
|
+
return versions.reduce((acc, version) => Math.max(acc, version), 0);
|
|
760
|
+
}
|
|
761
|
+
function deriveTxExtVersion(metadata) {
|
|
762
|
+
const versions = unifyMetadata(decAnyMetadata(metadata)).extrinsic.version;
|
|
763
|
+
return selectHostTxExtVersion(versions);
|
|
675
764
|
}
|
|
676
765
|
var deps = { deriveTxExtVersion };
|
|
677
766
|
function toHostExtensions(signedExtensions) {
|