@notabene/javascript-sdk 2.12.0 → 2.13.0-next.2

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.
@@ -0,0 +1,278 @@
1
+ import { decodeJwt } from 'jose';
2
+ import {
3
+ PersonType,
4
+ type OwnershipProof,
5
+ type TransactionResponse,
6
+ type Withdrawal,
7
+ } from '../types';
8
+ import {
9
+ mapToAppendPiiRequest,
10
+ mapToTransactCreateRequest,
11
+ mapToV1CreateRequest,
12
+ } from './mappers';
13
+ import type {
14
+ DelegateToken,
15
+ ResponseToTxRequestConfig,
16
+ TransactionCreateRequest,
17
+ TransactionCreateRequestV2,
18
+ TransactionIVMS101Request,
19
+ } from './types';
20
+
21
+ /**
22
+ * Fills in missing config values by extracting them from the delegate token and withdrawal data
23
+ * @internal
24
+ */
25
+ function enrichConfig(
26
+ config: ResponseToTxRequestConfig,
27
+ delegateToken: string,
28
+ withdrawal: Withdrawal,
29
+ ): ResponseToTxRequestConfig {
30
+ const enrichedConfig = { ...config };
31
+
32
+ // Extract originatorId from delegate token if not provided
33
+ if (!enrichedConfig.originatorId) {
34
+ try {
35
+ const tokenPayload = decodeJwt<DelegateToken>(delegateToken);
36
+ if (tokenPayload?.sub) {
37
+ enrichedConfig.originatorId = tokenPayload.sub;
38
+ }
39
+ } catch {
40
+ // If decoding fails, originatorId remains undefined
41
+ }
42
+ }
43
+
44
+ if (!enrichedConfig.beneficiaryId) {
45
+ if (withdrawal.counterparty?.type === PersonType.SELF) {
46
+ enrichedConfig.beneficiaryId = enrichedConfig.originatorId;
47
+ } else if (withdrawal.destination) {
48
+ // Generate beneficiaryId from destination if not provided.
49
+ // TODO: Replace with proper DID key identifier creation
50
+ enrichedConfig.beneficiaryId = `did:key:${withdrawal.destination}`;
51
+ }
52
+ }
53
+
54
+ return enrichedConfig;
55
+ }
56
+
57
+ /**
58
+ * Transforms a Notabene component response to a Version 1 API request body
59
+ *
60
+ * @param response - The response from the Notabene TX Create component
61
+ * @returns The transformed request body ready for the Version 1 API
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * import { componentResponseToV1TxCreateRequest } from '$lib/notabene-tx-transformer';
66
+ *
67
+ * withdrawal.on('complete', async (result) => {
68
+ * const requestBody = componentResponseToV1TxCreateRequest(result.response);
69
+ *
70
+ * await fetch(endpointUrl, {
71
+ * method: 'POST',
72
+ * body: JSON.stringify(requestBody)
73
+ * });
74
+ * });
75
+ * ```
76
+ */
77
+ export function componentResponseToV1TxCreateRequest(
78
+ response: TransactionResponse<Withdrawal>,
79
+ ): TransactionCreateRequest {
80
+ if (!response.txCreate || !response.ivms101) {
81
+ throw new Error(
82
+ 'Invalid response: missing required txCreate or ivms101 data',
83
+ );
84
+ }
85
+
86
+ const { value, ivms101, proof, txCreate } = response;
87
+
88
+ // Build withdrawal object from response data for V1 API
89
+ const withdrawal: Withdrawal = {
90
+ destination: value?.destination || '',
91
+ counterparty: value?.counterparty || {},
92
+ agent: value?.agent,
93
+ account: value?.account,
94
+ proof,
95
+ asset:
96
+ value?.asset ||
97
+ (typeof txCreate.transactionAsset === 'string'
98
+ ? txCreate.transactionAsset
99
+ : txCreate.transactionAsset?.caip19) ||
100
+ '',
101
+ amountDecimal:
102
+ value?.amountDecimal ||
103
+ (txCreate.transactionAmount ? parseFloat(txCreate.transactionAmount) : 0),
104
+ customer: value?.customer,
105
+ } as Withdrawal;
106
+
107
+ return mapToV1CreateRequest(withdrawal, txCreate, ivms101 as any);
108
+ }
109
+
110
+ /**
111
+ * Transforms a Notabene component response into txCreate, IVMS101, and confirmRelationship request bodies
112
+ *
113
+ * This is a convenience function that generates the transaction creation request,
114
+ * the IVMS101 data, and optionally the relationship confirmation proof in a single call,
115
+ * which is useful for V2 workflows where you need to create a transaction first,
116
+ * then append IVMS101 data to it, and finally confirm the relationship.
117
+ *
118
+ * @param response - The response from the Notabene Embedded Component
119
+ * @param delegateToken - The JWT delegate token for extracting the originator ID
120
+ * @param config - Optional configuration for IDs and reference
121
+ * @param config.originatorId - Optional originator ID (auto-extracted from delegateToken if not provided)
122
+ * @param config.beneficiaryId - Optional beneficiary ID (auto-generated if not provided)
123
+ * @param config.referenceId - Optional reference ID (auto-generated if not provided)
124
+ * @param config.originator - Optional originator data in V2 format
125
+ * @returns Object with `createTx`, `ivms101`, and optional `confirmRelationship` properties containing the respective request bodies
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * import { componentResponseToTxRequests } from '$lib/notabene-tx-transformer';
130
+ *
131
+ * withdrawal.on('complete', async (result) => {
132
+ * const { createTx, ivms101, confirmRelationship } = componentResponseToTxRequests(
133
+ * result.response,
134
+ * session.user.token,
135
+ * { originator: originatorData }
136
+ * );
137
+ *
138
+ * // First, create the transaction
139
+ * const txResponse = await fetch('/entity/${vaspDid}/tx', {
140
+ * method: 'POST',
141
+ * body: JSON.stringify(createTx)
142
+ * });
143
+ *
144
+ * // Then, append IVMS101 data
145
+ * const txId = txResponse.transfer['@id'];
146
+ * await fetch(`/entity/${vaspDid}/tx/${txId}/append`, {
147
+ * method: 'POST',
148
+ * body: JSON.stringify(ivms101)
149
+ * });
150
+ *
151
+ * // Finally, confirm relationship
152
+ * if (confirmRelationship) {
153
+ * await fetch(`/entity/${vaspDid}/relationship?to=${to}&from=${from}`, {
154
+ * method: 'PATCH',
155
+ * body: JSON.stringify({ proof: confirmRelationship.proof })
156
+ * });
157
+ * }
158
+ * });
159
+ * ```
160
+ */
161
+ export function componentResponseToTxRequests(
162
+ response: TransactionResponse<Withdrawal>,
163
+ delegateToken: string,
164
+ config: ResponseToTxRequestConfig = {},
165
+ ): {
166
+ createTx: TransactionCreateRequestV2;
167
+ ivms101: TransactionIVMS101Request;
168
+ confirmRelationship?: {
169
+ proof: OwnershipProof;
170
+ };
171
+ } {
172
+ if (!response.txCreate || !response.ivms101) {
173
+ throw new Error(
174
+ 'Invalid response: missing required txCreate or ivms101 data',
175
+ );
176
+ }
177
+
178
+ const { value, txCreate, ivms101, proof } = response;
179
+ const enrichedConfig = enrichConfig(config, delegateToken, value);
180
+
181
+ return {
182
+ createTx: mapToTransactCreateRequest(value, txCreate, enrichedConfig),
183
+ ivms101: mapToAppendPiiRequest(value, ivms101, enrichedConfig),
184
+ ...(proof && { confirmRelationship: { proof } }),
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Transforms a Notabene component response to a Version 2 transaction create API request body
190
+ *
191
+ * @param response - The response from the Notabene Embedded Component
192
+ * @param delegateToken - The JWT delegate token for extracting the originator ID
193
+ * @param config - Configuration object with optional IDs
194
+ * @param config.originatorId - Optional originator ID (auto-extracted from delegateToken if not provided)
195
+ * @param config.beneficiaryId - Optional beneficiary ID (auto-generated if not provided)
196
+ * @param config.referenceId - Optional reference ID for the transaction
197
+ * @returns The transformed request body ready for the Version 2 transaction create API
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * import { componentResponseToTxCreateRequest } from '$lib/notabene-tx-transformer';
202
+ *
203
+ * withdrawal.on('complete', async (result) => {
204
+ * const requestBody = componentResponseToTxCreateRequest(
205
+ * result.response,
206
+ * {
207
+ * originatorId: 'mailto:user@example.com',
208
+ * beneficiaryId: 'urn:beneficiary:recipient',
209
+ * referenceId: 'tx-12345'
210
+ * }
211
+ * );
212
+ *
213
+ * await fetch('/entity/${vaspDid}/tx', {
214
+ * method: 'POST',
215
+ * body: JSON.stringify(requestBody)
216
+ * });
217
+ * });
218
+ * ```
219
+ */
220
+ export function componentResponseToTxCreateRequest(
221
+ response: TransactionResponse<Withdrawal>,
222
+ delegateToken: string,
223
+ config: Omit<ResponseToTxRequestConfig, 'originator'> = {},
224
+ ) {
225
+ if (!response.txCreate || !response.ivms101) {
226
+ throw new Error(
227
+ 'Invalid response: missing required txCreate or ivms101 data',
228
+ );
229
+ }
230
+
231
+ const { value, txCreate } = response;
232
+ const enrichedConfig = enrichConfig(config, delegateToken, value);
233
+
234
+ return mapToTransactCreateRequest(value, txCreate, enrichedConfig);
235
+ }
236
+
237
+ /**
238
+ * Transforms a Notabene component response to IVMS101 format
239
+ *
240
+ * @param response - The response from the Notabene Embedded Component
241
+ * @param delegateToken - The JWT delegate token for extracting the originator ID
242
+ * @param config - Configuration object with optional IDs
243
+ * @param config.originatorId - Optional originator ID (auto-extracted from delegateToken if not provided)
244
+ * @param config.beneficiaryId - Optional beneficiary ID (auto-generated if not provided)
245
+ * @param config.referenceId - Optional reference ID for the transaction
246
+ * @param config.originator - Optional originator data in V2 format
247
+ * @returns The transformed request body in IVMS101 format
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * import { componentResponseToIVMS101 } from '$lib/notabene-tx-transformer';
252
+ *
253
+ * const requestBody = componentResponseToIVMS101(
254
+ * result.response,
255
+ * session.user.token,
256
+ * { originator: myOriginatorData }
257
+ * );
258
+ *
259
+ * await fetch('/entity/${vaspDid}/tx/${txId}/append', {
260
+ * method: 'POST',
261
+ * body: JSON.stringify(requestBody)
262
+ * });
263
+ * ```
264
+ */
265
+ export function componentResponseToIVMS101(
266
+ response: TransactionResponse<Withdrawal>,
267
+ delegateToken: string,
268
+ config: ResponseToTxRequestConfig = {},
269
+ ) {
270
+ if (!response.ivms101) {
271
+ throw new Error('Invalid response: missing required ivms101 data');
272
+ }
273
+
274
+ const { value, ivms101 } = response;
275
+ const enrichedConfig = enrichConfig(config, delegateToken, value);
276
+
277
+ return mapToAppendPiiRequest(value, ivms101, enrichedConfig);
278
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Types that are not available in @notabene/javascript-sdk
3
+ * These are specific to the API transformation logic
4
+ */
5
+
6
+ import type { Agent, DID } from '@taprsvp/types';
7
+ import type { BeneficiaryV2, OriginatorV2 } from '../ivms';
8
+
9
+ export interface DelegateToken {
10
+ sub?: DID;
11
+ iss?: DID;
12
+ scope?: 'delegate';
13
+ exp?: number;
14
+ iat?: number;
15
+ }
16
+
17
+ export interface ResponseToTxRequestConfig {
18
+ originatorId?: DID;
19
+ beneficiaryId?: DID;
20
+ referenceId?: string;
21
+ originator?: OriginatorV2;
22
+ }
23
+
24
+ export interface TransactionCreateRequest {
25
+ transactionAsset: any;
26
+ transactionAmount: string;
27
+ beneficiaryDid?: string;
28
+ originatorVASPdid: string;
29
+ beneficiaryVASPdid?: string;
30
+ beneficiaryVASPname?: string;
31
+ beneficiaryVASPwebsite?: string;
32
+ transactionBlockchainInfo: {
33
+ origin?: string;
34
+ destination?: string;
35
+ };
36
+ beneficiaryProof?: any;
37
+ beneficiary?: any;
38
+ originator?: any;
39
+ originatorEqualsBeneficiary?: boolean;
40
+ }
41
+
42
+ export interface TransactionCreateRequestV2 {
43
+ originator: {
44
+ '@id': string;
45
+ };
46
+ beneficiary: {
47
+ '@id': string;
48
+ };
49
+ asset: string;
50
+ amount: string;
51
+ agents: Agent[];
52
+ ref: string;
53
+ }
54
+
55
+ export interface TransactionIVMS101Request {
56
+ ivms101: {
57
+ originator?: OriginatorV2;
58
+ beneficiary?: BeneficiaryV2;
59
+ };
60
+ }
@@ -0,0 +1,61 @@
1
+ import type {
2
+ NaturalPersonName,
3
+ Person,
4
+ } from '@notabene/javascript-sdk/src/ivms/types';
5
+ import type { DID } from '@taprsvp/types';
6
+ import type { NaturalPersonNameV2, PersonV2 } from '../ivms';
7
+
8
+ /**
9
+ * Generates a backup DID identifier for a party
10
+ */
11
+ export function getPartyId(
12
+ identifier: 'originator' | 'beneficiary',
13
+ party?: { email?: string; name?: string },
14
+ ): DID {
15
+ if (party?.email) {
16
+ return `did:email:${party.email}`;
17
+ }
18
+ if (party?.name) {
19
+ return `did:name:${encodeURIComponent(party.name.replace(/\s+/g, '-'))}`;
20
+ }
21
+ throw new Error(
22
+ `Unable to generate ${identifier} ID: missing required ${identifier} information`,
23
+ );
24
+ }
25
+
26
+ /**
27
+ * Converts NaturalPersonName from V1 format to V2 format
28
+ * Changes the nameIdentifierType field name to naturalPersonNameIdentifierType
29
+ */
30
+ export function convertNaturalPersonNameToV2(
31
+ name: NaturalPersonName,
32
+ ): NaturalPersonNameV2 {
33
+ const { nameIdentifier, ...rest } = name;
34
+ return {
35
+ ...rest,
36
+ nameIdentifier: nameIdentifier?.map(({ nameIdentifierType, ...rest }) => ({
37
+ ...rest,
38
+ naturalPersonNameIdentifierType: nameIdentifierType,
39
+ })),
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Converts a Person from V1 format to V2 format
45
+ * Applies deep conversion to natural person names and adds account number
46
+ */
47
+ export function convertPersonToV2(
48
+ person: Person,
49
+ accountNumber: string,
50
+ ): PersonV2 {
51
+ return {
52
+ naturalPerson: person.naturalPerson
53
+ ? {
54
+ ...person.naturalPerson,
55
+ name: convertNaturalPersonNameToV2(person.naturalPerson.name),
56
+ }
57
+ : undefined,
58
+ legalPerson: person.legalPerson,
59
+ accountNumber: [accountNumber],
60
+ };
61
+ }
package/src/types.ts CHANGED
@@ -491,7 +491,7 @@ export type LegalPersonFields = Partial<{
491
491
  * @public
492
492
  */
493
493
  type OriginatorFields = {
494
- source?: Source;
494
+ source?: Source | Source[];
495
495
  };
496
496
 
497
497
  /**
@@ -877,7 +877,6 @@ export interface TransactionOptions {
877
877
  microTransfer?: {
878
878
  destination: BlockchainAddress;
879
879
  amountSubunits: string;
880
- timeout?: number; // Time to verify in seconds
881
880
  };
882
881
  fallbacks?: ProofTypes[];
883
882
  deminimis?: ThresholdOptions;
@@ -955,6 +954,18 @@ export enum ErrorIdentifierCode {
955
954
  TOKEN_INVALID = 'TOKEN_INVALID',
956
955
  }
957
956
 
957
+ /**
958
+ * Identifier codes for warning messages
959
+ * @public
960
+ */
961
+ export enum WarningIdentifierCode {
962
+ WALLET_ADDRESS_NOT_CONNECTED = 'WALLET_ADDRESS_NOT_CONNECTED',
963
+ WALLET_LOCKED = 'WALLET_LOCKED',
964
+ WALLET_UNREACHABLE = 'WALLET_UNREACHABLE',
965
+ JURISDICTIONAL_REQUIREMENTS_UNAVAILABLE = 'JURISDICTIONAL_REQUIREMENTS_UNAVAILABLE',
966
+ IDV_UNAVAILABLE = 'IDV_UNAVAILABLE',
967
+ }
968
+
958
969
  /**
959
970
  * Represents an error component message
960
971
  * @param message - Error message
@@ -994,12 +1005,14 @@ export type InvalidValue<T> = {
994
1005
  * Represents a warning component message
995
1006
  * @param message - Warning message
996
1007
  * @param description - Description of the warning message
1008
+ * @param identifier - Identifier code of the warning message
997
1009
  * @public
998
1010
  */
999
1011
  export type Warning = {
1000
1012
  type: CMType.WARNING;
1001
1013
  message: string;
1002
1014
  description?: string;
1015
+ identifier?: WarningIdentifierCode;
1003
1016
  };
1004
1017
 
1005
1018
  /**
@@ -208,7 +208,6 @@ export const arbitraryTransactionOptions =
208
208
  fc.record({
209
209
  destination: arbitraryBlockchainAddress(),
210
210
  amountSubunits: fc.string(),
211
- timeout: fc.option(fc.nat()),
212
211
  }),
213
212
  ),
214
213
  fallbacks: fc.option(