@parity/product-sdk-terminal 0.8.2 → 0.9.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 CHANGED
@@ -1,27 +1,33 @@
1
- import {
2
- requestResourceAllocation,
3
- sendResourceAllocation
4
- } from "./chunk-YS3GW2XW.js";
5
- import {
6
- createNodeStorageAdapter,
7
- sanitizeKey
8
- } from "./chunk-GHOFKVUG.js";
1
+ export { requestResourceAllocation, sendResourceAllocation } from './chunk-OKJQEB6P.js';
2
+ import { createNodeStorageAdapter, sanitizeKey } from './chunk-GHOFKVUG.js';
3
+ export { createNodeStorageAdapter } from './chunk-GHOFKVUG.js';
4
+ import { SS_PREVIEW_STAGE_ENDPOINTS, createPappAdapter, AllowanceError } from '@novasamatech/host-papp';
5
+ export { AllowanceError, SS_STABLE_STAGE_ENDPOINTS } from '@novasamatech/host-papp';
6
+ import { createLazyClient, createPapiStatementStoreAdapter, NoAllowanceError } from '@novasamatech/statement-store';
7
+ import { createLogger } from '@parity/product-sdk-logger';
8
+ import { getWsProvider } from '@polkadot-api/ws-provider';
9
+ import { unifyMetadata, decAnyMetadata } from '@polkadot-api/substrate-bindings';
10
+ import { deriveProductAccountPublicKey } from '@parity/product-sdk-keys';
11
+ import { AllowanceExpiredError } from '@parity/product-sdk-signer/errors';
12
+ export { AllowanceExpiredError, SignerError } from '@parity/product-sdk-signer/errors';
13
+ import { fromHex, toHex } from '@polkadot-api/utils';
14
+ import { gcm } from '@noble/ciphers/aes.js';
15
+ import { blake2b } from '@noble/hashes/blake2.js';
16
+ import { readFile } from 'fs/promises';
17
+ import { join } from 'path';
18
+ import { Enum, _void, Struct, str, Bytes, Vector } from 'scale-ts';
19
+
20
+ var StatementStoreNetworks = {
21
+ // host-papp has no constant for this host; its `SS_PASEO_STABLE_STAGE_ENDPOINTS`
22
+ // still names the pre-rename chain.
23
+ paseo: ["wss://paseo-people-next-system-rpc.polkadot.io"],
24
+ previewnet: SS_PREVIEW_STAGE_ENDPOINTS
25
+ };
9
26
 
10
27
  // src/adapter.ts
11
- import {
12
- createPappAdapter,
13
- SS_STABLE_STAGE_ENDPOINTS,
14
- SS_PASEO_STABLE_STAGE_ENDPOINTS
15
- } from "@novasamatech/host-papp";
16
- import {
17
- createLazyClient,
18
- createPapiStatementStoreAdapter
19
- } from "@novasamatech/statement-store";
20
- import { createLogger } from "@parity/product-sdk-logger";
21
- import { getWsProvider } from "@polkadot-api/ws-provider";
22
28
  var log = createLogger("terminal");
23
29
  function createTerminalAdapter(options) {
24
- const endpoints = options.endpoints ?? SS_PASEO_STABLE_STAGE_ENDPOINTS;
30
+ const endpoints = options.endpoints ?? StatementStoreNetworks.paseo;
25
31
  const storage = createNodeStorageAdapter(options.appId, options.storageDir);
26
32
  const HEARTBEAT_NEVER_MS = 2147483647;
27
33
  const rawLazyClient = createLazyClient(
@@ -106,295 +112,6 @@ async function teardown(sessions, lazyClient) {
106
112
  restore();
107
113
  }
108
114
  }
109
- if (void 0) {
110
- let fakeLazyClient = function() {
111
- let teardownCalls = 0;
112
- let disconnectCalls = 0;
113
- const fake = {
114
- getClient: (() => ({})),
115
- getRequestFn: (() => () => Promise.resolve()),
116
- getSubscribeFn: () => ((_method, _params, _onMessage, _onError) => {
117
- return () => {
118
- teardownCalls += 1;
119
- };
120
- }),
121
- disconnect: () => {
122
- disconnectCalls += 1;
123
- }
124
- };
125
- return {
126
- client: fake,
127
- get teardownCalls() {
128
- return teardownCalls;
129
- },
130
- get disconnectCalls() {
131
- return disconnectCalls;
132
- }
133
- };
134
- };
135
- fakeLazyClient2 = fakeLazyClient;
136
- const { describe, test, expect, vi } = void 0;
137
- describe("wrapLazyClient", () => {
138
- test("passes getClient / getRequestFn / disconnect through unchanged", () => {
139
- const fake = fakeLazyClient();
140
- const wrapped = wrapLazyClient(fake.client);
141
- wrapped.getClient();
142
- wrapped.disconnect();
143
- expect(fake.disconnectCalls).toBe(1);
144
- expect(typeof wrapped.getRequestFn).toBe("function");
145
- });
146
- test("getSubscribeFn returns a wrapped subscribe whose teardown invokes the inner teardown", () => {
147
- const fake = fakeLazyClient();
148
- const wrapped = wrapLazyClient(fake.client);
149
- const subscribe = wrapped.getSubscribeFn();
150
- const teardown2 = subscribe(
151
- "statement_subscribeStatement",
152
- [],
153
- () => {
154
- },
155
- () => {
156
- }
157
- );
158
- teardown2();
159
- expect(fake.teardownCalls).toBe(1);
160
- });
161
- test("awaitPendingUnsubs resolves after all wrapped teardowns settle", async () => {
162
- const fake = fakeLazyClient();
163
- const wrapped = wrapLazyClient(fake.client);
164
- const subscribe = wrapped.getSubscribeFn();
165
- for (let i = 0; i < 3; i++) {
166
- const teardown2 = subscribe(
167
- "statement_subscribeStatement",
168
- [],
169
- () => {
170
- },
171
- () => {
172
- }
173
- );
174
- teardown2();
175
- }
176
- await wrapped.awaitPendingUnsubs();
177
- expect(fake.teardownCalls).toBe(3);
178
- });
179
- test("awaitPendingUnsubs with no pending unsubs resolves immediately", async () => {
180
- const fake = fakeLazyClient();
181
- const wrapped = wrapLazyClient(fake.client);
182
- await expect(wrapped.awaitPendingUnsubs()).resolves.toBeUndefined();
183
- });
184
- test("an unsubscribe whose teardown throws is still tracked and resolved", async () => {
185
- const fake = {
186
- getClient: (() => ({})),
187
- getRequestFn: (() => () => Promise.resolve()),
188
- getSubscribeFn: () => ((_method, _params, _onMessage, _onError) => {
189
- return () => {
190
- throw new Error("teardown boom");
191
- };
192
- }),
193
- disconnect: () => {
194
- }
195
- };
196
- const wrapped = wrapLazyClient(fake);
197
- const subscribe = wrapped.getSubscribeFn();
198
- const teardown2 = subscribe(
199
- "statement_subscribeStatement",
200
- [],
201
- () => {
202
- },
203
- () => {
204
- }
205
- );
206
- try {
207
- teardown2();
208
- } catch {
209
- }
210
- await expect(wrapped.awaitPendingUnsubs()).resolves.toBeUndefined();
211
- });
212
- });
213
- describe("teardown", () => {
214
- test("orders sessions.dispose, drain, then disconnect", async () => {
215
- const order = [];
216
- const fake = fakeLazyClient();
217
- const wrapped = wrapLazyClient(fake.client);
218
- const innerDisconnect = wrapped.disconnect.bind(wrapped);
219
- wrapped.disconnect = () => {
220
- order.push("disconnect");
221
- innerDisconnect();
222
- };
223
- const sessions = {
224
- dispose: vi.fn(() => {
225
- order.push("dispose");
226
- const subscribe = wrapped.getSubscribeFn();
227
- subscribe(
228
- "statement_subscribeStatement",
229
- [],
230
- () => {
231
- },
232
- () => {
233
- }
234
- )();
235
- })
236
- };
237
- await teardown(sessions, wrapped);
238
- expect(order).toEqual(["dispose", "disconnect"]);
239
- expect(sessions.dispose).toHaveBeenCalledTimes(1);
240
- expect(fake.disconnectCalls).toBe(1);
241
- });
242
- test("disconnect runs even when there are no pending unsubs", async () => {
243
- const fake = fakeLazyClient();
244
- const wrapped = wrapLazyClient(fake.client);
245
- const sessions = { dispose: vi.fn() };
246
- await teardown(sessions, wrapped);
247
- expect(fake.disconnectCalls).toBe(1);
248
- });
249
- test("logs a warning if disconnect throws, doesn't propagate to caller", async () => {
250
- const fake = {
251
- getClient: (() => ({})),
252
- getRequestFn: (() => () => Promise.resolve()),
253
- getSubscribeFn: () => ((_method, _params, _onMessage, _onError) => () => {
254
- }),
255
- disconnect: () => {
256
- throw new Error("boom");
257
- }
258
- };
259
- const wrapped = wrapLazyClient(fake);
260
- const sessions = { dispose: vi.fn() };
261
- await expect(teardown(sessions, wrapped)).resolves.toBeUndefined();
262
- });
263
- test("awaits pending unsubs before calling disconnect", async () => {
264
- const fake = fakeLazyClient();
265
- const wrapped = wrapLazyClient(fake.client);
266
- let unsubResolved = false;
267
- const innerDisconnect = wrapped.disconnect.bind(wrapped);
268
- wrapped.disconnect = () => {
269
- expect(unsubResolved).toBe(true);
270
- innerDisconnect();
271
- };
272
- const sessions = {
273
- dispose: () => {
274
- const subscribe = wrapped.getSubscribeFn();
275
- subscribe(
276
- "statement_subscribeStatement",
277
- [],
278
- () => {
279
- },
280
- () => {
281
- }
282
- )();
283
- queueMicrotask(
284
- () => queueMicrotask(() => {
285
- unsubResolved = true;
286
- })
287
- );
288
- }
289
- };
290
- await teardown(sessions, wrapped);
291
- expect(fake.disconnectCalls).toBe(1);
292
- });
293
- test("suppresses the benign DestroyedError console.error fired during disconnect", async () => {
294
- const spy = vi.spyOn(console, "error").mockImplementation(() => {
295
- });
296
- const fake = {
297
- getClient: (() => ({})),
298
- getRequestFn: (() => () => Promise.resolve()),
299
- getSubscribeFn: () => ((_m, _p, _om, _oe) => () => {
300
- }),
301
- disconnect: () => {
302
- console.error(
303
- "Statement subscription error:",
304
- new Error("DestroyedError: Client destroyed")
305
- );
306
- }
307
- };
308
- const wrapped = wrapLazyClient(fake);
309
- await teardown({ dispose: () => {
310
- } }, wrapped);
311
- expect(spy).not.toHaveBeenCalledWith(
312
- "Statement subscription error:",
313
- expect.anything()
314
- );
315
- spy.mockRestore();
316
- });
317
- test("still surfaces a real (non-benign) console.error during disconnect", async () => {
318
- const spy = vi.spyOn(console, "error").mockImplementation(() => {
319
- });
320
- const realError = new Error("something actually went wrong");
321
- const fake = {
322
- getClient: (() => ({})),
323
- getRequestFn: (() => () => Promise.resolve()),
324
- getSubscribeFn: () => ((_m, _p, _om, _oe) => () => {
325
- }),
326
- disconnect: () => {
327
- console.error("real failure:", realError);
328
- }
329
- };
330
- const wrapped = wrapLazyClient(fake);
331
- await teardown({ dispose: () => {
332
- } }, wrapped);
333
- expect(spy).toHaveBeenCalledWith("real failure:", realError);
334
- spy.mockRestore();
335
- });
336
- test("restores console.error by the time teardown resolves", async () => {
337
- const before = console.error;
338
- const fake = fakeLazyClient();
339
- const wrapped = wrapLazyClient(fake.client);
340
- await teardown({ dispose: () => {
341
- } }, wrapped);
342
- expect(console.error).toBe(before);
343
- });
344
- test("two overlapping destroys do not strand the console.error patch", async () => {
345
- const before = console.error;
346
- const makeFake = () => wrapLazyClient({
347
- getClient: (() => ({})),
348
- getRequestFn: (() => () => Promise.resolve()),
349
- getSubscribeFn: () => ((_m, _p, _om, _oe) => () => {
350
- }),
351
- disconnect: () => {
352
- console.error(
353
- "Statement subscription error:",
354
- new Error("DestroyedError: Client destroyed")
355
- );
356
- }
357
- });
358
- await Promise.all([
359
- teardown({ dispose: () => {
360
- } }, makeFake()),
361
- teardown({ dispose: () => {
362
- } }, makeFake())
363
- ]);
364
- expect(console.error).toBe(before);
365
- });
366
- });
367
- describe("isBenignTeardownError", () => {
368
- test("matches the statement-store teardown noise", () => {
369
- expect(isBenignTeardownError(new Error("DestroyedError: Client destroyed"))).toBe(true);
370
- expect(isBenignTeardownError("Client destroyed")).toBe(true);
371
- const e = new Error("Client destroyed");
372
- e.name = "DestroyedError";
373
- expect(isBenignTeardownError(e)).toBe(true);
374
- });
375
- test("does not match a genuine error", () => {
376
- expect(isBenignTeardownError(new Error("connection refused"))).toBe(false);
377
- expect(isBenignTeardownError("BadProof")).toBe(false);
378
- expect(isBenignTeardownError(void 0)).toBe(false);
379
- });
380
- test("does not swallow the signer's own DestroyedError", () => {
381
- const signerError = new Error("SignerManager has been destroyed");
382
- signerError.name = "DestroyedError";
383
- expect(isBenignTeardownError(signerError)).toBe(false);
384
- });
385
- test("does not throw on a value String() would reject", () => {
386
- expect(() => isBenignTeardownError(/* @__PURE__ */ Object.create(null))).not.toThrow();
387
- expect(isBenignTeardownError(/* @__PURE__ */ Object.create(null))).toBe(false);
388
- });
389
- });
390
- }
391
- var fakeLazyClient2;
392
-
393
- // src/signer.ts
394
- import { NoAllowanceError } from "@novasamatech/statement-store";
395
- import { decAnyMetadata, unifyMetadata } from "@polkadot-api/substrate-bindings";
396
- import { deriveProductAccountPublicKey } from "@parity/product-sdk-keys";
397
- import { AllowanceExpiredError } from "@parity/product-sdk-signer/errors";
398
115
  function isNoAllowanceError(error) {
399
116
  for (let current = error, depth = 0; current != null && depth < 8; depth++) {
400
117
  if (current instanceof NoAllowanceError) return true;
@@ -412,6 +129,9 @@ function isNoAllowanceError(error) {
412
129
  function toAllowanceExpiredError(error) {
413
130
  return isNoAllowanceError(error) ? new AllowanceExpiredError("statementStore", error) : null;
414
131
  }
132
+ function toProductAccountId(ref) {
133
+ return [ref.productId, { tag: "Index", value: ref.derivationIndex }];
134
+ }
415
135
  function buildCreateTransactionRequest(productAccountId, callData, signedExtensions, txExtVersion) {
416
136
  const checkGenesis = signedExtensions.CheckGenesis;
417
137
  if (!checkGenesis) {
@@ -425,7 +145,8 @@ function buildCreateTransactionRequest(productAccountId, callData, signedExtensi
425
145
  value: {
426
146
  signer: productAccountId,
427
147
  // CheckGenesis carries the genesis hash as its additionalSigned (implicit) data.
428
- genesisHash: checkGenesis.additionalSigned,
148
+ // `toHex` is typed `string`; the codec wants the 0x literal type.
149
+ genesisHash: toHex(checkGenesis.additionalSigned),
429
150
  callData,
430
151
  extensions: Object.values(signedExtensions).map(
431
152
  ({ identifier, value, additionalSigned }) => ({
@@ -508,7 +229,7 @@ function deriveProductPublicKey(session, ref) {
508
229
  );
509
230
  }
510
231
  function buildSessionSigner(session, ref) {
511
- const productAccountId = [ref.productId, ref.derivationIndex];
232
+ const productAccountId = toProductAccountId(ref);
512
233
  const publicKey = ref.publicKey ?? deriveProductPublicKey(session, ref);
513
234
  return {
514
235
  publicKey,
@@ -528,391 +249,6 @@ function createSessionSigner(session, adapter, publicKey) {
528
249
  function createSessionSignerForAccount(session, ref) {
529
250
  return buildSessionSigner(session, ref);
530
251
  }
531
- if (void 0) {
532
- let makeSession = function(opts) {
533
- const accountIdBytes = opts.accountIdBytes ?? new Array(32).fill(0).map((_, i) => i);
534
- const rootAccountId = opts.rootAccountId === void 0 ? Array.from(DEV_ROOT_PUBLIC_KEY) : opts.rootAccountId;
535
- return {
536
- remoteAccount: { accountId: accountIdBytes },
537
- ...rootAccountId === null ? {} : { rootAccountId: new Uint8Array(rootAccountId) },
538
- signPayload: vi.fn(
539
- opts.signPayload ?? (async () => {
540
- throw new Error("signPayload not stubbed in this test");
541
- })
542
- ),
543
- signRaw: vi.fn(
544
- opts.signRaw ?? (async () => {
545
- throw new Error("signRaw not stubbed in this test");
546
- })
547
- ),
548
- createTransaction: vi.fn(
549
- opts.createTransaction ?? (async () => {
550
- throw new Error("createTransaction not stubbed in this test");
551
- })
552
- )
553
- };
554
- }, ext = function(identifier, value, additionalSigned) {
555
- return {
556
- identifier,
557
- value: new Uint8Array(value),
558
- additionalSigned: new Uint8Array(additionalSigned)
559
- };
560
- }, fakeAdapter = function(appId) {
561
- return { appId };
562
- };
563
- makeSession2 = makeSession, ext2 = ext, fakeAdapter2 = fakeAdapter;
564
- const { describe, test, expect, vi } = void 0;
565
- const { ok, err } = await null;
566
- const { seedToAccount } = await null;
567
- const DEV_ROOT_PUBLIC_KEY = seedToAccount(
568
- "bottom drive obey lake curtain smoke basket hold race lonely fit walk",
569
- ""
570
- ).publicKey;
571
- describe("createSessionSigner", () => {
572
- test("self-derives the product key from the session root when none is passed", () => {
573
- const walletBytes = Array.from({ length: 32 }, (_, i) => i);
574
- const signer = createSessionSigner(
575
- makeSession({
576
- accountIdBytes: walletBytes,
577
- rootAccountId: Array.from(DEV_ROOT_PUBLIC_KEY)
578
- }),
579
- fakeAdapter("test-app")
580
- );
581
- const expected = deriveProductAccountPublicKey(DEV_ROOT_PUBLIC_KEY, "test-app", 0);
582
- expect(signer.publicKey).toEqual(expected);
583
- expect(signer.publicKey).not.toEqual(new Uint8Array(walletBytes));
584
- });
585
- test("throws on a stale session with no root account key", () => {
586
- expect(
587
- () => createSessionSigner(makeSession({ rootAccountId: null }), fakeAdapter("test-app"))
588
- ).toThrow(INCOMPLETE_SESSION_MESSAGE);
589
- });
590
- test("uses the host-supplied product-account public key when provided", () => {
591
- const walletBytes = Array.from({ length: 32 }, (_, i) => i);
592
- const productKey = new Uint8Array(32).fill(171);
593
- const signer = createSessionSigner(
594
- makeSession({ accountIdBytes: walletBytes }),
595
- fakeAdapter("test-app"),
596
- productKey
597
- );
598
- expect(signer.publicKey).toEqual(productKey);
599
- expect(signer.publicKey).not.toEqual(new Uint8Array(walletBytes));
600
- });
601
- test("signBytes routes through session.signRaw with the Bytes tag", async () => {
602
- const sig = new Uint8Array([9, 8, 7, 6, 5, 4, 3, 2, 1]);
603
- const captured = [];
604
- const session = makeSession({
605
- signRaw: async (req2) => {
606
- captured.push(req2);
607
- return ok({ signature: sig });
608
- }
609
- });
610
- const signer = createSessionSigner(session, fakeAdapter("test-app"));
611
- const out = await signer.signBytes(new Uint8Array([1, 2, 3]));
612
- expect(out).toEqual(sig);
613
- expect(captured).toHaveLength(1);
614
- const req = captured[0];
615
- expect(req.productAccountId).toEqual(["test-app", 0]);
616
- expect(req.data.tag).toBe("Bytes");
617
- expect(req.data.value).toEqual(new Uint8Array([1, 2, 3]));
618
- });
619
- test("signBytes never calls session.signPayload (legacy-path regression guard)", async () => {
620
- const session = makeSession({
621
- signRaw: async () => ok({ signature: new Uint8Array([1]) })
622
- });
623
- const signer = createSessionSigner(session, fakeAdapter("test-app"));
624
- await signer.signBytes(new Uint8Array([1, 2, 3]));
625
- const sessionWithSpies = session;
626
- expect(sessionWithSpies.signPayload.mock.calls).toHaveLength(0);
627
- expect(sessionWithSpies.signRaw.mock.calls).toHaveLength(1);
628
- });
629
- test("signBytes throws with a clear error when the mobile rejects", async () => {
630
- const session = makeSession({
631
- signRaw: async () => err({ message: "user declined" })
632
- });
633
- const signer = createSessionSigner(session, fakeAdapter("test-app"));
634
- await expect(signer.signBytes(new Uint8Array([1]))).rejects.toThrow(
635
- "Mobile signing rejected: user declined"
636
- );
637
- });
638
- });
639
- describe("buildCreateTransactionRequest \u2014 tx payload (the AsPgas fix)", () => {
640
- test("prefers V4 (tx-ext version 0) on a dual V4/V5 runtime", () => {
641
- expect(selectTxExtVersion([4, 5])).toBe(0);
642
- });
643
- test("uses the V5 selector when the runtime offers no V4", () => {
644
- expect(selectTxExtVersion([5])).toBe(5);
645
- });
646
- test("maps a V4-only runtime to the wire sentinel", () => {
647
- expect(selectTxExtVersion([4])).toBe(0);
648
- });
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);
654
- });
655
- test("rejects metadata with no extrinsic version", () => {
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
- }
668
- });
669
- const checkGenesis = ext("CheckGenesis", [], [17, 34, 51]);
670
- test("wraps the payload as v1 with signer, callData, and txExtVersion", () => {
671
- const callData = new Uint8Array([202, 17]);
672
- const req = buildCreateTransactionRequest(
673
- ["my-app", 3],
674
- callData,
675
- { CheckGenesis: checkGenesis },
676
- 5
677
- );
678
- expect(req.payload.tag).toBe("v1");
679
- expect(req.payload.value.signer).toEqual(["my-app", 3]);
680
- expect(req.payload.value.callData).toEqual(callData);
681
- expect(req.payload.value.txExtVersion).toBe(5);
682
- });
683
- test("takes the genesis hash from CheckGenesis.additionalSigned", () => {
684
- const req = buildCreateTransactionRequest(
685
- ["my-app", 0],
686
- new Uint8Array([0]),
687
- { CheckGenesis: checkGenesis },
688
- 0
689
- );
690
- expect(req.payload.value.genesisHash).toEqual(new Uint8Array([17, 34, 51]));
691
- });
692
- test("maps every signed extension to { id, extra, additionalSigned }", () => {
693
- const req = buildCreateTransactionRequest(
694
- ["my-app", 0],
695
- new Uint8Array([0]),
696
- { CheckGenesis: checkGenesis, CheckNonce: ext("CheckNonce", [7], []) },
697
- 0
698
- );
699
- expect(req.payload.value.extensions).toEqual([
700
- {
701
- id: "CheckGenesis",
702
- extra: new Uint8Array([]),
703
- additionalSigned: new Uint8Array([17, 34, 51])
704
- },
705
- {
706
- id: "CheckNonce",
707
- extra: new Uint8Array([7]),
708
- additionalSigned: new Uint8Array([])
709
- }
710
- ]);
711
- });
712
- test("preserves an unknown signed extension verbatim (e.g. AsPgas)", () => {
713
- const req = buildCreateTransactionRequest(
714
- ["my-app", 0],
715
- new Uint8Array([0]),
716
- { CheckGenesis: checkGenesis, AsPgas: ext("AsPgas", [222, 173], [190, 239]) },
717
- 0
718
- );
719
- expect(req.payload.value.extensions).toContainEqual({
720
- id: "AsPgas",
721
- extra: new Uint8Array([222, 173]),
722
- additionalSigned: new Uint8Array([190, 239])
723
- });
724
- });
725
- test("throws a clear error when CheckGenesis is absent", () => {
726
- expect(
727
- () => buildCreateTransactionRequest(["my-app", 0], new Uint8Array([0]), {}, 0)
728
- ).toThrow(/CheckGenesis/);
729
- });
730
- });
731
- describe("requestSignedTransaction \u2014 SSO round-trip", () => {
732
- const request = buildCreateTransactionRequest(
733
- ["my-app", 0],
734
- new Uint8Array([0]),
735
- { CheckGenesis: ext("CheckGenesis", [], [1]) },
736
- 0
737
- );
738
- test("returns the signed extrinsic bytes from the mobile response", async () => {
739
- const signed = new Uint8Array([171, 205, 239]);
740
- const session = makeSession({ createTransaction: async () => ok(signed) });
741
- const out = await requestSignedTransaction(session, request);
742
- expect(out).toBe(signed);
743
- });
744
- test("calls session.createTransaction \u2014 never signRaw/signPayload \u2014 with the request", async () => {
745
- const captured = [];
746
- const session = makeSession({
747
- createTransaction: async (req) => {
748
- captured.push(req);
749
- return ok(new Uint8Array([1]));
750
- }
751
- });
752
- await requestSignedTransaction(session, request);
753
- expect(captured).toEqual([request]);
754
- const spies = session;
755
- expect(spies.createTransaction.mock.calls).toHaveLength(1);
756
- expect(spies.signPayload.mock.calls).toHaveLength(0);
757
- expect(spies.signRaw.mock.calls).toHaveLength(0);
758
- });
759
- test("throws a clear error when the mobile rejects", async () => {
760
- const session = makeSession({
761
- createTransaction: async () => err({ message: "user declined" })
762
- });
763
- await expect(requestSignedTransaction(session, request)).rejects.toThrow(
764
- "Mobile transaction signing rejected: user declined"
765
- );
766
- });
767
- test("throws AllowanceExpiredError when the failure is a NoAllowanceError", async () => {
768
- const underlying = new NoAllowanceError();
769
- const session = makeSession({
770
- createTransaction: async () => err(underlying)
771
- });
772
- await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(
773
- AllowanceExpiredError
774
- );
775
- await expect(requestSignedTransaction(session, request)).rejects.toMatchObject({
776
- name: "AllowanceExpiredError",
777
- resource: "statementStore",
778
- cause: underlying
779
- });
780
- });
781
- test("throws AllowanceExpiredError for a wire-serialized failure (message match only)", async () => {
782
- expect(new NoAllowanceError().message).toBe(
783
- "Submit failed, no allowance set for account"
784
- );
785
- const serialized = new Error("Submit failed, no allowance set for account");
786
- const session = makeSession({
787
- createTransaction: async () => err(serialized)
788
- });
789
- await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(
790
- AllowanceExpiredError
791
- );
792
- });
793
- test("throws AllowanceExpiredError when the NoAllowanceError is wrapped as a cause", async () => {
794
- const wrapped = new Error("submitRequest failed", { cause: new NoAllowanceError() });
795
- const session = makeSession({
796
- createTransaction: async () => err(wrapped)
797
- });
798
- await expect(requestSignedTransaction(session, request)).rejects.toBeInstanceOf(
799
- AllowanceExpiredError
800
- );
801
- });
802
- test("a non-allowance Error still surfaces as the generic rejection", async () => {
803
- const session = makeSession({
804
- createTransaction: async () => err(new Error("transport exploded"))
805
- });
806
- await expect(requestSignedTransaction(session, request)).rejects.toThrow(
807
- "Mobile transaction signing rejected: transport exploded"
808
- );
809
- });
810
- });
811
- describe("makeRawBytesSignCallback", () => {
812
- test("forwards data verbatim under the Bytes tag with the right productAccountId", async () => {
813
- const captured = [];
814
- const session = makeSession({
815
- signRaw: async (req2) => {
816
- captured.push(req2);
817
- return ok({ signature: new Uint8Array([255]) });
818
- }
819
- });
820
- const callback = makeRawBytesSignCallback(session, ["my-app", 5]);
821
- await callback(new Uint8Array([222, 173, 190, 239]));
822
- expect(captured).toHaveLength(1);
823
- const req = captured[0];
824
- expect(req.productAccountId).toEqual(["my-app", 5]);
825
- expect(req.data.tag).toBe("Bytes");
826
- expect(Array.from(req.data.value)).toEqual([222, 173, 190, 239]);
827
- });
828
- test("returns the raw signature bytes from the mobile response", async () => {
829
- const sig = new Uint8Array([66]);
830
- const session = makeSession({
831
- signRaw: async () => ok({ signature: sig })
832
- });
833
- const callback = makeRawBytesSignCallback(session, ["my-app", 0]);
834
- const out = await callback(new Uint8Array([0]));
835
- expect(out).toBe(sig);
836
- });
837
- test("throws AllowanceExpiredError when the failure is a NoAllowanceError", async () => {
838
- const underlying = new NoAllowanceError();
839
- const session = makeSession({
840
- signRaw: async () => err(underlying)
841
- });
842
- const callback = makeRawBytesSignCallback(session, ["my-app", 0]);
843
- await expect(callback(new Uint8Array([1]))).rejects.toBeInstanceOf(
844
- AllowanceExpiredError
845
- );
846
- await expect(callback(new Uint8Array([1]))).rejects.toMatchObject({
847
- resource: "statementStore",
848
- cause: underlying
849
- });
850
- });
851
- test("a non-allowance failure still surfaces as the generic rejection", async () => {
852
- const session = makeSession({
853
- signRaw: async () => err(new Error("user declined"))
854
- });
855
- const callback = makeRawBytesSignCallback(session, ["my-app", 0]);
856
- await expect(callback(new Uint8Array([1]))).rejects.toThrow(
857
- "Mobile signing rejected: user declined"
858
- );
859
- });
860
- });
861
- describe("createSessionSignerForAccount", () => {
862
- test("forwards productAccountId from the explicit ref", async () => {
863
- const captured = [];
864
- const session = makeSession({
865
- signRaw: async (req2) => {
866
- captured.push(req2);
867
- return ok({ signature: new Uint8Array([42]) });
868
- }
869
- });
870
- const signer = createSessionSignerForAccount(session, {
871
- productId: "my-app",
872
- derivationIndex: 7
873
- });
874
- await signer.signBytes(new Uint8Array([10, 20, 30]));
875
- expect(captured).toHaveLength(1);
876
- const req = captured[0];
877
- expect(req.productAccountId).toEqual(["my-app", 7]);
878
- expect(req.data.tag).toBe("Bytes");
879
- expect(req.data.value).toBeInstanceOf(Uint8Array);
880
- });
881
- test("supports a productId different from any adapter's appId", async () => {
882
- const captured = [];
883
- const session = makeSession({
884
- signRaw: async (req2) => {
885
- captured.push(req2);
886
- return ok({ signature: new Uint8Array([0]) });
887
- }
888
- });
889
- const signer = createSessionSignerForAccount(session, {
890
- productId: "external-product",
891
- derivationIndex: 0
892
- });
893
- await signer.signBytes(new Uint8Array([1]));
894
- const req = captured[0];
895
- expect(req.productAccountId).toEqual(["external-product", 0]);
896
- });
897
- });
898
- }
899
- var makeSession2;
900
- var ext2;
901
- var fakeAdapter2;
902
-
903
- // src/index.ts
904
- import { AllowanceExpiredError as AllowanceExpiredError2, SignerError } from "@parity/product-sdk-signer/errors";
905
-
906
- // src/allowance.ts
907
- import { AllowanceError } from "@novasamatech/host-papp";
908
-
909
- // src/allowance-cache.ts
910
- import { gcm } from "@noble/ciphers/aes.js";
911
- import { blake2b } from "@noble/hashes/blake2.js";
912
- import { fromHex, toHex } from "@polkadot-api/utils";
913
- import { readFile, writeFile } from "fs/promises";
914
- import { join } from "path";
915
- import { Bytes, Enum, Struct, Vector, _void, str } from "scale-ts";
916
252
  var AllowanceResourceKindCodec = Enum({
917
253
  bulletin: _void,
918
254
  statementStore: _void
@@ -998,154 +334,6 @@ async function hasBulletinAllowance(adapter, productId, sessionId) {
998
334
  async function hasStatementStoreAllowance(adapter, productId, sessionId) {
999
335
  return hasAllowanceForResource(adapter, productId, "statementStore", sessionId);
1000
336
  }
1001
- if (void 0) {
1002
- let makeAdapter = function(opts) {
1003
- return {
1004
- sessions: {
1005
- sessions: {
1006
- read: () => opts.sessions
1007
- }
1008
- },
1009
- allowance: {
1010
- getBulletinSigner: opts.getBulletinSigner ?? vi.fn(() => ok({})),
1011
- getStatementStoreProver: opts.getStatementStoreProver ?? vi.fn(() => ok({}))
1012
- }
1013
- // The convenience never touches the rest of the adapter; leave it unstubbed.
1014
- };
1015
- };
1016
- makeAdapter2 = makeAdapter;
1017
- const { describe, test, expect, vi } = void 0;
1018
- const { ok, err } = await null;
1019
- describe("getBulletinSigner \u2014 sessionId defaulting", () => {
1020
- test("uses the only paired session when sessionId is omitted", async () => {
1021
- const fakeSigner = { _tag: "signer" };
1022
- const getBulletinSignerFn = vi.fn(() => ok(fakeSigner));
1023
- const adapter = makeAdapter({
1024
- sessions: [{ id: "sess-only" }],
1025
- getBulletinSigner: getBulletinSignerFn
1026
- });
1027
- const result = await getBulletinSigner(adapter, "my-app.dot");
1028
- expect(result).toBe(fakeSigner);
1029
- expect(getBulletinSignerFn).toHaveBeenCalledWith("sess-only", "my-app.dot");
1030
- });
1031
- test("uses the explicit sessionId when supplied", async () => {
1032
- const fakeSigner = { _tag: "signer" };
1033
- const getBulletinSignerFn = vi.fn(() => ok(fakeSigner));
1034
- const adapter = makeAdapter({
1035
- sessions: [{ id: "sess-a" }, { id: "sess-b" }],
1036
- getBulletinSigner: getBulletinSignerFn
1037
- });
1038
- await getBulletinSigner(adapter, "my-app.dot", "sess-b");
1039
- expect(getBulletinSignerFn).toHaveBeenCalledWith("sess-b", "my-app.dot");
1040
- });
1041
- test("throws AllowanceError('NoSession') when zero sessions and no id", async () => {
1042
- const adapter = makeAdapter({ sessions: [] });
1043
- await expect(getBulletinSigner(adapter, "my-app.dot")).rejects.toBeInstanceOf(
1044
- AllowanceError
1045
- );
1046
- await expect(getBulletinSigner(adapter, "my-app.dot")).rejects.toMatchObject({
1047
- reason: "NoSession"
1048
- });
1049
- });
1050
- test("throws AllowanceError('NoSession') when multiple sessions and no id", async () => {
1051
- const adapter = makeAdapter({ sessions: [{ id: "a" }, { id: "b" }] });
1052
- await expect(getBulletinSigner(adapter, "my-app.dot")).rejects.toBeInstanceOf(
1053
- AllowanceError
1054
- );
1055
- await expect(getBulletinSigner(adapter, "my-app.dot")).rejects.toMatchObject({
1056
- reason: "NoSession"
1057
- });
1058
- });
1059
- });
1060
- describe("getBulletinSigner \u2014 error unwrapping", () => {
1061
- test("rethrows the AllowanceError from the underlying service as a thrown exception", async () => {
1062
- const underlyingErr = new AllowanceError("Rejected", "user said no");
1063
- const adapter = makeAdapter({
1064
- sessions: [{ id: "s" }],
1065
- getBulletinSigner: vi.fn(() => err(underlyingErr))
1066
- });
1067
- await expect(getBulletinSigner(adapter, "p")).rejects.toBe(underlyingErr);
1068
- });
1069
- });
1070
- describe("getStatementStoreProver \u2014 same defaulting", () => {
1071
- test("uses the only paired session when sessionId is omitted", async () => {
1072
- const fakeProver = { _tag: "prover" };
1073
- const getStatementStoreProverFn = vi.fn(() => ok(fakeProver));
1074
- const adapter = makeAdapter({
1075
- sessions: [{ id: "sess-only" }],
1076
- getStatementStoreProver: getStatementStoreProverFn
1077
- });
1078
- const result = await getStatementStoreProver(adapter, "my-app.dot");
1079
- expect(result).toBe(fakeProver);
1080
- expect(getStatementStoreProverFn).toHaveBeenCalledWith("sess-only", "my-app.dot");
1081
- });
1082
- test("uses the explicit sessionId when supplied", async () => {
1083
- const fakeProver = { _tag: "prover" };
1084
- const getStatementStoreProverFn = vi.fn(() => ok(fakeProver));
1085
- const adapter = makeAdapter({
1086
- sessions: [{ id: "sess-a" }, { id: "sess-b" }],
1087
- getStatementStoreProver: getStatementStoreProverFn
1088
- });
1089
- await getStatementStoreProver(adapter, "my-app.dot", "sess-b");
1090
- expect(getStatementStoreProverFn).toHaveBeenCalledWith("sess-b", "my-app.dot");
1091
- });
1092
- test("throws AllowanceError('NoSession') when zero sessions and no id", async () => {
1093
- const adapter = makeAdapter({ sessions: [] });
1094
- await expect(getStatementStoreProver(adapter, "my-app.dot")).rejects.toBeInstanceOf(
1095
- AllowanceError
1096
- );
1097
- await expect(getStatementStoreProver(adapter, "my-app.dot")).rejects.toMatchObject({
1098
- reason: "NoSession"
1099
- });
1100
- });
1101
- test("throws AllowanceError('NoSession') when multiple sessions and no id", async () => {
1102
- const adapter = makeAdapter({ sessions: [{ id: "a" }, { id: "b" }] });
1103
- await expect(getStatementStoreProver(adapter, "my-app.dot")).rejects.toBeInstanceOf(
1104
- AllowanceError
1105
- );
1106
- await expect(getStatementStoreProver(adapter, "my-app.dot")).rejects.toMatchObject({
1107
- reason: "NoSession"
1108
- });
1109
- });
1110
- });
1111
- describe("getStatementStoreProver \u2014 error unwrapping", () => {
1112
- test("rethrows the AllowanceError from the underlying service as a thrown exception", async () => {
1113
- const underlyingErr = new AllowanceError("Rejected", "user said no");
1114
- const adapter = makeAdapter({
1115
- sessions: [{ id: "s" }],
1116
- getStatementStoreProver: vi.fn(() => err(underlyingErr))
1117
- });
1118
- await expect(getStatementStoreProver(adapter, "p")).rejects.toBe(underlyingErr);
1119
- });
1120
- });
1121
- describe("has*Allowance \u2014 sessionId defaulting + storageDir fallback", () => {
1122
- test("hasBulletinAllowance throws AllowanceError('NoSession') when zero sessions and no id", async () => {
1123
- const adapter = makeAdapter({ sessions: [] });
1124
- await expect(hasBulletinAllowance(adapter, "p")).rejects.toBeInstanceOf(AllowanceError);
1125
- await expect(hasBulletinAllowance(adapter, "p")).rejects.toMatchObject({
1126
- reason: "NoSession"
1127
- });
1128
- });
1129
- test("hasBulletinAllowance returns false (conservative) when storageDir is undefined", async () => {
1130
- const adapter = makeAdapter({ sessions: [{ id: "sess-only" }] });
1131
- expect(await hasBulletinAllowance(adapter, "p")).toBe(false);
1132
- });
1133
- test("hasStatementStoreAllowance throws AllowanceError('NoSession') when zero sessions and no id", async () => {
1134
- const adapter = makeAdapter({ sessions: [] });
1135
- await expect(hasStatementStoreAllowance(adapter, "p")).rejects.toBeInstanceOf(
1136
- AllowanceError
1137
- );
1138
- });
1139
- test("hasStatementStoreAllowance returns false (conservative) when storageDir is undefined", async () => {
1140
- const adapter = makeAdapter({ sessions: [{ id: "sess-only" }] });
1141
- expect(await hasStatementStoreAllowance(adapter, "p")).toBe(false);
1142
- });
1143
- });
1144
- }
1145
- var makeAdapter2;
1146
-
1147
- // src/index.ts
1148
- import { AllowanceError as AllowanceError2 } from "@novasamatech/host-papp";
1149
337
 
1150
338
  // src/sessions.ts
1151
339
  function waitForSessions(adapter, timeoutMs = 3e3) {
@@ -1167,108 +355,10 @@ function waitForSessions(adapter, timeoutMs = 3e3) {
1167
355
  });
1168
356
  });
1169
357
  }
1170
- if (void 0) {
1171
- let fakeAdapter = function() {
1172
- const subscribers = /* @__PURE__ */ new Set();
1173
- const adapter = {
1174
- sessions: {
1175
- sessions: {
1176
- subscribe(cb) {
1177
- subscribers.add(cb);
1178
- return () => {
1179
- subscribers.delete(cb);
1180
- };
1181
- }
1182
- }
1183
- }
1184
- };
1185
- return {
1186
- adapter,
1187
- emit: (sessions) => {
1188
- for (const cb of subscribers) cb(sessions);
1189
- },
1190
- subscriberCount: () => subscribers.size
1191
- };
1192
- };
1193
- fakeAdapter2 = fakeAdapter;
1194
- const { describe, test, expect, vi } = void 0;
1195
- describe("waitForSessions", () => {
1196
- test("resolves with sessions on first non-empty emission", async () => {
1197
- const { adapter, emit } = fakeAdapter();
1198
- const promise = waitForSessions(adapter, 1e3);
1199
- const session = { remoteAccount: { accountId: new Uint8Array(32) } };
1200
- emit([session]);
1201
- await expect(promise).resolves.toEqual([session]);
1202
- });
1203
- test("ignores empty emissions and waits for a non-empty one", async () => {
1204
- const { adapter, emit } = fakeAdapter();
1205
- const promise = waitForSessions(adapter, 1e3);
1206
- emit([]);
1207
- emit([]);
1208
- const session = { remoteAccount: { accountId: new Uint8Array(32) } };
1209
- emit([session]);
1210
- await expect(promise).resolves.toEqual([session]);
1211
- });
1212
- test("resolves with [] after timeout when nothing is emitted", async () => {
1213
- vi.useFakeTimers();
1214
- try {
1215
- const { adapter } = fakeAdapter();
1216
- const promise = waitForSessions(adapter, 50);
1217
- vi.advanceTimersByTime(50);
1218
- await expect(promise).resolves.toEqual([]);
1219
- } finally {
1220
- vi.useRealTimers();
1221
- }
1222
- });
1223
- test("unsubscribes after resolving", async () => {
1224
- const { adapter, emit, subscriberCount } = fakeAdapter();
1225
- const session = { remoteAccount: { accountId: new Uint8Array(32) } };
1226
- const promise = waitForSessions(adapter, 1e3);
1227
- emit([session]);
1228
- await promise;
1229
- await Promise.resolve();
1230
- expect(subscriberCount()).toBe(0);
1231
- });
1232
- test("uses default 3000ms timeout when none is passed", async () => {
1233
- vi.useFakeTimers();
1234
- try {
1235
- const { adapter } = fakeAdapter();
1236
- const promise = waitForSessions(adapter);
1237
- vi.advanceTimersByTime(2999);
1238
- let settled = false;
1239
- promise.then(() => {
1240
- settled = true;
1241
- });
1242
- await Promise.resolve();
1243
- expect(settled).toBe(false);
1244
- vi.advanceTimersByTime(1);
1245
- await expect(promise).resolves.toEqual([]);
1246
- } finally {
1247
- vi.useRealTimers();
1248
- }
1249
- });
1250
- test("handles synchronous initial emission", async () => {
1251
- const session = { remoteAccount: { accountId: new Uint8Array(32) } };
1252
- const adapter = {
1253
- sessions: {
1254
- sessions: {
1255
- subscribe(cb) {
1256
- cb([session]);
1257
- return () => {
1258
- };
1259
- }
1260
- }
1261
- }
1262
- };
1263
- await expect(waitForSessions(adapter, 1e3)).resolves.toEqual([session]);
1264
- });
1265
- });
1266
- }
1267
- var fakeAdapter2;
1268
358
 
1269
359
  // src/qr-encode.ts
1270
360
  async function renderQrCode(data, options) {
1271
- const QRCode = await import("qrcode");
361
+ const QRCode = await import('qrcode');
1272
362
  const result = await QRCode.toString(data, {
1273
363
  type: "utf8",
1274
364
  errorCorrectionLevel: options?.errorCorrectionLevel ?? "M",
@@ -1276,44 +366,7 @@ async function renderQrCode(data, options) {
1276
366
  });
1277
367
  return result;
1278
368
  }
1279
- if (void 0) {
1280
- const { test, expect } = void 0;
1281
- test("renderQrCode produces non-empty output", async () => {
1282
- const result = await renderQrCode("https://example.com");
1283
- expect(result.length).toBeGreaterThan(0);
1284
- expect(result).toContain("\n");
1285
- });
1286
- test("renderQrCode contains Unicode block characters", async () => {
1287
- const result = await renderQrCode("test");
1288
- expect(/[▀▄█]/.test(result)).toBe(true);
1289
- });
1290
- test("different inputs produce different QR codes", async () => {
1291
- const a = await renderQrCode("hello");
1292
- const b = await renderQrCode("world");
1293
- expect(a).not.toBe(b);
1294
- });
1295
- }
1296
- export {
1297
- AllowanceError2 as AllowanceError,
1298
- AllowanceExpiredError2 as AllowanceExpiredError,
1299
- INCOMPLETE_SESSION_MESSAGE,
1300
- SS_PASEO_STABLE_STAGE_ENDPOINTS,
1301
- SS_STABLE_STAGE_ENDPOINTS,
1302
- SignerError,
1303
- createNodeStorageAdapter,
1304
- createSessionSigner,
1305
- createSessionSignerForAccount,
1306
- createTerminalAdapter,
1307
- deriveProductPublicKey,
1308
- getBulletinSigner,
1309
- getStatementStoreProver,
1310
- hasBulletinAllowance,
1311
- hasStatementStoreAllowance,
1312
- isBenignTeardownError,
1313
- renderQrCode,
1314
- requestResourceAllocation,
1315
- sendResourceAllocation,
1316
- sessionRootPublicKey,
1317
- waitForSessions
1318
- };
369
+
370
+ export { INCOMPLETE_SESSION_MESSAGE, StatementStoreNetworks, createSessionSigner, createSessionSignerForAccount, createTerminalAdapter, deriveProductPublicKey, getBulletinSigner, getStatementStoreProver, hasBulletinAllowance, hasStatementStoreAllowance, isBenignTeardownError, renderQrCode, sessionRootPublicKey, waitForSessions };
371
+ //# sourceMappingURL=index.js.map
1319
372
  //# sourceMappingURL=index.js.map