Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions packages/host-api/MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# host-api migration to `@parity/truapi`

This package is being reworked into a thin compatibility facade over
[`@parity/truapi`][1]. The Rust trait surface in `paritytech/truapi`
(`rust/crates/truapi/src/api/*.rs`) is the single source of truth for wire
ids, payload shapes, and dispatch.

The work tracking issue is [`paritytech/truapi#54`][issue].

[1]: https://github.com/paritytech/truapi/tree/main/js/packages/truapi
[issue]: https://github.com/paritytech/truapi/issues/54

## Status

### Done

- `@parity/truapi` added to `package.json` dependencies (currently a `file:`
link to the local truapi worktree; switch to the published version once
`@parity/truapi@0.2.0` ships).
- `src/truapi-transport.ts`: directional facade that wraps a legacy
`Provider` and produces a `@parity/truapi` `TrUApiTransport` instance.

### Not done

- `src/transport.ts` still owns its own request correlation, subscription
router, frame envelope, and subscription multiplexing. Replace its
internals with calls into the `@parity/truapi` transport returned from
`createTruapiTransportFacade()`. Keep the public `Transport` shape from
`./types.ts` byte-for-byte stable so downstream consumers (`host-papp`,
`product-react-renderer`, `product-bulletin`, `host-worker-sandbox`,
`host-api-wrapper`) do not break.
- Active handshake retry / readiness state currently lives in `isReady()`
inside `transport.ts`. Re-implement on top of the new transport's public
handshake helpers (the auto-response path inside `@parity/truapi` covers
passive handshake; active retry stays here as consumer policy).
- Drop subscription multiplexing (the `activeSubscriptions` map and
`getSubscriptionKey`). `@parity/truapi` exposes 1:1 wire subscriptions,
any consumer that relies on dedup adds its own multiplexer above this
package.
- Drop inbound `handleRequest` / `handleSubscription` from the public
surface, they exist only to support `host-container`, which migrates onto
`@parity/truapi-host` separately. Update `Transport` in `./types.ts`.
- Repoint the per-codec exports in `src/index.ts` (lines 36 onward) to
re-export from `@parity/truapi`'s generated `types.ts`. The legacy class
names (`SigningErr`, `CreateTransactionErr`, `AccountConnectionStatus`,
…) need either a thin alias layer or a one-shot rename in downstream
consumers, the generated equivalents use tagged-union shape
(`{ tag, value }`) rather than `new Err.Variant({...})` calls.
- Delete `src/protocol/v1/*.ts` once every type alias above is repointed.

### Tests

- `src/transport.spec.ts` covers subscription multiplexing today, that test
will need to move or get retired alongside the multiplexer.
- Add a 56-method byte-equivalence fixture test against a captured
`@novasamatech/host-api@0.7.7` baseline.
1 change: 1 addition & 0 deletions packages/host-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
],
"dependencies": {
"@novasamatech/scale": "0.7.8",
"@parity/truapi": "file:../../../truapi/.claude/worktrees/issue-54-truapi-host/js/packages/truapi",
"nanoevents": "9.1.0",
"nanoid": "5.1.9",
"neverthrow": "^8.2.0",
Expand Down
110 changes: 110 additions & 0 deletions packages/host-api/src/truapi-transport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* Facade transport built on `@parity/truapi`.
*
* Replaces the bespoke request correlation, subscription router, and wire
* frame envelope that `transport.ts` currently owns with the equivalent
* primitives from `@parity/truapi`. The public surface intentionally mirrors
* `Transport` from `./types.ts` so consumers of `createHostApi` and the
* factories in `host-container` can switch over without churn.
*
* What this file does NOT do, by design:
* - Subscription multiplexing or dedup. `@parity/truapi` exposes 1:1 wire
* subscriptions, products that need fan-out layer their own.
* - Inbound handler registration (`handleRequest`, `handleSubscription`).
* Hosts should use `@parity/truapi-host`'s typed dispatcher instead, and
* `host-container`'s `createContainer` is being migrated onto it in
* `truapi-container.ts`.
*
* Status: directional. Active handshake retry, connection-status events, and
* the legacy `postMessage`/`listenMessages` low-level helpers still need to
* be re-implemented on top of this. See `MIGRATION.md` for the punch list.
*/

import type { Provider as TruapiProvider, TrUApiTransport } from '@parity/truapi';
import { createTransport as createTruapiTransport } from '@parity/truapi';
import { createNanoEvents } from 'nanoevents';

import type { Provider } from './provider.js';
import type { ConnectionStatus, Logger } from './types.js';

/**
* Adapt a legacy `Provider` (postMessage/listenMessages over wire-frame
* codec objects) to the raw byte-level `Provider` shape `@parity/truapi`
* expects.
*
* The legacy provider already speaks SCALE-encoded wire frames, so this is
* a pass-through, message-codec wrapping and unwrapping is now owned by
* `@parity/truapi`'s `encodeWireMessage`/`decodeWireMessage`.
*/
function adaptProvider(provider: Provider): TruapiProvider {
return {
postMessage(bytes) {
// The legacy `Provider` postMessage accepts already-encoded bytes; the
// wrapping `MessageProvider.postMessage(message)` indirection that
// `transport.ts` adds to encode the wire-frame envelope is now
// performed by `@parity/truapi.createTransport`.
provider.postMessage(bytes);
},
subscribe(callback) {
const unsub = provider.subscribe(callback);
return () => unsub();
},
dispose() {
provider.dispose();
},
};
}

export interface TruapiTransportOptions {
logger?: Logger;
}

export interface TruapiTransport {
/**
* Underlying `@parity/truapi` transport. Generated client modules accept
* this directly; legacy `host-api` consumers wrap it via the facade
* methods below.
**/
readonly inner: TrUApiTransport;

/**
* Subscribe to connection-status changes. Mirrors the legacy
* `onConnectionStatusChange` surface.
**/
onConnectionStatusChange(callback: (status: ConnectionStatus) => void): VoidFunction;

/**
* Tear down the transport and detach provider listeners.
**/
destroy(): void;
}

/**
* Build a `@parity/truapi` transport bound to the supplied legacy
* `Provider`. Generated client modules (from `@parity/truapi`) accept the
* returned `inner` transport directly.
*/
export function createTruapiTransportFacade(
provider: Provider,
_options: TruapiTransportOptions = {},
): TruapiTransport {
const status = createNanoEvents<{
change: (status: ConnectionStatus) => void;
}>();
let currentStatus: ConnectionStatus = 'connecting';

const inner = createTruapiTransport(adaptProvider(provider));

return {
inner,
onConnectionStatusChange(callback) {
callback(currentStatus);
return status.on('change', callback);
},
destroy() {
inner.dispose();
currentStatus = 'disconnected';
status.emit('change', currentStatus);
},
};
}
56 changes: 56 additions & 0 deletions packages/host-container/MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# host-container migration to `@parity/truapi-host`

This package is being reworked into a thin compatibility facade over
[`@parity/truapi-host`][1]. The Rust trait surface in `paritytech/truapi`
drives both the wire ids and the typed handler interfaces emitted by the
generator.

The work tracking issue is [`paritytech/truapi#54`][issue].

[1]: https://github.com/paritytech/truapi/tree/main/js/packages/truapi-host
[issue]: https://github.com/paritytech/truapi/issues/54

## Status

### Done

- `@parity/truapi` and `@parity/truapi-host` added to `package.json`
dependencies (currently `file:` links to the local truapi worktree).
- `src/truapi-container.ts`: directional facade that forwards a fully
populated `TrUApiHostHandlers` to `createTrUApiServer`. The TypeScript
compiler enforces completeness; there are no throwing "Not
Implemented" stubs, the dispatcher contract requires handlers to
express every outcome (including missing implementations) as a typed
return rather than an exception.

### Not done

- `src/createContainer.ts` still owns:
- 40+ `handleXxx(handler)` slots (the public surface).
- `handleV1Request` / `handleV1Subscription` wrappers around versioned
payload envelopes.
- `makeNotImplementedSlot` / `makePermissionGatedRequestSlot` /
`makeDevicePermissionGatedRequestSlot`, all of which produce
method-specific `Unknown { reason: NOT_IMPLEMENTED }` defaults.
- `handleChainConnection()` (lines ~759–1099): polkadot-api bridge.
This stays inside this package, only its registration call should
change to forward to `@parity/truapi-host`'s typed handlers.
- Replace each `handleXxx` slot's update mechanism with a call into
`@parity/truapi-host`'s typed registration. The slot can remain as the
public API; under the hood it should mutate the handler reference that
`createTrUApiServer(...)` is dispatching against.
- Replace `handleV1Request` / `handleV1Subscription` with the typed
handler signatures from `@parity/truapi-host`'s generated `server.ts`.
The versioned envelope wrap/unwrap is now performed by the generator,
not by hand.
- Drop the dependency on `@novasamatech/host-api` once `host-api` itself
drops its inbound `handleRequest` / `handleSubscription` surface (see
`packages/host-api/MIGRATION.md`).

### Tests

- `src/chainConnectionManager.spec.ts` is unchanged scope, must continue
to pass.
- Add an integration test that wires a `@parity/truapi` client and a
`@parity/truapi-host` server through a `MessageChannel` and exercises
every wire method via the container's compat shims.
2 changes: 2 additions & 0 deletions packages/host-container/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"polkadot-api": ">=2",
"@polkadot-api/substrate-client": "^0.7.0",
"@novasamatech/host-api": "0.7.8",
"@parity/truapi": "file:../../../truapi/.claude/worktrees/issue-54-truapi-host/js/packages/truapi",
"@parity/truapi-host": "file:../../../truapi/.claude/worktrees/issue-54-truapi-host/js/packages/truapi-host",
"nanoid": "5.1.9",
"neverthrow": "^8.2.0"
},
Expand Down
36 changes: 36 additions & 0 deletions packages/host-container/src/truapi-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Facade host container built on `@parity/truapi-host`.
*
* Replaces the bespoke per-method dispatch, "Not Implemented" fallbacks, and
* `handleV1Request` / `handleV1Subscription` wrappers in `createContainer.ts`
* with the typed handler registration produced by `@parity/truapi-host`'s
* generator.
*
* The new public surface is structured-by-service, mirroring the Rust trait
* surface in `truapi`. Hosts that consume `createContainer()` today (with
* `handleXxx` slots) keep working through compat shims in
* `createContainer.ts`, which forward to `createTruapiContainer()` under the
* hood.
*
* Status: directional. The generated `TrUApiHostHandlers` interface forces
* the caller to supply every service handler (handlers must not throw, per
* the `@parity/truapi-host` contract; every outcome including unsupported
* versions and missing implementations is expressed as a typed return).
* Lifting each existing `handleXxx` slot's behaviour into the matching typed
* handler is the bulk of the remaining migration.
*/

import type { Provider as TruapiProvider } from '@parity/truapi';
import type { TrUApiHostHandlers, TrUApiHostServer } from '@parity/truapi-host';
import { createTrUApiServer } from '@parity/truapi-host';

/**
* Attach a host server built on `@parity/truapi-host` to a `Provider`.
*
* The caller supplies the full `TrUApiHostHandlers` shape. The TypeScript
* compiler enforces completeness, anything missing is flagged at the call
* site rather than papered over with a stub that would throw at runtime.
*/
export function createTruapiContainer(provider: TruapiProvider, handlers: TrUApiHostHandlers): TrUApiHostServer {
return createTrUApiServer(provider, handlers);
}