@parity/product-sdk-host 0.14.0 → 0.15.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/{chunk-3SWF5CWC.js → chunk-GDXSV7JV.js} +3 -2
- package/dist/chunk-GDXSV7JV.js.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.js +41 -17
- package/dist/index.js.map +1 -1
- package/dist/testing.js +3 -2
- package/dist/testing.js.map +1 -1
- package/package.json +4 -4
- package/src/accounts.ts +43 -17
- package/src/index.ts +1 -0
- package/src/papi-provider.ts +94 -26
- package/src/payments.ts +1 -1
- package/src/testing.ts +1 -0
- package/src/transport.ts +23 -1
- package/src/truapi.ts +2 -0
- package/dist/chunk-3SWF5CWC.js.map +0 -1
|
@@ -34,6 +34,7 @@ function subscribeWithInterrupt(observable, onNext) {
|
|
|
34
34
|
complete: () => interruptCallback?.()
|
|
35
35
|
});
|
|
36
36
|
return {
|
|
37
|
+
subscriptionId: sub.subscriptionId,
|
|
37
38
|
unsubscribe: () => sub.unsubscribe(),
|
|
38
39
|
onInterrupt: (callback) => {
|
|
39
40
|
interruptCallback = callback;
|
|
@@ -45,5 +46,5 @@ function subscribeWithInterrupt(observable, onNext) {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export { getClient, isCorrectEnvironment, setTruApiClient, subscribeWithInterrupt };
|
|
48
|
-
//# sourceMappingURL=chunk-
|
|
49
|
-
//# sourceMappingURL=chunk-
|
|
49
|
+
//# sourceMappingURL=chunk-GDXSV7JV.js.map
|
|
50
|
+
//# sourceMappingURL=chunk-GDXSV7JV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transport.ts"],"names":["sandboxGetClientSync","sandboxIsCorrectEnvironment"],"mappings":";;;AA8BA,IAAI,cAAA,GAAsC,IAAA;AAE1C,SAAS,iBAAA,GAA6B;AAClC,EAAA,IAAI;AAIA,IAAA,OAAO,OAAA,CAAQ,IAAI,QAAA,KAAa,YAAA;AAAA,EACpC,CAAA,CAAA,MAAQ;AAEJ,IAAA,OAAO,KAAA;AAAA,EACX;AACJ;AAYO,SAAS,gBAAgB,MAAA,EAAmC;AAC/D,EAAA,IAAI,MAAA,KAAW,IAAA,IAAQ,iBAAA,EAAkB,EAAG;AACxC,IAAA,OAAA,CAAQ,IAAA;AAAA,MACJ;AAAA,KACJ;AAAA,EACJ;AACA,EAAA,cAAA,GAAiB,MAAA;AACrB;AAMO,SAAS,aAAA,GAAqC;AACjD,EAAA,OAAO,kBAAkBA,eAAA,EAAqB;AAClD;AAMO,SAAS,oBAAA,GAAgC;AAC5C,EAAA,OAAO,cAAA,KAAmB,QAAQC,sBAAA,EAA4B;AAClE;AAMA,eAAsB,SAAA,GAA0C;AAC5D,EAAA,OAAO,aAAA,EAAc;AACzB;AAUO,SAAS,sBAAA,CACZ,YACA,MAAA,EACqB;AACrB,EAAA,IAAI,iBAAA;AACJ,EAAA,MAAM,GAAA,GAAM,WAAW,SAAA,CAAU;AAAA,IAC7B,IAAA,EAAM,MAAA;AAAA,IACN,KAAA,EAAO,CAAC,MAAA,KAAW,iBAAA,GAAoB,MAAM,CAAA;AAAA,IAC7C,QAAA,EAAU,MAAM,iBAAA;AAAoB,GACvC,CAAA;AACD,EAAA,OAAO;AAAA,IACH,gBAAgB,GAAA,CAAI,cAAA;AAAA,IACpB,WAAA,EAAa,MAAM,GAAA,CAAI,WAAA,EAAY;AAAA,IACnC,WAAA,EAAa,CAAC,QAAA,KAAa;AACvB,MAAA,iBAAA,GAAoB,QAAA;AACpB,MAAA,OAAO,MAAM;AACT,QAAA,IAAI,iBAAA,KAAsB,UAAU,iBAAA,GAAoB,MAAA;AAAA,MAC5D,CAAA;AAAA,IACJ;AAAA,GACJ;AACJ","file":"chunk-GDXSV7JV.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 and the lazily-built, cached client come from\n * `@parity/truapi/sandbox`; this module layers the product-sdk-specific glue on\n * top — an async {@link getClient} accessor and {@link subscribeWithInterrupt},\n * which 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 getClientSync as sandboxGetClientSync,\n isCorrectEnvironment as sandboxIsCorrectEnvironment,\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\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 */\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 clientOverride = client;\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 * 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(\"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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JsonRpcProvider, PolkadotSigner } from 'polkadot-api';
|
|
2
2
|
import { Topic, RemoteStatementStoreSubscribeItem, Statement, StatementProof, SignedStatement, HexString, scale, TrUApiClient, AllocatableResource, AllocationOutcome, VersionedHostGetUserIdError, HostRequestLoginResponse, VersionedHostRequestLoginError, ProductAccountId, ProductAccount as ProductAccount$1, VersionedHostAccountGetError, ProductProofContext, RingLocation, ContextualAlias as ContextualAlias$1, VersionedHostAccountGetAliasError, LegacyAccount, VersionedHostGetLegacyAccountsError, HostAccountCreateProofResponse, VersionedHostAccountCreateProofError, HostAccountConnectionStatusSubscribeItem, HostDevicePermissionRequest, RemotePermission, HostThemeSubscribeItem, ChatBotRegistrationStatus, HostChatCreateRoomRequest, ChatRoomRegistrationStatus, HostChatRegisterBotRequest, ChatMessageContent, ChatRoom, HostChatActionSubscribeItem, HostPaymentBalanceSubscribeItem, CoinPaymentPurseId, Balance, PaymentTopUpSource, HostPaymentStatusSubscribeItem, HostPushNotificationRequest, NotificationId } from '@parity/truapi';
|
|
3
|
-
export { AllocatableResource, AllocationOutcome, ChatMessageContent, ChatRoom, HexString, HostPaymentBalanceSubscribeItem, HostPaymentStatusSubscribeItem, NotificationId, PaymentTopUpSource, ProductAccountId, ProductProofContext, HostPushNotificationError as PushNotificationError, RemotePermission, RingLocation, SignedStatement, Statement, StatementProof, ThemeName, ThemeVariant, Topic } from '@parity/truapi';
|
|
3
|
+
export { AllocatableResource, AllocationOutcome, ChatMessageContent, ChatRoom, DerivationIndex, HexString, HostPaymentBalanceSubscribeItem, HostPaymentStatusSubscribeItem, NotificationId, PaymentTopUpSource, ProductAccountId, ProductProofContext, HostPushNotificationError as PushNotificationError, RemotePermission, RingLocation, SignedStatement, Statement, StatementProof, ThemeName, ThemeVariant, Topic } from '@parity/truapi';
|
|
4
4
|
import { SdkError } from '@parity/product-sdk-errors';
|
|
5
5
|
export { SdkError, isSdkError } from '@parity/product-sdk-errors';
|
|
6
6
|
import { Result } from '@parity/result';
|
|
@@ -405,9 +405,13 @@ type HostAccount = Omit<LegacyAccount, "publicKey"> & {
|
|
|
405
405
|
*
|
|
406
406
|
* Combines `@parity/truapi`'s `ProductAccountId` (the `{ dotNsIdentifier,
|
|
407
407
|
* derivationIndex }` lookup key) with the `ProductAccount` payload, with
|
|
408
|
-
* `publicKey` decoded to bytes
|
|
408
|
+
* `publicKey` decoded to bytes and `derivationIndex` kept as the plain
|
|
409
|
+
* numeric index (the adapter wraps it into the wire's tagged
|
|
410
|
+
* {@link DerivationIndex} selector).
|
|
409
411
|
*/
|
|
410
|
-
type ProductAccount = ProductAccountId & Omit<ProductAccount$1, "publicKey"> & {
|
|
412
|
+
type ProductAccount = Omit<ProductAccountId, "derivationIndex"> & Omit<ProductAccount$1, "publicKey"> & {
|
|
413
|
+
/** Plain account index within the product subtree. */
|
|
414
|
+
derivationIndex: number;
|
|
411
415
|
/** Raw public key bytes. */
|
|
412
416
|
publicKey: Uint8Array;
|
|
413
417
|
};
|
|
@@ -721,7 +725,7 @@ interface PaymentManager {
|
|
|
721
725
|
* const payments = await getPaymentManager();
|
|
722
726
|
* if (payments) {
|
|
723
727
|
* const sub = payments.subscribeBalance((b) => { ... });
|
|
724
|
-
* await payments.topUp(1_000_000n, { tag: "ProductAccount", value: { derivationIndex: 0 } });
|
|
728
|
+
* await payments.topUp(1_000_000n, { tag: "ProductAccount", value: { derivationIndex: { tag: "Left", value: 0 } } });
|
|
725
729
|
* const { id } = await payments.requestPayment(500n, "0x…");
|
|
726
730
|
* sub.unsubscribe();
|
|
727
731
|
* }
|
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-GDXSV7JV.js';
|
|
2
|
+
export { isCorrectEnvironment as isInsideContainerSync } from './chunk-GDXSV7JV.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';
|
|
@@ -174,8 +174,6 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
174
174
|
return (onMessage) => {
|
|
175
175
|
const activeFollows = /* @__PURE__ */ new Map();
|
|
176
176
|
const activeBroadcasts = /* @__PURE__ */ new Set();
|
|
177
|
-
let nextSubId = 0;
|
|
178
|
-
const getNextSubId = () => `follow_${nextSubId++}`;
|
|
179
177
|
function sendJsonRpcResponse(id, result) {
|
|
180
178
|
onMessage({ jsonrpc: "2.0", id, result });
|
|
181
179
|
}
|
|
@@ -196,24 +194,45 @@ function createHostPapiProvider(client, genesisHash) {
|
|
|
196
194
|
switch (method) {
|
|
197
195
|
case "chainHead_v1_follow": {
|
|
198
196
|
const [withRuntime] = params;
|
|
199
|
-
const syntheticSubId = getNextSubId();
|
|
200
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
|
+
};
|
|
201
205
|
ref.handle = subscribeWithInterrupt(
|
|
202
206
|
chain.followHeadSubscribe({ request: { genesisHash, withRuntime } }),
|
|
203
207
|
(item) => {
|
|
204
|
-
|
|
205
|
-
|
|
208
|
+
const followSubscriptionId2 = ref.handle?.subscriptionId;
|
|
209
|
+
if (!followSubscriptionId2) {
|
|
210
|
+
pendingItems.push(item);
|
|
211
|
+
return;
|
|
206
212
|
}
|
|
207
|
-
|
|
213
|
+
forwardItem(followSubscriptionId2, item);
|
|
208
214
|
}
|
|
209
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
|
+
}
|
|
210
226
|
ref.handle.onInterrupt(() => {
|
|
211
|
-
if (activeFollows.delete(
|
|
212
|
-
sendFollowEvent(
|
|
227
|
+
if (activeFollows.delete(followSubscriptionId)) {
|
|
228
|
+
sendFollowEvent(followSubscriptionId, { event: "stop" });
|
|
213
229
|
}
|
|
214
230
|
});
|
|
215
|
-
activeFollows.set(
|
|
216
|
-
sendJsonRpcResponse(id,
|
|
231
|
+
activeFollows.set(followSubscriptionId, ref.handle);
|
|
232
|
+
sendJsonRpcResponse(id, followSubscriptionId);
|
|
233
|
+
for (const item of pendingItems) {
|
|
234
|
+
forwardItem(followSubscriptionId, item);
|
|
235
|
+
}
|
|
217
236
|
break;
|
|
218
237
|
}
|
|
219
238
|
case "chainHead_v1_unfollow": {
|
|
@@ -577,6 +596,9 @@ function toHostExtensions(signedExtensions) {
|
|
|
577
596
|
additionalSigned: toHex(ext.additionalSigned)
|
|
578
597
|
}));
|
|
579
598
|
}
|
|
599
|
+
function toWireProductAccountId(dotNsIdentifier, derivationIndex) {
|
|
600
|
+
return { dotNsIdentifier, derivationIndex: { tag: "Left", value: derivationIndex } };
|
|
601
|
+
}
|
|
580
602
|
function adaptAccountsProvider(client) {
|
|
581
603
|
const account = client.account;
|
|
582
604
|
const signing = client.signing;
|
|
@@ -590,7 +612,9 @@ function adaptAccountsProvider(client) {
|
|
|
590
612
|
return account.requestLogin({ reason });
|
|
591
613
|
},
|
|
592
614
|
getProductAccount(dotNsIdentifier, derivationIndex = 0) {
|
|
593
|
-
return account.getAccount({
|
|
615
|
+
return account.getAccount({
|
|
616
|
+
productAccountId: toWireProductAccountId(dotNsIdentifier, derivationIndex)
|
|
617
|
+
}).map((response) => ({
|
|
594
618
|
publicKey: fromHex(response.account.publicKey),
|
|
595
619
|
dotNsIdentifier,
|
|
596
620
|
derivationIndex
|
|
@@ -626,10 +650,10 @@ function adaptAccountsProvider(client) {
|
|
|
626
650
|
}));
|
|
627
651
|
},
|
|
628
652
|
getProductAccountSigner(account_) {
|
|
629
|
-
const productAccountId =
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
653
|
+
const productAccountId = toWireProductAccountId(
|
|
654
|
+
account_.dotNsIdentifier,
|
|
655
|
+
account_.derivationIndex
|
|
656
|
+
);
|
|
633
657
|
return {
|
|
634
658
|
publicKey: account_.publicKey,
|
|
635
659
|
async signTx(callData, signedExtensions, metadata) {
|