@novasamatech/host-api 0.8.3 → 0.8.4

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,4 +1,3 @@
1
- export declare const Ed25519PrivateKey: import("scale-ts").Codec<Uint8Array<ArrayBufferLike>>;
2
1
  export declare const Sr25519SecretKey: import("scale-ts").Codec<Uint8Array<ArrayBufferLike>>;
3
2
  export declare const PaymentId: import("scale-ts").Codec<string>;
4
3
  export declare const CoinPaymentPurseId: import("scale-ts").Codec<number>;
@@ -3,14 +3,13 @@ import { Bytes, Option, Result, Struct, Vector, _void, str, u128, u32 } from 'sc
3
3
  import { GenericErr } from '../commonCodecs.js';
4
4
  import { DerivationIndex } from './accounts.js';
5
5
  // common types
6
- export const Ed25519PrivateKey = Bytes(32);
7
- export const Sr25519SecretKey = Bytes(32);
6
+ export const Sr25519SecretKey = Bytes(64);
8
7
  export const PaymentId = str;
9
8
  // Optional purse selector (RFC 0017). `undefined` (None) targets MAIN_PURSE.
10
9
  export const CoinPaymentPurseId = u32;
11
10
  export const PaymentTopUpSource = Enum({
12
11
  ProductAccount: DerivationIndex,
13
- PrivateKey: Ed25519PrivateKey,
12
+ PrivateKey: Sr25519SecretKey,
14
13
  Coins: Vector(Sr25519SecretKey),
15
14
  });
16
15
  export const PaymentBalance = Struct({
package/dist/transport.js CHANGED
@@ -284,11 +284,17 @@ export function createTransport(provider) {
284
284
  return;
285
285
  let interrupted = false;
286
286
  const unsubscribe = handler(payload.value, value => {
287
+ // The transport may be disposed while a producer still has a batched
288
+ // emission queued. Drop it instead of throwing 'Transport is disposed'.
289
+ if (disposed)
290
+ return;
287
291
  const receivePayload = enumValue(receiveAction, value);
288
292
  transport.postMessage(requestId, receivePayload);
289
293
  }, value => {
290
294
  interrupted = true;
291
295
  subscriptions.delete(requestId);
296
+ if (disposed)
297
+ return;
292
298
  transport.postMessage(requestId, enumValue(interruptAction, value));
293
299
  });
294
300
  if (interrupted) {
@@ -299,13 +305,25 @@ export function createTransport(provider) {
299
305
  }
300
306
  });
301
307
  const unsubStop = transport.listenMessages(stopAction, requestId => {
302
- subscriptions.get(requestId)?.();
308
+ const unsubscribe = subscriptions.get(requestId);
309
+ if (unsubscribe) {
310
+ subscriptions.delete(requestId);
311
+ unsubscribe();
312
+ }
303
313
  });
304
- return () => {
314
+ const teardown = () => {
305
315
  subscriptions.forEach(unsub => unsub());
316
+ subscriptions.clear();
306
317
  unsubStart();
307
318
  unsubStop();
319
+ unsubDestroy();
308
320
  };
321
+ // Tear down active producer subscriptions when the transport is destroyed.
322
+ // Consumers (e.g. host-container's subscription slot) discard the cleanup
323
+ // returned below, so without this a disposed transport leaves producers
324
+ // running and emitting into a dead transport.
325
+ const unsubDestroy = transport.onDestroy(teardown);
326
+ return teardown;
309
327
  },
310
328
  postMessage(requestId, payload) {
311
329
  checks();
@@ -58,6 +58,44 @@ describe('transport', () => {
58
58
  expect(s2Handler).toHaveBeenCalledTimes(2);
59
59
  });
60
60
  });
61
+ describe('disposal', () => {
62
+ it('tears down active producer subscriptions on destroy()', () => {
63
+ const providers = createProviders();
64
+ const events = createNanoEvents();
65
+ const host = createTransport(providers.host);
66
+ const sdk = createTransport(providers.sdk);
67
+ const hostUnsubscribe = vi.fn();
68
+ host.handleSubscription('host_account_connection_status_subscribe', (_, send) => {
69
+ const unsub = events.on('push', () => send({ tag: 'v1', value: 'connected' }));
70
+ return () => {
71
+ unsub();
72
+ hostUnsubscribe();
73
+ };
74
+ });
75
+ sdk.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, vi.fn());
76
+ host.destroy();
77
+ // The producer subscription opened by the handler must be torn down,
78
+ // otherwise it keeps emitting into a disposed transport.
79
+ expect(hostUnsubscribe).toHaveBeenCalledTimes(1);
80
+ });
81
+ it('does not throw when a producer emits after destroy()', () => {
82
+ const providers = createProviders();
83
+ const events = createNanoEvents();
84
+ const host = createTransport(providers.host);
85
+ const sdk = createTransport(providers.sdk);
86
+ host.handleSubscription('host_account_connection_status_subscribe', (_, send) => {
87
+ const unsub = events.on('push', () => send({ tag: 'v1', value: 'connected' }));
88
+ return unsub;
89
+ });
90
+ const handler = vi.fn();
91
+ sdk.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, handler);
92
+ host.destroy();
93
+ // A batched emission queued before disposal must not throw
94
+ // 'Transport is disposed'; it is silently dropped.
95
+ expect(() => events.emit('push')).not.toThrow();
96
+ expect(handler).not.toHaveBeenCalled();
97
+ });
98
+ });
61
99
  describe('debug hook', () => {
62
100
  it('emits outgoing events when postMessage is called and still delivers the message', () => {
63
101
  const providers = createProviders();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novasamatech/host-api",
3
3
  "type": "module",
4
- "version": "0.8.3",
4
+ "version": "0.8.4",
5
5
  "description": "Host API: transport implementation for host - product integration.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -22,7 +22,7 @@
22
22
  "README.md"
23
23
  ],
24
24
  "dependencies": {
25
- "@novasamatech/scale": "0.8.3",
25
+ "@novasamatech/scale": "0.8.4",
26
26
  "nanoevents": "9.1.0",
27
27
  "nanoid": "5.1.11",
28
28
  "neverthrow": "^8.2.0",