From 25b8940271259df3fa1d20e2df798f3facc7ecd2 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Thu, 30 Apr 2026 12:28:40 +0200 Subject: [PATCH 01/83] =?UTF-8?q?Add=20dotli=20=E2=86=92=20TrUAPI=20migrat?= =?UTF-8?q?ion=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inventory of the host-API boundary and the slim Phase-A callback surface the dotli refactor lands on, plus a status section describing the current bridge.ts + host-callbacks/ layout for onboarding. --- MIGRATION.md | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 MIGRATION.md diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..7356a5d --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,254 @@ +# dotli host layer migration to TrUAPI + +> Current status: this file started as the pre-migration inventory. The dotli +> host now boots a WASM TrUAPI runtime through +> `packages/ui/src/bridge.ts`, wires concrete callbacks from +> `packages/ui/src/host-callbacks/`, and uses `@truapi/host-web` for iframe +> transport. Keep the inventory below as historical context, but use this +> status section for onboarding. + +## Current TrUAPI integration + +dotli still has two separate protocol layers: + +1. Host-product API frames, now handled by TrUAPI. +2. dotli's internal host-smoldot protocol in `packages/protocol/`, which still + owns `.dot` resolution and remains outside TrUAPI. + +The active launch path is `packages/ui/src/bridge.ts`: + +- dynamically imports `@truapi/host-web` and the worker entrypoint + `@truapi/host-shared/dist/worker-runtime.js?worker` +- spawns a Web Worker that owns the truapi-server wasm core (smoldot's + CPU runs off the page main thread) +- calls `createWebWorkerHostRuntime(new HostWorker(), callbacks)` +- supplies dotli callbacks from `packages/ui/src/host-callbacks/handlers.ts` +- calls `createIframeHost(...)` with the product URL, sandbox policy, and + allowed origin + +`createIframeHost` keeps `MessageChannel` as the canonical transport for +products built with `@truapi/client`. After F1 it also accepts legacy +`@novasamatech/host-api` products that post Novasama-compatible byte frames via +`window.postMessage`; those frames decode through the same generated wire table +and enter the same Rust runtime. + +Account, signing, statement-store, and preimage behavior are no longer described +as missing callback coverage here. The current dotli tree has explicit +host-callback modules for those domains under `packages/ui/src/host-callbacks/`. + +Known limitation: nested dApp-in-dApp bridging from the old +`setupNestedBridgeDetector` path is still dropped. `createIframeHost` uses one +dedicated iframe transport and does not yet expose an `attachNested` API. + +## Historical inventory + +This document is the Phase 2 inventory for the host rewrite described in the +parent repo's plan (commit `d06c09b` on branch `pg/dotli-submodule`). It +categorizes every dotli module touched by the host-API boundary so Phase 3 can +delete, keep, or rewrite with confidence. + +Terminology note — dotli has **two** "protocol" layers that a casual reader +will conflate: + +1. **Host ↔ product protocol**, today provided by `@novasamatech/host-api` + + `@novasamatech/host-container` (external npm). This is what TrUAPI replaces. +2. **Host ↔ smoldot protocol**, dotli-internal, lives in `packages/protocol/` + and `apps/protocol/`. This is NOT replaced; it isolates smoldot on its own + origin and provides `.dot` name resolution. Keep it. + +The rewrite only touches layer (1). Layer (2) is load-bearing for `.dot` +resolution in `apps/host` and stays as-is. + +## Architecture today + +``` +dot.li (apps/host) → landing shell, resolves label.dot → CID + │ + ├── @dotli/protocol/client ────────→ protocol.dot.li iframe (smoldot, auth) + │ (LAYER 2 — keep) + │ + └── renderAppSubdomain(cid, label) + │ + ▼ + cid.app.dot.li iframe ←─────── @novasamatech/host-container bridge + (apps/sandbox) (LAYER 1 — REPLACE with TrUAPI) + │ setupContainer + wireContainerHandlers + │ setupNestedBridgeDetector + └── document.write(html) (packages/ui/src/container.ts) +``` + +Layer 1 wiring lives entirely in `packages/ui/src/{bridge,container}.ts`. The +sandbox app (`apps/sandbox/src/main.ts`) is a content renderer, not a host — +it does not touch the container API. `apps/host/src/main.ts` calls +`renderIframe` / `renderAppSubdomain` in `@dotli/ui/bridge`, which is where +`setupContainer` is invoked. + +## Replacement mapping + +`wireContainerHandlers` (the 600-line function in +`packages/ui/src/container.ts`) maps 1:1 onto a `WasmHostCallbacks` record +under `@truapi/host-shared`: + +| novasamatech handler | TrUAPI callback | Behavior migrates to | +| ------------------------------------------ | --------------------- | ---------------------------- | +| `handleFeatureSupported` | `featureSupported` | same: `isRemoteChainSupported` from `@dotli/protocol/client` | +| `handleChainConnection` | — | **dropped**: TrUAPI does not expose raw chain providers; layer-2 handles it via its own iframe | +| `handleAccountGet` | — | **out of scope** for this PR (no TrUAPI callback yet); keep behind a temp adapter or stub until contract lands | +| `handleGetNonProductAccounts` | — | same as above | +| `handleAccountConnectionStatusSubscribe` | — | same as above | +| `handleAccountGetAlias` | — | same as above | +| `handleSignPayload` / `handleSignRaw` | — | same as above | +| `handleStatementStore*` | — | same as above | +| `handlePreimage*` | — | same as above | +| `handleNavigateTo` | `navigateTo` | `dotNsUrl` parser + existing `window.open` logic | +| `handleDevicePermission` | `devicePermission` | `getPermissionStatus` / `setPermissionStatus` from `packages/ui/src/permissions.ts`, + `showPermissionRequestModal` | +| `handlePermission` (TransactionSubmit) | `remotePermission` | same permissions store; split into `ChainSubmit` + `StatementSubmit` (see `demos/hosts/web/src/callbacks/RemotePermission.ts:9`) | +| `handlePushNotification` | `pushNotification` | `showPushNotification` from `packages/ui/src/notification.ts` | +| `handleLocalStorageRead/Write/Clear` | `localStorageRead/Write/Clear` | `localStorage` with `storagePrefix = "dotli:${label}:"` | + +**Account / signing / statement-store / preimage are not covered by the +TrUAPI callback contract today.** The rewrite cannot delete that code without +breaking dotli. For this PR we: + +1. Replace only the callbacks present in `WasmHostCallbacks` (7 entries). +2. Keep dotli's account/signing/statement/preimage flows reachable via a + temporary adapter that continues to wire them onto the TrUAPI host's + product-side message boundary (or feature-gate them off, pending the + broader TrUAPI contract expansion). This adapter is the only piece of + `@novasamatech/host-container` that survives Phase 3. Flag in PR for + follow-up. + +## Delete (layer 1) + +| Path | Reason | +| ------------------------------------------ | ------ | +| `packages/ui/src/container.ts` | `wireContainerHandlers` + `setupContainer` + `setupNestedBridgeDetector` + `createWindowProvider`: replaced by `createIframeHost` + `createHostCallbacks` | +| `packages/ui/src/bridge.ts` (parts) | Keep `renderArchive` / `renderContent` / `prepareIframe` re-exports from `render.ts`; rewrite `renderIframe` + `renderAppSubdomain` to call `createIframeHost` with the new runtime | +| `@novasamatech/host-api` usage | Error classes (`SigningErr`, `StorageErr`, etc.) become unused once account/signing wiring migrates; drop the dep once the adapter above is retired | +| `@novasamatech/host-container` usage | `createContainer` / `createIframeProvider` replaced by TrUAPI runtime + iframe host | +| `@novasamatech/sdk-statement` mapping | `statement-store-mapping.ts` — unused once statement-store callbacks migrate; drop with follow-up | + +Every consumer of the deleted modules has to move to the new entry. The only +expected external import sites are: +- `packages/ui/src/bridge.ts` (obvious) +- `apps/host/src/main.ts` (via `@dotli/ui/bridge` only, should be untouched) + +## Keep (UI primitives called by callbacks) + +| Path | Role in Phase 3 | +| --------------------------------------- | --------------- | +| `packages/ui/src/permissions.ts` | `getPermissionStatus`, `setPermissionStatus`, `buildAllowAttribute` — called from `devicePermission` + `remotePermission` callbacks | +| `packages/ui/src/permission-modal.ts` | `showPermissionRequestModal` — consent UI, called from permission callbacks | +| `packages/ui/src/notification.ts` | `showNotification`, `showPushNotification` — called from push + permission denial callbacks | +| `packages/ui/src/alias-permission-modal.ts` | Account-alias consent modal — still used by the temporary adapter (see "Replacement mapping" above) | +| `packages/ui/src/preimage-modal.ts` | Used by temporary adapter | +| `packages/ui/src/password-prompt.ts` | Used by `apps/sandbox`, orthogonal | +| `packages/ui/src/topbar.ts` | UI shell, independent of host boundary | +| `packages/ui/src/ui.ts` | UI shell | +| `packages/ui/src/render.ts` | Iframe element creation, DOM plumbing (still used by `bridge.ts` for non-container paths) | +| `packages/shared/src/dotns-url.ts` | Parser used by `navigateTo` callback | +| `packages/auth/*` | Session, signing UI — used by temporary adapter | + +## Keep (host shell, unchanged) + +| Path | Reason | +| --------------------------------- | ------ | +| `apps/host/src/main.ts` | Landing + resolution + shell. Calls `@dotli/ui/bridge` which we rewrite; no direct host-API coupling | +| `apps/sandbox/src/main.ts` | Content fetch + render; not a host | +| `apps/protocol/*` | Layer-2 smoldot iframe; not touched | +| `packages/protocol/*` | Layer-2 client + messages + broker + auth-storage; not touched | +| `packages/resolver/*` | `.dot` name resolution; orthogonal | +| `packages/content/*` | Helia/IPFS archive fetch; orthogonal | +| `packages/storage/*` | CID cache; orthogonal | +| `packages/config/*` | Env + mode config; orthogonal | +| `packages/metrics/*` | Observability; orthogonal | +| `packages/sandbox-checker/*` | Sandbox lint overlay; orthogonal | +| `packages/shared/*` (minus dotns) | Shared helpers; orthogonal | + +## Rewrite (product launch entry) + +`packages/ui/src/bridge.ts` is the seam. Both `renderIframe(url, label)` and +`renderAppSubdomain(cid, label)` currently call `setupContainer` + +`setupNestedBridgeDetector` from `./container.ts`. Replace that pair with: + +```ts +import { createWebWasmRequestHostRuntime } from '@truapi/host-shared/dist/web-runtime.js'; +import { createIframeHost } from '@truapi/host-web'; +import { createHostCallbacks } from './handlers.js'; + +const runtime = await createWebWasmRequestHostRuntime( + createHostCallbacks({ log, label, storageScopeId: iframe.src }), +); + +const host = createIframeHost({ + iframeUrl: url, + allowedOrigin: new URL(url).origin, + container: app, + runtime, +}); +``` + +The `handlers.ts` / `callbacks/*.ts` files mirror the demo +(`demos/hosts/web/src/handlers.ts` and `src/callbacks/*.ts`) one-for-one; +each callback body swaps the demo's fixture/notifier call for the dotli +primitive per the "Replacement mapping" table above. + +The nested-bridge feature in `setupNestedBridgeDetector` (dApp-in-dApp) is +not supported by `createIframeHost` yet. Dotli has a `MAX_NESTED_BRIDGES` +config and multiple active users of nested bridges are unlikely, but **call +this out in the PR**: Phase 3 drops nested-bridge support until TrUAPI adds +an equivalent API. Track as a follow-up. + +## Dependency changes for Phase 3 + +Add to dotli root `package.json` (workspace-hoisted): + +```jsonc +"dependencies": { + "@truapi/client": "file:../../js/packages/truapi-client", + "@truapi/host-shared": "file:../../host-libs/js/shared", + "@truapi/host-web": "file:../../host-libs/js/web" +} +``` + +Path is relative to `web/host/` (dotli root), which resolves up into the +parent TrUAPI repo. `bun install` will symlink the local packages. + +Drop (once account/signing adapter retires): + +```jsonc +"@novasamatech/host-api": "...", +"@novasamatech/host-container": "...", +"@novasamatech/sdk-statement": "...", +"@novasamatech/statement-store": "...", +"@novasamatech/host-papp": "..." +``` + +Phase 3 keeps them for the temporary adapter. A follow-up PR drops them +along with `statement-store-mapping.ts`, `auth/signing.ts`, etc. + +## Phase 3 file plan + +New: +- `web/host/packages/ui/src/host-callbacks/handlers.ts` +- `web/host/packages/ui/src/host-callbacks/FeatureSupported.ts` +- `web/host/packages/ui/src/host-callbacks/NavigateTo.ts` +- `web/host/packages/ui/src/host-callbacks/PushNotification.ts` +- `web/host/packages/ui/src/host-callbacks/DevicePermission.ts` +- `web/host/packages/ui/src/host-callbacks/RemotePermission.ts` +- `web/host/packages/ui/src/host-callbacks/LocalStorage.ts` + +Modified: +- `web/host/packages/ui/src/bridge.ts` — swap `setupContainer` for `createIframeHost` + host runtime +- `web/host/packages/ui/package.json` — add `@truapi/*` deps (or at root if hoisted) +- `web/host/package.json` — hoist `@truapi/*` workspace deps + +Deleted: +- `web/host/packages/ui/src/container.ts` (entire file) + +Retained + rewired (temporary adapter for account/signing/statement/preimage): +- TODO in Phase 3: design a minimal shim that keeps those handlers reachable + over the TrUAPI iframe boundary, or accept that dotli temporarily loses + those features on `pg/truapi` until TrUAPI expands its callback set. + Recommend feature-flagging behind `import.meta.env.VITE_TRUAPI = "1"` for + the first iteration so the `main` branch is unaffected. From b37641db8c5d8db80f1c637a48090e8f7e27b811 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Thu, 30 Apr 2026 12:29:08 +0200 Subject: [PATCH 02/83] Add @truapi/* workspace deps + serve-from-monorepo Adds the three TrUAPI host packages (@truapi/client, @truapi/host-shared, @truapi/host-web) as file: workspace deps, alongside the existing novasamatech ones (those are dropped in a follow-up). Vite needs an explicit server.fs.allow entry rooted at the truapi monorepo so the file: dependency resolution can serve sources from outside the dotli package tree. --- apps/host/vite.config.ts | 4 + bun.lock | 184 ++++++++++++++++++++++++++++----------- packages/ui/package.json | 3 + 3 files changed, 141 insertions(+), 50 deletions(-) diff --git a/apps/host/vite.config.ts b/apps/host/vite.config.ts index dfa99b5..ccb1592 100644 --- a/apps/host/vite.config.ts +++ b/apps/host/vite.config.ts @@ -317,6 +317,7 @@ function sentry(): Plugin | false { const PACKAGES = resolve(import.meta.dirname, "../../packages"); const SANDBOX_CHECKER_SRC = resolve(PACKAGES, "sandbox-checker/src"); +const TRUAPI_REPO_ROOT = resolve(import.meta.dirname, "../../../.."); export default defineConfig({ base: process.env.VITE_APP_URL @@ -378,6 +379,9 @@ export default defineConfig({ }, }, server: { + fs: { + allow: [resolve(import.meta.dirname, "../.."), TRUAPI_REPO_ROOT], + }, headers: { "Service-Worker-Allowed": "/", "Access-Control-Allow-Origin": "*", diff --git a/bun.lock b/bun.lock index 51eba19..0c2e8fd 100644 --- a/bun.lock +++ b/bun.lock @@ -1,9 +1,8 @@ { "lockfileVersion": 1, - "configVersion": 1, + "configVersion": 0, "workspaces": { "": { - "name": "dotli", "devDependencies": { "prettier": "^3.8.3", "turbo": "^2.9.6", @@ -312,6 +311,9 @@ "@novasamatech/host-papp": "^0.7.0", "@novasamatech/sdk-statement": "^0.6.0", "@novasamatech/statement-store": "^0.7.0", + "@truapi/client": "file:../../../../js/packages/truapi-client", + "@truapi/host-shared": "file:../../../../host-libs/js/shared", + "@truapi/host-web": "file:../../../../host-libs/js/web", "neverthrow": "^8.2.0", "polkadot-api": "^2.1.0", "qrcode": "^1.5.4", @@ -433,57 +435,57 @@ "@ensdomains/content-hash": ["@ensdomains/content-hash@3.0.0", "", { "dependencies": { "@multiformats/sha3": "^2.0.17", "multiformats": "^12.0.1" } }, "sha512-5ongDPX9qDkemcYZ2rPXsRzCRDneoR2xujm2Tq3u0ZefQa49ZAURVKGqbJdjoL0VIZDfceLkBXekYKqkfrR5Ww=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], @@ -649,19 +651,19 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@novasamatech/host-api": ["@novasamatech/host-api@0.7.1", "", { "dependencies": { "@novasamatech/scale": "0.7.1", "nanoevents": "9.1.0", "nanoid": "5.1.9", "neverthrow": "^8.2.0", "scale-ts": "1.6.1" } }, "sha512-B+ld8mqw44xsGUvXdnrWI+QZujVtHDmrdnqieTStBWC27OWN5afzDNpaJ8cl/YAoPBIPFSOp48mtFe218CQ5zA=="], + "@novasamatech/host-api": ["@novasamatech/host-api@0.7.4", "", { "dependencies": { "@novasamatech/scale": "0.7.4", "nanoevents": "9.1.0", "nanoid": "5.1.9", "neverthrow": "^8.2.0", "scale-ts": "1.6.1" } }, "sha512-2Nd8QMNmpJI5vIKxJEVEIyVr9nt6+v50GY1/IbqM0yo1TiDXIrZJEW4m9C9sbqTR7wAqgVus47t4osvQtGt6wg=="], - "@novasamatech/host-container": ["@novasamatech/host-container@0.7.1", "", { "dependencies": { "@noble/hashes": "2.2.0", "@novasamatech/host-api": "0.7.1", "nanoid": "5.1.9", "neverthrow": "^8.2.0", "polkadot-api": ">=2" } }, "sha512-qlDYWu8xYoVbbFMy9UKgHQULroTFNZdC14I9VTFpP0fZvrpgqA8C0EpbiKSy2sfHhYcRs7wLUasTB4nuNOhIbQ=="], + "@novasamatech/host-container": ["@novasamatech/host-container@0.7.4", "", { "dependencies": { "@noble/hashes": "2.2.0", "@novasamatech/host-api": "0.7.4", "nanoid": "5.1.9", "neverthrow": "^8.2.0", "polkadot-api": ">=2" } }, "sha512-wlb3V50ndn+79HcZfoja9+EOoUsmQvDuVNBYV79euC31ffvklUw8unagOz4o35JO4XSfxuUREzu/GUmD2t5wiw=="], - "@novasamatech/host-papp": ["@novasamatech/host-papp@0.7.1", "", { "dependencies": { "@noble/ciphers": "2.2.0", "@noble/curves": "2.2.0", "@noble/hashes": "2.2.0", "@novasamatech/host-api": "0.7.1", "@novasamatech/scale": "0.7.1", "@novasamatech/statement-store": "0.7.1", "@novasamatech/storage-adapter": "0.7.1", "@polkadot-labs/hdkd-helpers": "^0.0.29", "nanoevents": "9.1.0", "nanoid": "5.1.9", "neverthrow": "^8.2.0", "polkadot-api": ">=2", "scale-ts": "1.6.1", "verifiablejs": "1.2.0" } }, "sha512-Q0kwYxT/wYJEMnmX0E1mU+a/iNBxdqCzbdhCFvTSoC0cM7LIPZI/52tl38LwaQJZyiI1aGC2wjR8BbguKRv5Kg=="], + "@novasamatech/host-papp": ["@novasamatech/host-papp@0.7.4", "", { "dependencies": { "@noble/ciphers": "2.2.0", "@noble/curves": "2.2.0", "@noble/hashes": "2.2.0", "@novasamatech/host-api": "0.7.4", "@novasamatech/scale": "0.7.4", "@novasamatech/statement-store": "0.7.4", "@novasamatech/storage-adapter": "0.7.4", "@polkadot-labs/hdkd-helpers": "^0.0.30", "nanoevents": "9.1.0", "nanoid": "5.1.9", "neverthrow": "^8.2.0", "polkadot-api": ">=2", "scale-ts": "1.6.1", "verifiablejs": "1.2.0" } }, "sha512-3P4yBZAKjg95AS7EiZ9laNa7hAIEklFacf1Z0ayMBYXombThnrSM+co7fQrTxj+PyRPFWk41bWJiFhU6NLBjlg=="], - "@novasamatech/scale": ["@novasamatech/scale@0.7.1", "", { "dependencies": { "@polkadot-api/utils": "^0.4.0", "scale-ts": "1.6.1" } }, "sha512-sx7Qya2uCUlx0h5IDywaclkbEBQyzOASbdZouHiNgYUujG7/4ETC1CKu690pFaaLs1R1rqpSaT3m/AS33z8M1w=="], + "@novasamatech/scale": ["@novasamatech/scale@0.7.4", "", { "dependencies": { "@polkadot-api/utils": "^0.4.0", "scale-ts": "1.6.1" } }, "sha512-kxdKxtSKBkgoXYMWD0iXZMOa4cDXRFZ1mRbMpHvnZG3jNCF9zr1QPo8jnTmut1vbHJ6O3o+At+4dI6CiU6SFOw=="], "@novasamatech/sdk-statement": ["@novasamatech/sdk-statement@0.6.0", "", { "dependencies": { "@polkadot-api/substrate-bindings": "0.20.1", "@polkadot-api/utils": "^0.4.0", "polkadot-api": "^2.0.2" } }, "sha512-NTqM+yS45iHgy87lVSWIpFozrTfCUWb7r4ZpsDtN1eFnfixXpDKpPXDWQsvPs+nh18r/jmoMgtlTjUltrS6mYQ=="], - "@novasamatech/statement-store": ["@novasamatech/statement-store@0.7.1", "", { "dependencies": { "@noble/ciphers": "2.2.0", "@noble/hashes": "2.2.0", "@novasamatech/scale": "0.7.1", "@novasamatech/sdk-statement": "^0.6.0", "@polkadot-api/substrate-bindings": "^0.20.1", "@polkadot-api/substrate-client": "^0.7.0", "@polkadot-labs/hdkd-helpers": "^0.0.29", "@scure/sr25519": "1.0.0", "nanoid": "5.1.9", "neverthrow": "^8.2.0", "polkadot-api": ">=2", "scale-ts": "1.6.1" } }, "sha512-Iilo9w84ieQvAYn7aRV2OJOsIEV/UAyeh/qkjSzDvZ8AOi6I+HV+bb/+fomg2CMfeQc0GIpu5MHuw3v7+QIDBA=="], + "@novasamatech/statement-store": ["@novasamatech/statement-store@0.7.4", "", { "dependencies": { "@noble/ciphers": "2.2.0", "@noble/hashes": "2.2.0", "@novasamatech/scale": "0.7.4", "@novasamatech/sdk-statement": "^0.6.0", "@polkadot-api/substrate-bindings": "^0.20.1", "@polkadot-api/substrate-client": "^0.7.0", "@polkadot-labs/hdkd-helpers": "^0.0.30", "@scure/sr25519": "2.2.0", "nanoid": "5.1.9", "neverthrow": "^8.2.0", "polkadot-api": ">=2", "scale-ts": "1.6.1" } }, "sha512-CVXxVb/EILJkZz4sLnc6tA/GdUmcpyEWOZZlc09cusAtFrgrYspDrsbnqusV6wbrTCLw2/H5UOvSGSRMTyov0g=="], - "@novasamatech/storage-adapter": ["@novasamatech/storage-adapter@0.7.1", "", { "dependencies": { "nanoevents": "^9.1.0", "neverthrow": "^8.2.0" } }, "sha512-hcHkJKgeXHIVaHug/qSWf/nY9fkdu2ymNN3kdXZB/L1IsWhFQJvf2ojYMq4jW8VvNKSPMllaHY2eNOWWmRZeGw=="], + "@novasamatech/storage-adapter": ["@novasamatech/storage-adapter@0.7.4", "", { "dependencies": { "nanoevents": "^9.1.0", "neverthrow": "^8.2.0" } }, "sha512-x17MQWcDrEbB82w7g9VSlteivi/0lbh7lTCpBSz6B/Ri4nlg54vvBuTj9md26GSVTHhd1bDdvSNfpoeLyXhPsA=="], "@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="], @@ -849,23 +851,23 @@ "@rx-state/core": ["@rx-state/core@0.1.4", "", { "peerDependencies": { "rxjs": ">=7" } }, "sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ=="], - "@scure/base": ["@scure/base@2.2.0", "", {}, "sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg=="], + "@scure/base": ["@scure/base@2.0.0", "", {}, "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="], "@scure/sr25519": ["@scure/sr25519@1.0.0", "", { "dependencies": { "@noble/curves": "~2.0.0", "@noble/hashes": "~2.0.0" } }, "sha512-b+uhK5akMINXZP95F3gJGcb5CMKYxf+q55fwMl0GoBwZDbWolmGNi1FrBSwuaZX5AhqS2byHiAueZgtDNpot2A=="], "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], - "@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.50.0", "", { "dependencies": { "@sentry/core": "10.50.0" } }, "sha512-42bxyRTxnCmYlWnvz4CxikuQNanw8UNma2WJrtxJ0f1MAJV2GhQGSHDLnA+lvFlmiz6qct3pfen/NXGyOTegTA=="], + "@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.51.0", "", { "dependencies": { "@sentry/core": "10.51.0" } }, "sha512-lNKBS4P7RUvf1niojXQWe9bU3gnBUCbST4Dj0pSiyat1N96cXVyHkeE+uGxowD0RrVWhs+kGHiVX3FcmRWF6sA=="], - "@sentry-internal/feedback": ["@sentry-internal/feedback@10.50.0", "", { "dependencies": { "@sentry/core": "10.50.0" } }, "sha512-0k9XZF0wn86f77mIO2U3gNNyDZooy139CnEanRzHinrN106vVzvBZ6TUEQoHtoO1fqQxr+nWWVrqV/PXUqk47w=="], + "@sentry-internal/feedback": ["@sentry-internal/feedback@10.51.0", "", { "dependencies": { "@sentry/core": "10.51.0" } }, "sha512-bCM95bcpphx28e6aU0bwRLxOgwosYsdNzezM1sM0pVOkb0TB3hDFRamramVDK+/Hp1o8qmRxS4c5w/A7YBZGkA=="], - "@sentry-internal/replay": ["@sentry-internal/replay@10.50.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.50.0", "@sentry/core": "10.50.0" } }, "sha512-51FYNfnvVLAWw1rrEWPFfwHuMRb9mkVCFGA4J9/un7SpeGBsQDziGB0Di4fsCxI7+EdSBpfLHPF0csKtCCw0oQ=="], + "@sentry-internal/replay": ["@sentry-internal/replay@10.51.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.51.0", "@sentry/core": "10.51.0" } }, "sha512-jCpI5HXSwK6ZT2HX70+mDRciAocHzSiDk4DTgvzV69Wvd+Ei5WLgE+d39eaEPsm8lUC0Ydntb5sJIB6uG9D4bw=="], - "@sentry-internal/replay-canvas": ["@sentry-internal/replay-canvas@10.50.0", "", { "dependencies": { "@sentry-internal/replay": "10.50.0", "@sentry/core": "10.50.0" } }, "sha512-jx6RKBmcJSWdI92qDGS/sBv1w+7Cww879Z/moX7bw7ipHa/Ts3iDcB3rgZwvhmi17U+mvYsbJeL2DXkPo3TjPw=="], + "@sentry-internal/replay-canvas": ["@sentry-internal/replay-canvas@10.51.0", "", { "dependencies": { "@sentry-internal/replay": "10.51.0", "@sentry/core": "10.51.0" } }, "sha512-8PW1Pp+Yl3lPwYqhBCr5SgkuhDanu9ZLzUqD2bPKL/ElqbM2eDVIWxq4z4ZzePrmZa6IcCjTv6sVQJ7Z4dLyLA=="], "@sentry/babel-plugin-component-annotate": ["@sentry/babel-plugin-component-annotate@5.2.0", "", {}, "sha512-8LbOI5Kzb5F0+7LVQPi2+zGz1iPiRRFhM+7uZ/ZQ33L9BmDOYNIy3xWxCfMw2JCuMXXaxF47XCjGmR22/B0WPg=="], - "@sentry/browser": ["@sentry/browser@10.50.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.50.0", "@sentry-internal/feedback": "10.50.0", "@sentry-internal/replay": "10.50.0", "@sentry-internal/replay-canvas": "10.50.0", "@sentry/core": "10.50.0" } }, "sha512-1f6rAvET6myiTaSeYqvaaBwvq1LfxqWjAPIoAW/NVC9bPMkeEcuvgDajHrnZMrBeWoJ81NMyoLkyX+iOc7MoFA=="], + "@sentry/browser": ["@sentry/browser@10.51.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.51.0", "@sentry-internal/feedback": "10.51.0", "@sentry-internal/replay": "10.51.0", "@sentry-internal/replay-canvas": "10.51.0", "@sentry/core": "10.51.0" } }, "sha512-Zdc0sKfenxUtW/OGhtJ7xHFN44bXR7YqxJ1zBDzlZfW0nTbeTTUZBq9z5NUw6qdS0Vs/i3V4qzAKTbRKWfqSEA=="], "@sentry/bundler-plugin-core": ["@sentry/bundler-plugin-core@5.2.0", "", { "dependencies": { "@babel/core": "^7.18.5", "@sentry/babel-plugin-component-annotate": "5.2.0", "@sentry/cli": "^2.58.5", "dotenv": "^16.3.1", "find-up": "^5.0.0", "glob": "^13.0.6", "magic-string": "~0.30.8" } }, "sha512-+C0x4gEIJRgoMwyRFGx+TFiJ1Po2BZlT1v61+PnouiaprKL5qtZG8n5PXx/5LPLDsVjSIcXjnDrTz9aSm8SJ3w=="], @@ -887,7 +889,7 @@ "@sentry/cli-win32-x64": ["@sentry/cli-win32-x64@2.58.5", "", { "os": "win32", "cpu": "x64" }, "sha512-IZf+XIMiQwj+5NzqbOQfywlOitmCV424Vtf9c+ep61AaVScUFD1TSrQbOcJJv5xGxhlxNOMNgMeZhdexdzrKZg=="], - "@sentry/core": ["@sentry/core@10.50.0", "", {}, "sha512-J4A+vzUO3adl0TkFCjaN1+4miamrjHiEIYuLHiuu1lmAjq5WIVw32ObvAh4yMwNtxyaEMosTrrh5M6f12XSJFg=="], + "@sentry/core": ["@sentry/core@10.51.0", "", {}, "sha512-Y45V/YXvVLEXmOdkbD1oG1gkRWFi9guCEGg3PlIlIpRjAbZUrvLGgjRJIc1E7XpSzmOnWbs5BbUxMv4PDaPj2w=="], "@sentry/rollup-plugin": ["@sentry/rollup-plugin@5.2.0", "", { "dependencies": { "@sentry/bundler-plugin-core": "5.2.0", "magic-string": "~0.30.8" }, "peerDependencies": { "rollup": ">=3.2.0" }, "optionalPeers": ["rollup"] }, "sha512-a8LfpvcYMFtFSroro5MpCcOoS528LeLfUHzxWURnpofOnY+Aso9Si4y4dFlna+RKqxCXjmFbn6CLnfI+YrHysQ=="], @@ -997,7 +999,7 @@ "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], - "ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], "anser": ["anser@1.4.10", "", {}, "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww=="], @@ -1015,7 +1017,7 @@ "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], - "axios": ["axios@1.15.2", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A=="], + "axios": ["axios@1.15.1", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-WOG+Jj8ZOvR0a3rAn+Tuf1UQJRxw5venr6DgdbJzngJE3qG7X0kL83CZGpdHMxEm+ZK3seAbvFsw4FfOfP9vxg=="], "babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.33.3", "", { "dependencies": { "hermes-parser": "0.33.3" } }, "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA=="], @@ -1023,7 +1025,7 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.20", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ=="], "bl": ["bl@5.1.0", "", { "dependencies": { "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ=="], @@ -1049,7 +1051,7 @@ "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - "caniuse-lite": ["caniuse-lite@1.0.30001790", "", {}, "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001788", "", {}, "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ=="], "cborg": ["cborg@5.1.0", "", { "bin": { "cborg": "lib/bin.js" } }, "sha512-OwailRORD5pIyxaLKKVdGHQ2OXWmYW7YsjDXnl64cIwWdQiyXRffVFsL34JR5c+BEeeSOHADM3cy/QNERH1SUw=="], @@ -1127,7 +1129,7 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "electron-to-chromium": ["electron-to-chromium@1.5.344", "", {}, "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg=="], + "electron-to-chromium": ["electron-to-chromium@1.5.341", "", {}, "sha512-1sZTssferjgDgaqRTc0ieP+ozzpOy7LQTPTtEW3yQFn4+ORdIAZWV5BthXPyHF7YqLvFJCUPhNhdAJQYlYUgiw=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -1151,7 +1153,7 @@ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - "esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -1597,7 +1599,7 @@ "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], - "node-releases": ["node-releases@2.0.38", "", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="], + "node-releases": ["node-releases@2.0.37", "", {}, "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg=="], "normalize-package-data": ["normalize-package-data@8.0.0", "", { "dependencies": { "hosted-git-info": "^9.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ=="], @@ -1619,7 +1621,7 @@ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - "ora": ["ora@9.4.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.3.2", "string-width": "^8.1.0" } }, "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ=="], + "ora": ["ora@9.3.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.3.1", "string-width": "^8.1.0" } }, "sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw=="], "p-defer": ["p-defer@4.0.1", "", {}, "sha512-Mr5KC5efvAK5VUptYEIopP1bakB85k2IWXaRC0rsh1uwn1L6M0LVml8OIQ4Gudg4oyZakf7FmeRLkMMtZW1i5A=="], @@ -1847,7 +1849,7 @@ "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], - "terser": ["terser@5.46.2", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw=="], + "terser": ["terser@5.46.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ=="], "throat": ["throat@5.0.0", "", {}, "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="], @@ -2001,6 +2003,12 @@ "@commander-js/extra-typings/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + "@dotli/ui/@truapi/client": ["@truapi/client@file:../../js/packages/truapi-client", { "dependencies": { "scale-ts": "^1.6.1" }, "devDependencies": { "typescript": "^5.7" } }], + + "@dotli/ui/@truapi/host-shared": ["@truapi/host-shared@file:../../host-libs/js/shared", { "dependencies": { "@truapi/client": "file:../../../js/packages/truapi-client" }, "devDependencies": { "typescript": "^5.7" } }], + + "@dotli/ui/@truapi/host-web": ["@truapi/host-web@file:../../host-libs/js/web", { "dependencies": { "@truapi/client": "file:../../../js/packages/truapi-client", "@truapi/host-shared": "file:../shared" }, "devDependencies": { "typescript": "^5.7" } }], + "@ensdomains/content-hash/multiformats": ["multiformats@12.1.3", "", {}, "sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], @@ -2013,6 +2021,8 @@ "@libp2p/autonat/protons-runtime": ["protons-runtime@6.0.1", "", { "dependencies": { "uint8-varint": "^2.0.4", "uint8arraylist": "^2.4.8", "uint8arrays": "^5.1.0" } }, "sha512-ONL+jDj143WA1m+WKLuuqBIaDKxm32dx6HfJdyujrRcni/6KkhXzVnyg22nH/Wwqmbwnd1BKUVkD1hMEWZFeww=="], + "@libp2p/circuit-relay-v2/nanoid": ["nanoid@5.1.7", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ=="], + "@libp2p/circuit-relay-v2/protons-runtime": ["protons-runtime@6.0.1", "", { "dependencies": { "uint8-varint": "^2.0.4", "uint8arraylist": "^2.4.8", "uint8arrays": "^5.1.0" } }, "sha512-ONL+jDj143WA1m+WKLuuqBIaDKxm32dx6HfJdyujrRcni/6KkhXzVnyg22nH/Wwqmbwnd1BKUVkD1hMEWZFeww=="], "@libp2p/crypto/@noble/curves": ["@noble/curves@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0" } }, "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ=="], @@ -2047,6 +2057,12 @@ "@novasamatech/host-papp/@noble/curves": ["@noble/curves@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0" } }, "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ=="], + "@novasamatech/host-papp/@polkadot-labs/hdkd-helpers": ["@polkadot-labs/hdkd-helpers@0.0.30", "", { "dependencies": { "@noble/curves": "^2.2.0", "@noble/hashes": "^2.2.0", "@scure/base": "^2.2.0", "@scure/sr25519": "^1.0.0", "scale-ts": "^1.6.1" } }, "sha512-qWmmD6ayj14RenDuDFfjF3sHS7ObqPzwIIMPcSVoDeKFSeQV7RY0HwyhC5CG4i6FoguMzak2dbtjYpNN5XQiwQ=="], + + "@novasamatech/statement-store/@polkadot-labs/hdkd-helpers": ["@polkadot-labs/hdkd-helpers@0.0.30", "", { "dependencies": { "@noble/curves": "^2.2.0", "@noble/hashes": "^2.2.0", "@scure/base": "^2.2.0", "@scure/sr25519": "^1.0.0", "scale-ts": "^1.6.1" } }, "sha512-qWmmD6ayj14RenDuDFfjF3sHS7ObqPzwIIMPcSVoDeKFSeQV7RY0HwyhC5CG4i6FoguMzak2dbtjYpNN5XQiwQ=="], + + "@novasamatech/statement-store/@scure/sr25519": ["@scure/sr25519@2.2.0", "", { "dependencies": { "@noble/curves": "~2.2.0", "@noble/hashes": "~2.2.0" } }, "sha512-UTOZb6Hzw44REQdl2SWNBhBFIoqOIhMLNIz3zYyVQLbqdshhuyuuxYoibKHlDg9oqdwdCHQe5LkTsevugPpUbw=="], + "@polkadot-api/cli/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], "@polkadot-api/smoldot/smoldot": ["smoldot@3.1.0", "", { "dependencies": { "ws": "^8.8.1" } }, "sha512-LZcex0BnfY1ORmuj0rKEF1aw7/gdFq6e5bBsLWd6x/fTt63mpAORhxf6fTsQcce0yLtbzkll8J+YKwhJLYATdw=="], @@ -2163,6 +2179,8 @@ "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + "vitest/vite": ["vite@8.0.9", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.10", "rolldown": "1.0.0-rc.16", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw=="], + "weald/ms": ["ms@3.0.0-canary.202508261828", "", {}, "sha512-NotsCoUCIUkojWCzQff4ttdCfIPoA1UGZsyQbi7KmqkNRfKCrvga8JJi2PknHymHOuor0cJSn/ylj52Cbt2IrQ=="], "write-package/read-pkg": ["read-pkg@9.0.1", "", { "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" } }, "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA=="], @@ -2171,8 +2189,30 @@ "yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], + "@dotli/ui/@truapi/client/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "@dotli/ui/@truapi/host-shared/@truapi/client": ["@truapi/client@file:../../js/packages/truapi-client", {}], + + "@dotli/ui/@truapi/host-shared/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "@dotli/ui/@truapi/host-web/@truapi/client": ["@truapi/client@file:../../js/packages/truapi-client", {}], + + "@dotli/ui/@truapi/host-web/@truapi/host-shared": ["@truapi/host-shared@file:../../host-libs/js/shared", {}], + + "@dotli/ui/@truapi/host-web/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "@jest/types/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "@novasamatech/host-papp/@polkadot-labs/hdkd-helpers/@scure/base": ["@scure/base@2.2.0", "", {}, "sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg=="], + + "@novasamatech/statement-store/@polkadot-labs/hdkd-helpers/@noble/curves": ["@noble/curves@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0" } }, "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ=="], + + "@novasamatech/statement-store/@polkadot-labs/hdkd-helpers/@scure/base": ["@scure/base@2.2.0", "", {}, "sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg=="], + + "@novasamatech/statement-store/@polkadot-labs/hdkd-helpers/@scure/sr25519": ["@scure/sr25519@1.0.0", "", { "dependencies": { "@noble/curves": "~2.0.0", "@noble/hashes": "~2.0.0" } }, "sha512-b+uhK5akMINXZP95F3gJGcb5CMKYxf+q55fwMl0GoBwZDbWolmGNi1FrBSwuaZX5AhqS2byHiAueZgtDNpot2A=="], + + "@novasamatech/statement-store/@scure/sr25519/@noble/curves": ["@noble/curves@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0" } }, "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ=="], + "@react-native/codegen/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], "@react-native/codegen/yargs/y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], @@ -2221,12 +2261,18 @@ "tar-stream/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + "vitest/vite/rolldown": ["rolldown@1.0.0-rc.16", "", { "dependencies": { "@oxc-project/types": "=0.126.0", "@rolldown/pluginutils": "1.0.0-rc.16" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.16", "@rolldown/binding-darwin-arm64": "1.0.0-rc.16", "@rolldown/binding-darwin-x64": "1.0.0-rc.16", "@rolldown/binding-freebsd-x64": "1.0.0-rc.16", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.16", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.16", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.16", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.16", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.16", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.16", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.16" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g=="], + "write-package/read-pkg/normalize-package-data": ["normalize-package-data@6.0.2", "", { "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g=="], "write-package/read-pkg/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="], "yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + "@novasamatech/statement-store/@polkadot-labs/hdkd-helpers/@scure/sr25519/@noble/curves": ["@noble/curves@2.0.1", "", { "dependencies": { "@noble/hashes": "2.0.1" } }, "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw=="], + + "@novasamatech/statement-store/@polkadot-labs/hdkd-helpers/@scure/sr25519/@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="], + "@react-native/codegen/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "metro/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -2235,10 +2281,48 @@ "react-native/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "vitest/vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.126.0", "", {}, "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ=="], + + "vitest/vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.16", "", { "os": "android", "cpu": "arm64" }, "sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA=="], + + "vitest/vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ=="], + + "vitest/vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ=="], + + "vitest/vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm" }, "sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "s390x" }, "sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "x64" }, "sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.16", "", { "os": "linux", "cpu": "x64" }, "sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w=="], + + "vitest/vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.16", "", { "os": "none", "cpu": "arm64" }, "sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA=="], + + "vitest/vite/rolldown/@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.16", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ=="], + + "vitest/vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q=="], + + "vitest/vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.16", "", { "os": "win32", "cpu": "x64" }, "sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g=="], + + "vitest/vite/rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.16", "", {}, "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA=="], + "write-package/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="], "yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "vitest/vite/rolldown/@rolldown/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], + + "vitest/vite/rolldown/@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + "write-package/read-pkg/normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], diff --git a/packages/ui/package.json b/packages/ui/package.json index 94da2e7..e3597f5 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -34,6 +34,9 @@ "@noble/hashes": "^2.2.0", "@novasamatech/sdk-statement": "^0.6.0", "@novasamatech/statement-store": "^0.7.0", + "@truapi/client": "file:../../../../js/packages/truapi-client", + "@truapi/host-shared": "file:../../../../host-libs/js/shared", + "@truapi/host-web": "file:../../../../host-libs/js/web", "neverthrow": "^8.2.0", "polkadot-api": "^2.1.0", "qrcode": "^1.5.4" From 266c21889c611884005752b2c9ea48dc0bc821f6 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Thu, 30 Apr 2026 12:29:23 +0200 Subject: [PATCH 03/83] Add host-callbacks/ adapters mirroring demo structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Concrete WasmHostCallbacks impls grouped per RFC domain. Each file owns the bridge between TrUAPI's wire types (Uint8Array Hex, tagged unions) and the dotli primitives the syscall lands on: - Account / Signing → @novasamatech/host-papp session ops (D1 retires) - Chain → smoldot or RPC providers (E1 retires) - LocalStorage → window.localStorage - OpenUrl → window.open - Preimage → Helia (D2 retires) - PromptPermission → permission-modal + permissions.ts cache - PushNotification → Notification API - StatementStore → sdk-statement / statement-store via @dotli/auth - handlers.ts composes them into a single CreateHostCallbacks impl @dotli/auth re-exports the novasamatech statement types so consumers don't take direct deps on those SDKs. --- packages/auth/src/statement.ts | 15 + packages/ui/src/host-callbacks/Account.ts | 134 ++++++++ packages/ui/src/host-callbacks/Chain.ts | 66 ++++ .../ui/src/host-callbacks/LocalStorage.ts | 46 +++ packages/ui/src/host-callbacks/OpenUrl.ts | 55 ++++ packages/ui/src/host-callbacks/Preimage.ts | 100 ++++++ .../ui/src/host-callbacks/PromptPermission.ts | 146 +++++++++ .../ui/src/host-callbacks/PushNotification.ts | 16 + packages/ui/src/host-callbacks/Signing.ts | 108 +++++++ .../ui/src/host-callbacks/StatementStore.ts | 290 ++++++++++++++++++ packages/ui/src/host-callbacks/handlers.ts | 55 ++++ packages/ui/src/host-callbacks/hex.ts | 17 + packages/ui/src/host-callbacks/rate-limit.ts | 21 ++ 13 files changed, 1069 insertions(+) create mode 100644 packages/auth/src/statement.ts create mode 100644 packages/ui/src/host-callbacks/Account.ts create mode 100644 packages/ui/src/host-callbacks/Chain.ts create mode 100644 packages/ui/src/host-callbacks/LocalStorage.ts create mode 100644 packages/ui/src/host-callbacks/OpenUrl.ts create mode 100644 packages/ui/src/host-callbacks/Preimage.ts create mode 100644 packages/ui/src/host-callbacks/PromptPermission.ts create mode 100644 packages/ui/src/host-callbacks/PushNotification.ts create mode 100644 packages/ui/src/host-callbacks/Signing.ts create mode 100644 packages/ui/src/host-callbacks/StatementStore.ts create mode 100644 packages/ui/src/host-callbacks/handlers.ts create mode 100644 packages/ui/src/host-callbacks/hex.ts create mode 100644 packages/ui/src/host-callbacks/rate-limit.ts diff --git a/packages/auth/src/statement.ts b/packages/auth/src/statement.ts new file mode 100644 index 0000000..3373c44 --- /dev/null +++ b/packages/auth/src/statement.ts @@ -0,0 +1,15 @@ +// Re-exports from the novasamatech statement-store / sdk-statement packages. +// @dotli/auth owns the novasamatech dependency graph; consumers in other +// packages (e.g. @dotli/ui's host-callbacks) should route through here so +// they don't take direct deps on those SDKs. + +export { + createSr25519Prover, + type StatementStoreAdapter, +} from "@novasamatech/statement-store"; + +export type { + Proof, + SignedStatement, + Statement, +} from "@novasamatech/sdk-statement"; diff --git a/packages/ui/src/host-callbacks/Account.ts b/packages/ui/src/host-callbacks/Account.ts new file mode 100644 index 0000000..3872622 --- /dev/null +++ b/packages/ui/src/host-callbacks/Account.ts @@ -0,0 +1,134 @@ +// Account adapters — temporary bridge between the TrUAPI callback surface +// and the host-papp session exposed by `@dotli/auth`. The endpoints that +// dotli historically supported through `container.handleAccount*` are +// preserved verbatim here; new / unused ones fall through to +// `createUnavailableCallbacks()`. + +import type * as T from "@truapi/client"; +import type { + SubscribeNoArgs, + WasmHostCallbacks, +} from "@truapi/host-shared"; +import type { UserSession } from "@novasamatech/host-papp"; +import { + getAuthState, + onAuthStateChange, + type AuthState, +} from "@dotli/auth/auth"; +import { deriveProductPublicKey } from "@dotli/auth/account"; +import { showAliasPermissionModal } from "../alias-permission-modal"; +import { createSubmitRateLimiter } from "./rate-limit"; + +function getSession(): UserSession | null { + const state = getAuthState(); + return state.status === "authenticated" ? state.session : null; +} + +function subscribeSession( + callback: (session: UserSession | null) => void, +): () => void { + callback(getSession()); + return onAuthStateChange((state: AuthState) => { + callback(state.status === "authenticated" ? state.session : null); + }); +} + +function createAccountGet(): WasmHostCallbacks["accountGet"] { + return (request) => { + const [dotNsIdentifier, derivationIndex] = request.value; + const session = getSession(); + if (!session) { + return Promise.reject(new Error("Not connected")); + } + const publicKey = deriveProductPublicKey( + session.remoteAccount.accountId, + dotNsIdentifier, + derivationIndex, + ); + return Promise.resolve({ tag: "V2", value: { publicKey } }); + }; +} + +function createAccountGetAlias( + label: string, +): WasmHostCallbacks["accountGetAlias"] { + const limiter = createSubmitRateLimiter(); + return async (request) => { + if (!limiter.allow()) { + throw new Error("Rate limited"); + } + + const session = getSession(); + if (!session) { + throw new Error("Not connected"); + } + + const productAccountId = request.value; + const [productIdentifier] = productAccountId; + + if (!productIdentifier.endsWith(".dot")) { + throw new Error("Invalid domain"); + } + + const identifier = label + ".dot"; + const isOwnDomain = identifier === productIdentifier; + + if (!isOwnDomain) { + await showAliasPermissionModal(identifier, productIdentifier); + } + + const result = await session + .getRingVrfAlias(productAccountId, identifier) + .match( + (alias) => alias, + (error) => { + throw new Error(error.message); + }, + ); + + return { + tag: "V2", + value: { context: result.context, alias: result.alias }, + }; + }; +} + +function createGetNonProductAccounts(): WasmHostCallbacks["getNonProductAccounts"] { + return () => { + const state = getAuthState(); + if (state.status === "authenticated") { + const account: T.Account = { + publicKey: state.session.remoteAccount.accountId, + name: state.identity?.liteUsername, + }; + return Promise.resolve({ tag: "V2", value: [account] }); + } + return Promise.resolve({ tag: "V2", value: [] }); + }; +} + +function createAccountConnectionStatusSubscribe(): SubscribeNoArgs { + return (sendItem) => { + return subscribeSession((session) => { + const status: T.AccountConnectionStatus = session + ? { tag: "Connected", value: undefined } + : { tag: "Disconnected", value: undefined }; + sendItem({ tag: "V2", value: status }); + }); + }; +} + +export function createAccountAdapters(label: string): Pick< + WasmHostCallbacks, + | "accountGet" + | "accountGetAlias" + | "getNonProductAccounts" + | "accountConnectionStatusSubscribe" +> { + return { + accountGet: createAccountGet(), + accountGetAlias: createAccountGetAlias(label), + getNonProductAccounts: createGetNonProductAccounts(), + accountConnectionStatusSubscribe: createAccountConnectionStatusSubscribe(), + }; +} diff --git a/packages/ui/src/host-callbacks/Chain.ts b/packages/ui/src/host-callbacks/Chain.ts new file mode 100644 index 0000000..9881f19 --- /dev/null +++ b/packages/ui/src/host-callbacks/Chain.ts @@ -0,0 +1,66 @@ +// dot.li — TrUAPI chain callback +// +// Routes product chain RPC traffic through whichever backend the user +// has selected in the host shell ("Light Client" via smoldot, or +// "RPC Node" via curated WSS endpoints). +// +// Without this callback, truapi-server would fall back to its own +// bundled smoldot — which would ignore the toggle, double the +// light-client footprint, and rebuild a fresh chain alongside the one +// dotli's resolver already maintains. Routing through dotli's existing +// providers reuses already-synced chains and respects the toggle. + +import type { ChainConnect, WasmHostCallbacks } from "@truapi/host-shared"; +import { getChainBackend } from "@dotli/config/mode"; +import { + createChainProvider as createSmoldotChainProvider, + isChainSupported as isSmoldotChainSupported, +} from "@dotli/resolver/chains"; +import { + createRpcChainProvider, + isRpcChainSupported, +} from "@dotli/resolver/rpc-chain"; +import { log } from "@dotli/shared/log"; + +export function createChainConnect(): ChainConnect { + return (genesisHash, onResponse) => { + // host-shared transports raw JSON strings; polkadot-api's + // JsonRpcProvider speaks typed objects. Bridge with parse/stringify + // at this boundary so the adapter is otherwise a pass-through. + const onMessage = (message: unknown): void => { + onResponse(JSON.stringify(message)); + }; + const backend = getChainBackend(); + if (backend === "rpc") { + if (!isRpcChainSupported(genesisHash)) { + log.warn( + `[dot.li truapi-chain] RPC backend doesn't support ${genesisHash}; product call will fail`, + ); + return null; + } + const provider = createRpcChainProvider(genesisHash); + if (!provider) return null; + const conn = provider(onMessage); + return { + send: (request) => conn.send(JSON.parse(request)), + close: conn.disconnect, + }; + } + + if (!isSmoldotChainSupported(genesisHash)) { + log.warn( + `[dot.li truapi-chain] smoldot backend doesn't support ${genesisHash}; product call will fail`, + ); + return null; + } + const provider = createSmoldotChainProvider(genesisHash); + if (!provider) return null; + const conn = provider(onMessage); + return { + send: (request) => conn.send(JSON.parse(request)), + close: conn.disconnect, + }; + }; +} + +export type ChainCallback = NonNullable; diff --git a/packages/ui/src/host-callbacks/LocalStorage.ts b/packages/ui/src/host-callbacks/LocalStorage.ts new file mode 100644 index 0000000..64d2539 --- /dev/null +++ b/packages/ui/src/host-callbacks/LocalStorage.ts @@ -0,0 +1,46 @@ +// Mirrors the legacy `container.handleLocalStorage*` bodies: base64 +// round-trip into `window.localStorage[storagePrefix + key]`. Throwing an +// Error is the documented contract on `WasmHostCallbacks` failure. + +import type { WasmHostCallbacks } from "@truapi/host-shared"; + +export function createLocalStorageRead( + storagePrefix: string, +): WasmHostCallbacks["localStorageRead"] { + return (key) => { + try { + const raw = localStorage.getItem(storagePrefix + key); + if (raw === null) { + return undefined; + } + return Uint8Array.from(atob(raw), (c) => c.charCodeAt(0)); + } catch { + throw new Error("Failed to read from storage"); + } + }; +} + +export function createLocalStorageWrite( + storagePrefix: string, +): WasmHostCallbacks["localStorageWrite"] { + return (key, value) => { + try { + const b64 = btoa(String.fromCharCode(...value)); + localStorage.setItem(storagePrefix + key, b64); + } catch { + throw new Error("Failed to write to storage"); + } + }; +} + +export function createLocalStorageClear( + storagePrefix: string, +): WasmHostCallbacks["localStorageClear"] { + return (key) => { + try { + localStorage.removeItem(storagePrefix + key); + } catch { + throw new Error("Failed to clear storage"); + } + }; +} diff --git a/packages/ui/src/host-callbacks/OpenUrl.ts b/packages/ui/src/host-callbacks/OpenUrl.ts new file mode 100644 index 0000000..b25b441 --- /dev/null +++ b/packages/ui/src/host-callbacks/OpenUrl.ts @@ -0,0 +1,55 @@ +// Navigation callback. The Rust core pre-normalizes URLs, but dotli still +// needs to classify the result so `.dot` domains land on the right host +// subdomain and localhost products wrap into the configured host origin. + +import type { WasmHostCallbacks } from "@truapi/host-shared"; +import { isLocalhost, BASE_DOMAIN } from "@dotli/config/config"; +import { dotNsUrl } from "@dotli/shared/dotns-url"; + +function identifierToLabel(identifier: string): string { + return identifier.slice(0, -".dot".length); +} + +function buildDotTargetUrl(label: string, pathname: string): string { + const suffix = pathname ? "/" + pathname : ""; + if (isLocalhost) { + return `http://${label}.localhost:${window.location.port}${suffix}`; + } + return `${window.location.protocol}//${label}.${BASE_DOMAIN}${suffix}`; +} + +function getHostOrigin(): string { + if (isLocalhost) { + return `http://localhost:${window.location.port}`; + } + return `${window.location.protocol}//${BASE_DOMAIN}`; +} + +export function createOpenUrl(): WasmHostCallbacks["openUrl"] { + return (url) => { + const dotUrl = dotNsUrl.parseDotNsDomain(url); + + if (dotUrl && dotNsUrl.isDotDomain(dotUrl.identifier)) { + window.open( + buildDotTargetUrl( + identifierToLabel(dotUrl.identifier), + dotUrl.pathname, + ), + "_blank", + ); + return; + } + + const localhostUrl = dotNsUrl.parseLocalhostUrl(url); + if (localhostUrl) { + const suffix = localhostUrl.pathname ? "/" + localhostUrl.pathname : ""; + window.open( + `${getHostOrigin()}/${localhostUrl.host}${suffix}`, + "_blank", + ); + return; + } + + window.open(dotNsUrl.normalizeUrl(url), "_blank"); + }; +} diff --git a/packages/ui/src/host-callbacks/Preimage.ts b/packages/ui/src/host-callbacks/Preimage.ts new file mode 100644 index 0000000..08bd095 --- /dev/null +++ b/packages/ui/src/host-callbacks/Preimage.ts @@ -0,0 +1,100 @@ +// Preimage lookup adapter — polls the user-selected content backend +// (Helia P2P or IPFS gateway) until the preimage is found or the +// subscription is dropped. Mirrors the legacy +// `container.handlePreimageLookupSubscribe` behaviour. + +import type * as T from "@truapi/client"; +import type { + Subscribe, + WasmHostCallbacks, +} from "@truapi/host-shared"; +import { concatBytes } from "@noble/hashes/utils.js"; +import { hashToCid } from "@dotli/content/preimage"; +import { fetchFromIpfs } from "@dotli/content/ipfs"; +import { getContentBackend } from "@dotli/config/mode"; +import { log } from "@dotli/shared/log"; +import { toHexPrefixed } from "./hex"; + +const POLL_INTERVAL_MS = 10_000; +const INITIAL_POLL_DELAY_MS = 1000; + +function createPreimageLookupSubscribe( + label: string, +): Subscribe< + T.RemotePreimageLookupSubscribeRequest, + T.RemotePreimageLookupSubscribeItem +> { + return (request, sendItem) => { + const key = toHexPrefixed(request.value); + log.warn(`[${label}] Preimage lookup subscribe, key: ${key}`); + + let stopped = false; + sendItem({ tag: "V2", value: undefined }); + + const poll = async (): Promise => { + if (stopped) { + return; + } + + const cid = hashToCid(key); + const cidString = cid.toString(); + const contentBackend = getContentBackend(); + try { + if (contentBackend === "p2p-helia") { + const { ensureHelia } = await import("@dotli/content/fetch"); + const helia = await ensureHelia(); + const chunks: Uint8Array[] = []; + const blockData = helia.blockstore.get(cid); + if (blockData instanceof Uint8Array) { + chunks.push(blockData); + } else if ( + typeof blockData === "object" && + Symbol.asyncIterator in Object(blockData) + ) { + for await (const chunk of blockData as AsyncIterable) { + chunks.push(chunk); + } + } + if (chunks.length > 0) { + const data = concatBytes(...chunks); + if (data.length > 0) { + sendItem({ tag: "V2", value: data }); + return; + } + } + } else { + const result = await fetchFromIpfs(cidString); + if (result.data.length > 0) { + sendItem({ tag: "V2", value: result.data }); + return; + } + } + } catch (err) { + log.warn( + `[${label}] preimage lookup via ${contentBackend} failed:`, + err, + ); + } + }; + + const intervalId = setInterval(() => void poll(), POLL_INTERVAL_MS); + const initialTimeoutId = setTimeout( + () => void poll(), + INITIAL_POLL_DELAY_MS, + ); + + return () => { + stopped = true; + clearInterval(intervalId); + clearTimeout(initialTimeoutId); + }; + }; +} + +export function createPreimageAdapters( + label: string, +): Pick { + return { + preimageLookupSubscribe: createPreimageLookupSubscribe(label), + }; +} diff --git a/packages/ui/src/host-callbacks/PromptPermission.ts b/packages/ui/src/host-callbacks/PromptPermission.ts new file mode 100644 index 0000000..84daffc --- /dev/null +++ b/packages/ui/src/host-callbacks/PromptPermission.ts @@ -0,0 +1,146 @@ +// Permission prompt — collapses the legacy device/remote split into one +// synchronous `true|false` decision. The existing consent modal is async, +// so for `ask` state we return false synchronously (denying the request) +// and kick off the modal plus iframe reload in the background. Once the +// user grants, the reload re-runs the product with the permission set to +// `granted` so the next call resolves true without a round-trip. + +import type { + HostPermission, + WasmHostCallbacks, +} from "@truapi/host-shared"; +import type { RemotePermission } from "@truapi/client"; +import { + getPermissionStatus, + isEnforceableDevicePermission, + setPermissionStatus, + type DevicePermissionName, + type EnforceablePermissionName, +} from "../permissions"; +import { showPermissionRequestModal } from "../permission-modal"; +import { showNotification } from "../notification"; +import { createSubmitRateLimiter } from "./rate-limit"; + +// Storage uses uppercase `NFC`; the TrUAPI codec emits Pascal-cased `Nfc`. +// Other device tags align one-to-one. +function toDevicePermissionName( + tag: (HostPermission & { tag: "Device" })["value"]["tag"], +): DevicePermissionName { + return tag === "Nfc" ? "NFC" : (tag as DevicePermissionName); +} + +// Remote tags that don't reach a host enforcement point: WebRtc is gated +// by the iframe `allow` attribute, and `Remote` (HTTP/WS) can't be +// reliably intercepted from inside the sandbox. Auto-grant either. +function gatedRemotePermissionName( + tag: RemotePermission["tag"], +): EnforceablePermissionName | null { + switch (tag) { + case "ChainSubmit": + case "StatementSubmit": + return tag; + case "Remote": + case "WebRtc": + return null; + } +} + +function promptDevice( + label: string, + name: EnforceablePermissionName, + limiter: { allow: () => boolean }, +): void { + if (!limiter.allow()) { + return; + } + showPermissionRequestModal(label, name) + .then(() => { + setPermissionStatus(label, name, "granted"); + setTimeout(() => { + window.dispatchEvent( + new CustomEvent("dotli:device-permission-changed", { + detail: { label, permission: name }, + }), + ); + }, 0); + }) + .catch(() => { + setPermissionStatus(label, name, "denied"); + }); +} + +function promptRemote( + label: string, + name: EnforceablePermissionName, + limiter: { allow: () => boolean }, +): void { + if (!limiter.allow()) { + return; + } + showPermissionRequestModal(label, name) + .then(() => { + setPermissionStatus(label, name, "granted"); + window.dispatchEvent( + new CustomEvent("dotli:permission-changed", { + detail: { label }, + }), + ); + }) + .catch(() => { + setPermissionStatus(label, name, "denied"); + }); +} + +export function createPromptPermission( + label: string, +): WasmHostCallbacks["promptPermission"] { + const limiter = createSubmitRateLimiter(); + return (permission) => { + if (permission.tag === "Device") { + const tag = toDevicePermissionName(permission.value.tag); + // Notifications / OpenUrl have no host-side enforcement point; + // auto-grant rather than show a modal whose deny button can't + // actually block the underlying browser API. + if (!isEnforceableDevicePermission(tag)) { + return true; + } + return decide(label, tag, "Device", limiter); + } + + const name = gatedRemotePermissionName(permission.value.tag); + if (name === null) { + return true; + } + return decide(label, name, "Remote", limiter); + }; + + function decide( + label: string, + name: EnforceablePermissionName, + kind: "Device" | "Remote", + limiter: { allow: () => boolean }, + ): boolean { + const status = getPermissionStatus(label, name); + if (status === "granted") { + return true; + } + if (status === "denied") { + showNotification({ + label: `${label}.dot`, + text: + kind === "Device" + ? `${name} access is blocked. Use the permissions menu in the top bar to change this.` + : "Transaction signing is blocked. Use the permissions menu in the top bar to change this.", + dismissMs: 6000, + browserNotification: false, + }); + return false; + } + if (kind === "Device") { + promptDevice(label, name, limiter); + } else { + promptRemote(label, name, limiter); + } + return false; + } +} diff --git a/packages/ui/src/host-callbacks/PushNotification.ts b/packages/ui/src/host-callbacks/PushNotification.ts new file mode 100644 index 0000000..a4503ec --- /dev/null +++ b/packages/ui/src/host-callbacks/PushNotification.ts @@ -0,0 +1,16 @@ +// Push-notification callback: mirrors the legacy +// `container.handlePushNotification` body — log the request and forward to +// `showPushNotification({ text, deeplink, label })`. + +import type { WasmHostCallbacks } from "@truapi/host-shared"; +import { log } from "@dotli/shared/log"; +import { showNotification } from "../notification"; + +export function createPushNotification( + label: string, +): WasmHostCallbacks["pushNotification"] { + return ({ text, deeplink }) => { + log.warn(`[${label}] Push notification:`, { text, deeplink }); + showNotification({ text, deeplink, label }); + }; +} diff --git a/packages/ui/src/host-callbacks/Signing.ts b/packages/ui/src/host-callbacks/Signing.ts new file mode 100644 index 0000000..bd33915 --- /dev/null +++ b/packages/ui/src/host-callbacks/Signing.ts @@ -0,0 +1,108 @@ +// Signing adapters — translate TrUAPI `HostSign*Request/Response` (Uint8Array +// `Hex` fields) into the `0x${string}` shape consumed by `@dotli/auth`'s Sign +// modals. These live here until dotli migrates to a TrUAPI-native signing +// flow (D1). + +import type * as T from "@truapi/client"; +import type { WasmHostCallbacks } from "@truapi/host-shared"; +import type { UserSession } from "@novasamatech/host-papp"; +import { getAuthState } from "@dotli/auth/auth"; +import { + showSignPayloadModal, + showSignRawModal, + type ContainerSignPayloadRequest, + type SigningResult as PappSigningResult, +} from "@dotli/auth/signing"; +import { log } from "@dotli/shared/log"; +import { getPermissionStatus } from "../permissions"; +import { showNotification } from "../notification"; +import { toHexPrefixed, fromHexPrefixed } from "./hex"; + +function getSession(): UserSession | null { + const state = getAuthState(); + return state.status === "authenticated" ? state.session : null; +} + +function toContainerSignPayload( + payload: T.SigningPayload, +): ContainerSignPayloadRequest["payload"] { + return { + blockHash: toHexPrefixed(payload.blockHash), + blockNumber: toHexPrefixed(payload.blockNumber), + era: toHexPrefixed(payload.era), + genesisHash: toHexPrefixed(payload.genesisHash), + method: toHexPrefixed(payload.method), + nonce: toHexPrefixed(payload.nonce), + specVersion: toHexPrefixed(payload.specVersion), + tip: toHexPrefixed(payload.tip), + transactionVersion: toHexPrefixed(payload.transactionVersion), + signedExtensions: payload.signedExtensions, + version: payload.version, + assetId: payload.assetId ? toHexPrefixed(payload.assetId) : undefined, + metadataHash: payload.metadataHash + ? toHexPrefixed(payload.metadataHash) + : undefined, + mode: payload.mode, + withSignedTransaction: payload.withSignedTransaction, + }; +} + +function toSigningResult(result: PappSigningResult): T.SigningResult { + return { + signature: fromHexPrefixed(result.signature), + signedTransaction: + result.signedTransaction !== undefined + ? fromHexPrefixed(result.signedTransaction) + : undefined, + }; +} + +function createSignPayload(label: string): WasmHostCallbacks["signPayload"] { + return async (request) => { + const payload = request.value; + log.warn(`[${label}] signPayload invoked:`, { + genesisHash: toHexPrefixed(payload.genesisHash), + }); + if (getPermissionStatus(label, "ChainSubmit") !== "granted") { + showNotification({ + label: `${label}.dot`, + text: 'Transaction blocked — enable "Sign Transactions" in the permissions menu.', + dismissMs: 6000, + browserNotification: false, + }); + throw new Error("Permission denied"); + } + const session = getSession(); + if (!session) { + throw new Error("Not connected"); + } + const result = await showSignPayloadModal( + session, + toContainerSignPayload(payload), + label, + ); + return { tag: "V2", value: toSigningResult(result) }; + }; +} + +function createSignRaw(label: string): WasmHostCallbacks["signRaw"] { + return async (request) => { + log.warn(`[${label}] signRaw invoked`); + const session = getSession(); + if (!session) { + throw new Error("Not connected"); + } + const result = await showSignRawModal(session, request.value.data, label); + return { tag: "V2", value: toSigningResult(result) }; + }; +} + +export function createSigningAdapters(label: string): Pick< + WasmHostCallbacks, + "signPayload" | "signRaw" +> { + return { + signPayload: createSignPayload(label), + signRaw: createSignRaw(label), + }; +} diff --git a/packages/ui/src/host-callbacks/StatementStore.ts b/packages/ui/src/host-callbacks/StatementStore.ts new file mode 100644 index 0000000..f014865 --- /dev/null +++ b/packages/ui/src/host-callbacks/StatementStore.ts @@ -0,0 +1,290 @@ +// Statement-store adapters — glue between the TrUAPI host callbacks and +// `@novasamatech/sdk-statement` / `@novasamatech/statement-store`. The +// SCALE ↔ SDK mapping (hex strings vs Uint8Array, lowercase vs PascalCase +// proof tags) was previously in `statement-store-mapping.ts`; it lives +// here until the Rust core grows native statement handling (Phase D). + +import * as T from "@truapi/client"; +import type { + Subscribe, + WasmHostCallbacks, +} from "@truapi/host-shared"; +import { + createSr25519Prover, + type StatementStoreAdapter, + type Proof, + type SignedStatement as SdkSignedStatement, + type Statement as SdkStatement, +} from "@dotli/auth/statement"; +import { + getStatementStore, + onStatementStoreReady, + readSessionSecret, + getAuthState, +} from "@dotli/auth/auth"; +import { log } from "@dotli/shared/log"; +import { toHexPrefixed, fromHexPrefixed } from "./hex"; +import { createSubmitRateLimiter } from "./rate-limit"; + +// ── SCALE ↔ SDK mapping ──────────────────────────────────── +// +// SDK types use `0x${string}` (SizedHex) for fixed-size byte fields; the +// TrUAPI wire encoding uses raw `Uint8Array`. The cast to `SizedHex` below +// is safe because `toHexPrefixed` always emits the `0x`-prefixed lowercase +// form the SDK expects. + +type Sized = `0x${string}` & { __size?: N }; + +function toSized(bytes: Uint8Array): Sized { + return toHexPrefixed(bytes); +} + +function mapSdkProof(proof: Proof): T.StatementProof { + switch (proof.type) { + case "ecdsa": + return { + tag: "Ecdsa", + value: { + signature: fromHexPrefixed(proof.value.signature), + signer: fromHexPrefixed(proof.value.signer), + }, + }; + case "ed25519": + return { + tag: "Ed25519", + value: { + signature: fromHexPrefixed(proof.value.signature), + signer: fromHexPrefixed(proof.value.signer), + }, + }; + case "sr25519": + return { + tag: "Sr25519", + value: { + signature: fromHexPrefixed(proof.value.signature), + signer: fromHexPrefixed(proof.value.signer), + }, + }; + case "onChain": + return { + tag: "OnChain", + value: { + who: fromHexPrefixed(proof.value.who), + blockHash: fromHexPrefixed(proof.value.blockHash), + event: proof.value.event, + }, + }; + } +} + +function mapHostProof(proof: T.StatementProof): Proof { + switch (proof.tag) { + case "Ecdsa": + return { + type: "ecdsa", + value: { + signature: toSized<65>(proof.value.signature), + signer: toSized<33>(proof.value.signer), + }, + }; + case "Ed25519": + return { + type: "ed25519", + value: { + signature: toSized<64>(proof.value.signature), + signer: toSized<32>(proof.value.signer), + }, + }; + case "Sr25519": + return { + type: "sr25519", + value: { + signature: toSized<64>(proof.value.signature), + signer: toSized<32>(proof.value.signer), + }, + }; + case "OnChain": + return { + type: "onChain", + value: { + who: toSized<32>(proof.value.who), + blockHash: toSized<32>(proof.value.blockHash), + event: proof.value.event, + }, + }; + } +} + +function mapSdkSignedStatement( + statement: SdkSignedStatement, +): T.SignedStatement { + const result: T.SignedStatement = { + proof: mapSdkProof(statement.proof), + topics: (statement.topics ?? []).map(fromHexPrefixed), + }; + if (statement.expiry !== undefined) { + result.expiry = statement.expiry; + } + if (statement.channel) { + result.channel = fromHexPrefixed(statement.channel); + } + if (statement.data) { + result.data = statement.data; + } + return result; +} + +function mapHostSignedStatement( + statement: T.SignedStatement, +): SdkSignedStatement { + const result: SdkSignedStatement = { + proof: mapHostProof(statement.proof), + topics: statement.topics.map((t) => toSized<32>(t)), + }; + if (statement.expiry !== undefined) { + result.expiry = statement.expiry; + } + if (statement.channel) { + result.channel = toSized<32>(statement.channel); + } + if (statement.data) { + result.data = statement.data; + } + return result; +} + +function mapHostStatement(statement: T.Statement): SdkStatement { + const result: SdkStatement = { + topics: statement.topics.map((t) => toSized<32>(t)), + }; + if (statement.proof) { + result.proof = mapHostProof(statement.proof); + } + if (statement.expiry !== undefined) { + result.expiry = statement.expiry; + } + if (statement.channel) { + result.channel = toSized<32>(statement.channel); + } + if (statement.data) { + result.data = statement.data; + } + return result; +} + +// ── Callbacks ────────────────────────────────────────────── + +function createStatementStoreSubscribe( + label: string, +): Subscribe< + T.RemoteStatementStoreSubscribeRequest, + T.RemoteStatementStoreSubscribeItem +> { + return (request, sendItem) => { + // Drop wildcard (`undefined`) entries; the adapter only accepts a list of + // fully specified topic bytes. + const topics = request.value.topics.filter( + (t): t is Uint8Array => t !== undefined, + ); + let innerUnsub: (() => void) | null = null; + let cancelled = false; + + const startSubscription = (store: StatementStoreAdapter): void => { + if (cancelled) { + return; + } + log.warn(`[${label}] Statement store subscribe, topics:`, topics.length); + innerUnsub = store.subscribeStatements( + { matchAll: topics }, + (page) => { + const signed = page.statements.filter( + (s): s is SdkSignedStatement => s.proof !== undefined, + ); + if (signed.length > 0) { + sendItem({ + tag: "V2", + value: signed.map(mapSdkSignedStatement), + }); + } + }, + ); + }; + + const store = getStatementStore(); + if (store) { + startSubscription(store); + } else { + void onStatementStoreReady().then(startSubscription); + } + + return () => { + cancelled = true; + innerUnsub?.(); + }; + }; +} + +function createStatementStoreSubmit( + label: string, +): WasmHostCallbacks["statementStoreSubmit"] { + const limiter = createSubmitRateLimiter(); + return async (request) => { + const store = getStatementStore(); + if (!store) { + throw new Error("Statement store not initialized"); + } + if (!limiter.allow()) { + throw new Error("Rate limited"); + } + // The TrUAPI wire payload is a raw SCALE-encoded SignedStatement; decode + // via the truapi-client codec before handing it to the SDK adapter. + const decoded = T.SignedStatement.dec(request.value); + const result = await store.submitStatement(mapHostSignedStatement(decoded)); + if (result.isErr()) { + log.warn(`[${label}] submitStatement failed:`, result.error.message); + throw result.error; + } + // The SDK adapter resolves to `void`; the TrUAPI response is a string + // field historically used for a tx hash. Return empty until the adapter + // surfaces one. + return { tag: "V2", value: "" }; + }; +} + +function createStatementStoreCreateProof( + label: string, +): WasmHostCallbacks["statementStoreCreateProof"] { + return async (request) => { + const state = getAuthState(); + if (state.status !== "authenticated") { + throw new Error("Unable to sign: not authenticated"); + } + const session = state.session; + const secret = await readSessionSecret(session.id); + if (!secret) { + throw new Error("Unable to sign: no session secret"); + } + const prover = createSr25519Prover(secret); + const proofResult = await prover.generateMessageProof( + mapHostStatement(request.value.statement), + ); + if (proofResult.isErr()) { + log.warn(`[${label}] createProof failed:`, proofResult.error.message); + throw proofResult.error; + } + return { tag: "V2", value: mapSdkProof(proofResult.value.proof) }; + }; +} + +export function createStatementStoreAdapters(label: string): Pick< + WasmHostCallbacks, + | "statementStoreSubscribe" + | "statementStoreSubmit" + | "statementStoreCreateProof" +> { + return { + statementStoreSubscribe: createStatementStoreSubscribe(label), + statementStoreSubmit: createStatementStoreSubmit(label), + statementStoreCreateProof: createStatementStoreCreateProof(label), + }; +} diff --git a/packages/ui/src/host-callbacks/handlers.ts b/packages/ui/src/host-callbacks/handlers.ts new file mode 100644 index 0000000..f8ef514 --- /dev/null +++ b/packages/ui/src/host-callbacks/handlers.ts @@ -0,0 +1,55 @@ +// Composes the full `WasmHostCallbacks` surface expected by +// `createWebWorkerHostRuntime`. Each callback lives in its own file, +// mirroring the demo host layout under `demos/hosts/web/src/callbacks/`. +// +// Scoping: +// - `label` identifies the dApp, used in topbar notifications, permission +// storage keys, and sign modal titles. +// - `storagePrefix` scopes `localStorage` per dApp (and per nested bridge). +// +// Callbacks not yet owned by the Rust core (accounts, signing, statement +// store, preimage) are wired through temporary adapters that bridge into +// `@dotli/auth`. Phase D will swap those for core-native handlers. + +import { + createUnavailableCallbacks, + type WasmHostCallbacks, +} from "@truapi/host-shared/dist/runtime.js"; +import { createOpenUrl } from "./OpenUrl"; +import { createPushNotification } from "./PushNotification"; +import { createPromptPermission } from "./PromptPermission"; +import { + createLocalStorageRead, + createLocalStorageWrite, + createLocalStorageClear, +} from "./LocalStorage"; +import { createAccountAdapters } from "./Account"; +import { createSigningAdapters } from "./Signing"; +import { createStatementStoreAdapters } from "./StatementStore"; +import { createPreimageAdapters } from "./Preimage"; +import { createChainConnect } from "./Chain"; + +export interface CreateHostCallbacksOptions { + label: string; + storagePrefix: string; +} + +export function createHostCallbacks( + options: CreateHostCallbacksOptions, +): WasmHostCallbacks { + const { label, storagePrefix } = options; + return { + ...createUnavailableCallbacks(), + openUrl: createOpenUrl(), + pushNotification: createPushNotification(label), + promptPermission: createPromptPermission(label), + localStorageRead: createLocalStorageRead(storagePrefix), + localStorageWrite: createLocalStorageWrite(storagePrefix), + localStorageClear: createLocalStorageClear(storagePrefix), + ...createAccountAdapters(label), + ...createSigningAdapters(label), + ...createStatementStoreAdapters(label), + ...createPreimageAdapters(label), + chainConnect: createChainConnect(), + }; +} diff --git a/packages/ui/src/host-callbacks/hex.ts b/packages/ui/src/host-callbacks/hex.ts new file mode 100644 index 0000000..3baccee --- /dev/null +++ b/packages/ui/src/host-callbacks/hex.ts @@ -0,0 +1,17 @@ +// Minimal hex utilities for the @truapi/client wire types (which use +// `Hex = Uint8Array`) bridging to the `0x${string}` shape host-papp expects. +// Kept local so the UI package does not need to depend on @novasamatech/host-api +// once Phase B5 drops that dep. + +import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js"; + +export type HexString = `0x${string}`; + +export function toHexPrefixed(bytes: Uint8Array): HexString { + return `0x${bytesToHex(bytes)}`; +} + +export function fromHexPrefixed(hex: string): Uint8Array { + const stripped = hex.startsWith("0x") ? hex.slice(2) : hex; + return hexToBytes(stripped); +} diff --git a/packages/ui/src/host-callbacks/rate-limit.ts b/packages/ui/src/host-callbacks/rate-limit.ts new file mode 100644 index 0000000..249daaf --- /dev/null +++ b/packages/ui/src/host-callbacks/rate-limit.ts @@ -0,0 +1,21 @@ +// Sliding-window rate limiter shared across host callbacks. + +const SUBMIT_WINDOW_MS = 10_000; +const SUBMIT_MAX_PER_WINDOW = 20; + +export function createSubmitRateLimiter(): { allow: () => boolean } { + const timestamps: number[] = []; + return { + allow() { + const now = Date.now(); + while (timestamps.length > 0 && timestamps[0] <= now - SUBMIT_WINDOW_MS) { + timestamps.shift(); + } + if (timestamps.length >= SUBMIT_MAX_PER_WINDOW) { + return false; + } + timestamps.push(now); + return true; + }, + }; +} From b28facf7d3ad7d2842d61392b56f7e79cde2384e Mon Sep 17 00:00:00 2001 From: pgherveou Date: Thu, 30 Apr 2026 12:29:36 +0200 Subject: [PATCH 04/83] Swap bridge.ts from setupContainer to createIframeHost Replaces the @novasamatech/host-container setupContainer call with @truapi/host-web's createIframeHost. The new bridge: - dynamically imports @truapi/host-web and the worker entrypoint @truapi/host-shared/dist/worker-runtime.js?worker - spawns a Web Worker that owns the truapi-server WASM core, so smoldot's CPU and JSON-RPC handling no longer block the page main thread (was a steady stream of '[Violation] message handler took 150ms+' warnings) - calls createWebWorkerHostRuntime(new HostWorker(), callbacks) - supplies the dotli callback set from packages/ui/src/host-callbacks/ - calls createIframeHost(...) with the product URL, sandbox policy, and allowed origin Nested dApp-in-dApp bridging (the old setupNestedBridgeDetector) is dropped; createIframeHost owns its own MessageChannel and does not yet expose an attachNested API. Tracked as a known regression in the parent refactor plan; the workaround lives in C1. --- packages/ui/src/bridge.ts | 182 ++++++++++++++++++++++---------------- packages/ui/src/render.ts | 6 +- 2 files changed, 108 insertions(+), 80 deletions(-) diff --git a/packages/ui/src/bridge.ts b/packages/ui/src/bridge.ts index 496b137..a92845e 100644 --- a/packages/ui/src/bridge.ts +++ b/packages/ui/src/bridge.ts @@ -1,9 +1,15 @@ -// dot.li — Host-container bridge +// dot.li — TrUAPI host bridge // -// Connects embedded dApps to host services (accounts, signing, chain -// connections, scoped storage) via postMessage protocol. -// Only imported by the host build — keeps smoldot, auth, and verifiable.js -// out of the sandbox bundle. +// Boots a WASM TrUAPI core instance and connects it to a sandboxed +// product iframe via `@truapi/host-web`. Each render swaps the running +// runtime, so disposing the last host tears down both the iframe and +// the core. +// +// Nested dApp-in-dApp composition (old `setupNestedBridgeDetector`) is +// dropped: `createIframeHost` uses a dedicated `MessageChannel`, so +// inner iframes have no host port. Tracked in §6.1 of the refactor plan +// as a known regression; a future `attachNested` API in `@truapi/host-web` +// will restore it. import { BASE_DOMAIN } from "@dotli/config/config"; import { @@ -14,24 +20,35 @@ import { import { m } from "@dotli/metrics/metrics"; import * as S from "@dotli/metrics/spans"; import { buildAllowAttribute } from "./permissions"; +import { createHostCallbacks } from "./host-callbacks/handlers"; // Re-export sandbox-safe rendering functions export { renderContent, renderArchive, prepareIframe } from "./render"; -// Eagerly load the container bridge chunk — starts downloading when -// this module is imported, so it's ready by the time we need it. +// Eagerly load the iframe host chunk + worker constructor so they're ready +// by the time we need them. The wasm core lives inside the worker; the host +// shell only owns the postMessage bridge, keeping smoldot's CPU off the +// main thread (no more `[Violation] 'message' handler took 150ms+`). const chunkLoadStart = performance.now(); -const containerChunkPromise = import("./container").then((mod) => { +const runtimeChunkPromise = Promise.all([ + import("@truapi/host-web"), + import("@truapi/host-shared/dist/worker-runtime.js?worker"), +]).then(([web, workerMod]) => { m.measure(S.BRIDGE_CHUNK_LOAD, performance.now() - chunkLoadStart); - return mod; + return { + createWebWorkerHostRuntime: web.createWebWorkerHostRuntime, + createIframeHost: web.createIframeHost, + HostWorker: workerMod.default as new () => Worker, + }; }); -void containerChunkPromise.catch(() => { +void runtimeChunkPromise.catch(() => { /* fire-and-forget */ }); const app = document.getElementById("app") ?? document.body; -let currentDispose: (() => void) | null = null; +let currentHost: { iframe: HTMLIFrameElement; dispose: () => void } | null = + null; let currentPanelDispose: (() => void) | null = null; // Track current product state for permission-grant reloads @@ -76,12 +93,46 @@ function getDeepPath(): string { return stripped + search + hash; } +function applyIframeStyling( + iframe: HTMLIFrameElement, + label: string, + opts: { topbarOffset: boolean }, +): void { + iframe.allow = `${buildAllowAttribute(label)}; cross-origin-isolated`; + iframe.style.cssText = opts.topbarOffset + ? "position:fixed;top:40px;left:0;width:100%;height:calc(100vh - 40px);border:none;margin:0;padding:0;" + : "position:fixed;top:0;left:0;width:100%;height:100vh;border:none;margin:0;padding:0;"; + document.body.style.margin = "0"; + document.body.style.overflow = "hidden"; +} + +async function createHost(args: { + iframeUrl: string; + allowedOrigin: string; + sandbox: string; + label: string; + container: HTMLElement; +}): Promise<{ iframe: HTMLIFrameElement; dispose: () => void }> { + const { createWebWorkerHostRuntime, createIframeHost, HostWorker } = + await runtimeChunkPromise; + const runtime = await createWebWorkerHostRuntime( + new HostWorker(), + createHostCallbacks({ + label: args.label, + storagePrefix: `dotli:${args.label}:`, + }), + ); + return createIframeHost({ + iframeUrl: args.iframeUrl, + allowedOrigin: args.allowedOrigin, + sandbox: args.sandbox, + container: args.container, + runtime, + }); +} + /** - * Render an iframe with the host-container bridge. - * - * Unlike the base renderIframe, this sets up the postMessage bridge - * between the host and the embedded dApp, enabling accounts, signing, - * chain connections, and scoped storage. + * Render a dApp iframe backed by the TrUAPI host bridge. */ export async function renderIframe(url: string, label: string): Promise { const stopSetup = m.timer(S.BRIDGE_SETUP); @@ -92,41 +143,28 @@ export async function renderIframe(url: string, label: string): Promise { currentUrl = url; currentCid = null; - const hasTopbar = document.getElementById("topbar") !== null; - const iframeStyle = hasTopbar - ? "position:fixed;top:40px;left:0;width:100%;height:calc(100vh - 40px);border:none;margin:0;padding:0;" - : "position:fixed;top:0;left:0;width:100%;height:100vh;border:none;margin:0;padding:0;"; - app.innerHTML = ""; - const iframe = document.createElement("iframe"); - // TODO: Review sandbox default permissions - iframe.sandbox.add( - "allow-scripts", - "allow-same-origin", - "allow-forms", - "allow-pointer-lock", - ); - iframe.allow = `${buildAllowAttribute(label)}; cross-origin-isolated`; - iframe.style.cssText = iframeStyle; - document.body.style.margin = "0"; - document.body.style.overflow = "hidden"; - app.appendChild(iframe); - const { setupContainer, setupNestedBridgeDetector } = - await containerChunkPromise; - const disposePrimary = setupContainer(iframe, url, label); - const disposeNested = setupNestedBridgeDetector(iframe, label); - currentDispose = () => { - disposePrimary(); - disposeNested(); - }; + const hasTopbar = document.getElementById("topbar") !== null; + const iframeUrl = new URL(url, window.location.href); + const host = await createHost({ + iframeUrl: iframeUrl.href, + allowedOrigin: iframeUrl.origin, + // TODO: Review sandbox default permissions + sandbox: "allow-scripts allow-same-origin allow-forms allow-pointer-lock", + label, + container: app, + }); + applyIframeStyling(host.iframe, label, { topbarOffset: hasTopbar }); + currentHost = host; if ( (import.meta.env.VITE_SANDBOX_CHECKER as string | undefined) !== undefined ) { - const { setupViolationPanel } = - await import("@dotli/sandbox-checker/sandbox-checker-ui"); - currentPanelDispose = setupViolationPanel(iframe); + const { setupViolationPanel } = await import( + "@dotli/sandbox-checker/sandbox-checker-ui" + ); + currentPanelDispose = setupViolationPanel(host.iframe); } stopSetup(); @@ -141,8 +179,9 @@ export async function renderIframe(url: string, label: string): Promise { * Render content in a cross-origin app subdomain iframe (cid.app.dot.li). * Used by the host build to delegate content fetching+rendering to the app context. * - * Sets up the container bridge targeting the app iframe. The app context - * acts as a transparent postMessage relay between the host and the dApp iframe. + * The app context acts as a transparent relay between the host and the dApp + * iframe. Only the app subdomain itself participates in the TrUAPI + * MessageChannel; any nested dApp iframe it loads is opaque to the host. */ export async function renderAppSubdomain( cid: string, @@ -208,45 +247,34 @@ export async function renderAppSubdomain( } } - const iframe = document.createElement("iframe"); - iframe.sandbox.add( - "allow-scripts", - "allow-same-origin", - "allow-forms", - "allow-pointer-lock", - "allow-popups", - ); - iframe.allow = buildAllowAttribute(label); - iframe.style.cssText = - "position:fixed;top:40px;left:0;width:100%;height:calc(100vh - 40px);border:none;margin:0;padding:0;"; - document.body.style.margin = "0"; - document.body.style.overflow = "hidden"; - // Keep the loading overlay visible — the sandbox will post status // messages via dotli:loading-status and a final done=true to dismiss it. - // Only remove non-loading children from #app before appending the iframe. + // Only remove non-loading children from #app before handing it off. const loading = app.querySelector(".loading"); app.innerHTML = ""; if (loading) { app.appendChild(loading); } - app.appendChild(iframe); - const { setupContainer, setupNestedBridgeDetector } = - await containerChunkPromise; - const disposePrimary = setupContainer(iframe, url, label); - const disposeNested = setupNestedBridgeDetector(iframe, label); - currentDispose = () => { - disposePrimary(); - disposeNested(); - }; + const iframeUrl = new URL(url); + const host = await createHost({ + iframeUrl: url, + allowedOrigin: iframeUrl.origin, + sandbox: + "allow-scripts allow-same-origin allow-forms allow-pointer-lock allow-popups", + label, + container: app, + }); + applyIframeStyling(host.iframe, label, { topbarOffset: true }); + currentHost = host; if ( (import.meta.env.VITE_SANDBOX_CHECKER as string | undefined) !== undefined ) { - const { setupViolationPanel } = - await import("@dotli/sandbox-checker/sandbox-checker-ui"); - currentPanelDispose = setupViolationPanel(iframe); + const { setupViolationPanel } = await import( + "@dotli/sandbox-checker/sandbox-checker-ui" + ); + currentPanelDispose = setupViolationPanel(host.iframe); } stopSetup(); @@ -271,8 +299,8 @@ function cleanup(): void { currentPanelDispose(); currentPanelDispose = null; } - if (currentDispose) { - currentDispose(); - currentDispose = null; + if (currentHost) { + currentHost.dispose(); + currentHost = null; } } diff --git a/packages/ui/src/render.ts b/packages/ui/src/render.ts index 4f973ed..a7935a7 100644 --- a/packages/ui/src/render.ts +++ b/packages/ui/src/render.ts @@ -3,9 +3,9 @@ // Takes fetched content and renders it in a sandboxed