@parity/product-sdk-signer 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -199,7 +199,18 @@ type OnConnect = (account: SignerAccount, ctx: ConnectContext) => void | Promise
199
199
  declare class SignerError extends Error {
200
200
  constructor(message: string, options?: ErrorOptions);
201
201
  }
202
- /** The Host API is not available (product-sdk not installed or not inside a container). */
202
+ /**
203
+ * The Host API is not available.
204
+ *
205
+ * Common causes:
206
+ * - The app is loaded outside a Polkadot host container (a regular browser tab
207
+ * under `npm run dev`, no iframe, no WebView). This is the dominant case
208
+ * during local development.
209
+ * - The optional `@novasamatech/host-api(-wrapper)` peer is not installed.
210
+ *
211
+ * Branch with `instanceof HostUnavailableError` to surface a "open this app
212
+ * in a Polkadot host, or pick a dev provider" message to the user.
213
+ */
203
214
  declare class HostUnavailableError extends SignerError {
204
215
  constructor(message?: string);
205
216
  }
@@ -277,8 +288,6 @@ interface HostProviderOptions {
277
288
  * `dotNsIdentifier`, skipping the legacy fetch entirely. For apps
278
289
  * that sign exclusively with a per-dapp derived account.
279
290
  *
280
- * `SignerAccount.name` is populated best-effort from
281
- * `accounts.getUserId().primaryUsername`; failures leave it null.
282
291
  * Signing is pinned to `createTransaction` (see PR #96).
283
292
  */
284
293
  productAccount?: {
@@ -286,6 +295,20 @@ interface HostProviderOptions {
286
295
  dotNsIdentifier: string;
287
296
  /** Derivation index within the app scope. Default: 0. */
288
297
  derivationIndex?: number;
298
+ /**
299
+ * Populate `SignerAccount.name` best-effort from
300
+ * `accounts.getUserId().primaryUsername`.
301
+ *
302
+ * On by default. Set to `false` to skip the fetch: `getUserId`
303
+ * triggers a host identity-permission prompt, so apps that don't
304
+ * render the user's name (those with their own display chain, e.g.
305
+ * registry username → fallback) can opt out and avoid the prompt.
306
+ * When enabled and the fetch fails (NotConnected, PermissionDenied,
307
+ * codec drift) the name stays null and connect still succeeds. The
308
+ * name can also be fetched later on demand via
309
+ * {@link HostProvider.getUserId}. Default: `true`.
310
+ */
311
+ requestName?: boolean;
289
312
  };
290
313
  }
291
314
  /**
@@ -368,13 +391,28 @@ interface ProductSdkModule {
368
391
  createAccountsProvider: () => AccountsProvider;
369
392
  /** Present from product-sdk ≥ 0.6; used to request TransactionSubmit. */
370
393
  hostApi?: HostApiPermissionBridge;
394
+ /**
395
+ * `sandboxTransport.isCorrectEnvironment()` returns `false` when the app
396
+ * is loaded outside a Polkadot host container (e.g. a regular browser
397
+ * tab). Calling `getLegacyAccounts()` / `getProductAccount()` in that
398
+ * state surfaces the upstream `Environment is not correct` exception,
399
+ * so we pre-check during `connect()` and raise a specific
400
+ * {@link HostUnavailableError} with actionable guidance instead.
401
+ */
402
+ sandboxTransport?: {
403
+ isCorrectEnvironment(): boolean;
404
+ };
371
405
  }
372
406
  /**
373
407
  * Provider for the Host API (Polkadot Desktop / Android).
374
408
  *
375
- * Dynamically imports `@novasamatech/host-api-wrapper` at runtime so it remains
376
- * an optional peer dependency. Apps running outside a host container will
377
- * gracefully get a `HOST_UNAVAILABLE` error.
409
+ * Dynamically imports `@novasamatech/host-api-wrapper` at runtime. Apps running
410
+ * outside a host container — e.g. a plain browser tab during `npm run dev` —
411
+ * resolve to {@link HostUnavailableError} with guidance on what to do (open
412
+ * the app inside a Polkadot host or pick a non-host provider). The check uses
413
+ * the wrapper's `sandboxTransport.isCorrectEnvironment()` predicate and runs
414
+ * before any host RPC call, so the user never sees the upstream
415
+ * `Environment is not correct` exception leaking through.
378
416
  *
379
417
  * Supports both non-product accounts (user's external wallets) and product
380
418
  * accounts (app-scoped derived accounts managed by the host).
@@ -429,6 +467,22 @@ declare class HostProvider implements SignerProvider {
429
467
  * Requires a prior successful `connect()` call.
430
468
  */
431
469
  getProductAccountAlias(dotNsIdentifier: string, derivationIndex?: number): Promise<Result<ContextualAlias, SignerError>>;
470
+ /**
471
+ * Fetch the connected user's primary username from the host.
472
+ *
473
+ * Use this to retrieve the name lazily — e.g. on a profile screen that
474
+ * actually displays it — when `connect()` ran without
475
+ * `productAccount.requestName` (the default) and so never fetched it.
476
+ * Like the connect-time fetch this triggers a host identity-permission
477
+ * prompt; unlike it, the result is returned as a structured `Result` so
478
+ * callers can react to a `PermissionDenied` / `NotConnected` rejection
479
+ * explicitly instead of silently falling back to a nameless account.
480
+ *
481
+ * Requires a prior successful `connect()` call.
482
+ */
483
+ getUserId(): Promise<Result<{
484
+ primaryUsername: string;
485
+ }, SignerError>>;
432
486
  /**
433
487
  * Create a Ring VRF proof for anonymous operations.
434
488
  *
package/dist/index.js CHANGED
@@ -348,6 +348,40 @@ var HostProvider = class {
348
348
  );
349
349
  }
350
350
  }
351
+ /**
352
+ * Fetch the connected user's primary username from the host.
353
+ *
354
+ * Use this to retrieve the name lazily — e.g. on a profile screen that
355
+ * actually displays it — when `connect()` ran without
356
+ * `productAccount.requestName` (the default) and so never fetched it.
357
+ * Like the connect-time fetch this triggers a host identity-permission
358
+ * prompt; unlike it, the result is returned as a structured `Result` so
359
+ * callers can react to a `PermissionDenied` / `NotConnected` rejection
360
+ * explicitly instead of silently falling back to a nameless account.
361
+ *
362
+ * Requires a prior successful `connect()` call.
363
+ */
364
+ async getUserId() {
365
+ if (!this.accountsProvider) {
366
+ return err(new HostUnavailableError("Host provider is not connected"));
367
+ }
368
+ try {
369
+ const result = await this.accountsProvider.getUserId().match(
370
+ (value) => value,
371
+ (error) => {
372
+ throw new Error(`Host rejected user id request: ${formatError(error)}`);
373
+ }
374
+ );
375
+ return ok(result);
376
+ } catch (cause) {
377
+ log2.error("failed to get user id", { cause });
378
+ return err(
379
+ new HostRejectedError(
380
+ cause instanceof Error ? cause.message : "Failed to get user id"
381
+ )
382
+ );
383
+ }
384
+ }
351
385
  /**
352
386
  * Create a Ring VRF proof for anonymous operations.
353
387
  *
@@ -392,6 +426,14 @@ var HostProvider = class {
392
426
  )
393
427
  );
394
428
  }
429
+ if (sdk.sandboxTransport && !sdk.sandboxTransport.isCorrectEnvironment()) {
430
+ log2.warn("not inside a host container \u2014 Host API unavailable");
431
+ return err(
432
+ new HostUnavailableError(
433
+ "Host API is not available: not running inside a Polkadot host container. Open this app inside Polkadot Desktop or the Polkadot Mobile WebView, or pick a non-host signer provider (e.g. dev accounts)."
434
+ )
435
+ );
436
+ }
395
437
  const provider = sdk.createAccountsProvider();
396
438
  this.accountsProvider = provider;
397
439
  let signerAccounts;
@@ -399,7 +441,8 @@ var HostProvider = class {
399
441
  const accountResult = await this.fetchProductSignerAccount(
400
442
  provider,
401
443
  this.productAccount.dotNsIdentifier,
402
- this.productAccount.derivationIndex ?? 0
444
+ this.productAccount.derivationIndex ?? 0,
445
+ this.productAccount.requestName ?? true
403
446
  );
404
447
  if (!accountResult.ok) return accountResult;
405
448
  signerAccounts = [accountResult.value];
@@ -413,6 +456,14 @@ var HostProvider = class {
413
456
  }
414
457
  );
415
458
  } catch (cause) {
459
+ if (cause instanceof Error && /environment is not correct/i.test(cause.message)) {
460
+ log2.warn("not inside a host container (detected at getLegacyAccounts)");
461
+ return err(
462
+ new HostUnavailableError(
463
+ "Host API is not available: not running inside a Polkadot host container. Open this app inside Polkadot Desktop or the Polkadot Mobile WebView, or pick a non-host signer provider (e.g. dev accounts)."
464
+ )
465
+ );
466
+ }
416
467
  log2.error("failed to get accounts from host", { cause });
417
468
  return err(
418
469
  new HostRejectedError(
@@ -458,8 +509,9 @@ var HostProvider = class {
458
509
  this.statusCleanup = typeof sub === "function" ? sub : () => sub.unsubscribe();
459
510
  return ok(signerAccounts);
460
511
  }
461
- async fetchProductSignerAccount(provider, dotNsIdentifier, derivationIndex) {
512
+ async fetchProductSignerAccount(provider, dotNsIdentifier, derivationIndex, requestName) {
462
513
  const fetchUsername = async () => {
514
+ if (!requestName) return null;
463
515
  try {
464
516
  return await provider.getUserId().match(
465
517
  (result) => result.primaryUsername,