@parity/product-sdk-terminal 0.3.1 → 0.4.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 +81 -2
- package/dist/index.d.ts +78 -2
- package/dist/index.js +251 -1
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +38 -29
- package/dist/testing.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -112,9 +112,88 @@ File-based storage adapter for Node.js. Data persists in `storageDir` (defaults
|
|
|
112
112
|
|
|
113
113
|
Waits for the session list to emit at least one entry, or resolves with `[]` after `timeoutMs`.
|
|
114
114
|
|
|
115
|
-
## Allowance
|
|
115
|
+
## Allowance signers — the canonical path
|
|
116
116
|
|
|
117
|
-
For CLIs that need to write to Bulletin
|
|
117
|
+
For CLIs that need to write to Bulletin or publish to the Statement Store: ask the paired wallet for an allowance slot, get a `PolkadotSigner` back, sign extrinsics with it. This is what most consumers want.
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import {
|
|
121
|
+
createTerminalAdapter,
|
|
122
|
+
getBulletinSigner,
|
|
123
|
+
getStatementStoreProver,
|
|
124
|
+
waitForSessions,
|
|
125
|
+
} from "@parity/product-sdk-terminal";
|
|
126
|
+
|
|
127
|
+
const adapter = createTerminalAdapter({ appId: "my-cli" });
|
|
128
|
+
|
|
129
|
+
// ...QR pair the phone, wait for the session...
|
|
130
|
+
await waitForSessions(adapter);
|
|
131
|
+
|
|
132
|
+
// First call prompts the wallet for an allowance slot; subsequent calls
|
|
133
|
+
// return the cached slot key — no wire round-trip.
|
|
134
|
+
const bulletinSigner = await getBulletinSigner(adapter, "my-cli.dot");
|
|
135
|
+
await bulletinClient.tx.TransactionStorage.store({ data }).signAndSubmit(bulletinSigner);
|
|
136
|
+
|
|
137
|
+
// Same idea, returns a StatementProver for `@novasamatech/statement-store` writes.
|
|
138
|
+
const prover = await getStatementStoreProver(adapter, "my-cli.dot");
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `getBulletinSigner(adapter, productId, sessionId?): Promise<PolkadotSigner>`
|
|
142
|
+
|
|
143
|
+
### `getStatementStoreProver(adapter, productId, sessionId?): Promise<StatementProver>`
|
|
144
|
+
|
|
145
|
+
Both default `sessionId` to the only paired session. With zero or more than one paired sessions and no explicit id, both throw `AllowanceError` with `reason: 'NoSession'`. Pass an explicit `sessionId` to disambiguate.
|
|
146
|
+
|
|
147
|
+
Failures from the host (`Rejected`, `NotAvailable`, codec drift) surface as `AllowanceError`:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { AllowanceError } from "@parity/product-sdk-terminal";
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const signer = await getBulletinSigner(adapter, "my-cli.dot");
|
|
154
|
+
} catch (err) {
|
|
155
|
+
if (err instanceof AllowanceError && err.reason === "Rejected") {
|
|
156
|
+
// user denied the allowance request on the phone
|
|
157
|
+
}
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Behind the scenes both helpers wrap `adapter.allowance` (inherited from host-papp's `PappAdapter`) and unwrap its neverthrow `ResultAsync` into a throwing `Promise`. If you want to keep neverthrow's `.match()` / `.mapErr()` ergonomics or need explicit multi-session handling, call `adapter.allowance.getBulletinSigner(sessionId, productId)` / `getStatementStoreProver(...)` directly.
|
|
163
|
+
|
|
164
|
+
### Cache-only probes — `hasBulletinAllowance` / `hasStatementStoreAllowance`
|
|
165
|
+
|
|
166
|
+
For paths that must run silently — login health checks, "would calling `getBulletinSigner` prompt the phone?" decisions, readiness probes — use the cache-only variants. They read host-papp's on-disk allowance file directly and **never** trigger a wallet prompt.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import {
|
|
170
|
+
getBulletinSigner,
|
|
171
|
+
hasBulletinAllowance,
|
|
172
|
+
} from "@parity/product-sdk-terminal";
|
|
173
|
+
|
|
174
|
+
if (await hasBulletinAllowance(adapter, "my-cli.dot")) {
|
|
175
|
+
// happy path — fetch the signer without risking a wallet prompt
|
|
176
|
+
const signer = await getBulletinSigner(adapter, "my-cli.dot");
|
|
177
|
+
} else {
|
|
178
|
+
// tell the user a wallet prompt will fire, then call getBulletinSigner
|
|
179
|
+
console.log("Approve the allowance request on your phone…");
|
|
180
|
+
const signer = await getBulletinSigner(adapter, "my-cli.dot");
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### `hasBulletinAllowance(adapter, productId, sessionId?): Promise<boolean>`
|
|
185
|
+
|
|
186
|
+
#### `hasStatementStoreAllowance(adapter, productId, sessionId?): Promise<boolean>`
|
|
187
|
+
|
|
188
|
+
Resolves `true` when a slot key for `(sessionId, productId, resource)` is already cached on disk; `false` when it is not (file absent, or present with no matching entry). Same `sessionId` defaulting as the fetching helpers: defaults to the only paired session, throws `AllowanceError('NoSession')` for zero or multiple paired sessions without an explicit id.
|
|
189
|
+
|
|
190
|
+
Rejects only on decrypt or decode failures — a corrupted cache file is a real failure, not a "no allowance" signal.
|
|
191
|
+
|
|
192
|
+
> The cache-only check uses an internal mirror of host-papp's `AllowanceRepository` codec because host-papp doesn't expose a public probe today. We'll switch to the upstream API when one ships; the public surface (`hasBulletinAllowance` / `hasStatementStoreAllowance`) won't change.
|
|
193
|
+
|
|
194
|
+
## Allowance management — manual cache + AP control (`./host` subpath)
|
|
195
|
+
|
|
196
|
+
For CLIs that need finer control — pre-allocating multiple resources in one wallet prompt, inspecting the cache before round-tripping, or building a signer over a cached key without consulting the wallet — the `@parity/product-sdk-terminal/host` subpath exposes the lower-level 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). Most CLIs don't need this — start with `getBulletinSigner` / `getStatementStoreProver` above.
|
|
118
197
|
|
|
119
198
|
```ts
|
|
120
199
|
import {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { T as TerminalAdapter } from './adapter-Dt3ryKnw.js';
|
|
2
2
|
export { a as TerminalAdapterOptions, c as createTerminalAdapter } from './adapter-Dt3ryKnw.js';
|
|
3
3
|
import { UserSession } from '@novasamatech/host-papp';
|
|
4
|
-
export { HostMetadata, PairingStatus, PappAdapter, SS_PASEO_STABLE_STAGE_ENDPOINTS, SS_STABLE_STAGE_ENDPOINTS, SigningPayloadRequest, SigningPayloadResponse, SigningRawRequest, StoredUserSession, UserSession } from '@novasamatech/host-papp';
|
|
4
|
+
export { AllowanceError, AllowanceErrorReason, AllowanceService, HostMetadata, PairingStatus, PappAdapter, SS_PASEO_STABLE_STAGE_ENDPOINTS, SS_STABLE_STAGE_ENDPOINTS, SigningPayloadRequest, SigningPayloadResponse, SigningRawRequest, StoredUserSession, UserSession } from '@novasamatech/host-papp';
|
|
5
5
|
import { PolkadotSigner } from 'polkadot-api';
|
|
6
|
+
import { StatementProver } from '@novasamatech/statement-store';
|
|
6
7
|
import { StorageAdapter } from '@novasamatech/storage-adapter';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -100,6 +101,81 @@ declare function createSessionSigner(session: UserSession, adapter: TerminalAdap
|
|
|
100
101
|
*/
|
|
101
102
|
declare function createSessionSignerForAccount(session: UserSession, ref: ProductAccountRef): PolkadotSigner;
|
|
102
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Get a `PolkadotSigner` for a Bulletin allowance slot.
|
|
106
|
+
*
|
|
107
|
+
* Allocates an allowance slot via the paired wallet (or returns the cached
|
|
108
|
+
* one), derives the slot-account keypair, and returns a `PolkadotSigner`
|
|
109
|
+
* that signs Bulletin extrinsics with it. Replaces the manual
|
|
110
|
+
* `requestResourceAllocation` + `createSlotAccountSigner` two-step for the
|
|
111
|
+
* common case.
|
|
112
|
+
*
|
|
113
|
+
* @param adapter Terminal adapter.
|
|
114
|
+
* @param productId The product id the slot is allocated under. Passed to the
|
|
115
|
+
* host as the calling product id in the allowance request.
|
|
116
|
+
* @param sessionId Paired session to allocate against. Defaults to the only
|
|
117
|
+
* paired session; throws `AllowanceError('NoSession')` when zero or more
|
|
118
|
+
* than one sessions are paired and no explicit id is supplied.
|
|
119
|
+
* @throws {AllowanceError} On rejection, missing session, host-side failure,
|
|
120
|
+
* or unexpected response shape.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* import { createTerminalAdapter, getBulletinSigner } from "@parity/product-sdk-terminal";
|
|
125
|
+
*
|
|
126
|
+
* const adapter = createTerminalAdapter({ appId: "my-cli" });
|
|
127
|
+
* // ... QR pair, wait for session ...
|
|
128
|
+
* const signer = await getBulletinSigner(adapter, "my-cli.dot");
|
|
129
|
+
* await client.bulletin.tx.TransactionStorage.store({ data }).signAndSubmit(signer);
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare function getBulletinSigner(adapter: TerminalAdapter, productId: string, sessionId?: string): Promise<PolkadotSigner>;
|
|
133
|
+
/**
|
|
134
|
+
* Get a `StatementProver` for a statement-store allowance slot.
|
|
135
|
+
*
|
|
136
|
+
* Allocates an allowance slot via the paired wallet (or returns the cached
|
|
137
|
+
* one) and returns the upstream `StatementProver` for the slot. Use when
|
|
138
|
+
* publishing statements through `@novasamatech/statement-store` without
|
|
139
|
+
* holding a long-lived key yourself.
|
|
140
|
+
*
|
|
141
|
+
* @param adapter Terminal adapter.
|
|
142
|
+
* @param productId The product id the slot is allocated under.
|
|
143
|
+
* @param sessionId Paired session to allocate against. Defaults to the only
|
|
144
|
+
* paired session; throws `AllowanceError('NoSession')` when zero or more
|
|
145
|
+
* than one sessions are paired and no explicit id is supplied.
|
|
146
|
+
* @throws {AllowanceError} On rejection, missing session, host-side failure,
|
|
147
|
+
* or unexpected response shape.
|
|
148
|
+
*/
|
|
149
|
+
declare function getStatementStoreProver(adapter: TerminalAdapter, productId: string, sessionId?: string): Promise<StatementProver>;
|
|
150
|
+
/**
|
|
151
|
+
* Cache-only probe for a Bulletin allowance slot. Resolves `true` when a
|
|
152
|
+
* slot key for `(sessionId, productId, bulletin)` is already cached on
|
|
153
|
+
* disk; `false` when it is not. Never prompts the paired wallet.
|
|
154
|
+
*
|
|
155
|
+
* Pair with {@link getBulletinSigner} for the "check first, fetch only
|
|
156
|
+
* if needed" flow:
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* if (await hasBulletinAllowance(adapter, "my-cli.dot")) {
|
|
161
|
+
* // happy path — fetch the signer without risking a wallet prompt
|
|
162
|
+
* const signer = await getBulletinSigner(adapter, "my-cli.dot");
|
|
163
|
+
* } else {
|
|
164
|
+
* // tell the user a wallet prompt will fire, then call getBulletinSigner
|
|
165
|
+
* }
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
declare function hasBulletinAllowance(adapter: TerminalAdapter, productId: string, sessionId?: string): Promise<boolean>;
|
|
169
|
+
/**
|
|
170
|
+
* Cache-only probe for a Statement Store allowance slot. Resolves `true`
|
|
171
|
+
* when a slot key for `(sessionId, productId, statementStore)` is already
|
|
172
|
+
* cached on disk; `false` when it is not. Never prompts the paired wallet.
|
|
173
|
+
*
|
|
174
|
+
* Pair with {@link getStatementStoreProver} for the "check first, fetch
|
|
175
|
+
* only if needed" flow.
|
|
176
|
+
*/
|
|
177
|
+
declare function hasStatementStoreAllowance(adapter: TerminalAdapter, productId: string, sessionId?: string): Promise<boolean>;
|
|
178
|
+
|
|
103
179
|
/**
|
|
104
180
|
* Helpers for working with persisted sessions on a `TerminalAdapter`.
|
|
105
181
|
*/
|
|
@@ -152,4 +228,4 @@ declare function renderQrCode(data: string, options?: QrRenderOptions): Promise<
|
|
|
152
228
|
*/
|
|
153
229
|
declare function createNodeStorageAdapter(appId: string, storageDir?: string): StorageAdapter;
|
|
154
230
|
|
|
155
|
-
export { type ProductAccountRef, type QrRenderOptions, TerminalAdapter, createNodeStorageAdapter, createSessionSigner, createSessionSignerForAccount, renderQrCode, waitForSessions };
|
|
231
|
+
export { type ProductAccountRef, type QrRenderOptions, TerminalAdapter, createNodeStorageAdapter, createSessionSigner, createSessionSignerForAccount, getBulletinSigner, getStatementStoreProver, hasBulletinAllowance, hasStatementStoreAllowance, renderQrCode, waitForSessions };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createNodeStorageAdapter
|
|
2
|
+
createNodeStorageAdapter,
|
|
3
|
+
sanitizeKey
|
|
3
4
|
} from "./chunk-GHOFKVUG.js";
|
|
4
5
|
|
|
5
6
|
// src/adapter.ts
|
|
@@ -616,6 +617,250 @@ var makeSession2;
|
|
|
616
617
|
var ext2;
|
|
617
618
|
var fakeAdapter2;
|
|
618
619
|
|
|
620
|
+
// src/allowance.ts
|
|
621
|
+
import { AllowanceError } from "@novasamatech/host-papp";
|
|
622
|
+
|
|
623
|
+
// src/allowance-cache.ts
|
|
624
|
+
import { gcm } from "@noble/ciphers/aes.js";
|
|
625
|
+
import { blake2b } from "@noble/hashes/blake2.js";
|
|
626
|
+
import { fromHex, toHex } from "@polkadot-api/utils";
|
|
627
|
+
import { readFile, writeFile } from "fs/promises";
|
|
628
|
+
import { join } from "path";
|
|
629
|
+
import { Bytes, Enum, Struct, Vector, _void, str } from "scale-ts";
|
|
630
|
+
var AllowanceResourceKindCodec = Enum({
|
|
631
|
+
bulletin: _void,
|
|
632
|
+
statementStore: _void
|
|
633
|
+
});
|
|
634
|
+
var StoredAllowanceEntryCodec = Struct({
|
|
635
|
+
productId: str,
|
|
636
|
+
resource: AllowanceResourceKindCodec,
|
|
637
|
+
slotAccountKey: Bytes()
|
|
638
|
+
});
|
|
639
|
+
var StoredAllowancesCodec = Vector(StoredAllowanceEntryCodec);
|
|
640
|
+
function getAes(appId) {
|
|
641
|
+
const key = blake2b(new TextEncoder().encode(appId), { dkLen: 16 });
|
|
642
|
+
const nonce = blake2b(new TextEncoder().encode("nonce"), { dkLen: 32 });
|
|
643
|
+
return gcm(key, nonce);
|
|
644
|
+
}
|
|
645
|
+
function allowanceFilePath(storageDir, appId, sessionId) {
|
|
646
|
+
return join(storageDir, `${sanitizeKey(appId, `AllowanceKeys_${sessionId}`)}.json`);
|
|
647
|
+
}
|
|
648
|
+
async function readStoredAllowances(storageDir, appId, sessionId) {
|
|
649
|
+
let hex;
|
|
650
|
+
try {
|
|
651
|
+
hex = await readFile(allowanceFilePath(storageDir, appId, sessionId), "utf-8");
|
|
652
|
+
} catch (err) {
|
|
653
|
+
if (err.code === "ENOENT") {
|
|
654
|
+
return [];
|
|
655
|
+
}
|
|
656
|
+
throw err;
|
|
657
|
+
}
|
|
658
|
+
const decrypted = getAes(appId).decrypt(fromHex(hex));
|
|
659
|
+
return StoredAllowancesCodec.dec(decrypted);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// src/allowance.ts
|
|
663
|
+
function resolveSessionId(adapter, sessionId) {
|
|
664
|
+
if (sessionId) return sessionId;
|
|
665
|
+
const sessions = adapter.sessions.sessions.read();
|
|
666
|
+
if (sessions.length === 0) {
|
|
667
|
+
throw new AllowanceError("NoSession", "No paired session \u2014 pair a phone first.");
|
|
668
|
+
}
|
|
669
|
+
if (sessions.length > 1) {
|
|
670
|
+
throw new AllowanceError(
|
|
671
|
+
"NoSession",
|
|
672
|
+
`Multiple paired sessions (${sessions.length}) \u2014 pass an explicit sessionId.`
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
return sessions[0].id;
|
|
676
|
+
}
|
|
677
|
+
async function getBulletinSigner(adapter, productId, sessionId) {
|
|
678
|
+
const resolvedSessionId = resolveSessionId(adapter, sessionId);
|
|
679
|
+
return adapter.allowance.getBulletinSigner(resolvedSessionId, productId).match(
|
|
680
|
+
(signer) => signer,
|
|
681
|
+
(err) => {
|
|
682
|
+
throw err;
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
async function getStatementStoreProver(adapter, productId, sessionId) {
|
|
687
|
+
const resolvedSessionId = resolveSessionId(adapter, sessionId);
|
|
688
|
+
return adapter.allowance.getStatementStoreProver(resolvedSessionId, productId).match(
|
|
689
|
+
(prover) => prover,
|
|
690
|
+
(err) => {
|
|
691
|
+
throw err;
|
|
692
|
+
}
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
async function hasAllowanceForResource(adapter, productId, resource, sessionId) {
|
|
696
|
+
const resolvedSessionId = resolveSessionId(adapter, sessionId);
|
|
697
|
+
if (adapter.storageDir === void 0) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
const entries = await readStoredAllowances(
|
|
701
|
+
adapter.storageDir,
|
|
702
|
+
adapter.appId,
|
|
703
|
+
resolvedSessionId
|
|
704
|
+
);
|
|
705
|
+
return entries.some(
|
|
706
|
+
(entry) => entry.productId === productId && entry.resource.tag === resource
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
async function hasBulletinAllowance(adapter, productId, sessionId) {
|
|
710
|
+
return hasAllowanceForResource(adapter, productId, "bulletin", sessionId);
|
|
711
|
+
}
|
|
712
|
+
async function hasStatementStoreAllowance(adapter, productId, sessionId) {
|
|
713
|
+
return hasAllowanceForResource(adapter, productId, "statementStore", sessionId);
|
|
714
|
+
}
|
|
715
|
+
if (void 0) {
|
|
716
|
+
let makeAdapter = function(opts) {
|
|
717
|
+
return {
|
|
718
|
+
sessions: {
|
|
719
|
+
sessions: {
|
|
720
|
+
read: () => opts.sessions
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
allowance: {
|
|
724
|
+
getBulletinSigner: opts.getBulletinSigner ?? vi.fn(() => ok({})),
|
|
725
|
+
getStatementStoreProver: opts.getStatementStoreProver ?? vi.fn(() => ok({}))
|
|
726
|
+
}
|
|
727
|
+
// The convenience never touches the rest of the adapter; leave it unstubbed.
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
makeAdapter2 = makeAdapter;
|
|
731
|
+
const { describe, test, expect, vi } = void 0;
|
|
732
|
+
const { ok, err } = await null;
|
|
733
|
+
describe("getBulletinSigner \u2014 sessionId defaulting", () => {
|
|
734
|
+
test("uses the only paired session when sessionId is omitted", async () => {
|
|
735
|
+
const fakeSigner = { _tag: "signer" };
|
|
736
|
+
const getBulletinSignerFn = vi.fn(() => ok(fakeSigner));
|
|
737
|
+
const adapter = makeAdapter({
|
|
738
|
+
sessions: [{ id: "sess-only" }],
|
|
739
|
+
getBulletinSigner: getBulletinSignerFn
|
|
740
|
+
});
|
|
741
|
+
const result = await getBulletinSigner(adapter, "my-app.dot");
|
|
742
|
+
expect(result).toBe(fakeSigner);
|
|
743
|
+
expect(getBulletinSignerFn).toHaveBeenCalledWith("sess-only", "my-app.dot");
|
|
744
|
+
});
|
|
745
|
+
test("uses the explicit sessionId when supplied", async () => {
|
|
746
|
+
const fakeSigner = { _tag: "signer" };
|
|
747
|
+
const getBulletinSignerFn = vi.fn(() => ok(fakeSigner));
|
|
748
|
+
const adapter = makeAdapter({
|
|
749
|
+
sessions: [{ id: "sess-a" }, { id: "sess-b" }],
|
|
750
|
+
getBulletinSigner: getBulletinSignerFn
|
|
751
|
+
});
|
|
752
|
+
await getBulletinSigner(adapter, "my-app.dot", "sess-b");
|
|
753
|
+
expect(getBulletinSignerFn).toHaveBeenCalledWith("sess-b", "my-app.dot");
|
|
754
|
+
});
|
|
755
|
+
test("throws AllowanceError('NoSession') when zero sessions and no id", async () => {
|
|
756
|
+
const adapter = makeAdapter({ sessions: [] });
|
|
757
|
+
await expect(getBulletinSigner(adapter, "my-app.dot")).rejects.toBeInstanceOf(
|
|
758
|
+
AllowanceError
|
|
759
|
+
);
|
|
760
|
+
await expect(getBulletinSigner(adapter, "my-app.dot")).rejects.toMatchObject({
|
|
761
|
+
reason: "NoSession"
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
test("throws AllowanceError('NoSession') when multiple sessions and no id", async () => {
|
|
765
|
+
const adapter = makeAdapter({ sessions: [{ id: "a" }, { id: "b" }] });
|
|
766
|
+
await expect(getBulletinSigner(adapter, "my-app.dot")).rejects.toBeInstanceOf(
|
|
767
|
+
AllowanceError
|
|
768
|
+
);
|
|
769
|
+
await expect(getBulletinSigner(adapter, "my-app.dot")).rejects.toMatchObject({
|
|
770
|
+
reason: "NoSession"
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
describe("getBulletinSigner \u2014 error unwrapping", () => {
|
|
775
|
+
test("rethrows the AllowanceError from the underlying service as a thrown exception", async () => {
|
|
776
|
+
const underlyingErr = new AllowanceError("Rejected", "user said no");
|
|
777
|
+
const adapter = makeAdapter({
|
|
778
|
+
sessions: [{ id: "s" }],
|
|
779
|
+
getBulletinSigner: vi.fn(() => err(underlyingErr))
|
|
780
|
+
});
|
|
781
|
+
await expect(getBulletinSigner(adapter, "p")).rejects.toBe(underlyingErr);
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
describe("getStatementStoreProver \u2014 same defaulting", () => {
|
|
785
|
+
test("uses the only paired session when sessionId is omitted", async () => {
|
|
786
|
+
const fakeProver = { _tag: "prover" };
|
|
787
|
+
const getStatementStoreProverFn = vi.fn(() => ok(fakeProver));
|
|
788
|
+
const adapter = makeAdapter({
|
|
789
|
+
sessions: [{ id: "sess-only" }],
|
|
790
|
+
getStatementStoreProver: getStatementStoreProverFn
|
|
791
|
+
});
|
|
792
|
+
const result = await getStatementStoreProver(adapter, "my-app.dot");
|
|
793
|
+
expect(result).toBe(fakeProver);
|
|
794
|
+
expect(getStatementStoreProverFn).toHaveBeenCalledWith("sess-only", "my-app.dot");
|
|
795
|
+
});
|
|
796
|
+
test("uses the explicit sessionId when supplied", async () => {
|
|
797
|
+
const fakeProver = { _tag: "prover" };
|
|
798
|
+
const getStatementStoreProverFn = vi.fn(() => ok(fakeProver));
|
|
799
|
+
const adapter = makeAdapter({
|
|
800
|
+
sessions: [{ id: "sess-a" }, { id: "sess-b" }],
|
|
801
|
+
getStatementStoreProver: getStatementStoreProverFn
|
|
802
|
+
});
|
|
803
|
+
await getStatementStoreProver(adapter, "my-app.dot", "sess-b");
|
|
804
|
+
expect(getStatementStoreProverFn).toHaveBeenCalledWith("sess-b", "my-app.dot");
|
|
805
|
+
});
|
|
806
|
+
test("throws AllowanceError('NoSession') when zero sessions and no id", async () => {
|
|
807
|
+
const adapter = makeAdapter({ sessions: [] });
|
|
808
|
+
await expect(getStatementStoreProver(adapter, "my-app.dot")).rejects.toBeInstanceOf(
|
|
809
|
+
AllowanceError
|
|
810
|
+
);
|
|
811
|
+
await expect(getStatementStoreProver(adapter, "my-app.dot")).rejects.toMatchObject({
|
|
812
|
+
reason: "NoSession"
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
test("throws AllowanceError('NoSession') when multiple sessions and no id", async () => {
|
|
816
|
+
const adapter = makeAdapter({ sessions: [{ id: "a" }, { id: "b" }] });
|
|
817
|
+
await expect(getStatementStoreProver(adapter, "my-app.dot")).rejects.toBeInstanceOf(
|
|
818
|
+
AllowanceError
|
|
819
|
+
);
|
|
820
|
+
await expect(getStatementStoreProver(adapter, "my-app.dot")).rejects.toMatchObject({
|
|
821
|
+
reason: "NoSession"
|
|
822
|
+
});
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
describe("getStatementStoreProver \u2014 error unwrapping", () => {
|
|
826
|
+
test("rethrows the AllowanceError from the underlying service as a thrown exception", async () => {
|
|
827
|
+
const underlyingErr = new AllowanceError("Rejected", "user said no");
|
|
828
|
+
const adapter = makeAdapter({
|
|
829
|
+
sessions: [{ id: "s" }],
|
|
830
|
+
getStatementStoreProver: vi.fn(() => err(underlyingErr))
|
|
831
|
+
});
|
|
832
|
+
await expect(getStatementStoreProver(adapter, "p")).rejects.toBe(underlyingErr);
|
|
833
|
+
});
|
|
834
|
+
});
|
|
835
|
+
describe("has*Allowance \u2014 sessionId defaulting + storageDir fallback", () => {
|
|
836
|
+
test("hasBulletinAllowance throws AllowanceError('NoSession') when zero sessions and no id", async () => {
|
|
837
|
+
const adapter = makeAdapter({ sessions: [] });
|
|
838
|
+
await expect(hasBulletinAllowance(adapter, "p")).rejects.toBeInstanceOf(AllowanceError);
|
|
839
|
+
await expect(hasBulletinAllowance(adapter, "p")).rejects.toMatchObject({
|
|
840
|
+
reason: "NoSession"
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
test("hasBulletinAllowance returns false (conservative) when storageDir is undefined", async () => {
|
|
844
|
+
const adapter = makeAdapter({ sessions: [{ id: "sess-only" }] });
|
|
845
|
+
expect(await hasBulletinAllowance(adapter, "p")).toBe(false);
|
|
846
|
+
});
|
|
847
|
+
test("hasStatementStoreAllowance throws AllowanceError('NoSession') when zero sessions and no id", async () => {
|
|
848
|
+
const adapter = makeAdapter({ sessions: [] });
|
|
849
|
+
await expect(hasStatementStoreAllowance(adapter, "p")).rejects.toBeInstanceOf(
|
|
850
|
+
AllowanceError
|
|
851
|
+
);
|
|
852
|
+
});
|
|
853
|
+
test("hasStatementStoreAllowance returns false (conservative) when storageDir is undefined", async () => {
|
|
854
|
+
const adapter = makeAdapter({ sessions: [{ id: "sess-only" }] });
|
|
855
|
+
expect(await hasStatementStoreAllowance(adapter, "p")).toBe(false);
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
var makeAdapter2;
|
|
860
|
+
|
|
861
|
+
// src/index.ts
|
|
862
|
+
import { AllowanceError as AllowanceError2 } from "@novasamatech/host-papp";
|
|
863
|
+
|
|
619
864
|
// src/sessions.ts
|
|
620
865
|
function waitForSessions(adapter, timeoutMs = 3e3) {
|
|
621
866
|
return new Promise((resolve) => {
|
|
@@ -763,12 +1008,17 @@ if (void 0) {
|
|
|
763
1008
|
});
|
|
764
1009
|
}
|
|
765
1010
|
export {
|
|
1011
|
+
AllowanceError2 as AllowanceError,
|
|
766
1012
|
SS_PASEO_STABLE_STAGE_ENDPOINTS,
|
|
767
1013
|
SS_STABLE_STAGE_ENDPOINTS,
|
|
768
1014
|
createNodeStorageAdapter,
|
|
769
1015
|
createSessionSigner,
|
|
770
1016
|
createSessionSignerForAccount,
|
|
771
1017
|
createTerminalAdapter,
|
|
1018
|
+
getBulletinSigner,
|
|
1019
|
+
getStatementStoreProver,
|
|
1020
|
+
hasBulletinAllowance,
|
|
1021
|
+
hasStatementStoreAllowance,
|
|
772
1022
|
renderQrCode,
|
|
773
1023
|
waitForSessions
|
|
774
1024
|
};
|