better support for Node.js ESM
This commit is contained in:
parent
e29be711a7
commit
94d0a78c00
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10, 12, 14, 16]
|
||||
node-version: [14, 16, 18]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
||||
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
|
||||
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
|
||||
[![Node.js CI](https://github.com/juanelas/bigint-mod-arith/workflows/Node.js%20CI/badge.svg)](https://github.com/juanelas/bigint-mod-arith/actions?query=workflow%3A%22Node.js+CI%22)
|
||||
[![Node.js CI](https://github.com/juanelas/bigint-mod-arith/workflows/build/badge.svg)](https://github.com/juanelas/bigint-mod-arith/actions?query=workflow%3A%22build%22)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/juanelas/bigint-mod-arith/badge.svg?branch=master)](https://coveralls.io/github/juanelas/bigint-mod-arith?branch=master)
|
||||
|
||||
# bigint-mod-arith
|
||||
|
|
|
@ -84,7 +84,7 @@ if (repoProvider) {
|
|||
iifeBundle = `[IIFE bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/${iifeBundlePath})`
|
||||
esmBundle = `[ESM bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/${esmBundlePath})`
|
||||
umdBundle = `[UMD bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/${umdBundlePath})`
|
||||
workflowBadget = `[![Node.js CI](https://github.com/${repoUsername}/${repoName}/workflows/Node.js%20CI/badge.svg)](https://github.com/${repoUsername}/${repoName}/actions?query=workflow%3A%22Node.js+CI%22)`
|
||||
workflowBadget = `[![Node.js CI](https://github.com/${repoUsername}/${repoName}/workflows/build/badge.svg)](https://github.com/${repoUsername}/${repoName}/actions?query=workflow%3A%22build%22)`
|
||||
coverallsBadge = `[![Coverage Status](https://coveralls.io/repos/github/${repoUsername}/${repoName}/badge.svg?branch=master)](https://coveralls.io/github/${repoUsername}/${repoName}?branch=master)`
|
||||
break
|
||||
|
|
@ -108,16 +108,16 @@ export default [
|
|||
typescriptPlugin(tsBundleOptions),
|
||||
resolve({
|
||||
browser: true,
|
||||
exportConditions: ['browser', 'module', 'import', 'default'],
|
||||
mainFields: ['browser', 'module', 'main']
|
||||
exportConditions: ['browser', 'default'],
|
||||
mainFields: ['browser', 'main']
|
||||
}),
|
||||
commonjs({
|
||||
namedExports: {
|
||||
'bn.js': ['BN'],
|
||||
'hash.js': ['hmac', 'ripemd160', 'sha256', 'sha512'],
|
||||
elliptic: ['ec'],
|
||||
'scrypt-js': ['scrypt', 'syncScrypt']
|
||||
}
|
||||
// namedExports: {
|
||||
// 'bn.js': ['BN'],
|
||||
// 'hash.js': ['hmac', 'ripemd160', 'sha256', 'sha512'],
|
||||
// elliptic: ['ec'],
|
||||
// 'scrypt-js': ['scrypt', 'syncScrypt']
|
||||
// }
|
||||
}),
|
||||
json()
|
||||
]
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
const path = require('path')
|
||||
|
||||
const puppeteer = require('puppeteer')
|
||||
const minimatch = require('minimatch')
|
||||
const glob = require('glob')
|
||||
const rootDir = path.join(__dirname, '../../..')
|
||||
|
||||
const browserTests = async (
|
||||
{
|
||||
|
@ -9,9 +14,9 @@ const browserTests = async (
|
|||
headless: false,
|
||||
devtools: true
|
||||
}
|
||||
}) => {
|
||||
const server = require('./server.js').server
|
||||
await server.init()
|
||||
}, testFiles) => {
|
||||
const server = require('./server.cjs').server
|
||||
await server.init(testFiles)
|
||||
await server.listen(serverPort)
|
||||
const browser = await puppeteer.launch(puppeteerOptions)
|
||||
const page = (await browser.pages())[0]
|
||||
|
@ -76,6 +81,24 @@ const browserTests = async (
|
|||
}
|
||||
}
|
||||
|
||||
function processedTestFiles (testFilesStr) {
|
||||
if (testFilesStr === undefined) {
|
||||
return undefined
|
||||
}
|
||||
// Let us first remove surrounding quotes in string (it gives issues in windows)
|
||||
testFilesStr = testFilesStr.replace(/^['"]/, '').replace(/['"]$/, '')
|
||||
const filenames = glob.sync(testFilesStr, { cwd: rootDir, matchBase: true })
|
||||
if (filenames.length > 0) {
|
||||
filenames.forEach(file => {
|
||||
const isTsTestFile = minimatch(file, '{test/**/*.ts,src/**/*.spec.ts}', { matchBase: true })
|
||||
if (!isTsTestFile) {
|
||||
throw new Error(`test file '${file}' not found`)
|
||||
}
|
||||
})
|
||||
}
|
||||
return filenames
|
||||
}
|
||||
|
||||
const opts = {
|
||||
// puppeteer options
|
||||
puppeteerOptions: {
|
||||
|
@ -92,6 +115,7 @@ const opts = {
|
|||
const args = process.argv.slice(2)
|
||||
if (args[0] === 'headless') {
|
||||
opts.puppeteerOptions.headless = true
|
||||
args.shift()
|
||||
}
|
||||
|
||||
browserTests(opts)
|
||||
browserTests(opts, processedTestFiles(args[0]))
|
|
@ -4,6 +4,7 @@ const fs = require('fs')
|
|||
const http = require('http')
|
||||
const path = require('path')
|
||||
const pkgJson = require('../../../package.json')
|
||||
require('dotenv').config()
|
||||
|
||||
const rollup = require('rollup')
|
||||
const resolve = require('@rollup/plugin-node-resolve').nodeResolve
|
||||
|
@ -54,10 +55,11 @@ const tsBundleOptions = {
|
|||
outDir: undefined // ignore outDir in tsconfig.json
|
||||
}
|
||||
|
||||
async function buildTests () {
|
||||
async function buildTests (testFiles) {
|
||||
// create a bundle
|
||||
const input = testFiles ?? [path.join(rootDir, pkgJson.directories.test, '**/*.ts'), path.join(rootDir, pkgJson.directories.src, '**/*.spec.ts')]
|
||||
const inputOptions = {
|
||||
input: [path.join(rootDir, pkgJson.directories.test, '**/*.ts'), path.join(rootDir, pkgJson.directories.src, '**/*.spec.ts')],
|
||||
input,
|
||||
plugins: [
|
||||
multi({ exports: true }),
|
||||
replace({
|
||||
|
@ -77,7 +79,12 @@ async function buildTests () {
|
|||
const bundle = await rollup.rollup(inputOptions)
|
||||
const { output } = await bundle.generate({ format: 'esm' })
|
||||
await bundle.close()
|
||||
return output[0].code
|
||||
let bundledCode = output[0].code
|
||||
const replacements = _getEnvVarsReplacements(bundledCode)
|
||||
for (const replacement in replacements) {
|
||||
bundledCode = bundledCode.replaceAll(replacement, replacements[replacement])
|
||||
}
|
||||
return bundledCode
|
||||
}
|
||||
|
||||
class TestServer {
|
||||
|
@ -85,8 +92,8 @@ class TestServer {
|
|||
this.server = http.createServer()
|
||||
}
|
||||
|
||||
async init () {
|
||||
const tests = await buildTests()
|
||||
async init (testFiles) {
|
||||
const tests = await buildTests(testFiles)
|
||||
this.server.on('request', function (req, res) {
|
||||
if (req.url === `/${name}.esm.js`) {
|
||||
fs.readFile(path.join(rootDir, pkgJson.directories.dist, 'bundles/esm.js'), function (err, data) {
|
||||
|
@ -148,4 +155,29 @@ class TestServer {
|
|||
}
|
||||
}
|
||||
|
||||
function _getEnvVarsReplacements (testsCode) {
|
||||
const replacements = {}
|
||||
const missingEnvVars = []
|
||||
for (const match of testsCode.matchAll(/process\.env\.(\w+)/g)) {
|
||||
const envVar = match[1]
|
||||
if (process.env[envVar] !== undefined) {
|
||||
replacements[match[0]] = '`' + process.env[envVar] + '`'
|
||||
} else {
|
||||
missingEnvVars.push(envVar)
|
||||
}
|
||||
}
|
||||
for (const match of testsCode.matchAll(/process\.env\[['"](\w+)['"]\]/g)) {
|
||||
const envVar = match[1]
|
||||
if (process.env[envVar] !== undefined) {
|
||||
replacements[match[0]] = '`' + process.env[envVar] + '`'
|
||||
} else {
|
||||
missingEnvVars.push(envVar)
|
||||
}
|
||||
}
|
||||
if (missingEnvVars.length > 0) {
|
||||
throw EvalError('The folloinwg environment variables are missing in your .env file: ' + missingEnvVars)
|
||||
}
|
||||
return replacements
|
||||
}
|
||||
|
||||
exports.server = new TestServer()
|
|
@ -3,9 +3,9 @@ const fs = require('fs')
|
|||
const path = require('path')
|
||||
|
||||
const rollup = require('rollup')
|
||||
const loadAndParseConfigFile = require('rollup/dist/loadConfigFile')
|
||||
const loadAndParseConfigFile = require('rollup/dist/loadConfigFile.js')
|
||||
|
||||
const Builder = require('./Builder.js')
|
||||
const Builder = require('./Builder.cjs')
|
||||
|
||||
const rootDir = path.join(__dirname, '../../../../')
|
||||
const pkgJson = require(path.join(rootDir, 'package.json'))
|
|
@ -4,7 +4,7 @@ const fs = require('fs')
|
|||
const ts = require('typescript')
|
||||
const JSON5 = require('json5')
|
||||
|
||||
const Builder = require('./Builder.js')
|
||||
const Builder = require('./Builder.cjs')
|
||||
|
||||
const rootDir = path.join(__dirname, '../../../../')
|
||||
const mochaTsRelativeDir = '.mocha-ts'
|
|
@ -5,8 +5,10 @@ const path = require('path')
|
|||
const chai = require('chai')
|
||||
const rimraf = require('rimraf')
|
||||
|
||||
const RollupBuilder = require('./builders/RollupBuilder.js')
|
||||
const TestsBuilder = require('./builders/TestsBuilder.js')
|
||||
const RollupBuilder = require('./builders/RollupBuilder.cjs')
|
||||
const TestsBuilder = require('./builders/TestsBuilder.cjs')
|
||||
|
||||
require('dotenv').config()
|
||||
|
||||
const rootDir = path.join(__dirname, '../../../')
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
35
docs/API.md
35
docs/API.md
|
@ -43,7 +43,7 @@ The absolute value of a
|
|||
|
||||
#### Defined in
|
||||
|
||||
[abs.ts:8](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/abs.ts#L8)
|
||||
[abs.ts:8](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/abs.ts#L8)
|
||||
|
||||
___
|
||||
|
||||
|
@ -67,7 +67,7 @@ The bit length
|
|||
|
||||
#### Defined in
|
||||
|
||||
[bitLength.ts:7](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/bitLength.ts#L7)
|
||||
[bitLength.ts:7](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/bitLength.ts#L7)
|
||||
|
||||
___
|
||||
|
||||
|
@ -78,7 +78,8 @@ ___
|
|||
An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm.
|
||||
Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).
|
||||
|
||||
**`throws`** {RangeError}
|
||||
**`Throws`**
|
||||
|
||||
This excepction is thrown if a or b are less than 0
|
||||
|
||||
#### Parameters
|
||||
|
@ -96,7 +97,7 @@ A triple (g, x, y), such that ax + by = g = gcd(a, b).
|
|||
|
||||
#### Defined in
|
||||
|
||||
[egcd.ts:18](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/egcd.ts#L18)
|
||||
[egcd.ts:18](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/egcd.ts#L18)
|
||||
|
||||
___
|
||||
|
||||
|
@ -121,7 +122,7 @@ The greatest common divisor of a and b
|
|||
|
||||
#### Defined in
|
||||
|
||||
[gcd.ts:10](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/gcd.ts#L10)
|
||||
[gcd.ts:10](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/gcd.ts#L10)
|
||||
|
||||
___
|
||||
|
||||
|
@ -146,7 +147,7 @@ The least common multiple of a and b
|
|||
|
||||
#### Defined in
|
||||
|
||||
[lcm.ts:10](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/lcm.ts#L10)
|
||||
[lcm.ts:10](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/lcm.ts#L10)
|
||||
|
||||
___
|
||||
|
||||
|
@ -171,7 +172,7 @@ Maximum of numbers a and b
|
|||
|
||||
#### Defined in
|
||||
|
||||
[max.ts:9](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/max.ts#L9)
|
||||
[max.ts:9](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/max.ts#L9)
|
||||
|
||||
___
|
||||
|
||||
|
@ -196,7 +197,7 @@ Minimum of numbers a and b
|
|||
|
||||
#### Defined in
|
||||
|
||||
[min.ts:9](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/min.ts#L9)
|
||||
[min.ts:9](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/min.ts#L9)
|
||||
|
||||
___
|
||||
|
||||
|
@ -206,7 +207,8 @@ ___
|
|||
|
||||
Modular inverse.
|
||||
|
||||
**`throws`** {RangeError}
|
||||
**`Throws`**
|
||||
|
||||
Excpeption thorwn when a does not have inverse modulo n
|
||||
|
||||
#### Parameters
|
||||
|
@ -224,7 +226,7 @@ The inverse modulo n
|
|||
|
||||
#### Defined in
|
||||
|
||||
[modInv.ts:14](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/modInv.ts#L14)
|
||||
[modInv.ts:14](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/modInv.ts#L14)
|
||||
|
||||
___
|
||||
|
||||
|
@ -234,7 +236,8 @@ ___
|
|||
|
||||
Modular exponentiation b**e mod n. Currently using the right-to-left binary method
|
||||
|
||||
**`throws`** {RangeError}
|
||||
**`Throws`**
|
||||
|
||||
Excpeption thrown when n is not > 0
|
||||
|
||||
#### Parameters
|
||||
|
@ -253,7 +256,7 @@ b**e mod n
|
|||
|
||||
#### Defined in
|
||||
|
||||
[modPow.ts:16](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/modPow.ts#L16)
|
||||
[modPow.ts:16](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/modPow.ts#L16)
|
||||
|
||||
___
|
||||
|
||||
|
@ -263,10 +266,12 @@ ___
|
|||
|
||||
Finds the smallest positive element that is congruent to a in modulo n
|
||||
|
||||
**`remarks`**
|
||||
**`Remarks`**
|
||||
|
||||
a and b must be the same type, either number or bigint
|
||||
|
||||
**`throws`** {RangeError}
|
||||
**`Throws`**
|
||||
|
||||
Excpeption thrown when n is not > 0
|
||||
|
||||
#### Parameters
|
||||
|
@ -284,4 +289,4 @@ A bigint with the smallest positive representation of a modulo n
|
|||
|
||||
#### Defined in
|
||||
|
||||
[toZn.ts:15](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/toZn.ts#L15)
|
||||
[toZn.ts:15](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/toZn.ts#L15)
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#### Defined in
|
||||
|
||||
[egcd.ts:2](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/egcd.ts#L2)
|
||||
[egcd.ts:2](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/egcd.ts#L2)
|
||||
|
||||
___
|
||||
|
||||
|
@ -26,7 +26,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[egcd.ts:3](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/egcd.ts#L3)
|
||||
[egcd.ts:3](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/egcd.ts#L3)
|
||||
|
||||
___
|
||||
|
||||
|
@ -36,4 +36,4 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[egcd.ts:4](https://github.com/juanelas/bigint-mod-arith/blob/41c8b15/src/ts/egcd.ts#L4)
|
||||
[egcd.ts:4](https://github.com/juanelas/bigint-mod-arith/blob/e29be71/src/ts/egcd.ts#L4)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
|
@ -23,6 +23,7 @@
|
|||
"engines": {
|
||||
"node": ">=10.4.0"
|
||||
},
|
||||
"type": "module",
|
||||
"types": "./types/index.d.ts",
|
||||
"main": "./dist/cjs/index.node.cjs",
|
||||
"browser": "./dist/esm/index.browser.js",
|
||||
|
@ -31,7 +32,8 @@
|
|||
".": {
|
||||
"node": {
|
||||
"require": "./dist/cjs/index.node.cjs",
|
||||
"import": "./dist/esm/index.node.js"
|
||||
"import": "./dist/esm/index.node.js",
|
||||
"module": "./dist/esm/index.node.js"
|
||||
},
|
||||
"default": "./dist/esm/index.browser.js"
|
||||
},
|
||||
|
@ -51,18 +53,21 @@
|
|||
"build": "run-s lint build:js docs",
|
||||
"build:js": "rollup -c build/rollup.config.js",
|
||||
"clean": "rimraf .nyc_output .mocha-ts coverage dist types docs",
|
||||
"coverage": "nyc --check-coverage --exclude build --exclude '{src/ts/**/*.spec.ts,test/**/*.ts,.mocha-ts/**/*}' --reporter=text --reporter=lcov node ./build/bin/mocha-ts.js --require build/testing/mocha/mocha-init.js '{src/ts/**/*.spec.ts,test/**/*.ts}'",
|
||||
"docs": "node build/build.docs.js",
|
||||
"coverage": "nyc --check-coverage --exclude build --exclude '{src/ts/**/*.spec.ts,test/**/*.ts,.mocha-ts/**/*}' --reporter=text --reporter=lcov node ./build/bin/mocha-ts.cjs --require build/testing/mocha/mocha-init.js '{src/ts/**/*.spec.ts,test/**/*.ts}'",
|
||||
"docs": "node build/build.docs.cjs",
|
||||
"git:add": "git add -A",
|
||||
"lint": "ts-standard --fix",
|
||||
"mocha": "node ./build/bin/mocha-ts.js --require build/testing/mocha/mocha-init.js ",
|
||||
"version": "run-s build test:browser-headless coverage git:add",
|
||||
"mocha-ts": "node ./build/bin/mocha-ts.cjs --require build/testing/mocha/mocha-init.cjs ",
|
||||
"mocha-ts:browser": "node build/testing/browser/index.cjs ",
|
||||
"mocha-ts:browser-headless": "node build/testing/browser/index.cjs headless ",
|
||||
"preversion": "run-s clean lint build:js coverage test:browser-headless",
|
||||
"version": "run-s docs git:add",
|
||||
"postversion": "git push --follow-tags",
|
||||
"test": "run-s test:browser-headless test:node",
|
||||
"test:browser": "node build/testing/browser/index.js",
|
||||
"test:browser-headless": "node build/testing/browser/index.js headless",
|
||||
"test:node": "npm run mocha -- '{src/ts/**/*.spec.ts,test/**/*.ts}'",
|
||||
"watch": "npm run mocha -- --watch '{src/ts/**/*.spec.ts,test/**/*.ts}'"
|
||||
"test:browser": "npm run mocha-ts:browser ",
|
||||
"test:browser-headless": "npm run mocha-ts:browser-headless ",
|
||||
"test:node": "npm run mocha-ts -- '{src/ts/**/*.spec.ts,test/**/*.ts}'",
|
||||
"watch": "npm run mocha-ts:node -- --watch '{src/ts/**/*.spec.ts,test/**/*.ts}'"
|
||||
},
|
||||
"ts-standard": {
|
||||
"env": [
|
||||
|
@ -83,30 +88,31 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^21.0.0",
|
||||
"@rollup/plugin-commonjs": "^22.0.0",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-multi-entry": "^4.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.5",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.2.0",
|
||||
"@types/chai": "^4.2.22",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"chai": "^4.3.3",
|
||||
"dotenv": "^16.0.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"glob": "^7.2.0",
|
||||
"glob": "^8.0.1",
|
||||
"json5": "^2.2.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"mocha": "^9.1.1",
|
||||
"minimatch": "^5.0.1",
|
||||
"mocha": "^10.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^15.1.0",
|
||||
"pirates": "^4.0.1",
|
||||
"puppeteer": "^12.0.1",
|
||||
"puppeteer": "^15.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.57.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"ts-standard": "^11.0.0",
|
||||
"tslib": "^2.3.1",
|
||||
"typedoc": "^0.22.0",
|
||||
"typedoc": "^0.23.0",
|
||||
"typedoc-plugin-markdown": "^3.11.0",
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue