@novasamatech/host-container 0.7.9-5 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  import type { Provider } from '@novasamatech/host-api';
2
- import type { Container } from './types.js';
3
- export declare function createContainer(provider: Provider): Container;
2
+ import type { Container, CreateContainerOptions } from './types.js';
3
+ export declare function createContainer(provider: Provider, options?: CreateContainerOptions): Container;
@@ -1,6 +1,7 @@
1
1
  import { ChatBotRegistrationErr, ChatMessagePostingErr, ChatRoomRegistrationErr, CreateProofErr, CreateTransactionErr, DeriveEntropyErr, DevicePermission, GenericError, GetUserIdErr, LoginErr, NavigateToErr, PaymentBalanceErr, PaymentRequestErr, PaymentStatusErr, PaymentTopUpErr, PreimageSubmitErr, PushNotificationError, RemotePermission, RequestCredentialsErr, ResourceAllocationErr, SigningErr, StatementProofErr, StorageErr, createTransport, enumValue, isEnumVariant, resultErr, resultOk, } from '@novasamatech/host-api';
2
2
  import { err, errAsync, ok, okAsync } from 'neverthrow';
3
3
  import { createChainConnectionManager } from './chainConnectionManager.js';
4
+ import { emitHostApiDebugMessage, registerHostApiDebugSource } from './debugBus.js';
4
5
  const UNSUPPORTED_MESSAGE_FORMAT_ERROR = 'Unsupported message format';
5
6
  const NOT_IMPLEMENTED = 'Not implemented';
6
7
  function guardVersion(value, tag, error) {
@@ -12,11 +13,21 @@ function guardVersion(value, tag, error) {
12
13
  }
13
14
  return err(error);
14
15
  }
15
- export function createContainer(provider) {
16
+ export function createContainer(provider, options = {}) {
16
17
  const transport = createTransport(provider);
17
18
  if (!transport.isCorrectEnvironment()) {
18
19
  throw new Error('Transport is not available: dapp provider has incorrect environment');
19
20
  }
21
+ const { productId } = options;
22
+ // EXPERIMENTAL: forward every transport-level message into the
23
+ // process-global debug bus, tagged with this container's productId.
24
+ // The forwarder is registered as a bus *source* and only attaches to
25
+ // `transport.onDebugMessage` while the bus has at least one subscriber —
26
+ // otherwise the transport's lazy `Message.dec` path stays cold.
27
+ const unregisterGlobalDebugSource = registerHostApiDebugSource(() => transport.onDebugMessage(({ direction, requestId, payload }) => {
28
+ emitHostApiDebugMessage({ direction, productId, requestId, payload });
29
+ }));
30
+ transport.onDestroy(unregisterGlobalDebugSource);
20
31
  function init() {
21
32
  // init status subscription
22
33
  transport.isReady();
@@ -625,5 +636,10 @@ export function createContainer(provider) {
625
636
  dispose() {
626
637
  transport.destroy();
627
638
  },
639
+ onDebugMessage(callback) {
640
+ return transport.onDebugMessage(({ direction, requestId, payload }) => {
641
+ callback({ direction, productId, requestId, payload });
642
+ });
643
+ },
628
644
  };
629
645
  }
@@ -0,0 +1,18 @@
1
+ import type { HostApiDebugMessageEvent } from './types.js';
2
+ type DebugSource = () => VoidFunction;
3
+ /** @internal Used by `createContainer` to forward its transport's debug events. */
4
+ export declare function emitHostApiDebugMessage(event: HostApiDebugMessageEvent): void;
5
+ /**
6
+ * @internal Register a transport-level forwarder for the global bus.
7
+ * The source is activated only while the bus has at least one subscriber
8
+ * and deactivated when the last one unsubscribes — this preserves the
9
+ * transport's lazy decode path (no `Message.dec` per frame) when nobody
10
+ * is listening downstream.
11
+ */
12
+ export declare function registerHostApiDebugSource(source: DebugSource): VoidFunction;
13
+ /**
14
+ * EXPERIMENTAL. Subscribe to every host ↔ product message across all
15
+ * containers in the current process. Returns an unsubscribe function.
16
+ */
17
+ export declare function onHostApiDebugMessage(callback: (event: HostApiDebugMessageEvent) => void): VoidFunction;
18
+ export {};
@@ -0,0 +1,73 @@
1
+ import { createNanoEvents } from 'nanoevents';
2
+ /**
3
+ * EXPERIMENTAL: process-global bus that aggregates debug events from
4
+ * every container created in this process. Subscribing here gives a
5
+ * single subscriber visibility into all host ↔ product traffic across
6
+ * every active container, annotated with the `productId` passed to
7
+ * `createContainer` (if any).
8
+ */
9
+ const bus = createNanoEvents();
10
+ const sources = new Set();
11
+ const activeSources = new Map();
12
+ let subscriberCount = 0;
13
+ function activateSource(source) {
14
+ if (activeSources.has(source))
15
+ return;
16
+ activeSources.set(source, source());
17
+ }
18
+ function deactivateSource(source) {
19
+ const unsubscribe = activeSources.get(source);
20
+ if (!unsubscribe)
21
+ return;
22
+ activeSources.delete(source);
23
+ unsubscribe();
24
+ }
25
+ /** @internal Used by `createContainer` to forward its transport's debug events. */
26
+ export function emitHostApiDebugMessage(event) {
27
+ bus.emit('message', event);
28
+ }
29
+ /**
30
+ * @internal Register a transport-level forwarder for the global bus.
31
+ * The source is activated only while the bus has at least one subscriber
32
+ * and deactivated when the last one unsubscribes — this preserves the
33
+ * transport's lazy decode path (no `Message.dec` per frame) when nobody
34
+ * is listening downstream.
35
+ */
36
+ export function registerHostApiDebugSource(source) {
37
+ sources.add(source);
38
+ if (subscriberCount > 0)
39
+ activateSource(source);
40
+ let disposed = false;
41
+ return () => {
42
+ if (disposed)
43
+ return;
44
+ disposed = true;
45
+ sources.delete(source);
46
+ deactivateSource(source);
47
+ };
48
+ }
49
+ /**
50
+ * EXPERIMENTAL. Subscribe to every host ↔ product message across all
51
+ * containers in the current process. Returns an unsubscribe function.
52
+ */
53
+ export function onHostApiDebugMessage(callback) {
54
+ const wasZero = subscriberCount === 0;
55
+ subscriberCount++;
56
+ if (wasZero) {
57
+ for (const source of sources)
58
+ activateSource(source);
59
+ }
60
+ const unsubscribe = bus.on('message', callback);
61
+ let disposed = false;
62
+ return () => {
63
+ if (disposed)
64
+ return;
65
+ disposed = true;
66
+ unsubscribe();
67
+ subscriberCount--;
68
+ if (subscriberCount === 0) {
69
+ for (const source of [...activeSources.keys()])
70
+ deactivateSource(source);
71
+ }
72
+ };
73
+ }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export { createWebviewProvider } from './createWebviewProvider.js';
2
2
  export { createIframeProvider } from './createIframeProvider.js';
3
3
  export { createContainer } from './createContainer.js';
4
- export type { Container, ContainerHandlerOf } from './types.js';
4
+ export type { Container, ContainerHandlerOf, CreateContainerOptions, HostApiDebugMessageEvent } from './types.js';
5
+ export { onHostApiDebugMessage } from './debugBus.js';
5
6
  export { deriveProductEntropy } from './deriveEntropy.js';
6
7
  export { createRateLimiter } from './rateLimiter.js';
7
8
  export type { CreateRateLimiterConfig, RateLimiter, RateLimiterConfig, RateLimiterStrategy } from './rateLimiter.js';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { createWebviewProvider } from './createWebviewProvider.js';
2
2
  export { createIframeProvider } from './createIframeProvider.js';
3
3
  export { createContainer } from './createContainer.js';
4
+ export { onHostApiDebugMessage } from './debugBus.js';
4
5
  export { deriveProductEntropy } from './deriveEntropy.js';
5
6
  export { createRateLimiter } from './rateLimiter.js';
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Codec, CodecType, ConnectionStatus, HexString, HostApiProtocol, Subscription, VersionedProtocolRequest, VersionedProtocolSubscription } from '@novasamatech/host-api';
1
+ import type { Codec, CodecType, ConnectionStatus, HexString, HostApiProtocol, MessagePayloadSchema, Subscription, VersionedProtocolRequest, VersionedProtocolSubscription } from '@novasamatech/host-api';
2
2
  import { CustomRendererNode } from '@novasamatech/host-api';
3
3
  import type { ResultAsync, errAsync } from 'neverthrow';
4
4
  import { okAsync } from 'neverthrow';
@@ -52,6 +52,25 @@ type InferRequestHandler<V extends string, T extends VersionedProtocolRequest> =
52
52
  type InferSubscribeHandler<V extends string, T extends VersionedProtocolSubscription> = (callback: (params: WithVersion<V, CodecValue<T['start']>>, send: (payload: WithVersion<V, CodecValue<T['receive']>>) => void, interrupt: (payload: WithVersion<V, CodecValue<T['interrupt']>>) => void) => VoidFunction) => VoidFunction;
53
53
  type InferHandler<V extends string, T extends VersionedProtocolRequest | VersionedProtocolSubscription> = T extends VersionedProtocolRequest ? InferRequestHandler<V, T> : T extends VersionedProtocolSubscription ? InferSubscribeHandler<V, T> : never;
54
54
  export type ContainerHandlerOf<T extends (...args: any[]) => any> = Parameters<T>[0];
55
+ /**
56
+ * EXPERIMENTAL. Event describing a single message observed on a
57
+ * container's transport, in decoded form, tagged with the productId
58
+ * that was passed to `createContainer`.
59
+ */
60
+ export type HostApiDebugMessageEvent = {
61
+ direction: 'incoming' | 'outgoing';
62
+ productId: string | undefined;
63
+ requestId: string;
64
+ payload: MessagePayloadSchema;
65
+ };
66
+ export type CreateContainerOptions = {
67
+ /**
68
+ * Optional identifier for the product this container talks to.
69
+ * When set, every debug event emitted via `onDebugMessage` and the
70
+ * global `onHostApiDebugMessage` bus is tagged with this value.
71
+ */
72
+ productId?: string;
73
+ };
55
74
  export type Container = {
56
75
  handleFeatureSupported: InferHandler<'v1', HostApiProtocol['host_feature_supported']>;
57
76
  handleDevicePermission: InferHandler<'v1', HostApiProtocol['host_device_permission']>;
@@ -102,5 +121,11 @@ export type Container = {
102
121
  isReady(): Promise<boolean>;
103
122
  dispose(): void;
104
123
  subscribeProductConnectionStatus(callback: (connectionStatus: ConnectionStatus) => void): VoidFunction;
124
+ /**
125
+ * EXPERIMENTAL. Subscribe to every message crossing this container's
126
+ * transport in either direction, in decoded form. Returns an
127
+ * unsubscribe function.
128
+ */
129
+ onDebugMessage(callback: (event: HostApiDebugMessageEvent) => void): VoidFunction;
105
130
  };
106
131
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/host-container",
3
3
  "type": "module",
4
- "version": "0.7.9-5",
4
+ "version": "0.7.9",
5
5
  "description": "Host container for hosting and managing products within the Polkadot ecosystem.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -28,7 +28,8 @@
28
28
  "@noble/hashes": "2.2.0",
29
29
  "polkadot-api": ">=2",
30
30
  "@polkadot-api/substrate-client": "^0.7.0",
31
- "@novasamatech/host-api": "0.7.9-5",
31
+ "@novasamatech/host-api": "0.7.9",
32
+ "nanoevents": "9.1.0",
32
33
  "nanoid": "5.1.9",
33
34
  "neverthrow": "^8.2.0"
34
35
  },