@yuants/utils
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -1,3 +0,3 @@ | ||
import { sign } from 'tweetnacl'; | ||
import bs58 from 'bs58'; | ||
import { box, sign } from 'tweetnacl'; | ||
/** | ||
@@ -53,2 +53,84 @@ * create a new key pair | ||
}; | ||
/** | ||
* Generate a new X25519 key pair (for key exchange) | ||
* | ||
* @returns the public key and the private key (both base58 encoded) | ||
* @public | ||
*/ | ||
export const generateX25519KeyPair = () => { | ||
const { publicKey, secretKey } = box.keyPair(); | ||
return { public_key: bs58.encode(publicKey), private_key: bs58.encode(secretKey) }; | ||
}; | ||
/** | ||
* Derive a shared AES-GCM 256-bits key from a public key (other's) and a private key (your's) | ||
* @param publicKey - the other's public key to derive the shared key from (base58 encoded) | ||
* @param privateKey - the your's private key to derive the shared key from (base58 encoded) | ||
* @returns the shared key (base58 encoded) | ||
* @public | ||
*/ | ||
export const deriveSharedKey = (publicKey, privateKey) => { | ||
const publicKeyUint8Array = bs58.decode(publicKey); | ||
const privateKeyUint8Array = bs58.decode(privateKey); | ||
const sharedKey = box.before(publicKeyUint8Array, privateKeyUint8Array); | ||
return bs58.encode(sharedKey); | ||
}; | ||
/** | ||
* Convert a Uint8Array to a base58 encoded string | ||
* @param data - the data to encode | ||
* @returns the base58 encoded string | ||
* @public | ||
*/ | ||
export const encodeBase58 = (data) => { | ||
return bs58.encode(data); | ||
}; | ||
/** | ||
* Convert a base58 encoded string to a Uint8Array | ||
* @param data - the base58 data to decode | ||
* @returns the decoded Uint8Array | ||
* @public | ||
*/ | ||
export const decodeBase58 = (data) => { | ||
return bs58.decode(data); | ||
}; | ||
// isomorphic crypto both in browser and nodejs | ||
// @ts-ignore | ||
const crypto = globalThis.crypto || require('crypto'); | ||
/** | ||
* Encrypt data with AES-GCM (random IV) | ||
* @param data - the data to encrypt | ||
* @param base58_key - the key to encrypt the data with (base58 encoded) | ||
* @returns the encrypted data (IV + encrypted data) | ||
* @public | ||
*/ | ||
export const encrypt = async (data, base58_key) => { | ||
const keyUint8Array = bs58.decode(base58_key); | ||
const iv = crypto.getRandomValues(new Uint8Array(12)); // 初始化向量 | ||
const theKey = await crypto.subtle.importKey('raw', keyUint8Array, 'AES-GCM', false, [ | ||
'encrypt', | ||
'decrypt', | ||
]); | ||
const encryptedData = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, theKey, data); | ||
const combinedData = new Uint8Array(iv.length + encryptedData.byteLength); | ||
combinedData.set(iv, 0); | ||
combinedData.set(new Uint8Array(encryptedData), iv.length); | ||
return combinedData; | ||
}; | ||
/** | ||
* Decrypt data with AES-GCM | ||
* @param data - the data to decrypt (IV + encrypted data) | ||
* @param base58_key - the key to decrypt the data with (base58 encoded) | ||
* @returns the decrypted data | ||
* @public | ||
*/ | ||
export const decrypt = async (data, base58_key) => { | ||
const iv = data.slice(0, 12); | ||
const encryptedData = data.slice(12); | ||
const keyUint8Array = bs58.decode(base58_key); | ||
const theKey = await crypto.subtle.importKey('raw', keyUint8Array, 'AES-GCM', false, [ | ||
'encrypt', | ||
'decrypt', | ||
]); | ||
const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, theKey, encryptedData); | ||
return new Uint8Array(decrypted); | ||
}; | ||
//# sourceMappingURL=crypto.js.map |
@@ -37,2 +37,45 @@ /** | ||
/** | ||
* Convert a base58 encoded string to a Uint8Array | ||
* @param data - the base58 data to decode | ||
* @returns the decoded Uint8Array | ||
* @public | ||
*/ | ||
export declare const decodeBase58: (data: string) => Uint8Array; | ||
/** | ||
* Decrypt data with AES-GCM | ||
* @param data - the data to decrypt (IV + encrypted data) | ||
* @param base58_key - the key to decrypt the data with (base58 encoded) | ||
* @returns the decrypted data | ||
* @public | ||
*/ | ||
export declare const decrypt: (data: Uint8Array, base58_key: string) => Promise<Uint8Array>; | ||
/** | ||
* Derive a shared AES-GCM 256-bits key from a public key (other's) and a private key (your's) | ||
* @param publicKey - the other's public key to derive the shared key from (base58 encoded) | ||
* @param privateKey - the your's private key to derive the shared key from (base58 encoded) | ||
* @returns the shared key (base58 encoded) | ||
* @public | ||
*/ | ||
export declare const deriveSharedKey: (publicKey: string, privateKey: string) => string; | ||
/** | ||
* Convert a Uint8Array to a base58 encoded string | ||
* @param data - the data to encode | ||
* @returns the base58 encoded string | ||
* @public | ||
*/ | ||
export declare const encodeBase58: (data: Uint8Array) => string; | ||
/** | ||
* Encrypt data with AES-GCM (random IV) | ||
* @param data - the data to encrypt | ||
* @param base58_key - the key to encrypt the data with (base58 encoded) | ||
* @returns the encrypted data (IV + encrypted data) | ||
* @public | ||
*/ | ||
export declare const encrypt: (data: Uint8Array, base58_key: string) => Promise<Uint8Array>; | ||
/** | ||
* create a key pair from a secret key | ||
@@ -49,2 +92,13 @@ * @param privateKey - the private key to create the key pair from (base58 encoded) | ||
/** | ||
* Generate a new X25519 key pair (for key exchange) | ||
* | ||
* @returns the public key and the private key (both base58 encoded) | ||
* @public | ||
*/ | ||
export declare const generateX25519KeyPair: () => { | ||
public_key: string; | ||
private_key: string; | ||
}; | ||
/** | ||
* list and watch a source of items, and apply consumer to each newly added item, | ||
@@ -51,0 +105,0 @@ * the consumer should return an observable that completes when the item is fully processed, |
@@ -41,2 +41,50 @@ /** | ||
export declare const verifyMessage: (message: string, signature: string, publicKey: string) => boolean; | ||
/** | ||
* Generate a new X25519 key pair (for key exchange) | ||
* | ||
* @returns the public key and the private key (both base58 encoded) | ||
* @public | ||
*/ | ||
export declare const generateX25519KeyPair: () => { | ||
public_key: string; | ||
private_key: string; | ||
}; | ||
/** | ||
* Derive a shared AES-GCM 256-bits key from a public key (other's) and a private key (your's) | ||
* @param publicKey - the other's public key to derive the shared key from (base58 encoded) | ||
* @param privateKey - the your's private key to derive the shared key from (base58 encoded) | ||
* @returns the shared key (base58 encoded) | ||
* @public | ||
*/ | ||
export declare const deriveSharedKey: (publicKey: string, privateKey: string) => string; | ||
/** | ||
* Convert a Uint8Array to a base58 encoded string | ||
* @param data - the data to encode | ||
* @returns the base58 encoded string | ||
* @public | ||
*/ | ||
export declare const encodeBase58: (data: Uint8Array) => string; | ||
/** | ||
* Convert a base58 encoded string to a Uint8Array | ||
* @param data - the base58 data to decode | ||
* @returns the decoded Uint8Array | ||
* @public | ||
*/ | ||
export declare const decodeBase58: (data: string) => Uint8Array; | ||
/** | ||
* Encrypt data with AES-GCM (random IV) | ||
* @param data - the data to encrypt | ||
* @param base58_key - the key to encrypt the data with (base58 encoded) | ||
* @returns the encrypted data (IV + encrypted data) | ||
* @public | ||
*/ | ||
export declare const encrypt: (data: Uint8Array, base58_key: string) => Promise<Uint8Array>; | ||
/** | ||
* Decrypt data with AES-GCM | ||
* @param data - the data to decrypt (IV + encrypted data) | ||
* @param base58_key - the key to decrypt the data with (base58 encoded) | ||
* @returns the decrypted data | ||
* @public | ||
*/ | ||
export declare const decrypt: (data: Uint8Array, base58_key: string) => Promise<Uint8Array>; | ||
//# sourceMappingURL=crypto.d.ts.map |
@@ -6,5 +6,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.verifyMessage = exports.signMessage = exports.fromPrivateKey = exports.createKeyPair = void 0; | ||
exports.decrypt = exports.encrypt = exports.decodeBase58 = exports.encodeBase58 = exports.deriveSharedKey = exports.generateX25519KeyPair = exports.verifyMessage = exports.signMessage = exports.fromPrivateKey = exports.createKeyPair = void 0; | ||
const bs58_1 = __importDefault(require("bs58")); | ||
const tweetnacl_1 = require("tweetnacl"); | ||
const bs58_1 = __importDefault(require("bs58")); | ||
/** | ||
@@ -64,2 +64,90 @@ * create a new key pair | ||
exports.verifyMessage = verifyMessage; | ||
/** | ||
* Generate a new X25519 key pair (for key exchange) | ||
* | ||
* @returns the public key and the private key (both base58 encoded) | ||
* @public | ||
*/ | ||
const generateX25519KeyPair = () => { | ||
const { publicKey, secretKey } = tweetnacl_1.box.keyPair(); | ||
return { public_key: bs58_1.default.encode(publicKey), private_key: bs58_1.default.encode(secretKey) }; | ||
}; | ||
exports.generateX25519KeyPair = generateX25519KeyPair; | ||
/** | ||
* Derive a shared AES-GCM 256-bits key from a public key (other's) and a private key (your's) | ||
* @param publicKey - the other's public key to derive the shared key from (base58 encoded) | ||
* @param privateKey - the your's private key to derive the shared key from (base58 encoded) | ||
* @returns the shared key (base58 encoded) | ||
* @public | ||
*/ | ||
const deriveSharedKey = (publicKey, privateKey) => { | ||
const publicKeyUint8Array = bs58_1.default.decode(publicKey); | ||
const privateKeyUint8Array = bs58_1.default.decode(privateKey); | ||
const sharedKey = tweetnacl_1.box.before(publicKeyUint8Array, privateKeyUint8Array); | ||
return bs58_1.default.encode(sharedKey); | ||
}; | ||
exports.deriveSharedKey = deriveSharedKey; | ||
/** | ||
* Convert a Uint8Array to a base58 encoded string | ||
* @param data - the data to encode | ||
* @returns the base58 encoded string | ||
* @public | ||
*/ | ||
const encodeBase58 = (data) => { | ||
return bs58_1.default.encode(data); | ||
}; | ||
exports.encodeBase58 = encodeBase58; | ||
/** | ||
* Convert a base58 encoded string to a Uint8Array | ||
* @param data - the base58 data to decode | ||
* @returns the decoded Uint8Array | ||
* @public | ||
*/ | ||
const decodeBase58 = (data) => { | ||
return bs58_1.default.decode(data); | ||
}; | ||
exports.decodeBase58 = decodeBase58; | ||
// isomorphic crypto both in browser and nodejs | ||
// @ts-ignore | ||
const crypto = globalThis.crypto || require('crypto'); | ||
/** | ||
* Encrypt data with AES-GCM (random IV) | ||
* @param data - the data to encrypt | ||
* @param base58_key - the key to encrypt the data with (base58 encoded) | ||
* @returns the encrypted data (IV + encrypted data) | ||
* @public | ||
*/ | ||
const encrypt = async (data, base58_key) => { | ||
const keyUint8Array = bs58_1.default.decode(base58_key); | ||
const iv = crypto.getRandomValues(new Uint8Array(12)); // 初始化向量 | ||
const theKey = await crypto.subtle.importKey('raw', keyUint8Array, 'AES-GCM', false, [ | ||
'encrypt', | ||
'decrypt', | ||
]); | ||
const encryptedData = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, theKey, data); | ||
const combinedData = new Uint8Array(iv.length + encryptedData.byteLength); | ||
combinedData.set(iv, 0); | ||
combinedData.set(new Uint8Array(encryptedData), iv.length); | ||
return combinedData; | ||
}; | ||
exports.encrypt = encrypt; | ||
/** | ||
* Decrypt data with AES-GCM | ||
* @param data - the data to decrypt (IV + encrypted data) | ||
* @param base58_key - the key to decrypt the data with (base58 encoded) | ||
* @returns the decrypted data | ||
* @public | ||
*/ | ||
const decrypt = async (data, base58_key) => { | ||
const iv = data.slice(0, 12); | ||
const encryptedData = data.slice(12); | ||
const keyUint8Array = bs58_1.default.decode(base58_key); | ||
const theKey = await crypto.subtle.importKey('raw', keyUint8Array, 'AES-GCM', false, [ | ||
'encrypt', | ||
'decrypt', | ||
]); | ||
const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, theKey, encryptedData); | ||
return new Uint8Array(decrypted); | ||
}; | ||
exports.decrypt = decrypt; | ||
//# sourceMappingURL=crypto.js.map |
{ | ||
"name": "@yuants/utils", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"main": "lib/index.js", | ||
@@ -5,0 +5,0 @@ "module": "dist/index.js", |
{ | ||
"libraries/utils/CHANGELOG.json": "09d796e24921810d28d1cd6438a7c2e5102cd0dc", | ||
"libraries/utils/CHANGELOG.md": "e30eec229987d365d72435822aa061eb25440a8b", | ||
"libraries/utils/CHANGELOG.json": "f6bf6328abddd15c0ce3c65711637d67947fa891", | ||
"libraries/utils/CHANGELOG.md": "9dca7d4b98f1a8e9715d281c46d739a209fbf7d2", | ||
"libraries/utils/api-extractor.json": "62f4fd324425b9a235f0c117975967aab09ced0c", | ||
@@ -8,7 +8,7 @@ "libraries/utils/config/jest.config.json": "4bb17bde3ee911163a3edb36a6eb71491d80b1bd", | ||
"libraries/utils/config/typescript.json": "854907e8a821f2050f6533368db160c649c25348", | ||
"libraries/utils/etc/utils.api.md": "2a9b58498d1a15b6e8ba512a4920795535c96a12", | ||
"libraries/utils/package.json": "57ddf0af46c8d9e16b2d296ea62cc472c40f32fc", | ||
"libraries/utils/etc/utils.api.md": "cf8829df4ceac3044907c3d1842631b44bdb4b94", | ||
"libraries/utils/package.json": "4ba3b8b3f6dd81abb03cfb26ba029a09681f78e0", | ||
"libraries/utils/src/async-iterator-interop.test.ts": "ec6e2470d1ace812d5fac14450a2568ff8a4dfd5", | ||
"libraries/utils/src/async-iterator-interop.ts": "8dbb6150c1d75ed4d5c11be6fec6290fd8c0a561", | ||
"libraries/utils/src/crypto.ts": "e037ef3330dcd764d52ba24a280c78c336dc7faa", | ||
"libraries/utils/src/crypto.ts": "bf43e1f195c554c2e324ec171c8acbf075f7b417", | ||
"libraries/utils/src/index.ts": "72bc55afb3398495923a188333996788753db7f5", | ||
@@ -15,0 +15,0 @@ "libraries/utils/src/order-utils.ts": "e0340bdfd925c6a1943871f37638947657dd0e4c", |
@@ -248,2 +248,180 @@ { | ||
"kind": "Variable", | ||
"canonicalReference": "@yuants/utils!decodeBase58:var", | ||
"docComment": "/**\n * Convert a base58 encoded string to a Uint8Array\n *\n * @param data - the base58 data to decode\n *\n * @returns the decoded Uint8Array\n *\n * @public\n */\n", | ||
"excerptTokens": [ | ||
{ | ||
"kind": "Content", | ||
"text": "decodeBase58: " | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": "(data: string) => " | ||
}, | ||
{ | ||
"kind": "Reference", | ||
"text": "Uint8Array", | ||
"canonicalReference": "!Uint8Array:interface" | ||
} | ||
], | ||
"isReadonly": true, | ||
"releaseTag": "Public", | ||
"name": "decodeBase58", | ||
"variableTypeTokenRange": { | ||
"startIndex": 1, | ||
"endIndex": 3 | ||
} | ||
}, | ||
{ | ||
"kind": "Variable", | ||
"canonicalReference": "@yuants/utils!decrypt:var", | ||
"docComment": "/**\n * Decrypt data with AES-GCM\n *\n * @param data - the data to decrypt (IV + encrypted data)\n *\n * @param base58_key - the key to decrypt the data with (base58 encoded)\n *\n * @returns the decrypted data\n *\n * @public\n */\n", | ||
"excerptTokens": [ | ||
{ | ||
"kind": "Content", | ||
"text": "decrypt: " | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": "(data: " | ||
}, | ||
{ | ||
"kind": "Reference", | ||
"text": "Uint8Array", | ||
"canonicalReference": "!Uint8Array:interface" | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": ", base58_key: string) => " | ||
}, | ||
{ | ||
"kind": "Reference", | ||
"text": "Promise", | ||
"canonicalReference": "!Promise:interface" | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": "<" | ||
}, | ||
{ | ||
"kind": "Reference", | ||
"text": "Uint8Array", | ||
"canonicalReference": "!Uint8Array:interface" | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": ">" | ||
} | ||
], | ||
"isReadonly": true, | ||
"releaseTag": "Public", | ||
"name": "decrypt", | ||
"variableTypeTokenRange": { | ||
"startIndex": 1, | ||
"endIndex": 8 | ||
} | ||
}, | ||
{ | ||
"kind": "Variable", | ||
"canonicalReference": "@yuants/utils!deriveSharedKey:var", | ||
"docComment": "/**\n * Derive a shared AES-GCM 256-bits key from a public key (other's) and a private key (your's)\n *\n * @param publicKey - the other's public key to derive the shared key from (base58 encoded)\n *\n * @param privateKey - the your's private key to derive the shared key from (base58 encoded)\n *\n * @returns the shared key (base58 encoded)\n *\n * @public\n */\n", | ||
"excerptTokens": [ | ||
{ | ||
"kind": "Content", | ||
"text": "deriveSharedKey: " | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": "(publicKey: string, privateKey: string) => string" | ||
} | ||
], | ||
"isReadonly": true, | ||
"releaseTag": "Public", | ||
"name": "deriveSharedKey", | ||
"variableTypeTokenRange": { | ||
"startIndex": 1, | ||
"endIndex": 2 | ||
} | ||
}, | ||
{ | ||
"kind": "Variable", | ||
"canonicalReference": "@yuants/utils!encodeBase58:var", | ||
"docComment": "/**\n * Convert a Uint8Array to a base58 encoded string\n *\n * @param data - the data to encode\n *\n * @returns the base58 encoded string\n *\n * @public\n */\n", | ||
"excerptTokens": [ | ||
{ | ||
"kind": "Content", | ||
"text": "encodeBase58: " | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": "(data: " | ||
}, | ||
{ | ||
"kind": "Reference", | ||
"text": "Uint8Array", | ||
"canonicalReference": "!Uint8Array:interface" | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": ") => string" | ||
} | ||
], | ||
"isReadonly": true, | ||
"releaseTag": "Public", | ||
"name": "encodeBase58", | ||
"variableTypeTokenRange": { | ||
"startIndex": 1, | ||
"endIndex": 4 | ||
} | ||
}, | ||
{ | ||
"kind": "Variable", | ||
"canonicalReference": "@yuants/utils!encrypt:var", | ||
"docComment": "/**\n * Encrypt data with AES-GCM (random IV)\n *\n * @param data - the data to encrypt\n *\n * @param base58_key - the key to encrypt the data with (base58 encoded)\n *\n * @returns the encrypted data (IV + encrypted data)\n *\n * @public\n */\n", | ||
"excerptTokens": [ | ||
{ | ||
"kind": "Content", | ||
"text": "encrypt: " | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": "(data: " | ||
}, | ||
{ | ||
"kind": "Reference", | ||
"text": "Uint8Array", | ||
"canonicalReference": "!Uint8Array:interface" | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": ", base58_key: string) => " | ||
}, | ||
{ | ||
"kind": "Reference", | ||
"text": "Promise", | ||
"canonicalReference": "!Promise:interface" | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": "<" | ||
}, | ||
{ | ||
"kind": "Reference", | ||
"text": "Uint8Array", | ||
"canonicalReference": "!Uint8Array:interface" | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": ">" | ||
} | ||
], | ||
"isReadonly": true, | ||
"releaseTag": "Public", | ||
"name": "encrypt", | ||
"variableTypeTokenRange": { | ||
"startIndex": 1, | ||
"endIndex": 8 | ||
} | ||
}, | ||
{ | ||
"kind": "Variable", | ||
"canonicalReference": "@yuants/utils!fromPrivateKey:var", | ||
@@ -271,2 +449,24 @@ "docComment": "/**\n * create a key pair from a secret key\n *\n * @param privateKey - the private key to create the key pair from (base58 encoded)\n *\n * @returns the public key and the private key (both base58 encoded)\n *\n * @public\n */\n", | ||
"kind": "Variable", | ||
"canonicalReference": "@yuants/utils!generateX25519KeyPair:var", | ||
"docComment": "/**\n * Generate a new X25519 key pair (for key exchange)\n *\n * @returns the public key and the private key (both base58 encoded)\n *\n * @public\n */\n", | ||
"excerptTokens": [ | ||
{ | ||
"kind": "Content", | ||
"text": "generateX25519KeyPair: " | ||
}, | ||
{ | ||
"kind": "Content", | ||
"text": "() => {\n public_key: string;\n private_key: string;\n}" | ||
} | ||
], | ||
"isReadonly": true, | ||
"releaseTag": "Public", | ||
"name": "generateX25519KeyPair", | ||
"variableTypeTokenRange": { | ||
"startIndex": 1, | ||
"endIndex": 2 | ||
} | ||
}, | ||
{ | ||
"kind": "Variable", | ||
"canonicalReference": "@yuants/utils!listWatch:var", | ||
@@ -273,0 +473,0 @@ "docComment": "/**\n * list and watch a source of items, and apply consumer to each newly added item, the consumer should return an observable that completes when the item is fully processed,\n *\n * consumer will be cancelled when the item is removed.\n *\n * @param hashKey - hash key function to group items\n *\n * @param consumer - consumer function to process each item\n *\n * @returns \n *\n * @public\n */\n", |
@@ -24,2 +24,17 @@ ## API Report File for "@yuants/utils" | ||
// @public | ||
export const decodeBase58: (data: string) => Uint8Array; | ||
// @public | ||
export const decrypt: (data: Uint8Array, base58_key: string) => Promise<Uint8Array>; | ||
// @public | ||
export const deriveSharedKey: (publicKey: string, privateKey: string) => string; | ||
// @public | ||
export const encodeBase58: (data: Uint8Array) => string; | ||
// @public | ||
export const encrypt: (data: Uint8Array, base58_key: string) => Promise<Uint8Array>; | ||
// @public | ||
export const fromPrivateKey: (privateKey: string) => { | ||
@@ -31,2 +46,8 @@ public_key: string; | ||
// @public | ||
export const generateX25519KeyPair: () => { | ||
public_key: string; | ||
private_key: string; | ||
}; | ||
// @public | ||
export const listWatch: <T, K>(hashKey: (item: T) => string, consumer: (item: T) => Observable<K>) => OperatorFunction<T[], K>; | ||
@@ -33,0 +54,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
166015
2542