@novasamatech/host-api 0.10.2 → 0.11.0
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/hostApi.js +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/protocol/impl.d.ts +44 -3
- package/dist/protocol/impl.js +11 -1
- package/dist/protocol/messageCodec.d.ts +92 -7
- package/dist/protocol/v1/payments.d.ts +94 -25
- package/dist/protocol/v1/payments.js +35 -7
- package/dist/transport.js +65 -54
- package/dist/transport.spec.js +128 -0
- package/package.json +2 -2
package/dist/hostApi.js
CHANGED
|
@@ -293,6 +293,9 @@ export function createHostApi(transport) {
|
|
|
293
293
|
paymentStatusSubscribe(args, callback) {
|
|
294
294
|
return transport.subscribe('host_payment_status_subscribe', args, callback);
|
|
295
295
|
},
|
|
296
|
+
paymentTopUpStatusSubscribe(args, callback) {
|
|
297
|
+
return transport.subscribe('host_payment_top_up_status_subscribe', args, callback);
|
|
298
|
+
},
|
|
296
299
|
requestResourceAllocation(payload) {
|
|
297
300
|
return makeRequest(transport.request('host_request_resource_allocation', payload), reason => ({
|
|
298
301
|
tag: payload.tag,
|
package/dist/index.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export { NotificationId, PushNotification, PushNotificationError } from './proto
|
|
|
31
31
|
export { NavigateToErr } from './protocol/v1/navigation.js';
|
|
32
32
|
export { PreimageKey, PreimageSubmitErr, PreimageValue } from './protocol/v1/preimage.js';
|
|
33
33
|
export { AllocatableResource, AllocationOutcome, ResourceAllocationErr } from './protocol/v1/resourceAllocation.js';
|
|
34
|
-
export { PaymentBalance, PaymentBalanceErr, PaymentId, PaymentReceipt, PaymentRequestErr, PaymentStatus, PaymentStatusErr, PaymentTopUpErr, PaymentTopUpSource, } from './protocol/v1/payments.js';
|
|
34
|
+
export { PaymentBalance, PaymentBalanceErr, PaymentId, PaymentReceipt, PaymentRequestErr, PaymentStatus, PaymentStatusErr, PaymentTopUpErr, PaymentTopUpId, PaymentTopUpSource, PaymentTopUpStatus, PaymentTopUpStatusErr, } from './protocol/v1/payments.js';
|
|
35
35
|
export { Arrangement, BorderStyle, ButtonVariant, ColorToken, ContentAlignment, CustomRendererNode, Dimensions, HorizontalAlignment, Modifier, Shape, Size, TypographyStyle, VerticalAlignment, } from './protocol/v1/customRenderer.js';
|
|
36
36
|
export { ChainHeadEvent, ChainHeadFollowV1_start, ChainIdentifier, ChainInfoErr, OperationStartedResult, RuntimeType, StorageQueryItem, StorageQueryType, StorageResultItem, TransactionBroadcastV1_request, TransactionBroadcastV1_response, TransactionStopV1_request, TransactionStopV1_response, } from './protocol/v1/chainInteraction.js';
|
|
37
37
|
export { CoinPaymentBalance, CoinPaymentCheque, CoinPaymentErr, CoinPaymentListenForItem, CoinPaymentPurseId, CoinPaymentPurseInfo, CoinPaymentReceivable, CoinPaymentStatus, CoinPaymentTransmissionChannel, } from './protocol/v1/coinPayment.js';
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ export { NotificationId, PushNotification, PushNotificationError } from './proto
|
|
|
23
23
|
export { NavigateToErr } from './protocol/v1/navigation.js';
|
|
24
24
|
export { PreimageKey, PreimageSubmitErr, PreimageValue } from './protocol/v1/preimage.js';
|
|
25
25
|
export { AllocatableResource, AllocationOutcome, ResourceAllocationErr } from './protocol/v1/resourceAllocation.js';
|
|
26
|
-
export { PaymentBalance, PaymentBalanceErr, PaymentId, PaymentReceipt, PaymentRequestErr, PaymentStatus, PaymentStatusErr, PaymentTopUpErr, PaymentTopUpSource, } from './protocol/v1/payments.js';
|
|
26
|
+
export { PaymentBalance, PaymentBalanceErr, PaymentId, PaymentReceipt, PaymentRequestErr, PaymentStatus, PaymentStatusErr, PaymentTopUpErr, PaymentTopUpId, PaymentTopUpSource, PaymentTopUpStatus, PaymentTopUpStatusErr, } from './protocol/v1/payments.js';
|
|
27
27
|
export { Arrangement, BorderStyle, ButtonVariant, ColorToken, ContentAlignment, CustomRendererNode, Dimensions, HorizontalAlignment, Modifier, Shape, Size, TypographyStyle, VerticalAlignment, } from './protocol/v1/customRenderer.js';
|
|
28
28
|
export { ChainHeadEvent, ChainHeadFollowV1_start, ChainIdentifier, ChainInfoErr, OperationStartedResult, RuntimeType, StorageQueryItem, StorageQueryType, StorageResultItem, TransactionBroadcastV1_request, TransactionBroadcastV1_response, TransactionStopV1_request, TransactionStopV1_response, } from './protocol/v1/chainInteraction.js';
|
|
29
29
|
export { CoinPaymentBalance, CoinPaymentCheque, CoinPaymentErr, CoinPaymentListenForItem, CoinPaymentPurseId, CoinPaymentPurseInfo, CoinPaymentReceivable, CoinPaymentStatus, CoinPaymentTransmissionChannel, } from './protocol/v1/coinPayment.js';
|
package/dist/protocol/impl.d.ts
CHANGED
|
@@ -942,11 +942,10 @@ export declare const hostApiProtocol: {
|
|
|
942
942
|
tag: "Coins";
|
|
943
943
|
value: Uint8Array<ArrayBufferLike>[];
|
|
944
944
|
};
|
|
945
|
+
id: Uint8Array<ArrayBufferLike>;
|
|
945
946
|
}>, Codec<import("./callError.js").CallResultValue<undefined, import("@novasamatech/scale").CodecError<{
|
|
946
947
|
reason: string;
|
|
947
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
948
|
-
credited: bigint;
|
|
949
|
-
}, "PaymentTopUpErr::PartialPayment">>>];
|
|
948
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">>>];
|
|
950
949
|
}>;
|
|
951
950
|
readonly host_payment_request: VersionedProtocolRequest<{
|
|
952
951
|
readonly v1: [Codec<{
|
|
@@ -1482,5 +1481,47 @@ export declare const hostApiProtocol: {
|
|
|
1482
1481
|
reason: string;
|
|
1483
1482
|
}, "WorkerErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "WorkerErr::TooManyOpen">>>];
|
|
1484
1483
|
}>;
|
|
1484
|
+
readonly host_payment_top_up_status_subscribe: VersionedProtocolSubscription<{
|
|
1485
|
+
readonly v1: [import("@novasamatech/scale").BytesCodec<number>, Codec<{
|
|
1486
|
+
tag: "Detecting";
|
|
1487
|
+
value: undefined;
|
|
1488
|
+
} | {
|
|
1489
|
+
tag: "Claiming";
|
|
1490
|
+
value: undefined;
|
|
1491
|
+
} | {
|
|
1492
|
+
tag: "Claimed";
|
|
1493
|
+
value: {
|
|
1494
|
+
finalized: boolean;
|
|
1495
|
+
};
|
|
1496
|
+
} | {
|
|
1497
|
+
tag: "ClaimedPartially";
|
|
1498
|
+
value: {
|
|
1499
|
+
actualClaimed: bigint;
|
|
1500
|
+
};
|
|
1501
|
+
} | {
|
|
1502
|
+
tag: "NotClaimed";
|
|
1503
|
+
value: undefined;
|
|
1504
|
+
}>, [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
1505
|
+
reason: string;
|
|
1506
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
1507
|
+
reason: string;
|
|
1508
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>] & {
|
|
1509
|
+
enc: import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
1510
|
+
reason: string;
|
|
1511
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>;
|
|
1512
|
+
dec: import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
1513
|
+
reason: string;
|
|
1514
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>;
|
|
1515
|
+
} & {
|
|
1516
|
+
readonly NotFound: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
1517
|
+
readonly Unknown: import("@novasamatech/scale").ErrCodec<{
|
|
1518
|
+
reason: string;
|
|
1519
|
+
}, "PaymentTopUpStatusErr::Unknown">;
|
|
1520
|
+
} & {
|
|
1521
|
+
[Symbol.hasInstance](v: unknown): v is import("@novasamatech/scale").CodecError<{
|
|
1522
|
+
reason: string;
|
|
1523
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
1524
|
+
}];
|
|
1525
|
+
}>;
|
|
1485
1526
|
};
|
|
1486
1527
|
export {};
|
package/dist/protocol/impl.js
CHANGED
|
@@ -14,7 +14,7 @@ import { StorageClearV1_request, StorageClearV1_response, StorageReadV1_request,
|
|
|
14
14
|
import { LocaleSubscribeV1_interrupt, LocaleSubscribeV1_receive, LocaleSubscribeV1_start } from './v1/locale.js';
|
|
15
15
|
import { NavigateToV1_request, NavigateToV1_response } from './v1/navigation.js';
|
|
16
16
|
import { PushNotificationCancelV1_request, PushNotificationCancelV1_response, PushNotificationV1_request, PushNotificationV1_response, } from './v1/notification.js';
|
|
17
|
-
import { PaymentBalanceSubscribeV1_interrupt, PaymentBalanceSubscribeV1_receive, PaymentBalanceSubscribeV1_start, PaymentRequestV1_request, PaymentRequestV1_response, PaymentStatusSubscribeV1_interrupt, PaymentStatusSubscribeV1_receive, PaymentStatusSubscribeV1_start, PaymentTopUpV1_request, PaymentTopUpV1_response, } from './v1/payments.js';
|
|
17
|
+
import { PaymentBalanceSubscribeV1_interrupt, PaymentBalanceSubscribeV1_receive, PaymentBalanceSubscribeV1_start, PaymentRequestV1_request, PaymentRequestV1_response, PaymentStatusSubscribeV1_interrupt, PaymentStatusSubscribeV1_receive, PaymentStatusSubscribeV1_start, PaymentTopUpStatusSubscribeV1_interrupt, PaymentTopUpStatusSubscribeV1_receive, PaymentTopUpStatusSubscribeV1_start, PaymentTopUpV1_request, PaymentTopUpV1_response, } from './v1/payments.js';
|
|
18
18
|
import { PreimageLookupSubscribeV1_interrupt, PreimageLookupSubscribeV1_receive, PreimageLookupSubscribeV1_start, PreimageSubmitV1_request, PreimageSubmitV1_response, } from './v1/preimage.js';
|
|
19
19
|
import { RemotePermissionV1_request, RemotePermissionV1_response } from './v1/remotePermission.js';
|
|
20
20
|
import { RequestResourceAllocationV1_request, RequestResourceAllocationV1_response } from './v1/resourceAllocation.js';
|
|
@@ -317,4 +317,14 @@ export const hostApiProtocol = {
|
|
|
317
317
|
host_worker_end_operation: versionedRequest(indexer.request(), {
|
|
318
318
|
v1: [WorkerEndOperationV1_request, WorkerEndOperationV1_response],
|
|
319
319
|
}),
|
|
320
|
+
// Appended rather than placed next to `host_payment_top_up`: the index is a
|
|
321
|
+
// single running counter, so slotting it into the payment block would shift
|
|
322
|
+
// every id after it.
|
|
323
|
+
host_payment_top_up_status_subscribe: versionedSubscription(indexer.subscription(), {
|
|
324
|
+
v1: [
|
|
325
|
+
PaymentTopUpStatusSubscribeV1_start,
|
|
326
|
+
PaymentTopUpStatusSubscribeV1_receive,
|
|
327
|
+
PaymentTopUpStatusSubscribeV1_interrupt,
|
|
328
|
+
],
|
|
329
|
+
}),
|
|
320
330
|
};
|
|
@@ -995,13 +995,12 @@ export declare const MessagePayload: EnumCodec<Record<"host_handshake_request",
|
|
|
995
995
|
tag: "Coins";
|
|
996
996
|
value: Uint8Array<ArrayBufferLike>[];
|
|
997
997
|
};
|
|
998
|
+
id: Uint8Array<ArrayBufferLike>;
|
|
998
999
|
}>;
|
|
999
1000
|
}>> & Record<"host_payment_top_up_response", EnumCodec<{
|
|
1000
1001
|
readonly v1: Codec<import("./callError.js").CallResultValue<undefined, import("@novasamatech/scale").CodecError<{
|
|
1001
1002
|
reason: string;
|
|
1002
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
1003
|
-
credited: bigint;
|
|
1004
|
-
}, "PaymentTopUpErr::PartialPayment">>>;
|
|
1003
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">>>;
|
|
1005
1004
|
}>> & Record<"host_payment_request_request", EnumCodec<{
|
|
1006
1005
|
readonly v1: Codec<{
|
|
1007
1006
|
from: number | undefined;
|
|
@@ -1577,7 +1576,52 @@ export declare const MessagePayload: EnumCodec<Record<"host_handshake_request",
|
|
|
1577
1576
|
readonly v1: Codec<import("./callError.js").CallResultValue<undefined, import("@novasamatech/scale").CodecError<{
|
|
1578
1577
|
reason: string;
|
|
1579
1578
|
}, "WorkerErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "WorkerErr::TooManyOpen">>>;
|
|
1580
|
-
}
|
|
1579
|
+
}>> & Record<"host_payment_top_up_status_subscribe_start", EnumCodec<{
|
|
1580
|
+
readonly v1: import("@novasamatech/scale").BytesCodec<number>;
|
|
1581
|
+
}>> & Record<"host_payment_top_up_status_subscribe_receive", EnumCodec<{
|
|
1582
|
+
readonly v1: Codec<{
|
|
1583
|
+
tag: "Detecting";
|
|
1584
|
+
value: undefined;
|
|
1585
|
+
} | {
|
|
1586
|
+
tag: "Claiming";
|
|
1587
|
+
value: undefined;
|
|
1588
|
+
} | {
|
|
1589
|
+
tag: "Claimed";
|
|
1590
|
+
value: {
|
|
1591
|
+
finalized: boolean;
|
|
1592
|
+
};
|
|
1593
|
+
} | {
|
|
1594
|
+
tag: "ClaimedPartially";
|
|
1595
|
+
value: {
|
|
1596
|
+
actualClaimed: bigint;
|
|
1597
|
+
};
|
|
1598
|
+
} | {
|
|
1599
|
+
tag: "NotClaimed";
|
|
1600
|
+
value: undefined;
|
|
1601
|
+
}>;
|
|
1602
|
+
}>> & Record<"host_payment_top_up_status_subscribe_interrupt", EnumCodec<{
|
|
1603
|
+
readonly v1: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
1604
|
+
reason: string;
|
|
1605
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
1606
|
+
reason: string;
|
|
1607
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>] & {
|
|
1608
|
+
enc: import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
1609
|
+
reason: string;
|
|
1610
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>;
|
|
1611
|
+
dec: import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
1612
|
+
reason: string;
|
|
1613
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>;
|
|
1614
|
+
} & {
|
|
1615
|
+
readonly NotFound: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
1616
|
+
readonly Unknown: import("@novasamatech/scale").ErrCodec<{
|
|
1617
|
+
reason: string;
|
|
1618
|
+
}, "PaymentTopUpStatusErr::Unknown">;
|
|
1619
|
+
} & {
|
|
1620
|
+
[Symbol.hasInstance](v: unknown): v is import("@novasamatech/scale").CodecError<{
|
|
1621
|
+
reason: string;
|
|
1622
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
1623
|
+
};
|
|
1624
|
+
}>> & Record<"host_payment_top_up_status_subscribe_stop", Codec<undefined>>>;
|
|
1581
1625
|
export declare const Message: Codec<{
|
|
1582
1626
|
requestId: string;
|
|
1583
1627
|
payload: {
|
|
@@ -3006,6 +3050,7 @@ export declare const Message: Codec<{
|
|
|
3006
3050
|
tag: "Coins";
|
|
3007
3051
|
value: Uint8Array<ArrayBufferLike>[];
|
|
3008
3052
|
};
|
|
3053
|
+
id: Uint8Array<ArrayBufferLike>;
|
|
3009
3054
|
};
|
|
3010
3055
|
};
|
|
3011
3056
|
} | {
|
|
@@ -3014,9 +3059,7 @@ export declare const Message: Codec<{
|
|
|
3014
3059
|
tag: "v1";
|
|
3015
3060
|
value: import("./callError.js").CallResultValue<undefined, import("@novasamatech/scale").CodecError<{
|
|
3016
3061
|
reason: string;
|
|
3017
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
3018
|
-
credited: bigint;
|
|
3019
|
-
}, "PaymentTopUpErr::PartialPayment">>;
|
|
3062
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">>;
|
|
3020
3063
|
};
|
|
3021
3064
|
} | {
|
|
3022
3065
|
tag: "host_payment_request_request";
|
|
@@ -3756,6 +3799,48 @@ export declare const Message: Codec<{
|
|
|
3756
3799
|
reason: string;
|
|
3757
3800
|
}, "WorkerErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "WorkerErr::TooManyOpen">>;
|
|
3758
3801
|
};
|
|
3802
|
+
} | {
|
|
3803
|
+
tag: "host_payment_top_up_status_subscribe_start";
|
|
3804
|
+
value: {
|
|
3805
|
+
tag: "v1";
|
|
3806
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
3807
|
+
};
|
|
3808
|
+
} | {
|
|
3809
|
+
tag: "host_payment_top_up_status_subscribe_receive";
|
|
3810
|
+
value: {
|
|
3811
|
+
tag: "v1";
|
|
3812
|
+
value: {
|
|
3813
|
+
tag: "Detecting";
|
|
3814
|
+
value: undefined;
|
|
3815
|
+
} | {
|
|
3816
|
+
tag: "Claiming";
|
|
3817
|
+
value: undefined;
|
|
3818
|
+
} | {
|
|
3819
|
+
tag: "Claimed";
|
|
3820
|
+
value: {
|
|
3821
|
+
finalized: boolean;
|
|
3822
|
+
};
|
|
3823
|
+
} | {
|
|
3824
|
+
tag: "ClaimedPartially";
|
|
3825
|
+
value: {
|
|
3826
|
+
actualClaimed: bigint;
|
|
3827
|
+
};
|
|
3828
|
+
} | {
|
|
3829
|
+
tag: "NotClaimed";
|
|
3830
|
+
value: undefined;
|
|
3831
|
+
};
|
|
3832
|
+
};
|
|
3833
|
+
} | {
|
|
3834
|
+
tag: "host_payment_top_up_status_subscribe_interrupt";
|
|
3835
|
+
value: {
|
|
3836
|
+
tag: "v1";
|
|
3837
|
+
value: import("@novasamatech/scale").CodecError<{
|
|
3838
|
+
reason: string;
|
|
3839
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
3840
|
+
};
|
|
3841
|
+
} | {
|
|
3842
|
+
tag: "host_payment_top_up_status_subscribe_stop";
|
|
3843
|
+
value: undefined;
|
|
3759
3844
|
};
|
|
3760
3845
|
}>;
|
|
3761
3846
|
export type MessageAction = MessagePayloadSchema['tag'];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const Sr25519SecretKey: import("@novasamatech/scale").BytesCodec<number>;
|
|
2
2
|
export declare const PaymentId: import("scale-ts").Codec<string>;
|
|
3
|
+
/** Product-supplied, opaque 32-byte idempotency key for a top up. */
|
|
4
|
+
export declare const PaymentTopUpId: import("@novasamatech/scale").BytesCodec<number>;
|
|
3
5
|
export declare const CoinPaymentPurseId: import("scale-ts").Codec<number>;
|
|
4
6
|
export declare const PaymentTopUpSource: import("scale-ts").Codec<{
|
|
5
7
|
tag: "ProductAccount";
|
|
@@ -33,6 +35,26 @@ export declare const PaymentStatus: import("scale-ts").Codec<{
|
|
|
33
35
|
tag: "Completed";
|
|
34
36
|
value: undefined;
|
|
35
37
|
}>;
|
|
38
|
+
export declare const PaymentTopUpStatus: import("scale-ts").Codec<{
|
|
39
|
+
tag: "Detecting";
|
|
40
|
+
value: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
tag: "Claiming";
|
|
43
|
+
value: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
tag: "Claimed";
|
|
46
|
+
value: {
|
|
47
|
+
finalized: boolean;
|
|
48
|
+
};
|
|
49
|
+
} | {
|
|
50
|
+
tag: "ClaimedPartially";
|
|
51
|
+
value: {
|
|
52
|
+
actualClaimed: bigint;
|
|
53
|
+
};
|
|
54
|
+
} | {
|
|
55
|
+
tag: "NotClaimed";
|
|
56
|
+
value: undefined;
|
|
57
|
+
}>;
|
|
36
58
|
export declare const PaymentBalanceErr: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
37
59
|
reason: string;
|
|
38
60
|
}, "PaymentBalanceErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentBalanceErr::PermissionDenied">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
@@ -54,43 +76,49 @@ export declare const PaymentBalanceErr: [import("scale-ts").Encoder<import("@nov
|
|
|
54
76
|
reason: string;
|
|
55
77
|
}, "PaymentBalanceErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentBalanceErr::PermissionDenied">;
|
|
56
78
|
};
|
|
57
|
-
export declare const PartialPaymentErr: import("scale-ts").Codec<{
|
|
58
|
-
credited: bigint;
|
|
59
|
-
}>;
|
|
60
79
|
export declare const PaymentTopUpErr: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
61
80
|
reason: string;
|
|
62
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
63
|
-
credited: bigint;
|
|
64
|
-
}, "PaymentTopUpErr::PartialPayment">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
81
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
65
82
|
reason: string;
|
|
66
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
67
|
-
credited: bigint;
|
|
68
|
-
}, "PaymentTopUpErr::PartialPayment">>] & {
|
|
83
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">>] & {
|
|
69
84
|
enc: import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
70
85
|
reason: string;
|
|
71
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
72
|
-
credited: bigint;
|
|
73
|
-
}, "PaymentTopUpErr::PartialPayment">>;
|
|
86
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">>;
|
|
74
87
|
dec: import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
75
88
|
reason: string;
|
|
76
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
77
|
-
credited: bigint;
|
|
78
|
-
}, "PaymentTopUpErr::PartialPayment">>;
|
|
89
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">>;
|
|
79
90
|
} & {
|
|
80
|
-
readonly InsufficientFunds: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpErr::InsufficientFunds">;
|
|
81
91
|
readonly InvalidSource: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpErr::InvalidSource">;
|
|
82
|
-
readonly
|
|
83
|
-
|
|
84
|
-
}, "PaymentTopUpErr::PartialPayment">;
|
|
92
|
+
readonly AlreadyExists: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpErr::AlreadyExists">;
|
|
93
|
+
readonly SourceBusy: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpErr::SourceBusy">;
|
|
85
94
|
readonly Unknown: import("@novasamatech/scale").ErrCodec<{
|
|
86
95
|
reason: string;
|
|
87
96
|
}, "PaymentTopUpErr::Unknown">;
|
|
88
97
|
} & {
|
|
89
98
|
[Symbol.hasInstance](v: unknown): v is import("@novasamatech/scale").CodecError<{
|
|
90
99
|
reason: string;
|
|
91
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">;
|
|
101
|
+
};
|
|
102
|
+
export declare const PaymentTopUpStatusErr: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
103
|
+
reason: string;
|
|
104
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
105
|
+
reason: string;
|
|
106
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>] & {
|
|
107
|
+
enc: import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
108
|
+
reason: string;
|
|
109
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>;
|
|
110
|
+
dec: import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
111
|
+
reason: string;
|
|
112
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>;
|
|
113
|
+
} & {
|
|
114
|
+
readonly NotFound: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
115
|
+
readonly Unknown: import("@novasamatech/scale").ErrCodec<{
|
|
116
|
+
reason: string;
|
|
117
|
+
}, "PaymentTopUpStatusErr::Unknown">;
|
|
118
|
+
} & {
|
|
119
|
+
[Symbol.hasInstance](v: unknown): v is import("@novasamatech/scale").CodecError<{
|
|
120
|
+
reason: string;
|
|
121
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
94
122
|
};
|
|
95
123
|
export declare const PaymentRequestErr: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<undefined, "PaymentRequestErr::Rejected"> | import("@novasamatech/scale").CodecError<{
|
|
96
124
|
reason: string;
|
|
@@ -181,12 +209,11 @@ export declare const PaymentTopUpV1_request: import("scale-ts").Codec<{
|
|
|
181
209
|
tag: "Coins";
|
|
182
210
|
value: Uint8Array<ArrayBufferLike>[];
|
|
183
211
|
};
|
|
212
|
+
id: Uint8Array<ArrayBufferLike>;
|
|
184
213
|
}>;
|
|
185
214
|
export declare const PaymentTopUpV1_response: import("scale-ts").Codec<import("../callError.js").CallResultValue<undefined, import("@novasamatech/scale").CodecError<{
|
|
186
215
|
reason: string;
|
|
187
|
-
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::
|
|
188
|
-
credited: bigint;
|
|
189
|
-
}, "PaymentTopUpErr::PartialPayment">>>;
|
|
216
|
+
}, "PaymentTopUpErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::InvalidSource"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::AlreadyExists"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpErr::SourceBusy">>>;
|
|
190
217
|
export declare const PaymentRequestV1_request: import("scale-ts").Codec<{
|
|
191
218
|
from: number | undefined;
|
|
192
219
|
amount: bigint;
|
|
@@ -229,3 +256,45 @@ export declare const PaymentStatusSubscribeV1_interrupt: [import("scale-ts").Enc
|
|
|
229
256
|
reason: string;
|
|
230
257
|
}, "PaymentStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentStatusErr::PaymentNotFound">;
|
|
231
258
|
};
|
|
259
|
+
export declare const PaymentTopUpStatusSubscribeV1_start: import("@novasamatech/scale").BytesCodec<number>;
|
|
260
|
+
export declare const PaymentTopUpStatusSubscribeV1_receive: import("scale-ts").Codec<{
|
|
261
|
+
tag: "Detecting";
|
|
262
|
+
value: undefined;
|
|
263
|
+
} | {
|
|
264
|
+
tag: "Claiming";
|
|
265
|
+
value: undefined;
|
|
266
|
+
} | {
|
|
267
|
+
tag: "Claimed";
|
|
268
|
+
value: {
|
|
269
|
+
finalized: boolean;
|
|
270
|
+
};
|
|
271
|
+
} | {
|
|
272
|
+
tag: "ClaimedPartially";
|
|
273
|
+
value: {
|
|
274
|
+
actualClaimed: bigint;
|
|
275
|
+
};
|
|
276
|
+
} | {
|
|
277
|
+
tag: "NotClaimed";
|
|
278
|
+
value: undefined;
|
|
279
|
+
}>;
|
|
280
|
+
export declare const PaymentTopUpStatusSubscribeV1_interrupt: [import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
281
|
+
reason: string;
|
|
282
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>, import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
283
|
+
reason: string;
|
|
284
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>] & {
|
|
285
|
+
enc: import("scale-ts").Encoder<import("@novasamatech/scale").CodecError<{
|
|
286
|
+
reason: string;
|
|
287
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>;
|
|
288
|
+
dec: import("scale-ts").Decoder<import("@novasamatech/scale").CodecError<{
|
|
289
|
+
reason: string;
|
|
290
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">>;
|
|
291
|
+
} & {
|
|
292
|
+
readonly NotFound: import("@novasamatech/scale").ErrCodec<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
293
|
+
readonly Unknown: import("@novasamatech/scale").ErrCodec<{
|
|
294
|
+
reason: string;
|
|
295
|
+
}, "PaymentTopUpStatusErr::Unknown">;
|
|
296
|
+
} & {
|
|
297
|
+
[Symbol.hasInstance](v: unknown): v is import("@novasamatech/scale").CodecError<{
|
|
298
|
+
reason: string;
|
|
299
|
+
}, "PaymentTopUpStatusErr::Unknown"> | import("@novasamatech/scale").CodecError<undefined, "PaymentTopUpStatusErr::NotFound">;
|
|
300
|
+
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Bytes, Enum, ErrEnum } from '@novasamatech/scale';
|
|
2
|
-
import { Option, Struct, Vector, _void, str, u128, u32 } from 'scale-ts';
|
|
2
|
+
import { Option, Struct, Vector, _void, bool, str, u128, u32 } from 'scale-ts';
|
|
3
3
|
import { CallResult } from '../callError.js';
|
|
4
4
|
import { GenericErr } from '../commonCodecs.js';
|
|
5
5
|
import { DerivationIndex } from './accounts.js';
|
|
6
6
|
// common types
|
|
7
7
|
export const Sr25519SecretKey = Bytes(64);
|
|
8
8
|
export const PaymentId = str;
|
|
9
|
+
/** Product-supplied, opaque 32-byte idempotency key for a top up. */
|
|
10
|
+
export const PaymentTopUpId = Bytes(32);
|
|
9
11
|
// Optional purse selector (RFC 0017). `undefined` (None) targets MAIN_PURSE.
|
|
10
12
|
export const CoinPaymentPurseId = u32;
|
|
11
13
|
export const PaymentTopUpSource = Enum({
|
|
@@ -25,19 +27,40 @@ export const PaymentStatus = Enum({
|
|
|
25
27
|
Completed: _void,
|
|
26
28
|
Failed: str,
|
|
27
29
|
});
|
|
30
|
+
// A status never advances once it reaches a terminal variant (`Claimed` with
|
|
31
|
+
// `finalized`, `ClaimedPartially`, `NotClaimed`); the host takes no further
|
|
32
|
+
// action and keeps the status readable indefinitely.
|
|
33
|
+
export const PaymentTopUpStatus = Enum({
|
|
34
|
+
// Waiting for the requested amount to appear on the source.
|
|
35
|
+
Detecting: _void,
|
|
36
|
+
Claiming: _void,
|
|
37
|
+
// At least the requested amount was claimed. Terminal once `finalized`.
|
|
38
|
+
Claimed: Struct({ finalized: bool }),
|
|
39
|
+
// Terminal: the host could only claim `actualClaimed`, less than requested.
|
|
40
|
+
// Usually means the source never held enough balance to cover the request —
|
|
41
|
+
// e.g. a re-orged pre-funding transfer. Requesting an amount below the
|
|
42
|
+
// smallest coinage denomination lands here too: the host claims
|
|
43
|
+
// `amount - amount % 2^min_coinage_exponent`.
|
|
44
|
+
ClaimedPartially: Struct({ actualClaimed: u128 }),
|
|
45
|
+
// Terminal: nothing could be claimed, most likely because the host never
|
|
46
|
+
// observed any balance at the source.
|
|
47
|
+
NotClaimed: _void,
|
|
48
|
+
});
|
|
28
49
|
// errors
|
|
29
50
|
export const PaymentBalanceErr = ErrEnum('PaymentBalanceErr', {
|
|
30
51
|
PermissionDenied: [_void, 'permission denied'],
|
|
31
52
|
Unknown: [GenericErr, 'unknown error'],
|
|
32
53
|
});
|
|
33
|
-
// Carries the amount that was credited when only some coins could be claimed.
|
|
34
|
-
export const PartialPaymentErr = Struct({
|
|
35
|
-
credited: u128,
|
|
36
|
-
});
|
|
37
54
|
export const PaymentTopUpErr = ErrEnum('PaymentTopUpErr', {
|
|
38
|
-
InsufficientFunds: [_void, 'insufficient funds'],
|
|
39
55
|
InvalidSource: [_void, 'invalid source'],
|
|
40
|
-
|
|
56
|
+
AlreadyExists: [_void, 'a top up with this id already exists'],
|
|
57
|
+
// Only one top up can be live per source; the source frees up once the
|
|
58
|
+
// previous top up reaches a terminal status.
|
|
59
|
+
SourceBusy: [_void, 'source already has a top up in progress'],
|
|
60
|
+
Unknown: [GenericErr, 'unknown error'],
|
|
61
|
+
});
|
|
62
|
+
export const PaymentTopUpStatusErr = ErrEnum('PaymentTopUpStatusErr', {
|
|
63
|
+
NotFound: [_void, 'top up not found'],
|
|
41
64
|
Unknown: [GenericErr, 'unknown error'],
|
|
42
65
|
});
|
|
43
66
|
export const PaymentRequestErr = ErrEnum('PaymentRequestErr', {
|
|
@@ -60,6 +83,7 @@ export const PaymentTopUpV1_request = Struct({
|
|
|
60
83
|
into: Option(CoinPaymentPurseId),
|
|
61
84
|
amount: u128,
|
|
62
85
|
source: PaymentTopUpSource,
|
|
86
|
+
id: PaymentTopUpId,
|
|
63
87
|
});
|
|
64
88
|
export const PaymentTopUpV1_response = CallResult(_void, PaymentTopUpErr);
|
|
65
89
|
// host_payment_request
|
|
@@ -73,3 +97,7 @@ export const PaymentRequestV1_response = CallResult(PaymentReceipt, PaymentReque
|
|
|
73
97
|
export const PaymentStatusSubscribeV1_start = PaymentId;
|
|
74
98
|
export const PaymentStatusSubscribeV1_receive = PaymentStatus;
|
|
75
99
|
export const PaymentStatusSubscribeV1_interrupt = PaymentStatusErr;
|
|
100
|
+
// host_payment_top_up_status_subscribe
|
|
101
|
+
export const PaymentTopUpStatusSubscribeV1_start = PaymentTopUpId;
|
|
102
|
+
export const PaymentTopUpStatusSubscribeV1_receive = PaymentTopUpStatus;
|
|
103
|
+
export const PaymentTopUpStatusSubscribeV1_interrupt = PaymentTopUpStatusErr;
|
package/dist/transport.js
CHANGED
|
@@ -80,6 +80,56 @@ export function createTransport(provider) {
|
|
|
80
80
|
const messageProvider = createMessageProvider(provider);
|
|
81
81
|
// subscriptions management (multiplexing)
|
|
82
82
|
const activeSubscriptions = new Map();
|
|
83
|
+
// Wires a real subscription on the transport; later subscribers with the same
|
|
84
|
+
// start payload join it through `listeners` instead of opening another. The
|
|
85
|
+
// first listener is registered before `start` is posted: an in-process host
|
|
86
|
+
// may send its first value synchronously while handling it.
|
|
87
|
+
function openSubscription(method, subscriptionKey, startPayload, listener) {
|
|
88
|
+
const requestId = createRequestId();
|
|
89
|
+
const stopAction = composeAction(method, 'stop');
|
|
90
|
+
const interruptAction = composeAction(method, 'interrupt');
|
|
91
|
+
const receiveAction = composeAction(method, 'receive');
|
|
92
|
+
const unsubscribeReceive = transport.listenMessages(receiveAction, (receivedId, data) => {
|
|
93
|
+
if (receivedId === requestId) {
|
|
94
|
+
for (const listener of subscription.listeners) {
|
|
95
|
+
try {
|
|
96
|
+
listener.call(data.value);
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
provider.logger.error(`subscription "${method}" listener threw`, e);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const unsubscribeInterrupt = transport.listenMessages(interruptAction, (receivedId, data) => {
|
|
105
|
+
if (receivedId === requestId) {
|
|
106
|
+
// The host has dropped this subscription. Tear it down first so a
|
|
107
|
+
// re-subscribe with the same payload, even from inside onInterrupt,
|
|
108
|
+
// opens a fresh one instead of joining this dead entry.
|
|
109
|
+
stopSubscription();
|
|
110
|
+
subscription.latchedInterrupt = { payload: data.value };
|
|
111
|
+
subscription.interruptEvents.emit('interrupt', data.value);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
const stopSubscription = () => {
|
|
115
|
+
activeSubscriptions.delete(subscriptionKey);
|
|
116
|
+
unsubscribeReceive();
|
|
117
|
+
unsubscribeInterrupt();
|
|
118
|
+
};
|
|
119
|
+
const subscription = {
|
|
120
|
+
requestId,
|
|
121
|
+
listeners: [listener],
|
|
122
|
+
interruptEvents: createNanoEvents(),
|
|
123
|
+
kill: () => {
|
|
124
|
+
stopSubscription();
|
|
125
|
+
const stopPayload = enumValue(stopAction, undefined);
|
|
126
|
+
transport.postMessage(requestId, stopPayload);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
activeSubscriptions.set(subscriptionKey, subscription);
|
|
130
|
+
transport.postMessage(requestId, startPayload);
|
|
131
|
+
return subscription;
|
|
132
|
+
}
|
|
83
133
|
// Lazy provider subscription — zero per-message decode cost while no
|
|
84
134
|
// debug listener is attached.
|
|
85
135
|
let debugListenerCount = 0;
|
|
@@ -208,17 +258,14 @@ export function createTransport(provider) {
|
|
|
208
258
|
},
|
|
209
259
|
subscribe(method, payload, callback) {
|
|
210
260
|
checks();
|
|
211
|
-
const events = createNanoEvents();
|
|
212
261
|
const startAction = composeAction(method, 'start');
|
|
213
262
|
const startPayload = enumValue(startAction, payload);
|
|
214
263
|
const subscriptionKey = getSubscriptionKey(method, startPayload);
|
|
215
|
-
let subscription = activeSubscriptions.get(subscriptionKey);
|
|
216
264
|
function unsubscribeListener() {
|
|
217
265
|
const subscription = activeSubscriptions.get(subscriptionKey);
|
|
218
266
|
if (subscription) {
|
|
219
267
|
const newListeners = subscription.listeners.filter(listener => listener.call !== callback);
|
|
220
268
|
if (newListeners.length === 0) {
|
|
221
|
-
activeSubscriptions.delete(subscriptionKey);
|
|
222
269
|
subscription.kill();
|
|
223
270
|
}
|
|
224
271
|
else {
|
|
@@ -230,61 +277,21 @@ export function createTransport(provider) {
|
|
|
230
277
|
call: callback,
|
|
231
278
|
unsubscribe: unsubscribeListener,
|
|
232
279
|
};
|
|
233
|
-
const
|
|
280
|
+
const existing = activeSubscriptions.get(subscriptionKey);
|
|
281
|
+
existing?.listeners.push(listener);
|
|
282
|
+
const subscription = existing ?? openSubscription(method, subscriptionKey, startPayload, listener);
|
|
283
|
+
return {
|
|
234
284
|
unsubscribe: unsubscribeListener,
|
|
235
285
|
onInterrupt(callback) {
|
|
236
|
-
|
|
286
|
+
if (subscription.latchedInterrupt) {
|
|
287
|
+
callback(subscription.latchedInterrupt.payload);
|
|
288
|
+
return () => {
|
|
289
|
+
/* already delivered */
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return subscription.interruptEvents.on('interrupt', callback);
|
|
237
293
|
},
|
|
238
294
|
};
|
|
239
|
-
// wiring up a real subscription
|
|
240
|
-
if (!subscription) {
|
|
241
|
-
const requestId = createRequestId();
|
|
242
|
-
const stopAction = composeAction(method, 'stop');
|
|
243
|
-
const interruptAction = composeAction(method, 'interrupt');
|
|
244
|
-
const receiveAction = composeAction(method, 'receive');
|
|
245
|
-
const unsubscribeReceive = transport.listenMessages(receiveAction, (receivedId, data) => {
|
|
246
|
-
if (receivedId === requestId) {
|
|
247
|
-
const subscription = activeSubscriptions.get(subscriptionKey);
|
|
248
|
-
if (subscription) {
|
|
249
|
-
for (const listener of subscription.listeners) {
|
|
250
|
-
try {
|
|
251
|
-
listener.call(data.value);
|
|
252
|
-
}
|
|
253
|
-
catch (e) {
|
|
254
|
-
provider.logger.error(`subscription "${method}" listener threw`, e);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
const unsubscribeInterrupt = transport.listenMessages(interruptAction, (receivedId, data) => {
|
|
261
|
-
if (receivedId === requestId) {
|
|
262
|
-
events.emit('interrupt', data.value);
|
|
263
|
-
stopSubscription();
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
const stopSubscription = () => {
|
|
267
|
-
unsubscribeReceive();
|
|
268
|
-
unsubscribeInterrupt();
|
|
269
|
-
events.events = {};
|
|
270
|
-
};
|
|
271
|
-
// creating subscription
|
|
272
|
-
subscription = {
|
|
273
|
-
requestId,
|
|
274
|
-
kill: () => {
|
|
275
|
-
stopSubscription();
|
|
276
|
-
const stopPayload = enumValue(stopAction, undefined);
|
|
277
|
-
transport.postMessage(requestId, stopPayload);
|
|
278
|
-
},
|
|
279
|
-
listeners: [listener],
|
|
280
|
-
};
|
|
281
|
-
activeSubscriptions.set(subscriptionKey, subscription);
|
|
282
|
-
transport.postMessage(requestId, startPayload);
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
subscription.listeners.push(listener);
|
|
286
|
-
}
|
|
287
|
-
return publicSubscription;
|
|
288
295
|
},
|
|
289
296
|
handleSubscription(method, handler) {
|
|
290
297
|
checks();
|
|
@@ -302,8 +309,12 @@ export function createTransport(provider) {
|
|
|
302
309
|
transport.postMessage(requestId, receivePayload);
|
|
303
310
|
}, value => {
|
|
304
311
|
interrupted = true;
|
|
312
|
+
// Undefined while the handler is still running; then the handler's
|
|
313
|
+
// own cleanup, which must run since no `stop` will ever arrive.
|
|
314
|
+
const cleanup = subscriptions.get(requestId);
|
|
305
315
|
subscriptions.delete(requestId);
|
|
306
316
|
transport.postMessage(requestId, enumValue(interruptAction, value));
|
|
317
|
+
cleanup?.();
|
|
307
318
|
});
|
|
308
319
|
if (interrupted) {
|
|
309
320
|
unsubscribe();
|
package/dist/transport.spec.js
CHANGED
|
@@ -57,6 +57,134 @@ describe('transport', () => {
|
|
|
57
57
|
expect(s1Handler).toHaveBeenCalledTimes(1);
|
|
58
58
|
expect(s2Handler).toHaveBeenCalledTimes(2);
|
|
59
59
|
});
|
|
60
|
+
it('starts a fresh host subscription when re-subscribing with the same payload after an interrupt', () => {
|
|
61
|
+
const providers = createProviders();
|
|
62
|
+
const events = createNanoEvents();
|
|
63
|
+
const host = createTransport(providers.host);
|
|
64
|
+
const sdk = createTransport(providers.sdk);
|
|
65
|
+
const containerHandler = vi.fn((_, send, interrupt) => {
|
|
66
|
+
const unsubPush = events.on('push', () => send({ tag: 'v1', value: 'connected' }));
|
|
67
|
+
const unsubInterrupt = events.on('interrupt', () => interrupt({ tag: 'v1', value: undefined }));
|
|
68
|
+
return () => {
|
|
69
|
+
unsubPush();
|
|
70
|
+
unsubInterrupt();
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
host.handleSubscription('host_account_connection_status_subscribe', containerHandler);
|
|
74
|
+
const first = vi.fn();
|
|
75
|
+
sdk.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, first);
|
|
76
|
+
expect(containerHandler).toHaveBeenCalledTimes(1);
|
|
77
|
+
events.emit('interrupt');
|
|
78
|
+
const second = vi.fn();
|
|
79
|
+
sdk.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, second);
|
|
80
|
+
// The interrupted subscription is gone: the second subscribe must open a
|
|
81
|
+
// new one on the host rather than joining the dead entry.
|
|
82
|
+
expect(containerHandler).toHaveBeenCalledTimes(2);
|
|
83
|
+
events.emit('push');
|
|
84
|
+
expect(first).not.toHaveBeenCalled();
|
|
85
|
+
expect(second).toHaveBeenCalledTimes(1);
|
|
86
|
+
});
|
|
87
|
+
it('delivers an interrupt raised synchronously from the host handler to a later onInterrupt listener', () => {
|
|
88
|
+
const providers = createProviders();
|
|
89
|
+
const host = createTransport(providers.host);
|
|
90
|
+
const sdk = createTransport(providers.sdk);
|
|
91
|
+
host.handleSubscription('host_account_connection_status_subscribe', (_, _send, interrupt) => {
|
|
92
|
+
interrupt({ tag: 'v1', value: undefined });
|
|
93
|
+
return vi.fn();
|
|
94
|
+
});
|
|
95
|
+
const subscription = sdk.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, vi.fn());
|
|
96
|
+
// With an in-process host the interrupt arrives before subscribe() has
|
|
97
|
+
// returned, so onInterrupt is necessarily attached afterwards.
|
|
98
|
+
const interrupted = vi.fn();
|
|
99
|
+
subscription.onInterrupt(interrupted);
|
|
100
|
+
expect(interrupted).toHaveBeenCalledExactlyOnceWith({ tag: 'v1', value: undefined });
|
|
101
|
+
});
|
|
102
|
+
it('delivers an interrupt to every subscriber sharing a multiplexed subscription', () => {
|
|
103
|
+
const providers = createProviders();
|
|
104
|
+
const events = createNanoEvents();
|
|
105
|
+
const host = createTransport(providers.host);
|
|
106
|
+
const sdk = createTransport(providers.sdk);
|
|
107
|
+
host.handleSubscription('host_account_connection_status_subscribe', (_, _send, interrupt) => events.on('interrupt', () => interrupt({ tag: 'v1', value: undefined })));
|
|
108
|
+
const first = vi.fn();
|
|
109
|
+
const second = vi.fn();
|
|
110
|
+
sdk
|
|
111
|
+
.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, vi.fn())
|
|
112
|
+
.onInterrupt(first);
|
|
113
|
+
sdk
|
|
114
|
+
.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, vi.fn())
|
|
115
|
+
.onInterrupt(second);
|
|
116
|
+
events.emit('interrupt');
|
|
117
|
+
expect(first).toHaveBeenCalledExactlyOnceWith({ tag: 'v1', value: undefined });
|
|
118
|
+
expect(second).toHaveBeenCalledExactlyOnceWith({ tag: 'v1', value: undefined });
|
|
119
|
+
});
|
|
120
|
+
it('delivers a later interrupt to a listener attached before it', () => {
|
|
121
|
+
const providers = createProviders();
|
|
122
|
+
const events = createNanoEvents();
|
|
123
|
+
const host = createTransport(providers.host);
|
|
124
|
+
const sdk = createTransport(providers.sdk);
|
|
125
|
+
host.handleSubscription('host_account_connection_status_subscribe', (_, _send, interrupt) => events.on('interrupt', () => interrupt({ tag: 'v1', value: undefined })));
|
|
126
|
+
const interrupted = vi.fn();
|
|
127
|
+
sdk
|
|
128
|
+
.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, vi.fn())
|
|
129
|
+
.onInterrupt(interrupted);
|
|
130
|
+
events.emit('interrupt');
|
|
131
|
+
expect(interrupted).toHaveBeenCalledExactlyOnceWith({ tag: 'v1', value: undefined });
|
|
132
|
+
});
|
|
133
|
+
it('drops receive frames and makes unsubscribe a no-op once interrupted', () => {
|
|
134
|
+
const providers = createProviders();
|
|
135
|
+
const events = createNanoEvents();
|
|
136
|
+
const host = createTransport(providers.host);
|
|
137
|
+
const sdk = createTransport(providers.sdk);
|
|
138
|
+
const hostCleanup = vi.fn();
|
|
139
|
+
host.handleSubscription('host_account_connection_status_subscribe', (_, send, interrupt) => {
|
|
140
|
+
events.on('interrupt', () => interrupt({ tag: 'v1', value: undefined }));
|
|
141
|
+
events.on('push', () => send({ tag: 'v1', value: 'connected' }));
|
|
142
|
+
return hostCleanup;
|
|
143
|
+
});
|
|
144
|
+
const callback = vi.fn();
|
|
145
|
+
const subscription = sdk.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, callback);
|
|
146
|
+
events.emit('interrupt');
|
|
147
|
+
// The host-side handler is already torn down by the interrupt itself.
|
|
148
|
+
expect(hostCleanup).toHaveBeenCalledTimes(1);
|
|
149
|
+
events.emit('push');
|
|
150
|
+
expect(callback).not.toHaveBeenCalled();
|
|
151
|
+
// Nothing is left to stop on the host, so no second cleanup.
|
|
152
|
+
subscription.unsubscribe();
|
|
153
|
+
expect(hostCleanup).toHaveBeenCalledTimes(1);
|
|
154
|
+
});
|
|
155
|
+
it('opens a fresh host subscription when re-subscribing from inside onInterrupt', () => {
|
|
156
|
+
const providers = createProviders();
|
|
157
|
+
const events = createNanoEvents();
|
|
158
|
+
const host = createTransport(providers.host);
|
|
159
|
+
const sdk = createTransport(providers.sdk);
|
|
160
|
+
const containerHandler = vi.fn((_, send, interrupt) => {
|
|
161
|
+
events.on('interrupt', () => interrupt({ tag: 'v1', value: undefined }));
|
|
162
|
+
return events.on('push', () => send({ tag: 'v1', value: 'connected' }));
|
|
163
|
+
});
|
|
164
|
+
host.handleSubscription('host_account_connection_status_subscribe', containerHandler);
|
|
165
|
+
const retried = vi.fn();
|
|
166
|
+
sdk
|
|
167
|
+
.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, vi.fn())
|
|
168
|
+
.onInterrupt(() => {
|
|
169
|
+
sdk.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, retried);
|
|
170
|
+
});
|
|
171
|
+
events.emit('interrupt');
|
|
172
|
+
expect(containerHandler).toHaveBeenCalledTimes(2);
|
|
173
|
+
events.emit('push');
|
|
174
|
+
expect(retried).toHaveBeenCalledTimes(1);
|
|
175
|
+
});
|
|
176
|
+
it('delivers a value the host sends synchronously while handling start', () => {
|
|
177
|
+
const providers = createProviders();
|
|
178
|
+
const host = createTransport(providers.host);
|
|
179
|
+
const sdk = createTransport(providers.sdk);
|
|
180
|
+
host.handleSubscription('host_account_connection_status_subscribe', (_, send) => {
|
|
181
|
+
send({ tag: 'v1', value: 'connected' });
|
|
182
|
+
return vi.fn();
|
|
183
|
+
});
|
|
184
|
+
const callback = vi.fn();
|
|
185
|
+
sdk.subscribe('host_account_connection_status_subscribe', { tag: 'v1', value: undefined }, callback);
|
|
186
|
+
expect(callback).toHaveBeenCalledExactlyOnceWith({ tag: 'v1', value: 'connected' });
|
|
187
|
+
});
|
|
60
188
|
it('isolates a throwing subscriber so siblings on the same subscription still receive', () => {
|
|
61
189
|
const providers = createProviders();
|
|
62
190
|
const events = createNanoEvents();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novasamatech/host-api",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.0",
|
|
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.
|
|
25
|
+
"@novasamatech/scale": "0.11.0",
|
|
26
26
|
"nanoevents": "10.0.0",
|
|
27
27
|
"nanoid": "6.0.1",
|
|
28
28
|
"neverthrow": "^8.2.0",
|