@notabene/javascript-sdk 2.19.1 → 2.20.0-next.1
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/cjs/notabene.cjs +3 -3
- package/dist/cjs/notabene.d.ts +37 -1
- package/dist/cjs/package.json +1 -1
- package/dist/esm/notabene.d.ts +37 -1
- package/dist/esm/notabene.js +163 -156
- package/dist/esm/package.json +1 -1
- package/dist/notabene.d.ts +37 -1
- package/dist/notabene.js +163 -156
- package/package.json +1 -1
- package/src/notabene.ts +1 -0
- package/src/types.ts +17 -0
- package/src/utils/__tests__/connections.test.ts +29 -0
- package/src/utils/connections.ts +25 -1
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"author": "Notabene <developers@notabene.id>",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"packageManager": "yarn@4.5.1",
|
|
13
|
-
"version": "2.
|
|
13
|
+
"version": "2.20.0-next.1",
|
|
14
14
|
"source": "src/notabene.ts",
|
|
15
15
|
"main": "dist/cjs/notabene.cjs",
|
|
16
16
|
"module": "dist/esm/notabene.js",
|
package/src/notabene.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -584,6 +584,12 @@ export interface Transaction extends ComponentRequest {
|
|
|
584
584
|
export interface RefreshSource {
|
|
585
585
|
url: URI;
|
|
586
586
|
key: string;
|
|
587
|
+
/**
|
|
588
|
+
* The connection id (also embedded in `url`). Exposed directly so consumers can
|
|
589
|
+
* correlate a Counterparty Assist webhook event (whose `payload.id` is this id)
|
|
590
|
+
* without parsing it out of the url.
|
|
591
|
+
*/
|
|
592
|
+
id: string;
|
|
587
593
|
}
|
|
588
594
|
|
|
589
595
|
export interface Refreshable {
|
|
@@ -881,6 +887,17 @@ export type CounterpartyAssistConfig =
|
|
|
881
887
|
counterpartyTypes: PersonType[];
|
|
882
888
|
/** @remarks Requires a transactionId to be passed in. */
|
|
883
889
|
identityVerification?: IdentityVerificationConfig;
|
|
890
|
+
/**
|
|
891
|
+
* Optional URL that receives an `EC.counterpartyAssist*` webhook when the
|
|
892
|
+
* counterparty completes or closes their part. MVP-only per-link override;
|
|
893
|
+
* superseded by Portal-managed subscriptions post-migration.
|
|
894
|
+
*
|
|
895
|
+
* Transmitted and stored as plaintext, so do not embed secrets (e.g. `?token=`)
|
|
896
|
+
* in this URL. Payloads carry the encrypted `sealed` blob (a successful decrypt
|
|
897
|
+
* is itself an authenticity check); signed delivery is provided by the future
|
|
898
|
+
* Svix-backed pipeline.
|
|
899
|
+
*/
|
|
900
|
+
webhookUrl?: string;
|
|
884
901
|
};
|
|
885
902
|
|
|
886
903
|
/**
|
|
@@ -4,6 +4,7 @@ import type { DID, RefreshSource } from '../../types';
|
|
|
4
4
|
import {
|
|
5
5
|
ConnectionManager,
|
|
6
6
|
type ConnectionStatus,
|
|
7
|
+
decryptCounterpartyAssistPayload,
|
|
7
8
|
getRefreshResult,
|
|
8
9
|
type TransactionType,
|
|
9
10
|
} from '../connections';
|
|
@@ -53,6 +54,7 @@ const arbConnectionMetadata = fc.record({
|
|
|
53
54
|
participants: fc.array(fc.string(), { minLength: 1 }), // At least one participant
|
|
54
55
|
nodeUrl: fc.webUrl(),
|
|
55
56
|
transactionType: fc.constantFrom<TransactionType>('withdraw', 'deposit'),
|
|
57
|
+
webhookUrl: fc.option(fc.webUrl(), { nil: undefined }),
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
describe('ConnectionManager', () => {
|
|
@@ -317,6 +319,7 @@ describe('ConnectionManager', () => {
|
|
|
317
319
|
const refreshSource: RefreshSource = {
|
|
318
320
|
url: 'https://test-endpoint.com',
|
|
319
321
|
key: sealed.key,
|
|
322
|
+
id: 'test-id',
|
|
320
323
|
};
|
|
321
324
|
|
|
322
325
|
// Common metadata for all tests
|
|
@@ -385,6 +388,7 @@ describe('ConnectionManager', () => {
|
|
|
385
388
|
const completedRefreshSource: RefreshSource = {
|
|
386
389
|
url: 'https://test-endpoint.com',
|
|
387
390
|
key: completedSealed.key,
|
|
391
|
+
id: 'test-id',
|
|
388
392
|
};
|
|
389
393
|
|
|
390
394
|
const mockResponse = createMockResponse(
|
|
@@ -435,3 +439,28 @@ describe('ConnectionManager', () => {
|
|
|
435
439
|
});
|
|
436
440
|
});
|
|
437
441
|
});
|
|
442
|
+
|
|
443
|
+
describe('decryptCounterpartyAssistPayload', () => {
|
|
444
|
+
it('round-trips a sealed payload back to the original object', async () => {
|
|
445
|
+
const original = { result: { ref: 'abc' }, tx: { amount: '10' } };
|
|
446
|
+
const sealed = await seal(original);
|
|
447
|
+
|
|
448
|
+
const decrypted = await decryptCounterpartyAssistPayload<typeof original>({
|
|
449
|
+
sealed: sealed.ciphertext,
|
|
450
|
+
key: sealed.key,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
expect(decrypted).toEqual(original);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('throws when the key is wrong', async () => {
|
|
457
|
+
const sealed = await seal({ a: 1 });
|
|
458
|
+
const other = await seal({ b: 2 });
|
|
459
|
+
await expect(
|
|
460
|
+
decryptCounterpartyAssistPayload({
|
|
461
|
+
sealed: sealed.ciphertext,
|
|
462
|
+
key: other.key,
|
|
463
|
+
}),
|
|
464
|
+
).rejects.toThrow();
|
|
465
|
+
});
|
|
466
|
+
});
|
package/src/utils/connections.ts
CHANGED
|
@@ -24,6 +24,14 @@ export interface ConnectionMetadata {
|
|
|
24
24
|
readonly participants: readonly string[];
|
|
25
25
|
readonly transactionType: TransactionType;
|
|
26
26
|
readonly locale?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Delivery target for Counterparty Assist completion/closure webhooks.
|
|
29
|
+
* Sent and stored as plaintext so the connection can route the event, so do not
|
|
30
|
+
* embed secrets (e.g. `?token=`) in this URL. Webhook payloads carry the encrypted
|
|
31
|
+
* `sealed` blob, whose successful decryption is itself an authenticity check;
|
|
32
|
+
* signed delivery is provided by the future Svix-backed pipeline.
|
|
33
|
+
*/
|
|
34
|
+
readonly webhookUrl?: string;
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
export interface ConnectionResponse<T extends ComponentRequest> {
|
|
@@ -61,7 +69,7 @@ export type ConnectionResult<T extends ComponentRequest> =
|
|
|
61
69
|
* @returns Promise resolving to connection result with decrypted data
|
|
62
70
|
*/
|
|
63
71
|
export async function getRefreshResult<T extends ComponentRequest>(
|
|
64
|
-
refreshSource: RefreshSource,
|
|
72
|
+
refreshSource: Pick<RefreshSource, 'url' | 'key'>,
|
|
65
73
|
): Promise<ConnectionResult<T>> {
|
|
66
74
|
const response = await fetch(refreshSource.url, {
|
|
67
75
|
method: 'GET',
|
|
@@ -109,6 +117,22 @@ export async function getRefreshResult<T extends ComponentRequest>(
|
|
|
109
117
|
};
|
|
110
118
|
}
|
|
111
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Decrypt a Counterparty Assist webhook payload with the key the VASP captured
|
|
122
|
+
* at connection creation. Single call, no network fetch (the webhook already
|
|
123
|
+
* carried the ciphertext).
|
|
124
|
+
* @public
|
|
125
|
+
*/
|
|
126
|
+
export async function decryptCounterpartyAssistPayload<T>({
|
|
127
|
+
sealed,
|
|
128
|
+
key,
|
|
129
|
+
}: {
|
|
130
|
+
sealed: string;
|
|
131
|
+
key: string;
|
|
132
|
+
}): Promise<T> {
|
|
133
|
+
return unseal<T>({ ciphertext: sealed, key });
|
|
134
|
+
}
|
|
135
|
+
|
|
112
136
|
/**
|
|
113
137
|
* Manages encrypted connections using Cloudflare Durable Objects
|
|
114
138
|
*/
|