mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
refactor utils, inputs and constants into /common
This commit is contained in:
16
app/App.tsx
16
app/App.tsx
@@ -38,7 +38,7 @@ import { config } from "@gluestack-ui/config" // Optional if you want to use def
|
||||
|
||||
// @ts-ignore
|
||||
import PassportReader from 'react-native-passport-reader';
|
||||
import {checkInputs, getFirstName} from './utils/checks';
|
||||
import {getFirstName, formatDuration, formatProof} from './utils/utils';
|
||||
import {
|
||||
DEFAULT_PNUMBER,
|
||||
DEFAULT_DOB,
|
||||
@@ -46,9 +46,17 @@ import {
|
||||
DEFAULT_ADDRESS,
|
||||
LOCAL_IP,
|
||||
} from '@env';
|
||||
import {DataHash, PassportData} from './types/passportData';
|
||||
import {arraysAreEqual, bytesToBigDecimal, dataHashesObjToArray, formatAndConcatenateDataHashes, formatDuration, formatMrz, formatProof, splitToWords} from './utils/utils';
|
||||
import {hash, toUnsignedByte} from './utils/computeEContent';
|
||||
import {DataHash, PassportData} from '../common/src/utils/types';
|
||||
import {
|
||||
hash,
|
||||
toUnsignedByte,
|
||||
checkInputs,
|
||||
bytesToBigDecimal,
|
||||
dataHashesObjToArray,
|
||||
formatAndConcatenateDataHashes,
|
||||
formatMrz,
|
||||
splitToWords
|
||||
} from '../common/src/utils/utils';
|
||||
|
||||
console.log('DEFAULT_PNUMBER', DEFAULT_PNUMBER);
|
||||
console.log('LOCAL_IP', LOCAL_IP);
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
||||
const path = require('path');
|
||||
const extraNodeModules = {
|
||||
'common': path.resolve(__dirname + '/../common'),
|
||||
};
|
||||
const watchFolders = [
|
||||
path.resolve(__dirname + '/../common')
|
||||
];
|
||||
|
||||
/**
|
||||
* Metro configuration
|
||||
@@ -6,6 +13,11 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
||||
*
|
||||
* @type {import('metro-config').MetroConfig}
|
||||
*/
|
||||
const config = {};
|
||||
const config = {
|
||||
resolver: {
|
||||
extraNodeModules,
|
||||
},
|
||||
watchFolders,
|
||||
};
|
||||
|
||||
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"@types/react-native-dotenv": "^0.2.0",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"babel-jest": "^29.2.1",
|
||||
"babel-plugin-module-resolver": "^5.0.0",
|
||||
"eslint": "^8.19.0",
|
||||
"jest": "^29.2.1",
|
||||
"metro-react-native-babel-preset": "0.76.7",
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import * as crypto from 'crypto';
|
||||
import {
|
||||
arraysAreEqual,
|
||||
dataHashesObjToArray,
|
||||
formatMrz,
|
||||
assembleMrz,
|
||||
findTimeOfSignature,
|
||||
parsePubKeyString,
|
||||
formatAndConcatenateDataHashes,
|
||||
assembleEContent,
|
||||
} from '../utils/utils';
|
||||
import * as forge from 'node-forge';
|
||||
import passportData from '../server/passportData.json';
|
||||
import {DataHash} from '../types/passportData';
|
||||
|
||||
// This script tests the whole flow from MRZ to signature
|
||||
// The passportData is imported from passportData.json written by the server
|
||||
|
||||
const mrz = passportData.mrz;
|
||||
|
||||
console.log('mrz: ', mrz);
|
||||
|
||||
// Transforms the dataHashes object into an array of arrays
|
||||
const dataHashes = passportData.dataGroupHashes as DataHash[];
|
||||
|
||||
const mrzHash = hash(formatMrz(mrz));
|
||||
|
||||
console.log('mrzHash:', mrzHash);
|
||||
console.log(
|
||||
'mrzHash === dataHashes[0][1] ?',
|
||||
arraysAreEqual(mrzHash, dataHashes[0][1] as number[]),
|
||||
);
|
||||
|
||||
const concatenatedDataHashes = formatAndConcatenateDataHashes(
|
||||
mrzHash,
|
||||
dataHashes,
|
||||
);
|
||||
|
||||
const concatenatedDataHashesHashDigest = hash(concatenatedDataHashes);
|
||||
|
||||
// check that concatenatedDataHashesHashDigest is at the right place of passportData.eContent
|
||||
const sliceOfEContent = passportData.eContent.slice(72, 72 + 32);
|
||||
|
||||
console.log(
|
||||
'Are they equal ?',
|
||||
arraysAreEqual(sliceOfEContent, concatenatedDataHashesHashDigest),
|
||||
);
|
||||
|
||||
// now let's verify the signature
|
||||
// const {modulus, exponent} = parsePubKeyString(passportData.publicKey);
|
||||
// Create the public key
|
||||
const rsa = forge.pki.rsa;
|
||||
const publicKey = rsa.setPublicKey(
|
||||
new forge.jsbn.BigInteger(passportData.modulus, 10),
|
||||
new forge.jsbn.BigInteger("10001", 16),
|
||||
);
|
||||
|
||||
// SHA-256 hash of the eContent
|
||||
const md = forge.md.sha256.create();
|
||||
md.update(forge.util.binary.raw.encode(new Uint8Array(passportData.eContent)));
|
||||
const hashOfEContent = md.digest().getBytes();
|
||||
|
||||
console.log('modulus', passportData.modulus);
|
||||
console.log('eContent', bytesToBigDecimal(passportData.eContent));
|
||||
console.log('signature', bytesToBigDecimal(passportData.encryptedDigest));
|
||||
|
||||
// Signature verification
|
||||
const signatureBytes = Buffer.from(passportData.encryptedDigest).toString(
|
||||
'binary',
|
||||
);
|
||||
const valid = publicKey.verify(hashOfEContent, signatureBytes);
|
||||
|
||||
if (valid) {
|
||||
console.log('The signature is valid.');
|
||||
} else {
|
||||
console.log('The signature is not valid.');
|
||||
}
|
||||
|
||||
function hash(bytesArray: number[]): number[] {
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(Buffer.from(bytesArray));
|
||||
return Array.from(hash.digest()).map(x => (x < 128 ? x : x - 256));
|
||||
}
|
||||
function bytesToBigDecimal(arr: number[]): string {
|
||||
let result = BigInt(0);
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
result = result * BigInt(256) + BigInt(arr[i] & 0xff);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
function hexToDecimal(hex: string): string {
|
||||
return BigInt(`0x${hex}`).toString();
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
export type MrzInfo = {
|
||||
compositeCheckDigit: string;
|
||||
dateOfBirth: string;
|
||||
dateOfBirthCheckDigit: string;
|
||||
dateOfExpiry: string;
|
||||
dateOfExpiryCheckDigit: string;
|
||||
documentCode: string;
|
||||
documentNumber: string;
|
||||
documentNumberCheckDigit: string;
|
||||
documentType: number;
|
||||
gender: string;
|
||||
issuingState: string;
|
||||
nationality: string;
|
||||
optionalData1: string;
|
||||
primaryIdentifier: string;
|
||||
secondaryIdentifier: string;
|
||||
};
|
||||
|
||||
export type DataHash = [number, number[]];
|
||||
|
||||
export type PassportData = {
|
||||
mrz: string;
|
||||
signatureAlgorithm: string;
|
||||
pubKey: {modulus?: string, curveName?: string, publicKeyQ?: string};
|
||||
dataGroupHashes: DataHash[];
|
||||
eContent: number[];
|
||||
encryptedDigest: number[];
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
export function checkInputs(
|
||||
passportNumber: string,
|
||||
dateOfBirth: string,
|
||||
dateOfExpiry: string,
|
||||
): boolean {
|
||||
if (passportNumber.length !== 9) {
|
||||
throw new Error('Passport number must be 9 characters long');
|
||||
}
|
||||
if (dateOfBirth.length !== 6) {
|
||||
throw new Error('Date of birth must be 6 characters long');
|
||||
}
|
||||
if (dateOfExpiry.length !== 6) {
|
||||
throw new Error('Date of expiry must be 6 characters long');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getFirstName(mrz: string): string {
|
||||
const names = mrz.split("<<");
|
||||
const firstName = names[1].split("<")[0].trim();
|
||||
const capitalized = firstName.charAt(0) + firstName.slice(1).toLowerCase();
|
||||
return capitalized || "Unknown";
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import {PassportData} from '../types/passportData';
|
||||
import {
|
||||
arraysAreEqual,
|
||||
assembleEContent,
|
||||
assembleMrz,
|
||||
findTimeOfSignature,
|
||||
formatAndConcatenateDataHashes,
|
||||
formatMrz,
|
||||
} from './utils';
|
||||
import {sha256} from 'js-sha256';
|
||||
|
||||
// hash logic here because the one in utils.ts only works with node
|
||||
export function hash(bytesArray: number[]) {
|
||||
let unsignedBytesArray = bytesArray.map(toUnsignedByte);
|
||||
let hash = sha256(unsignedBytesArray);
|
||||
return hexToSignedBytes(hash);
|
||||
}
|
||||
|
||||
export function hexToSignedBytes(hexString: string) {
|
||||
let bytes = [];
|
||||
for (let i = 0; i < hexString.length - 1; i += 2) {
|
||||
let byte = parseInt(hexString.substr(i, 2), 16);
|
||||
bytes.push(byte >= 128 ? byte - 256 : byte);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function toUnsignedByte(signedByte: number) {
|
||||
return signedByte < 0 ? signedByte + 256 : signedByte;
|
||||
}
|
||||
@@ -1,200 +1,8 @@
|
||||
import {DataHash, MrzInfo, PassportData} from '../types/passportData';
|
||||
|
||||
export function toUnsigned(byte: number) {
|
||||
return byte & 0xff;
|
||||
}
|
||||
|
||||
export function arraysAreEqual(array1: number[], array2: number[]) {
|
||||
return (
|
||||
array1.length === array2.length &&
|
||||
array1.every((value, index) => value === array2[index])
|
||||
);
|
||||
}
|
||||
|
||||
export function toSigned(byte: number) {
|
||||
return byte > 127 ? byte - 256 : byte;
|
||||
}
|
||||
|
||||
export function dataHashesObjToArray(dataHashes: {
|
||||
[key: string]: number[];
|
||||
}): DataHash[] {
|
||||
return Object.keys(dataHashes)
|
||||
.map(key => {
|
||||
const dataHash = dataHashes[key as keyof typeof dataHashes];
|
||||
return [Number(key), dataHash];
|
||||
})
|
||||
.sort((a, b) => (a[0] as number) - (b[0] as number)) as DataHash[];
|
||||
}
|
||||
|
||||
export function assembleMrz(mrzInfo: MrzInfo) {
|
||||
return (
|
||||
mrzInfo.documentCode +
|
||||
'<' +
|
||||
mrzInfo.issuingState +
|
||||
mrzInfo.primaryIdentifier +
|
||||
'<<' +
|
||||
mrzInfo.secondaryIdentifier +
|
||||
mrzInfo.documentNumber +
|
||||
mrzInfo.documentNumberCheckDigit +
|
||||
mrzInfo.nationality +
|
||||
mrzInfo.dateOfBirth +
|
||||
mrzInfo.dateOfBirthCheckDigit +
|
||||
mrzInfo.gender.substring(0, 1) +
|
||||
mrzInfo.dateOfExpiry +
|
||||
mrzInfo.dateOfExpiryCheckDigit +
|
||||
mrzInfo.optionalData1 +
|
||||
mrzInfo.compositeCheckDigit
|
||||
);
|
||||
}
|
||||
|
||||
export function formatMrz(mrz: string) {
|
||||
const mrzCharcodes = [...mrz].map(char => char.charCodeAt(0));
|
||||
|
||||
// console.log('mrzCharcodes:', mrzCharcodes);
|
||||
|
||||
mrzCharcodes.unshift(88); // the length of the mrz data
|
||||
mrzCharcodes.unshift(95, 31); // the MRZ_INFO_TAG
|
||||
mrzCharcodes.unshift(91); // the new length of the whole array
|
||||
mrzCharcodes.unshift(97); // the tag for DG1
|
||||
|
||||
// console.log('mrzCharcodes with tags:', mrzCharcodes);
|
||||
|
||||
return mrzCharcodes;
|
||||
}
|
||||
|
||||
// Example: [49, 15, 23, 13, 49, 57, 49, 50, 49, 54, 49, 55, 50, 50, 51, 56, 90]
|
||||
// Is "191216172238Z" - 16th December 2019, 17:22:38 UTC
|
||||
export function findTimeOfSignature(eContentDecomposed: any) {
|
||||
const timeElement: any = eContentDecomposed.elements.find(
|
||||
(element: any) => element.elements[0].identifier === '1.2.840.113549.1.9.5',
|
||||
);
|
||||
|
||||
if (!timeElement) {
|
||||
throw new Error('No time element found in eContentDecomposed');
|
||||
}
|
||||
|
||||
const timeFound = timeElement.elements[1].elements[0].time;
|
||||
|
||||
// Adding the 4 bytes of the ASN.1 tag and length
|
||||
// 49 : SET, 15 : LGT, 23 : UTCTIME, 13 : LGT
|
||||
timeFound.unshift(...[49, 15, 23, 13]);
|
||||
|
||||
return timeFound;
|
||||
}
|
||||
|
||||
export function parsePubKeyString(pubKeyString: string) {
|
||||
const modulusMatch = pubKeyString.match(/modulus: ([\w\d]+)\s*public/);
|
||||
const publicExponentMatch = pubKeyString.match(/public exponent: (\w+)/);
|
||||
|
||||
const modulus = modulusMatch ? modulusMatch[1] : null;
|
||||
const exponent = publicExponentMatch ? publicExponentMatch[1] : null;
|
||||
|
||||
// console.log('Modulus:', modulus);
|
||||
// console.log('Public Exponent:', exponent);
|
||||
|
||||
if (!modulus || !exponent) {
|
||||
throw new Error('Could not parse public key string');
|
||||
}
|
||||
|
||||
return {
|
||||
modulus,
|
||||
exponent,
|
||||
};
|
||||
}
|
||||
|
||||
export function formatAndConcatenateDataHashes(
|
||||
mrzHash: number[],
|
||||
dataHashes: DataHash[],
|
||||
) {
|
||||
// Let's replace the first array with the MRZ hash
|
||||
dataHashes.shift();
|
||||
dataHashes.unshift([1, mrzHash]);
|
||||
|
||||
let concat: number[] = []
|
||||
|
||||
// Starting sequence. Should be the same for everybody, but not sure
|
||||
concat.push(...[
|
||||
48, -126, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3, 4, 2, 1,
|
||||
48, -126, 1, 17,
|
||||
])
|
||||
|
||||
for(const dataHash of dataHashes) {
|
||||
concat.push(...[48, 37, 2, 1, dataHash[0], 4, 32, ...dataHash[1]])
|
||||
}
|
||||
|
||||
return concat;
|
||||
}
|
||||
|
||||
export function assembleEContent(
|
||||
messageDigest: number[],
|
||||
timeOfSignature: number[],
|
||||
) {
|
||||
const constructedEContent = [];
|
||||
|
||||
// Detailed description is in private file r&d.ts for now
|
||||
// First, the tag and length, assumed to be always the same
|
||||
constructedEContent.push(...[49, 102]);
|
||||
|
||||
// 1.2.840.113549.1.9.3 is RFC_3369_CONTENT_TYPE_OID
|
||||
constructedEContent.push(
|
||||
...[48, 21, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 3],
|
||||
);
|
||||
// 2.23.136.1.1.1 is ldsSecurityObject
|
||||
constructedEContent.push(...[49, 8, 6, 6, 103, -127, 8, 1, 1, 1]);
|
||||
|
||||
// 1.2.840.113549.1.9.5 is signing-time
|
||||
constructedEContent.push(
|
||||
...[48, 28, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 5],
|
||||
);
|
||||
// time of the signature
|
||||
constructedEContent.push(...timeOfSignature);
|
||||
// 1.2.840.113549.1.9.4 is RFC_3369_MESSAGE_DIGEST_OID
|
||||
constructedEContent.push(
|
||||
...[48, 47, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 4],
|
||||
);
|
||||
// TAG and length of the message digest
|
||||
constructedEContent.push(...[49, 34, 4, 32]);
|
||||
|
||||
constructedEContent.push(...messageDigest);
|
||||
return constructedEContent;
|
||||
}
|
||||
|
||||
export const toBinaryString = (byte: any) => {
|
||||
const binary = (parseInt(byte, 10) & 0xFF).toString(2).padStart(8, '0');
|
||||
return binary;
|
||||
};
|
||||
|
||||
export function splitToWords(
|
||||
number: bigint,
|
||||
wordsize: bigint,
|
||||
numberElement: bigint
|
||||
) {
|
||||
let t = number
|
||||
const words: string[] = []
|
||||
for (let i = BigInt(0); i < numberElement; ++i) {
|
||||
const baseTwo = BigInt(2)
|
||||
|
||||
words.push(`${t % BigInt(Math.pow(Number(baseTwo), Number(wordsize)))}`)
|
||||
t = BigInt(t / BigInt(Math.pow(Number(BigInt(2)), Number(wordsize))))
|
||||
}
|
||||
if (!(t == BigInt(0))) {
|
||||
throw `Number ${number} does not fit in ${(
|
||||
wordsize * numberElement
|
||||
).toString()} bits`
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
export function bytesToBigDecimal(arr: number[]): string {
|
||||
let result = BigInt(0);
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
result = result * BigInt(256) + BigInt(arr[i] & 0xff);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
export function hexToDecimal(hex: string): string {
|
||||
return BigInt(`0x${hex}`).toString();
|
||||
export function getFirstName(mrz: string): string {
|
||||
const names = mrz.split("<<");
|
||||
const firstName = names[1].split("<")[0].trim();
|
||||
const capitalized = firstName.charAt(0) + firstName.slice(1).toLowerCase();
|
||||
return capitalized || "Unknown";
|
||||
}
|
||||
|
||||
export function formatDuration(durationInMs: number) {
|
||||
|
||||
@@ -3604,6 +3604,17 @@ babel-plugin-jest-hoist@^29.5.0:
|
||||
"@types/babel__core" "^7.1.14"
|
||||
"@types/babel__traverse" "^7.0.6"
|
||||
|
||||
babel-plugin-module-resolver@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.0.tgz#2b7fc176bd55da25f516abf96015617b4f70fc73"
|
||||
integrity sha512-g0u+/ChLSJ5+PzYwLwP8Rp8Rcfowz58TJNCe+L/ui4rpzE/mg//JVX0EWBUYoxaextqnwuGHzfGp2hh0PPV25Q==
|
||||
dependencies:
|
||||
find-babel-config "^2.0.0"
|
||||
glob "^8.0.3"
|
||||
pkg-up "^3.1.0"
|
||||
reselect "^4.1.7"
|
||||
resolve "^1.22.1"
|
||||
|
||||
babel-plugin-polyfill-corejs2@^0.4.4:
|
||||
version "0.4.5"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c"
|
||||
@@ -3767,6 +3778,13 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
brace-expansion@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
|
||||
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
braces@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
@@ -4829,6 +4847,14 @@ finalhandler@1.2.0:
|
||||
statuses "2.0.1"
|
||||
unpipe "~1.0.0"
|
||||
|
||||
find-babel-config@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-2.0.0.tgz#a8216f825415a839d0f23f4d18338a1cc966f701"
|
||||
integrity sha512-dOKT7jvF3hGzlW60Gc3ONox/0rRZ/tz7WCil0bqA1In/3I8f1BctpXahRnEKDySZqci7u+dqq93sZST9fOJpFw==
|
||||
dependencies:
|
||||
json5 "^2.1.1"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
find-cache-dir@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
|
||||
@@ -4930,6 +4956,11 @@ function-bind@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
function-bind@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
|
||||
function.prototype.name@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
|
||||
@@ -5009,6 +5040,17 @@ glob@^7.1.3, glob@^7.1.4:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^8.0.3:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
|
||||
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^5.0.1"
|
||||
once "^1.3.0"
|
||||
|
||||
globals@^11.1.0:
|
||||
version "11.12.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
@@ -5103,6 +5145,13 @@ has@^1.0.3:
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hasown@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
|
||||
integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
|
||||
hermes-estree@0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.12.0.tgz#8a289f9aee854854422345e6995a48613bac2ca8"
|
||||
@@ -5299,6 +5348,13 @@ is-core-module@^2.11.0, is-core-module@^2.9.0:
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-core-module@^2.13.0:
|
||||
version "2.13.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
|
||||
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
|
||||
dependencies:
|
||||
hasown "^2.0.0"
|
||||
|
||||
is-date-object@^1.0.1:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
|
||||
@@ -5977,7 +6033,7 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||
|
||||
json5@^2.2.2:
|
||||
json5@^2.1.1, json5@^2.2.2:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
@@ -6479,6 +6535,13 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatc
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^5.0.1:
|
||||
version "5.1.6"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
|
||||
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimist@^1.2.6:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
@@ -6870,6 +6933,13 @@ pkg-dir@^4.2.0:
|
||||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
pkg-up@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
||||
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
@@ -7248,6 +7318,11 @@ require-main-filename@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||
|
||||
reselect@^4.1.7:
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
|
||||
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
@@ -7284,6 +7359,15 @@ resolve@^1.14.2, resolve@^1.20.0:
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
resolve@^1.22.1:
|
||||
version "1.22.8"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
|
||||
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
||||
dependencies:
|
||||
is-core-module "^2.13.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
resolve@^2.0.0-next.4:
|
||||
version "2.0.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660"
|
||||
|
||||
Reference in New Issue
Block a user