@novasamatech/host-api 0.8.2 → 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.
package/dist/protocol/impl.d.ts
CHANGED
|
@@ -858,10 +858,15 @@ export declare const hostApiProtocol: {
|
|
|
858
858
|
} | {
|
|
859
859
|
tag: "PrivateKey";
|
|
860
860
|
value: Uint8Array<ArrayBufferLike>;
|
|
861
|
+
} | {
|
|
862
|
+
tag: "Coins";
|
|
863
|
+
value: Uint8Array<ArrayBufferLike>[];
|
|
861
864
|
};
|
|
862
865
|
}>, Codec<import("scale-ts").ResultPayload<undefined, import("@novasamatech/scale").CodecError<{
|
|
863
866
|
reason: string;
|
|
864
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
867
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
868
|
+
credited: bigint;
|
|
869
|
+
}, "PaymentTopUpErr::PartialPayment">>>];
|
|
865
870
|
}>;
|
|
866
871
|
readonly host_payment_request: VersionedProtocolRequest<{
|
|
867
872
|
readonly v1: [Codec<{
|
|
@@ -911,12 +911,17 @@ export declare const MessagePayload: EnumCodec<Record<"host_handshake_request",
|
|
|
911
911
|
} | {
|
|
912
912
|
tag: "PrivateKey";
|
|
913
913
|
value: Uint8Array<ArrayBufferLike>;
|
|
914
|
+
} | {
|
|
915
|
+
tag: "Coins";
|
|
916
|
+
value: Uint8Array<ArrayBufferLike>[];
|
|
914
917
|
};
|
|
915
918
|
}>;
|
|
916
919
|
}>> & Record<"host_payment_top_up_response", EnumCodec<{
|
|
917
920
|
readonly v1: Codec<import("scale-ts").ResultPayload<undefined, import("@novasamatech/scale").CodecError<{
|
|
918
921
|
reason: string;
|
|
919
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
922
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
923
|
+
credited: bigint;
|
|
924
|
+
}, "PaymentTopUpErr::PartialPayment">>>;
|
|
920
925
|
}>> & Record<"host_payment_request_request", EnumCodec<{
|
|
921
926
|
readonly v1: Codec<{
|
|
922
927
|
from: number | undefined;
|
|
@@ -2405,6 +2410,9 @@ export declare const Message: Codec<{
|
|
|
2405
2410
|
} | {
|
|
2406
2411
|
tag: "PrivateKey";
|
|
2407
2412
|
value: Uint8Array<ArrayBufferLike>;
|
|
2413
|
+
} | {
|
|
2414
|
+
tag: "Coins";
|
|
2415
|
+
value: Uint8Array<ArrayBufferLike>[];
|
|
2408
2416
|
};
|
|
2409
2417
|
};
|
|
2410
2418
|
};
|
|
@@ -2414,7 +2422,9 @@ export declare const Message: Codec<{
|
|
|
2414
2422
|
tag: "v1";
|
|
2415
2423
|
value: import("scale-ts").ResultPayload<undefined, import("@novasamatech/scale").CodecError<{
|
|
2416
2424
|
reason: string;
|
|
2417
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
2425
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
2426
|
+
credited: bigint;
|
|
2427
|
+
}, "PaymentTopUpErr::PartialPayment">>;
|
|
2418
2428
|
};
|
|
2419
2429
|
} | {
|
|
2420
2430
|
tag: "host_payment_request_request";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const Sr25519SecretKey: import("scale-ts").Codec<Uint8Array<ArrayBufferLike>>;
|
|
2
2
|
export declare const PaymentId: import("scale-ts").Codec<string>;
|
|
3
3
|
export declare const CoinPaymentPurseId: import("scale-ts").Codec<number>;
|
|
4
4
|
export declare const PaymentTopUpSource: import("scale-ts").Codec<{
|
|
@@ -7,6 +7,9 @@ export declare const PaymentTopUpSource: import("scale-ts").Codec<{
|
|
|
7
7
|
} | {
|
|
8
8
|
tag: "PrivateKey";
|
|
9
9
|
value: Uint8Array<ArrayBufferLike>;
|
|
10
|
+
} | {
|
|
11
|
+
tag: "Coins";
|
|
12
|
+
value: Uint8Array<ArrayBufferLike>[];
|
|
10
13
|
}>;
|
|
11
14
|
export declare const PaymentBalance: import("scale-ts").Codec<{
|
|
12
15
|
available: bigint;
|
|
@@ -45,27 +48,43 @@ export declare const PaymentBalanceErr: [import("scale-ts").Encoder<import("@nov
|
|
|
45
48
|
reason: string;
|
|
46
49
|
}, "PaymentBalanceErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentBalanceErr::PermissionDenied">;
|
|
47
50
|
};
|
|
51
|
+
export declare const PartialPaymentErr: import("scale-ts").Codec<{
|
|
52
|
+
credited: bigint;
|
|
53
|
+
}>;
|
|
48
54
|
export declare const PaymentTopUpErr: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
49
55
|
reason: string;
|
|
50
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
56
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
57
|
+
credited: bigint;
|
|
58
|
+
}, "PaymentTopUpErr::PartialPayment">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
51
59
|
reason: string;
|
|
52
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
60
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
61
|
+
credited: bigint;
|
|
62
|
+
}, "PaymentTopUpErr::PartialPayment">>] & {
|
|
53
63
|
enc: import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
54
64
|
reason: string;
|
|
55
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
65
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
66
|
+
credited: bigint;
|
|
67
|
+
}, "PaymentTopUpErr::PartialPayment">>;
|
|
56
68
|
dec: import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
57
69
|
reason: string;
|
|
58
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
70
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
71
|
+
credited: bigint;
|
|
72
|
+
}, "PaymentTopUpErr::PartialPayment">>;
|
|
59
73
|
} & {
|
|
60
74
|
readonly InsufficientFunds: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpErr::InsufficientFunds">;
|
|
61
75
|
readonly InvalidSource: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpErr::InvalidSource">;
|
|
76
|
+
readonly PartialPayment: import("@novasamatech/scale").ErrCodec<{
|
|
77
|
+
credited: bigint;
|
|
78
|
+
}, "PaymentTopUpErr::PartialPayment">;
|
|
62
79
|
readonly Unknown: import("@novasamatech/scale").ErrCodec<{
|
|
63
80
|
reason: string;
|
|
64
81
|
}, "PaymentTopUpErr::Unknown">;
|
|
65
82
|
} & {
|
|
66
83
|
[Symbol.hasInstance](v: unknown): v is import("@novasamatech/scale").CodecError<{
|
|
67
84
|
reason: string;
|
|
68
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
85
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
86
|
+
credited: bigint;
|
|
87
|
+
}, "PaymentTopUpErr::PartialPayment">;
|
|
69
88
|
};
|
|
70
89
|
export declare const PaymentRequestErr: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<undefined, "PaymentRequestErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
71
90
|
reason: string;
|
|
@@ -146,11 +165,16 @@ export declare const PaymentTopUpV1_request: import("scale-ts").Codec<{
|
|
|
146
165
|
} | {
|
|
147
166
|
tag: "PrivateKey";
|
|
148
167
|
value: Uint8Array<ArrayBufferLike>;
|
|
168
|
+
} | {
|
|
169
|
+
tag: "Coins";
|
|
170
|
+
value: Uint8Array<ArrayBufferLike>[];
|
|
149
171
|
};
|
|
150
172
|
}>;
|
|
151
173
|
export declare const PaymentTopUpV1_response: import("scale-ts").Codec<import("scale-ts").ResultPayload<undefined, import("@novasamatech/scale").CodecError<{
|
|
152
174
|
reason: string;
|
|
153
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"
|
|
175
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InsufficientFunds"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<{
|
|
176
|
+
credited: bigint;
|
|
177
|
+
}, "PaymentTopUpErr::PartialPayment">>>;
|
|
154
178
|
export declare const PaymentRequestV1_request: import("scale-ts").Codec<{
|
|
155
179
|
from: number | undefined;
|
|
156
180
|
amount: bigint;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Enum, ErrEnum } from '@novasamatech/scale';
|
|
2
|
-
import { Bytes, Option, Result, Struct, _void, str, u128, u32 } from 'scale-ts';
|
|
2
|
+
import { Bytes, Option, Result, Struct, Vector, _void, str, u128, u32 } from 'scale-ts';
|
|
3
3
|
import { GenericErr } from '../commonCodecs.js';
|
|
4
4
|
import { DerivationIndex } from './accounts.js';
|
|
5
5
|
// common types
|
|
6
|
-
export const
|
|
6
|
+
export const Sr25519SecretKey = Bytes(64);
|
|
7
7
|
export const PaymentId = str;
|
|
8
8
|
// Optional purse selector (RFC 0017). `undefined` (None) targets MAIN_PURSE.
|
|
9
9
|
export const CoinPaymentPurseId = u32;
|
|
10
10
|
export const PaymentTopUpSource = Enum({
|
|
11
11
|
ProductAccount: DerivationIndex,
|
|
12
|
-
PrivateKey:
|
|
12
|
+
PrivateKey: Sr25519SecretKey,
|
|
13
|
+
Coins: Vector(Sr25519SecretKey),
|
|
13
14
|
});
|
|
14
15
|
export const PaymentBalance = Struct({
|
|
15
16
|
available: u128,
|
|
@@ -27,9 +28,14 @@ export const PaymentBalanceErr = ErrEnum('PaymentBalanceErr', {
|
|
|
27
28
|
PermissionDenied: [_void, 'permission denied'],
|
|
28
29
|
Unknown: [GenericErr, 'unknown error'],
|
|
29
30
|
});
|
|
31
|
+
// Carries the amount that was credited when only some coins could be claimed.
|
|
32
|
+
export const PartialPaymentErr = Struct({
|
|
33
|
+
credited: u128,
|
|
34
|
+
});
|
|
30
35
|
export const PaymentTopUpErr = ErrEnum('PaymentTopUpErr', {
|
|
31
36
|
InsufficientFunds: [_void, 'insufficient funds'],
|
|
32
37
|
InvalidSource: [_void, 'invalid source'],
|
|
38
|
+
PartialPayment: [PartialPaymentErr, ({ credited }) => `partial payment: credited ${credited}`],
|
|
33
39
|
Unknown: [GenericErr, 'unknown error'],
|
|
34
40
|
});
|
|
35
41
|
export const PaymentRequestErr = ErrEnum('PaymentRequestErr', {
|
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
|
-
|
|
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();
|
package/dist/transport.spec.js
CHANGED
|
@@ -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.
|
|
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.
|
|
25
|
+
"@novasamatech/scale": "0.8.4",
|
|
26
26
|
"nanoevents": "9.1.0",
|
|
27
27
|
"nanoid": "5.1.11",
|
|
28
28
|
"neverthrow": "^8.2.0",
|