@parity/product-sdk-terminal 0.8.0 → 0.8.1
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 +27 -17
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -439,25 +439,23 @@ function buildCreateTransactionRequest(productAccountId, callData, signedExtensi
|
|
|
439
439
|
}
|
|
440
440
|
};
|
|
441
441
|
}
|
|
442
|
-
var
|
|
443
|
-
function selectTxExtVersion(formatVersions
|
|
442
|
+
var V5_FORMAT_SELECTOR = 5;
|
|
443
|
+
function selectTxExtVersion(formatVersions) {
|
|
444
444
|
if (formatVersions.length === 0) {
|
|
445
445
|
throw new Error("No extrinsic version found in metadata");
|
|
446
446
|
}
|
|
447
447
|
if (formatVersions.includes(4)) {
|
|
448
448
|
return 0;
|
|
449
449
|
}
|
|
450
|
-
if (
|
|
451
|
-
return
|
|
450
|
+
if (formatVersions.includes(5)) {
|
|
451
|
+
return V5_FORMAT_SELECTOR;
|
|
452
452
|
}
|
|
453
453
|
throw new Error(
|
|
454
|
-
`Runtime offers no
|
|
454
|
+
`Runtime offers no extrinsic format 4 or 5 (offers: ${formatVersions.join(", ")}); the host protocol has no txExtVersion for it.`
|
|
455
455
|
);
|
|
456
456
|
}
|
|
457
457
|
function txExtVersionFromMetadata(metadata) {
|
|
458
|
-
|
|
459
|
-
const txExtVersions = Object.keys(extrinsic.signedExtensions).map(Number);
|
|
460
|
-
return selectTxExtVersion(extrinsic.version, txExtVersions);
|
|
458
|
+
return selectTxExtVersion(unifyMetadata(decAnyMetadata(metadata)).extrinsic.version);
|
|
461
459
|
}
|
|
462
460
|
async function requestSignedTransaction(session, request) {
|
|
463
461
|
const result = await session.createTransaction(request);
|
|
@@ -640,21 +638,33 @@ if (void 0) {
|
|
|
640
638
|
});
|
|
641
639
|
describe("buildCreateTransactionRequest \u2014 tx payload (the AsPgas fix)", () => {
|
|
642
640
|
test("prefers V4 (tx-ext version 0) on a dual V4/V5 runtime", () => {
|
|
643
|
-
expect(selectTxExtVersion([4, 5]
|
|
641
|
+
expect(selectTxExtVersion([4, 5])).toBe(0);
|
|
644
642
|
});
|
|
645
|
-
test("uses the
|
|
646
|
-
expect(selectTxExtVersion([5]
|
|
643
|
+
test("uses the V5 selector when the runtime offers no V4", () => {
|
|
644
|
+
expect(selectTxExtVersion([5])).toBe(5);
|
|
647
645
|
});
|
|
648
646
|
test("maps a V4-only runtime to the wire sentinel", () => {
|
|
649
|
-
expect(selectTxExtVersion([4]
|
|
647
|
+
expect(selectTxExtVersion([4])).toBe(0);
|
|
650
648
|
});
|
|
651
|
-
test("
|
|
652
|
-
expect(
|
|
649
|
+
test("prefers V5 over a format it does not know", () => {
|
|
650
|
+
expect(selectTxExtVersion([5, 6])).toBe(5);
|
|
651
|
+
});
|
|
652
|
+
test("rejects a runtime offering neither format 4 nor 5", () => {
|
|
653
|
+
expect(() => selectTxExtVersion([6])).toThrow(/no extrinsic format 4 or 5/i);
|
|
653
654
|
});
|
|
654
655
|
test("rejects metadata with no extrinsic version", () => {
|
|
655
|
-
expect(() => selectTxExtVersion([]
|
|
656
|
-
|
|
657
|
-
|
|
656
|
+
expect(() => selectTxExtVersion([])).toThrow("No extrinsic version found in metadata");
|
|
657
|
+
});
|
|
658
|
+
test("txExtVersionFromMetadata reads the format list out of every tracked chain's metadata", async () => {
|
|
659
|
+
const { readFileSync, readdirSync } = await null;
|
|
660
|
+
const { join: join2 } = await null;
|
|
661
|
+
const dir = join2(import.meta.dirname, "..", "..", "descriptors", ".papi", "metadata");
|
|
662
|
+
const blobs = readdirSync(dir).filter((name) => name.endsWith(".scale"));
|
|
663
|
+
expect(blobs.length, "raise when a chain is added").toBeGreaterThanOrEqual(11);
|
|
664
|
+
for (const name of blobs) {
|
|
665
|
+
const metadata = new Uint8Array(readFileSync(join2(dir, name)));
|
|
666
|
+
expect(txExtVersionFromMetadata(metadata), name).toBe(0);
|
|
667
|
+
}
|
|
658
668
|
});
|
|
659
669
|
const checkGenesis = ext("CheckGenesis", [], [17, 34, 51]);
|
|
660
670
|
test("wraps the payload as v1 with signer, callData, and txExtVersion", () => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter.ts","../src/signer.ts","../src/index.ts","../src/allowance.ts","../src/allowance-cache.ts","../src/sessions.ts","../src/qr-encode.ts"],"sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Node.js adapter for the Polkadot host-papp SDK.\n *\n * Provides Node.js-compatible implementations of the SDK's storage and\n * transport layers, enabling QR login, attestation, and signing in\n * terminal/CLI environments.\n */\nimport {\n createPappAdapter,\n type PappAdapter,\n type HostMetadata,\n SS_STABLE_STAGE_ENDPOINTS,\n SS_PASEO_STABLE_STAGE_ENDPOINTS,\n} from \"@novasamatech/host-papp\";\nimport {\n createLazyClient,\n createPapiStatementStoreAdapter,\n type LazyClient,\n} from \"@novasamatech/statement-store\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\nimport { getWsProvider } from \"@polkadot-api/ws-provider\";\n\nimport { createNodeStorageAdapter } from \"./node-storage.js\";\n\nconst log = createLogger(\"terminal\");\n\n/** Options for creating a terminal adapter. */\nexport interface TerminalAdapterOptions {\n /** Unique app identifier. Used as the storage namespace. */\n appId: string;\n /** Statement store WebSocket endpoints. Defaults to Paseo stable endpoints. */\n endpoints?: string[];\n /** Optional host metadata for the Sign-In screen. */\n hostMetadata?: HostMetadata;\n /**\n * Directory where session files are persisted. Defaults to\n * `~/.polkadot-apps/`. Override in tests to point at a temporary\n * directory populated with `createTestSession` from\n * `@parity/product-sdk-terminal/testing`.\n */\n storageDir?: string;\n}\n\n/**\n * Create a terminal adapter backed by the host-papp SDK.\n *\n * This sets up:\n * - File-based storage in `~/.polkadot-apps/` (since Node.js has no localStorage)\n * - WebSocket connection to the statement store\n * - The full SSO flow: QR pairing + on-chain attestation\n * - Session manager for signing requests\n */\n/** A PappAdapter with the `appId` it was created with and a `destroy` method for cleanup. */\nexport type TerminalAdapter = PappAdapter & {\n /** The `appId` passed to {@link createTerminalAdapter}. Useful for {@link createSessionSigner}. */\n readonly appId: string;\n /**\n * The on-disk storage directory used for sessions and (when the\n * host-runner facet is in use) for the allowance-key cache the\n * `./host` subpath maintains. `undefined` when the default\n * is in use; set explicitly when the caller passed `storageDir` to\n * {@link createTerminalAdapter}.\n */\n readonly storageDir?: string;\n /**\n * Disconnect the WebSocket and release resources.\n *\n * @remarks\n * Idempotent. Returns a Promise that resolves once all in-flight\n * statement-subscription teardowns have settled and the underlying\n * substrate client has been disconnected. **Awaiting is recommended\n * but not required** — callers that don't await get the same\n * fire-and-forget shape the previous version had, but they may see\n * the destroy-time RPC traffic finish after the function returns.\n *\n * The implementation tracks the server-side `statement_unsubscribe…`\n * RPCs `sessions.dispose()` fires, then awaits them via\n * `Promise.allSettled` before destroying the substrate-client request\n * queue. Draining those RPCs is not enough on its own: destroying the\n * client while a statement-subscription observable is still live makes\n * `@novasamatech/statement-store` log `Statement subscription error:\n * Client destroyed` to `console.error` synchronously inside the\n * disconnect. So the disconnect runs under a scoped `console.error` filter\n * that drops only that benign line ({@link isBenignTeardownError}),\n * restored in a `finally` — every other `console.error` passes through.\n *\n * Limit: this covers the statement-subscription half only. An auth\n * subscription still open at `destroy()` (released by `sso.abort()`, not\n * `sessions.dispose()`) raises an unhandled `Error: Not connected` from the\n * same teardown, which this does not suppress. Tracked as a follow-up.\n */\n destroy(): Promise<void>;\n};\n\nexport function createTerminalAdapter(options: TerminalAdapterOptions): TerminalAdapter {\n const endpoints = options.endpoints ?? SS_PASEO_STABLE_STAGE_ENDPOINTS;\n\n const storage = createNodeStorageAdapter(options.appId, options.storageDir);\n // ws-provider 0.9 takes endpoints positionally; relies on the global\n // WebSocket (Node ≥21) unless `websocketClass` is supplied.\n //\n // heartbeatTimeout uses setTimeout under the hood, which clamps to a\n // 32-bit signed integer. Passing Infinity triggers a noisy\n // `TimeoutOverflowWarning` on every reschedule. Use the int32 max\n // (~24.8 days) — effectively-never for any CLI session.\n const HEARTBEAT_NEVER_MS = 2_147_483_647;\n const rawLazyClient = createLazyClient(\n getWsProvider(endpoints, { heartbeatTimeout: HEARTBEAT_NEVER_MS }),\n );\n const trackedLazyClient = wrapLazyClient(rawLazyClient);\n const statementStore = createPapiStatementStoreAdapter(trackedLazyClient);\n\n const adapter = createPappAdapter({\n appId: options.appId,\n hostMetadata: options.hostMetadata,\n adapters: {\n storage,\n lazyClient: trackedLazyClient,\n statementStore,\n },\n });\n\n let destroyPromise: Promise<void> | null = null;\n return {\n ...adapter,\n appId: options.appId,\n storageDir: options.storageDir,\n destroy(): Promise<void> {\n if (destroyPromise) return destroyPromise;\n destroyPromise = teardown(adapter.sessions, trackedLazyClient);\n return destroyPromise;\n },\n };\n}\n\n/**\n * Lazy-client wrapper that tracks server-side unsubscribe RPCs as Promises.\n *\n * `lazyClient.getSubscribeFn` returns a function whose teardown callback\n * fires `c._request(unsubscribeMethod, ...)` with `noop` `onSuccess` /\n * `onError` — the unsubscribe is fire-and-forget by upstream design. We\n * intercept those requests by replacing their callbacks with handlers\n * that resolve a tracking Promise on either outcome (success OR error —\n * we just need to know the request settled, not whether it succeeded).\n *\n * `awaitPendingUnsubs()` returns a Promise that settles when every\n * tracked unsubscribe has completed. Used by `teardown` to drain before\n * calling `disconnect`.\n *\n * The wrapper is otherwise transparent: `getClient`, `getRequestFn`,\n * `disconnect` pass through unchanged.\n */\ntype TrackedLazyClient = LazyClient & {\n awaitPendingUnsubs(): Promise<void>;\n};\n\nfunction wrapLazyClient(inner: LazyClient): TrackedLazyClient {\n const pendingUnsubs = new Set<Promise<void>>();\n const innerGetSubscribeFn = inner.getSubscribeFn.bind(inner);\n\n return {\n ...inner,\n getClient: inner.getClient.bind(inner),\n getRequestFn: inner.getRequestFn.bind(inner),\n disconnect: inner.disconnect.bind(inner),\n\n getSubscribeFn() {\n // Each call returns a new SubscribeFn. We wrap the teardown\n // callback so any unsubscribe RPC it fires gets tracked.\n const innerSubscribe = innerGetSubscribeFn();\n return ((method, params, onMessage, onError) => {\n const innerTeardown = innerSubscribe(method, params, onMessage, onError);\n return () => {\n // Track the unsubscribe with a Promise that resolves\n // after the microtask queue drains. The upstream code\n // uses `noop` callbacks on the actual RPC, so we\n // can't directly observe completion — but by the\n // time `innerTeardown()` returns, the `_request`\n // has been queued. Two microtask hops are enough\n // for the request to flush through the\n // substrate-client's send pipeline.\n //\n // If `innerTeardown()` throws synchronously, we let\n // the throw escape (the caller — `sessions.dispose()`\n // — is in the best position to decide what to do)\n // but the tracker still resolves so\n // `awaitPendingUnsubs()` doesn't hang and\n // `destroy()` still completes.\n const tracked = new Promise<void>((resolve) => {\n queueMicrotask(() => queueMicrotask(resolve));\n });\n pendingUnsubs.add(tracked);\n void tracked.finally(() => pendingUnsubs.delete(tracked));\n innerTeardown();\n };\n }) as ReturnType<LazyClient[\"getSubscribeFn\"]>;\n },\n\n async awaitPendingUnsubs(): Promise<void> {\n // Snapshot so additions made during the await don't extend\n // the wait indefinitely (sessions.dispose() should have fired\n // them all synchronously by the time we're called).\n const snapshot = Array.from(pendingUnsubs);\n await Promise.allSettled(snapshot);\n },\n };\n}\n\n/**\n * Whether `error` is the benign teardown noise `@novasamatech/statement-store`\n * emits when a statement subscription's observable errors because the client\n * was destroyed underneath it — the raw-client's `DestroyedError: Client\n * destroyed`.\n *\n * Matches on the message text only. Matching the `DestroyedError` *name* would\n * also swallow `@parity/product-sdk-signer`'s own `DestroyedError` (a direct\n * dependency, message \"SignerManager has been destroyed\"), so a real signer\n * failure during teardown would vanish — and this predicate is exported, so it\n * would carry that to consumers. `Client destroyed` is unique to the upstream\n * line.\n *\n * Exported so a consumer's own `console.error` guard can drop this line without\n * reinventing the match. {@link createTerminalAdapter}'s own `destroy()` already\n * suppresses it (see {@link suppressBenignTeardownErrors}); this is for code\n * paths outside that window.\n */\nexport function isBenignTeardownError(error: unknown): boolean {\n // Read the message without stringifying the whole value: `String()` on a\n // null-prototype object throws, and `console.error` is usually called from a\n // `catch`, so the wrapper must never throw where the real one would print.\n const message = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n return /Client destroyed/.test(message);\n}\n\n/**\n * Temporarily drop {@link isBenignTeardownError} lines from `console.error`,\n * returning a restore function.\n *\n * The noise comes from an unconditional `console.error('Statement subscription\n * error:', …)` inside `@novasamatech/statement-store`'s rpc adapter, emitted\n * synchronously while `disconnect()` destroys the client with a subscription\n * observable still live. Draining the tracked unsubscribe RPCs does not cover\n * that observable's error emission, so the ordering-only approach still lets\n * one line through — hence a scoped suppression.\n *\n * Deliberately narrow: the upstream call passes a plain string first and the\n * error second, so the wrapper checks *every* argument ({@link isBenignTeardownError}\n * on any of them) and drops the call only when the benign error is present.\n * Every other `console.error` passes through untouched, so a genuine error\n * during teardown is still visible.\n */\nfunction suppressBenignTeardownErrors(): () => void {\n const original = console.error;\n const wrapper = (...args: unknown[]) => {\n if (args.some(isBenignTeardownError)) return;\n original.apply(console, args as Parameters<typeof console.error>);\n };\n console.error = wrapper;\n // Restore only if we're still the active wrapper — if another patcher\n // layered on top after us, resetting to `original` would clobber theirs.\n return () => {\n if (console.error === wrapper) console.error = original;\n };\n}\n\n/**\n * Drain pending unsubscribes, then disconnect. Order is deterministic:\n *\n * 1. `sessions.dispose()` walks each open statement-subscription and\n * triggers their RPC unsubscribe via the wrapped subscribe-fn's\n * teardown callback. Each unsubscribe is recorded as a tracked\n * Promise on the wrapper.\n * 2. `awaitPendingUnsubs()` waits for those tracked Promises to\n * settle (resolution OR rejection — we just need confirmation the\n * RPC has left the substrate-client send pipeline).\n * 3. `disconnect()` calls `substrateClient.destroy()`.\n *\n * Draining the unsubscribe RPCs does not stop `@novasamatech/statement-store`\n * from logging `Statement subscription error: DestroyedError` when the client\n * is destroyed while a subscription observable is still live. That log is\n * synchronous inside `disconnect()`, so the call runs under a scoped\n * {@link suppressBenignTeardownErrors} restored in a `finally` — no timer, and\n * nothing to leak if two `destroy()`s overlap. Only the benign line is dropped.\n *\n * If the disconnect call itself throws, log and continue rather than\n * propagating — caller can `await destroy()` without `try/catch`.\n *\n * Known limit: this closes the statement-subscription half. An auth\n * subscription still established at `destroy()` (released only by\n * `sso.abort()`, not `sessions.dispose()`) raises an unhandled\n * `Error: Not connected` from the same teardown, which this does not catch.\n * Tracked as a follow-up.\n */\nasync function teardown(\n sessions: { dispose(): void },\n lazyClient: TrackedLazyClient,\n): Promise<void> {\n log.debug(\"destroying terminal adapter\");\n sessions.dispose();\n await lazyClient.awaitPendingUnsubs();\n const restore = suppressBenignTeardownErrors();\n try {\n lazyClient.disconnect();\n } catch (e) {\n log.warn(\"lazyClient.disconnect threw during destroy\", { error: e });\n } finally {\n // The benign log is emitted synchronously inside `disconnect()`, so by\n // here it has already been swallowed — restore immediately. Restoring in\n // `finally` (not a `setTimeout`) is what keeps overlapping destroys from\n // stranding the patch: each call reverts before the next inspects it.\n restore();\n }\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n /**\n * Build a fake LazyClient whose `getSubscribeFn` records each subscribe\n * call and whose returned teardown is observable. Lets tests assert on\n * teardown invocation and on `disconnect` ordering.\n */\n function fakeLazyClient(): {\n client: LazyClient;\n teardownCalls: number;\n disconnectCalls: number;\n } {\n let teardownCalls = 0;\n let disconnectCalls = 0;\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_method, _params, _onMessage, _onError) => {\n return () => {\n teardownCalls += 1;\n };\n }) as ReturnType<LazyClient[\"getSubscribeFn\"]>,\n disconnect: () => {\n disconnectCalls += 1;\n },\n } as LazyClient;\n return {\n client: fake,\n get teardownCalls() {\n return teardownCalls;\n },\n get disconnectCalls() {\n return disconnectCalls;\n },\n };\n }\n\n describe(\"wrapLazyClient\", () => {\n test(\"passes getClient / getRequestFn / disconnect through unchanged\", () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n\n wrapped.getClient();\n wrapped.disconnect();\n expect(fake.disconnectCalls).toBe(1);\n expect(typeof wrapped.getRequestFn).toBe(\"function\");\n });\n\n test(\"getSubscribeFn returns a wrapped subscribe whose teardown invokes the inner teardown\", () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n\n const subscribe = wrapped.getSubscribeFn();\n const teardown = subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n );\n teardown();\n\n expect(fake.teardownCalls).toBe(1);\n });\n\n test(\"awaitPendingUnsubs resolves after all wrapped teardowns settle\", async () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n const subscribe = wrapped.getSubscribeFn();\n\n // Simulate three live subscriptions being torn down in the\n // same tick (what `sessions.dispose()` does internally).\n for (let i = 0; i < 3; i++) {\n const teardown = subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n );\n teardown();\n }\n\n // All three pending — but resolves once microtasks flush.\n await wrapped.awaitPendingUnsubs();\n expect(fake.teardownCalls).toBe(3);\n });\n\n test(\"awaitPendingUnsubs with no pending unsubs resolves immediately\", async () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n await expect(wrapped.awaitPendingUnsubs()).resolves.toBeUndefined();\n });\n\n test(\"an unsubscribe whose teardown throws is still tracked and resolved\", async () => {\n // Even if the inner teardown throws, the tracker still needs\n // to settle — otherwise destroy() would hang forever.\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_method, _params, _onMessage, _onError) => {\n return () => {\n throw new Error(\"teardown boom\");\n };\n }) as ReturnType<LazyClient[\"getSubscribeFn\"]>,\n disconnect: () => {},\n } as LazyClient;\n\n const wrapped = wrapLazyClient(fake);\n const subscribe = wrapped.getSubscribeFn();\n const teardown = subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n );\n\n // The wrapper's inner try/finally lets the throw escape (as\n // expected — we don't swallow user-visible errors), but the\n // tracker still resolves. Catch-and-await pattern:\n try {\n teardown();\n } catch {\n // expected\n }\n await expect(wrapped.awaitPendingUnsubs()).resolves.toBeUndefined();\n });\n });\n\n describe(\"teardown\", () => {\n test(\"orders sessions.dispose, drain, then disconnect\", async () => {\n const order: string[] = [];\n\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n // Override disconnect to capture ordering.\n const innerDisconnect = wrapped.disconnect.bind(wrapped);\n wrapped.disconnect = () => {\n order.push(\"disconnect\");\n innerDisconnect();\n };\n\n const sessions = {\n dispose: vi.fn(() => {\n order.push(\"dispose\");\n // Simulate sessions.dispose firing one teardown.\n const subscribe = wrapped.getSubscribeFn();\n subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n )();\n }),\n };\n\n await teardown(sessions, wrapped);\n\n expect(order).toEqual([\"dispose\", \"disconnect\"]);\n expect(sessions.dispose).toHaveBeenCalledTimes(1);\n expect(fake.disconnectCalls).toBe(1);\n });\n\n test(\"disconnect runs even when there are no pending unsubs\", async () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n const sessions = { dispose: vi.fn() };\n\n await teardown(sessions, wrapped);\n\n expect(fake.disconnectCalls).toBe(1);\n });\n\n test(\"logs a warning if disconnect throws, doesn't propagate to caller\", async () => {\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_method, _params, _onMessage, _onError) => () => {}) as ReturnType<\n LazyClient[\"getSubscribeFn\"]\n >,\n disconnect: () => {\n throw new Error(\"boom\");\n },\n } as LazyClient;\n\n const wrapped = wrapLazyClient(fake);\n const sessions = { dispose: vi.fn() };\n\n await expect(teardown(sessions, wrapped)).resolves.toBeUndefined();\n });\n\n test(\"awaits pending unsubs before calling disconnect\", async () => {\n // The whole point of the fix: disconnect must not run while\n // unsubscribe RPCs are still queued. Verify ordering even\n // when the unsubs take multiple microtasks to settle.\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n\n let unsubResolved = false;\n const innerDisconnect = wrapped.disconnect.bind(wrapped);\n wrapped.disconnect = () => {\n // Disconnect must not run before the unsubscribe has\n // resolved. If it does, this assertion fires.\n expect(unsubResolved).toBe(true);\n innerDisconnect();\n };\n\n const sessions = {\n dispose: () => {\n const subscribe = wrapped.getSubscribeFn();\n subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n )();\n // mark resolution after the microtasks the wrapper queues\n queueMicrotask(() =>\n queueMicrotask(() => {\n unsubResolved = true;\n }),\n );\n },\n };\n\n await teardown(sessions, wrapped);\n expect(fake.disconnectCalls).toBe(1);\n });\n\n test(\"suppresses the benign DestroyedError console.error fired during disconnect\", async () => {\n // The upstream statement-store adapter logs `Statement subscription\n // error: DestroyedError` from a finalizer when disconnect destroys\n // the client under a live subscription. Simulate that by logging it\n // from inside disconnect; teardown must swallow it.\n const spy = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_m, _p, _om, _oe) => () => {}) as ReturnType<LazyClient[\"getSubscribeFn\"]>,\n disconnect: () => {\n console.error(\n \"Statement subscription error:\",\n new Error(\"DestroyedError: Client destroyed\"),\n );\n },\n } as LazyClient;\n const wrapped = wrapLazyClient(fake);\n\n await teardown({ dispose: () => {} }, wrapped);\n\n // The benign line never reached the underlying console.error.\n expect(spy).not.toHaveBeenCalledWith(\n \"Statement subscription error:\",\n expect.anything(),\n );\n spy.mockRestore();\n });\n\n test(\"still surfaces a real (non-benign) console.error during disconnect\", async () => {\n const spy = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const realError = new Error(\"something actually went wrong\");\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_m, _p, _om, _oe) => () => {}) as ReturnType<LazyClient[\"getSubscribeFn\"]>,\n disconnect: () => {\n console.error(\"real failure:\", realError);\n },\n } as LazyClient;\n const wrapped = wrapLazyClient(fake);\n\n await teardown({ dispose: () => {} }, wrapped);\n\n expect(spy).toHaveBeenCalledWith(\"real failure:\", realError);\n spy.mockRestore();\n });\n\n test(\"restores console.error by the time teardown resolves\", async () => {\n const before = console.error;\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n\n await teardown({ dispose: () => {} }, wrapped);\n\n // Restore is synchronous in a `finally`, no timer to wait on.\n expect(console.error).toBe(before);\n });\n\n test(\"two overlapping destroys do not strand the console.error patch\", async () => {\n // Each teardown restores in `finally`, so even interleaved calls\n // leave the real console.error reachable — no wrapper left installed.\n const before = console.error;\n const makeFake = () =>\n wrapLazyClient({\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_m, _p, _om, _oe) => () => {}) as ReturnType<\n LazyClient[\"getSubscribeFn\"]\n >,\n disconnect: () => {\n console.error(\n \"Statement subscription error:\",\n new Error(\"DestroyedError: Client destroyed\"),\n );\n },\n } as LazyClient);\n\n await Promise.all([\n teardown({ dispose: () => {} }, makeFake()),\n teardown({ dispose: () => {} }, makeFake()),\n ]);\n\n expect(console.error).toBe(before);\n });\n });\n\n describe(\"isBenignTeardownError\", () => {\n test(\"matches the statement-store teardown noise\", () => {\n expect(isBenignTeardownError(new Error(\"DestroyedError: Client destroyed\"))).toBe(true);\n expect(isBenignTeardownError(\"Client destroyed\")).toBe(true);\n const e = new Error(\"Client destroyed\");\n e.name = \"DestroyedError\";\n expect(isBenignTeardownError(e)).toBe(true);\n });\n\n test(\"does not match a genuine error\", () => {\n expect(isBenignTeardownError(new Error(\"connection refused\"))).toBe(false);\n expect(isBenignTeardownError(\"BadProof\")).toBe(false);\n expect(isBenignTeardownError(undefined)).toBe(false);\n });\n\n test(\"does not swallow the signer's own DestroyedError\", () => {\n // `@parity/product-sdk-signer`'s DestroyedError shares the name but\n // not the message, so matching on the name (not the message) would\n // silently drop a real signer failure during teardown.\n const signerError = new Error(\"SignerManager has been destroyed\");\n signerError.name = \"DestroyedError\";\n expect(isBenignTeardownError(signerError)).toBe(false);\n });\n\n test(\"does not throw on a value String() would reject\", () => {\n // `console.error` is usually called from a `catch`, and a\n // null-prototype object prints fine there but throws under `String()`.\n expect(() => isBenignTeardownError(Object.create(null))).not.toThrow();\n expect(isBenignTeardownError(Object.create(null))).toBe(false);\n });\n });\n}\n\nexport { SS_STABLE_STAGE_ENDPOINTS, SS_PASEO_STABLE_STAGE_ENDPOINTS };\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Create a PolkadotSigner from a QR-paired session.\n *\n * Routes **transaction signing** through `session.createTransaction` — the\n * host-papp `CreateTransactionRequest`/`CreateTransactionResponse` SSO\n * message pair. We hand the mobile app the structured\n * `ProductAccountTransaction` (`signer`, `genesisHash`, `callData`, the\n * per-extension `{ extra, additionalSigned }`, and `txExtVersion`) and it\n * **builds and signs** the full extrinsic, returning the signed bytes.\n *\n * Two properties fall out of this:\n * - The wallet can **decode and display** the transaction instead of\n * blind-signing an opaque hash (which is what the older\n * `session.signRaw({ tag: \"Payload\" })` route did).\n * - Every signed extension the chain declares — including ones PAPI's PJS\n * adapter doesn't know (e.g. `AsPgas` on Paseo Next v2) — is forwarded\n * verbatim as `{ id, extra, additionalSigned }`, so nothing is lost in\n * translation.\n *\n * **Raw-message signing** (`signBytes`) keeps the `session.signRaw` `Bytes`\n * tag so the wallet applies the `<Bytes>...</Bytes>` anti-phishing wrap once\n * on its side. Correct for arbitrary user data; wrong for extrinsic payloads\n * (which is why the two paths split).\n *\n * @example\n * ```ts\n * const [session] = adapter.sessions.sessions.read();\n *\n * // Default account — uses [adapter.appId, 0]:\n * const signer = createSessionSigner(session, adapter);\n *\n * // Non-default derivation index, or a different productId:\n * const subSigner = createSessionSignerForAccount(session, {\n * productId: \"my-product\",\n * derivationIndex: 3,\n * });\n *\n * await contract.publish.tx(domain, cid, { signer, origin });\n * ```\n */\nimport type { UserSession } from \"@novasamatech/host-papp\";\nimport { NoAllowanceError } from \"@novasamatech/statement-store\";\nimport { decAnyMetadata, unifyMetadata } from \"@polkadot-api/substrate-bindings\";\nimport { deriveProductAccountPublicKey } from \"@parity/product-sdk-keys\";\nimport { AllowanceExpiredError } from \"@parity/product-sdk-signer/errors\";\nimport type { PolkadotSigner } from \"polkadot-api\";\n\nimport type { TerminalAdapter } from \"./adapter.js\";\n\n/**\n * Detect the statement-store `NoAllowanceError` — the chain-side rejection a\n * sign request hits when the session's statement-store allowance has lapsed.\n *\n * Walks the `cause` chain: host-papp may wrap the underlying submit failure\n * before it reaches the `createTransaction`/`signRaw` error channel. Besides\n * `instanceof`, matches by constructor name (a duplicated copy of\n * `@novasamatech/statement-store` in the module graph defeats `instanceof`)\n * and by the fixed message statement-store mints — the only signal left once\n * the error crossed a serialization boundary (`NoAllowanceError` never sets\n * `this.name`, so name-matching would never fire).\n */\nfunction isNoAllowanceError(error: unknown): boolean {\n // Bounded walk — cause chains are short; the cap guards against cycles.\n for (let current = error, depth = 0; current != null && depth < 8; depth++) {\n if (current instanceof NoAllowanceError) return true;\n if (current instanceof Error) {\n if (\n current.constructor.name === \"NoAllowanceError\" ||\n current.message.includes(\"no allowance set\")\n ) {\n return true;\n }\n current = current.cause;\n } else {\n break;\n }\n }\n return false;\n}\n\n/**\n * Map a sign failure to the typed {@link AllowanceExpiredError} when its cause\n * is the statement-store `NoAllowanceError`; `null` otherwise. Single decision\n * point for both sign paths — `signTx` (via `session.createTransaction`) and\n * `signBytes` (via `session.signRaw`) both submit through the statement store,\n * so the lapsed resource is always `\"statementStore\"`.\n *\n * The caller *throws* the returned error (PAPI's `PolkadotSigner` contract is\n * a rejecting Promise), making allowance expiry a typed, catchable condition\n * rather than a generic `Error`.\n */\nfunction toAllowanceExpiredError(error: unknown): AllowanceExpiredError | null {\n return isNoAllowanceError(error) ? new AllowanceExpiredError(\"statementStore\", error) : null;\n}\n\n/**\n * Identifies which sub-account of a paired session should sign.\n *\n * Mirrors the `host-papp` wire format `productAccountId: [productId, derivationIndex]`:\n * `productId` is the dotNS-style identifier for the requesting product (matches\n * the adapter's `appId` in normal usage); `derivationIndex` is the BIP32-style\n * child-key index, where `0` is the session's default account.\n */\nexport interface ProductAccountRef {\n /** The product identifier. Usually equal to the adapter's `appId`. */\n productId: string;\n /** Child-key derivation index. `0` is the default account. */\n derivationIndex: number;\n /**\n * The **product account's** sr25519 public key (32 bytes), as derived by the\n * host for `[productId, derivationIndex]`.\n *\n * PAPI stamps this into the extrinsic's signer address and verifies the\n * signature against it, so it must be the *product* account's key — not the\n * wallet's selected/root account (`session.remoteAccount.accountId`). A\n * mismatch produces an invalid signature.\n *\n * When omitted, the signer soft-derives it from the session's root account\n * (`mnemonic + \"/product/{productId}/{derivationIndex}\"`), which is correct\n * for every product account. Supply it explicitly only to avoid the\n * re-derivation or when you've already derived it elsewhere.\n */\n publicKey?: Uint8Array;\n}\n\n/**\n * The `signedExtensions` map PAPI hands to `PolkadotSigner.signTx`: keyed by\n * extension identifier, each entry carries the SCALE-encoded `extra` (goes in\n * the extrinsic body, as `value`) and `additionalSigned` (implicit data).\n */\ntype PapiSignedExtensions = Parameters<PolkadotSigner[\"signTx\"]>[1];\n\n/**\n * Build the host-papp `CreateTransactionRequest` from PAPI's `signTx` inputs.\n *\n * Maps every signed extension verbatim into the wire `{ id, extra,\n * additionalSigned }` triplets — no allow-list, no reshaping — so chain-specific\n * extensions (e.g. `AsPgas` on Paseo Next v2) survive. The mobile app uses\n * these plus `txExtVersion` to assemble and sign the extrinsic.\n *\n * Pure and synchronous so the wire-shape contract can be unit-tested without a\n * paired phone; the metadata decode + SSO round-trip live in {@link makeTxSignTx}.\n */\nfunction buildCreateTransactionRequest(\n productAccountId: [string, number],\n callData: Uint8Array,\n signedExtensions: PapiSignedExtensions,\n txExtVersion: number,\n): Parameters<UserSession[\"createTransaction\"]>[0] {\n const checkGenesis = signedExtensions.CheckGenesis;\n if (!checkGenesis) {\n throw new Error(\n \"Cannot build transaction: chain did not provide the CheckGenesis signed extension\",\n );\n }\n\n return {\n payload: {\n tag: \"v1\" as const,\n value: {\n signer: productAccountId,\n // CheckGenesis carries the genesis hash as its additionalSigned (implicit) data.\n genesisHash: checkGenesis.additionalSigned,\n callData,\n extensions: Object.values(signedExtensions).map(\n ({ identifier, value, additionalSigned }) => ({\n id: identifier,\n extra: value,\n additionalSigned,\n }),\n ),\n txExtVersion,\n },\n },\n };\n}\n\n/** The transaction-extension version used by V5 general transactions. */\nconst GENERAL_TX_EXT_VERSION = 5;\n\n/**\n * Choose the wire `txExtVersion` — the **transaction-extension** version the\n * paired host must assemble under, which is distinct from the extrinsic\n * *format* version (4 / 5).\n *\n * - `formatVersions` — `metadata.extrinsic.version`, the extrinsic *formats* the\n * runtime accepts (e.g. `[4]`, `[5]`, `[4, 5]`).\n * - `txExtVersions` — the keys of `metadata.extrinsic.signedExtensions`, the\n * transaction-extension versions the runtime supports (its v16\n * `transactionExtensionsByVersion`; always includes `0`).\n *\n * A V4 signed extrinsic always uses transaction-extension version `0` (a fixed\n * sentinel). Prefer V4 while it is offered — it carries the account signature\n * in its envelope and the host can build it. Only when V4 is absent do we fall\n * to a V5 general transaction, whose transaction-extension version is `5` (the\n * key the runtime and host agree on for the general format); require it to be\n * present in the map rather than assuming it.\n *\n * Passing the extrinsic *format* number here was the bug (host-rust-core#528):\n * `5` is both a format version and the general extension version, so it looked\n * right, but the two are unrelated in general and the host decodes extension\n * values by this number.\n */\nfunction selectTxExtVersion(\n formatVersions: readonly number[],\n txExtVersions: readonly number[],\n): number {\n if (formatVersions.length === 0) {\n throw new Error(\"No extrinsic version found in metadata\");\n }\n if (formatVersions.includes(4)) {\n return 0;\n }\n if (txExtVersions.includes(GENERAL_TX_EXT_VERSION)) {\n return GENERAL_TX_EXT_VERSION;\n }\n throw new Error(\n `Runtime offers no V4 extrinsic and no transaction-extension version ${GENERAL_TX_EXT_VERSION} ` +\n `(supported: ${txExtVersions.join(\", \") || \"none\"}); cannot select a txExtVersion the host can assemble.`,\n );\n}\n\nfunction txExtVersionFromMetadata(metadata: Uint8Array): number {\n const extrinsic = unifyMetadata(decAnyMetadata(metadata)).extrinsic;\n const txExtVersions = Object.keys(extrinsic.signedExtensions).map(Number);\n return selectTxExtVersion(extrinsic.version, txExtVersions);\n}\n\n/**\n * Send the request to the paired wallet's `createTransaction` and unwrap it.\n *\n * Extracted and named so the SSO round-trip + error handling can be\n * unit-tested without a real phone or chain metadata; the metadata decode and\n * payload assembly live in {@link makeTxSignTx} / {@link buildCreateTransactionRequest}.\n */\nasync function requestSignedTransaction(\n session: UserSession,\n request: Parameters<UserSession[\"createTransaction\"]>[0],\n): Promise<Uint8Array> {\n const result = await session.createTransaction(request);\n if (result.isErr()) {\n const expired = toAllowanceExpiredError(result.error);\n if (expired) throw expired;\n throw new Error(`Mobile transaction signing rejected: ${result.error.message}`);\n }\n // host-papp returns the fully signed extrinsic bytes.\n return result.value;\n}\n\n/**\n * The `signTx` function PAPI calls for transaction signing.\n *\n * Forwards the structured payload to the paired mobile wallet via\n * `session.createTransaction`; the wallet builds + signs the extrinsic and\n * returns the signed bytes. Unlike the old `signRaw({ tag: \"Payload\" })`\n * route, the wallet sees a real transaction (not opaque bytes) and unknown\n * signed extensions are preserved by {@link buildCreateTransactionRequest}.\n */\nfunction makeTxSignTx(\n session: UserSession,\n productAccountId: [string, number],\n): PolkadotSigner[\"signTx\"] {\n return async (callData, signedExtensions, metadata) =>\n requestSignedTransaction(\n session,\n buildCreateTransactionRequest(\n productAccountId,\n callData,\n signedExtensions,\n txExtVersionFromMetadata(metadata),\n ),\n );\n}\n\n/**\n * Sign function for arbitrary-byte signing (`PolkadotSigner.signBytes`).\n *\n * Routes to `session.signRaw` under the `Bytes` tag — the wallet's\n * raw-bytes interactor, which applies the standard `<Bytes>...</Bytes>`\n * anti-phishing envelope before signing.\n *\n * Provided as a separate function so `buildSessionSigner` can override\n * the `signBytes` produced by `getPolkadotSigner` (which would otherwise\n * funnel raw-bytes signing through the same `sign` callback as tx signing\n * — the wrong wire tag for arbitrary user data).\n */\nfunction makeRawBytesSignCallback(session: UserSession, productAccountId: [string, number]) {\n return async (data: Uint8Array): Promise<Uint8Array> => {\n const result = await session.signRaw({\n productAccountId,\n data: { tag: \"Bytes\", value: data },\n });\n\n if (result.isErr()) {\n const expired = toAllowanceExpiredError(result.error);\n if (expired) throw expired;\n throw new Error(`Mobile signing rejected: ${result.error.message}`);\n }\n\n return result.value.signature;\n };\n}\n\nexport const INCOMPLETE_SESSION_MESSAGE =\n 'Stored login session is missing the root account public key. Run \"logout\" and then \"login\" to pair again.';\n\n/**\n * The session's handshake-time root account public key (`rootUserAccountId` =\n * the user's bare-mnemonic keypair on current mobile builds). This is the\n * parent key product accounts soft-derive from. host-papp's live `UserSession`\n * doesn't surface it on the public type, so we read it structurally.\n *\n * @throws {@link INCOMPLETE_SESSION_MESSAGE} if the session predates the\n * `rootAccountId` field (a stale login that must re-pair).\n */\nexport function sessionRootPublicKey(session: UserSession): Uint8Array {\n const rootAccountId = (session as { rootAccountId?: Uint8Array }).rootAccountId;\n const publicKey = rootAccountId ? new Uint8Array(rootAccountId) : new Uint8Array();\n if (publicKey.length !== 32) {\n throw new Error(INCOMPLETE_SESSION_MESSAGE);\n }\n return publicKey;\n}\n\n/**\n * Soft-derive a product account's sr25519 public key from a paired session's\n * root account.\n *\n * This is the single source of truth for product-account math. The session\n * signer uses it to stamp the correct signer address; consumers that need the\n * product address *without* building a signer (e.g. a login-status display\n * triple) call it directly so the displayed address can't desync from what the\n * signer actually signs with.\n *\n * sr25519 soft derivation is composable on public keys alone, so deriving from\n * `session.rootAccountId` locally produces the SAME key the host derives\n * privately via `mnemonic + \"/product/{productId}/{derivationIndex}\"`.\n *\n * @throws {@link INCOMPLETE_SESSION_MESSAGE} on a stale session with no root key.\n */\nexport function deriveProductPublicKey(session: UserSession, ref: ProductAccountRef): Uint8Array {\n return deriveProductAccountPublicKey(\n sessionRootPublicKey(session),\n ref.productId,\n ref.derivationIndex,\n );\n}\n\nfunction buildSessionSigner(session: UserSession, ref: ProductAccountRef): PolkadotSigner {\n const productAccountId: [string, number] = [ref.productId, ref.derivationIndex];\n\n // The signer's public key must be the *product* account's key — the one the\n // wallet signs with for [productId, derivationIndex] — not the wallet's\n // selected root account. PAPI stamps this into the extrinsic's address and\n // verifies against it, so a mismatch yields an invalid signature.\n //\n // When the caller supplies `ref.publicKey` we trust it; otherwise we\n // soft-derive it from the session root. We deliberately do NOT fall back to\n // `session.remoteAccount.accountId` (the selected account) — that silently\n // produces an invalid signature for any product account that isn't the\n // currently-selected one.\n const publicKey = ref.publicKey ?? deriveProductPublicKey(session, ref);\n\n return {\n publicKey,\n // Transaction signing → host-papp `createTransaction`: the wallet builds\n // and signs the extrinsic, so it can show a decoded tx (no `<Bytes>`\n // envelope) and unknown signed extensions (e.g. `AsPgas`) survive.\n signTx: makeTxSignTx(session, productAccountId),\n // Arbitrary-byte signing keeps the `Bytes` tag so the wallet applies the\n // `<Bytes>...</Bytes>` anti-phishing envelope exactly once on its side\n // (correct for non-extrinsic user data).\n signBytes: makeRawBytesSignCallback(session, productAccountId),\n };\n}\n\n/**\n * Create a `PolkadotSigner` backed by a QR-paired mobile wallet session,\n * using the session's **default account** (`derivationIndex: 0`).\n *\n * For non-default sub-accounts, use {@link createSessionSignerForAccount}.\n *\n * @param session The paired user session.\n * @param adapter The {@link TerminalAdapter} that loaded the session. Its `appId`\n * is used as the `productId` in the wire request.\n * @param publicKey The product account's sr25519 public key for\n * `[adapter.appId, 0]`. Optional — when omitted it's soft-derived from the\n * session root. See {@link ProductAccountRef.publicKey}.\n */\nexport function createSessionSigner(\n session: UserSession,\n adapter: TerminalAdapter,\n publicKey?: Uint8Array,\n): PolkadotSigner {\n return buildSessionSigner(session, { productId: adapter.appId, derivationIndex: 0, publicKey });\n}\n\n/**\n * Create a `PolkadotSigner` for a specific sub-account of a paired session.\n *\n * Use this when you need a derivation index other than `0`, or a `productId`\n * different from the adapter's `appId`. For the common default-account case,\n * prefer {@link createSessionSigner}.\n *\n * @param session The paired user session.\n * @param ref The product account to sign as: `{ productId, derivationIndex }`.\n */\nexport function createSessionSignerForAccount(\n session: UserSession,\n ref: ProductAccountRef,\n): PolkadotSigner {\n return buildSessionSigner(session, ref);\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n const { ok, err } = await import(\"neverthrow\");\n const { seedToAccount } = await import(\"@parity/product-sdk-keys\");\n\n // A real sr25519 root public key — the derivation runs actual ristretto255\n // point math, so a fixture root must be a valid curve point, not arbitrary\n // bytes. Derived once from a fixed dev seed.\n const DEV_ROOT_PUBLIC_KEY = seedToAccount(\n \"bottom drive obey lake curtain smoke basket hold race lonely fit walk\",\n \"\",\n ).publicKey;\n\n /**\n * Build a minimal `UserSession`-shaped stub. `signPayload`, `signRaw`, and\n * `createTransaction` accept request-capturing functions so tests can assert\n * on exactly which host-papp method got called and with what payload.\n */\n function makeSession(opts: {\n signPayload?: (req: unknown) => Promise<unknown>;\n signRaw?: (req: unknown) => Promise<unknown>;\n createTransaction?: (req: unknown) => Promise<unknown>;\n accountIdBytes?: number[];\n /** Root account the product key is soft-derived from when publicKey is\n * omitted. Defaults to a valid 32-byte key; pass `null` to simulate a\n * stale session that predates the `rootAccountId` field. */\n rootAccountId?: number[] | null;\n }): UserSession {\n const accountIdBytes = opts.accountIdBytes ?? new Array(32).fill(0).map((_, i) => i);\n const rootAccountId =\n opts.rootAccountId === undefined ? Array.from(DEV_ROOT_PUBLIC_KEY) : opts.rootAccountId;\n return {\n remoteAccount: { accountId: accountIdBytes },\n ...(rootAccountId === null ? {} : { rootAccountId: new Uint8Array(rootAccountId) }),\n signPayload: vi.fn(\n opts.signPayload ??\n (async () => {\n throw new Error(\"signPayload not stubbed in this test\");\n }),\n ),\n signRaw: vi.fn(\n opts.signRaw ??\n (async () => {\n throw new Error(\"signRaw not stubbed in this test\");\n }),\n ),\n createTransaction: vi.fn(\n opts.createTransaction ??\n (async () => {\n throw new Error(\"createTransaction not stubbed in this test\");\n }),\n ),\n } as unknown as UserSession;\n }\n\n /**\n * A minimal PAPI `signedExtensions` entry. `value` is the SCALE-encoded\n * \"extra\" (extrinsic body); `additionalSigned` is the implicit data.\n */\n function ext(identifier: string, value: number[], additionalSigned: number[]) {\n return {\n identifier,\n value: new Uint8Array(value),\n additionalSigned: new Uint8Array(additionalSigned),\n };\n }\n\n function fakeAdapter(appId: string): TerminalAdapter {\n // Only the `appId` field matters for these tests.\n return { appId } as unknown as TerminalAdapter;\n }\n\n describe(\"createSessionSigner\", () => {\n test(\"self-derives the product key from the session root when none is passed\", () => {\n // No explicit publicKey → derive from rootAccountId, NOT the selected\n // account. The derived key must equal deriveProductAccountPublicKey\n // and must differ from remoteAccount.accountId (the old buggy fallback).\n const walletBytes = Array.from({ length: 32 }, (_, i) => i);\n const signer = createSessionSigner(\n makeSession({\n accountIdBytes: walletBytes,\n rootAccountId: Array.from(DEV_ROOT_PUBLIC_KEY),\n }),\n fakeAdapter(\"test-app\"),\n );\n const expected = deriveProductAccountPublicKey(DEV_ROOT_PUBLIC_KEY, \"test-app\", 0);\n expect(signer.publicKey).toEqual(expected);\n expect(signer.publicKey).not.toEqual(new Uint8Array(walletBytes));\n });\n\n test(\"throws on a stale session with no root account key\", () => {\n expect(() =>\n createSessionSigner(makeSession({ rootAccountId: null }), fakeAdapter(\"test-app\")),\n ).toThrow(INCOMPLETE_SESSION_MESSAGE);\n });\n\n test(\"uses the host-supplied product-account public key when provided\", () => {\n // The product account's key differs from the wallet's selected\n // account (`remoteAccount.accountId`). PAPI must stamp the *product*\n // key into the extrinsic, so the explicit key must win.\n const walletBytes = Array.from({ length: 32 }, (_, i) => i);\n const productKey = new Uint8Array(32).fill(0xab);\n const signer = createSessionSigner(\n makeSession({ accountIdBytes: walletBytes }),\n fakeAdapter(\"test-app\"),\n productKey,\n );\n expect(signer.publicKey).toEqual(productKey);\n expect(signer.publicKey).not.toEqual(new Uint8Array(walletBytes));\n });\n\n test(\"signBytes routes through session.signRaw with the Bytes tag\", async () => {\n const sig = new Uint8Array([9, 8, 7, 6, 5, 4, 3, 2, 1]);\n const captured: unknown[] = [];\n const session = makeSession({\n signRaw: async (req) => {\n captured.push(req);\n return ok({ signature: sig });\n },\n });\n const signer = createSessionSigner(session, fakeAdapter(\"test-app\"));\n\n const out = await signer.signBytes(new Uint8Array([1, 2, 3]));\n expect(out).toEqual(sig);\n\n // The bytes route forwards data verbatim under the `Bytes` tag.\n // Mobile applies the <Bytes>...</Bytes> envelope on its side.\n expect(captured).toHaveLength(1);\n const req = captured[0] as {\n productAccountId: [string, number];\n data: { tag: string; value: Uint8Array };\n };\n expect(req.productAccountId).toEqual([\"test-app\", 0]);\n expect(req.data.tag).toBe(\"Bytes\");\n expect(req.data.value).toEqual(new Uint8Array([1, 2, 3]));\n });\n\n test(\"signBytes never calls session.signPayload (legacy-path regression guard)\", async () => {\n // The PJS-bridged path used to route tx signing through\n // `session.signPayload`. The new PAPI-native path retires\n // `signPayload` entirely for this signer. This test guards\n // against accidentally re-introducing the legacy call.\n const session = makeSession({\n signRaw: async () => ok({ signature: new Uint8Array([1]) }),\n });\n const signer = createSessionSigner(session, fakeAdapter(\"test-app\"));\n\n await signer.signBytes(new Uint8Array([1, 2, 3]));\n\n const sessionWithSpies = session as unknown as {\n signPayload: { mock: { calls: unknown[] } };\n signRaw: { mock: { calls: unknown[] } };\n };\n expect(sessionWithSpies.signPayload.mock.calls).toHaveLength(0);\n expect(sessionWithSpies.signRaw.mock.calls).toHaveLength(1);\n });\n\n test(\"signBytes throws with a clear error when the mobile rejects\", async () => {\n const session = makeSession({\n signRaw: async () => err({ message: \"user declined\" }),\n });\n const signer = createSessionSigner(session, fakeAdapter(\"test-app\"));\n\n await expect(signer.signBytes(new Uint8Array([1]))).rejects.toThrow(\n \"Mobile signing rejected: user declined\",\n );\n });\n });\n\n describe(\"buildCreateTransactionRequest — tx payload (the AsPgas fix)\", () => {\n // Asserts the wire shape handed to the wallet. The full PAPI signTx →\n // metadata decode → SSO round-trip is exercised by the manual smoke\n // test in `manual-tests/qr-pair-and-sign.mjs` since CI cannot drive\n // a real phone.\n\n test(\"prefers V4 (tx-ext version 0) on a dual V4/V5 runtime\", () => {\n expect(selectTxExtVersion([4, 5], [0])).toBe(0);\n });\n\n test(\"uses the general tx-ext version 5 when no V4 fallback exists\", () => {\n expect(selectTxExtVersion([5], [0, 5])).toBe(5);\n });\n\n test(\"maps a V4-only runtime to the wire sentinel\", () => {\n expect(selectTxExtVersion([4], [0])).toBe(0);\n });\n\n test(\"does not confuse extrinsic format 5 with a tx-ext version\", () => {\n expect(() => selectTxExtVersion([5], [0])).toThrow(/no.*version 5/i);\n });\n\n test(\"rejects metadata with no extrinsic version\", () => {\n expect(() => selectTxExtVersion([], [0])).toThrow(\n \"No extrinsic version found in metadata\",\n );\n });\n\n const checkGenesis = ext(\"CheckGenesis\", [], [0x11, 0x22, 0x33]);\n\n test(\"wraps the payload as v1 with signer, callData, and txExtVersion\", () => {\n const callData = new Uint8Array([0xca, 0x11]);\n const req = buildCreateTransactionRequest(\n [\"my-app\", 3],\n callData,\n { CheckGenesis: checkGenesis },\n 5,\n );\n expect(req.payload.tag).toBe(\"v1\");\n expect(req.payload.value.signer).toEqual([\"my-app\", 3]);\n expect(req.payload.value.callData).toEqual(callData);\n expect(req.payload.value.txExtVersion).toBe(5);\n });\n\n test(\"takes the genesis hash from CheckGenesis.additionalSigned\", () => {\n const req = buildCreateTransactionRequest(\n [\"my-app\", 0],\n new Uint8Array([0]),\n { CheckGenesis: checkGenesis },\n 0,\n );\n expect(req.payload.value.genesisHash).toEqual(new Uint8Array([0x11, 0x22, 0x33]));\n });\n\n test(\"maps every signed extension to { id, extra, additionalSigned }\", () => {\n const req = buildCreateTransactionRequest(\n [\"my-app\", 0],\n new Uint8Array([0]),\n { CheckGenesis: checkGenesis, CheckNonce: ext(\"CheckNonce\", [0x07], []) },\n 0,\n );\n expect(req.payload.value.extensions).toEqual([\n {\n id: \"CheckGenesis\",\n extra: new Uint8Array([]),\n additionalSigned: new Uint8Array([0x11, 0x22, 0x33]),\n },\n {\n id: \"CheckNonce\",\n extra: new Uint8Array([0x07]),\n additionalSigned: new Uint8Array([]),\n },\n ]);\n });\n\n test(\"preserves an unknown signed extension verbatim (e.g. AsPgas)\", () => {\n // The whole reason for moving off PJS: an extension PAPI's PJS\n // adapter doesn't know must pass through untouched.\n const req = buildCreateTransactionRequest(\n [\"my-app\", 0],\n new Uint8Array([0]),\n { CheckGenesis: checkGenesis, AsPgas: ext(\"AsPgas\", [0xde, 0xad], [0xbe, 0xef]) },\n 0,\n );\n expect(req.payload.value.extensions).toContainEqual({\n id: \"AsPgas\",\n extra: new Uint8Array([0xde, 0xad]),\n additionalSigned: new Uint8Array([0xbe, 0xef]),\n });\n });\n\n test(\"throws a clear error when CheckGenesis is absent\", () => {\n expect(() =>\n buildCreateTransactionRequest([\"my-app\", 0], new Uint8Array([0]), {}, 0),\n ).toThrow(/CheckGenesis/);\n });\n });\n\n describe(\"requestSignedTransaction — SSO round-trip\", () => {\n const request = buildCreateTransactionRequest(\n [\"my-app\", 0],\n new Uint8Array([0]),\n { CheckGenesis: ext(\"CheckGenesis\", [], [0x01]) },\n 0,\n );\n\n test(\"returns the signed extrinsic bytes from the mobile response\", async () => {\n const signed = new Uint8Array([0xab, 0xcd, 0xef]);\n const session = makeSession({ createTransaction: async () => ok(signed) });\n\n const out = await requestSignedTransaction(session, request);\n expect(out).toBe(signed);\n });\n\n test(\"calls session.createTransaction — never signRaw/signPayload — with the request\", async () => {\n const captured: unknown[] = [];\n const session = makeSession({\n createTransaction: async (req) => {\n captured.push(req);\n return ok(new Uint8Array([1]));\n },\n });\n\n await requestSignedTransaction(session, request);\n\n expect(captured).toEqual([request]);\n const spies = session as unknown as {\n signPayload: { mock: { calls: unknown[] } };\n signRaw: { mock: { calls: unknown[] } };\n createTransaction: { mock: { calls: unknown[] } };\n };\n expect(spies.createTransaction.mock.calls).toHaveLength(1);\n expect(spies.signPayload.mock.calls).toHaveLength(0);\n expect(spies.signRaw.mock.calls).toHaveLength(0);\n });\n\n test(\"throws a clear error when the mobile rejects\", async () => {\n const session = makeSession({\n createTransaction: async () => err({ message: \"user declined\" }),\n });\n await expect(requestSignedTransaction(session, request)).rejects.toThrow(\n \"Mobile transaction signing rejected: user declined\",\n );\n });\n\n test(\"throws AllowanceExpiredError when the failure is a NoAllowanceError\", async () => {\n const underlying = new NoAllowanceError();\n const session = makeSession({\n createTransaction: async () => err(underlying),\n });\n\n await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(\n AllowanceExpiredError,\n );\n await expect(requestSignedTransaction(session, request)).rejects.toMatchObject({\n name: \"AllowanceExpiredError\",\n resource: \"statementStore\",\n cause: underlying,\n });\n });\n\n test(\"throws AllowanceExpiredError for a wire-serialized failure (message match only)\", async () => {\n // After a serialization boundary the NoAllowanceError prototype\n // and constructor name are gone — the fixed message upstream\n // mints is the only remaining signal. This pins statement-store's\n // exact wording: if upstream rephrases it, this test fails\n // instead of the classification silently downgrading to the\n // generic rejection.\n // Pin upstream's wording first: if statement-store rephrases the\n // message, this assertion fails loudly.\n expect(new NoAllowanceError().message).toBe(\n \"Submit failed, no allowance set for account\",\n );\n\n const serialized = new Error(\"Submit failed, no allowance set for account\");\n const session = makeSession({\n createTransaction: async () => err(serialized),\n });\n\n await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(\n AllowanceExpiredError,\n );\n });\n\n test(\"throws AllowanceExpiredError when the NoAllowanceError is wrapped as a cause\", async () => {\n // host-papp may wrap the chain-side submit failure before it hits\n // the createTransaction error channel — the cause chain must be\n // walked, not just the top-level error.\n const wrapped = new Error(\"submitRequest failed\", { cause: new NoAllowanceError() });\n const session = makeSession({\n createTransaction: async () => err(wrapped),\n });\n\n await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(\n AllowanceExpiredError,\n );\n });\n\n test(\"a non-allowance Error still surfaces as the generic rejection\", async () => {\n const session = makeSession({\n createTransaction: async () => err(new Error(\"transport exploded\")),\n });\n\n await expect(requestSignedTransaction(session, request)).rejects.toThrow(\n \"Mobile transaction signing rejected: transport exploded\",\n );\n });\n });\n\n describe(\"makeRawBytesSignCallback\", () => {\n test(\"forwards data verbatim under the Bytes tag with the right productAccountId\", async () => {\n const captured: unknown[] = [];\n const session = makeSession({\n signRaw: async (req) => {\n captured.push(req);\n return ok({ signature: new Uint8Array([0xff]) });\n },\n });\n\n const callback = makeRawBytesSignCallback(session, [\"my-app\", 5]);\n await callback(new Uint8Array([0xde, 0xad, 0xbe, 0xef]));\n\n expect(captured).toHaveLength(1);\n const req = captured[0] as {\n productAccountId: [string, number];\n data: { tag: string; value: Uint8Array };\n };\n expect(req.productAccountId).toEqual([\"my-app\", 5]);\n expect(req.data.tag).toBe(\"Bytes\");\n expect(Array.from(req.data.value)).toEqual([0xde, 0xad, 0xbe, 0xef]);\n });\n\n test(\"returns the raw signature bytes from the mobile response\", async () => {\n const sig = new Uint8Array([0x42]);\n const session = makeSession({\n signRaw: async () => ok({ signature: sig }),\n });\n\n const callback = makeRawBytesSignCallback(session, [\"my-app\", 0]);\n const out = await callback(new Uint8Array([0]));\n\n expect(out).toBe(sig);\n });\n\n test(\"throws AllowanceExpiredError when the failure is a NoAllowanceError\", async () => {\n const underlying = new NoAllowanceError();\n const session = makeSession({\n signRaw: async () => err(underlying),\n });\n\n const callback = makeRawBytesSignCallback(session, [\"my-app\", 0]);\n await expect(callback(new Uint8Array([1]))).rejects.toBeInstanceOf(\n AllowanceExpiredError,\n );\n await expect(callback(new Uint8Array([1]))).rejects.toMatchObject({\n resource: \"statementStore\",\n cause: underlying,\n });\n });\n\n test(\"a non-allowance failure still surfaces as the generic rejection\", async () => {\n const session = makeSession({\n signRaw: async () => err(new Error(\"user declined\")),\n });\n\n const callback = makeRawBytesSignCallback(session, [\"my-app\", 0]);\n await expect(callback(new Uint8Array([1]))).rejects.toThrow(\n \"Mobile signing rejected: user declined\",\n );\n });\n });\n\n describe(\"createSessionSignerForAccount\", () => {\n test(\"forwards productAccountId from the explicit ref\", async () => {\n const captured: unknown[] = [];\n const session = makeSession({\n signRaw: async (req) => {\n captured.push(req);\n return ok({ signature: new Uint8Array([42]) });\n },\n });\n\n const signer = createSessionSignerForAccount(session, {\n productId: \"my-app\",\n derivationIndex: 7,\n });\n await signer.signBytes(new Uint8Array([10, 20, 30]));\n\n expect(captured).toHaveLength(1);\n const req = captured[0] as {\n productAccountId: [string, number];\n data: { tag: string; value: Uint8Array };\n };\n expect(req.productAccountId).toEqual([\"my-app\", 7]);\n expect(req.data.tag).toBe(\"Bytes\");\n expect(req.data.value).toBeInstanceOf(Uint8Array);\n });\n\n test(\"supports a productId different from any adapter's appId\", async () => {\n const captured: unknown[] = [];\n const session = makeSession({\n signRaw: async (req) => {\n captured.push(req);\n return ok({ signature: new Uint8Array([0]) });\n },\n });\n\n const signer = createSessionSignerForAccount(session, {\n productId: \"external-product\",\n derivationIndex: 0,\n });\n await signer.signBytes(new Uint8Array([1]));\n\n const req = captured[0] as { productAccountId: [string, number] };\n expect(req.productAccountId).toEqual([\"external-product\", 0]);\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n// Terminal Adapter\nexport {\n createTerminalAdapter,\n isBenignTeardownError,\n SS_STABLE_STAGE_ENDPOINTS,\n SS_PASEO_STABLE_STAGE_ENDPOINTS,\n} from \"./adapter.js\";\nexport type { TerminalAdapterOptions, TerminalAdapter } from \"./adapter.js\";\n\n// Session Signer\nexport {\n createSessionSigner,\n createSessionSignerForAccount,\n deriveProductPublicKey,\n sessionRootPublicKey,\n INCOMPLETE_SESSION_MESSAGE,\n} from \"./signer.js\";\nexport type { ProductAccountRef } from \"./signer.js\";\n// Thrown by the session signers' signTx/signBytes when the paired session's\n// allowance has lapsed (chain-side NoAllowanceError). Authored in\n// @parity/product-sdk-signer — the SDK's signer error taxonomy — and\n// re-exported here so terminal consumers can catch it without a second import.\nexport { AllowanceExpiredError, SignerError } from \"@parity/product-sdk-signer/errors\";\n\n// Allowance service — `adapter.allowance` lives on every TerminalAdapter (inherited\n// from host-papp's PappAdapter). These helpers default the session id to the only\n// paired session and unwrap the underlying ResultAsync into a throw, matching the\n// idiom of the rest of this package.\n//\n// The `has*Allowance` variants are cache-only probes that never prompt the\n// paired wallet — safe to call from login health-check paths that must not\n// surface a phone dialog.\nexport {\n getBulletinSigner,\n getStatementStoreProver,\n hasBulletinAllowance,\n hasStatementStoreAllowance,\n} from \"./allowance.js\";\nexport { AllowanceError } from \"@novasamatech/host-papp\";\nexport type { AllowanceService, AllowanceErrorReason } from \"@novasamatech/host-papp\";\n\n// Resource allocation (RFC-0010). `requestResourceAllocation` is the cached,\n// adapter-scoped entry point; `sendResourceAllocation` is the raw wire call for\n// consumers that hold only a productId and manage their own policy. The\n// resource / outcome types are derived from `UserSession` so they can't drift\n// from the host codec.\nexport { requestResourceAllocation, sendResourceAllocation } from \"./host.js\";\nexport type {\n AllocatableResource,\n ApAllocationOutcome,\n OnExistingAllowancePolicy,\n RequestResourceAllocationOptions,\n} from \"./host.js\";\n\n// Session helpers\nexport { waitForSessions } from \"./sessions.js\";\n\n// QR Encoding\nexport { renderQrCode } from \"./qr-encode.js\";\nexport type { QrRenderOptions } from \"./qr-encode.js\";\n\n// Storage adapter (advanced use — most callers get this implicitly via createTerminalAdapter)\nexport { createNodeStorageAdapter } from \"./node-storage.js\";\n\n// Re-export SDK types consumers will need\nexport type {\n PappAdapter,\n HostMetadata,\n PairingStatus,\n UserSession,\n StoredUserSession,\n SigningPayloadRequest,\n SigningRawRequest,\n SigningPayloadResponse,\n} from \"@novasamatech/host-papp\";\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Convenience wrappers around `adapter.allowance` for CLI consumers.\n *\n * The host-papp `AllowanceService` (exposed as `adapter.allowance`) returns\n * neverthrow `ResultAsync` values and requires the caller to pass a\n * `sessionId` explicitly. CLIs almost always run with one paired session at\n * a time, so passing `sessionId` is redundant and the neverthrow envelope\n * doesn't fit the rest of `@parity/product-sdk-terminal`'s throwy/async\n * idiom (`createSessionSigner`, `requestResourceAllocation`).\n *\n * These helpers:\n * - default the `sessionId` to the single paired session when there's\n * exactly one (the common CLI case);\n * - unwrap the `ResultAsync` into a `Promise<T>` that throws an\n * {@link AllowanceError} on failure;\n * - leave the underlying `adapter.allowance` intact for callers who want\n * explicit multi-session handling or neverthrow's `.match` ergonomics.\n *\n * @module\n */\nimport { AllowanceError } from \"@novasamatech/host-papp\";\nimport type { StatementProver } from \"@novasamatech/statement-store\";\nimport type { PolkadotSigner } from \"polkadot-api\";\n\nimport type { TerminalAdapter } from \"./adapter.js\";\nimport { type AllowanceResourceKind, readStoredAllowances } from \"./allowance-cache.js\";\n\n/**\n * Pick the session id to use for an allowance request, defaulting to the\n * single paired session when no explicit id is supplied.\n *\n * @throws {AllowanceError} `NoSession` when zero sessions or `>1` sessions\n * are paired without an explicit id — both cases are ambiguous and the\n * caller should resolve them before calling the convenience.\n */\nfunction resolveSessionId(adapter: TerminalAdapter, sessionId?: string): string {\n if (sessionId) return sessionId;\n const sessions = adapter.sessions.sessions.read();\n if (sessions.length === 0) {\n // Same error class host-papp emits internally so catch-by-instanceof\n // handlers behave identically for both paths.\n throw new AllowanceError(\"NoSession\", \"No paired session — pair a phone first.\");\n }\n if (sessions.length > 1) {\n throw new AllowanceError(\n \"NoSession\",\n `Multiple paired sessions (${sessions.length}) — pass an explicit sessionId.`,\n );\n }\n return sessions[0]!.id;\n}\n\n/**\n * Get a `PolkadotSigner` for a Bulletin allowance slot.\n *\n * Allocates an allowance slot via the paired wallet (or returns the cached\n * one), derives the slot-account keypair, and returns a `PolkadotSigner`\n * that signs Bulletin extrinsics with it. Replaces the manual\n * `requestResourceAllocation` + `createSlotAccountSigner` two-step for the\n * common case.\n *\n * @param adapter Terminal adapter.\n * @param productId The product id the slot is allocated under. Passed to the\n * host as the calling product id in the allowance request.\n * @param sessionId Paired session to allocate against. Defaults to the only\n * paired session; throws `AllowanceError('NoSession')` when zero or more\n * than one sessions are paired and no explicit id is supplied.\n * @throws {AllowanceError} On rejection, missing session, host-side failure,\n * or unexpected response shape.\n *\n * @example\n * ```ts\n * import { createTerminalAdapter, getBulletinSigner } from \"@parity/product-sdk-terminal\";\n *\n * const adapter = createTerminalAdapter({ appId: \"my-cli\" });\n * // ... QR pair, wait for session ...\n * const signer = await getBulletinSigner(adapter, \"my-cli.dot\");\n * await client.bulletin.tx.TransactionStorage.store({ data }).signAndSubmit(signer);\n * ```\n */\nexport async function getBulletinSigner(\n adapter: TerminalAdapter,\n productId: string,\n sessionId?: string,\n): Promise<PolkadotSigner> {\n const resolvedSessionId = resolveSessionId(adapter, sessionId);\n return adapter.allowance.getBulletinSigner(resolvedSessionId, productId).match(\n (signer) => signer,\n (err) => {\n throw err;\n },\n );\n}\n\n/**\n * Get a `StatementProver` for a statement-store allowance slot.\n *\n * Allocates an allowance slot via the paired wallet (or returns the cached\n * one) and returns the upstream `StatementProver` for the slot. Use when\n * publishing statements through `@novasamatech/statement-store` without\n * holding a long-lived key yourself.\n *\n * @param adapter Terminal adapter.\n * @param productId The product id the slot is allocated under.\n * @param sessionId Paired session to allocate against. Defaults to the only\n * paired session; throws `AllowanceError('NoSession')` when zero or more\n * than one sessions are paired and no explicit id is supplied.\n * @throws {AllowanceError} On rejection, missing session, host-side failure,\n * or unexpected response shape.\n */\nexport async function getStatementStoreProver(\n adapter: TerminalAdapter,\n productId: string,\n sessionId?: string,\n): Promise<StatementProver> {\n const resolvedSessionId = resolveSessionId(adapter, sessionId);\n return adapter.allowance.getStatementStoreProver(resolvedSessionId, productId).match(\n (prover) => prover,\n (err) => {\n throw err;\n },\n );\n}\n\n/**\n * Cache-only probe: read host-papp's encrypted on-disk allowance list and\n * resolve to whether an entry exists for `(sessionId, productId, resource)`.\n *\n * Never invokes `requestResourceAllocation`, so this is safe to call from\n * paths that must not prompt the paired wallet (e.g. login health checks).\n * Resolves `false` for: file absent, file present with no entry for the\n * tuple. Rejects only on decrypt / decode failures — a corrupted cache\n * file is a real failure that should not silently degrade into \"no\n * allowance.\"\n *\n * Reads the file at `<storageDir>/<appId>_AllowanceKeys_<sessionId>.json`\n * — same path host-papp writes to from its `AllowanceRepository`. The\n * decode is via a vendored mirror of host-papp's internal codec; see\n * `allowance-cache.ts` for drift-detection guards.\n */\nasync function hasAllowanceForResource(\n adapter: TerminalAdapter,\n productId: string,\n resource: AllowanceResourceKind,\n sessionId?: string,\n): Promise<boolean> {\n const resolvedSessionId = resolveSessionId(adapter, sessionId);\n if (adapter.storageDir === undefined) {\n // The on-disk cache only exists when the adapter knows where its files\n // live. Without an explicit `storageDir` the production host-papp\n // backend may write through a non-disk storage adapter that this\n // helper can't read; we conservatively answer false so callers don't\n // skip pairing on a missed cache.\n return false;\n }\n const entries = await readStoredAllowances(\n adapter.storageDir,\n adapter.appId,\n resolvedSessionId,\n );\n return entries.some(\n (entry) => entry.productId === productId && entry.resource.tag === resource,\n );\n}\n\n/**\n * Cache-only probe for a Bulletin allowance slot. Resolves `true` when a\n * slot key for `(sessionId, productId, bulletin)` is already cached on\n * disk; `false` when it is not. Never prompts the paired wallet.\n *\n * Pair with {@link getBulletinSigner} for the \"check first, fetch only\n * if needed\" flow:\n *\n * @example\n * ```ts\n * if (await hasBulletinAllowance(adapter, \"my-cli.dot\")) {\n * // happy path — fetch the signer without risking a wallet prompt\n * const signer = await getBulletinSigner(adapter, \"my-cli.dot\");\n * } else {\n * // tell the user a wallet prompt will fire, then call getBulletinSigner\n * }\n * ```\n */\nexport async function hasBulletinAllowance(\n adapter: TerminalAdapter,\n productId: string,\n sessionId?: string,\n): Promise<boolean> {\n return hasAllowanceForResource(adapter, productId, \"bulletin\", sessionId);\n}\n\n/**\n * Cache-only probe for a Statement Store allowance slot. Resolves `true`\n * when a slot key for `(sessionId, productId, statementStore)` is already\n * cached on disk; `false` when it is not. Never prompts the paired wallet.\n *\n * Pair with {@link getStatementStoreProver} for the \"check first, fetch\n * only if needed\" flow.\n */\nexport async function hasStatementStoreAllowance(\n adapter: TerminalAdapter,\n productId: string,\n sessionId?: string,\n): Promise<boolean> {\n return hasAllowanceForResource(adapter, productId, \"statementStore\", sessionId);\n}\n\n// ── vitest in-source tests ─────────────────────────────────────────────────\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n const { ok, err } = await import(\"neverthrow\");\n\n type FakeSession = { id: string };\n\n function makeAdapter(opts: {\n sessions: FakeSession[];\n getBulletinSigner?: ReturnType<typeof vi.fn>;\n getStatementStoreProver?: ReturnType<typeof vi.fn>;\n }): TerminalAdapter {\n return {\n sessions: {\n sessions: {\n read: () => opts.sessions,\n },\n },\n allowance: {\n getBulletinSigner: opts.getBulletinSigner ?? vi.fn(() => ok({})),\n getStatementStoreProver: opts.getStatementStoreProver ?? vi.fn(() => ok({})),\n },\n // The convenience never touches the rest of the adapter; leave it unstubbed.\n } as unknown as TerminalAdapter;\n }\n\n describe(\"getBulletinSigner — sessionId defaulting\", () => {\n test(\"uses the only paired session when sessionId is omitted\", async () => {\n const fakeSigner = { _tag: \"signer\" };\n const getBulletinSignerFn = vi.fn(() => ok(fakeSigner));\n const adapter = makeAdapter({\n sessions: [{ id: \"sess-only\" }],\n getBulletinSigner: getBulletinSignerFn,\n });\n\n const result = await getBulletinSigner(adapter, \"my-app.dot\");\n expect(result).toBe(fakeSigner);\n expect(getBulletinSignerFn).toHaveBeenCalledWith(\"sess-only\", \"my-app.dot\");\n });\n\n test(\"uses the explicit sessionId when supplied\", async () => {\n const fakeSigner = { _tag: \"signer\" };\n const getBulletinSignerFn = vi.fn(() => ok(fakeSigner));\n const adapter = makeAdapter({\n sessions: [{ id: \"sess-a\" }, { id: \"sess-b\" }],\n getBulletinSigner: getBulletinSignerFn,\n });\n\n await getBulletinSigner(adapter, \"my-app.dot\", \"sess-b\");\n expect(getBulletinSignerFn).toHaveBeenCalledWith(\"sess-b\", \"my-app.dot\");\n });\n\n test(\"throws AllowanceError('NoSession') when zero sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [] });\n await expect(getBulletinSigner(adapter, \"my-app.dot\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n await expect(getBulletinSigner(adapter, \"my-app.dot\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n\n test(\"throws AllowanceError('NoSession') when multiple sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [{ id: \"a\" }, { id: \"b\" }] });\n await expect(getBulletinSigner(adapter, \"my-app.dot\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n await expect(getBulletinSigner(adapter, \"my-app.dot\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n });\n\n describe(\"getBulletinSigner — error unwrapping\", () => {\n test(\"rethrows the AllowanceError from the underlying service as a thrown exception\", async () => {\n const underlyingErr = new AllowanceError(\"Rejected\", \"user said no\");\n const adapter = makeAdapter({\n sessions: [{ id: \"s\" }],\n getBulletinSigner: vi.fn(() => err(underlyingErr)),\n });\n // The wrapper rethrows the original error instance — handlers\n // catching by `instanceof AllowanceError` see the same object the\n // underlying service produced.\n await expect(getBulletinSigner(adapter, \"p\")).rejects.toBe(underlyingErr);\n });\n });\n\n describe(\"getStatementStoreProver — same defaulting\", () => {\n test(\"uses the only paired session when sessionId is omitted\", async () => {\n const fakeProver = { _tag: \"prover\" };\n const getStatementStoreProverFn = vi.fn(() => ok(fakeProver));\n const adapter = makeAdapter({\n sessions: [{ id: \"sess-only\" }],\n getStatementStoreProver: getStatementStoreProverFn,\n });\n\n const result = await getStatementStoreProver(adapter, \"my-app.dot\");\n expect(result).toBe(fakeProver);\n expect(getStatementStoreProverFn).toHaveBeenCalledWith(\"sess-only\", \"my-app.dot\");\n });\n\n test(\"uses the explicit sessionId when supplied\", async () => {\n const fakeProver = { _tag: \"prover\" };\n const getStatementStoreProverFn = vi.fn(() => ok(fakeProver));\n const adapter = makeAdapter({\n sessions: [{ id: \"sess-a\" }, { id: \"sess-b\" }],\n getStatementStoreProver: getStatementStoreProverFn,\n });\n\n await getStatementStoreProver(adapter, \"my-app.dot\", \"sess-b\");\n expect(getStatementStoreProverFn).toHaveBeenCalledWith(\"sess-b\", \"my-app.dot\");\n });\n\n test(\"throws AllowanceError('NoSession') when zero sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [] });\n await expect(getStatementStoreProver(adapter, \"my-app.dot\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n await expect(getStatementStoreProver(adapter, \"my-app.dot\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n\n test(\"throws AllowanceError('NoSession') when multiple sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [{ id: \"a\" }, { id: \"b\" }] });\n await expect(getStatementStoreProver(adapter, \"my-app.dot\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n await expect(getStatementStoreProver(adapter, \"my-app.dot\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n });\n\n describe(\"getStatementStoreProver — error unwrapping\", () => {\n test(\"rethrows the AllowanceError from the underlying service as a thrown exception\", async () => {\n const underlyingErr = new AllowanceError(\"Rejected\", \"user said no\");\n const adapter = makeAdapter({\n sessions: [{ id: \"s\" }],\n getStatementStoreProver: vi.fn(() => err(underlyingErr)),\n });\n await expect(getStatementStoreProver(adapter, \"p\")).rejects.toBe(underlyingErr);\n });\n });\n\n describe(\"has*Allowance — sessionId defaulting + storageDir fallback\", () => {\n // Cache-hit / cache-miss against real disk + the host-papp codec are\n // covered by `allowance.interop.test.ts`. These unit tests cover the\n // branches that don't reach the filesystem.\n\n test(\"hasBulletinAllowance throws AllowanceError('NoSession') when zero sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [] });\n await expect(hasBulletinAllowance(adapter, \"p\")).rejects.toBeInstanceOf(AllowanceError);\n await expect(hasBulletinAllowance(adapter, \"p\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n\n test(\"hasBulletinAllowance returns false (conservative) when storageDir is undefined\", async () => {\n // Production: a TerminalAdapter without an explicit storageDir\n // may be backed by a non-disk StorageAdapter that this helper\n // can't read. Returning false avoids the trap of \"we report\n // 'no allowance cached' confidently when we just couldn't read.\"\n const adapter = makeAdapter({ sessions: [{ id: \"sess-only\" }] });\n // Mock has no storageDir field — the helper should short-circuit.\n expect(await hasBulletinAllowance(adapter, \"p\")).toBe(false);\n });\n\n test(\"hasStatementStoreAllowance throws AllowanceError('NoSession') when zero sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [] });\n await expect(hasStatementStoreAllowance(adapter, \"p\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n });\n\n test(\"hasStatementStoreAllowance returns false (conservative) when storageDir is undefined\", async () => {\n const adapter = makeAdapter({ sessions: [{ id: \"sess-only\" }] });\n expect(await hasStatementStoreAllowance(adapter, \"p\")).toBe(false);\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Private mirror of host-papp's `AllowanceRepository` on-disk format.\n *\n * The repository codec + AES-GCM scheme are internal to host-papp — only the\n * higher-level `AllowanceService` is on the public surface, and that service\n * exposes no cache-only probe (its `getBulletinSigner` / `getStatementStoreProver`\n * fall through to `requestResourceAllocation` on cache miss, which prompts the\n * paired wallet). For \"is there an allowance slot for this tuple on disk?\"\n * answers — needed by login-readiness checks that must not pop a phone\n * dialog — we have to read the storage file ourselves.\n *\n * This module vendors:\n * - the SCALE codec for the `Vector(StoredAllowanceEntry)` blob host-papp\n * writes,\n * - the AES-GCM key + nonce derivation host-papp uses\n * (`blake2b(appId, 16)` / `blake2b(\"nonce\", 32)`),\n * - the storage-key naming convention (`AllowanceKeys_<sessionId>`).\n *\n * Drift guards:\n * - `satisfies Codec<StoredAllowanceEntry>` catches type-shape drift at\n * build time.\n * - `allowance.interop.test.ts` round-trips an encoded entry through the\n * real host-papp `AllowanceService.getBulletinSigner` and asserts the\n * derived signer's pubkey matches — that catches byte-level drift at\n * test time.\n *\n * TODO: drop this mirror when host-papp's `AllowanceService` gains a\n * cache-only check (e.g. `hasAllowance(sessionId, productId, resource)`)\n * on its public surface. Until then we have to read the file ourselves.\n *\n * @module\n */\nimport { gcm } from \"@noble/ciphers/aes.js\";\nimport { blake2b } from \"@noble/hashes/blake2.js\";\nimport { fromHex, toHex } from \"@polkadot-api/utils\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { Bytes, type Codec, Enum, Struct, Vector, _void, str } from \"scale-ts\";\n\nimport { sanitizeKey } from \"./node-storage.js\";\n\n/** Resource kinds host-papp allocates slot accounts for. */\nexport type AllowanceResourceKind = \"bulletin\" | \"statementStore\";\n\n/** One entry in the persisted allowance list. */\nexport type StoredAllowanceEntry = {\n productId: string;\n resource: { tag: AllowanceResourceKind; value: undefined };\n slotAccountKey: Uint8Array;\n};\n\nconst AllowanceResourceKindCodec = Enum({\n bulletin: _void,\n statementStore: _void,\n});\n\nconst StoredAllowanceEntryCodec = Struct({\n productId: str,\n resource: AllowanceResourceKindCodec,\n slotAccountKey: Bytes(),\n}) satisfies Codec<StoredAllowanceEntry>;\n\nconst StoredAllowancesCodec = Vector(StoredAllowanceEntryCodec);\n\n/**\n * AES-GCM key + nonce derivation host-papp uses for both the allowance\n * repository and the user-secrets repository — same salt (appId) seeds both.\n */\nfunction getAes(appId: string) {\n const key = blake2b(new TextEncoder().encode(appId), { dkLen: 16 });\n const nonce = blake2b(new TextEncoder().encode(\"nonce\"), { dkLen: 32 });\n return gcm(key, nonce);\n}\n\n/**\n * Storage file path host-papp writes the allowance list to, derived from\n * `appId` and `sessionId` via the same `sanitizeKey` rule the rest of the\n * package uses.\n */\nfunction allowanceFilePath(storageDir: string, appId: string, sessionId: string): string {\n return join(storageDir, `${sanitizeKey(appId, `AllowanceKeys_${sessionId}`)}.json`);\n}\n\n/**\n * Read and decrypt the allowance list for a session. Returns `[]` when the\n * file is absent (the steady-state \"never paired for this session\" case);\n * throws on decrypt or decode failure (a corrupted file is a real failure,\n * not a \"no allowance\" signal — silently treating it as empty would mask\n * a bug).\n */\nexport async function readStoredAllowances(\n storageDir: string,\n appId: string,\n sessionId: string,\n): Promise<StoredAllowanceEntry[]> {\n let hex: string;\n try {\n hex = await readFile(allowanceFilePath(storageDir, appId, sessionId), \"utf-8\");\n } catch (err) {\n // ENOENT is the only error we treat as \"no entries\".\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return [];\n }\n throw err;\n }\n const decrypted = getAes(appId).decrypt(fromHex(hex));\n return StoredAllowancesCodec.dec(decrypted);\n}\n\n/**\n * Encode + encrypt + write an allowance list to disk. Used by the interop\n * test to pre-seed a cache entry without having to drive a wallet round-trip.\n * Not exported from the package — internal to `*.interop.test.ts`.\n */\nexport async function writeStoredAllowances(\n storageDir: string,\n appId: string,\n sessionId: string,\n entries: StoredAllowanceEntry[],\n): Promise<void> {\n const encoded = StoredAllowancesCodec.enc(entries);\n const encrypted = toHex(getAes(appId).encrypt(encoded));\n await writeFile(allowanceFilePath(storageDir, appId, sessionId), encrypted, \"utf-8\");\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Helpers for working with persisted sessions on a `TerminalAdapter`.\n */\nimport type { UserSession } from \"@novasamatech/host-papp\";\n\nimport type { TerminalAdapter } from \"./adapter.js\";\n\n/**\n * Wait for the adapter to load at least one persisted session, or resolve\n * with an empty array after `timeoutMs`.\n *\n * The session manager loads sessions from storage asynchronously, so a\n * synchronous `adapter.sessions.sessions.read()` immediately after\n * `createTerminalAdapter()` may return `[]` even when sessions exist on\n * disk. Use this helper to give the loader a chance to populate before\n * deciding whether the user is logged in.\n */\nexport function waitForSessions(\n adapter: TerminalAdapter,\n timeoutMs = 3000,\n): Promise<UserSession[]> {\n return new Promise((resolve) => {\n let resolved = false;\n let unsub: (() => void) | null = null;\n\n const finish = (sessions: UserSession[]) => {\n if (resolved) return;\n resolved = true;\n // Defer unsub so the synchronous initial emission can complete\n // before we tear down the subscription.\n queueMicrotask(() => unsub?.());\n resolve(sessions);\n };\n\n const timer = setTimeout(() => finish([]), timeoutMs);\n\n unsub = adapter.sessions.sessions.subscribe((sessions: UserSession[]) => {\n if (sessions.length > 0) {\n clearTimeout(timer);\n finish(sessions);\n }\n });\n });\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n type Subscriber = (sessions: UserSession[]) => void;\n\n function fakeAdapter(): {\n adapter: TerminalAdapter;\n emit: (sessions: UserSession[]) => void;\n subscriberCount: () => number;\n } {\n const subscribers = new Set<Subscriber>();\n const adapter = {\n sessions: {\n sessions: {\n subscribe(cb: Subscriber) {\n subscribers.add(cb);\n return () => {\n subscribers.delete(cb);\n };\n },\n },\n },\n } as unknown as TerminalAdapter;\n return {\n adapter,\n emit: (sessions) => {\n for (const cb of subscribers) cb(sessions);\n },\n subscriberCount: () => subscribers.size,\n };\n }\n\n describe(\"waitForSessions\", () => {\n test(\"resolves with sessions on first non-empty emission\", async () => {\n const { adapter, emit } = fakeAdapter();\n const promise = waitForSessions(adapter, 1000);\n const session = { remoteAccount: { accountId: new Uint8Array(32) } } as UserSession;\n emit([session]);\n await expect(promise).resolves.toEqual([session]);\n });\n\n test(\"ignores empty emissions and waits for a non-empty one\", async () => {\n const { adapter, emit } = fakeAdapter();\n const promise = waitForSessions(adapter, 1000);\n emit([]);\n emit([]);\n const session = { remoteAccount: { accountId: new Uint8Array(32) } } as UserSession;\n emit([session]);\n await expect(promise).resolves.toEqual([session]);\n });\n\n test(\"resolves with [] after timeout when nothing is emitted\", async () => {\n vi.useFakeTimers();\n try {\n const { adapter } = fakeAdapter();\n const promise = waitForSessions(adapter, 50);\n vi.advanceTimersByTime(50);\n await expect(promise).resolves.toEqual([]);\n } finally {\n vi.useRealTimers();\n }\n });\n\n test(\"unsubscribes after resolving\", async () => {\n const { adapter, emit, subscriberCount } = fakeAdapter();\n const session = { remoteAccount: { accountId: new Uint8Array(32) } } as UserSession;\n const promise = waitForSessions(adapter, 1000);\n emit([session]);\n await promise;\n // Wait for queueMicrotask\n await Promise.resolve();\n expect(subscriberCount()).toBe(0);\n });\n\n test(\"uses default 3000ms timeout when none is passed\", async () => {\n vi.useFakeTimers();\n try {\n const { adapter } = fakeAdapter();\n // No timeoutMs argument — exercises the `?? 3000` default branch.\n const promise = waitForSessions(adapter);\n // Just short of 3000ms: still pending.\n vi.advanceTimersByTime(2999);\n let settled = false;\n promise.then(() => {\n settled = true;\n });\n await Promise.resolve();\n expect(settled).toBe(false);\n // Cross the default boundary.\n vi.advanceTimersByTime(1);\n await expect(promise).resolves.toEqual([]);\n } finally {\n vi.useRealTimers();\n }\n });\n\n test(\"handles synchronous initial emission\", async () => {\n // Some subscribables emit current value synchronously inside subscribe().\n const session = { remoteAccount: { accountId: new Uint8Array(32) } } as UserSession;\n const adapter = {\n sessions: {\n sessions: {\n subscribe(cb: Subscriber) {\n cb([session]);\n return () => {};\n },\n },\n },\n } as unknown as TerminalAdapter;\n await expect(waitForSessions(adapter, 1000)).resolves.toEqual([session]);\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Terminal QR code rendering using Unicode half-block characters.\n *\n * Wraps the `qrcode` npm package's UTF-8 renderer, which packs two\n * module rows per terminal line using half-block characters.\n */\n\n/** Options for QR code rendering. */\nexport interface QrRenderOptions {\n /** Error correction level. Default: \"M\". */\n errorCorrectionLevel?: \"L\" | \"M\" | \"Q\" | \"H\";\n /** Quiet zone size in modules. Default: 2. */\n margin?: number;\n}\n\n/**\n * Encode a string as a QR code rendered in Unicode half-block characters.\n *\n * Returns a multi-line string suitable for `console.log`.\n */\nexport async function renderQrCode(data: string, options?: QrRenderOptions): Promise<string> {\n const QRCode = await import(\"qrcode\");\n const result = await QRCode.toString(data, {\n type: \"utf8\",\n errorCorrectionLevel: options?.errorCorrectionLevel ?? \"M\",\n margin: options?.margin ?? 2,\n });\n return result;\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"renderQrCode produces non-empty output\", async () => {\n const result = await renderQrCode(\"https://example.com\");\n expect(result.length).toBeGreaterThan(0);\n expect(result).toContain(\"\\n\");\n });\n\n test(\"renderQrCode contains Unicode block characters\", async () => {\n const result = await renderQrCode(\"test\");\n expect(/[▀▄█]/.test(result)).toBe(true);\n });\n\n test(\"different inputs produce different QR codes\", async () => {\n const a = await renderQrCode(\"hello\");\n const b = await renderQrCode(\"world\");\n expect(a).not.toBe(b);\n });\n}\n"],"mappings":"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","names":["fakeLazyClient","teardown","makeSession","ext","fakeAdapter","req","AllowanceExpiredError","makeAdapter","AllowanceError","fakeAdapter"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts","../src/signer.ts","../src/index.ts","../src/allowance.ts","../src/allowance-cache.ts","../src/sessions.ts","../src/qr-encode.ts"],"sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Node.js adapter for the Polkadot host-papp SDK.\n *\n * Provides Node.js-compatible implementations of the SDK's storage and\n * transport layers, enabling QR login, attestation, and signing in\n * terminal/CLI environments.\n */\nimport {\n createPappAdapter,\n type PappAdapter,\n type HostMetadata,\n SS_STABLE_STAGE_ENDPOINTS,\n SS_PASEO_STABLE_STAGE_ENDPOINTS,\n} from \"@novasamatech/host-papp\";\nimport {\n createLazyClient,\n createPapiStatementStoreAdapter,\n type LazyClient,\n} from \"@novasamatech/statement-store\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\nimport { getWsProvider } from \"@polkadot-api/ws-provider\";\n\nimport { createNodeStorageAdapter } from \"./node-storage.js\";\n\nconst log = createLogger(\"terminal\");\n\n/** Options for creating a terminal adapter. */\nexport interface TerminalAdapterOptions {\n /** Unique app identifier. Used as the storage namespace. */\n appId: string;\n /** Statement store WebSocket endpoints. Defaults to Paseo stable endpoints. */\n endpoints?: string[];\n /** Optional host metadata for the Sign-In screen. */\n hostMetadata?: HostMetadata;\n /**\n * Directory where session files are persisted. Defaults to\n * `~/.polkadot-apps/`. Override in tests to point at a temporary\n * directory populated with `createTestSession` from\n * `@parity/product-sdk-terminal/testing`.\n */\n storageDir?: string;\n}\n\n/**\n * Create a terminal adapter backed by the host-papp SDK.\n *\n * This sets up:\n * - File-based storage in `~/.polkadot-apps/` (since Node.js has no localStorage)\n * - WebSocket connection to the statement store\n * - The full SSO flow: QR pairing + on-chain attestation\n * - Session manager for signing requests\n */\n/** A PappAdapter with the `appId` it was created with and a `destroy` method for cleanup. */\nexport type TerminalAdapter = PappAdapter & {\n /** The `appId` passed to {@link createTerminalAdapter}. Useful for {@link createSessionSigner}. */\n readonly appId: string;\n /**\n * The on-disk storage directory used for sessions and (when the\n * host-runner facet is in use) for the allowance-key cache the\n * `./host` subpath maintains. `undefined` when the default\n * is in use; set explicitly when the caller passed `storageDir` to\n * {@link createTerminalAdapter}.\n */\n readonly storageDir?: string;\n /**\n * Disconnect the WebSocket and release resources.\n *\n * @remarks\n * Idempotent. Returns a Promise that resolves once all in-flight\n * statement-subscription teardowns have settled and the underlying\n * substrate client has been disconnected. **Awaiting is recommended\n * but not required** — callers that don't await get the same\n * fire-and-forget shape the previous version had, but they may see\n * the destroy-time RPC traffic finish after the function returns.\n *\n * The implementation tracks the server-side `statement_unsubscribe…`\n * RPCs `sessions.dispose()` fires, then awaits them via\n * `Promise.allSettled` before destroying the substrate-client request\n * queue. Draining those RPCs is not enough on its own: destroying the\n * client while a statement-subscription observable is still live makes\n * `@novasamatech/statement-store` log `Statement subscription error:\n * Client destroyed` to `console.error` synchronously inside the\n * disconnect. So the disconnect runs under a scoped `console.error` filter\n * that drops only that benign line ({@link isBenignTeardownError}),\n * restored in a `finally` — every other `console.error` passes through.\n *\n * Limit: this covers the statement-subscription half only. An auth\n * subscription still open at `destroy()` (released by `sso.abort()`, not\n * `sessions.dispose()`) raises an unhandled `Error: Not connected` from the\n * same teardown, which this does not suppress. Tracked as a follow-up.\n */\n destroy(): Promise<void>;\n};\n\nexport function createTerminalAdapter(options: TerminalAdapterOptions): TerminalAdapter {\n const endpoints = options.endpoints ?? SS_PASEO_STABLE_STAGE_ENDPOINTS;\n\n const storage = createNodeStorageAdapter(options.appId, options.storageDir);\n // ws-provider 0.9 takes endpoints positionally; relies on the global\n // WebSocket (Node ≥21) unless `websocketClass` is supplied.\n //\n // heartbeatTimeout uses setTimeout under the hood, which clamps to a\n // 32-bit signed integer. Passing Infinity triggers a noisy\n // `TimeoutOverflowWarning` on every reschedule. Use the int32 max\n // (~24.8 days) — effectively-never for any CLI session.\n const HEARTBEAT_NEVER_MS = 2_147_483_647;\n const rawLazyClient = createLazyClient(\n getWsProvider(endpoints, { heartbeatTimeout: HEARTBEAT_NEVER_MS }),\n );\n const trackedLazyClient = wrapLazyClient(rawLazyClient);\n const statementStore = createPapiStatementStoreAdapter(trackedLazyClient);\n\n const adapter = createPappAdapter({\n appId: options.appId,\n hostMetadata: options.hostMetadata,\n adapters: {\n storage,\n lazyClient: trackedLazyClient,\n statementStore,\n },\n });\n\n let destroyPromise: Promise<void> | null = null;\n return {\n ...adapter,\n appId: options.appId,\n storageDir: options.storageDir,\n destroy(): Promise<void> {\n if (destroyPromise) return destroyPromise;\n destroyPromise = teardown(adapter.sessions, trackedLazyClient);\n return destroyPromise;\n },\n };\n}\n\n/**\n * Lazy-client wrapper that tracks server-side unsubscribe RPCs as Promises.\n *\n * `lazyClient.getSubscribeFn` returns a function whose teardown callback\n * fires `c._request(unsubscribeMethod, ...)` with `noop` `onSuccess` /\n * `onError` — the unsubscribe is fire-and-forget by upstream design. We\n * intercept those requests by replacing their callbacks with handlers\n * that resolve a tracking Promise on either outcome (success OR error —\n * we just need to know the request settled, not whether it succeeded).\n *\n * `awaitPendingUnsubs()` returns a Promise that settles when every\n * tracked unsubscribe has completed. Used by `teardown` to drain before\n * calling `disconnect`.\n *\n * The wrapper is otherwise transparent: `getClient`, `getRequestFn`,\n * `disconnect` pass through unchanged.\n */\ntype TrackedLazyClient = LazyClient & {\n awaitPendingUnsubs(): Promise<void>;\n};\n\nfunction wrapLazyClient(inner: LazyClient): TrackedLazyClient {\n const pendingUnsubs = new Set<Promise<void>>();\n const innerGetSubscribeFn = inner.getSubscribeFn.bind(inner);\n\n return {\n ...inner,\n getClient: inner.getClient.bind(inner),\n getRequestFn: inner.getRequestFn.bind(inner),\n disconnect: inner.disconnect.bind(inner),\n\n getSubscribeFn() {\n // Each call returns a new SubscribeFn. We wrap the teardown\n // callback so any unsubscribe RPC it fires gets tracked.\n const innerSubscribe = innerGetSubscribeFn();\n return ((method, params, onMessage, onError) => {\n const innerTeardown = innerSubscribe(method, params, onMessage, onError);\n return () => {\n // Track the unsubscribe with a Promise that resolves\n // after the microtask queue drains. The upstream code\n // uses `noop` callbacks on the actual RPC, so we\n // can't directly observe completion — but by the\n // time `innerTeardown()` returns, the `_request`\n // has been queued. Two microtask hops are enough\n // for the request to flush through the\n // substrate-client's send pipeline.\n //\n // If `innerTeardown()` throws synchronously, we let\n // the throw escape (the caller — `sessions.dispose()`\n // — is in the best position to decide what to do)\n // but the tracker still resolves so\n // `awaitPendingUnsubs()` doesn't hang and\n // `destroy()` still completes.\n const tracked = new Promise<void>((resolve) => {\n queueMicrotask(() => queueMicrotask(resolve));\n });\n pendingUnsubs.add(tracked);\n void tracked.finally(() => pendingUnsubs.delete(tracked));\n innerTeardown();\n };\n }) as ReturnType<LazyClient[\"getSubscribeFn\"]>;\n },\n\n async awaitPendingUnsubs(): Promise<void> {\n // Snapshot so additions made during the await don't extend\n // the wait indefinitely (sessions.dispose() should have fired\n // them all synchronously by the time we're called).\n const snapshot = Array.from(pendingUnsubs);\n await Promise.allSettled(snapshot);\n },\n };\n}\n\n/**\n * Whether `error` is the benign teardown noise `@novasamatech/statement-store`\n * emits when a statement subscription's observable errors because the client\n * was destroyed underneath it — the raw-client's `DestroyedError: Client\n * destroyed`.\n *\n * Matches on the message text only. Matching the `DestroyedError` *name* would\n * also swallow `@parity/product-sdk-signer`'s own `DestroyedError` (a direct\n * dependency, message \"SignerManager has been destroyed\"), so a real signer\n * failure during teardown would vanish — and this predicate is exported, so it\n * would carry that to consumers. `Client destroyed` is unique to the upstream\n * line.\n *\n * Exported so a consumer's own `console.error` guard can drop this line without\n * reinventing the match. {@link createTerminalAdapter}'s own `destroy()` already\n * suppresses it (see {@link suppressBenignTeardownErrors}); this is for code\n * paths outside that window.\n */\nexport function isBenignTeardownError(error: unknown): boolean {\n // Read the message without stringifying the whole value: `String()` on a\n // null-prototype object throws, and `console.error` is usually called from a\n // `catch`, so the wrapper must never throw where the real one would print.\n const message = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n return /Client destroyed/.test(message);\n}\n\n/**\n * Temporarily drop {@link isBenignTeardownError} lines from `console.error`,\n * returning a restore function.\n *\n * The noise comes from an unconditional `console.error('Statement subscription\n * error:', …)` inside `@novasamatech/statement-store`'s rpc adapter, emitted\n * synchronously while `disconnect()` destroys the client with a subscription\n * observable still live. Draining the tracked unsubscribe RPCs does not cover\n * that observable's error emission, so the ordering-only approach still lets\n * one line through — hence a scoped suppression.\n *\n * Deliberately narrow: the upstream call passes a plain string first and the\n * error second, so the wrapper checks *every* argument ({@link isBenignTeardownError}\n * on any of them) and drops the call only when the benign error is present.\n * Every other `console.error` passes through untouched, so a genuine error\n * during teardown is still visible.\n */\nfunction suppressBenignTeardownErrors(): () => void {\n const original = console.error;\n const wrapper = (...args: unknown[]) => {\n if (args.some(isBenignTeardownError)) return;\n original.apply(console, args as Parameters<typeof console.error>);\n };\n console.error = wrapper;\n // Restore only if we're still the active wrapper — if another patcher\n // layered on top after us, resetting to `original` would clobber theirs.\n return () => {\n if (console.error === wrapper) console.error = original;\n };\n}\n\n/**\n * Drain pending unsubscribes, then disconnect. Order is deterministic:\n *\n * 1. `sessions.dispose()` walks each open statement-subscription and\n * triggers their RPC unsubscribe via the wrapped subscribe-fn's\n * teardown callback. Each unsubscribe is recorded as a tracked\n * Promise on the wrapper.\n * 2. `awaitPendingUnsubs()` waits for those tracked Promises to\n * settle (resolution OR rejection — we just need confirmation the\n * RPC has left the substrate-client send pipeline).\n * 3. `disconnect()` calls `substrateClient.destroy()`.\n *\n * Draining the unsubscribe RPCs does not stop `@novasamatech/statement-store`\n * from logging `Statement subscription error: DestroyedError` when the client\n * is destroyed while a subscription observable is still live. That log is\n * synchronous inside `disconnect()`, so the call runs under a scoped\n * {@link suppressBenignTeardownErrors} restored in a `finally` — no timer, and\n * nothing to leak if two `destroy()`s overlap. Only the benign line is dropped.\n *\n * If the disconnect call itself throws, log and continue rather than\n * propagating — caller can `await destroy()` without `try/catch`.\n *\n * Known limit: this closes the statement-subscription half. An auth\n * subscription still established at `destroy()` (released only by\n * `sso.abort()`, not `sessions.dispose()`) raises an unhandled\n * `Error: Not connected` from the same teardown, which this does not catch.\n * Tracked as a follow-up.\n */\nasync function teardown(\n sessions: { dispose(): void },\n lazyClient: TrackedLazyClient,\n): Promise<void> {\n log.debug(\"destroying terminal adapter\");\n sessions.dispose();\n await lazyClient.awaitPendingUnsubs();\n const restore = suppressBenignTeardownErrors();\n try {\n lazyClient.disconnect();\n } catch (e) {\n log.warn(\"lazyClient.disconnect threw during destroy\", { error: e });\n } finally {\n // The benign log is emitted synchronously inside `disconnect()`, so by\n // here it has already been swallowed — restore immediately. Restoring in\n // `finally` (not a `setTimeout`) is what keeps overlapping destroys from\n // stranding the patch: each call reverts before the next inspects it.\n restore();\n }\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n /**\n * Build a fake LazyClient whose `getSubscribeFn` records each subscribe\n * call and whose returned teardown is observable. Lets tests assert on\n * teardown invocation and on `disconnect` ordering.\n */\n function fakeLazyClient(): {\n client: LazyClient;\n teardownCalls: number;\n disconnectCalls: number;\n } {\n let teardownCalls = 0;\n let disconnectCalls = 0;\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_method, _params, _onMessage, _onError) => {\n return () => {\n teardownCalls += 1;\n };\n }) as ReturnType<LazyClient[\"getSubscribeFn\"]>,\n disconnect: () => {\n disconnectCalls += 1;\n },\n } as LazyClient;\n return {\n client: fake,\n get teardownCalls() {\n return teardownCalls;\n },\n get disconnectCalls() {\n return disconnectCalls;\n },\n };\n }\n\n describe(\"wrapLazyClient\", () => {\n test(\"passes getClient / getRequestFn / disconnect through unchanged\", () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n\n wrapped.getClient();\n wrapped.disconnect();\n expect(fake.disconnectCalls).toBe(1);\n expect(typeof wrapped.getRequestFn).toBe(\"function\");\n });\n\n test(\"getSubscribeFn returns a wrapped subscribe whose teardown invokes the inner teardown\", () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n\n const subscribe = wrapped.getSubscribeFn();\n const teardown = subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n );\n teardown();\n\n expect(fake.teardownCalls).toBe(1);\n });\n\n test(\"awaitPendingUnsubs resolves after all wrapped teardowns settle\", async () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n const subscribe = wrapped.getSubscribeFn();\n\n // Simulate three live subscriptions being torn down in the\n // same tick (what `sessions.dispose()` does internally).\n for (let i = 0; i < 3; i++) {\n const teardown = subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n );\n teardown();\n }\n\n // All three pending — but resolves once microtasks flush.\n await wrapped.awaitPendingUnsubs();\n expect(fake.teardownCalls).toBe(3);\n });\n\n test(\"awaitPendingUnsubs with no pending unsubs resolves immediately\", async () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n await expect(wrapped.awaitPendingUnsubs()).resolves.toBeUndefined();\n });\n\n test(\"an unsubscribe whose teardown throws is still tracked and resolved\", async () => {\n // Even if the inner teardown throws, the tracker still needs\n // to settle — otherwise destroy() would hang forever.\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_method, _params, _onMessage, _onError) => {\n return () => {\n throw new Error(\"teardown boom\");\n };\n }) as ReturnType<LazyClient[\"getSubscribeFn\"]>,\n disconnect: () => {},\n } as LazyClient;\n\n const wrapped = wrapLazyClient(fake);\n const subscribe = wrapped.getSubscribeFn();\n const teardown = subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n );\n\n // The wrapper's inner try/finally lets the throw escape (as\n // expected — we don't swallow user-visible errors), but the\n // tracker still resolves. Catch-and-await pattern:\n try {\n teardown();\n } catch {\n // expected\n }\n await expect(wrapped.awaitPendingUnsubs()).resolves.toBeUndefined();\n });\n });\n\n describe(\"teardown\", () => {\n test(\"orders sessions.dispose, drain, then disconnect\", async () => {\n const order: string[] = [];\n\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n // Override disconnect to capture ordering.\n const innerDisconnect = wrapped.disconnect.bind(wrapped);\n wrapped.disconnect = () => {\n order.push(\"disconnect\");\n innerDisconnect();\n };\n\n const sessions = {\n dispose: vi.fn(() => {\n order.push(\"dispose\");\n // Simulate sessions.dispose firing one teardown.\n const subscribe = wrapped.getSubscribeFn();\n subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n )();\n }),\n };\n\n await teardown(sessions, wrapped);\n\n expect(order).toEqual([\"dispose\", \"disconnect\"]);\n expect(sessions.dispose).toHaveBeenCalledTimes(1);\n expect(fake.disconnectCalls).toBe(1);\n });\n\n test(\"disconnect runs even when there are no pending unsubs\", async () => {\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n const sessions = { dispose: vi.fn() };\n\n await teardown(sessions, wrapped);\n\n expect(fake.disconnectCalls).toBe(1);\n });\n\n test(\"logs a warning if disconnect throws, doesn't propagate to caller\", async () => {\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_method, _params, _onMessage, _onError) => () => {}) as ReturnType<\n LazyClient[\"getSubscribeFn\"]\n >,\n disconnect: () => {\n throw new Error(\"boom\");\n },\n } as LazyClient;\n\n const wrapped = wrapLazyClient(fake);\n const sessions = { dispose: vi.fn() };\n\n await expect(teardown(sessions, wrapped)).resolves.toBeUndefined();\n });\n\n test(\"awaits pending unsubs before calling disconnect\", async () => {\n // The whole point of the fix: disconnect must not run while\n // unsubscribe RPCs are still queued. Verify ordering even\n // when the unsubs take multiple microtasks to settle.\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n\n let unsubResolved = false;\n const innerDisconnect = wrapped.disconnect.bind(wrapped);\n wrapped.disconnect = () => {\n // Disconnect must not run before the unsubscribe has\n // resolved. If it does, this assertion fires.\n expect(unsubResolved).toBe(true);\n innerDisconnect();\n };\n\n const sessions = {\n dispose: () => {\n const subscribe = wrapped.getSubscribeFn();\n subscribe(\n \"statement_subscribeStatement\",\n [],\n () => {},\n () => {},\n )();\n // mark resolution after the microtasks the wrapper queues\n queueMicrotask(() =>\n queueMicrotask(() => {\n unsubResolved = true;\n }),\n );\n },\n };\n\n await teardown(sessions, wrapped);\n expect(fake.disconnectCalls).toBe(1);\n });\n\n test(\"suppresses the benign DestroyedError console.error fired during disconnect\", async () => {\n // The upstream statement-store adapter logs `Statement subscription\n // error: DestroyedError` from a finalizer when disconnect destroys\n // the client under a live subscription. Simulate that by logging it\n // from inside disconnect; teardown must swallow it.\n const spy = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_m, _p, _om, _oe) => () => {}) as ReturnType<LazyClient[\"getSubscribeFn\"]>,\n disconnect: () => {\n console.error(\n \"Statement subscription error:\",\n new Error(\"DestroyedError: Client destroyed\"),\n );\n },\n } as LazyClient;\n const wrapped = wrapLazyClient(fake);\n\n await teardown({ dispose: () => {} }, wrapped);\n\n // The benign line never reached the underlying console.error.\n expect(spy).not.toHaveBeenCalledWith(\n \"Statement subscription error:\",\n expect.anything(),\n );\n spy.mockRestore();\n });\n\n test(\"still surfaces a real (non-benign) console.error during disconnect\", async () => {\n const spy = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const realError = new Error(\"something actually went wrong\");\n const fake = {\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_m, _p, _om, _oe) => () => {}) as ReturnType<LazyClient[\"getSubscribeFn\"]>,\n disconnect: () => {\n console.error(\"real failure:\", realError);\n },\n } as LazyClient;\n const wrapped = wrapLazyClient(fake);\n\n await teardown({ dispose: () => {} }, wrapped);\n\n expect(spy).toHaveBeenCalledWith(\"real failure:\", realError);\n spy.mockRestore();\n });\n\n test(\"restores console.error by the time teardown resolves\", async () => {\n const before = console.error;\n const fake = fakeLazyClient();\n const wrapped = wrapLazyClient(fake.client);\n\n await teardown({ dispose: () => {} }, wrapped);\n\n // Restore is synchronous in a `finally`, no timer to wait on.\n expect(console.error).toBe(before);\n });\n\n test(\"two overlapping destroys do not strand the console.error patch\", async () => {\n // Each teardown restores in `finally`, so even interleaved calls\n // leave the real console.error reachable — no wrapper left installed.\n const before = console.error;\n const makeFake = () =>\n wrapLazyClient({\n getClient: (() =>\n ({}) as ReturnType<LazyClient[\"getClient\"]>) as LazyClient[\"getClient\"],\n getRequestFn: (() => () => Promise.resolve()) as LazyClient[\"getRequestFn\"],\n getSubscribeFn: () =>\n ((_m, _p, _om, _oe) => () => {}) as ReturnType<\n LazyClient[\"getSubscribeFn\"]\n >,\n disconnect: () => {\n console.error(\n \"Statement subscription error:\",\n new Error(\"DestroyedError: Client destroyed\"),\n );\n },\n } as LazyClient);\n\n await Promise.all([\n teardown({ dispose: () => {} }, makeFake()),\n teardown({ dispose: () => {} }, makeFake()),\n ]);\n\n expect(console.error).toBe(before);\n });\n });\n\n describe(\"isBenignTeardownError\", () => {\n test(\"matches the statement-store teardown noise\", () => {\n expect(isBenignTeardownError(new Error(\"DestroyedError: Client destroyed\"))).toBe(true);\n expect(isBenignTeardownError(\"Client destroyed\")).toBe(true);\n const e = new Error(\"Client destroyed\");\n e.name = \"DestroyedError\";\n expect(isBenignTeardownError(e)).toBe(true);\n });\n\n test(\"does not match a genuine error\", () => {\n expect(isBenignTeardownError(new Error(\"connection refused\"))).toBe(false);\n expect(isBenignTeardownError(\"BadProof\")).toBe(false);\n expect(isBenignTeardownError(undefined)).toBe(false);\n });\n\n test(\"does not swallow the signer's own DestroyedError\", () => {\n // `@parity/product-sdk-signer`'s DestroyedError shares the name but\n // not the message, so matching on the name (not the message) would\n // silently drop a real signer failure during teardown.\n const signerError = new Error(\"SignerManager has been destroyed\");\n signerError.name = \"DestroyedError\";\n expect(isBenignTeardownError(signerError)).toBe(false);\n });\n\n test(\"does not throw on a value String() would reject\", () => {\n // `console.error` is usually called from a `catch`, and a\n // null-prototype object prints fine there but throws under `String()`.\n expect(() => isBenignTeardownError(Object.create(null))).not.toThrow();\n expect(isBenignTeardownError(Object.create(null))).toBe(false);\n });\n });\n}\n\nexport { SS_STABLE_STAGE_ENDPOINTS, SS_PASEO_STABLE_STAGE_ENDPOINTS };\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Create a PolkadotSigner from a QR-paired session.\n *\n * Routes **transaction signing** through `session.createTransaction` — the\n * host-papp `CreateTransactionRequest`/`CreateTransactionResponse` SSO\n * message pair. We hand the mobile app the structured\n * `ProductAccountTransaction` (`signer`, `genesisHash`, `callData`, the\n * per-extension `{ extra, additionalSigned }`, and `txExtVersion`) and it\n * **builds and signs** the full extrinsic, returning the signed bytes.\n *\n * Two properties fall out of this:\n * - The wallet can **decode and display** the transaction instead of\n * blind-signing an opaque hash (which is what the older\n * `session.signRaw({ tag: \"Payload\" })` route did).\n * - Every signed extension the chain declares — including ones PAPI's PJS\n * adapter doesn't know (e.g. `AsPgas` on Paseo Next v2) — is forwarded\n * verbatim as `{ id, extra, additionalSigned }`, so nothing is lost in\n * translation.\n *\n * **Raw-message signing** (`signBytes`) keeps the `session.signRaw` `Bytes`\n * tag so the wallet applies the `<Bytes>...</Bytes>` anti-phishing wrap once\n * on its side. Correct for arbitrary user data; wrong for extrinsic payloads\n * (which is why the two paths split).\n *\n * @example\n * ```ts\n * const [session] = adapter.sessions.sessions.read();\n *\n * // Default account — uses [adapter.appId, 0]:\n * const signer = createSessionSigner(session, adapter);\n *\n * // Non-default derivation index, or a different productId:\n * const subSigner = createSessionSignerForAccount(session, {\n * productId: \"my-product\",\n * derivationIndex: 3,\n * });\n *\n * await contract.publish.tx(domain, cid, { signer, origin });\n * ```\n */\nimport type { UserSession } from \"@novasamatech/host-papp\";\nimport { NoAllowanceError } from \"@novasamatech/statement-store\";\nimport { decAnyMetadata, unifyMetadata } from \"@polkadot-api/substrate-bindings\";\nimport { deriveProductAccountPublicKey } from \"@parity/product-sdk-keys\";\nimport { AllowanceExpiredError } from \"@parity/product-sdk-signer/errors\";\nimport type { PolkadotSigner } from \"polkadot-api\";\n\nimport type { TerminalAdapter } from \"./adapter.js\";\n\n/**\n * Detect the statement-store `NoAllowanceError` — the chain-side rejection a\n * sign request hits when the session's statement-store allowance has lapsed.\n *\n * Walks the `cause` chain: host-papp may wrap the underlying submit failure\n * before it reaches the `createTransaction`/`signRaw` error channel. Besides\n * `instanceof`, matches by constructor name (a duplicated copy of\n * `@novasamatech/statement-store` in the module graph defeats `instanceof`)\n * and by the fixed message statement-store mints — the only signal left once\n * the error crossed a serialization boundary (`NoAllowanceError` never sets\n * `this.name`, so name-matching would never fire).\n */\nfunction isNoAllowanceError(error: unknown): boolean {\n // Bounded walk — cause chains are short; the cap guards against cycles.\n for (let current = error, depth = 0; current != null && depth < 8; depth++) {\n if (current instanceof NoAllowanceError) return true;\n if (current instanceof Error) {\n if (\n current.constructor.name === \"NoAllowanceError\" ||\n current.message.includes(\"no allowance set\")\n ) {\n return true;\n }\n current = current.cause;\n } else {\n break;\n }\n }\n return false;\n}\n\n/**\n * Map a sign failure to the typed {@link AllowanceExpiredError} when its cause\n * is the statement-store `NoAllowanceError`; `null` otherwise. Single decision\n * point for both sign paths — `signTx` (via `session.createTransaction`) and\n * `signBytes` (via `session.signRaw`) both submit through the statement store,\n * so the lapsed resource is always `\"statementStore\"`.\n *\n * The caller *throws* the returned error (PAPI's `PolkadotSigner` contract is\n * a rejecting Promise), making allowance expiry a typed, catchable condition\n * rather than a generic `Error`.\n */\nfunction toAllowanceExpiredError(error: unknown): AllowanceExpiredError | null {\n return isNoAllowanceError(error) ? new AllowanceExpiredError(\"statementStore\", error) : null;\n}\n\n/**\n * Identifies which sub-account of a paired session should sign.\n *\n * Mirrors the `host-papp` wire format `productAccountId: [productId, derivationIndex]`:\n * `productId` is the dotNS-style identifier for the requesting product (matches\n * the adapter's `appId` in normal usage); `derivationIndex` is the BIP32-style\n * child-key index, where `0` is the session's default account.\n */\nexport interface ProductAccountRef {\n /** The product identifier. Usually equal to the adapter's `appId`. */\n productId: string;\n /** Child-key derivation index. `0` is the default account. */\n derivationIndex: number;\n /**\n * The **product account's** sr25519 public key (32 bytes), as derived by the\n * host for `[productId, derivationIndex]`.\n *\n * PAPI stamps this into the extrinsic's signer address and verifies the\n * signature against it, so it must be the *product* account's key — not the\n * wallet's selected/root account (`session.remoteAccount.accountId`). A\n * mismatch produces an invalid signature.\n *\n * When omitted, the signer soft-derives it from the session's root account\n * (`mnemonic + \"/product/{productId}/{derivationIndex}\"`), which is correct\n * for every product account. Supply it explicitly only to avoid the\n * re-derivation or when you've already derived it elsewhere.\n */\n publicKey?: Uint8Array;\n}\n\n/**\n * The `signedExtensions` map PAPI hands to `PolkadotSigner.signTx`: keyed by\n * extension identifier, each entry carries the SCALE-encoded `extra` (goes in\n * the extrinsic body, as `value`) and `additionalSigned` (implicit data).\n */\ntype PapiSignedExtensions = Parameters<PolkadotSigner[\"signTx\"]>[1];\n\n/**\n * Build the host-papp `CreateTransactionRequest` from PAPI's `signTx` inputs.\n *\n * Maps every signed extension verbatim into the wire `{ id, extra,\n * additionalSigned }` triplets — no allow-list, no reshaping — so chain-specific\n * extensions (e.g. `AsPgas` on Paseo Next v2) survive. The mobile app uses\n * these plus `txExtVersion` to assemble and sign the extrinsic.\n *\n * Pure and synchronous so the wire-shape contract can be unit-tested without a\n * paired phone; the metadata decode + SSO round-trip live in {@link makeTxSignTx}.\n */\nfunction buildCreateTransactionRequest(\n productAccountId: [string, number],\n callData: Uint8Array,\n signedExtensions: PapiSignedExtensions,\n txExtVersion: number,\n): Parameters<UserSession[\"createTransaction\"]>[0] {\n const checkGenesis = signedExtensions.CheckGenesis;\n if (!checkGenesis) {\n throw new Error(\n \"Cannot build transaction: chain did not provide the CheckGenesis signed extension\",\n );\n }\n\n return {\n payload: {\n tag: \"v1\" as const,\n value: {\n signer: productAccountId,\n // CheckGenesis carries the genesis hash as its additionalSigned (implicit) data.\n genesisHash: checkGenesis.additionalSigned,\n callData,\n extensions: Object.values(signedExtensions).map(\n ({ identifier, value, additionalSigned }) => ({\n id: identifier,\n extra: value,\n additionalSigned,\n }),\n ),\n txExtVersion,\n },\n },\n };\n}\n\nconst V5_FORMAT_SELECTOR = 5;\n\n/**\n * Pick the `txExtVersion` for the paired host's `createTransaction` from the extrinsic\n * formats the runtime offers. The host treats the field as a format switch: `0` builds\n * V4, `5` builds a V5 general transaction, anything else is `NotSupported`. Prefer V4\n * while offered, since it carries the account signature in its envelope. The host\n * derives the transaction-extension version from the metadata itself.\n */\nfunction selectTxExtVersion(formatVersions: readonly number[]): number {\n if (formatVersions.length === 0) {\n throw new Error(\"No extrinsic version found in metadata\");\n }\n if (formatVersions.includes(4)) {\n return 0;\n }\n if (formatVersions.includes(5)) {\n return V5_FORMAT_SELECTOR;\n }\n throw new Error(\n `Runtime offers no extrinsic format 4 or 5 (offers: ${formatVersions.join(\", \")}); the host protocol has no txExtVersion for it.`,\n );\n}\n\nfunction txExtVersionFromMetadata(metadata: Uint8Array): number {\n return selectTxExtVersion(unifyMetadata(decAnyMetadata(metadata)).extrinsic.version);\n}\n\n/**\n * Send the request to the paired wallet's `createTransaction` and unwrap it.\n *\n * Extracted and named so the SSO round-trip + error handling can be\n * unit-tested without a real phone or chain metadata; the metadata decode and\n * payload assembly live in {@link makeTxSignTx} / {@link buildCreateTransactionRequest}.\n */\nasync function requestSignedTransaction(\n session: UserSession,\n request: Parameters<UserSession[\"createTransaction\"]>[0],\n): Promise<Uint8Array> {\n const result = await session.createTransaction(request);\n if (result.isErr()) {\n const expired = toAllowanceExpiredError(result.error);\n if (expired) throw expired;\n throw new Error(`Mobile transaction signing rejected: ${result.error.message}`);\n }\n // host-papp returns the fully signed extrinsic bytes.\n return result.value;\n}\n\n/**\n * The `signTx` function PAPI calls for transaction signing.\n *\n * Forwards the structured payload to the paired mobile wallet via\n * `session.createTransaction`; the wallet builds + signs the extrinsic and\n * returns the signed bytes. Unlike the old `signRaw({ tag: \"Payload\" })`\n * route, the wallet sees a real transaction (not opaque bytes) and unknown\n * signed extensions are preserved by {@link buildCreateTransactionRequest}.\n */\nfunction makeTxSignTx(\n session: UserSession,\n productAccountId: [string, number],\n): PolkadotSigner[\"signTx\"] {\n return async (callData, signedExtensions, metadata) =>\n requestSignedTransaction(\n session,\n buildCreateTransactionRequest(\n productAccountId,\n callData,\n signedExtensions,\n txExtVersionFromMetadata(metadata),\n ),\n );\n}\n\n/**\n * Sign function for arbitrary-byte signing (`PolkadotSigner.signBytes`).\n *\n * Routes to `session.signRaw` under the `Bytes` tag — the wallet's\n * raw-bytes interactor, which applies the standard `<Bytes>...</Bytes>`\n * anti-phishing envelope before signing.\n *\n * Provided as a separate function so `buildSessionSigner` can override\n * the `signBytes` produced by `getPolkadotSigner` (which would otherwise\n * funnel raw-bytes signing through the same `sign` callback as tx signing\n * — the wrong wire tag for arbitrary user data).\n */\nfunction makeRawBytesSignCallback(session: UserSession, productAccountId: [string, number]) {\n return async (data: Uint8Array): Promise<Uint8Array> => {\n const result = await session.signRaw({\n productAccountId,\n data: { tag: \"Bytes\", value: data },\n });\n\n if (result.isErr()) {\n const expired = toAllowanceExpiredError(result.error);\n if (expired) throw expired;\n throw new Error(`Mobile signing rejected: ${result.error.message}`);\n }\n\n return result.value.signature;\n };\n}\n\nexport const INCOMPLETE_SESSION_MESSAGE =\n 'Stored login session is missing the root account public key. Run \"logout\" and then \"login\" to pair again.';\n\n/**\n * The session's handshake-time root account public key (`rootUserAccountId` =\n * the user's bare-mnemonic keypair on current mobile builds). This is the\n * parent key product accounts soft-derive from. host-papp's live `UserSession`\n * doesn't surface it on the public type, so we read it structurally.\n *\n * @throws {@link INCOMPLETE_SESSION_MESSAGE} if the session predates the\n * `rootAccountId` field (a stale login that must re-pair).\n */\nexport function sessionRootPublicKey(session: UserSession): Uint8Array {\n const rootAccountId = (session as { rootAccountId?: Uint8Array }).rootAccountId;\n const publicKey = rootAccountId ? new Uint8Array(rootAccountId) : new Uint8Array();\n if (publicKey.length !== 32) {\n throw new Error(INCOMPLETE_SESSION_MESSAGE);\n }\n return publicKey;\n}\n\n/**\n * Soft-derive a product account's sr25519 public key from a paired session's\n * root account.\n *\n * This is the single source of truth for product-account math. The session\n * signer uses it to stamp the correct signer address; consumers that need the\n * product address *without* building a signer (e.g. a login-status display\n * triple) call it directly so the displayed address can't desync from what the\n * signer actually signs with.\n *\n * sr25519 soft derivation is composable on public keys alone, so deriving from\n * `session.rootAccountId` locally produces the SAME key the host derives\n * privately via `mnemonic + \"/product/{productId}/{derivationIndex}\"`.\n *\n * @throws {@link INCOMPLETE_SESSION_MESSAGE} on a stale session with no root key.\n */\nexport function deriveProductPublicKey(session: UserSession, ref: ProductAccountRef): Uint8Array {\n return deriveProductAccountPublicKey(\n sessionRootPublicKey(session),\n ref.productId,\n ref.derivationIndex,\n );\n}\n\nfunction buildSessionSigner(session: UserSession, ref: ProductAccountRef): PolkadotSigner {\n const productAccountId: [string, number] = [ref.productId, ref.derivationIndex];\n\n // The signer's public key must be the *product* account's key — the one the\n // wallet signs with for [productId, derivationIndex] — not the wallet's\n // selected root account. PAPI stamps this into the extrinsic's address and\n // verifies against it, so a mismatch yields an invalid signature.\n //\n // When the caller supplies `ref.publicKey` we trust it; otherwise we\n // soft-derive it from the session root. We deliberately do NOT fall back to\n // `session.remoteAccount.accountId` (the selected account) — that silently\n // produces an invalid signature for any product account that isn't the\n // currently-selected one.\n const publicKey = ref.publicKey ?? deriveProductPublicKey(session, ref);\n\n return {\n publicKey,\n // Transaction signing → host-papp `createTransaction`: the wallet builds\n // and signs the extrinsic, so it can show a decoded tx (no `<Bytes>`\n // envelope) and unknown signed extensions (e.g. `AsPgas`) survive.\n signTx: makeTxSignTx(session, productAccountId),\n // Arbitrary-byte signing keeps the `Bytes` tag so the wallet applies the\n // `<Bytes>...</Bytes>` anti-phishing envelope exactly once on its side\n // (correct for non-extrinsic user data).\n signBytes: makeRawBytesSignCallback(session, productAccountId),\n };\n}\n\n/**\n * Create a `PolkadotSigner` backed by a QR-paired mobile wallet session,\n * using the session's **default account** (`derivationIndex: 0`).\n *\n * For non-default sub-accounts, use {@link createSessionSignerForAccount}.\n *\n * @param session The paired user session.\n * @param adapter The {@link TerminalAdapter} that loaded the session. Its `appId`\n * is used as the `productId` in the wire request.\n * @param publicKey The product account's sr25519 public key for\n * `[adapter.appId, 0]`. Optional — when omitted it's soft-derived from the\n * session root. See {@link ProductAccountRef.publicKey}.\n */\nexport function createSessionSigner(\n session: UserSession,\n adapter: TerminalAdapter,\n publicKey?: Uint8Array,\n): PolkadotSigner {\n return buildSessionSigner(session, { productId: adapter.appId, derivationIndex: 0, publicKey });\n}\n\n/**\n * Create a `PolkadotSigner` for a specific sub-account of a paired session.\n *\n * Use this when you need a derivation index other than `0`, or a `productId`\n * different from the adapter's `appId`. For the common default-account case,\n * prefer {@link createSessionSigner}.\n *\n * @param session The paired user session.\n * @param ref The product account to sign as: `{ productId, derivationIndex }`.\n */\nexport function createSessionSignerForAccount(\n session: UserSession,\n ref: ProductAccountRef,\n): PolkadotSigner {\n return buildSessionSigner(session, ref);\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n const { ok, err } = await import(\"neverthrow\");\n const { seedToAccount } = await import(\"@parity/product-sdk-keys\");\n\n // A real sr25519 root public key — the derivation runs actual ristretto255\n // point math, so a fixture root must be a valid curve point, not arbitrary\n // bytes. Derived once from a fixed dev seed.\n const DEV_ROOT_PUBLIC_KEY = seedToAccount(\n \"bottom drive obey lake curtain smoke basket hold race lonely fit walk\",\n \"\",\n ).publicKey;\n\n /**\n * Build a minimal `UserSession`-shaped stub. `signPayload`, `signRaw`, and\n * `createTransaction` accept request-capturing functions so tests can assert\n * on exactly which host-papp method got called and with what payload.\n */\n function makeSession(opts: {\n signPayload?: (req: unknown) => Promise<unknown>;\n signRaw?: (req: unknown) => Promise<unknown>;\n createTransaction?: (req: unknown) => Promise<unknown>;\n accountIdBytes?: number[];\n /** Root account the product key is soft-derived from when publicKey is\n * omitted. Defaults to a valid 32-byte key; pass `null` to simulate a\n * stale session that predates the `rootAccountId` field. */\n rootAccountId?: number[] | null;\n }): UserSession {\n const accountIdBytes = opts.accountIdBytes ?? new Array(32).fill(0).map((_, i) => i);\n const rootAccountId =\n opts.rootAccountId === undefined ? Array.from(DEV_ROOT_PUBLIC_KEY) : opts.rootAccountId;\n return {\n remoteAccount: { accountId: accountIdBytes },\n ...(rootAccountId === null ? {} : { rootAccountId: new Uint8Array(rootAccountId) }),\n signPayload: vi.fn(\n opts.signPayload ??\n (async () => {\n throw new Error(\"signPayload not stubbed in this test\");\n }),\n ),\n signRaw: vi.fn(\n opts.signRaw ??\n (async () => {\n throw new Error(\"signRaw not stubbed in this test\");\n }),\n ),\n createTransaction: vi.fn(\n opts.createTransaction ??\n (async () => {\n throw new Error(\"createTransaction not stubbed in this test\");\n }),\n ),\n } as unknown as UserSession;\n }\n\n /**\n * A minimal PAPI `signedExtensions` entry. `value` is the SCALE-encoded\n * \"extra\" (extrinsic body); `additionalSigned` is the implicit data.\n */\n function ext(identifier: string, value: number[], additionalSigned: number[]) {\n return {\n identifier,\n value: new Uint8Array(value),\n additionalSigned: new Uint8Array(additionalSigned),\n };\n }\n\n function fakeAdapter(appId: string): TerminalAdapter {\n // Only the `appId` field matters for these tests.\n return { appId } as unknown as TerminalAdapter;\n }\n\n describe(\"createSessionSigner\", () => {\n test(\"self-derives the product key from the session root when none is passed\", () => {\n // No explicit publicKey → derive from rootAccountId, NOT the selected\n // account. The derived key must equal deriveProductAccountPublicKey\n // and must differ from remoteAccount.accountId (the old buggy fallback).\n const walletBytes = Array.from({ length: 32 }, (_, i) => i);\n const signer = createSessionSigner(\n makeSession({\n accountIdBytes: walletBytes,\n rootAccountId: Array.from(DEV_ROOT_PUBLIC_KEY),\n }),\n fakeAdapter(\"test-app\"),\n );\n const expected = deriveProductAccountPublicKey(DEV_ROOT_PUBLIC_KEY, \"test-app\", 0);\n expect(signer.publicKey).toEqual(expected);\n expect(signer.publicKey).not.toEqual(new Uint8Array(walletBytes));\n });\n\n test(\"throws on a stale session with no root account key\", () => {\n expect(() =>\n createSessionSigner(makeSession({ rootAccountId: null }), fakeAdapter(\"test-app\")),\n ).toThrow(INCOMPLETE_SESSION_MESSAGE);\n });\n\n test(\"uses the host-supplied product-account public key when provided\", () => {\n // The product account's key differs from the wallet's selected\n // account (`remoteAccount.accountId`). PAPI must stamp the *product*\n // key into the extrinsic, so the explicit key must win.\n const walletBytes = Array.from({ length: 32 }, (_, i) => i);\n const productKey = new Uint8Array(32).fill(0xab);\n const signer = createSessionSigner(\n makeSession({ accountIdBytes: walletBytes }),\n fakeAdapter(\"test-app\"),\n productKey,\n );\n expect(signer.publicKey).toEqual(productKey);\n expect(signer.publicKey).not.toEqual(new Uint8Array(walletBytes));\n });\n\n test(\"signBytes routes through session.signRaw with the Bytes tag\", async () => {\n const sig = new Uint8Array([9, 8, 7, 6, 5, 4, 3, 2, 1]);\n const captured: unknown[] = [];\n const session = makeSession({\n signRaw: async (req) => {\n captured.push(req);\n return ok({ signature: sig });\n },\n });\n const signer = createSessionSigner(session, fakeAdapter(\"test-app\"));\n\n const out = await signer.signBytes(new Uint8Array([1, 2, 3]));\n expect(out).toEqual(sig);\n\n // The bytes route forwards data verbatim under the `Bytes` tag.\n // Mobile applies the <Bytes>...</Bytes> envelope on its side.\n expect(captured).toHaveLength(1);\n const req = captured[0] as {\n productAccountId: [string, number];\n data: { tag: string; value: Uint8Array };\n };\n expect(req.productAccountId).toEqual([\"test-app\", 0]);\n expect(req.data.tag).toBe(\"Bytes\");\n expect(req.data.value).toEqual(new Uint8Array([1, 2, 3]));\n });\n\n test(\"signBytes never calls session.signPayload (legacy-path regression guard)\", async () => {\n // The PJS-bridged path used to route tx signing through\n // `session.signPayload`. The new PAPI-native path retires\n // `signPayload` entirely for this signer. This test guards\n // against accidentally re-introducing the legacy call.\n const session = makeSession({\n signRaw: async () => ok({ signature: new Uint8Array([1]) }),\n });\n const signer = createSessionSigner(session, fakeAdapter(\"test-app\"));\n\n await signer.signBytes(new Uint8Array([1, 2, 3]));\n\n const sessionWithSpies = session as unknown as {\n signPayload: { mock: { calls: unknown[] } };\n signRaw: { mock: { calls: unknown[] } };\n };\n expect(sessionWithSpies.signPayload.mock.calls).toHaveLength(0);\n expect(sessionWithSpies.signRaw.mock.calls).toHaveLength(1);\n });\n\n test(\"signBytes throws with a clear error when the mobile rejects\", async () => {\n const session = makeSession({\n signRaw: async () => err({ message: \"user declined\" }),\n });\n const signer = createSessionSigner(session, fakeAdapter(\"test-app\"));\n\n await expect(signer.signBytes(new Uint8Array([1]))).rejects.toThrow(\n \"Mobile signing rejected: user declined\",\n );\n });\n });\n\n describe(\"buildCreateTransactionRequest — tx payload (the AsPgas fix)\", () => {\n // Asserts the wire shape handed to the wallet. The full PAPI signTx →\n // metadata decode → SSO round-trip is exercised by the manual smoke\n // test in `manual-tests/qr-pair-and-sign.mjs` since CI cannot drive\n // a real phone.\n\n test(\"prefers V4 (tx-ext version 0) on a dual V4/V5 runtime\", () => {\n expect(selectTxExtVersion([4, 5])).toBe(0);\n });\n\n test(\"uses the V5 selector when the runtime offers no V4\", () => {\n expect(selectTxExtVersion([5])).toBe(5);\n });\n\n test(\"maps a V4-only runtime to the wire sentinel\", () => {\n expect(selectTxExtVersion([4])).toBe(0);\n });\n\n test(\"prefers V5 over a format it does not know\", () => {\n // max(formats) would send 6, which no host accepts.\n expect(selectTxExtVersion([5, 6])).toBe(5);\n });\n\n test(\"rejects a runtime offering neither format 4 nor 5\", () => {\n expect(() => selectTxExtVersion([6])).toThrow(/no extrinsic format 4 or 5/i);\n });\n\n test(\"rejects metadata with no extrinsic version\", () => {\n expect(() => selectTxExtVersion([])).toThrow(\"No extrinsic version found in metadata\");\n });\n\n test(\"txExtVersionFromMetadata reads the format list out of every tracked chain's metadata\", async () => {\n const { readFileSync, readdirSync } = await import(\"node:fs\");\n const { join } = await import(\"node:path\");\n // Not new URL(): without treeshake the literal reaches dist, and bundlers resolve it.\n const dir = join(import.meta.dirname, \"..\", \"..\", \"descriptors\", \".papi\", \"metadata\");\n const blobs = readdirSync(dir).filter((name) => name.endsWith(\".scale\"));\n\n expect(blobs.length, \"raise when a chain is added\").toBeGreaterThanOrEqual(11);\n // Every deployed runtime still offers format 4, so V4 wins. Fails the day one drops it.\n for (const name of blobs) {\n const metadata = new Uint8Array(readFileSync(join(dir, name)));\n expect(txExtVersionFromMetadata(metadata), name).toBe(0);\n }\n });\n\n const checkGenesis = ext(\"CheckGenesis\", [], [0x11, 0x22, 0x33]);\n\n test(\"wraps the payload as v1 with signer, callData, and txExtVersion\", () => {\n const callData = new Uint8Array([0xca, 0x11]);\n const req = buildCreateTransactionRequest(\n [\"my-app\", 3],\n callData,\n { CheckGenesis: checkGenesis },\n 5,\n );\n expect(req.payload.tag).toBe(\"v1\");\n expect(req.payload.value.signer).toEqual([\"my-app\", 3]);\n expect(req.payload.value.callData).toEqual(callData);\n expect(req.payload.value.txExtVersion).toBe(5);\n });\n\n test(\"takes the genesis hash from CheckGenesis.additionalSigned\", () => {\n const req = buildCreateTransactionRequest(\n [\"my-app\", 0],\n new Uint8Array([0]),\n { CheckGenesis: checkGenesis },\n 0,\n );\n expect(req.payload.value.genesisHash).toEqual(new Uint8Array([0x11, 0x22, 0x33]));\n });\n\n test(\"maps every signed extension to { id, extra, additionalSigned }\", () => {\n const req = buildCreateTransactionRequest(\n [\"my-app\", 0],\n new Uint8Array([0]),\n { CheckGenesis: checkGenesis, CheckNonce: ext(\"CheckNonce\", [0x07], []) },\n 0,\n );\n expect(req.payload.value.extensions).toEqual([\n {\n id: \"CheckGenesis\",\n extra: new Uint8Array([]),\n additionalSigned: new Uint8Array([0x11, 0x22, 0x33]),\n },\n {\n id: \"CheckNonce\",\n extra: new Uint8Array([0x07]),\n additionalSigned: new Uint8Array([]),\n },\n ]);\n });\n\n test(\"preserves an unknown signed extension verbatim (e.g. AsPgas)\", () => {\n // The whole reason for moving off PJS: an extension PAPI's PJS\n // adapter doesn't know must pass through untouched.\n const req = buildCreateTransactionRequest(\n [\"my-app\", 0],\n new Uint8Array([0]),\n { CheckGenesis: checkGenesis, AsPgas: ext(\"AsPgas\", [0xde, 0xad], [0xbe, 0xef]) },\n 0,\n );\n expect(req.payload.value.extensions).toContainEqual({\n id: \"AsPgas\",\n extra: new Uint8Array([0xde, 0xad]),\n additionalSigned: new Uint8Array([0xbe, 0xef]),\n });\n });\n\n test(\"throws a clear error when CheckGenesis is absent\", () => {\n expect(() =>\n buildCreateTransactionRequest([\"my-app\", 0], new Uint8Array([0]), {}, 0),\n ).toThrow(/CheckGenesis/);\n });\n });\n\n describe(\"requestSignedTransaction — SSO round-trip\", () => {\n const request = buildCreateTransactionRequest(\n [\"my-app\", 0],\n new Uint8Array([0]),\n { CheckGenesis: ext(\"CheckGenesis\", [], [0x01]) },\n 0,\n );\n\n test(\"returns the signed extrinsic bytes from the mobile response\", async () => {\n const signed = new Uint8Array([0xab, 0xcd, 0xef]);\n const session = makeSession({ createTransaction: async () => ok(signed) });\n\n const out = await requestSignedTransaction(session, request);\n expect(out).toBe(signed);\n });\n\n test(\"calls session.createTransaction — never signRaw/signPayload — with the request\", async () => {\n const captured: unknown[] = [];\n const session = makeSession({\n createTransaction: async (req) => {\n captured.push(req);\n return ok(new Uint8Array([1]));\n },\n });\n\n await requestSignedTransaction(session, request);\n\n expect(captured).toEqual([request]);\n const spies = session as unknown as {\n signPayload: { mock: { calls: unknown[] } };\n signRaw: { mock: { calls: unknown[] } };\n createTransaction: { mock: { calls: unknown[] } };\n };\n expect(spies.createTransaction.mock.calls).toHaveLength(1);\n expect(spies.signPayload.mock.calls).toHaveLength(0);\n expect(spies.signRaw.mock.calls).toHaveLength(0);\n });\n\n test(\"throws a clear error when the mobile rejects\", async () => {\n const session = makeSession({\n createTransaction: async () => err({ message: \"user declined\" }),\n });\n await expect(requestSignedTransaction(session, request)).rejects.toThrow(\n \"Mobile transaction signing rejected: user declined\",\n );\n });\n\n test(\"throws AllowanceExpiredError when the failure is a NoAllowanceError\", async () => {\n const underlying = new NoAllowanceError();\n const session = makeSession({\n createTransaction: async () => err(underlying),\n });\n\n await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(\n AllowanceExpiredError,\n );\n await expect(requestSignedTransaction(session, request)).rejects.toMatchObject({\n name: \"AllowanceExpiredError\",\n resource: \"statementStore\",\n cause: underlying,\n });\n });\n\n test(\"throws AllowanceExpiredError for a wire-serialized failure (message match only)\", async () => {\n // After a serialization boundary the NoAllowanceError prototype\n // and constructor name are gone — the fixed message upstream\n // mints is the only remaining signal. This pins statement-store's\n // exact wording: if upstream rephrases it, this test fails\n // instead of the classification silently downgrading to the\n // generic rejection.\n // Pin upstream's wording first: if statement-store rephrases the\n // message, this assertion fails loudly.\n expect(new NoAllowanceError().message).toBe(\n \"Submit failed, no allowance set for account\",\n );\n\n const serialized = new Error(\"Submit failed, no allowance set for account\");\n const session = makeSession({\n createTransaction: async () => err(serialized),\n });\n\n await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(\n AllowanceExpiredError,\n );\n });\n\n test(\"throws AllowanceExpiredError when the NoAllowanceError is wrapped as a cause\", async () => {\n // host-papp may wrap the chain-side submit failure before it hits\n // the createTransaction error channel — the cause chain must be\n // walked, not just the top-level error.\n const wrapped = new Error(\"submitRequest failed\", { cause: new NoAllowanceError() });\n const session = makeSession({\n createTransaction: async () => err(wrapped),\n });\n\n await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(\n AllowanceExpiredError,\n );\n });\n\n test(\"a non-allowance Error still surfaces as the generic rejection\", async () => {\n const session = makeSession({\n createTransaction: async () => err(new Error(\"transport exploded\")),\n });\n\n await expect(requestSignedTransaction(session, request)).rejects.toThrow(\n \"Mobile transaction signing rejected: transport exploded\",\n );\n });\n });\n\n describe(\"makeRawBytesSignCallback\", () => {\n test(\"forwards data verbatim under the Bytes tag with the right productAccountId\", async () => {\n const captured: unknown[] = [];\n const session = makeSession({\n signRaw: async (req) => {\n captured.push(req);\n return ok({ signature: new Uint8Array([0xff]) });\n },\n });\n\n const callback = makeRawBytesSignCallback(session, [\"my-app\", 5]);\n await callback(new Uint8Array([0xde, 0xad, 0xbe, 0xef]));\n\n expect(captured).toHaveLength(1);\n const req = captured[0] as {\n productAccountId: [string, number];\n data: { tag: string; value: Uint8Array };\n };\n expect(req.productAccountId).toEqual([\"my-app\", 5]);\n expect(req.data.tag).toBe(\"Bytes\");\n expect(Array.from(req.data.value)).toEqual([0xde, 0xad, 0xbe, 0xef]);\n });\n\n test(\"returns the raw signature bytes from the mobile response\", async () => {\n const sig = new Uint8Array([0x42]);\n const session = makeSession({\n signRaw: async () => ok({ signature: sig }),\n });\n\n const callback = makeRawBytesSignCallback(session, [\"my-app\", 0]);\n const out = await callback(new Uint8Array([0]));\n\n expect(out).toBe(sig);\n });\n\n test(\"throws AllowanceExpiredError when the failure is a NoAllowanceError\", async () => {\n const underlying = new NoAllowanceError();\n const session = makeSession({\n signRaw: async () => err(underlying),\n });\n\n const callback = makeRawBytesSignCallback(session, [\"my-app\", 0]);\n await expect(callback(new Uint8Array([1]))).rejects.toBeInstanceOf(\n AllowanceExpiredError,\n );\n await expect(callback(new Uint8Array([1]))).rejects.toMatchObject({\n resource: \"statementStore\",\n cause: underlying,\n });\n });\n\n test(\"a non-allowance failure still surfaces as the generic rejection\", async () => {\n const session = makeSession({\n signRaw: async () => err(new Error(\"user declined\")),\n });\n\n const callback = makeRawBytesSignCallback(session, [\"my-app\", 0]);\n await expect(callback(new Uint8Array([1]))).rejects.toThrow(\n \"Mobile signing rejected: user declined\",\n );\n });\n });\n\n describe(\"createSessionSignerForAccount\", () => {\n test(\"forwards productAccountId from the explicit ref\", async () => {\n const captured: unknown[] = [];\n const session = makeSession({\n signRaw: async (req) => {\n captured.push(req);\n return ok({ signature: new Uint8Array([42]) });\n },\n });\n\n const signer = createSessionSignerForAccount(session, {\n productId: \"my-app\",\n derivationIndex: 7,\n });\n await signer.signBytes(new Uint8Array([10, 20, 30]));\n\n expect(captured).toHaveLength(1);\n const req = captured[0] as {\n productAccountId: [string, number];\n data: { tag: string; value: Uint8Array };\n };\n expect(req.productAccountId).toEqual([\"my-app\", 7]);\n expect(req.data.tag).toBe(\"Bytes\");\n expect(req.data.value).toBeInstanceOf(Uint8Array);\n });\n\n test(\"supports a productId different from any adapter's appId\", async () => {\n const captured: unknown[] = [];\n const session = makeSession({\n signRaw: async (req) => {\n captured.push(req);\n return ok({ signature: new Uint8Array([0]) });\n },\n });\n\n const signer = createSessionSignerForAccount(session, {\n productId: \"external-product\",\n derivationIndex: 0,\n });\n await signer.signBytes(new Uint8Array([1]));\n\n const req = captured[0] as { productAccountId: [string, number] };\n expect(req.productAccountId).toEqual([\"external-product\", 0]);\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n// Terminal Adapter\nexport {\n createTerminalAdapter,\n isBenignTeardownError,\n SS_STABLE_STAGE_ENDPOINTS,\n SS_PASEO_STABLE_STAGE_ENDPOINTS,\n} from \"./adapter.js\";\nexport type { TerminalAdapterOptions, TerminalAdapter } from \"./adapter.js\";\n\n// Session Signer\nexport {\n createSessionSigner,\n createSessionSignerForAccount,\n deriveProductPublicKey,\n sessionRootPublicKey,\n INCOMPLETE_SESSION_MESSAGE,\n} from \"./signer.js\";\nexport type { ProductAccountRef } from \"./signer.js\";\n// Thrown by the session signers' signTx/signBytes when the paired session's\n// allowance has lapsed (chain-side NoAllowanceError). Authored in\n// @parity/product-sdk-signer — the SDK's signer error taxonomy — and\n// re-exported here so terminal consumers can catch it without a second import.\nexport { AllowanceExpiredError, SignerError } from \"@parity/product-sdk-signer/errors\";\n\n// Allowance service — `adapter.allowance` lives on every TerminalAdapter (inherited\n// from host-papp's PappAdapter). These helpers default the session id to the only\n// paired session and unwrap the underlying ResultAsync into a throw, matching the\n// idiom of the rest of this package.\n//\n// The `has*Allowance` variants are cache-only probes that never prompt the\n// paired wallet — safe to call from login health-check paths that must not\n// surface a phone dialog.\nexport {\n getBulletinSigner,\n getStatementStoreProver,\n hasBulletinAllowance,\n hasStatementStoreAllowance,\n} from \"./allowance.js\";\nexport { AllowanceError } from \"@novasamatech/host-papp\";\nexport type { AllowanceService, AllowanceErrorReason } from \"@novasamatech/host-papp\";\n\n// Resource allocation (RFC-0010). `requestResourceAllocation` is the cached,\n// adapter-scoped entry point; `sendResourceAllocation` is the raw wire call for\n// consumers that hold only a productId and manage their own policy. The\n// resource / outcome types are derived from `UserSession` so they can't drift\n// from the host codec.\nexport { requestResourceAllocation, sendResourceAllocation } from \"./host.js\";\nexport type {\n AllocatableResource,\n ApAllocationOutcome,\n OnExistingAllowancePolicy,\n RequestResourceAllocationOptions,\n} from \"./host.js\";\n\n// Session helpers\nexport { waitForSessions } from \"./sessions.js\";\n\n// QR Encoding\nexport { renderQrCode } from \"./qr-encode.js\";\nexport type { QrRenderOptions } from \"./qr-encode.js\";\n\n// Storage adapter (advanced use — most callers get this implicitly via createTerminalAdapter)\nexport { createNodeStorageAdapter } from \"./node-storage.js\";\n\n// Re-export SDK types consumers will need\nexport type {\n PappAdapter,\n HostMetadata,\n PairingStatus,\n UserSession,\n StoredUserSession,\n SigningPayloadRequest,\n SigningRawRequest,\n SigningPayloadResponse,\n} from \"@novasamatech/host-papp\";\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Convenience wrappers around `adapter.allowance` for CLI consumers.\n *\n * The host-papp `AllowanceService` (exposed as `adapter.allowance`) returns\n * neverthrow `ResultAsync` values and requires the caller to pass a\n * `sessionId` explicitly. CLIs almost always run with one paired session at\n * a time, so passing `sessionId` is redundant and the neverthrow envelope\n * doesn't fit the rest of `@parity/product-sdk-terminal`'s throwy/async\n * idiom (`createSessionSigner`, `requestResourceAllocation`).\n *\n * These helpers:\n * - default the `sessionId` to the single paired session when there's\n * exactly one (the common CLI case);\n * - unwrap the `ResultAsync` into a `Promise<T>` that throws an\n * {@link AllowanceError} on failure;\n * - leave the underlying `adapter.allowance` intact for callers who want\n * explicit multi-session handling or neverthrow's `.match` ergonomics.\n *\n * @module\n */\nimport { AllowanceError } from \"@novasamatech/host-papp\";\nimport type { StatementProver } from \"@novasamatech/statement-store\";\nimport type { PolkadotSigner } from \"polkadot-api\";\n\nimport type { TerminalAdapter } from \"./adapter.js\";\nimport { type AllowanceResourceKind, readStoredAllowances } from \"./allowance-cache.js\";\n\n/**\n * Pick the session id to use for an allowance request, defaulting to the\n * single paired session when no explicit id is supplied.\n *\n * @throws {AllowanceError} `NoSession` when zero sessions or `>1` sessions\n * are paired without an explicit id — both cases are ambiguous and the\n * caller should resolve them before calling the convenience.\n */\nfunction resolveSessionId(adapter: TerminalAdapter, sessionId?: string): string {\n if (sessionId) return sessionId;\n const sessions = adapter.sessions.sessions.read();\n if (sessions.length === 0) {\n // Same error class host-papp emits internally so catch-by-instanceof\n // handlers behave identically for both paths.\n throw new AllowanceError(\"NoSession\", \"No paired session — pair a phone first.\");\n }\n if (sessions.length > 1) {\n throw new AllowanceError(\n \"NoSession\",\n `Multiple paired sessions (${sessions.length}) — pass an explicit sessionId.`,\n );\n }\n return sessions[0]!.id;\n}\n\n/**\n * Get a `PolkadotSigner` for a Bulletin allowance slot.\n *\n * Allocates an allowance slot via the paired wallet (or returns the cached\n * one), derives the slot-account keypair, and returns a `PolkadotSigner`\n * that signs Bulletin extrinsics with it. Replaces the manual\n * `requestResourceAllocation` + `createSlotAccountSigner` two-step for the\n * common case.\n *\n * @param adapter Terminal adapter.\n * @param productId The product id the slot is allocated under. Passed to the\n * host as the calling product id in the allowance request.\n * @param sessionId Paired session to allocate against. Defaults to the only\n * paired session; throws `AllowanceError('NoSession')` when zero or more\n * than one sessions are paired and no explicit id is supplied.\n * @throws {AllowanceError} On rejection, missing session, host-side failure,\n * or unexpected response shape.\n *\n * @example\n * ```ts\n * import { createTerminalAdapter, getBulletinSigner } from \"@parity/product-sdk-terminal\";\n *\n * const adapter = createTerminalAdapter({ appId: \"my-cli\" });\n * // ... QR pair, wait for session ...\n * const signer = await getBulletinSigner(adapter, \"my-cli.dot\");\n * await client.bulletin.tx.TransactionStorage.store({ data }).signAndSubmit(signer);\n * ```\n */\nexport async function getBulletinSigner(\n adapter: TerminalAdapter,\n productId: string,\n sessionId?: string,\n): Promise<PolkadotSigner> {\n const resolvedSessionId = resolveSessionId(adapter, sessionId);\n return adapter.allowance.getBulletinSigner(resolvedSessionId, productId).match(\n (signer) => signer,\n (err) => {\n throw err;\n },\n );\n}\n\n/**\n * Get a `StatementProver` for a statement-store allowance slot.\n *\n * Allocates an allowance slot via the paired wallet (or returns the cached\n * one) and returns the upstream `StatementProver` for the slot. Use when\n * publishing statements through `@novasamatech/statement-store` without\n * holding a long-lived key yourself.\n *\n * @param adapter Terminal adapter.\n * @param productId The product id the slot is allocated under.\n * @param sessionId Paired session to allocate against. Defaults to the only\n * paired session; throws `AllowanceError('NoSession')` when zero or more\n * than one sessions are paired and no explicit id is supplied.\n * @throws {AllowanceError} On rejection, missing session, host-side failure,\n * or unexpected response shape.\n */\nexport async function getStatementStoreProver(\n adapter: TerminalAdapter,\n productId: string,\n sessionId?: string,\n): Promise<StatementProver> {\n const resolvedSessionId = resolveSessionId(adapter, sessionId);\n return adapter.allowance.getStatementStoreProver(resolvedSessionId, productId).match(\n (prover) => prover,\n (err) => {\n throw err;\n },\n );\n}\n\n/**\n * Cache-only probe: read host-papp's encrypted on-disk allowance list and\n * resolve to whether an entry exists for `(sessionId, productId, resource)`.\n *\n * Never invokes `requestResourceAllocation`, so this is safe to call from\n * paths that must not prompt the paired wallet (e.g. login health checks).\n * Resolves `false` for: file absent, file present with no entry for the\n * tuple. Rejects only on decrypt / decode failures — a corrupted cache\n * file is a real failure that should not silently degrade into \"no\n * allowance.\"\n *\n * Reads the file at `<storageDir>/<appId>_AllowanceKeys_<sessionId>.json`\n * — same path host-papp writes to from its `AllowanceRepository`. The\n * decode is via a vendored mirror of host-papp's internal codec; see\n * `allowance-cache.ts` for drift-detection guards.\n */\nasync function hasAllowanceForResource(\n adapter: TerminalAdapter,\n productId: string,\n resource: AllowanceResourceKind,\n sessionId?: string,\n): Promise<boolean> {\n const resolvedSessionId = resolveSessionId(adapter, sessionId);\n if (adapter.storageDir === undefined) {\n // The on-disk cache only exists when the adapter knows where its files\n // live. Without an explicit `storageDir` the production host-papp\n // backend may write through a non-disk storage adapter that this\n // helper can't read; we conservatively answer false so callers don't\n // skip pairing on a missed cache.\n return false;\n }\n const entries = await readStoredAllowances(\n adapter.storageDir,\n adapter.appId,\n resolvedSessionId,\n );\n return entries.some(\n (entry) => entry.productId === productId && entry.resource.tag === resource,\n );\n}\n\n/**\n * Cache-only probe for a Bulletin allowance slot. Resolves `true` when a\n * slot key for `(sessionId, productId, bulletin)` is already cached on\n * disk; `false` when it is not. Never prompts the paired wallet.\n *\n * Pair with {@link getBulletinSigner} for the \"check first, fetch only\n * if needed\" flow:\n *\n * @example\n * ```ts\n * if (await hasBulletinAllowance(adapter, \"my-cli.dot\")) {\n * // happy path — fetch the signer without risking a wallet prompt\n * const signer = await getBulletinSigner(adapter, \"my-cli.dot\");\n * } else {\n * // tell the user a wallet prompt will fire, then call getBulletinSigner\n * }\n * ```\n */\nexport async function hasBulletinAllowance(\n adapter: TerminalAdapter,\n productId: string,\n sessionId?: string,\n): Promise<boolean> {\n return hasAllowanceForResource(adapter, productId, \"bulletin\", sessionId);\n}\n\n/**\n * Cache-only probe for a Statement Store allowance slot. Resolves `true`\n * when a slot key for `(sessionId, productId, statementStore)` is already\n * cached on disk; `false` when it is not. Never prompts the paired wallet.\n *\n * Pair with {@link getStatementStoreProver} for the \"check first, fetch\n * only if needed\" flow.\n */\nexport async function hasStatementStoreAllowance(\n adapter: TerminalAdapter,\n productId: string,\n sessionId?: string,\n): Promise<boolean> {\n return hasAllowanceForResource(adapter, productId, \"statementStore\", sessionId);\n}\n\n// ── vitest in-source tests ─────────────────────────────────────────────────\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n const { ok, err } = await import(\"neverthrow\");\n\n type FakeSession = { id: string };\n\n function makeAdapter(opts: {\n sessions: FakeSession[];\n getBulletinSigner?: ReturnType<typeof vi.fn>;\n getStatementStoreProver?: ReturnType<typeof vi.fn>;\n }): TerminalAdapter {\n return {\n sessions: {\n sessions: {\n read: () => opts.sessions,\n },\n },\n allowance: {\n getBulletinSigner: opts.getBulletinSigner ?? vi.fn(() => ok({})),\n getStatementStoreProver: opts.getStatementStoreProver ?? vi.fn(() => ok({})),\n },\n // The convenience never touches the rest of the adapter; leave it unstubbed.\n } as unknown as TerminalAdapter;\n }\n\n describe(\"getBulletinSigner — sessionId defaulting\", () => {\n test(\"uses the only paired session when sessionId is omitted\", async () => {\n const fakeSigner = { _tag: \"signer\" };\n const getBulletinSignerFn = vi.fn(() => ok(fakeSigner));\n const adapter = makeAdapter({\n sessions: [{ id: \"sess-only\" }],\n getBulletinSigner: getBulletinSignerFn,\n });\n\n const result = await getBulletinSigner(adapter, \"my-app.dot\");\n expect(result).toBe(fakeSigner);\n expect(getBulletinSignerFn).toHaveBeenCalledWith(\"sess-only\", \"my-app.dot\");\n });\n\n test(\"uses the explicit sessionId when supplied\", async () => {\n const fakeSigner = { _tag: \"signer\" };\n const getBulletinSignerFn = vi.fn(() => ok(fakeSigner));\n const adapter = makeAdapter({\n sessions: [{ id: \"sess-a\" }, { id: \"sess-b\" }],\n getBulletinSigner: getBulletinSignerFn,\n });\n\n await getBulletinSigner(adapter, \"my-app.dot\", \"sess-b\");\n expect(getBulletinSignerFn).toHaveBeenCalledWith(\"sess-b\", \"my-app.dot\");\n });\n\n test(\"throws AllowanceError('NoSession') when zero sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [] });\n await expect(getBulletinSigner(adapter, \"my-app.dot\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n await expect(getBulletinSigner(adapter, \"my-app.dot\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n\n test(\"throws AllowanceError('NoSession') when multiple sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [{ id: \"a\" }, { id: \"b\" }] });\n await expect(getBulletinSigner(adapter, \"my-app.dot\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n await expect(getBulletinSigner(adapter, \"my-app.dot\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n });\n\n describe(\"getBulletinSigner — error unwrapping\", () => {\n test(\"rethrows the AllowanceError from the underlying service as a thrown exception\", async () => {\n const underlyingErr = new AllowanceError(\"Rejected\", \"user said no\");\n const adapter = makeAdapter({\n sessions: [{ id: \"s\" }],\n getBulletinSigner: vi.fn(() => err(underlyingErr)),\n });\n // The wrapper rethrows the original error instance — handlers\n // catching by `instanceof AllowanceError` see the same object the\n // underlying service produced.\n await expect(getBulletinSigner(adapter, \"p\")).rejects.toBe(underlyingErr);\n });\n });\n\n describe(\"getStatementStoreProver — same defaulting\", () => {\n test(\"uses the only paired session when sessionId is omitted\", async () => {\n const fakeProver = { _tag: \"prover\" };\n const getStatementStoreProverFn = vi.fn(() => ok(fakeProver));\n const adapter = makeAdapter({\n sessions: [{ id: \"sess-only\" }],\n getStatementStoreProver: getStatementStoreProverFn,\n });\n\n const result = await getStatementStoreProver(adapter, \"my-app.dot\");\n expect(result).toBe(fakeProver);\n expect(getStatementStoreProverFn).toHaveBeenCalledWith(\"sess-only\", \"my-app.dot\");\n });\n\n test(\"uses the explicit sessionId when supplied\", async () => {\n const fakeProver = { _tag: \"prover\" };\n const getStatementStoreProverFn = vi.fn(() => ok(fakeProver));\n const adapter = makeAdapter({\n sessions: [{ id: \"sess-a\" }, { id: \"sess-b\" }],\n getStatementStoreProver: getStatementStoreProverFn,\n });\n\n await getStatementStoreProver(adapter, \"my-app.dot\", \"sess-b\");\n expect(getStatementStoreProverFn).toHaveBeenCalledWith(\"sess-b\", \"my-app.dot\");\n });\n\n test(\"throws AllowanceError('NoSession') when zero sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [] });\n await expect(getStatementStoreProver(adapter, \"my-app.dot\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n await expect(getStatementStoreProver(adapter, \"my-app.dot\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n\n test(\"throws AllowanceError('NoSession') when multiple sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [{ id: \"a\" }, { id: \"b\" }] });\n await expect(getStatementStoreProver(adapter, \"my-app.dot\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n await expect(getStatementStoreProver(adapter, \"my-app.dot\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n });\n\n describe(\"getStatementStoreProver — error unwrapping\", () => {\n test(\"rethrows the AllowanceError from the underlying service as a thrown exception\", async () => {\n const underlyingErr = new AllowanceError(\"Rejected\", \"user said no\");\n const adapter = makeAdapter({\n sessions: [{ id: \"s\" }],\n getStatementStoreProver: vi.fn(() => err(underlyingErr)),\n });\n await expect(getStatementStoreProver(adapter, \"p\")).rejects.toBe(underlyingErr);\n });\n });\n\n describe(\"has*Allowance — sessionId defaulting + storageDir fallback\", () => {\n // Cache-hit / cache-miss against real disk + the host-papp codec are\n // covered by `allowance.interop.test.ts`. These unit tests cover the\n // branches that don't reach the filesystem.\n\n test(\"hasBulletinAllowance throws AllowanceError('NoSession') when zero sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [] });\n await expect(hasBulletinAllowance(adapter, \"p\")).rejects.toBeInstanceOf(AllowanceError);\n await expect(hasBulletinAllowance(adapter, \"p\")).rejects.toMatchObject({\n reason: \"NoSession\",\n });\n });\n\n test(\"hasBulletinAllowance returns false (conservative) when storageDir is undefined\", async () => {\n // Production: a TerminalAdapter without an explicit storageDir\n // may be backed by a non-disk StorageAdapter that this helper\n // can't read. Returning false avoids the trap of \"we report\n // 'no allowance cached' confidently when we just couldn't read.\"\n const adapter = makeAdapter({ sessions: [{ id: \"sess-only\" }] });\n // Mock has no storageDir field — the helper should short-circuit.\n expect(await hasBulletinAllowance(adapter, \"p\")).toBe(false);\n });\n\n test(\"hasStatementStoreAllowance throws AllowanceError('NoSession') when zero sessions and no id\", async () => {\n const adapter = makeAdapter({ sessions: [] });\n await expect(hasStatementStoreAllowance(adapter, \"p\")).rejects.toBeInstanceOf(\n AllowanceError,\n );\n });\n\n test(\"hasStatementStoreAllowance returns false (conservative) when storageDir is undefined\", async () => {\n const adapter = makeAdapter({ sessions: [{ id: \"sess-only\" }] });\n expect(await hasStatementStoreAllowance(adapter, \"p\")).toBe(false);\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Private mirror of host-papp's `AllowanceRepository` on-disk format.\n *\n * The repository codec + AES-GCM scheme are internal to host-papp — only the\n * higher-level `AllowanceService` is on the public surface, and that service\n * exposes no cache-only probe (its `getBulletinSigner` / `getStatementStoreProver`\n * fall through to `requestResourceAllocation` on cache miss, which prompts the\n * paired wallet). For \"is there an allowance slot for this tuple on disk?\"\n * answers — needed by login-readiness checks that must not pop a phone\n * dialog — we have to read the storage file ourselves.\n *\n * This module vendors:\n * - the SCALE codec for the `Vector(StoredAllowanceEntry)` blob host-papp\n * writes,\n * - the AES-GCM key + nonce derivation host-papp uses\n * (`blake2b(appId, 16)` / `blake2b(\"nonce\", 32)`),\n * - the storage-key naming convention (`AllowanceKeys_<sessionId>`).\n *\n * Drift guards:\n * - `satisfies Codec<StoredAllowanceEntry>` catches type-shape drift at\n * build time.\n * - `allowance.interop.test.ts` round-trips an encoded entry through the\n * real host-papp `AllowanceService.getBulletinSigner` and asserts the\n * derived signer's pubkey matches — that catches byte-level drift at\n * test time.\n *\n * TODO: drop this mirror when host-papp's `AllowanceService` gains a\n * cache-only check (e.g. `hasAllowance(sessionId, productId, resource)`)\n * on its public surface. Until then we have to read the file ourselves.\n *\n * @module\n */\nimport { gcm } from \"@noble/ciphers/aes.js\";\nimport { blake2b } from \"@noble/hashes/blake2.js\";\nimport { fromHex, toHex } from \"@polkadot-api/utils\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { Bytes, type Codec, Enum, Struct, Vector, _void, str } from \"scale-ts\";\n\nimport { sanitizeKey } from \"./node-storage.js\";\n\n/** Resource kinds host-papp allocates slot accounts for. */\nexport type AllowanceResourceKind = \"bulletin\" | \"statementStore\";\n\n/** One entry in the persisted allowance list. */\nexport type StoredAllowanceEntry = {\n productId: string;\n resource: { tag: AllowanceResourceKind; value: undefined };\n slotAccountKey: Uint8Array;\n};\n\nconst AllowanceResourceKindCodec = Enum({\n bulletin: _void,\n statementStore: _void,\n});\n\nconst StoredAllowanceEntryCodec = Struct({\n productId: str,\n resource: AllowanceResourceKindCodec,\n slotAccountKey: Bytes(),\n}) satisfies Codec<StoredAllowanceEntry>;\n\nconst StoredAllowancesCodec = Vector(StoredAllowanceEntryCodec);\n\n/**\n * AES-GCM key + nonce derivation host-papp uses for both the allowance\n * repository and the user-secrets repository — same salt (appId) seeds both.\n */\nfunction getAes(appId: string) {\n const key = blake2b(new TextEncoder().encode(appId), { dkLen: 16 });\n const nonce = blake2b(new TextEncoder().encode(\"nonce\"), { dkLen: 32 });\n return gcm(key, nonce);\n}\n\n/**\n * Storage file path host-papp writes the allowance list to, derived from\n * `appId` and `sessionId` via the same `sanitizeKey` rule the rest of the\n * package uses.\n */\nfunction allowanceFilePath(storageDir: string, appId: string, sessionId: string): string {\n return join(storageDir, `${sanitizeKey(appId, `AllowanceKeys_${sessionId}`)}.json`);\n}\n\n/**\n * Read and decrypt the allowance list for a session. Returns `[]` when the\n * file is absent (the steady-state \"never paired for this session\" case);\n * throws on decrypt or decode failure (a corrupted file is a real failure,\n * not a \"no allowance\" signal — silently treating it as empty would mask\n * a bug).\n */\nexport async function readStoredAllowances(\n storageDir: string,\n appId: string,\n sessionId: string,\n): Promise<StoredAllowanceEntry[]> {\n let hex: string;\n try {\n hex = await readFile(allowanceFilePath(storageDir, appId, sessionId), \"utf-8\");\n } catch (err) {\n // ENOENT is the only error we treat as \"no entries\".\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return [];\n }\n throw err;\n }\n const decrypted = getAes(appId).decrypt(fromHex(hex));\n return StoredAllowancesCodec.dec(decrypted);\n}\n\n/**\n * Encode + encrypt + write an allowance list to disk. Used by the interop\n * test to pre-seed a cache entry without having to drive a wallet round-trip.\n * Not exported from the package — internal to `*.interop.test.ts`.\n */\nexport async function writeStoredAllowances(\n storageDir: string,\n appId: string,\n sessionId: string,\n entries: StoredAllowanceEntry[],\n): Promise<void> {\n const encoded = StoredAllowancesCodec.enc(entries);\n const encrypted = toHex(getAes(appId).encrypt(encoded));\n await writeFile(allowanceFilePath(storageDir, appId, sessionId), encrypted, \"utf-8\");\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Helpers for working with persisted sessions on a `TerminalAdapter`.\n */\nimport type { UserSession } from \"@novasamatech/host-papp\";\n\nimport type { TerminalAdapter } from \"./adapter.js\";\n\n/**\n * Wait for the adapter to load at least one persisted session, or resolve\n * with an empty array after `timeoutMs`.\n *\n * The session manager loads sessions from storage asynchronously, so a\n * synchronous `adapter.sessions.sessions.read()` immediately after\n * `createTerminalAdapter()` may return `[]` even when sessions exist on\n * disk. Use this helper to give the loader a chance to populate before\n * deciding whether the user is logged in.\n */\nexport function waitForSessions(\n adapter: TerminalAdapter,\n timeoutMs = 3000,\n): Promise<UserSession[]> {\n return new Promise((resolve) => {\n let resolved = false;\n let unsub: (() => void) | null = null;\n\n const finish = (sessions: UserSession[]) => {\n if (resolved) return;\n resolved = true;\n // Defer unsub so the synchronous initial emission can complete\n // before we tear down the subscription.\n queueMicrotask(() => unsub?.());\n resolve(sessions);\n };\n\n const timer = setTimeout(() => finish([]), timeoutMs);\n\n unsub = adapter.sessions.sessions.subscribe((sessions: UserSession[]) => {\n if (sessions.length > 0) {\n clearTimeout(timer);\n finish(sessions);\n }\n });\n });\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest;\n\n type Subscriber = (sessions: UserSession[]) => void;\n\n function fakeAdapter(): {\n adapter: TerminalAdapter;\n emit: (sessions: UserSession[]) => void;\n subscriberCount: () => number;\n } {\n const subscribers = new Set<Subscriber>();\n const adapter = {\n sessions: {\n sessions: {\n subscribe(cb: Subscriber) {\n subscribers.add(cb);\n return () => {\n subscribers.delete(cb);\n };\n },\n },\n },\n } as unknown as TerminalAdapter;\n return {\n adapter,\n emit: (sessions) => {\n for (const cb of subscribers) cb(sessions);\n },\n subscriberCount: () => subscribers.size,\n };\n }\n\n describe(\"waitForSessions\", () => {\n test(\"resolves with sessions on first non-empty emission\", async () => {\n const { adapter, emit } = fakeAdapter();\n const promise = waitForSessions(adapter, 1000);\n const session = { remoteAccount: { accountId: new Uint8Array(32) } } as UserSession;\n emit([session]);\n await expect(promise).resolves.toEqual([session]);\n });\n\n test(\"ignores empty emissions and waits for a non-empty one\", async () => {\n const { adapter, emit } = fakeAdapter();\n const promise = waitForSessions(adapter, 1000);\n emit([]);\n emit([]);\n const session = { remoteAccount: { accountId: new Uint8Array(32) } } as UserSession;\n emit([session]);\n await expect(promise).resolves.toEqual([session]);\n });\n\n test(\"resolves with [] after timeout when nothing is emitted\", async () => {\n vi.useFakeTimers();\n try {\n const { adapter } = fakeAdapter();\n const promise = waitForSessions(adapter, 50);\n vi.advanceTimersByTime(50);\n await expect(promise).resolves.toEqual([]);\n } finally {\n vi.useRealTimers();\n }\n });\n\n test(\"unsubscribes after resolving\", async () => {\n const { adapter, emit, subscriberCount } = fakeAdapter();\n const session = { remoteAccount: { accountId: new Uint8Array(32) } } as UserSession;\n const promise = waitForSessions(adapter, 1000);\n emit([session]);\n await promise;\n // Wait for queueMicrotask\n await Promise.resolve();\n expect(subscriberCount()).toBe(0);\n });\n\n test(\"uses default 3000ms timeout when none is passed\", async () => {\n vi.useFakeTimers();\n try {\n const { adapter } = fakeAdapter();\n // No timeoutMs argument — exercises the `?? 3000` default branch.\n const promise = waitForSessions(adapter);\n // Just short of 3000ms: still pending.\n vi.advanceTimersByTime(2999);\n let settled = false;\n promise.then(() => {\n settled = true;\n });\n await Promise.resolve();\n expect(settled).toBe(false);\n // Cross the default boundary.\n vi.advanceTimersByTime(1);\n await expect(promise).resolves.toEqual([]);\n } finally {\n vi.useRealTimers();\n }\n });\n\n test(\"handles synchronous initial emission\", async () => {\n // Some subscribables emit current value synchronously inside subscribe().\n const session = { remoteAccount: { accountId: new Uint8Array(32) } } as UserSession;\n const adapter = {\n sessions: {\n sessions: {\n subscribe(cb: Subscriber) {\n cb([session]);\n return () => {};\n },\n },\n },\n } as unknown as TerminalAdapter;\n await expect(waitForSessions(adapter, 1000)).resolves.toEqual([session]);\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Terminal QR code rendering using Unicode half-block characters.\n *\n * Wraps the `qrcode` npm package's UTF-8 renderer, which packs two\n * module rows per terminal line using half-block characters.\n */\n\n/** Options for QR code rendering. */\nexport interface QrRenderOptions {\n /** Error correction level. Default: \"M\". */\n errorCorrectionLevel?: \"L\" | \"M\" | \"Q\" | \"H\";\n /** Quiet zone size in modules. Default: 2. */\n margin?: number;\n}\n\n/**\n * Encode a string as a QR code rendered in Unicode half-block characters.\n *\n * Returns a multi-line string suitable for `console.log`.\n */\nexport async function renderQrCode(data: string, options?: QrRenderOptions): Promise<string> {\n const QRCode = await import(\"qrcode\");\n const result = await QRCode.toString(data, {\n type: \"utf8\",\n errorCorrectionLevel: options?.errorCorrectionLevel ?? \"M\",\n margin: options?.margin ?? 2,\n });\n return result;\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"renderQrCode produces non-empty output\", async () => {\n const result = await renderQrCode(\"https://example.com\");\n expect(result.length).toBeGreaterThan(0);\n expect(result).toContain(\"\\n\");\n });\n\n test(\"renderQrCode contains Unicode block characters\", async () => {\n const result = await renderQrCode(\"test\");\n expect(/[▀▄█]/.test(result)).toBe(true);\n });\n\n test(\"different inputs produce different QR codes\", async () => {\n const a = await renderQrCode(\"hello\");\n const b = await renderQrCode(\"world\");\n expect(a).not.toBe(b);\n });\n}\n"],"mappings":"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","names":["fakeLazyClient","teardown","makeSession","ext","fakeAdapter","req","join","AllowanceExpiredError","makeAdapter","AllowanceError","fakeAdapter"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parity/product-sdk-terminal",
|
|
3
3
|
"description": "QR code login and signing for CLI/terminal apps via Polkadot mobile wallet pairing",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"polkadot-api": "^2.1.6",
|
|
44
44
|
"qrcode": "^1.5.4",
|
|
45
45
|
"scale-ts": "^1.6.1",
|
|
46
|
-
"@parity/product-sdk-keys": "0.3.
|
|
47
|
-
"@parity/product-sdk-
|
|
48
|
-
"@parity/product-sdk-
|
|
46
|
+
"@parity/product-sdk-keys": "0.3.23",
|
|
47
|
+
"@parity/product-sdk-signer": "0.14.3",
|
|
48
|
+
"@parity/product-sdk-logger": "0.1.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@novasamatech/substrate-slot-sr25519-wasm": "^0.8.9",
|