@parity/product-sdk-terminal 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -49
- package/dist/adapter-Dt3ryKnw.d.ts +72 -0
- package/dist/{chunk-5MCLSFKQ.js → chunk-GHOFKVUG.js} +1 -1
- package/dist/chunk-GHOFKVUG.js.map +1 -0
- package/dist/host.d.ts +113 -0
- package/dist/host.js +1405 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +59 -62
- package/dist/index.js +192 -202
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +12 -4
- package/dist/testing.js.map +1 -1
- package/package.json +15 -18
- package/dist/chunk-5MCLSFKQ.js.map +0 -1
- package/dist/loader.mjs +0 -76
- package/dist/register.d.ts +0 -2
- package/dist/register.js +0 -9
- package/dist/register.js.map +0 -1
- package/scripts/patch-wasm.sh +0 -27
package/README.md
CHANGED
|
@@ -16,22 +16,7 @@ pnpm add @parity/product-sdk-terminal
|
|
|
16
16
|
|
|
17
17
|
**Requires Node ≥21.** The package relies on the global `WebSocket` exposed by Node 21+ (via `@polkadot-api/ws-provider@0.9`). On older Node versions the WebSocket connection fails at runtime with `WebSocket is not defined`.
|
|
18
18
|
|
|
19
|
-
**
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
node --import @parity/product-sdk-terminal/register app.js
|
|
23
|
-
tsx --import @parity/product-sdk-terminal/register app.ts
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Or in your `package.json` scripts:
|
|
27
|
-
|
|
28
|
-
```json
|
|
29
|
-
{
|
|
30
|
-
"scripts": {
|
|
31
|
-
"start": "tsx --import @parity/product-sdk-terminal/register index.ts"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
```
|
|
19
|
+
> **Note (≥0.3):** The `@parity/product-sdk-terminal/register` WASM loader hook has been removed. It existed only to patch `verifiablejs` (a browser-only inline-WASM dependency of `@novasamatech/host-papp`). host-papp ≥0.7.9 no longer uses `verifiablejs` — its sr25519 primitives are pure JS — so no loader hook is needed. Drop any `--import @parity/product-sdk-terminal/register` flags from your `node`/`tsx` invocations.
|
|
35
20
|
|
|
36
21
|
## Quick Start
|
|
37
22
|
|
|
@@ -41,7 +26,6 @@ import { createTerminalAdapter, renderQrCode, waitForSessions } from "@parity/pr
|
|
|
41
26
|
// 1. Create the adapter
|
|
42
27
|
const adapter = createTerminalAdapter({
|
|
43
28
|
appId: "my-terminal-app",
|
|
44
|
-
metadataUrl: "https://example.com/metadata.json",
|
|
45
29
|
});
|
|
46
30
|
|
|
47
31
|
// 2. Subscribe to pairing status to show the QR code
|
|
@@ -79,7 +63,6 @@ Creates a terminal adapter backed by the host-papp SDK.
|
|
|
79
63
|
|
|
80
64
|
**Options:**
|
|
81
65
|
- `appId` -- unique app identifier (used as storage namespace)
|
|
82
|
-
- `metadataUrl` -- URL to metadata JSON shown during pairing
|
|
83
66
|
- `endpoints?` -- statement store WebSocket endpoints (defaults to Paseo)
|
|
84
67
|
- `hostMetadata?` -- optional host environment info
|
|
85
68
|
- `storageDir?` -- override the on-disk session directory (defaults to `~/.polkadot-apps/`). Useful in tests and containerised environments.
|
|
@@ -129,6 +112,44 @@ File-based storage adapter for Node.js. Data persists in `storageDir` (defaults
|
|
|
129
112
|
|
|
130
113
|
Waits for the session list to emit at least one entry, or resolves with `[]` after `timeoutMs`.
|
|
131
114
|
|
|
115
|
+
## Allowance management (`./host` subpath)
|
|
116
|
+
|
|
117
|
+
For CLIs that need to write to Bulletin / Statement Store / Asset Hub smart contracts: the QR-paired mobile wallet is the Account Holder, the CLI plays the Host role, and `@parity/product-sdk-terminal/host` exposes that Host-runner surface (the [RFC-10](https://github.com/paritytech/triangle-js-sdks/blob/valentunn-rfc-0010/docs/rfcs/0010-allowance.md) Accounts Protocol companion, an on-disk allowance-key cache, and a local sr25519 signer over the cached keys).
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import {
|
|
121
|
+
ensureSlotAccountSigner,
|
|
122
|
+
requestResourceAllocation,
|
|
123
|
+
getCachedAllocation,
|
|
124
|
+
createSlotAccountSigner,
|
|
125
|
+
} from "@parity/product-sdk-terminal/host";
|
|
126
|
+
|
|
127
|
+
// First call prompts the wallet; subsequent calls are wire-free.
|
|
128
|
+
const signer = await ensureSlotAccountSigner(session, adapter, {
|
|
129
|
+
tag: "BulletInAllowance",
|
|
130
|
+
value: undefined,
|
|
131
|
+
});
|
|
132
|
+
await bulletinTx.submitAndWatch(signer);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `ensureSlotAccountSigner(session, adapter, resource): Promise<PolkadotSigner>`
|
|
136
|
+
|
|
137
|
+
The one call most CLIs want. Cache hit → returns signer; cache miss → allocates via the wallet, then returns signer. Throws on `Rejected` / `NotAvailable`. Slot-table resources only (`BulletInAllowance`, `StatementStoreAllowance`).
|
|
138
|
+
|
|
139
|
+
### `requestResourceAllocation(session, adapter, resources, options?): Promise<ApAllocationOutcome[]>`
|
|
140
|
+
|
|
141
|
+
Lower-level: pre-allocate any combination of resources. Granted keys cached at `{storageDir}/{appId}_AllowanceKeys.json` (`0o600`, atomic write). `onExisting` auto-picks `Ignore`/`Increase` based on cache state; override via `options.onExisting`.
|
|
142
|
+
|
|
143
|
+
### `getCachedAllocation(adapter, resource): Promise<CachedAllocation | null>`
|
|
144
|
+
|
|
145
|
+
Read-only cache lookup; no wire round-trip.
|
|
146
|
+
|
|
147
|
+
### `createSlotAccountSigner(adapter, resource): Promise<PolkadotSigner | null>`
|
|
148
|
+
|
|
149
|
+
Build a `PolkadotSigner` from the cached slot key without round-tripping. Returns `null` if nothing's cached (use `ensureSlotAccountSigner` for the round-trip-if-missing flow).
|
|
150
|
+
|
|
151
|
+
> **Caveats.** `AutoSigning` currently returns `NotAvailable` on both Android and iOS wallets. Slot renewal at expiry depends on [paritytech/individuality#931](https://github.com/paritytech/individuality/pull/931) (open) — first-time allocation works end-to-end today.
|
|
152
|
+
|
|
132
153
|
## Migration from `@polkadot-apps/terminal`
|
|
133
154
|
|
|
134
155
|
For consumers moving from `@polkadot-apps/terminal` v0.2.0 / v0.3.0. Existing sessions on disk (`~/.polkadot-apps/`) carry over — same `appId`, same path. No re-pairing required for the migration itself.
|
|
@@ -136,7 +157,7 @@ For consumers moving from `@polkadot-apps/terminal` v0.2.0 / v0.3.0. Existing se
|
|
|
136
157
|
| Concern | `@polkadot-apps/terminal` | `@parity/product-sdk-terminal` |
|
|
137
158
|
| --- | --- | --- |
|
|
138
159
|
| Package name | `@polkadot-apps/terminal` | `@parity/product-sdk-terminal` |
|
|
139
|
-
|
|
|
160
|
+
| WASM loader hook | `--import @polkadot-apps/terminal/register` (required) | **removed** — no `--import` needed (host-papp ≥0.7.9 dropped `verifiablejs`) |
|
|
140
161
|
| `createTerminalAdapter` | `async` — returned `Promise<TerminalAdapter>` | **sync** — returns `TerminalAdapter` directly. Drop the `await`. |
|
|
141
162
|
| Default account signer | `createSessionSigner(session)` | `createSessionSigner(session, adapter)` — pass the adapter as second arg |
|
|
142
163
|
| Non-default sub-account signer | not exposed | `createSessionSignerForAccount(session, { productId, derivationIndex })` |
|
|
@@ -157,7 +178,7 @@ The single-argument `createSessionSigner(session)` from `@polkadot-apps/terminal
|
|
|
157
178
|
### Migration steps
|
|
158
179
|
|
|
159
180
|
1. **Replace the dep**: `pnpm remove @polkadot-apps/terminal && pnpm add @parity/product-sdk-terminal`
|
|
160
|
-
2. **
|
|
181
|
+
2. **Remove the `--import @.../register` flag** from your `node` / `tsx` invocations or `package.json` scripts — the WASM loader hook no longer exists (and is no longer needed).
|
|
161
182
|
3. **Drop `await`** in front of `createTerminalAdapter(...)` calls.
|
|
162
183
|
4. **Update each `createSessionSigner` call site**: change `createSessionSigner(session)` → `createSessionSigner(session, adapter)`.
|
|
163
184
|
5. **Verify Node version** is ≥21 (`node --version`).
|
|
@@ -183,7 +204,6 @@ const { sessionId } = await createTestSession({
|
|
|
183
204
|
|
|
184
205
|
const adapter = createTerminalAdapter({
|
|
185
206
|
appId: "my-terminal-app",
|
|
186
|
-
metadataUrl: "https://example.com/metadata.json",
|
|
187
207
|
storageDir,
|
|
188
208
|
});
|
|
189
209
|
const sessions = await waitForSessions(adapter);
|
|
@@ -201,10 +221,10 @@ const sessions = await waitForSessions(adapter);
|
|
|
201
221
|
|
|
202
222
|
After login and attestation, the paired wallet can sign both transactions and raw messages via the statement store. The `PolkadotSigner` returned by `createSessionSigner` routes each path to the right host-papp method:
|
|
203
223
|
|
|
204
|
-
- **Transactions** (`signTx` from polkadot-api's perspective — what `submitAndWatch`, `signSubmitAndWatch`, contract method calls, etc. invoke) go through `session.
|
|
205
|
-
- **Raw bytes** (`signBytes`) go through `session.signRaw
|
|
224
|
+
- **Transactions** (`signTx` from polkadot-api's perspective — what `submitAndWatch`, `signSubmitAndWatch`, contract method calls, etc. invoke) go through `session.signRaw` with the `Payload` tag. polkadot-api assembles the full SCALE-encoded signing payload from runtime metadata — `callData ‖ extras ‖ additionalSigneds` for every signed extension the chain declares — and hands the bytes to the wallet as an opaque hex blob. The wallet signs the payload as-is, with no envelope wrapping. Any signed extension declared by the runtime (including extensions polkadot-api doesn't know about, e.g. `AsPgas` on Paseo Next v2) survives end-to-end because the wallet doesn't inspect the bytes — it just signs them.
|
|
225
|
+
- **Raw bytes** (`signBytes`) go through `session.signRaw` with the `Bytes` tag. Mobile applies the standard `<Bytes>...</Bytes>` anti-phishing wrap before signing — appropriate for arbitrary data, the same behavior `signRaw` has across all Polkadot wallets.
|
|
206
226
|
|
|
207
|
-
> **Note on
|
|
227
|
+
> **Note on previous bugs.** Versions prior to `0.1.1` routed *all* signing through `signRaw` with no tag distinction, producing `BadProof`-rejected signatures. Versions `0.1.1` through `0.2.x` then used `polkadot-api/pjs-signer`, which threw `PJS does not support this signed-extension: <name>` on any extension outside its eight built-in mappers (notably `AsPgas`). The current path bypasses both pitfalls.
|
|
208
228
|
|
|
209
229
|
## Notes
|
|
210
230
|
|
|
@@ -214,16 +234,6 @@ The adapter uses `@polkadot-api/ws-provider@0.9`, which relies on the global `We
|
|
|
214
234
|
|
|
215
235
|
The default WebSocket is constructed without `followRedirects: true`, so endpoints behind an HTTP redirect will fail to connect. If you must point at an endpoint that does, supply the resolved URL directly via the `endpoints` option.
|
|
216
236
|
|
|
217
|
-
### `ExperimentalWarning: Importing WebAssembly module instances`
|
|
218
|
-
|
|
219
|
-
You'll see this warning at startup:
|
|
220
|
-
|
|
221
|
-
```
|
|
222
|
-
(node:NNNNN) ExperimentalWarning: Importing WebAssembly module instances is an experimental feature and might change at any time
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
It's emitted by Node when the loader hook imports the `verifiablejs` WASM. Harmless. To silence it, run with `--no-warnings=ExperimentalWarning`.
|
|
226
|
-
|
|
227
237
|
## How It Works
|
|
228
238
|
|
|
229
239
|
1. **QR Pairing** -- generates Sr25519 + P256 keypairs, encodes a `polkadotapp://pair?handshake=0x...` deep link, subscribes to the statement store
|
|
@@ -243,20 +253,6 @@ Sessions are persisted to `~/.polkadot-apps/` and survive across restarts. The S
|
|
|
243
253
|
|
|
244
254
|
## Future Work
|
|
245
255
|
|
|
246
|
-
- **`KvStore`↔`StorageAdapter` bridge.** This package implements its own file-backed `StorageAdapter` for Node.js (`createNodeStorageAdapter`). Once `@parity/product-sdk-storage` grows a file backend with the same `read/write/clear/subscribe` `ResultAsync` shape, replace `node-storage.ts` with a thin adapter over it.
|
|
256
|
+
- **`KvStore`↔`StorageAdapter` bridge.** This package implements its own file-backed `StorageAdapter` for Node.js (`createNodeStorageAdapter`). Once `@parity/product-sdk-local-storage` grows a file backend with the same `read/write/clear/subscribe` `ResultAsync` shape, replace `node-storage.ts` with a thin adapter over it.
|
|
247
257
|
- **Codec re-exports from `@parity/product-sdk-statement-store`.** `testing.ts` imports session-account codec helpers (`AccountIdCodec`, `LocalSessionAccountCodec`, etc.) directly from `@novasamatech/statement-store`. Re-exporting them through the in-monorepo wrapper would let this package depend only on workspace siblings.
|
|
248
|
-
- **Embedded host runner for allowance / attestation refresh.** Today this package consumes a paired session and signs against it, but cannot renew the on-chain attestation that gates allowance writes — once it expires the user has to re-do the full QR pairing. The proposed fix is a new `./host` sub-export (in addition to `.`, `./register`, `./testing`) exposing roughly:
|
|
249
|
-
```ts
|
|
250
|
-
// proposed shape — not yet implemented
|
|
251
|
-
export interface AllowanceManager {
|
|
252
|
-
isExpired(): boolean;
|
|
253
|
-
refresh(): ResultAsync<void, Error>;
|
|
254
|
-
currentAttestation(): ResultAsync<Attestation, Error>;
|
|
255
|
-
}
|
|
256
|
-
export function createAllowanceManager(
|
|
257
|
-
adapter: TerminalAdapter,
|
|
258
|
-
options?: { hostEndpoint?: string },
|
|
259
|
-
): AllowanceManager;
|
|
260
|
-
```
|
|
261
|
-
Implementation should sit on top of `@parity/product-sdk-host`'s container/storage primitives so the host runner is shared with browser/desktop hosts rather than being CLI-specific. This is the gap Tarik flagged as "the CLI might also need to run some kind of host to get allowances".
|
|
262
258
|
- **`@noble/*` major version drift.** This package pins `@noble/{ciphers,curves,hashes}: ^2.x` because upstream `@polkadot-apps/terminal` did, and the `testing.ts` codec helpers use the v2 import paths (`@noble/hashes/blake2.js`, `@noble/curves/nist.js`). The rest of the monorepo is on `^1.x`. Both majors coexist in the lockfile; not a runtime problem today but worth a coordinated bump. Either move the whole monorepo to v2, or rewrite `testing.ts` against v1 paths (`@noble/hashes/blake2b.js` etc.).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { PappAdapter, HostMetadata } from '@novasamatech/host-papp';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Node.js adapter for the Polkadot host-papp SDK.
|
|
5
|
+
*
|
|
6
|
+
* Provides Node.js-compatible implementations of the SDK's storage and
|
|
7
|
+
* transport layers, enabling QR login, attestation, and signing in
|
|
8
|
+
* terminal/CLI environments.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Options for creating a terminal adapter. */
|
|
12
|
+
interface TerminalAdapterOptions {
|
|
13
|
+
/** Unique app identifier. Used as the storage namespace. */
|
|
14
|
+
appId: string;
|
|
15
|
+
/** Statement store WebSocket endpoints. Defaults to Paseo stable endpoints. */
|
|
16
|
+
endpoints?: string[];
|
|
17
|
+
/** Optional host metadata for the Sign-In screen. */
|
|
18
|
+
hostMetadata?: HostMetadata;
|
|
19
|
+
/**
|
|
20
|
+
* Directory where session files are persisted. Defaults to
|
|
21
|
+
* `~/.polkadot-apps/`. Override in tests to point at a temporary
|
|
22
|
+
* directory populated with `createTestSession` from
|
|
23
|
+
* `@parity/product-sdk-terminal/testing`.
|
|
24
|
+
*/
|
|
25
|
+
storageDir?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a terminal adapter backed by the host-papp SDK.
|
|
29
|
+
*
|
|
30
|
+
* This sets up:
|
|
31
|
+
* - File-based storage in `~/.polkadot-apps/` (since Node.js has no localStorage)
|
|
32
|
+
* - WebSocket connection to the statement store
|
|
33
|
+
* - The full SSO flow: QR pairing + on-chain attestation
|
|
34
|
+
* - Session manager for signing requests
|
|
35
|
+
*/
|
|
36
|
+
/** A PappAdapter with the `appId` it was created with and a `destroy` method for cleanup. */
|
|
37
|
+
type TerminalAdapter = PappAdapter & {
|
|
38
|
+
/** The `appId` passed to {@link createTerminalAdapter}. Useful for {@link createSessionSigner}. */
|
|
39
|
+
readonly appId: string;
|
|
40
|
+
/**
|
|
41
|
+
* The on-disk storage directory used for sessions and (when the
|
|
42
|
+
* host-runner facet is in use) for the allowance-key cache the
|
|
43
|
+
* `./host` subpath maintains. `undefined` when the default
|
|
44
|
+
* is in use; set explicitly when the caller passed `storageDir` to
|
|
45
|
+
* {@link createTerminalAdapter}.
|
|
46
|
+
*/
|
|
47
|
+
readonly storageDir?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Disconnect the WebSocket and release resources.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* Idempotent. Returns a Promise that resolves once all in-flight
|
|
53
|
+
* statement-subscription teardowns have settled and the underlying
|
|
54
|
+
* substrate client has been disconnected. **Awaiting is recommended
|
|
55
|
+
* but not required** — callers that don't await get the same
|
|
56
|
+
* fire-and-forget shape the previous version had, but they may see
|
|
57
|
+
* the destroy-time RPC traffic finish after the function returns.
|
|
58
|
+
*
|
|
59
|
+
* The implementation tracks the server-side `statement_unsubscribe…`
|
|
60
|
+
* RPCs `sessions.dispose()` fires, then awaits them via
|
|
61
|
+
* `Promise.allSettled` before destroying the substrate-client request
|
|
62
|
+
* queue. No timing-based guesses; no global-state mutations of
|
|
63
|
+
* `console.error` or `process.on('unhandledRejection')`. Pending
|
|
64
|
+
* subscribes (where `onSuccess` hasn't fired yet) are cancelled by
|
|
65
|
+
* the underlying `getSubscribeFn` teardown via `cancelRequest()`,
|
|
66
|
+
* which is the in-band fast-path and doesn't surface as a rejection.
|
|
67
|
+
*/
|
|
68
|
+
destroy(): Promise<void>;
|
|
69
|
+
};
|
|
70
|
+
declare function createTerminalAdapter(options: TerminalAdapterOptions): TerminalAdapter;
|
|
71
|
+
|
|
72
|
+
export { type TerminalAdapter as T, type TerminalAdapterOptions as a, createTerminalAdapter as c };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/node-storage.ts"],"sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * File-based StorageAdapter for Node.js environments.\n *\n * Implements the @novasamatech/storage-adapter interface using JSON files\n * in ~/.polkadot-apps/. Node.js doesn't have localStorage, so this\n * provides persistent storage for the SDK's session and secret data.\n */\nimport type { StorageAdapter } from \"@novasamatech/storage-adapter\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\nimport { fromPromise } from \"neverthrow\";\nimport { join } from \"node:path\";\nimport { readFile, writeFile, mkdir, unlink } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\n\nconst log = createLogger(\"terminal\");\nconst DEFAULT_STORAGE_DIR = join(homedir(), \".polkadot-apps\");\n\n/**\n * Compute the storage filename for a given appId+key pair.\n *\n * Exposed (rather than kept private) so the test-session helper in\n * `./testing.ts` can target the same file the live adapter reads from\n * without having to duplicate the sanitization rule.\n */\nexport function sanitizeKey(appId: string, key: string): string {\n return `${appId}_${key}`.replace(/[^a-zA-Z0-9_.-]/g, \"_\");\n}\n\nfunction toError(e: unknown): Error {\n return e instanceof Error ? e : new Error(String(e));\n}\n\n/**\n * Create a file-based StorageAdapter for use with the host-papp SDK in Node.js.\n *\n * Data is stored as individual JSON files in the given directory\n * (defaults to `~/.polkadot-apps/`).\n */\nexport function createNodeStorageAdapter(appId: string, storageDir?: string): StorageAdapter {\n const dir = storageDir ?? DEFAULT_STORAGE_DIR;\n let dirCreated = false;\n const subscribers = new Map<string, Set<(value: string | null) => unknown>>();\n\n function fp(key: string): string {\n return join(dir, `${sanitizeKey(appId, key)}.json`);\n }\n\n async function ensureDir(): Promise<void> {\n if (dirCreated) return;\n await mkdir(dir, { recursive: true });\n dirCreated = true;\n }\n\n function notifySubscribers(key: string, value: string | null) {\n const subs = subscribers.get(key);\n if (subs) {\n for (const cb of subs) {\n try {\n cb(value);\n } catch (e) {\n log.warn(\"storage subscriber callback threw\", { key, error: e });\n }\n }\n }\n }\n\n return {\n read(key: string) {\n return fromPromise(\n readFile(fp(key), \"utf-8\").catch((e) => {\n // Missing files are expected (a missing key reads as null);\n // log at debug so consumers can opt in to seeing reads when\n // diagnosing \"why isn't my session loading\".\n if ((e as NodeJS.ErrnoException).code !== \"ENOENT\") {\n log.warn(\"storage read failed\", { key, error: e });\n } else {\n log.debug(\"storage read miss\", { key });\n }\n return null;\n }),\n toError,\n );\n },\n\n write(key: string, value: string) {\n return fromPromise(\n ensureDir()\n .then(() => writeFile(fp(key), value, \"utf-8\"))\n .then(() => {\n notifySubscribers(key, value);\n }),\n toError,\n ).map(() => undefined as undefined);\n },\n\n clear(key: string) {\n return fromPromise(\n unlink(fp(key))\n .catch(() => {})\n .then(() => {\n notifySubscribers(key, null);\n }),\n toError,\n ).map(() => undefined as undefined);\n },\n\n subscribe(key: string, callback: (value: string | null) => unknown) {\n if (!subscribers.has(key)) {\n subscribers.set(key, new Set());\n }\n subscribers.get(key)!.add(callback);\n return () => {\n subscribers.get(key)?.delete(callback);\n };\n },\n };\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect, beforeEach, afterAll } = import.meta.vitest;\n const { mkdtemp, rm } = await import(\"node:fs/promises\");\n const { tmpdir } = await import(\"node:os\");\n const { configure } = await import(\"@parity/product-sdk-logger\");\n\n let testDir: string;\n\n beforeEach(async () => {\n // Silence the logger so tests that exercise the warn paths don't\n // pollute stderr with expected log output.\n configure({ handler: () => {} });\n testDir = await mkdtemp(join(tmpdir(), \"terminal-storage-test-\"));\n });\n\n afterAll(async () => {\n // Clean up any remaining test dirs\n try {\n await rm(testDir, { recursive: true });\n } catch {\n /* ignore */\n }\n });\n\n describe(\"createNodeStorageAdapter\", () => {\n test(\"read returns null for missing key\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n const result = await store.read(\"nonexistent\");\n expect(result.isOk()).toBe(true);\n expect(result._unsafeUnwrap()).toBeNull();\n });\n\n test(\"write and read round-trip\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n await store.write(\"key1\", \"hello\");\n const result = await store.read(\"key1\");\n expect(result._unsafeUnwrap()).toBe(\"hello\");\n });\n\n test(\"write overwrites existing value\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n await store.write(\"key1\", \"first\");\n await store.write(\"key1\", \"second\");\n const result = await store.read(\"key1\");\n expect(result._unsafeUnwrap()).toBe(\"second\");\n });\n\n test(\"clear removes key\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n await store.write(\"key1\", \"value\");\n await store.clear(\"key1\");\n const result = await store.read(\"key1\");\n expect(result._unsafeUnwrap()).toBeNull();\n });\n\n test(\"clear is safe for missing key\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n const result = await store.clear(\"nonexistent\");\n expect(result.isOk()).toBe(true);\n });\n\n test(\"different appIds are isolated\", async () => {\n const storeA = createNodeStorageAdapter(\"app-a\", testDir);\n const storeB = createNodeStorageAdapter(\"app-b\", testDir);\n await storeA.write(\"key\", \"from-a\");\n await storeB.write(\"key\", \"from-b\");\n expect((await storeA.read(\"key\"))._unsafeUnwrap()).toBe(\"from-a\");\n expect((await storeB.read(\"key\"))._unsafeUnwrap()).toBe(\"from-b\");\n });\n\n test(\"subscribe notifies on write\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n const values: (string | null)[] = [];\n store.subscribe(\"key1\", (v) => values.push(v));\n\n await store.write(\"key1\", \"hello\");\n expect(values).toEqual([\"hello\"]);\n });\n\n test(\"subscribe notifies on clear\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n const values: (string | null)[] = [];\n await store.write(\"key1\", \"hello\");\n store.subscribe(\"key1\", (v) => values.push(v));\n\n await store.clear(\"key1\");\n expect(values).toEqual([null]);\n });\n\n test(\"unsubscribe stops notifications\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n const values: (string | null)[] = [];\n const unsub = store.subscribe(\"key1\", (v) => values.push(v));\n\n await store.write(\"key1\", \"first\");\n unsub();\n await store.write(\"key1\", \"second\");\n\n expect(values).toEqual([\"first\"]);\n });\n\n test(\"subscriber errors do not break other subscribers\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n const values: string[] = [];\n store.subscribe(\"key1\", () => {\n throw new Error(\"boom\");\n });\n store.subscribe(\"key1\", (v) => {\n if (v) values.push(v);\n });\n\n await store.write(\"key1\", \"hello\");\n expect(values).toEqual([\"hello\"]);\n });\n\n test(\"sanitizes special characters in keys\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n await store.write(\"key/with:special chars!\", \"value\");\n const result = await store.read(\"key/with:special chars!\");\n expect(result._unsafeUnwrap()).toBe(\"value\");\n });\n\n test(\"handles JSON values\", async () => {\n const store = createNodeStorageAdapter(\"test\", testDir);\n const obj = { name: \"test\", count: 42, nested: { ok: true } };\n await store.write(\"json\", JSON.stringify(obj));\n const raw = (await store.read(\"json\"))._unsafeUnwrap();\n expect(JSON.parse(raw!)).toEqual(obj);\n });\n });\n\n describe(\"toError\", () => {\n test(\"returns Error instances unchanged\", () => {\n const original = new TypeError(\"boom\");\n expect(toError(original)).toBe(original);\n });\n\n test(\"wraps non-Error string values\", () => {\n const result = toError(\"primitive failure\");\n expect(result).toBeInstanceOf(Error);\n expect(result.message).toBe(\"primitive failure\");\n });\n\n test(\"wraps non-Error nullish values\", () => {\n const result = toError(null);\n expect(result).toBeInstanceOf(Error);\n expect(result.message).toBe(\"null\");\n });\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { UserSession } from '@novasamatech/host-papp';
|
|
2
|
+
import { ResultAsync } from 'neverthrow';
|
|
3
|
+
import { PolkadotSigner } from 'polkadot-api';
|
|
4
|
+
import { T as TerminalAdapter } from './adapter-Dt3ryKnw.js';
|
|
5
|
+
|
|
6
|
+
/** One cached allowance entry. Hex strings are 0x-prefixed. */
|
|
7
|
+
type CachedAllocation = {
|
|
8
|
+
tag: "BulletInAllowance";
|
|
9
|
+
slotAccountKey: string;
|
|
10
|
+
} | {
|
|
11
|
+
tag: "StatementStoreAllowance";
|
|
12
|
+
slotAccountKey: string;
|
|
13
|
+
} | {
|
|
14
|
+
tag: "SmartContractAllowance";
|
|
15
|
+
dest: number;
|
|
16
|
+
} | {
|
|
17
|
+
tag: "AutoSigning";
|
|
18
|
+
productDerivationSecret: string;
|
|
19
|
+
productRootPrivateKey: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* `PolkadotSigner` backed by the cached slot account key. Returns `null`
|
|
24
|
+
* when nothing's cached for `(adapter.appId, resource)`. Throws for SC
|
|
25
|
+
* and AutoSigning (no slot account key — see module docstring).
|
|
26
|
+
*
|
|
27
|
+
* Signs locally with sr25519 — no wallet round-trip on the hot path.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const signer = await createSlotAccountSigner(adapter, {
|
|
32
|
+
* tag: "BulletInAllowance", value: undefined,
|
|
33
|
+
* });
|
|
34
|
+
* if (signer) await tx.submitAndWatch(signer);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function createSlotAccountSigner(adapter: TerminalAdapter, resource: AllocatableResource): Promise<PolkadotSigner | null>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Host-runner facet of `@parity/product-sdk-terminal`. A CLI using this
|
|
41
|
+
* package plays the Host role per RFC-10; this module implements the
|
|
42
|
+
* three §Stakeholders Host responsibilities (AP client, cache, signer)
|
|
43
|
+
* over `@novasamatech/host-papp@0.7.7`'s `UserSession`.
|
|
44
|
+
*
|
|
45
|
+
* @module
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
type ResourceAllocationRequest = Parameters<UserSession["requestResourceAllocation"]>[0];
|
|
49
|
+
/**
|
|
50
|
+
* One resource a Host can request from the Account Holder. AutoSigning
|
|
51
|
+
* currently returns `NotAvailable` on both Android and iOS wallets.
|
|
52
|
+
*/
|
|
53
|
+
type AllocatableResource = ResourceAllocationRequest["resources"][number];
|
|
54
|
+
/**
|
|
55
|
+
* `"Ignore"`: return existing keys if any, else allocate one slot.
|
|
56
|
+
* `"Increase"`: add one slot to an existing allowance account.
|
|
57
|
+
*/
|
|
58
|
+
type OnExistingAllowancePolicy = ResourceAllocationRequest["onExisting"];
|
|
59
|
+
type ExtractOk<R> = R extends ResultAsync<infer T, unknown> ? T : never;
|
|
60
|
+
/**
|
|
61
|
+
* Per-resource outcome. `Allocated.value` carries the materialized payload
|
|
62
|
+
* (slot account key for Bulletin/SSS; subtree key + secret for AutoSigning;
|
|
63
|
+
* undefined for SC). Each entry is independent — no rollback on partial
|
|
64
|
+
* success.
|
|
65
|
+
*/
|
|
66
|
+
type ApAllocationOutcome = ExtractOk<ReturnType<UserSession["requestResourceAllocation"]>>[number];
|
|
67
|
+
interface RequestResourceAllocationOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Override the auto-picked `onExisting`. Default: `Ignore` unless every
|
|
70
|
+
* requested resource is a cached slot-table variant, then `Increase`.
|
|
71
|
+
*/
|
|
72
|
+
onExisting?: OnExistingAllowancePolicy;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Send the AP `request_resource_allocation` message over the paired
|
|
76
|
+
* session; block on the user's mobile dialog; return outcomes in request
|
|
77
|
+
* order. Granted key material is cached on disk so subsequent calls skip
|
|
78
|
+
* the wallet prompt.
|
|
79
|
+
*
|
|
80
|
+
* @throws If the session call fails (transport, timeout, AH protocol error).
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* const [session] = adapter.sessions.sessions.read();
|
|
85
|
+
* const outcomes = await requestResourceAllocation(session, adapter, [
|
|
86
|
+
* { tag: "BulletInAllowance", value: undefined },
|
|
87
|
+
* ]);
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
declare function requestResourceAllocation(session: UserSession, adapter: TerminalAdapter, resources: AllocatableResource[], options?: RequestResourceAllocationOptions): Promise<ApAllocationOutcome[]>;
|
|
91
|
+
/**
|
|
92
|
+
* Read a cached allowance entry without going over the wire. Returns
|
|
93
|
+
* `null` if nothing's cached for `(adapter.appId, resource)`. For
|
|
94
|
+
* `SmartContractAllowance`, the same `dest` must be passed — entries
|
|
95
|
+
* are keyed per-dest.
|
|
96
|
+
*/
|
|
97
|
+
declare function getCachedAllocation(adapter: TerminalAdapter, resource: AllocatableResource): Promise<CachedAllocation | null>;
|
|
98
|
+
/**
|
|
99
|
+
* Cache hit → return signer. Cache miss → call
|
|
100
|
+
* {@link requestResourceAllocation} for `[resource]`, then build the signer.
|
|
101
|
+
* Throws on `Rejected` / `NotAvailable`.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* const signer = await ensureSlotAccountSigner(session, adapter, {
|
|
106
|
+
* tag: "BulletInAllowance", value: undefined,
|
|
107
|
+
* });
|
|
108
|
+
* await someBulletinTx.submitAndWatch(signer);
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function ensureSlotAccountSigner(session: UserSession, adapter: TerminalAdapter, resource: AllocatableResource): Promise<PolkadotSigner>;
|
|
112
|
+
|
|
113
|
+
export { type AllocatableResource, type ApAllocationOutcome, type CachedAllocation, type OnExistingAllowancePolicy, type RequestResourceAllocationOptions, createSlotAccountSigner, ensureSlotAccountSigner, getCachedAllocation, requestResourceAllocation };
|