@notabene/javascript-sdk 1.35.2 → 2.0.0-RC
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/LICENSE.md +21 -0
- package/README.md +498 -268
- package/dist/notabene.cjs +1 -0
- package/dist/notabene.d.ts +1565 -0
- package/dist/notabene.js +343 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/package.json +50 -63
- package/src/__tests__/notabene.test.ts +270 -0
- package/src/components/EmbeddedComponent.ts +237 -0
- package/src/components/__tests__/EmbeddedComponent.test.ts +530 -0
- package/src/ivms/types.ts +232 -152
- package/src/locales.ts +47 -0
- package/src/notabene.ts +292 -293
- package/src/types.ts +1068 -78
- package/src/utils/MessageEventManager.ts +115 -0
- package/src/utils/__tests__/MessageEventManager.test.ts +119 -0
- package/src/utils/__tests__/urls.test.ts +112 -0
- package/src/utils/arbitraries.ts +244 -0
- package/src/utils/caip.ts +16 -0
- package/src/utils/urls.ts +40 -0
- package/.editorconfig +0 -10
- package/.envrc.template +0 -1
- package/.eslintignore +0 -3
- package/.eslintrc.js +0 -13
- package/.gitlab-ci.yml +0 -74
- package/.husky/commit-msg +0 -4
- package/.husky/pre-commit +0 -4
- package/.husky/pre-push +0 -4
- package/.nvmrc +0 -1
- package/.prettierrc +0 -4
- package/.releaserc.json +0 -16
- package/.tool-versions +0 -3
- package/.vscode/extensions.json +0 -7
- package/.vscode/settings.json +0 -11
- package/.yarn/releases/yarn-3.2.2.cjs +0 -783
- package/.yarnrc.yml +0 -5
- package/CODEOWNERS +0 -1
- package/dist/lib/index.js +0 -379
- package/jest.config.js +0 -5
- package/public/index.html +0 -93
- package/src/lib/index.ts +0 -2
- package/src/module/index.ts +0 -10
- package/src/zoidComponentProps.ts +0 -81
- package/tsconfig.json +0 -18
- /package/dist/{es/index.js → js/notabene.js} +0 -0
- /package/{public/js/.gitkeep → src/arbitraries.ts} +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ComponentMessage, HostMessage } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Callback function for handling component messages.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam T - The type of data contained in the component message
|
|
7
|
+
* @param message - The message object containing the component data and type
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type MessageCallback<T> = (message: ComponentMessage<T>) => void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Manages message event communication through MessagePorts.
|
|
15
|
+
*
|
|
16
|
+
* This class handles bidirectional communication between components using MessagePorts,
|
|
17
|
+
* allowing subscription to specific message types and dispatching messages to registered callbacks.
|
|
18
|
+
*
|
|
19
|
+
* @typeParam T - The type of data contained in messages received from components
|
|
20
|
+
* @typeParam O - The type of data contained in messages sent from the host
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export class MessageEventManager<T, O> {
|
|
24
|
+
private listeners: Map<string, Set<MessageCallback<T>>> = new Map();
|
|
25
|
+
private port?: MessagePort;
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
this.handleMessage = this.handleMessage.bind(this);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Sets up the message port for communication.
|
|
33
|
+
*
|
|
34
|
+
* Initializes the MessagePort for receiving messages by setting up the message handler
|
|
35
|
+
* and starting the port.
|
|
36
|
+
*
|
|
37
|
+
* @param port - The MessagePort instance to use for communication
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
setPort(port: MessagePort): void {
|
|
41
|
+
this.port = port;
|
|
42
|
+
this.port.onmessage = this.handleMessage;
|
|
43
|
+
this.port.start();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Registers a callback for a specific message type.
|
|
48
|
+
*
|
|
49
|
+
* When messages of the specified type are received, the callback will be executed
|
|
50
|
+
* with the message data.
|
|
51
|
+
*
|
|
52
|
+
* @param messageType - The type of message to listen for
|
|
53
|
+
* @param callback - The callback function to execute when matching messages are received
|
|
54
|
+
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
on(messageType: string, callback: MessageCallback<T>): () => void {
|
|
58
|
+
if (!this.listeners.has(messageType)) {
|
|
59
|
+
this.listeners.set(messageType, new Set());
|
|
60
|
+
}
|
|
61
|
+
this.listeners.get(messageType)!.add(callback);
|
|
62
|
+
return () => this.off(messageType, callback);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Removes a callback for a specific message type.
|
|
67
|
+
*
|
|
68
|
+
* If the callback is the last one registered for the message type,
|
|
69
|
+
* the message type entry will be removed entirely.
|
|
70
|
+
*
|
|
71
|
+
* @param messageType - The type of message to remove listener from
|
|
72
|
+
* @param callback - The callback function to remove
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
off(messageType: string, callback: MessageCallback<T>): void {
|
|
76
|
+
const callbacks = this.listeners.get(messageType);
|
|
77
|
+
if (callbacks) {
|
|
78
|
+
callbacks.delete(callback);
|
|
79
|
+
if (callbacks.size === 0) {
|
|
80
|
+
this.listeners.delete(messageType);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Internal message handler for processing received messages.
|
|
87
|
+
*
|
|
88
|
+
* Validates incoming messages and dispatches them to registered callbacks
|
|
89
|
+
* based on the message type.
|
|
90
|
+
*
|
|
91
|
+
* @param event - The message event containing the component message
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
private handleMessage(event: MessageEvent<ComponentMessage<T>>): void {
|
|
95
|
+
// console.log('received message', event.data);
|
|
96
|
+
const message = event.data;
|
|
97
|
+
if (typeof message === 'object' && message !== null && 'type' in message) {
|
|
98
|
+
const messageType = message.type as string;
|
|
99
|
+
const callbacks = this.listeners.get(messageType);
|
|
100
|
+
if (callbacks) {
|
|
101
|
+
callbacks.forEach((callback) => callback(message));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Sends a message through the message port
|
|
108
|
+
* @param message The host message to send
|
|
109
|
+
*/
|
|
110
|
+
send(message: HostMessage<T, O>): void {
|
|
111
|
+
if (this.port) {
|
|
112
|
+
this.port.postMessage(message);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
HMType,
|
|
4
|
+
type TransactionOptions,
|
|
5
|
+
type UpdateValue,
|
|
6
|
+
type Withdrawal,
|
|
7
|
+
} from '../../types';
|
|
8
|
+
import { MessageEventManager } from '../MessageEventManager';
|
|
9
|
+
|
|
10
|
+
describe('MessageEventManager', () => {
|
|
11
|
+
let messageEventManager: MessageEventManager<Withdrawal, TransactionOptions>;
|
|
12
|
+
let mockPort: MessagePort;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
mockPort = {
|
|
16
|
+
onmessage: null,
|
|
17
|
+
postMessage: vi.fn(),
|
|
18
|
+
start: vi.fn(),
|
|
19
|
+
} as unknown as MessagePort;
|
|
20
|
+
|
|
21
|
+
messageEventManager = new MessageEventManager<
|
|
22
|
+
Withdrawal,
|
|
23
|
+
TransactionOptions
|
|
24
|
+
>();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('setPort', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
messageEventManager.setPort(mockPort);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should start the port when initialized', () => {
|
|
33
|
+
expect(mockPort.start).toHaveBeenCalled();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should subscribe and receive messages of a specific type', () => {
|
|
37
|
+
const readyHandler = vi.fn();
|
|
38
|
+
messageEventManager.on('ready', readyHandler);
|
|
39
|
+
|
|
40
|
+
const readyMessage = { type: 'ready' };
|
|
41
|
+
mockPort.onmessage!({ data: readyMessage } as MessageEvent);
|
|
42
|
+
|
|
43
|
+
expect(readyHandler).toHaveBeenCalledWith(readyMessage);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should not call handlers for different message types', () => {
|
|
47
|
+
const updateHandler = vi.fn();
|
|
48
|
+
const errorHandler = vi.fn();
|
|
49
|
+
messageEventManager.on('update', updateHandler);
|
|
50
|
+
messageEventManager.on('error', errorHandler);
|
|
51
|
+
|
|
52
|
+
const errorMessage = { type: 'error', message: 'Something went wrong' };
|
|
53
|
+
mockPort.onmessage!({ data: errorMessage } as MessageEvent);
|
|
54
|
+
|
|
55
|
+
expect(updateHandler).not.toHaveBeenCalled();
|
|
56
|
+
expect(errorHandler).toHaveBeenCalledWith(errorMessage);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should unsubscribe handlers', () => {
|
|
60
|
+
const updateHandler = vi.fn();
|
|
61
|
+
messageEventManager.on('update', updateHandler);
|
|
62
|
+
messageEventManager.off('update', updateHandler);
|
|
63
|
+
|
|
64
|
+
const updateMessage = { type: 'update', data: { foo: 'bar' } };
|
|
65
|
+
mockPort.onmessage!({ data: updateMessage } as MessageEvent);
|
|
66
|
+
|
|
67
|
+
expect(updateHandler).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should allow multiple subscribers for the same message type', () => {
|
|
71
|
+
const updateHandler1 = vi.fn();
|
|
72
|
+
const updateHandler2 = vi.fn();
|
|
73
|
+
messageEventManager.on('update', updateHandler1);
|
|
74
|
+
messageEventManager.on('update', updateHandler2);
|
|
75
|
+
|
|
76
|
+
const updateMessage = { type: 'update', data: { foo: 'bar' } };
|
|
77
|
+
mockPort.onmessage!({ data: updateMessage } as MessageEvent);
|
|
78
|
+
|
|
79
|
+
expect(updateHandler1).toHaveBeenCalledWith(updateMessage);
|
|
80
|
+
expect(updateHandler2).toHaveBeenCalledWith(updateMessage);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should send messages through the port', () => {
|
|
84
|
+
const message: UpdateValue<Withdrawal, TransactionOptions> = {
|
|
85
|
+
type: HMType.UPDATE,
|
|
86
|
+
value: { requestId: 'id' },
|
|
87
|
+
};
|
|
88
|
+
messageEventManager.send(message);
|
|
89
|
+
|
|
90
|
+
expect(mockPort.postMessage).toHaveBeenCalledWith(message);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should ignore messages without a type', () => {
|
|
94
|
+
const handler = vi.fn();
|
|
95
|
+
messageEventManager.on('update', handler);
|
|
96
|
+
|
|
97
|
+
const invalidMessage = { data: 'no type field' };
|
|
98
|
+
mockPort.onmessage!({ data: invalidMessage } as MessageEvent);
|
|
99
|
+
|
|
100
|
+
expect(handler).not.toHaveBeenCalled();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('on returns unsubscribe function', () => {
|
|
104
|
+
const messageType = 'customEvent';
|
|
105
|
+
const callback = vi.fn();
|
|
106
|
+
const unsubscribe = messageEventManager.on(messageType, callback);
|
|
107
|
+
|
|
108
|
+
// Verify it's a function
|
|
109
|
+
expect(typeof unsubscribe).toBe('function');
|
|
110
|
+
|
|
111
|
+
// Call it and verify the handler is removed
|
|
112
|
+
unsubscribe();
|
|
113
|
+
|
|
114
|
+
// Simulate a message - callback should not be called
|
|
115
|
+
mockPort.onmessage!({ data: { type: messageType } } as MessageEvent);
|
|
116
|
+
expect(callback).not.toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { fc, test } from '@fast-check/vitest';
|
|
2
|
+
import { describe, expect } from 'vitest';
|
|
3
|
+
import { decodeFragmentToObject, encodeObjectToFragment } from '../urls';
|
|
4
|
+
|
|
5
|
+
describe('encodeObjectToFragment', () => {
|
|
6
|
+
test.prop([
|
|
7
|
+
fc.record({
|
|
8
|
+
key1: fc.string(),
|
|
9
|
+
key2: fc.string(),
|
|
10
|
+
key3: fc.string(),
|
|
11
|
+
}),
|
|
12
|
+
])('Should correctly encode simple string values', (obj) => {
|
|
13
|
+
const encoded = encodeObjectToFragment(obj);
|
|
14
|
+
const parts = encoded.split('&');
|
|
15
|
+
|
|
16
|
+
return Object.entries(obj).every(([key, value]) => {
|
|
17
|
+
const part = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
18
|
+
return parts.includes(part);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test.prop([
|
|
23
|
+
fc.record({
|
|
24
|
+
str: fc.string(),
|
|
25
|
+
num: fc.float(),
|
|
26
|
+
bool: fc.boolean(),
|
|
27
|
+
}),
|
|
28
|
+
])('Should handle different primitive types', (obj) => {
|
|
29
|
+
const encoded = encodeObjectToFragment(obj);
|
|
30
|
+
const parts = encoded.split('&');
|
|
31
|
+
|
|
32
|
+
return Object.entries(obj).every(([key, value]) => {
|
|
33
|
+
const part = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
34
|
+
return parts.includes(part);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test.prop([
|
|
39
|
+
fc.record({
|
|
40
|
+
obj: fc.object(),
|
|
41
|
+
arr: fc.array(fc.string()),
|
|
42
|
+
nested: fc.record({
|
|
43
|
+
a: fc.string(),
|
|
44
|
+
b: fc.integer(),
|
|
45
|
+
}),
|
|
46
|
+
}),
|
|
47
|
+
])('Should properly encode nested objects', (obj) => {
|
|
48
|
+
const encoded = encodeObjectToFragment(obj);
|
|
49
|
+
const parts = encoded.split('&');
|
|
50
|
+
|
|
51
|
+
return Object.entries(obj).every(([key, value]) => {
|
|
52
|
+
const part = `${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(value))}`;
|
|
53
|
+
return parts.includes(part);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('Should skip undefined or null values', () => {
|
|
58
|
+
const obj = {
|
|
59
|
+
valid: 'value',
|
|
60
|
+
empty: undefined,
|
|
61
|
+
null: null,
|
|
62
|
+
falsy: 0,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const encoded = encodeObjectToFragment(obj);
|
|
66
|
+
expect(encoded).toBe('valid=value&falsy=0');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('Should handle empty objects', () => {
|
|
70
|
+
const encoded = encodeObjectToFragment({});
|
|
71
|
+
expect(encoded).toBe('');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('decodeFragmentToObject', () => {
|
|
76
|
+
test.prop([
|
|
77
|
+
fc.record({
|
|
78
|
+
key1: fc.string(),
|
|
79
|
+
key2: fc.string(),
|
|
80
|
+
key3: fc.string(),
|
|
81
|
+
}),
|
|
82
|
+
])('Should correctly decode URL fragment to object', (obj) => {
|
|
83
|
+
const fragment =
|
|
84
|
+
'#' +
|
|
85
|
+
Object.entries(obj)
|
|
86
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
87
|
+
.join('&');
|
|
88
|
+
|
|
89
|
+
const decoded = decodeFragmentToObject(fragment);
|
|
90
|
+
return Object.entries(obj).every(([k, v]) => decoded[k] === v);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('Should handle empty fragment', () => {
|
|
94
|
+
const decoded = decodeFragmentToObject('#');
|
|
95
|
+
expect(decoded).toEqual({});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('Should handle fragment without values', () => {
|
|
99
|
+
const decoded = decodeFragmentToObject('#key1=&key2=');
|
|
100
|
+
expect(decoded).toEqual({ key1: '', key2: '' });
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('Should decode special characters', () => {
|
|
104
|
+
const fragment = '#key1=%20space%20&key2=%3Dequals%3D&key3=%26ampersand%26';
|
|
105
|
+
const decoded = decodeFragmentToObject(fragment);
|
|
106
|
+
expect(decoded).toEqual({
|
|
107
|
+
key1: ' space ',
|
|
108
|
+
key2: '=equals=',
|
|
109
|
+
key3: '&ersand&',
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import fc from 'fast-check';
|
|
2
|
+
import type {
|
|
3
|
+
Agent,
|
|
4
|
+
BlockchainAddress,
|
|
5
|
+
CAIP10,
|
|
6
|
+
CAIP19,
|
|
7
|
+
CAIP2,
|
|
8
|
+
CAIP220,
|
|
9
|
+
CallbackOptions,
|
|
10
|
+
ConnectionOptions,
|
|
11
|
+
ConnectionRequest,
|
|
12
|
+
Counterparty,
|
|
13
|
+
CryptoCredential,
|
|
14
|
+
DepositRequest,
|
|
15
|
+
DepositRequestOptions,
|
|
16
|
+
DID,
|
|
17
|
+
FieldTypes,
|
|
18
|
+
ISOCurrency,
|
|
19
|
+
ISODate,
|
|
20
|
+
NaturalPerson,
|
|
21
|
+
ThresholdOptions,
|
|
22
|
+
TransactionAsset,
|
|
23
|
+
TransactionOptions,
|
|
24
|
+
TravelAddress,
|
|
25
|
+
VASPOptions,
|
|
26
|
+
Withdrawal,
|
|
27
|
+
} from '../types';
|
|
28
|
+
import {
|
|
29
|
+
AgentType,
|
|
30
|
+
PersonType,
|
|
31
|
+
ProofTypes,
|
|
32
|
+
ValidationSections,
|
|
33
|
+
} from '../types';
|
|
34
|
+
import {
|
|
35
|
+
CAIP10_MATCHER,
|
|
36
|
+
CAIP19_MATCHER,
|
|
37
|
+
CAIP220_MATCHER,
|
|
38
|
+
CAIP2_MATCHER,
|
|
39
|
+
} from './caip';
|
|
40
|
+
|
|
41
|
+
const LOCALE = new RegExp(/^[a-z]{2}(-[A-Z]{2})?$/);
|
|
42
|
+
const COMPONENT_MATCHER = /^[a-z0-9-_]+$/;
|
|
43
|
+
|
|
44
|
+
// Arbitraries
|
|
45
|
+
export const arbitraryCAIP10 = (): fc.Arbitrary<CAIP10> =>
|
|
46
|
+
fc.stringMatching(CAIP10_MATCHER) as fc.Arbitrary<CAIP10>;
|
|
47
|
+
|
|
48
|
+
export const arbitraryCAIP2 = (): fc.Arbitrary<CAIP2> =>
|
|
49
|
+
fc.stringMatching(CAIP2_MATCHER) as fc.Arbitrary<CAIP2>;
|
|
50
|
+
|
|
51
|
+
export const arbitraryCAIP19 = (): fc.Arbitrary<CAIP19> =>
|
|
52
|
+
fc.stringMatching(CAIP19_MATCHER) as fc.Arbitrary<CAIP19>;
|
|
53
|
+
|
|
54
|
+
export const arbitraryCAIP220 = (): fc.Arbitrary<CAIP220> =>
|
|
55
|
+
fc.stringMatching(CAIP220_MATCHER) as fc.Arbitrary<CAIP220>;
|
|
56
|
+
|
|
57
|
+
export const arbitraryLocale = (): fc.Arbitrary<string> =>
|
|
58
|
+
fc.stringMatching(LOCALE) as fc.Arbitrary<string>;
|
|
59
|
+
|
|
60
|
+
// Arbitraries
|
|
61
|
+
export const arbitraryComponent = (): fc.Arbitrary<string> =>
|
|
62
|
+
fc.stringMatching(COMPONENT_MATCHER);
|
|
63
|
+
|
|
64
|
+
export const abitraryCallbackOptions = (): fc.Arbitrary<CallbackOptions> =>
|
|
65
|
+
fc.record({
|
|
66
|
+
callback: fc.webUrl(),
|
|
67
|
+
redirectUri: fc.webUrl(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const arbitraryDID = (): fc.Arbitrary<DID> =>
|
|
71
|
+
fc.string().map((s) => `did:ethr:${s}` as DID);
|
|
72
|
+
|
|
73
|
+
export const arbitraryBlockchainAddress = (): fc.Arbitrary<BlockchainAddress> =>
|
|
74
|
+
fc.hexaString({ minLength: 40, maxLength: 40 }).map((s) => `0x${s}`);
|
|
75
|
+
|
|
76
|
+
export const arbitraryTransactionAsset = (): fc.Arbitrary<TransactionAsset> =>
|
|
77
|
+
fc.oneof(
|
|
78
|
+
fc.string(), // NotabeneAsset
|
|
79
|
+
arbitraryCAIP19(), // CAIP19
|
|
80
|
+
fc.string().map((s) => `DTI${s.slice(0, 5)}`), // DTI
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
export const arbitraryAgent = (): fc.Arbitrary<Agent> =>
|
|
84
|
+
fc.record({
|
|
85
|
+
did: arbitraryDID(),
|
|
86
|
+
type: fc.constantFrom(...Object.values(AgentType)),
|
|
87
|
+
logo: fc.option(fc.webUrl()),
|
|
88
|
+
url: fc.option(fc.webUrl()),
|
|
89
|
+
name: fc.option(fc.string()),
|
|
90
|
+
verified: fc.boolean(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export const arbitraryCounterparty = (): fc.Arbitrary<Counterparty> =>
|
|
94
|
+
arbitraryNaturalPerson(); // For simplicity we'll just use NaturalPerson
|
|
95
|
+
|
|
96
|
+
export const arbitraryWithdrawal = (): fc.Arbitrary<Withdrawal> =>
|
|
97
|
+
fc.record({
|
|
98
|
+
agent: arbitraryAgent(),
|
|
99
|
+
counterparty: arbitraryCounterparty(),
|
|
100
|
+
asset: arbitraryTransactionAsset(),
|
|
101
|
+
amountDecimal: fc.float(),
|
|
102
|
+
destination: fc.oneof(arbitraryBlockchainAddress(), arbitraryCAIP10()),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export const arbitraryConnectionRequest = (): fc.Arbitrary<ConnectionRequest> =>
|
|
106
|
+
fc.record({
|
|
107
|
+
asset: arbitraryTransactionAsset(),
|
|
108
|
+
requestId: fc.option(fc.uuid()),
|
|
109
|
+
customer: fc.option(arbitraryCounterparty()),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Helper for ISO date format
|
|
113
|
+
export const arbitraryISODate = (): fc.Arbitrary<ISODate> =>
|
|
114
|
+
fc.date().map((d) => {
|
|
115
|
+
const [year, month, day] = d.toISOString().split('T')[0].split('-');
|
|
116
|
+
return `${year}-${month}-${day}` as ISODate;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Helper for ISO currency
|
|
120
|
+
export const arbitraryISOCurrency = (): fc.Arbitrary<ISOCurrency> =>
|
|
121
|
+
fc.constantFrom('USD', 'EUR', 'GBP', 'JPY');
|
|
122
|
+
|
|
123
|
+
export const arbitraryNaturalPerson = (): fc.Arbitrary<NaturalPerson> =>
|
|
124
|
+
fc.record({
|
|
125
|
+
type: fc.constant(PersonType.NATURAL),
|
|
126
|
+
name: fc.string(),
|
|
127
|
+
accountNumber: fc.option(fc.string()),
|
|
128
|
+
did: fc.option(arbitraryDID()),
|
|
129
|
+
verified: fc.option(fc.boolean()),
|
|
130
|
+
website: fc.option(fc.webUrl()),
|
|
131
|
+
phone: fc.option(fc.string()),
|
|
132
|
+
email: fc.option(fc.string()),
|
|
133
|
+
dateOfBirth: fc.option(arbitraryISODate()),
|
|
134
|
+
placeOfBirth: fc.option(fc.string()),
|
|
135
|
+
countryOfResidence: fc.option(fc.string()),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
export const arbitraryTravelAddress = (): fc.Arbitrary<TravelAddress> =>
|
|
139
|
+
fc.string().map((s) => `ta${s}` as TravelAddress);
|
|
140
|
+
|
|
141
|
+
export const arbitraryCryptoCredential = (): fc.Arbitrary<CryptoCredential> =>
|
|
142
|
+
fc
|
|
143
|
+
.tuple(fc.string(), fc.string())
|
|
144
|
+
.map(([a, b]) => `${a}.${b}.mastercard` as CryptoCredential);
|
|
145
|
+
|
|
146
|
+
export const arbitraryDepositRequest = (): fc.Arbitrary<DepositRequest> =>
|
|
147
|
+
fc.record({
|
|
148
|
+
agent: arbitraryAgent(),
|
|
149
|
+
counterparty: arbitraryCounterparty(),
|
|
150
|
+
asset: arbitraryTransactionAsset(),
|
|
151
|
+
amountDecimal: fc.float(),
|
|
152
|
+
destination: fc.oneof(arbitraryBlockchainAddress(), arbitraryCAIP10()),
|
|
153
|
+
travelAddress: fc.option(arbitraryTravelAddress()),
|
|
154
|
+
cryptoCredential: fc.option(arbitraryCryptoCredential()),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Options arbitraries
|
|
158
|
+
export const arbitraryThresholdOptions = (): fc.Arbitrary<ThresholdOptions> =>
|
|
159
|
+
fc.record({
|
|
160
|
+
threshold: fc.float(),
|
|
161
|
+
currency: arbitraryISOCurrency(),
|
|
162
|
+
proofTypes: fc.option(
|
|
163
|
+
fc.array(fc.constantFrom(...Object.values(ProofTypes))),
|
|
164
|
+
),
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
export const arbitraryFieldTypes = (): fc.Arbitrary<FieldTypes> =>
|
|
168
|
+
fc.record({
|
|
169
|
+
naturalPerson: fc.option(
|
|
170
|
+
fc.dictionary(
|
|
171
|
+
fc.string(),
|
|
172
|
+
fc.oneof(
|
|
173
|
+
fc.boolean(),
|
|
174
|
+
fc.record({
|
|
175
|
+
optional: fc.boolean(),
|
|
176
|
+
transmit: fc.boolean(),
|
|
177
|
+
}),
|
|
178
|
+
),
|
|
179
|
+
),
|
|
180
|
+
),
|
|
181
|
+
legalPerson: fc.option(
|
|
182
|
+
fc.dictionary(
|
|
183
|
+
fc.string(),
|
|
184
|
+
fc.oneof(
|
|
185
|
+
fc.boolean(),
|
|
186
|
+
fc.record({
|
|
187
|
+
optional: fc.boolean(),
|
|
188
|
+
transmit: fc.boolean(),
|
|
189
|
+
}),
|
|
190
|
+
),
|
|
191
|
+
),
|
|
192
|
+
),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
export const arbitraryVASPOptions = (): fc.Arbitrary<VASPOptions> =>
|
|
196
|
+
fc.record({
|
|
197
|
+
addUnknown: fc.option(fc.boolean()),
|
|
198
|
+
onlyActive: fc.option(fc.boolean()),
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
export const arbitraryTransactionOptions =
|
|
202
|
+
(): fc.Arbitrary<TransactionOptions> =>
|
|
203
|
+
fc.record({
|
|
204
|
+
proofs: fc.option(
|
|
205
|
+
fc.record({
|
|
206
|
+
microTransfer: fc.option(
|
|
207
|
+
fc.record({
|
|
208
|
+
destination: arbitraryBlockchainAddress(),
|
|
209
|
+
amountSubunits: fc.string(),
|
|
210
|
+
timeout: fc.option(fc.nat()),
|
|
211
|
+
}),
|
|
212
|
+
),
|
|
213
|
+
fallbacks: fc.option(
|
|
214
|
+
fc.array(fc.constantFrom(...Object.values(ProofTypes))),
|
|
215
|
+
),
|
|
216
|
+
deminimis: fc.option(arbitraryThresholdOptions()),
|
|
217
|
+
}),
|
|
218
|
+
),
|
|
219
|
+
allowedAgentTypes: fc.option(
|
|
220
|
+
fc.array(fc.constantFrom(...Object.values(AgentType))),
|
|
221
|
+
),
|
|
222
|
+
allowedCounterpartyTypes: fc.option(
|
|
223
|
+
fc.array(fc.constantFrom(...Object.values(PersonType))),
|
|
224
|
+
),
|
|
225
|
+
fields: fc.option(arbitraryFieldTypes()),
|
|
226
|
+
vasps: fc.option(arbitraryVASPOptions()),
|
|
227
|
+
hide: fc.option(
|
|
228
|
+
fc.array(fc.constantFrom(...Object.values(ValidationSections))),
|
|
229
|
+
),
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
export const arbitraryDepositRequestOptions =
|
|
233
|
+
(): fc.Arbitrary<DepositRequestOptions> =>
|
|
234
|
+
fc.record({
|
|
235
|
+
showQrCode: fc.option(fc.boolean()),
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
export const arbitraryConnectionOptions =
|
|
239
|
+
(): fc.Arbitrary<ConnectionOptions> => {
|
|
240
|
+
const transactionOptions = arbitraryTransactionOptions();
|
|
241
|
+
return fc.record({
|
|
242
|
+
proofs: transactionOptions.map((opt) => opt.proofs),
|
|
243
|
+
});
|
|
244
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Regexes
|
|
2
|
+
export const ACCOUNT_ADDRESS_REGEX = '[-.%a-zA-Z0-9]{1,128}';
|
|
3
|
+
export const CAIP2_REGEX = '[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}';
|
|
4
|
+
export const CAIP10_REGEX = `^${CAIP2_REGEX}:${ACCOUNT_ADDRESS_REGEX}$`;
|
|
5
|
+
export const CAIP19_REGEX = `^${CAIP2_REGEX}/[-a-z0-9]{3,8}:[-.%a-zA-Z0-9]{1,128}(/[-.%a-zA-Z0-9]{1,78})?$`;
|
|
6
|
+
export const CAIP220_REGEX = `^${CAIP2_REGEX}:[block:]?tx/[-.%a-zA-Z0-9]{1,128}$`;
|
|
7
|
+
export const CRYPTO_SYMBOL_REGEX = '^[A-Z0-9]{2,10}$';
|
|
8
|
+
export const DTI_REGEX = '^[A-Za-z0-9]{10}$';
|
|
9
|
+
export const COMBINED_ASSET_REGEX = `(${CRYPTO_SYMBOL_REGEX})|(${CAIP19_REGEX})|(${DTI_REGEX})`;
|
|
10
|
+
|
|
11
|
+
// Matchers
|
|
12
|
+
export const CAIP2_MATCHER = new RegExp(CAIP2_REGEX);
|
|
13
|
+
export const CAIP10_MATCHER = new RegExp(CAIP10_REGEX);
|
|
14
|
+
export const CAIP19_MATCHER = new RegExp(CAIP19_REGEX);
|
|
15
|
+
export const CAIP220_MATCHER = new RegExp(CAIP220_REGEX);
|
|
16
|
+
export const COMBINED_ASSET_MATCHER = new RegExp(COMBINED_ASSET_REGEX);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function encodeObjectToFragment(obj: any) {
|
|
2
|
+
return Object.entries(obj)
|
|
3
|
+
.map(([key, value]) => {
|
|
4
|
+
// Change condition to check for undefined/null specifically
|
|
5
|
+
if (value === undefined || value === null) return undefined;
|
|
6
|
+
const encodedKey = encodeURIComponent(key);
|
|
7
|
+
const encodedValue = encodeURIComponent(
|
|
8
|
+
typeof value === 'object' ? JSON.stringify(value) : String(value), // Use String() to handle all primitive types
|
|
9
|
+
);
|
|
10
|
+
return `${encodedKey}=${encodedValue}`;
|
|
11
|
+
})
|
|
12
|
+
.filter((entry) => entry !== undefined)
|
|
13
|
+
.join('&');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Decodes a URL fragment into an object
|
|
17
|
+
*
|
|
18
|
+
* @param fragment - The URL fragment to decode
|
|
19
|
+
* @returns An object containing the decoded key-value pairs
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
export function decodeFragmentToObject(
|
|
23
|
+
fragment: string,
|
|
24
|
+
): Record<string, string> {
|
|
25
|
+
const withoutHash = fragment.slice(1);
|
|
26
|
+
if (!withoutHash) return {}; // Return empty object for empty fragment
|
|
27
|
+
|
|
28
|
+
const pairs = withoutHash.split('&').filter(Boolean); // Filter out empty strings
|
|
29
|
+
return pairs.reduce(
|
|
30
|
+
(obj, pair) => {
|
|
31
|
+
const [key, value] = pair.split('=');
|
|
32
|
+
if (key) {
|
|
33
|
+
// Only add if key exists
|
|
34
|
+
obj[decodeURIComponent(key)] = value ? decodeURIComponent(value) : '';
|
|
35
|
+
}
|
|
36
|
+
return obj;
|
|
37
|
+
},
|
|
38
|
+
{} as Record<string, string>,
|
|
39
|
+
);
|
|
40
|
+
}
|
package/.editorconfig
DELETED
package/.envrc.template
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export GITLAB_TOKEN="your-gitlab-token"
|
package/.eslintignore
DELETED
package/.eslintrc.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
root: true,
|
|
3
|
-
parser: '@typescript-eslint/parser',
|
|
4
|
-
plugins: ['@typescript-eslint'],
|
|
5
|
-
extends: [
|
|
6
|
-
'eslint:recommended',
|
|
7
|
-
'plugin:@typescript-eslint/recommended',
|
|
8
|
-
'plugin:prettier/recommended',
|
|
9
|
-
],
|
|
10
|
-
rules: {
|
|
11
|
-
'@typescript-eslint/ban-ts-comment': 'off',
|
|
12
|
-
},
|
|
13
|
-
};
|