@zkpassport/sdk 0.2.4 → 0.2.6

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/README.md CHANGED
@@ -15,21 +15,21 @@ npm install @zkpassport/sdk
15
15
  ## How to use
16
16
 
17
17
  ```ts
18
- import { ZKPassport, EU_COUNTRIES } from '@zkpassport/sdk'
18
+ import { ZKPassport, EU_COUNTRIES } from "@zkpassport/sdk"
19
19
 
20
20
  // Replace with your domain
21
- const zkPassport = new ZKPassport('demo.zkpassport.id')
21
+ const zkPassport = new ZKPassport("demo.zkpassport.id")
22
22
 
23
23
  // Specify your app name, logo and the purpose of the request
24
24
  // you'll send to your visitors or users
25
25
  const queryBuilder = await zkPassport.request({
26
- name: 'ZKPassport',
27
- logo: 'https://zkpassport.id/logo.png',
28
- purpose: 'Prove you are an adult from the EU but not from Scandinavia',
26
+ name: "ZKPassport",
27
+ logo: "https://zkpassport.id/logo.png",
28
+ purpose: "Prove you are an adult from the EU but not from Scandinavia",
29
29
  // The scope is optional and can be used to scope the unique identifier
30
30
  // of the request to a specific use case
31
31
  // By default, the request's unique identifier is scoped to your domain name only
32
- scope: 'eu-adult-not-scandinavia',
32
+ scope: "eu-adult-not-scandinavia",
33
33
  })
34
34
 
35
35
  // Specify the data you want to disclose
@@ -47,10 +47,10 @@ const {
47
47
  onReject,
48
48
  onError,
49
49
  } = queryBuilder
50
- .disclose('firstname')
51
- .gte('age', 18)
52
- .in('nationality', EU_COUNTRIES)
53
- .out('nationality', ['Sweden', 'Denmark'])
50
+ .disclose("firstname")
51
+ .gte("age", 18)
52
+ .in("nationality", EU_COUNTRIES)
53
+ .out("nationality", ["Sweden", "Denmark"])
54
54
  .done()
55
55
 
56
56
  // Generate a QR Code with the url and let your user scan it
@@ -60,12 +60,12 @@ onRequestReceived(() => {
60
60
  // The user scanned the QR code or clicked the link to the request
61
61
  // Essentially, this means the request popup is now opened
62
62
  // on the user phone
63
- console.log('Request received')
63
+ console.log("Request received")
64
64
  })
65
65
 
66
66
  onGeneratingProof(() => {
67
67
  // The user accepted the request and the proof is being generated
68
- console.log('Generating proof')
68
+ console.log("Generating proof")
69
69
  })
70
70
 
71
71
  // You probably don't need to use this callback
@@ -75,10 +75,10 @@ onProofGenerated(({ proof, vkeyHash, version, name }: ProofResult) => {
75
75
  // Here, you can retrieve the proof manually and verify it
76
76
  // But note that the verification of the proofs is handled
77
77
  // automatically by the SDK
78
- console.log('Proof generated', proof)
79
- console.log('Verification key hash', vkeyHash)
80
- console.log('Version', version)
81
- console.log('Name', name)
78
+ console.log("Proof generated", proof)
79
+ console.log("Verification key hash", vkeyHash)
80
+ console.log("Version", version)
81
+ console.log("Name", name)
82
82
  })
83
83
 
84
84
  // That's the callback you're looking for
@@ -93,22 +93,22 @@ onResult(
93
93
  result: QueryResult
94
94
  }) => {
95
95
  // All the proofs have been generated and the final result is available
96
- console.log('firstname', result.firstname.disclose.result)
97
- console.log('age over 18', result.age.gte.result)
98
- console.log('nationality in EU', result.nationality.in.result)
99
- console.log('nationality not from Scandinavia', result.nationality.out.result)
96
+ console.log("firstname", result.firstname.disclose.result)
97
+ console.log("age over 18", result.age.gte.result)
98
+ console.log("nationality in EU", result.nationality.in.result)
99
+ console.log("nationality not from Scandinavia", result.nationality.out.result)
100
100
  // You can also retrieved what were the values originally requested
101
- console.log('age over', result.age.gte.expected)
102
- console.log('nationality in', result.nationality.in.expected)
103
- console.log('nationality not in', result.nationality.out.expected)
101
+ console.log("age over", result.age.gte.expected)
102
+ console.log("nationality in", result.nationality.in.expected)
103
+ console.log("nationality not in", result.nationality.out.expected)
104
104
  // You can make sure the proof are valid by checking verified is set to true
105
- console.log('proofs are valid', verified)
105
+ console.log("proofs are valid", verified)
106
106
  // You can also retrieve the unique identifier associated to this request
107
107
  // The assumption is that the unique identifier will be the same if coming
108
108
  // from the same ID for the same domain name and scope
109
109
  // So you can use it to identify if the user has already provided the proof
110
110
  // for this specific use case
111
- console.log('unique identifier', uniqueIdentifier)
111
+ console.log("unique identifier", uniqueIdentifier)
112
112
  },
113
113
  )
114
114
  ```
@@ -122,17 +122,17 @@ You can integrate `@zkpassport/sdk` into a Next.js application by creating a bac
122
122
  **App Router:** `app/api/zkpassport/route.ts`
123
123
 
124
124
  ```typescript
125
- import { NextResponse } from 'next/server'
126
- import { ZKPassport } from '@zkpassport/sdk'
125
+ import { NextResponse } from "next/server"
126
+ import { ZKPassport } from "@zkpassport/sdk"
127
127
 
128
128
  export async function GET() {
129
- const zkPassport = new ZKPassport('demo.zkpassport.id') // Replace with your domain
129
+ const zkPassport = new ZKPassport("demo.zkpassport.id") // Replace with your domain
130
130
  const queryBuilder = await zkPassport.request({
131
- name: 'ZKPassport Demo',
132
- logo: 'https://via.placeholder.com/150',
133
- purpose: 'Verify user nationality and first name',
131
+ name: "ZKPassport Demo",
132
+ logo: "https://via.placeholder.com/150",
133
+ purpose: "Verify user nationality and first name",
134
134
  })
135
- const { url } = queryBuilder.disclose('nationality').disclose('firstname').done()
135
+ const { url } = queryBuilder.disclose("nationality").disclose("firstname").done()
136
136
  return NextResponse.json({ url })
137
137
  }
138
138
  ```
@@ -142,14 +142,14 @@ export async function GET() {
142
142
  **App Router:** `app/page.tsx`
143
143
 
144
144
  ```tsx
145
- 'use client'
146
- import { useEffect, useState } from 'react'
145
+ "use client"
146
+ import { useEffect, useState } from "react"
147
147
 
148
148
  export default function Home() {
149
149
  const [verificationUrl, setVerificationUrl] = useState<string | null>(null)
150
150
 
151
151
  useEffect(() => {
152
- fetch('/api/zkpassport')
152
+ fetch("/api/zkpassport")
153
153
  .then((res) => res.json())
154
154
  .then((data) => setVerificationUrl(data.url))
155
155
  .catch(console.error)
@@ -1,7 +1,7 @@
1
1
  export declare function generateECDHKeyPair(): Promise<{
2
- privateKey: Uint8Array;
3
- publicKey: Uint8Array;
2
+ privateKey: import("@noble/secp256k1").Bytes;
3
+ publicKey: import("@noble/secp256k1").Bytes;
4
4
  }>;
5
- export declare function getSharedSecret(privateKey: string, publicKey: string): Promise<Uint8Array>;
6
- export declare function encrypt(message: string, sharedSecret: Uint8Array, topic: string): Promise<Uint8Array>;
5
+ export declare function getSharedSecret(privateKey: string, publicKey: string): Promise<Uint8Array<ArrayBuffer>>;
6
+ export declare function encrypt(message: string, sharedSecret: Uint8Array, topic: string): Promise<Uint8Array<ArrayBufferLike>>;
7
7
  export declare function decrypt(ciphertext: Uint8Array, sharedSecret: Uint8Array, topic: string): Promise<string>;
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  Object.defineProperty(exports, "__esModule", { value: true });
26
36
  exports.generateECDHKeyPair = generateECDHKeyPair;
27
37
  exports.getSharedSecret = getSharedSecret;
@@ -134,6 +134,8 @@ export declare class ZKPassport {
134
134
  private topicToRequestReceived;
135
135
  private topicToService;
136
136
  private topicToProofs;
137
+ private topicToExpectedProofCount;
138
+ private topicToResults;
137
139
  private onRequestReceivedCallbacks;
138
140
  private onGeneratingProofCallbacks;
139
141
  private onBridgeConnectCallbacks;
@@ -142,6 +144,8 @@ export declare class ZKPassport {
142
144
  private onRejectCallbacks;
143
145
  private onErrorCallbacks;
144
146
  constructor(_domain?: string);
147
+ private handleResult;
148
+ private setExpectedProofCount;
145
149
  /**
146
150
  * @notice Handle an encrypted message.
147
151
  * @param request The request.
package/dist/cjs/index.js CHANGED
@@ -14,7 +14,26 @@ const node_gzip_1 = require("node-gzip");
14
14
  //import initNoirC from '@noir-lang/noirc_abi'
15
15
  //import initACVM from '@noir-lang/acvm_js'
16
16
  const en_json_1 = tslib_1.__importDefault(require("i18n-iso-countries/langs/en.json"));
17
+ const buffer_1 = require("buffer/");
18
+ // If Buffer is not defined, then we use the Buffer from the buffer package
19
+ if (typeof globalThis.Buffer === "undefined") {
20
+ globalThis.Buffer = buffer_1.Buffer;
21
+ }
17
22
  (0, i18n_iso_countries_1.registerLocale)(en_json_1.default);
23
+ function hasRequestedAccessToField(credentialsRequest, field) {
24
+ const fieldValue = credentialsRequest[field];
25
+ const isDefined = fieldValue !== undefined && fieldValue !== null;
26
+ if (!isDefined) {
27
+ return false;
28
+ }
29
+ for (const key in fieldValue) {
30
+ if (fieldValue[key] !== undefined &&
31
+ fieldValue[key] !== null) {
32
+ return true;
33
+ }
34
+ }
35
+ return false;
36
+ }
18
37
  function normalizeCountry(country) {
19
38
  let normalizedCountry;
20
39
  const alpha3 = (0, i18n_iso_countries_1.getAlpha3Code)(country, "en");
@@ -56,6 +75,8 @@ class ZKPassport {
56
75
  this.topicToRequestReceived = {};
57
76
  this.topicToService = {};
58
77
  this.topicToProofs = {};
78
+ this.topicToExpectedProofCount = {};
79
+ this.topicToResults = {};
59
80
  this.onRequestReceivedCallbacks = {};
60
81
  this.onGeneratingProofCallbacks = {};
61
82
  this.onBridgeConnectCallbacks = {};
@@ -74,6 +95,70 @@ class ZKPassport {
74
95
  await Promise.all([initACVM(acvm), initNoirC(noirc)])
75
96
  this.wasmVerifierInit = true
76
97
  }*/
98
+ async handleResult(topic) {
99
+ const result = this.topicToResults[topic];
100
+ // Clear the results straight away to avoid concurrency issues
101
+ delete this.topicToResults[topic];
102
+ // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
103
+ const { uniqueIdentifier, verified } = await this.verify(topic, this.topicToProofs[topic], result);
104
+ await Promise.all(this.onResultCallbacks[topic].map((callback) => callback({
105
+ uniqueIdentifier,
106
+ verified,
107
+ result,
108
+ })));
109
+ // Clear the expected proof count
110
+ delete this.topicToExpectedProofCount[topic];
111
+ }
112
+ setExpectedProofCount(topic) {
113
+ const fields = Object.keys(this.topicToConfig[topic]).filter((key) => hasRequestedAccessToField(this.topicToConfig[topic], key));
114
+ const neededCircuits = [];
115
+ // Determine which circuits are needed based on the requested fields
116
+ for (const field of fields) {
117
+ for (const key in this.topicToConfig[topic][field]) {
118
+ switch (key) {
119
+ case "eq":
120
+ case "disclose":
121
+ if (field !== "age" && !neededCircuits.includes("disclose_bytes")) {
122
+ neededCircuits.push("disclose_bytes");
123
+ }
124
+ else if (field === "age" && !neededCircuits.includes("compare_age")) {
125
+ neededCircuits.push("compare_age");
126
+ }
127
+ break;
128
+ case "gte":
129
+ case "gt":
130
+ case "lte":
131
+ case "lt":
132
+ case "range":
133
+ if (field === "age" && !neededCircuits.includes("compare_age")) {
134
+ neededCircuits.push("compare_age");
135
+ }
136
+ else if (field === "expiry_date" && !neededCircuits.includes("compare_expiry")) {
137
+ neededCircuits.push("compare_expiry");
138
+ }
139
+ else if (field === "birthdate" && !neededCircuits.includes("compare_birthdate")) {
140
+ neededCircuits.push("compare_birthdate");
141
+ }
142
+ break;
143
+ case "in":
144
+ if (field === "nationality" && !neededCircuits.includes("inclusion_check_country")) {
145
+ neededCircuits.push("inclusion_check_country");
146
+ }
147
+ break;
148
+ case "out":
149
+ if (field === "nationality" && !neededCircuits.includes("exclusion_check_country")) {
150
+ neededCircuits.push("exclusion_check_country");
151
+ }
152
+ break;
153
+ }
154
+ }
155
+ }
156
+ // From the circuits needed, determine the expected proof count
157
+ // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
158
+ // Each separate needed circuit adds 1 disclosure proof
159
+ this.topicToExpectedProofCount[topic] =
160
+ neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length;
161
+ }
77
162
  /**
78
163
  * @notice Handle an encrypted message.
79
164
  * @param request The request.
@@ -92,7 +177,7 @@ class ZKPassport {
92
177
  else if (request.method === "proof") {
93
178
  logger_1.noLogger.debug(`User generated proof`);
94
179
  // Uncompress the proof and convert it to a hex string
95
- const bytesProof = Buffer.from(request.params.proof, "base64");
180
+ const bytesProof = buffer_1.Buffer.from(request.params.proof, "base64");
96
181
  const uncompressedProof = await (0, node_gzip_1.ungzip)(bytesProof);
97
182
  // The gzip lib in the app compress the proof as ASCII
98
183
  // and since the app passes the proof as a hex string, we can
@@ -106,19 +191,44 @@ class ZKPassport {
106
191
  };
107
192
  this.topicToProofs[topic].push(processedProof);
108
193
  await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)));
194
+ // If the results were received before all the proofs were generated,
195
+ // we can handle the result now
196
+ if (this.topicToResults[topic] &&
197
+ this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
198
+ await this.handleResult(topic);
199
+ }
109
200
  }
110
201
  else if (request.method === "done") {
111
202
  logger_1.noLogger.debug(`User sent the query result`);
112
- // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
113
- const { uniqueIdentifier, verified } = await this.verify(topic, this.topicToProofs[topic], request.params);
114
- await Promise.all(this.onResultCallbacks[topic].map((callback) => callback({
115
- uniqueIdentifier,
116
- verified,
117
- result: request.params,
118
- })));
203
+ this.topicToResults[topic] = request.params;
204
+ // Make sure all the proofs have been received, otherwise we'll handle the result later
205
+ // once the proofs have all been received
206
+ if (this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
207
+ await this.handleResult(topic);
208
+ }
119
209
  }
120
210
  else if (request.method === "error") {
121
- await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(request.params.error)));
211
+ const error = request.params.error;
212
+ if (error && error === "This ID is not supported yet") {
213
+ // This means the user has an ID that is not supported yet
214
+ // So we won't receive any proofs and we can handle the result now
215
+ this.topicToExpectedProofCount[topic] = 0;
216
+ if (this.topicToResults[topic]) {
217
+ await this.handleResult(topic);
218
+ }
219
+ }
220
+ else if (error && error.startsWith("Cannot generate proof")) {
221
+ // This means one of the disclosure proofs failed to be generated
222
+ // So we need to remove one from the expected proof count
223
+ this.topicToExpectedProofCount[topic] -= 1;
224
+ // If the expected proof count is now equal to the number of proofs received
225
+ // and the results were received, we can handle the result now
226
+ if (this.topicToResults[topic] &&
227
+ this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
228
+ await this.handleResult(topic);
229
+ }
230
+ }
231
+ await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(error)));
122
232
  }
123
233
  }
124
234
  getZkPassportRequest(topic) {
@@ -171,9 +281,10 @@ class ZKPassport {
171
281
  return this.getZkPassportRequest(topic)
172
282
  },*/
173
283
  done: () => {
174
- const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
175
- const base64Service = Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
284
+ const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
285
+ const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
176
286
  const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[topic].publicKey);
287
+ this.setExpectedProofCount(topic);
177
288
  return {
178
289
  url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}`,
179
290
  requestId: topic,
@@ -204,6 +315,7 @@ class ZKPassport {
204
315
  this.topicToConfig[topic] = {};
205
316
  this.topicToService[topic] = { name, logo, purpose, scope };
206
317
  this.topicToProofs[topic] = [];
318
+ this.topicToExpectedProofCount[topic] = 0;
207
319
  this.onRequestReceivedCallbacks[topic] = [];
208
320
  this.onGeneratingProofCallbacks[topic] = [];
209
321
  this.onBridgeConnectCallbacks[topic] = [];
@@ -226,7 +338,7 @@ class ZKPassport {
226
338
  logger_1.noLogger.debug("[frontend] Received handshake:", event.data);
227
339
  this.topicToRequestReceived[topic] = true;
228
340
  this.topicToSharedSecret[topic] = await (0, encryption_1.getSharedSecret)((0, utils_2.bytesToHex)(keyPair.privateKey), data.params.pubkey);
229
- logger_1.noLogger.debug("[frontend] Shared secret:", Buffer.from(this.topicToSharedSecret[topic]).toString("hex"));
341
+ logger_1.noLogger.debug("[frontend] Shared secret:", buffer_1.Buffer.from(this.topicToSharedSecret[topic]).toString("hex"));
230
342
  const encryptedMessage = await (0, json_rpc_1.createEncryptedJsonRpcRequest)("hello", null, this.topicToSharedSecret[topic], topic);
231
343
  logger_1.noLogger.debug("[frontend] Sending encrypted message:", encryptedMessage);
232
344
  wsClient.send(JSON.stringify(encryptedMessage));
@@ -745,6 +857,16 @@ class ZKPassport {
745
857
  isCorrect = false;
746
858
  break;
747
859
  }
860
+ // Check the countryList is in ascending order
861
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
862
+ // as it is requirement in the circuit for the exclusion check to work
863
+ for (let i = 1; i < countryList.length; i++) {
864
+ if (countryList[i] < countryList[i - 1]) {
865
+ console.warn("The nationality exclusion list has not been sorted, and thus the proof cannot be trusted");
866
+ isCorrect = false;
867
+ break;
868
+ }
869
+ }
748
870
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
749
871
  }
750
872
  else if (proof.name === "inclusion_check_country") {
@@ -789,7 +911,7 @@ class ZKPassport {
789
911
  proofsToVerify = this.topicToProofs[requestId];
790
912
  if (!proofsToVerify || proofsToVerify.length < 4) {
791
913
  // It may happen that a request returns a result without proofs
792
- // Meaning the ID is supported yet by ZKPassport circuits,
914
+ // Meaning the ID is not supported yet by ZKPassport circuits,
793
915
  // so the results has to be trusted and cannot be independently verified
794
916
  return { uniqueIdentifier: undefined, verified: false };
795
917
  }
@@ -811,7 +933,7 @@ class ZKPassport {
811
933
  for (const proof of proofsToVerify) {
812
934
  const proofData = (0, utils_1.getProofData)(proof.proof, true);
813
935
  const hostedPackagedCircuit = await (0, utils_1.getHostedPackagedCircuitByName)(proof.version, proof.name);
814
- const vkeyBytes = Buffer.from(hostedPackagedCircuit.vkey, "base64");
936
+ const vkeyBytes = buffer_1.Buffer.from(hostedPackagedCircuit.vkey, "base64");
815
937
  try {
816
938
  verified = await verifier.verifyUltraHonkProof(proofData, new Uint8Array(vkeyBytes));
817
939
  }
@@ -836,8 +958,8 @@ class ZKPassport {
836
958
  */
837
959
  getUrl(requestId) {
838
960
  const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[requestId].publicKey);
839
- const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
840
- const base64Service = Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
961
+ const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
962
+ const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
841
963
  return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}`;
842
964
  }
843
965
  /**
@@ -851,6 +973,8 @@ class ZKPassport {
851
973
  delete this.topicToConfig[requestId];
852
974
  delete this.topicToSharedSecret[requestId];
853
975
  delete this.topicToProofs[requestId];
976
+ delete this.topicToExpectedProofCount[requestId];
977
+ delete this.topicToResults[requestId];
854
978
  this.onRequestReceivedCallbacks[requestId] = [];
855
979
  this.onGeneratingProofCallbacks[requestId] = [];
856
980
  this.onBridgeConnectCallbacks[requestId] = [];
@@ -1,7 +1,7 @@
1
1
  export declare function generateECDHKeyPair(): Promise<{
2
- privateKey: Uint8Array;
3
- publicKey: Uint8Array;
2
+ privateKey: import("@noble/secp256k1").Bytes;
3
+ publicKey: import("@noble/secp256k1").Bytes;
4
4
  }>;
5
- export declare function getSharedSecret(privateKey: string, publicKey: string): Promise<Uint8Array>;
6
- export declare function encrypt(message: string, sharedSecret: Uint8Array, topic: string): Promise<Uint8Array>;
5
+ export declare function getSharedSecret(privateKey: string, publicKey: string): Promise<Uint8Array<ArrayBuffer>>;
6
+ export declare function encrypt(message: string, sharedSecret: Uint8Array, topic: string): Promise<Uint8Array<ArrayBufferLike>>;
7
7
  export declare function decrypt(ciphertext: Uint8Array, sharedSecret: Uint8Array, topic: string): Promise<string>;
@@ -134,6 +134,8 @@ export declare class ZKPassport {
134
134
  private topicToRequestReceived;
135
135
  private topicToService;
136
136
  private topicToProofs;
137
+ private topicToExpectedProofCount;
138
+ private topicToResults;
137
139
  private onRequestReceivedCallbacks;
138
140
  private onGeneratingProofCallbacks;
139
141
  private onBridgeConnectCallbacks;
@@ -142,6 +144,8 @@ export declare class ZKPassport {
142
144
  private onRejectCallbacks;
143
145
  private onErrorCallbacks;
144
146
  constructor(_domain?: string);
147
+ private handleResult;
148
+ private setExpectedProofCount;
145
149
  /**
146
150
  * @notice Handle an encrypted message.
147
151
  * @param request The request.
package/dist/esm/index.js CHANGED
@@ -10,7 +10,26 @@ import { ungzip } from "node-gzip";
10
10
  //import initNoirC from '@noir-lang/noirc_abi'
11
11
  //import initACVM from '@noir-lang/acvm_js'
12
12
  import i18en from "i18n-iso-countries/langs/en.json";
13
+ import { Buffer } from "buffer/";
14
+ // If Buffer is not defined, then we use the Buffer from the buffer package
15
+ if (typeof globalThis.Buffer === "undefined") {
16
+ globalThis.Buffer = Buffer;
17
+ }
13
18
  registerLocale(i18en);
19
+ function hasRequestedAccessToField(credentialsRequest, field) {
20
+ const fieldValue = credentialsRequest[field];
21
+ const isDefined = fieldValue !== undefined && fieldValue !== null;
22
+ if (!isDefined) {
23
+ return false;
24
+ }
25
+ for (const key in fieldValue) {
26
+ if (fieldValue[key] !== undefined &&
27
+ fieldValue[key] !== null) {
28
+ return true;
29
+ }
30
+ }
31
+ return false;
32
+ }
14
33
  function normalizeCountry(country) {
15
34
  let normalizedCountry;
16
35
  const alpha3 = getAlpha3Code(country, "en");
@@ -46,6 +65,8 @@ export class ZKPassport {
46
65
  this.topicToRequestReceived = {};
47
66
  this.topicToService = {};
48
67
  this.topicToProofs = {};
68
+ this.topicToExpectedProofCount = {};
69
+ this.topicToResults = {};
49
70
  this.onRequestReceivedCallbacks = {};
50
71
  this.onGeneratingProofCallbacks = {};
51
72
  this.onBridgeConnectCallbacks = {};
@@ -64,6 +85,70 @@ export class ZKPassport {
64
85
  await Promise.all([initACVM(acvm), initNoirC(noirc)])
65
86
  this.wasmVerifierInit = true
66
87
  }*/
88
+ async handleResult(topic) {
89
+ const result = this.topicToResults[topic];
90
+ // Clear the results straight away to avoid concurrency issues
91
+ delete this.topicToResults[topic];
92
+ // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
93
+ const { uniqueIdentifier, verified } = await this.verify(topic, this.topicToProofs[topic], result);
94
+ await Promise.all(this.onResultCallbacks[topic].map((callback) => callback({
95
+ uniqueIdentifier,
96
+ verified,
97
+ result,
98
+ })));
99
+ // Clear the expected proof count
100
+ delete this.topicToExpectedProofCount[topic];
101
+ }
102
+ setExpectedProofCount(topic) {
103
+ const fields = Object.keys(this.topicToConfig[topic]).filter((key) => hasRequestedAccessToField(this.topicToConfig[topic], key));
104
+ const neededCircuits = [];
105
+ // Determine which circuits are needed based on the requested fields
106
+ for (const field of fields) {
107
+ for (const key in this.topicToConfig[topic][field]) {
108
+ switch (key) {
109
+ case "eq":
110
+ case "disclose":
111
+ if (field !== "age" && !neededCircuits.includes("disclose_bytes")) {
112
+ neededCircuits.push("disclose_bytes");
113
+ }
114
+ else if (field === "age" && !neededCircuits.includes("compare_age")) {
115
+ neededCircuits.push("compare_age");
116
+ }
117
+ break;
118
+ case "gte":
119
+ case "gt":
120
+ case "lte":
121
+ case "lt":
122
+ case "range":
123
+ if (field === "age" && !neededCircuits.includes("compare_age")) {
124
+ neededCircuits.push("compare_age");
125
+ }
126
+ else if (field === "expiry_date" && !neededCircuits.includes("compare_expiry")) {
127
+ neededCircuits.push("compare_expiry");
128
+ }
129
+ else if (field === "birthdate" && !neededCircuits.includes("compare_birthdate")) {
130
+ neededCircuits.push("compare_birthdate");
131
+ }
132
+ break;
133
+ case "in":
134
+ if (field === "nationality" && !neededCircuits.includes("inclusion_check_country")) {
135
+ neededCircuits.push("inclusion_check_country");
136
+ }
137
+ break;
138
+ case "out":
139
+ if (field === "nationality" && !neededCircuits.includes("exclusion_check_country")) {
140
+ neededCircuits.push("exclusion_check_country");
141
+ }
142
+ break;
143
+ }
144
+ }
145
+ }
146
+ // From the circuits needed, determine the expected proof count
147
+ // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
148
+ // Each separate needed circuit adds 1 disclosure proof
149
+ this.topicToExpectedProofCount[topic] =
150
+ neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length;
151
+ }
67
152
  /**
68
153
  * @notice Handle an encrypted message.
69
154
  * @param request The request.
@@ -96,19 +181,44 @@ export class ZKPassport {
96
181
  };
97
182
  this.topicToProofs[topic].push(processedProof);
98
183
  await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)));
184
+ // If the results were received before all the proofs were generated,
185
+ // we can handle the result now
186
+ if (this.topicToResults[topic] &&
187
+ this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
188
+ await this.handleResult(topic);
189
+ }
99
190
  }
100
191
  else if (request.method === "done") {
101
192
  logger.debug(`User sent the query result`);
102
- // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
103
- const { uniqueIdentifier, verified } = await this.verify(topic, this.topicToProofs[topic], request.params);
104
- await Promise.all(this.onResultCallbacks[topic].map((callback) => callback({
105
- uniqueIdentifier,
106
- verified,
107
- result: request.params,
108
- })));
193
+ this.topicToResults[topic] = request.params;
194
+ // Make sure all the proofs have been received, otherwise we'll handle the result later
195
+ // once the proofs have all been received
196
+ if (this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
197
+ await this.handleResult(topic);
198
+ }
109
199
  }
110
200
  else if (request.method === "error") {
111
- await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(request.params.error)));
201
+ const error = request.params.error;
202
+ if (error && error === "This ID is not supported yet") {
203
+ // This means the user has an ID that is not supported yet
204
+ // So we won't receive any proofs and we can handle the result now
205
+ this.topicToExpectedProofCount[topic] = 0;
206
+ if (this.topicToResults[topic]) {
207
+ await this.handleResult(topic);
208
+ }
209
+ }
210
+ else if (error && error.startsWith("Cannot generate proof")) {
211
+ // This means one of the disclosure proofs failed to be generated
212
+ // So we need to remove one from the expected proof count
213
+ this.topicToExpectedProofCount[topic] -= 1;
214
+ // If the expected proof count is now equal to the number of proofs received
215
+ // and the results were received, we can handle the result now
216
+ if (this.topicToResults[topic] &&
217
+ this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
218
+ await this.handleResult(topic);
219
+ }
220
+ }
221
+ await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(error)));
112
222
  }
113
223
  }
114
224
  getZkPassportRequest(topic) {
@@ -164,6 +274,7 @@ export class ZKPassport {
164
274
  const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
165
275
  const base64Service = Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
166
276
  const pubkey = bytesToHex(this.topicToKeyPair[topic].publicKey);
277
+ this.setExpectedProofCount(topic);
167
278
  return {
168
279
  url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}`,
169
280
  requestId: topic,
@@ -194,6 +305,7 @@ export class ZKPassport {
194
305
  this.topicToConfig[topic] = {};
195
306
  this.topicToService[topic] = { name, logo, purpose, scope };
196
307
  this.topicToProofs[topic] = [];
308
+ this.topicToExpectedProofCount[topic] = 0;
197
309
  this.onRequestReceivedCallbacks[topic] = [];
198
310
  this.onGeneratingProofCallbacks[topic] = [];
199
311
  this.onBridgeConnectCallbacks[topic] = [];
@@ -735,6 +847,16 @@ export class ZKPassport {
735
847
  isCorrect = false;
736
848
  break;
737
849
  }
850
+ // Check the countryList is in ascending order
851
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
852
+ // as it is requirement in the circuit for the exclusion check to work
853
+ for (let i = 1; i < countryList.length; i++) {
854
+ if (countryList[i] < countryList[i - 1]) {
855
+ console.warn("The nationality exclusion list has not been sorted, and thus the proof cannot be trusted");
856
+ isCorrect = false;
857
+ break;
858
+ }
859
+ }
738
860
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
739
861
  }
740
862
  else if (proof.name === "inclusion_check_country") {
@@ -779,7 +901,7 @@ export class ZKPassport {
779
901
  proofsToVerify = this.topicToProofs[requestId];
780
902
  if (!proofsToVerify || proofsToVerify.length < 4) {
781
903
  // It may happen that a request returns a result without proofs
782
- // Meaning the ID is supported yet by ZKPassport circuits,
904
+ // Meaning the ID is not supported yet by ZKPassport circuits,
783
905
  // so the results has to be trusted and cannot be independently verified
784
906
  return { uniqueIdentifier: undefined, verified: false };
785
907
  }
@@ -841,6 +963,8 @@ export class ZKPassport {
841
963
  delete this.topicToConfig[requestId];
842
964
  delete this.topicToSharedSecret[requestId];
843
965
  delete this.topicToProofs[requestId];
966
+ delete this.topicToExpectedProofCount[requestId];
967
+ delete this.topicToResults[requestId];
844
968
  this.onRequestReceivedCallbacks[requestId] = [];
845
969
  this.onGeneratingProofCallbacks[requestId] = [];
846
970
  this.onBridgeConnectCallbacks[requestId] = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zkpassport/sdk",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Privacy-preserving identity verification using passports and ID cards",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -34,13 +34,15 @@
34
34
  "@types/node-gzip": "^1.1.3",
35
35
  "@types/ws": "^8.5.12",
36
36
  "jest": "^29.7.0",
37
+ "ts-node": "^10.9.2",
37
38
  "typescript": "^5.6.2"
38
39
  },
39
40
  "dependencies": {
40
41
  "@aztec/bb.js": "^0.67.0",
41
42
  "@noble/ciphers": "^1.2.1",
42
43
  "@noble/secp256k1": "^2.2.3",
43
- "@zkpassport/utils": "^0.2.12",
44
+ "@zkpassport/utils": "^0.2.16",
45
+ "buffer": "^6.0.3",
44
46
  "i18n-iso-countries": "^7.12.0",
45
47
  "node-gzip": "^1.1.2",
46
48
  "ws": "^8.18.0"
package/src/index.ts CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  DisclosedData,
31
31
  formatName,
32
32
  getHostedPackagedCircuitByName,
33
+ Query,
33
34
  } from "@zkpassport/utils"
34
35
  import { bytesToHex } from "@noble/ciphers/utils"
35
36
  import { getWebSocketClient, WebSocketClient } from "./websocket"
@@ -40,9 +41,32 @@ import { ungzip } from "node-gzip"
40
41
  //import initNoirC from '@noir-lang/noirc_abi'
41
42
  //import initACVM from '@noir-lang/acvm_js'
42
43
  import i18en from "i18n-iso-countries/langs/en.json"
44
+ import { Buffer } from "buffer/"
45
+
46
+ // If Buffer is not defined, then we use the Buffer from the buffer package
47
+ if (typeof globalThis.Buffer === "undefined") {
48
+ globalThis.Buffer = Buffer as any
49
+ }
43
50
 
44
51
  registerLocale(i18en)
45
52
 
53
+ function hasRequestedAccessToField(credentialsRequest: Query, field: IDCredential): boolean {
54
+ const fieldValue = credentialsRequest[field as keyof Query]
55
+ const isDefined = fieldValue !== undefined && fieldValue !== null
56
+ if (!isDefined) {
57
+ return false
58
+ }
59
+ for (const key in fieldValue) {
60
+ if (
61
+ fieldValue[key as keyof typeof fieldValue] !== undefined &&
62
+ fieldValue[key as keyof typeof fieldValue] !== null
63
+ ) {
64
+ return true
65
+ }
66
+ }
67
+ return false
68
+ }
69
+
46
70
  function normalizeCountry(country: CountryName | Alpha3Code) {
47
71
  let normalizedCountry: Alpha3Code | undefined
48
72
  const alpha3 = getAlpha3Code(country, "en") as Alpha3Code | undefined
@@ -242,6 +266,8 @@ export class ZKPassport {
242
266
  { name: string; logo: string; purpose: string; scope?: string }
243
267
  > = {}
244
268
  private topicToProofs: Record<string, Array<ProofResult>> = {}
269
+ private topicToExpectedProofCount: Record<string, number> = {}
270
+ private topicToResults: Record<string, QueryResult> = {}
245
271
 
246
272
  private onRequestReceivedCallbacks: Record<string, Array<() => void>> = {}
247
273
  private onGeneratingProofCallbacks: Record<string, Array<(topic: string) => void>> = {}
@@ -275,6 +301,79 @@ export class ZKPassport {
275
301
  this.wasmVerifierInit = true
276
302
  }*/
277
303
 
304
+ private async handleResult(topic: string) {
305
+ const result = this.topicToResults[topic]
306
+ // Clear the results straight away to avoid concurrency issues
307
+ delete this.topicToResults[topic]
308
+ // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
309
+ const { uniqueIdentifier, verified } = await this.verify(
310
+ topic,
311
+ this.topicToProofs[topic],
312
+ result,
313
+ )
314
+ await Promise.all(
315
+ this.onResultCallbacks[topic].map((callback) =>
316
+ callback({
317
+ uniqueIdentifier,
318
+ verified,
319
+ result,
320
+ }),
321
+ ),
322
+ )
323
+ // Clear the expected proof count
324
+ delete this.topicToExpectedProofCount[topic]
325
+ }
326
+
327
+ private setExpectedProofCount(topic: string) {
328
+ const fields = Object.keys(this.topicToConfig[topic] as Query).filter((key) =>
329
+ hasRequestedAccessToField(this.topicToConfig[topic] as Query, key as IDCredential),
330
+ )
331
+ const neededCircuits: string[] = []
332
+ // Determine which circuits are needed based on the requested fields
333
+ for (const field of fields) {
334
+ for (const key in this.topicToConfig[topic][field as IDCredential]) {
335
+ switch (key) {
336
+ case "eq":
337
+ case "disclose":
338
+ if (field !== "age" && !neededCircuits.includes("disclose_bytes")) {
339
+ neededCircuits.push("disclose_bytes")
340
+ } else if (field === "age" && !neededCircuits.includes("compare_age")) {
341
+ neededCircuits.push("compare_age")
342
+ }
343
+ break
344
+ case "gte":
345
+ case "gt":
346
+ case "lte":
347
+ case "lt":
348
+ case "range":
349
+ if (field === "age" && !neededCircuits.includes("compare_age")) {
350
+ neededCircuits.push("compare_age")
351
+ } else if (field === "expiry_date" && !neededCircuits.includes("compare_expiry")) {
352
+ neededCircuits.push("compare_expiry")
353
+ } else if (field === "birthdate" && !neededCircuits.includes("compare_birthdate")) {
354
+ neededCircuits.push("compare_birthdate")
355
+ }
356
+ break
357
+ case "in":
358
+ if (field === "nationality" && !neededCircuits.includes("inclusion_check_country")) {
359
+ neededCircuits.push("inclusion_check_country")
360
+ }
361
+ break
362
+ case "out":
363
+ if (field === "nationality" && !neededCircuits.includes("exclusion_check_country")) {
364
+ neededCircuits.push("exclusion_check_country")
365
+ }
366
+ break
367
+ }
368
+ }
369
+ }
370
+ // From the circuits needed, determine the expected proof count
371
+ // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
372
+ // Each separate needed circuit adds 1 disclosure proof
373
+ this.topicToExpectedProofCount[topic] =
374
+ neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length
375
+ }
376
+
278
377
  /**
279
378
  * @notice Handle an encrypted message.
280
379
  * @param request The request.
@@ -311,27 +410,45 @@ export class ZKPassport {
311
410
  await Promise.all(
312
411
  this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)),
313
412
  )
413
+ // If the results were received before all the proofs were generated,
414
+ // we can handle the result now
415
+ if (
416
+ this.topicToResults[topic] &&
417
+ this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length
418
+ ) {
419
+ await this.handleResult(topic)
420
+ }
314
421
  } else if (request.method === "done") {
315
422
  logger.debug(`User sent the query result`)
316
- // Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
317
- const { uniqueIdentifier, verified } = await this.verify(
318
- topic,
319
- this.topicToProofs[topic],
320
- request.params,
321
- )
322
- await Promise.all(
323
- this.onResultCallbacks[topic].map((callback) =>
324
- callback({
325
- uniqueIdentifier,
326
- verified,
327
- result: request.params,
328
- }),
329
- ),
330
- )
423
+ this.topicToResults[topic] = request.params
424
+ // Make sure all the proofs have been received, otherwise we'll handle the result later
425
+ // once the proofs have all been received
426
+ if (this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
427
+ await this.handleResult(topic)
428
+ }
331
429
  } else if (request.method === "error") {
332
- await Promise.all(
333
- this.onErrorCallbacks[topic].map((callback) => callback(request.params.error)),
334
- )
430
+ const error = request.params.error
431
+ if (error && error === "This ID is not supported yet") {
432
+ // This means the user has an ID that is not supported yet
433
+ // So we won't receive any proofs and we can handle the result now
434
+ this.topicToExpectedProofCount[topic] = 0
435
+ if (this.topicToResults[topic]) {
436
+ await this.handleResult(topic)
437
+ }
438
+ } else if (error && error.startsWith("Cannot generate proof")) {
439
+ // This means one of the disclosure proofs failed to be generated
440
+ // So we need to remove one from the expected proof count
441
+ this.topicToExpectedProofCount[topic] -= 1
442
+ // If the expected proof count is now equal to the number of proofs received
443
+ // and the results were received, we can handle the result now
444
+ if (
445
+ this.topicToResults[topic] &&
446
+ this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length
447
+ ) {
448
+ await this.handleResult(topic)
449
+ }
450
+ }
451
+ await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(error)))
335
452
  }
336
453
  }
337
454
 
@@ -396,6 +513,7 @@ export class ZKPassport {
396
513
  "base64",
397
514
  )
398
515
  const pubkey = bytesToHex(this.topicToKeyPair[topic].publicKey)
516
+ this.setExpectedProofCount(topic)
399
517
  return {
400
518
  url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}`,
401
519
  requestId: topic,
@@ -442,7 +560,7 @@ export class ZKPassport {
442
560
  scope?: string
443
561
  topicOverride?: string
444
562
  keyPairOverride?: { privateKey: Uint8Array; publicKey: Uint8Array }
445
- }) {
563
+ }): Promise<QueryBuilder> {
446
564
  const topic = topicOverride || randomBytes(16).toString("hex")
447
565
 
448
566
  const keyPair = keyPairOverride || (await generateECDHKeyPair())
@@ -454,6 +572,7 @@ export class ZKPassport {
454
572
  this.topicToConfig[topic] = {}
455
573
  this.topicToService[topic] = { name, logo, purpose, scope }
456
574
  this.topicToProofs[topic] = []
575
+ this.topicToExpectedProofCount[topic] = 0
457
576
 
458
577
  this.onRequestReceivedCallbacks[topic] = []
459
578
  this.onGeneratingProofCallbacks[topic] = []
@@ -1108,6 +1227,18 @@ export class ZKPassport {
1108
1227
  isCorrect = false
1109
1228
  break
1110
1229
  }
1230
+ // Check the countryList is in ascending order
1231
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
1232
+ // as it is requirement in the circuit for the exclusion check to work
1233
+ for (let i = 1; i < countryList.length; i++) {
1234
+ if (countryList[i] < countryList[i - 1]) {
1235
+ console.warn(
1236
+ "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1237
+ )
1238
+ isCorrect = false
1239
+ break
1240
+ }
1241
+ }
1111
1242
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1112
1243
  } else if (proof.name === "inclusion_check_country") {
1113
1244
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
@@ -1161,7 +1292,7 @@ export class ZKPassport {
1161
1292
  proofsToVerify = this.topicToProofs[requestId]
1162
1293
  if (!proofsToVerify || proofsToVerify.length < 4) {
1163
1294
  // It may happen that a request returns a result without proofs
1164
- // Meaning the ID is supported yet by ZKPassport circuits,
1295
+ // Meaning the ID is not supported yet by ZKPassport circuits,
1165
1296
  // so the results has to be trusted and cannot be independently verified
1166
1297
  return { uniqueIdentifier: undefined, verified: false }
1167
1298
  }
@@ -1232,6 +1363,8 @@ export class ZKPassport {
1232
1363
  delete this.topicToConfig[requestId]
1233
1364
  delete this.topicToSharedSecret[requestId]
1234
1365
  delete this.topicToProofs[requestId]
1366
+ delete this.topicToExpectedProofCount[requestId]
1367
+ delete this.topicToResults[requestId]
1235
1368
  this.onRequestReceivedCallbacks[requestId] = []
1236
1369
  this.onGeneratingProofCallbacks[requestId] = []
1237
1370
  this.onBridgeConnectCallbacks[requestId] = []
package/tsconfig.json CHANGED
@@ -12,8 +12,7 @@
12
12
  "rootDir": "./src",
13
13
  "baseUrl": ".",
14
14
  "paths": {
15
- "@/*": ["./src/*"],
16
- "@/types": ["src/types/index.ts"]
15
+ "@/*": ["./src/*"]
17
16
  },
18
17
  "declaration": true,
19
18
  "isolatedModules": true,