@cofhe/sdk 0.1.1 → 0.2.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/CHANGELOG.md +14 -0
- package/adapters/ethers6.ts +28 -28
- package/adapters/hardhat.ts +0 -1
- package/adapters/index.test.ts +14 -19
- package/adapters/smartWallet.ts +81 -73
- package/adapters/test-utils.ts +45 -45
- package/adapters/types.ts +3 -3
- package/chains/chains/localcofhe.ts +14 -0
- package/chains/chains.test.ts +2 -1
- package/chains/index.ts +3 -1
- package/core/baseBuilder.ts +30 -49
- package/core/client.test.ts +94 -77
- package/core/client.ts +133 -149
- package/core/clientTypes.ts +108 -0
- package/core/config.test.ts +22 -11
- package/core/config.ts +16 -9
- package/core/decrypt/decryptHandleBuilder.ts +51 -45
- package/core/decrypt/{tnSealOutput.ts → tnSealOutputV1.ts} +1 -1
- package/core/decrypt/tnSealOutputV2.ts +298 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +16 -10
- package/core/encrypt/encryptInputsBuilder.test.ts +132 -116
- package/core/encrypt/encryptInputsBuilder.ts +159 -111
- package/core/encrypt/encryptUtils.ts +6 -3
- package/core/encrypt/zkPackProveVerify.ts +70 -8
- package/core/error.ts +0 -2
- package/core/fetchKeys.test.ts +1 -18
- package/core/fetchKeys.ts +0 -26
- package/core/index.ts +29 -17
- package/core/keyStore.ts +65 -38
- package/core/permits.test.ts +253 -1
- package/core/permits.ts +80 -16
- package/core/types.ts +198 -152
- package/core/utils.ts +43 -1
- package/dist/adapters.d.cts +38 -20
- package/dist/adapters.d.ts +38 -20
- package/dist/chains.cjs +14 -1
- package/dist/chains.d.cts +23 -1
- package/dist/chains.d.ts +23 -1
- package/dist/chains.js +1 -1
- package/dist/{chunk-LU7BMUUT.js → chunk-UGBVZNRT.js} +39 -25
- package/dist/{chunk-GZCQQYVI.js → chunk-WEAZ25JO.js} +14 -2
- package/dist/{chunk-KFGPTJ6X.js → chunk-WGCRJCBR.js} +1920 -1692
- package/dist/{types-bB7wLj0q.d.cts → clientTypes-5_1nwtUe.d.cts} +308 -347
- package/dist/{types-PhwGgQvs.d.ts → clientTypes-Es7fyi65.d.ts} +308 -347
- package/dist/core.cjs +2872 -2632
- package/dist/core.d.cts +101 -6
- package/dist/core.d.ts +101 -6
- package/dist/core.js +3 -3
- package/dist/node.cjs +2716 -2520
- package/dist/node.d.cts +3 -3
- package/dist/node.d.ts +3 -3
- package/dist/node.js +4 -3
- package/dist/{permit-S9CnI6MF.d.cts → permit-fUSe6KKq.d.cts} +31 -15
- package/dist/{permit-S9CnI6MF.d.ts → permit-fUSe6KKq.d.ts} +31 -15
- package/dist/permits.cjs +39 -24
- package/dist/permits.d.cts +137 -148
- package/dist/permits.d.ts +137 -148
- package/dist/permits.js +1 -1
- package/dist/web.cjs +2929 -2518
- package/dist/web.d.cts +21 -5
- package/dist/web.d.ts +21 -5
- package/dist/web.js +185 -9
- package/dist/zkProve.worker.cjs +93 -0
- package/dist/zkProve.worker.d.cts +2 -0
- package/dist/zkProve.worker.d.ts +2 -0
- package/dist/zkProve.worker.js +91 -0
- package/node/client.test.ts +20 -25
- package/node/encryptInputs.test.ts +18 -38
- package/node/index.ts +1 -0
- package/package.json +14 -14
- package/permits/index.ts +1 -0
- package/permits/localstorage.test.ts +0 -1
- package/permits/permit.test.ts +25 -22
- package/permits/permit.ts +30 -21
- package/permits/sealing.test.ts +3 -3
- package/permits/sealing.ts +2 -2
- package/permits/store.ts +5 -7
- package/permits/test-utils.ts +1 -1
- package/permits/types.ts +17 -0
- package/permits/utils.ts +0 -1
- package/permits/validation.ts +24 -4
- package/web/client.web.test.ts +20 -25
- package/web/config.web.test.ts +0 -2
- package/web/encryptInputs.web.test.ts +31 -54
- package/web/index.ts +65 -1
- package/web/storage.ts +19 -5
- package/web/worker.builder.web.test.ts +148 -0
- package/web/worker.config.web.test.ts +329 -0
- package/web/worker.output.web.test.ts +84 -0
- package/web/workerManager.test.ts +80 -0
- package/web/workerManager.ts +214 -0
- package/web/workerManager.web.test.ts +114 -0
- package/web/zkProve.worker.ts +133 -0
- package/core/result.test.ts +0 -180
- package/core/result.ts +0 -67
- package/core/test-utils.ts +0 -45
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { getWorkerManager, terminateWorker, areWorkersAvailable } from './workerManager.js';
|
|
3
|
+
|
|
4
|
+
describe('WorkerManager (Browser)', () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
// Clean up worker after each test
|
|
7
|
+
terminateWorker();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('areWorkersAvailable', () => {
|
|
11
|
+
it('should return true in browser environment', () => {
|
|
12
|
+
// In real browser with Playwright, workers should be available
|
|
13
|
+
expect(areWorkersAvailable()).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('Worker lifecycle', () => {
|
|
18
|
+
it('should initialize worker successfully', async () => {
|
|
19
|
+
const manager = getWorkerManager();
|
|
20
|
+
expect(manager).toBeDefined();
|
|
21
|
+
|
|
22
|
+
// Worker should be available
|
|
23
|
+
expect(areWorkersAvailable()).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should handle worker termination', () => {
|
|
27
|
+
const manager = getWorkerManager();
|
|
28
|
+
expect(manager).toBeDefined();
|
|
29
|
+
|
|
30
|
+
terminateWorker();
|
|
31
|
+
|
|
32
|
+
// Should be able to get a new instance
|
|
33
|
+
const newManager = getWorkerManager();
|
|
34
|
+
expect(newManager).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('submitProof', () => {
|
|
39
|
+
it('should reject invalid data with error message', async () => {
|
|
40
|
+
const manager = getWorkerManager();
|
|
41
|
+
|
|
42
|
+
// Submit with invalid data - should fail quickly
|
|
43
|
+
await expect(manager.submitProof('invalid', 'invalid', [], new Uint8Array())).rejects.toThrow();
|
|
44
|
+
}, 35000);
|
|
45
|
+
|
|
46
|
+
it('should reject invalid message type', async () => {
|
|
47
|
+
const manager = getWorkerManager();
|
|
48
|
+
|
|
49
|
+
// Submit with invalid data that will cause worker error
|
|
50
|
+
await expect(
|
|
51
|
+
manager.submitProof(
|
|
52
|
+
'', // empty key
|
|
53
|
+
'', // empty crs
|
|
54
|
+
[{ utype: 'invalid', data: 'test' }],
|
|
55
|
+
new Uint8Array([1, 2, 3])
|
|
56
|
+
)
|
|
57
|
+
).rejects.toThrow();
|
|
58
|
+
}, 35000);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Concurrent requests', () => {
|
|
62
|
+
it('should handle multiple concurrent proof requests', async () => {
|
|
63
|
+
const manager = getWorkerManager();
|
|
64
|
+
|
|
65
|
+
// Submit multiple requests concurrently
|
|
66
|
+
const requests = [
|
|
67
|
+
manager.submitProof('key1', 'crs1', [], new Uint8Array([1])),
|
|
68
|
+
manager.submitProof('key2', 'crs2', [], new Uint8Array([2])),
|
|
69
|
+
manager.submitProof('key3', 'crs3', [], new Uint8Array([3])),
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
// All should reject (invalid data), but shouldn't crash
|
|
73
|
+
const results = await Promise.allSettled(requests);
|
|
74
|
+
|
|
75
|
+
expect(results).toHaveLength(3);
|
|
76
|
+
results.forEach((result) => {
|
|
77
|
+
expect(result.status).toBe('rejected');
|
|
78
|
+
});
|
|
79
|
+
}, 35000);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('Worker error handling', () => {
|
|
83
|
+
it('should handle worker errors gracefully', async () => {
|
|
84
|
+
const manager = getWorkerManager();
|
|
85
|
+
|
|
86
|
+
// This should cause an error in the worker
|
|
87
|
+
await expect(
|
|
88
|
+
manager.submitProof(
|
|
89
|
+
'malformed-hex',
|
|
90
|
+
'malformed-crs',
|
|
91
|
+
[{ utype: 'uint128', data: 'not-a-number' }],
|
|
92
|
+
new Uint8Array([])
|
|
93
|
+
)
|
|
94
|
+
).rejects.toThrow();
|
|
95
|
+
}, 35000);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('Termination during processing', () => {
|
|
99
|
+
it('should reject pending requests on termination', async () => {
|
|
100
|
+
const manager = getWorkerManager();
|
|
101
|
+
|
|
102
|
+
// Start a request (will timeout)
|
|
103
|
+
const proofPromise = manager.submitProof('test', 'test', [], new Uint8Array());
|
|
104
|
+
|
|
105
|
+
// Terminate immediately
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
terminateWorker();
|
|
108
|
+
}, 100);
|
|
109
|
+
|
|
110
|
+
// Request should be rejected
|
|
111
|
+
await expect(proofPromise).rejects.toThrow();
|
|
112
|
+
}, 5000);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Worker for ZK Proof Generation
|
|
3
|
+
* Performs heavy WASM computation off the main thread
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/// <reference lib="webworker" />
|
|
7
|
+
/* eslint-disable no-undef */
|
|
8
|
+
|
|
9
|
+
import type { ZkProveWorkerRequest, ZkProveWorkerResponse } from '../core/encrypt/zkPackProveVerify.js';
|
|
10
|
+
|
|
11
|
+
// TFHE module (will be initialized on first use)
|
|
12
|
+
let tfheModule: any = null;
|
|
13
|
+
let initialized = false;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize TFHE in worker context
|
|
17
|
+
*/
|
|
18
|
+
async function initTfhe() {
|
|
19
|
+
if (initialized) return;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// Dynamic import of tfhe module
|
|
23
|
+
tfheModule = await import('tfhe');
|
|
24
|
+
await tfheModule.default();
|
|
25
|
+
await tfheModule.init_panic_hook();
|
|
26
|
+
initialized = true;
|
|
27
|
+
console.log('[Worker] TFHE initialized');
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('[Worker] Failed to initialize TFHE:', error);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Convert hex string to Uint8Array
|
|
36
|
+
*/
|
|
37
|
+
function fromHexString(hexString: string): Uint8Array {
|
|
38
|
+
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
39
|
+
const arr = cleanString.replace(/^0x/, '').match(/.{1,2}/g);
|
|
40
|
+
if (!arr) return new Uint8Array();
|
|
41
|
+
return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Main message handler
|
|
46
|
+
*/
|
|
47
|
+
self.onmessage = async (event: MessageEvent) => {
|
|
48
|
+
const { id, type, fheKeyHex, crsHex, items, metadata } = event.data as ZkProveWorkerRequest;
|
|
49
|
+
|
|
50
|
+
if (type !== 'zkProve') {
|
|
51
|
+
self.postMessage({
|
|
52
|
+
id,
|
|
53
|
+
type: 'error',
|
|
54
|
+
error: 'Invalid message type',
|
|
55
|
+
} as ZkProveWorkerResponse);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
// Initialize TFHE if needed
|
|
61
|
+
await initTfhe();
|
|
62
|
+
|
|
63
|
+
if (!tfheModule) {
|
|
64
|
+
throw new Error('TFHE module not initialized');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Deserialize FHE public key and CRS from hex strings
|
|
68
|
+
const fheKeyBytes = fromHexString(fheKeyHex);
|
|
69
|
+
const crsBytes = fromHexString(crsHex);
|
|
70
|
+
|
|
71
|
+
const fheKey = tfheModule.TfheCompactPublicKey.deserialize(fheKeyBytes);
|
|
72
|
+
const crs = tfheModule.CompactPkeCrs.deserialize(crsBytes);
|
|
73
|
+
|
|
74
|
+
// Create builder
|
|
75
|
+
const builder = tfheModule.ProvenCompactCiphertextList.builder(fheKey);
|
|
76
|
+
|
|
77
|
+
// Pack all items (duplicate of zkPack logic)
|
|
78
|
+
for (const item of items) {
|
|
79
|
+
switch (item.utype) {
|
|
80
|
+
case 'bool':
|
|
81
|
+
builder.push_boolean(Boolean(item.data));
|
|
82
|
+
break;
|
|
83
|
+
case 'uint8':
|
|
84
|
+
builder.push_u8(Number(item.data));
|
|
85
|
+
break;
|
|
86
|
+
case 'uint16':
|
|
87
|
+
builder.push_u16(Number(item.data));
|
|
88
|
+
break;
|
|
89
|
+
case 'uint32':
|
|
90
|
+
builder.push_u32(Number(item.data));
|
|
91
|
+
break;
|
|
92
|
+
case 'uint64':
|
|
93
|
+
builder.push_u64(BigInt(item.data));
|
|
94
|
+
break;
|
|
95
|
+
case 'uint128':
|
|
96
|
+
builder.push_u128(BigInt(item.data));
|
|
97
|
+
break;
|
|
98
|
+
case 'uint160':
|
|
99
|
+
builder.push_u160(BigInt(item.data));
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
throw new Error(`Unsupported type: ${item.utype}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// THE HEAVY OPERATION - but in worker thread!
|
|
107
|
+
const metadataBytes = new Uint8Array(metadata);
|
|
108
|
+
const compactList = builder.build_with_proof_packed(crs, metadataBytes, 1);
|
|
109
|
+
|
|
110
|
+
// Serialize result
|
|
111
|
+
const result = compactList.serialize();
|
|
112
|
+
|
|
113
|
+
// Send success response
|
|
114
|
+
self.postMessage({
|
|
115
|
+
id,
|
|
116
|
+
type: 'success',
|
|
117
|
+
result: Array.from(result),
|
|
118
|
+
} as ZkProveWorkerResponse);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
// Send error response
|
|
121
|
+
self.postMessage({
|
|
122
|
+
id,
|
|
123
|
+
type: 'error',
|
|
124
|
+
error: error instanceof Error ? error.message : String(error),
|
|
125
|
+
} as ZkProveWorkerResponse);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Signal ready - send proper message format
|
|
130
|
+
self.postMessage({
|
|
131
|
+
id: 'init',
|
|
132
|
+
type: 'ready',
|
|
133
|
+
} as ZkProveWorkerResponse);
|
package/core/result.test.ts
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { ResultOk, ResultErr, ResultErrOrInternal, resultWrapper, resultWrapperSync, type Result } from './result.js';
|
|
3
|
-
import { CofhesdkError, CofhesdkErrorCode } from './error.js';
|
|
4
|
-
|
|
5
|
-
export const expectResultError = <T>(result: Result<T>, errorPartial: string) => {
|
|
6
|
-
expect(result.success).to.eq(false, 'Result should be an error');
|
|
7
|
-
expect(result.error!.message).to.include(errorPartial, `Error should contain error partial: ${errorPartial}`);
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const expectResultSuccess = <T>(result: Result<T>): T => {
|
|
11
|
-
expect(result.success).to.eq(true, 'Result should be a success');
|
|
12
|
-
return result.data!;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const expectResultValue = <T>(result: Result<T>, value: T): T => {
|
|
16
|
-
expect(result.success).to.eq(true, 'Result should be a success');
|
|
17
|
-
expect(result.data).to.eq(value, `Result should have the expected value ${value}`);
|
|
18
|
-
return result.data!;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
describe('Result Type', () => {
|
|
22
|
-
describe('ResultOk', () => {
|
|
23
|
-
it('should create a successful result', () => {
|
|
24
|
-
const result = ResultOk('test data');
|
|
25
|
-
expect(result.success).toBe(true);
|
|
26
|
-
expect(result.data).toBe('test data');
|
|
27
|
-
expect(result.error).toBe(null);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should handle different data types', () => {
|
|
31
|
-
const stringResult = ResultOk('string');
|
|
32
|
-
const numberResult = ResultOk(42);
|
|
33
|
-
const objectResult = ResultOk({ key: 'value' });
|
|
34
|
-
const nullResult = ResultOk(null);
|
|
35
|
-
|
|
36
|
-
expect(stringResult.data).toBe('string');
|
|
37
|
-
expect(numberResult.data).toBe(42);
|
|
38
|
-
expect(objectResult.data).toEqual({ key: 'value' });
|
|
39
|
-
expect(nullResult.data).toBe(null);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('ResultErr', () => {
|
|
44
|
-
it('should create an error result', () => {
|
|
45
|
-
const error = new CofhesdkError({
|
|
46
|
-
code: CofhesdkErrorCode.InternalError,
|
|
47
|
-
message: 'Test error',
|
|
48
|
-
});
|
|
49
|
-
const result = ResultErr(error);
|
|
50
|
-
|
|
51
|
-
expect(result.success).toBe(false);
|
|
52
|
-
expect(result.data).toBe(null);
|
|
53
|
-
expect(result.error).toBe(error);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe('Error Utilities', () => {
|
|
59
|
-
describe('ResultErrOrInternal', () => {
|
|
60
|
-
it('should wrap unknown errors as internal errors', () => {
|
|
61
|
-
const result = ResultErrOrInternal(new Error('string error'));
|
|
62
|
-
expect(result.success).toBe(false);
|
|
63
|
-
expect(result.error!).toBeInstanceOf(CofhesdkError);
|
|
64
|
-
expect(result.error!.code).toBe(CofhesdkErrorCode.InternalError);
|
|
65
|
-
expect(result.error!.message).toBe('An internal error occurred | Caused by: string error');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should preserve CofhesdkError instances', () => {
|
|
69
|
-
const originalError = new CofhesdkError({
|
|
70
|
-
code: CofhesdkErrorCode.MissingPublicClient,
|
|
71
|
-
message: 'Public client missing',
|
|
72
|
-
});
|
|
73
|
-
const result = ResultErrOrInternal(originalError);
|
|
74
|
-
expect(result.success).toBe(false);
|
|
75
|
-
expect(result.error!).toBe(originalError);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should handle Error instances', () => {
|
|
79
|
-
const error = new Error('Standard error');
|
|
80
|
-
const result = ResultErrOrInternal(error);
|
|
81
|
-
expect(result.success).toBe(false);
|
|
82
|
-
expect(result.error!).toBeInstanceOf(CofhesdkError);
|
|
83
|
-
expect(result.error!.cause).toBe(error);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('Result Wrappers', () => {
|
|
89
|
-
describe('resultWrapperSync', () => {
|
|
90
|
-
it('should wrap successful sync function', () => {
|
|
91
|
-
const result = resultWrapperSync(() => 'success');
|
|
92
|
-
expect(result.success).toBe(true);
|
|
93
|
-
expect(result.data).toBe('success');
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should wrap failing sync function', () => {
|
|
97
|
-
const result = resultWrapperSync(() => {
|
|
98
|
-
throw new Error('Sync error');
|
|
99
|
-
});
|
|
100
|
-
expect(result.success).toBe(false);
|
|
101
|
-
expect(result.error).toBeInstanceOf(CofhesdkError);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe('resultWrapper', () => {
|
|
106
|
-
it('should wrap successful async function', async () => {
|
|
107
|
-
const result = await resultWrapper(async () => 'async success');
|
|
108
|
-
expect(result.success).toBe(true);
|
|
109
|
-
expect(result.data).toBe('async success');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should wrap failing async function', async () => {
|
|
113
|
-
const result = await resultWrapper(async () => {
|
|
114
|
-
throw new Error('Async error');
|
|
115
|
-
});
|
|
116
|
-
expect(result.success).toBe(false);
|
|
117
|
-
expect(result.error).toBeInstanceOf(CofhesdkError);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should handle async operations', async () => {
|
|
121
|
-
const result = await resultWrapper(async () => {
|
|
122
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
123
|
-
return 'delayed result';
|
|
124
|
-
});
|
|
125
|
-
expect(result.success).toBe(true);
|
|
126
|
-
expect(result.data).toBe('delayed result');
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should handle Promise rejections', async () => {
|
|
130
|
-
const result = await resultWrapper(async () => {
|
|
131
|
-
return Promise.reject(new Error('Promise rejection'));
|
|
132
|
-
});
|
|
133
|
-
expect(result.success).toBe(false);
|
|
134
|
-
expect(result.error).toBeInstanceOf(CofhesdkError);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe('Result Type Guards', () => {
|
|
140
|
-
it('should correctly identify success results', () => {
|
|
141
|
-
const successResult = ResultOk('data');
|
|
142
|
-
expect(successResult.success).toBe(true);
|
|
143
|
-
expect(successResult.data).toBe('data');
|
|
144
|
-
expect(successResult.error).toBe(null);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should correctly identify error results', () => {
|
|
148
|
-
const errorResult = ResultErr(
|
|
149
|
-
new CofhesdkError({
|
|
150
|
-
code: CofhesdkErrorCode.InternalError,
|
|
151
|
-
message: 'Test error',
|
|
152
|
-
})
|
|
153
|
-
);
|
|
154
|
-
expect(errorResult.success).toBe(false);
|
|
155
|
-
expect(errorResult.data).toBe(null);
|
|
156
|
-
expect(errorResult.error).toBeInstanceOf(CofhesdkError);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('Result Test Utilities', () => {
|
|
161
|
-
it('expectResultSuccess should return data for success', () => {
|
|
162
|
-
const result = ResultOk('data');
|
|
163
|
-
expect(expectResultSuccess(result)).toBe('data');
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('expectResultValue should validate and return matching data', () => {
|
|
167
|
-
const result = ResultOk('expected');
|
|
168
|
-
expect(expectResultValue(result, 'expected')).toBe('expected');
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('expectResultError should validate error messages', () => {
|
|
172
|
-
const result = ResultErr(
|
|
173
|
-
new CofhesdkError({
|
|
174
|
-
code: CofhesdkErrorCode.InternalError,
|
|
175
|
-
message: 'Test error message',
|
|
176
|
-
})
|
|
177
|
-
);
|
|
178
|
-
expectResultError(result, 'Test error');
|
|
179
|
-
});
|
|
180
|
-
});
|
package/core/result.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-unused-vars */
|
|
2
|
-
import { CofhesdkError, CofhesdkErrorCode } from './error.js';
|
|
3
|
-
|
|
4
|
-
export type Result<T> = { success: true; data: T; error: null } | { success: false; data: null; error: CofhesdkError };
|
|
5
|
-
|
|
6
|
-
export const ResultErr = <T>(error: CofhesdkError): Result<T> => ({
|
|
7
|
-
success: false,
|
|
8
|
-
data: null,
|
|
9
|
-
error,
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export const ResultOk = <T>(data: T): Result<T> => ({
|
|
13
|
-
success: true,
|
|
14
|
-
data,
|
|
15
|
-
error: null,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export const ResultErrOrInternal = <T>(error: unknown): Result<T> => {
|
|
19
|
-
return ResultErr(CofhesdkError.fromError(error));
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const ResultHttpError = (error: unknown, url: string, status?: number): CofhesdkError => {
|
|
23
|
-
if (error instanceof CofhesdkError) return error;
|
|
24
|
-
|
|
25
|
-
const message = status ? `HTTP error ${status} from ${url}` : `HTTP request failed for ${url}`;
|
|
26
|
-
|
|
27
|
-
return new CofhesdkError({
|
|
28
|
-
code: CofhesdkErrorCode.InternalError,
|
|
29
|
-
message,
|
|
30
|
-
cause: error instanceof Error ? error : undefined,
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const ResultValidationError = (message: string): CofhesdkError => {
|
|
35
|
-
return new CofhesdkError({
|
|
36
|
-
code: CofhesdkErrorCode.InvalidPermitData,
|
|
37
|
-
message,
|
|
38
|
-
});
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Async resultWrapper
|
|
42
|
-
export const resultWrapper = async <T>(
|
|
43
|
-
tryFn: () => Promise<T>,
|
|
44
|
-
catchFn?: (error: CofhesdkError) => void,
|
|
45
|
-
finallyFn?: () => void
|
|
46
|
-
): Promise<Result<T>> => {
|
|
47
|
-
try {
|
|
48
|
-
const result = await tryFn();
|
|
49
|
-
return ResultOk(result);
|
|
50
|
-
} catch (error) {
|
|
51
|
-
const result = ResultErrOrInternal(error);
|
|
52
|
-
catchFn?.(result.error!);
|
|
53
|
-
return result as Result<T>;
|
|
54
|
-
} finally {
|
|
55
|
-
finallyFn?.();
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// Sync resultWrapper
|
|
60
|
-
export const resultWrapperSync = <T>(fn: () => T): Result<T> => {
|
|
61
|
-
try {
|
|
62
|
-
const result = fn();
|
|
63
|
-
return ResultOk(result);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
return ResultErrOrInternal(error);
|
|
66
|
-
}
|
|
67
|
-
};
|
package/core/test-utils.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { expect } from 'vitest';
|
|
2
|
-
import { type Result } from './result.js';
|
|
3
|
-
import { CofhesdkError, CofhesdkErrorCode } from './error.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Assert that a Result is successful and return its data
|
|
7
|
-
* Useful for type narrowing in tests
|
|
8
|
-
*/
|
|
9
|
-
export const expectResultSuccess = <T>(result: Result<T>): NonNullable<T> => {
|
|
10
|
-
expect(result.error, `Result error: ${result.error?.toString()}`).toBe(null);
|
|
11
|
-
expect(result.success, `Result: ${bigintSafeJsonStringify(result)}`).toBe(true);
|
|
12
|
-
expect(result.data, `Result: ${bigintSafeJsonStringify(result)}`).not.toBe(null);
|
|
13
|
-
return result.data!;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Assert that a Result is an error and optionally verify the error details
|
|
18
|
-
*/
|
|
19
|
-
export const expectResultError = <T>(
|
|
20
|
-
result: Result<T>,
|
|
21
|
-
errorCode?: CofhesdkErrorCode,
|
|
22
|
-
errorMessage?: string
|
|
23
|
-
): CofhesdkError => {
|
|
24
|
-
expect(result.success, `Result: ${bigintSafeJsonStringify(result)}`).toBe(false);
|
|
25
|
-
expect(result.data, `Result: ${bigintSafeJsonStringify(result)}`).toBe(null);
|
|
26
|
-
expect(result.error, `Result: ${bigintSafeJsonStringify(result)}`).not.toBe(null);
|
|
27
|
-
const error = result.error as CofhesdkError;
|
|
28
|
-
expect(error, `Result error: ${result.error?.toString()}`).toBeInstanceOf(CofhesdkError);
|
|
29
|
-
if (errorCode) {
|
|
30
|
-
expect(error.code, `Result error: ${result.error?.toString()}`).toBe(errorCode);
|
|
31
|
-
}
|
|
32
|
-
if (errorMessage) {
|
|
33
|
-
expect(error.message, `Result error: ${result.error?.toString()}`).toBe(errorMessage);
|
|
34
|
-
}
|
|
35
|
-
return error;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const bigintSafeJsonStringify = (value: unknown): string => {
|
|
39
|
-
return JSON.stringify(value, (key, value) => {
|
|
40
|
-
if (typeof value === 'bigint') {
|
|
41
|
-
return `${value}n`;
|
|
42
|
-
}
|
|
43
|
-
return value;
|
|
44
|
-
});
|
|
45
|
-
};
|