refactor: move build system and don't bundle code

This commit is contained in:
Derrick Hammer 2023-07-11 18:50:03 -04:00
parent 7acfd37d61
commit e2dd9a34a9
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
88 changed files with 19774 additions and 11104 deletions

View File

@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,116 +0,0 @@
name: build, test (node and browser), coverage, publish to NPM
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
jobs:
build:
name: build
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v3
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 18.x
registry-url: "https://registry.npmjs.org"
- name: install
run: npm ci
- name: build
run: npm run build
nodetests:
name: tests in Node.js
needs: [build]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16.x, 18.x, 20.x]
# When set to true, GitHub cancels all in-progress jobs if any matrix job fails.
fail-fast: false
# The maximum number of jobs that can run simultaneously. Set to 1 if you can't run tests in parallel
# max-parallel: 1
steps:
- name: Git checkout
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
registry-url: "https://registry.npmjs.org"
- name: install
run: npm i
- name: node esm tests
run: npm run test:node-esm
# env:
# VARIABLE1: ${{ secrets.VARIABLE1 }}
# VARIABLE2: ${{ secrets.VARIABLE2 }}
- name: node cjs tests
run: npm run test:node-cjs
# env:
# VARIABLE1: ${{ secrets.VARIABLE1 }}
# VARIABLE2: ${{ secrets.VARIABLE2 }}
browsertests:
needs: [build]
name: tests in browser
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v3
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 18.x
registry-url: "https://registry.npmjs.org"
- name: install
run: npm ci
- name: browser tests
run: npm run test:browser-headless
# env:
# VARIABLE1: ${{ secrets.VARIABLE1 }}
# VARIABLE2: ${{ secrets.VARIABLE2 }}
publish:
needs: [nodetests, browsertests]
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v3
- name: Install Node.js, NPM and Yarn
uses: actions/setup-node@v3
with:
node-version: "18.x"
registry-url: "https://registry.npmjs.org"
- name: install
run: npm ci
- name: coverage
run: npm run coverage
- name: send report to coveralls.io
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: NPM publish
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

13
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Build/Publish
on:
push:
branches:
- master
- develop
- develop-*
jobs:
main:
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
secrets: inherit

5
.presetterrc.json Normal file
View File

@ -0,0 +1,5 @@
{
"preset": [
"@lumeweb/node-library-preset"
]
}

View File

@ -1,3 +0,0 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -1,168 +0,0 @@
#! /usr/bin/env node
const fs = require('fs')
const path = require('path')
const glob = require('glob')
const minimatch = require('minimatch').minimatch
const rimraf = require('rimraf')
const runScript = require('../run-script.cjs')
const rootDir = path.join(__dirname, '../..')
const pkgJson = require(path.join(rootDir, 'package.json'))
const mochaTsRelativeDir = pkgJson.directories['mocha-ts']
const mochaTsDir = path.join(rootDir, mochaTsRelativeDir)
// clean .mocha-ts directory
rimraf.sync(mochaTsDir)
const semaphorePath = `${mochaTsRelativeDir}/semaphore`
const tempDir = mochaTsDir
fs.mkdirSync(tempDir, { recursive: true })
const usage = `Usage: mocha-ts [options] [spec]
mocha against ts tests and modules
Arguments:
spec One or more files, directories, or globs to test (default:
"{src/ts/**/*.spec.ts,test/**/*.ts}")
Options:
-w, --watch run in watch mode. Since mocha only supports CJS in watch
mode. This option implies -cjs as well (default: false)
-cjs, --commonjs run tests against the CJS bundle instead of the ESM one
(default: false)
-h, --help display help for command
`
function parse () {
const args = process.argv.slice(2)
const help = getBooleanOption(args, '--help', '-h')
if (help) {
console.log(usage)
process.exit()
}
const requiredFile = getOption(args, '--require')
const watch = getBooleanOption(args, '--watch', '-w')
const commonjs = getBooleanOption(args, '--commonjs', '-cjs')
if (commonjs === false && watch === true) {
console.log('ERROR: mocha in watch mode only supports commonjs')
console.log(usage)
process.exit(1)
}
let testsGlob = args.pop()
if (testsGlob === undefined) {
testsGlob = '{src/ts/**/*.spec.ts,test/**/*.ts}'
} else {
testsGlob = testsGlob.replace(/^['"]/, '').replace(/['"]$/, '') // Let us remove surrounding quotes in string (it gives issues in windows)
}
const mochaArgs = []
if (requiredFile !== '') {
mochaArgs.push('--require')
mochaArgs.push(requiredFile)
}
mochaArgs.push('--require')
mochaArgs.push('build/testing/mocha/mocha-init.cjs')
if (watch) {
mochaArgs.push('-w')
mochaArgs.push('--watch-files')
mochaArgs.push(semaphorePath)
}
if (testsGlob.substring(0, 1) === '-') {
console.log(usage)
process.exit(9)
}
let filenames = []
try {
filenames = glob.sync(testsGlob, { cwd: rootDir, matchBase: true })
} catch (error) {}
if (filenames.length === 0) {
console.error('invalid or empty glob pattern: ' + testsGlob)
console.log()
console.log(usage)
process.exit(9)
}
const testFiles = []
const jsTestFiles = []
if (filenames.length > 0) {
filenames.forEach(file => {
const isTsTestFile = minimatch(file, '{test/**/*.ts,src/**/*.spec.ts}', { matchBase: true })
if (isTsTestFile) {
testFiles.push(file)
const extension = commonjs ? 'cjs' : 'js'
jsTestFiles.push(`${mochaTsRelativeDir}/${file.slice(0, -3)}.${extension}`)
}
})
}
mochaArgs.push(...jsTestFiles)
return {
mochaArgs,
testFiles,
commonjs
}
}
const processedArgs = parse()
const commonjs = processedArgs.commonjs
const testFiles = processedArgs.testFiles
const mochaArgs = processedArgs.mochaArgs
// prepare setup for mocha (it should be written to a JSON file that will be loaded by the mocha-init.cjs)
const mochaSetup = {
testFiles,
commonjs
}
fs.writeFileSync(path.join(tempDir, 'testSetup.json'), JSON.stringify(mochaSetup, undefined, 2), { encoding: 'utf-8' })
if (commonjs) {
console.log('\x1b[33m [mocha-ts] Running tests against the CommonJS module \x1b[0m\n')
} else {
console.log('\x1b[33m [mocha-ts] Running tests against the ESM module \x1b[0m\n')
}
const rollupBuilder = require('../testing/mocha/builders/RollupBuilder.cjs').rollupBuilder
rollupBuilder.start({ commonjs, watch: false }).then(() => {
rollupBuilder.close()
const testsBuilder = require('../testing/mocha/builders/TestsBuilder.cjs').testBuilder
testsBuilder.start({ commonjs, testFiles }).then(() => {
testsBuilder.close()
// Now run mocha
runScript(path.join(rootDir, 'node_modules/mocha/bin/mocha'), mochaArgs)
})
})
function getBooleanOption (args, ...optionNames) {
let found = false
optionNames.forEach((option) => {
const index = args.indexOf(option)
if (index > -1) {
found = true
args.splice(index, 1)
}
})
return found
}
function getOption (args, option) {
const index = args.indexOf(option)
if (index > -1 && index < args.length - 2) {
return args.splice(index, 2)[1]
}
return ''
}

View File

@ -1,205 +0,0 @@
'use strict'
const fs = require('fs')
const TypeDoc = require('typedoc')
const path = require('path')
const json5 = require('json5')
const pkgJson = require('../package.json')
const rimraf = require('rimraf')
const rootDir = path.join(__dirname, '..')
const templateFilePath = path.join(rootDir, pkgJson.directories.src, 'docs/index.md')
let template = fs.readFileSync(templateFilePath, { encoding: 'utf-8' })
async function main () {
// Generate API doc with typedoc
await typedoc()
// Translate relaitive links to project's root
replaceRelativeLinks()
// Let us replace variables and badges
variableReplacements()
const readmeFile = path.join(rootDir, 'README.md')
fs.writeFileSync(readmeFile, template)
}
main()
/* ------------------------------------------------------------------------- |
| UTILITY FUNCTIONS |
| ------------------------------------------------------------------------- */
function camelise (str) {
return str.replace(/-([a-z])/g,
function (m, w) {
return w.toUpperCase()
})
}
async function typedoc () {
const app = new TypeDoc.Application()
// prepare tsconfig
const tsConfigPath = path.join(rootDir, 'tsconfig.json')
const tempTsConfigPath = path.join(rootDir, '.tsconfig.json')
const tsConfig = json5.parse(fs.readFileSync(tsConfigPath, 'utf8'))
tsConfig.include = ['src/ts/**/*', 'build/typings/**/*.d.ts']
tsConfig.exclude = ['src/**/*.spec.ts']
fs.writeFileSync(tempTsConfigPath, JSON.stringify(tsConfig, undefined, 2))
// If you want TypeDoc to load tsconfig.json / typedoc.json files
app.options.addReader(new TypeDoc.TSConfigReader())
// app.options.addReader(new TypeDoc.TypeDocReader())
app.bootstrap({
// typedoc options here
tsconfig: tempTsConfigPath,
entryPoints: ['src/ts/index.ts'],
disableSources: true,
plugin: ['typedoc-plugin-markdown'],
includeVersion: true,
entryDocument: 'API.md',
readme: 'none',
hideBreadcrumbs: true,
excludePrivate: true
})
const project = app.convert()
if (project) {
// Project may not have converted correctly
const output = path.join(rootDir, './docs')
// Rendered docs
await app.generateDocs(project, output)
}
rimraf.sync(tempTsConfigPath)
}
function getRepositoryData () {
let ret
if (typeof pkgJson.repository === 'string') {
const repodata = pkgJson.repository.split(/[:/]/)
const repoProvider = repodata[0]
if (repoProvider === 'github' || repoProvider === 'gitlab' || repoProvider === 'bitbucket') {
ret = {
repoProvider,
repoUsername: repodata[1],
repoName: repodata.slice(2).join('/')
}
}
} else if (typeof pkgJson.repository === 'object' && pkgJson.repository.type === 'git' && pkgJson.repository.url !== 'undefined') {
const regex = /(?:.+?\+)?http[s]?:\/\/(?<repoProvider>[\w._-]+)\.\w{2,3}\/(?<repoUsername>[\w._-]+)\/(?<repoName>[\w._\-/]+?)\.git/
const match = pkgJson.repository.url.match(regex)
ret = {
repoProvider: match[1],
repoUsername: match[2],
repoName: match[3],
repoDirectory: pkgJson.repository.directory
}
}
if (typeof ret === 'object') {
if (typeof pkgJson.nodeBrowserSkel === 'object' && typeof pkgJson.nodeBrowserSkel.git === 'object' && typeof pkgJson.nodeBrowserSkel.git.branch === 'string') {
ret.branch = pkgJson.nodeBrowserSkel.git.branch
} else {
ret.branch = (ret.repoProvider === 'github') ? 'main' : 'master'
}
}
return ret
}
function variableReplacements () {
const { repoProvider, repoUsername, repoName, repoDirectory, branch } = getRepositoryData() || {}
const regex = /^(?:(?<scope>@.*?)\/)?(?<name>.*)/ // We are going to take only the package name part if there is a scope, e.g. @my-org/package-name
const { name } = pkgJson.name.match(regex).groups
const camelCaseName = camelise(name)
const iifeBundlePath = pkgJson.exports['./iife-browser-bundle'] !== undefined ? path.relative('.', pkgJson.exports['./iife-browser-bundle']) : undefined
const esmBundlePath = pkgJson.exports['./esm-browser-bundle'] !== undefined ? path.relative('.', pkgJson.exports['./esm-browser-bundle']) : undefined
const umdBundlePath = pkgJson.exports['./umd-browser-bundle'] !== undefined ? path.relative('.', pkgJson.exports['./umd-browser-bundle']) : undefined
let useWorkflowBadge = false
let useCoverallsBadge = false
if (pkgJson.nodeBrowserSkel !== undefined && pkgJson.nodeBrowserSkel.badges !== undefined) {
if (pkgJson.nodeBrowserSkel.badges.workflow === true) {
useWorkflowBadge = true
}
if (pkgJson.nodeBrowserSkel.badges.coveralls === true) {
useCoverallsBadge = true
}
}
let iifeBundle, esmBundle, umdBundle, workflowBadge, coverallsBadge
if (repoProvider) {
switch (repoProvider) {
case 'github':
iifeBundle = iifeBundlePath !== undefined ? `[IIFE bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/${branch}/${repoDirectory !== undefined ? repoDirectory + '/' : ''}${iifeBundlePath})` : undefined
esmBundle = esmBundlePath !== undefined ? `[ESM bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/${branch}/${repoDirectory !== undefined ? repoDirectory + '/' : ''}${esmBundlePath})` : undefined
umdBundle = umdBundlePath !== undefined ? `[UMD bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/${branch}/${repoDirectory !== undefined ? repoDirectory + '/' : ''}${umdBundlePath})` : undefined
workflowBadge = useWorkflowBadge ? `[![Node.js CI](https://github.com/${repoUsername}/${repoName}/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/${repoUsername}/${repoName}/actions/workflows/build-and-test.yml)` : undefined
coverallsBadge = useCoverallsBadge ? `[![Coverage Status](https://coveralls.io/repos/github/${repoUsername}/${repoName}/badge.svg?branch=${branch})](https://coveralls.io/github/${repoUsername}/${repoName}?branch=${branch})` : undefined
break
case 'gitlab':
iifeBundle = iifeBundlePath !== undefined ? `[IIFE bundle](https://gitlab.com/${repoUsername}/${repoName}/-/raw/${branch}/${repoDirectory !== undefined ? repoDirectory + '/' : ''}${iifeBundlePath}?inline=false)` : undefined
esmBundle = esmBundlePath !== undefined ? `[ESM bundle](https://gitlab.com/${repoUsername}/${repoName}/-/raw/${branch}/${repoDirectory !== undefined ? repoDirectory + '/' : ''}${esmBundlePath}?inline=false)` : undefined
umdBundle = umdBundlePath !== undefined ? `[UMD bundle](https://gitlab.com/${repoUsername}/${repoName}/-/raw/${branch}/${repoDirectory !== undefined ? repoDirectory + '/' : ''}${umdBundlePath}?inline=false)` : undefined
break
default:
break
}
}
template = template
.replace(/\{\{PKG_NAME\}\}/g, pkgJson.name)
.replace(/\{\{PKG_LICENSE\}\}/g, pkgJson.license.replace('-', '_'))
.replace(/\{\{PKG_DESCRIPTION\}\}/g, pkgJson.description)
.replace(/\{\{PKG_CAMELCASE\}\}/g, camelCaseName)
.replace(/\{\{IIFE_BUNDLE\}\}/g, iifeBundle || 'IIFE bundle')
.replace(/\{\{ESM_BUNDLE\}\}/g, esmBundle || 'ESM bundle')
.replace(/\{\{UMD_BUNDLE\}\}/g, umdBundle || 'UMD bundle')
if (repoProvider && repoProvider === 'github') {
template = template.replace(/\{\{GITHUB_ACTIONS_BADGES\}\}\n/gs, (workflowBadge ? `${workflowBadge}\n` : '') + (coverallsBadge ? `${coverallsBadge}\n` : ''))
} else {
template = template.replace(/\{\{GITHUB_ACTIONS_BADGES\}\}\n/gs, '')
}
}
function replaceRelativeLinks () {
const replacements = []
const relativePathRegex = /(\[[\w\s\d]+\]\()(?!(?:http:\/\/)|(?:https:\/\/))([\w\d;,/?:@&=+$-_.!~*'()\\#]+)\)/g
const matches = template.matchAll(relativePathRegex)
if (matches) {
for (const match of matches) {
const index = (match.index ?? 0) + match[1].length
const filepath = match[2]
if (!path.isAbsolute(filepath)) {
const absoluteFilePath = path.join(path.dirname(templateFilePath), filepath)
if (!fs.existsSync(absoluteFilePath)) {
console.warn(`File ${absoluteFilePath} is linked in your index.md but it does not exist. Ignoring`)
} else {
const replacement = path.relative(rootDir, absoluteFilePath)
replacements.push({ index, length: filepath.length, replacement })
}
}
}
const sortedReplacements = replacements.sort((a, b) => a.index - b.index)
let ret = ''
let index = 0
for (const replacement of sortedReplacements) {
ret += template.slice(index, replacement.index)
ret += replacement.replacement
index = replacement.index + replacement.length
}
ret += template.slice(index)
template = ret
}
}

View File

@ -1,52 +0,0 @@
import { mkdirSync, writeFileSync } from 'fs'
import ts from 'typescript'
import { join, dirname, extname } from 'path'
import { sync } from 'rimraf'
import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const { readJsonConfigFile, sys, parseJsonSourceFileConfigFileContent, createCompilerHost, createProgram } = ts
const rootDir = join(__dirname, '..')
const srcFile = join(rootDir, 'src/ts/index.ts')
const tsConfigPath = join(rootDir, 'tsconfig.json')
const configFile = readJsonConfigFile(tsConfigPath, (file) => {
return sys.readFile(file)
})
const tsConfig = parseJsonSourceFileConfigFileContent(configFile, sys, dirname(tsConfigPath))
export const compile = (outDir) => {
const compilerOptions = {
...tsConfig.options,
removeComments: false,
declaration: true,
declarationMap: true,
emitDeclarationOnly: true,
outDir
}
const host = createCompilerHost(compilerOptions)
host.writeFile = (fileName, contents) => {
mkdirSync(dirname(fileName), { recursive: true })
writeFileSync(fileName, contents)
// we also write the .d.cts types
let fileName2 = ''
if (extname(fileName) === '.ts') {
fileName2 = fileName.slice(0, -2) + 'cts'
} else { // ext is .d.ts.map
fileName2 = fileName.slice(0, -6) + 'cts.map'
}
writeFileSync(fileName2, contents)
}
// Clear the types dir
sync(outDir)
// Prepare and emit the d.ts files
const program = createProgram([srcFile], compilerOptions, host)
program.emit()
}

View File

@ -1,240 +0,0 @@
'use strict'
import commonjs from '@rollup/plugin-commonjs'
import inject from '@rollup/plugin-inject'
import json from '@rollup/plugin-json'
import { nodeResolve as resolve } from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'
import terser from '@rollup/plugin-terser'
import rollupPluginTs from '@rollup/plugin-typescript'
import { existsSync, readFileSync } from 'fs'
import { builtinModules } from 'module'
import { join } from 'path'
import dts from 'rollup-plugin-dts'
import { compile } from './rollup-plugin-dts.js'
import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const rootDir = join(__dirname, '..')
const pkgJson = JSON.parse(readFileSync(join(rootDir, 'package.json')))
const pkgJsonLock = JSON.parse(readFileSync(join(rootDir, 'package-lock.json')))
const srcDir = join(rootDir, 'src', 'ts')
const tsConfigPath = join(rootDir, 'tsconfig.json')
function camelise (str) {
return str.replace(/-([a-z])/g,
function (m, w) {
return w.toUpperCase()
})
}
function isDevDependency (moduleName) {
const packageEntry = pkgJsonLock.packages['node_modules/' + moduleName]
return (packageEntry ?? {}).dev === true
}
const regex = /^(?:(?<scope>@.*?)\/)?(?<name>.*)/ // We are going to take only the package name part if there is a scope, e.g. @my-org/package-name
const { name } = pkgJson.name.match(regex).groups
const pkgCamelisedName = camelise(name)
const input = join(srcDir, 'index.ts')
if (existsSync(input) !== true) throw new Error('The entry point should be index.ts')
const tsPluginOptions = {
tsconfig: tsConfigPath,
outDir: undefined,
include: ['src/ts/**/*', 'build/typings/**/*.d.ts'],
exclude: ['src/**/*.spec.ts']
}
function compileDts (outDir) {
return {
name: 'compile-dts',
closeBundle () {
compile(outDir)
}
}
}
function resolveOnly (module) { // if a dev dependency is imported we will resolve it so that the dist modules always work
const moduleNameMatch = module.match(/^(?:@[a-z0-9_-]+\/)?(?:node:)?[a-z0-9_-]+/)
if (moduleNameMatch === null || moduleNameMatch.length !== 1) {
return false
}
const moduleName = moduleNameMatch[0].replace(/^node:/, '')
// don't resolve if it is a native module
if (builtinModules.includes(moduleName)) {
return false
}
if (isDevDependency(moduleName)) {
console.warn(`\x1b[33m⚠ WARM: dev dependency \x1b[0m${module}\x1b[33m being bundled. Should it be a dependency instead?\x1b[0m`)
return true
}
return false
}
const tmpDeclarationsDir = join(rootDir, '.types')
export default [
{ // Browser ESM
input,
output: [
{
file: join(rootDir, pkgJson.exports['.'].default.default),
format: 'es',
plugins: [
terser()
]
}
],
plugins: [
replace({
IS_BROWSER: true,
environment: 'browser',
_MODULE_TYPE: "'ESM'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
preventAssignment: true
}),
rollupPluginTs(tsPluginOptions),
commonjs({ extensions: ['.js', '.cjs', '.jsx', '.cjsx'] }),
json(),
resolve({
browser: true,
exportConditions: ['browser', 'default'],
mainFields: ['browser', 'module', 'main'],
resolveOnly
})
]
},
{ // Browser bundles
input,
output: [
{
file: join(rootDir, pkgJson.exports['./esm-browser-bundle-nomin']),
format: 'es'
},
{
file: join(rootDir, pkgJson.exports['./esm-browser-bundle']),
format: 'es',
plugins: [terser()]
},
{
file: join(rootDir, pkgJson.exports['./iife-browser-bundle']),
format: 'iife',
name: pkgCamelisedName,
plugins: [terser()]
},
{
file: join(rootDir, pkgJson.exports['./umd-browser-bundle']),
format: 'umd',
name: pkgCamelisedName,
plugins: [terser()]
}
],
plugins: [
replace({
IS_BROWSER: true,
environment: 'browser',
_MODULE_TYPE: "'BUNDLE'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
preventAssignment: true
}),
rollupPluginTs({
...tsPluginOptions,
sourceMap: false
}),
commonjs({ extensions: ['.js', '.cjs', '.jsx', '.cjsx'] }),
json(),
resolve({ browser: true })
]
},
{ // Node CJS
input,
output: [
{
file: join(rootDir, pkgJson.exports['.'].node.require.default),
format: 'cjs',
exports: 'auto',
interop: 'auto',
dynamicImportInCjs: false,
plugins: [terser()]
}
],
plugins: [
replace({
'await import(': 'require(',
delimiters: ['', ''],
preventAssignment: true
}),
replace({
IS_BROWSER: false,
environment: 'nodejs',
_MODULE_TYPE: "'CJS'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
preventAssignment: true
}),
rollupPluginTs(tsPluginOptions),
inject({
crypto: ['crypto', 'webcrypto']
}),
commonjs({ extensions: ['.js', '.cjs', '.jsx', '.cjsx'] }),
json(),
resolve({
exportConditions: ['node'],
resolveOnly
})
]
},
{ // Node ESM and type declarations
input,
output: [
{
file: join(rootDir, pkgJson.exports['.'].node.import.default),
format: 'es',
plugins: [terser()]
}
],
plugins: [
replace({
IS_BROWSER: false,
environment: 'nodejs',
_MODULE_TYPE: "'ESM'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
__filename: 'fileURLToPath(import.meta.url)',
__dirname: 'fileURLToPath(new URL(\'.\', import.meta.url))',
preventAssignment: true
}),
rollupPluginTs(tsPluginOptions),
compileDts(tmpDeclarationsDir),
inject({
crypto: ['crypto', 'webcrypto'],
fileURLToPath: ['url', 'fileURLToPath']
}),
commonjs({ extensions: ['.js', '.cjs', '.jsx', '.cjsx'] }),
json(),
resolve({
exportConditions: ['node'],
resolveOnly
})
]
},
{
input: join(tmpDeclarationsDir, 'index.d.ts'),
output: [{ file: 'dist/index.d.ts', format: 'es' }],
plugins: [
dts({
respectExternal: true
})
],
external: (module) => {
if (/^[./]/.test(module)) {
return false
}
return !resolveOnly(module)
}
}
]

View File

@ -1,26 +0,0 @@
const childProcess = require('child_process')
const rootDir = require('path').join(__dirname, '../')
function runScript (scriptPath, args) {
return new Promise((resolve, reject) => {
const cmd = childProcess.fork(scriptPath, args, {
cwd: rootDir
})
cmd.on('error', (error) => {
reject(error)
})
// execute the callback once the process has finished running
cmd.on('exit', function (code) {
if (code !== 0) {
const error = new Error('exit code ' + code)
reject(error)
}
resolve()
})
})
}
module.exports = runScript

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

View File

@ -1,120 +0,0 @@
const path = require('path')
const puppeteer = require('puppeteer')
const minimatch = require('minimatch').minimatch
const glob = require('glob')
const rootDir = path.join(__dirname, '../../..')
const pkgJson = require(path.join(rootDir, 'package.json'))
const browserTests = async (
{
logWarnings = false,
serverPort = 38000,
keepServerRunning = false,
puppeteerOptions = {
headless: false,
devtools: true
}
}, 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]
page.on('console', function (message) {
const ignore = message.type() === 'warning' && !logWarnings
if (ignore) return
let text = (message.args().length > 0) ? message.args()[0].remoteObject().value : message.text()
const args = []
if (message.args() !== undefined && message.args().length > 1) {
for (let i = 1; i < message.args().length; i++) {
args.push(message.args()[i].remoteObject().value)
}
}
if (message.type() === 'error' && message.location()) {
text = `${message.location().url} : ${text}`
}
let consoleType = 'log'
switch (message.type()) {
case 'error':
consoleType = 'error'
break
case 'warning':
consoleType = 'warn'
break
default:
break
}
console[consoleType](text, ...args)
})
page.on('error', function (err) { page.emit(new Error(err)) })
page.on('close', async () => {
await close()
})
page.goto('http://localhost:38000/').then(async () => {
const watchDog = page.waitForFunction('_mocha.state === \'stopped\'', { timeout: 0 })
await watchDog.catch(async (reason) => {
console.error(reason)
})
if (puppeteerOptions.headless === 'new') {
await close()
}
}).catch(async (reason) => {
console.error(reason)
})
async function close () {
console.log('Closing browser tests...')
await browser.close().catch(() => {})
if (keepServerRunning !== true) {
await server.close().catch(() => {})
}
}
}
function processedTestFiles (testFilesStr) {
if (testFilesStr === undefined) {
testFilesStr = [pkgJson.directories.test + '/**/*.ts', pkgJson.directories.src + '/**/*.spec.ts']
} else {
// 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) {
throw new Error('no test files found for ' + testFilesStr)
} else {
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: {
headless: false,
devtools: true,
// slowMo: 100,
timeout: 0
},
logWarnings: false, // log warnings in Node console (usually not needed)
keepServerRunning: false, // keep server running until manually closed with ctrl-c. In combination with puppeteerOptions.headless (or just connecting any browser to the test page) allows debugging in browser
serverPort: 38000
}
const args = process.argv.slice(2)
if (args[0] === 'headless') {
opts.puppeteerOptions.headless = 'new'
args.shift()
}
browserTests(opts, processedTestFiles(args[0]))

View File

@ -1,211 +0,0 @@
'use strict'
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
const replace = require('@rollup/plugin-replace')
const typescriptPlugin = require('@rollup/plugin-typescript')
const commonjs = require('@rollup/plugin-commonjs')
const json = require('@rollup/plugin-json')
const multi = require('@rollup/plugin-multi-entry')
const runScript = require('../../run-script.cjs')
const rootDir = path.join(__dirname, '..', '..', '..')
const regex = /^(?:(?<scope>@.*?)\/)?(?<name>.*)/ // We are going to take only the package name part if there is a scope, e.g. @my-org/package-name
const { name } = pkgJson.name.match(regex).groups
const indexHtml = `<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>${name}</title>
<script src="/mocha.js"></script>
<script src="/chai.js"></script>
</head>
<body>
</body>
<div id="mocha"></div>
<script>
mocha.setup({
ui: 'bdd',
reporter: 'spec',
color: 'true',
timeout: 90000
})
</script>
<script type="module">
import './tests.js'
window._mocha = mocha.run()
</script>
</html>`
const tsBundleOptions = {
tsconfig: path.join(rootDir, 'tsconfig.json'),
outDir: undefined, // ignore outDir in tsconfig.json
sourceMap: false
// include: ['src/ts/**/*', 'build/typings/**/*.d.ts']
}
async function buildTests (testFiles) {
// create a bundle
const inputOptions = {
input: testFiles,
plugins: [
multi(),
replace({
IS_BROWSER: true,
_MODULE_TYPE: "'ESM'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
preventAssignment: true
}),
typescriptPlugin(tsBundleOptions),
commonjs({ extensions: ['.js', '.cjs', '.jsx', '.cjsx'] }),
json(),
resolve({ browser: true }),
replace({
'#pkg': `/${name}.esm.js`,
delimiters: ['', ''],
preventAssignment: true
})
],
external: [`/${name}.esm.js`]
}
const bundle = await rollup.rollup(inputOptions)
const { output } = await bundle.generate({ format: 'es' })
await bundle.close()
let bundledCode = output[0].code
const replacements = _getEnvVarsReplacements(bundledCode)
for (const replacement in replacements) {
const regExp = new RegExp(replacement, 'g')
bundledCode = bundledCode.replace(regExp, replacements[replacement])
}
return bundledCode
}
class TestServer {
constructor () {
this.server = http.createServer()
}
async init (testFiles) {
/** Let us first check if the necessary files are built, and if not, build */
if (!fs.existsSync(pkgJson.exports['./esm-browser-bundle-nomin'])) {
await runScript(path.join(rootDir, 'node_modules', '.bin', 'rollup'), ['-c', 'build/rollup.config.js'])
}
const tests = await buildTests(testFiles)
this.server.on('request', function (req, res) {
if (req.url === `/${name}.esm.js`) {
fs.readFile(path.join(rootDir, pkgJson.exports['./esm-browser-bundle-nomin']), function (err, data) {
if (err) {
res.writeHead(404)
res.end(JSON.stringify(err))
return
}
res.writeHead(200, { 'Content-Type': 'text/javascript' })
res.end(data)
})
} else if (req.url === '/index.html' || req.url === '/') {
res.writeHead(200)
res.end(indexHtml)
} else if (req.url === '/tests.js') {
res.writeHead(200, { 'Content-Type': 'text/javascript' })
res.end(tests)
} else if (req.url === '/mocha.js') {
fs.readFile(path.join(rootDir, 'node_modules/mocha/mocha.js'), function (err, data) {
if (err) {
res.writeHead(404)
res.end(JSON.stringify(err))
return
}
res.writeHead(200, { 'Content-Type': 'text/javascript' })
res.end(data)
})
} else if (req.url === '/mocha.js.map') {
fs.readFile(path.join(rootDir, 'node_modules/mocha/mocha.js.map'), function (err, data) {
if (err) {
res.writeHead(404)
res.end(JSON.stringify(err))
return
}
res.writeHead(200, { 'Content-Type': 'text/javascript' })
res.end(data)
})
} else if (req.url === '/chai.js' || req.url === '/chai') {
fs.readFile(path.join(rootDir, 'node_modules/chai/chai.js'), function (err, data) {
if (err) {
res.writeHead(404)
res.end(JSON.stringify(err))
return
}
res.writeHead(200, { 'Content-Type': 'text/javascript' })
res.end(data)
})
} else if (req.url === '/favicon.ico') {
fs.readFile(path.join(__dirname, 'favicon.ico'), function (err, data) {
if (err) {
res.writeHead(404)
res.end(JSON.stringify(err))
return
}
res.writeHead(200, { 'Content-Type': 'application/ico' })
res.end(data)
})
} else {
res.writeHead(404)
res.end()
}
})
}
listen (port = 38080) {
return new Promise((resolve, reject) => {
this.server.listen(port, error => {
if (error) return reject(error)
console.log(`Testing server listenning at http://localhost:${port}`)
return resolve()
})
})
}
close () {
return new Promise((resolve, reject) => {
this.server.close(error => (error) ? reject(error) : resolve())
})
}
}
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 {
replacements[match[0]] = undefined
}
}
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 {
replacements[match[0]] = undefined
}
}
if (missingEnvVars.length > 0) {
console.warn('The following environment variables are missing in your .env file and will be replaced with "undefined": ' + [...(new Set(missingEnvVars)).values()].join(', '))
}
return replacements
}
exports.server = new TestServer()

View File

@ -1,62 +0,0 @@
const EventEmitter = require('events')
const fs = require('fs')
const path = require('path')
module.exports = class Builder extends EventEmitter {
constructor (semaphoreFile, name = 'builder') {
super()
this.name = name
fs.mkdirSync(path.dirname(semaphoreFile), { recursive: true })
this.semaphoreFile = semaphoreFile
if (!fs.existsSync(this.semaphoreFile)) {
fs.writeFileSync(this.semaphoreFile, '', { encoding: 'utf8' })
}
this._ready = false
this.on('message', (...message) => {
if (message !== undefined) {
console.log(`\x1b[33m [${this.name}]`, ...message, '\x1b[0m')
}
})
this.on('error', (...error) => {
if (error !== undefined) {
console.error(`\x1b[31m❗ [${this.name}]`, ...error, '\x1b[0m')
}
})
this.on('ready', (updateSemaphore = true) => {
const now = Date.now()
if (updateSemaphore) {
fs.utimesSync(this.semaphoreFile, now, now)
}
this._ready = true
})
this.on('busy', () => {
this._ready = false
})
}
ready () {
return new Promise(resolve => {
if (this._ready === true) return resolve()
this.once('ready', () => {
resolve()
})
})
}
async start () {
}
async close () {}
clean () {
fs.rmSync(this.semaphoreFile, { force: true })
}
}

View File

@ -1,158 +0,0 @@
const EventEmitter = require('events')
const fs = require('fs')
const path = require('path')
const rollup = require('rollup')
const loadAndParseConfigFile = require('rollup/loadConfigFile').loadConfigFile
const Builder = require('./Builder.cjs')
const rootDir = path.join(__dirname, '../../../../')
const pkgJson = require(path.join(rootDir, 'package.json'))
const mochaTsRelativeDir = pkgJson.directories['mocha-ts']
const mochaTsDir = path.join(rootDir, mochaTsRelativeDir)
class RollupBuilder extends Builder {
constructor ({ name, configPath, tempDir }) {
super(path.join(tempDir, 'semaphore'), name)
this.tempDir = tempDir
this.configPath = configPath
this.firstBuild = true
}
async start ({ watch = false, commonjs = false }) {
await super.start()
this.watch = watch
this.commonjs = commonjs
this.watchedModule = commonjs ? pkgJson.exports['.'].node.require.default : pkgJson.exports['.'].node.import.default
const { options } = await loadAndParseConfigFile(this.configPath)
// Instead of compiling all the outputs let us just take the one we are using with mocha (either cjs or esm)
const rollupOptions = options.filter(bundle => {
const file = (bundle.output[0].dir !== undefined)
? path.join(bundle.output[0].dir, bundle.output[0].entryFileNames)
: bundle.output[0].file
return file === path.join(rootDir, this.watchedModule)
})[0]
if (rollupOptions.output.length > 1) {
rollupOptions.output = rollupOptions.output[0]
}
rollupOptions.output[0].sourcemap = 'inline'
rollupOptions.output[0].sourcemapExcludeSources = true
this.builder = new RollupBundler({ rollupOptions, watch: this.watch, watchedModule: this.watchedModule })
this.builder.on('event', event => {
let updateSemaphore = true
switch (event.code) {
case 'START':
this.emit('busy')
if (this.firstBuild === true) {
this.emit('message', 'building your module...')
} else {
this.emit('message', 'file changes detected. Rebuilding module files...')
}
break
case 'BUNDLE_END':
if (event.result) event.result.close()
break
case 'END':
if (event.result) event.result.close()
// fs.mkdirSync(path.join(this.tempDir, path.dirname(this.watchedModule)), { recursive: true })
// // console.log(path.join(this.tempDir, path.dirname(this.watchedModule)))
// fs.copyFileSync(this.watchedModule, path.join(this.tempDir, this.watchedModule))
if (this.firstBuild) {
this.firstBuild = false
updateSemaphore = false
}
this.emit('ready', updateSemaphore)
break
case 'ERROR':
if (event.result) event.result.close()
this.emit('error', event.error)
fs.writeFileSync(path.join(rootDir, this.watchedModule), '', 'utf8')
// fs.writeFileSync(path.join(this.tempDir, this.watchedModule), '', 'utf8')
this.emit('ready')
break
default:
this.emit('busy')
break
}
})
this.builder.start()
return await this.ready()
}
async close () {
await super.close()
this.builder.close()
}
}
class RollupBundler extends EventEmitter {
constructor ({ rollupOptions, watchedModule, watch = false }) {
super()
this.rollupOptions = rollupOptions
this.watch = watch
this.watchedModule = watchedModule
}
async start () {
if (this.watch === true) {
this.watcher = rollup.watch(this.rollupOptions)
this.watcher.on('event', event => {
this.emit('event', event)
})
} else {
await this._bundle()
// if (!fs.existsSync(path.join(rootDir, this.watchedModule))) {
// await this._bundle()
// } else {
// this.emit('event', { code: 'END', noBuild: true })
// }
}
}
async _bundle () {
this.emit('event', { code: 'START' })
for (const optionsObj of [].concat(this.rollupOptions)) {
try {
const bundle = await rollup.rollup(optionsObj)
try {
await Promise.all(optionsObj.output.map(bundle.write))
this.emit('event', { code: 'BUNDLE_END' })
} catch (error) {
this.emit('event', { code: 'ERROR', error })
}
} catch (error) {
this.emit('event', { code: 'ERROR', error })
}
}
this.emit('event', { code: 'END' })
}
close () {
if (this.watcher !== undefined) this.watcher.close()
}
}
exports.RollupBuilder = RollupBuilder
exports.rollupBuilder = new RollupBuilder({
name: 'rollup',
configPath: path.join(rootDir, pkgJson.directories.build, 'rollup.config.js'),
tempDir: mochaTsDir
})

View File

@ -1,194 +0,0 @@
const path = require('path')
const fs = require('fs')
const ts = require('typescript')
const json5 = require('json5')
const Builder = require('./Builder.cjs')
const rootDir = path.join(__dirname, '../../../../')
const pkgJson = require(path.join(rootDir, 'package.json'))
const mochaTsRelativeDir = pkgJson.directories['mocha-ts']
const mochaTsDir = path.join(rootDir, mochaTsRelativeDir)
const formatHost = {
getCanonicalFileName: path => path,
getCurrentDirectory: ts.sys.getCurrentDirectory,
getNewLine: () => ts.sys.newLine
}
function fileChecksum (filePath) {
return require('crypto')
.createHash('md5')
.update(fs.readFileSync(filePath, { encoding: 'utf-8' }), 'utf8')
.digest('hex')
}
function renameJsToCjs (dir, fileList = []) {
const files = fs.readdirSync(dir)
files.forEach(file => {
const srcFile = path.join(dir, file)
if (fs.statSync(srcFile).isDirectory()) {
fileList = renameJsToCjs(srcFile, fileList)
} else {
const match = file.match(/(.*)\.js$/)
if (match !== null) {
const filename = match[1]
const dstFile = path.join(dir, `${filename}.cjs`)
fs.renameSync(srcFile, dstFile)
const fileContents = fs.readFileSync(dstFile, 'utf8')
const updatedFileContents = fileContents.replace(/(require\([`'"])(\..*[^.]{5})([`'"])/g, '$1$2.cjs$3')
fs.writeFileSync(dstFile, updatedFileContents, { encoding: 'utf8' })
}
}
})
}
function fixJsonAssertsInESMTests (dir, fileList = []) {
const files = fs.readdirSync(dir)
files.forEach(file => {
const srcFile = path.join(dir, file)
if (fs.statSync(srcFile).isDirectory()) {
fileList = fixJsonAssertsInESMTests(srcFile, fileList)
} else {
const match = file.match(/(.*)\.js$/)
if (match !== null) {
const fileContents = fs.readFileSync(srcFile, 'utf8')
const updatedFileContents = fileContents.replace(/(import\([`'"]\..*\.json[`'"])\)/g, '$1, { assert: { type: "json" } })')
fs.writeFileSync(srcFile, updatedFileContents, { encoding: 'utf8' })
}
}
})
}
class TestsBuilder extends Builder {
constructor ({ name, configPath, tempDir }) {
super(path.join(tempDir, 'semaphore'), name)
this.tempDir = tempDir
if (fs.existsSync(configPath) !== true) throw new Error(`Couldn't find a tsconfig file at ${configPath}`)
this.tsConfigPath = configPath
this.testFilesChecksums = {}
}
async start ({ testFiles = [], commonjs = false }) {
await super.start()
this.commonjs = commonjs
const tsConfig = json5.parse(fs.readFileSync(this.tsConfigPath, 'utf8'))
if (testFiles.length > 0) {
delete tsConfig.files
tsConfig.include = ['build/typings/**/*.d.ts'].concat(testFiles)
for (let i = 0; i < testFiles.length; i++) {
this.testFilesChecksums[testFiles[i]] = fileChecksum(testFiles[i])
}
} else {
tsConfig.include = ['build/typings/**/*.d.ts', 'test/**/*', 'src/ts/**/*.spec.ts']
}
tsConfig.exclude = ['src/ts/**/!(.spec).ts']
if (this.commonjs) {
tsConfig.compilerOptions.module = 'commonjs'
}
// "noResolve": true
// tsConfig.compilerOptions.noResolve = true
// we don't need declaration files
tsConfig.compilerOptions.declaration = false
// we need to emit files
tsConfig.compilerOptions.noEmit = false
// source mapping eases debuging
tsConfig.compilerOptions.inlineSourceMap = true
tsConfig.compilerOptions.rootDir = '.'
// Removed typeroots (it causes issues)
tsConfig.compilerOptions.typeRoots = undefined
tsConfig.compilerOptions.outDir = path.isAbsolute(this.tempDir) ? path.relative(rootDir, this.tempDir) : this.tempDir
this.tempTsConfigPath = path.join(rootDir, '.tsconfig.json')
fs.writeFileSync(this.tempTsConfigPath, JSON.stringify(tsConfig, undefined, 2), { encoding: 'utf-8' })
const createProgram = ts.createSemanticDiagnosticsBuilderProgram
const reportDiagnostic = (diagnostic) => {
const filePath = path.relative(rootDir, diagnostic.file.fileName)
const tranpiledJsPath = `${path.join(this.tempDir, filePath).slice(0, -3)}.js`
const errorLine = diagnostic.file.text.slice(0, diagnostic.start).split(/\r\n|\r|\n/).length
if (fs.existsSync(tranpiledJsPath)) {
fs.writeFileSync(tranpiledJsPath, '', 'utf8')
}
this.emit('error', `[Error ${diagnostic.code}]`, `${filePath}:${errorLine}`, ':', ts.flattenDiagnosticMessageText(diagnostic.messageText, formatHost.getNewLine()))
}
const reportWatchStatusChanged = (diagnostic, newLine, options, errorCount) => {
if (errorCount !== undefined) {
// only change semaphore if test files are modified
let updateSemaphore = false
for (let i = 0; i < testFiles.length; i++) {
const checksum = fileChecksum(testFiles[i])
if (this.testFilesChecksums[testFiles[i]] !== checksum) {
updateSemaphore = true
this.testFilesChecksums[testFiles[i]] = checksum
}
}
if (this.commonjs) {
renameJsToCjs(mochaTsDir)
} else {
fixJsonAssertsInESMTests(mochaTsDir)
}
this.emit('ready', updateSemaphore)
} else {
this.emit('busy')
if (diagnostic.code === 6031) {
this.emit('message', 'transpiling your tests...')
} else if (diagnostic.code === 6032) {
this.emit('message', 'file changes detected. Transpiling your tests...')
}
}
}
// Note that there is another overload for `createWatchCompilerHost` that takes
// a set of root files.
this.host = ts.createWatchCompilerHost(
this.tempTsConfigPath,
{},
ts.sys,
createProgram,
reportDiagnostic,
reportWatchStatusChanged
)
// `createWatchProgram` creates an initial program, watches files, and updates
// the program over time.
this.watcher = ts.createWatchProgram(this.host)
this.watcher.getProgram()
return await this.ready()
}
async close () {
await super.close()
this.watcher.close()
fs.unlinkSync(this.tempTsConfigPath)
}
}
exports.TestsBuilder = TestsBuilder
exports.testBuilder = new TestsBuilder({
name: 'tsc',
configPath: path.join(rootDir, 'tsconfig.json'),
tempDir: mochaTsDir
})

View File

@ -1,68 +0,0 @@
'use strict'
const fs = require('fs')
const path = require('path')
const chai = require('chai')
const rimraf = require('rimraf')
require('dotenv').config()
const rollupBuilder = require('./builders/RollupBuilder.cjs').rollupBuilder
const testsBuilder = require('./builders/TestsBuilder.cjs').testBuilder
const rootDir = path.join(__dirname, '../../../')
const pkgJson = require(path.join(rootDir, 'package.json'))
const mochaTsRelativeDir = pkgJson.directories['mocha-ts']
const tempDir = path.join(rootDir, mochaTsRelativeDir)
global.chai = chai
global.IS_BROWSER = false
const watch = process.argv.includes('--watch') || process.argv.includes('-w')
const setup = JSON.parse(fs.readFileSync(path.join(tempDir, 'testSetup.json'), 'utf-8'))
const testFiles = setup.testFiles
let commonjs = setup.commonjs
commonjs = watch ? true : commonjs // mocha in watch mode only supports commonjs
global._MODULE_TYPE = commonjs ? 'CJS' : 'ESM'
exports.mochaGlobalSetup = async function () {
if (watch) {
await rollupBuilder.start({ commonjs, watch })
testsBuilder.start({ testFiles, commonjs })
}
}
exports.mochaHooks = {
beforeAll: [
async function () {
this.timeout('120000')
if (watch) {
await Promise.all([rollupBuilder.ready(), testsBuilder.ready()])
// reset any transpiled module (just delete the cache so it is fully reloaded)
for (const key in require.cache) {
const relativePath = path.relative(rootDir, key)
if (relativePath.startsWith(`.mocha-ts${path.sep}`) || relativePath.startsWith(`dist${path.sep}`)) {
delete require.cache[key]
}
}
}
}
]
}
exports.mochaGlobalTeardown = async function () {
if (watch) {
await testsBuilder.close()
await rollupBuilder.close()
}
// I use the sync version of rimraf precisely because it blocks the
// main thread and thus the mocha watcher, which otherwise would complain
// about files being deleted
rimraf.sync(tempDir, { disableGlob: true })
}

View File

@ -1,3 +0,0 @@
declare const IS_BROWSER: boolean
declare const _MODULE_TYPE: string
declare const _NPM_PKG_VERSION: string

533
dist/bundle.esm.js vendored
View File

@ -1,533 +0,0 @@
function n(n){return n>=0?n:-n}function t(n){if("number"==typeof n&&(n=BigInt(n)),1n===n)return 1;let t=1;do{t++;}while((n>>=1n)>1n);return t}function r(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),n<=0n||t<=0n)throw new RangeError("a and b MUST be > 0");let r=0n,e=1n,o=1n,i=0n;for(;0n!==n;){const u=t/n,f=t%n,g=r-o*u,c=e-i*u;t=n,n=f,r=o,e=i,o=g,i=c;}return {g:t,x:r,y:e}}function e(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),t<=0n)throw new RangeError("n must be > 0");const r=n%t;return r<0n?r+t:r}function o(n,t){const o=r(e(n,t),t);if(1n!==o.g)throw new RangeError(`${n.toString()} does not have inverse modulo ${t.toString()}`);return e(o.x,t)}function i(n,t,r){if(n.length!==t.length)throw new RangeError("The remainders and modulos arrays should have the same length");const i=r??t.reduce(((n,t)=>n*t),1n);return t.reduce(((t,r,u)=>{const f=i/r;return e(t+f*o(f,r)%i*n[u]%i,i)}),0n)}function u(t,r){let e="number"==typeof t?BigInt(n(t)):n(t),o="number"==typeof r?BigInt(n(r)):n(r);if(0n===e)return o;if(0n===o)return e;let i=0n;for(;0n===(1n&(e|o));)e>>=1n,o>>=1n,i++;for(;0n===(1n&e);)e>>=1n;do{for(;0n===(1n&o);)o>>=1n;if(e>o){const n=e;e=o,o=n;}o-=e;}while(0n!==o);return e<<i}function f(t,r){return "number"==typeof t&&(t=BigInt(t)),"number"==typeof r&&(r=BigInt(r)),0n===t&&0n===r?BigInt(0):n(t/u(t,r)*r)}function g(n,t){return n>=t?n:t}function c(n,t){return n>=t?t:n}function m(n,t){const r=BigInt(t);return e(n.map((n=>BigInt(n)%r)).reduce(((n,t)=>n+t%r),0n),r)}function p(n,t){const r=BigInt(t);return e(n.map((n=>BigInt(n)%r)).reduce(((n,t)=>n*t%r),1n),r)}function a(n){return n.map((n=>n[0]**(n[1]-1n)*(n[0]-1n))).reduce(((n,t)=>t*n),1n)}function s(t,r,u,f){if("number"==typeof t&&(t=BigInt(t)),"number"==typeof r&&(r=BigInt(r)),"number"==typeof u&&(u=BigInt(u)),u<=0n)throw new RangeError("n must be > 0");if(1n===u)return 0n;if(t=e(t,u),r<0n)return o(s(t,n(r),u,f),u);if(void 0!==f)return function(n,t,r,e){const o=e.map((n=>n[0]**n[1])),u=e.map((n=>a([n]))),f=u.map(((r,e)=>s(n,t%r,o[e])));return i(f,o,r)}(t,r,u,function(n){const t={};return n.forEach((n=>{if("bigint"==typeof n||"number"==typeof n){const r=String(n);void 0===t[r]?t[r]={p:BigInt(n),k:1n}:t[r].k+=1n;}else {const r=String(n[0]);void 0===t[r]?t[r]={p:BigInt(n[0]),k:BigInt(n[1])}:t[r].k+=BigInt(n[1]);}})),Object.values(t).map((n=>[n.p,n.k]))}(f));let g=1n;for(;r>0;)r%2n===1n&&(g=g*t%u),r/=2n,t=t**2n%u;return g}
function fromBuffer(buf) {
let ret = 0n;
for (const i of buf.values()) {
const bi = BigInt(i);
ret = (ret << 8n) + bi;
}
return ret;
}
function randBytes(byteLength, forceLength = false) {
if (byteLength < 1)
throw new RangeError('byteLength MUST be > 0');
return new Promise(function (resolve, reject) {
{
const buf = new Uint8Array(byteLength);
if (byteLength <= 65536) {
self.crypto.getRandomValues(buf);
}
else {
for (let i = 0; i < Math.ceil(byteLength / 65536); i++) {
const begin = i * 65536;
const end = ((begin + 65535) < byteLength) ? begin + 65535 : byteLength - 1;
self.crypto.getRandomValues(buf.subarray(begin, end));
}
}
if (forceLength)
buf[0] = buf[0] | 128;
resolve(buf);
}
});
}
function randBytesSync(byteLength, forceLength = false) {
if (byteLength < 1)
throw new RangeError('byteLength MUST be > 0');
{
const buf = new Uint8Array(byteLength);
if (byteLength <= 65536) {
self.crypto.getRandomValues(buf);
}
else {
for (let i = 0; i < Math.ceil(byteLength / 65536); i++) {
const begin = i * 65536;
const end = ((begin + 65535) < byteLength) ? begin + 65535 : byteLength - 1;
self.crypto.getRandomValues(buf.subarray(begin, end));
}
}
if (forceLength)
buf[0] = buf[0] | 128;
return buf;
}
}
function randBits(bitLength, forceLength = false) {
if (bitLength < 1)
throw new RangeError('bitLength MUST be > 0');
const byteLength = Math.ceil(bitLength / 8);
const bitLengthMod8 = bitLength % 8;
return new Promise((resolve, reject) => {
randBytes(byteLength, false).then(function (rndBytes) {
if (bitLengthMod8 !== 0) {
rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1);
}
if (forceLength) {
const mask = (bitLengthMod8 !== 0) ? 2 ** (bitLengthMod8 - 1) : 128;
rndBytes[0] = rndBytes[0] | mask;
}
resolve(rndBytes);
});
});
}
function randBitsSync(bitLength, forceLength = false) {
if (bitLength < 1)
throw new RangeError('bitLength MUST be > 0');
const byteLength = Math.ceil(bitLength / 8);
const rndBytes = randBytesSync(byteLength, false);
const bitLengthMod8 = bitLength % 8;
if (bitLengthMod8 !== 0) {
rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1);
}
if (forceLength) {
const mask = (bitLengthMod8 !== 0) ? 2 ** (bitLengthMod8 - 1) : 128;
rndBytes[0] = rndBytes[0] | mask;
}
return rndBytes;
}
function randBetween(max, min = 1n) {
if (max <= min)
throw new RangeError('Arguments MUST be: max > min');
const interval = max - min;
const bitLen = t(interval);
let rnd;
do {
const buf = randBitsSync(bitLen);
rnd = fromBuffer(buf);
} while (rnd > interval);
return rnd + min;
}
function _workerUrl(workerCode) {
workerCode = `(() => {${workerCode}})()`;
const _blob = new Blob([workerCode], { type: 'text/javascript' });
return window.URL.createObjectURL(_blob);
}
let _useWorkers = false;
{
if (self.Worker !== undefined)
_useWorkers = true;
}
function isProbablyPrime(w, iterations = 16, disableWorkers = false) {
if (typeof w === 'number') {
w = BigInt(w);
}
if (w < 0n)
throw RangeError('w MUST be >= 0');
{
return new Promise((resolve, reject) => {
const worker = new Worker(_isProbablyPrimeWorkerUrl());
worker.onmessage = (event) => {
if (event?.data?._bcu?.isPrime !== undefined) {
worker.terminate();
resolve(event.data._bcu.isPrime);
}
};
worker.onmessageerror = (event) => {
reject(event);
};
const msg = {
_bcu: {
rnd: w,
iterations,
id: 0
}
};
worker.postMessage(msg);
});
}
}
function _isProbablyPrime(w, iterations) {
if (w === 2n)
return true;
else if ((w & 1n) === 0n || w === 1n)
return false;
const firstPrimes = [
3n,
5n,
7n,
11n,
13n,
17n,
19n,
23n,
29n,
31n,
37n,
41n,
43n,
47n,
53n,
59n,
61n,
67n,
71n,
73n,
79n,
83n,
89n,
97n,
101n,
103n,
107n,
109n,
113n,
127n,
131n,
137n,
139n,
149n,
151n,
157n,
163n,
167n,
173n,
179n,
181n,
191n,
193n,
197n,
199n,
211n,
223n,
227n,
229n,
233n,
239n,
241n,
251n,
257n,
263n,
269n,
271n,
277n,
281n,
283n,
293n,
307n,
311n,
313n,
317n,
331n,
337n,
347n,
349n,
353n,
359n,
367n,
373n,
379n,
383n,
389n,
397n,
401n,
409n,
419n,
421n,
431n,
433n,
439n,
443n,
449n,
457n,
461n,
463n,
467n,
479n,
487n,
491n,
499n,
503n,
509n,
521n,
523n,
541n,
547n,
557n,
563n,
569n,
571n,
577n,
587n,
593n,
599n,
601n,
607n,
613n,
617n,
619n,
631n,
641n,
643n,
647n,
653n,
659n,
661n,
673n,
677n,
683n,
691n,
701n,
709n,
719n,
727n,
733n,
739n,
743n,
751n,
757n,
761n,
769n,
773n,
787n,
797n,
809n,
811n,
821n,
823n,
827n,
829n,
839n,
853n,
857n,
859n,
863n,
877n,
881n,
883n,
887n,
907n,
911n,
919n,
929n,
937n,
941n,
947n,
953n,
967n,
971n,
977n,
983n,
991n,
997n,
1009n,
1013n,
1019n,
1021n,
1031n,
1033n,
1039n,
1049n,
1051n,
1061n,
1063n,
1069n,
1087n,
1091n,
1093n,
1097n,
1103n,
1109n,
1117n,
1123n,
1129n,
1151n,
1153n,
1163n,
1171n,
1181n,
1187n,
1193n,
1201n,
1213n,
1217n,
1223n,
1229n,
1231n,
1237n,
1249n,
1259n,
1277n,
1279n,
1283n,
1289n,
1291n,
1297n,
1301n,
1303n,
1307n,
1319n,
1321n,
1327n,
1361n,
1367n,
1373n,
1381n,
1399n,
1409n,
1423n,
1427n,
1429n,
1433n,
1439n,
1447n,
1451n,
1453n,
1459n,
1471n,
1481n,
1483n,
1487n,
1489n,
1493n,
1499n,
1511n,
1523n,
1531n,
1543n,
1549n,
1553n,
1559n,
1567n,
1571n,
1579n,
1583n,
1597n
];
for (let i = 0; i < firstPrimes.length && (firstPrimes[i] <= w); i++) {
const p = firstPrimes[i];
if (w === p)
return true;
else if (w % p === 0n)
return false;
}
let a = 0n;
const d = w - 1n;
let aux = d;
while (aux % 2n === 0n) {
aux /= 2n;
++a;
}
const m = d / (2n ** a);
do {
const b = randBetween(d, 2n);
let z = s(b, m, w);
if (z === 1n || z === d)
continue;
let j = 1;
while (j < a) {
z = s(z, 2n, w);
if (z === d)
break;
if (z === 1n)
return false;
j++;
}
if (z !== d)
return false;
} while (--iterations !== 0);
return true;
}
function _isProbablyPrimeWorkerUrl() {
let workerCode = `
'use strict';
const ${r.name} = ${r.toString()};
const ${o.name} = ${o.toString()};
const ${s.name} = ${s.toString()};
const ${e.name} = ${e.toString()};
const ${randBitsSync.name} = ${randBitsSync.toString()};
const ${randBytesSync.name} = ${randBytesSync.toString()};
const ${randBetween.name} = ${randBetween.toString()};
const ${isProbablyPrime.name} = ${_isProbablyPrime.toString()};
${t.toString()};
${fromBuffer.toString()};`;
workerCode += `
onmessage = async function(msg) {
if (msg !== undefined && msg.data !== undefined && msg.data._bcu !== undefined && msg.data._bcu.id !== undefined && msg.data._bcu.iterations !== undefined && msg.data._bcu.rnd !== undefined) {
const msgToParent = {
_bcu: {
isPrime: await ${isProbablyPrime.name}(msg.data._bcu.rnd, msg.data._bcu.iterations),
value: msg.data._bcu.rnd,
id: msg.data._bcu.id
}
};
postMessage(msgToParent);
}
}`;
return _workerUrl(workerCode);
}
function prime(bitLength, iterations = 16) {
if (bitLength < 1)
throw new RangeError('bitLength MUST be > 0');
if (!_useWorkers) {
let rnd = 0n;
do {
rnd = fromBuffer(randBitsSync(bitLength, true));
} while (!_isProbablyPrime(rnd, iterations));
return new Promise((resolve) => { resolve(rnd); });
}
return new Promise((resolve, reject) => {
const workerList = [];
const _onmessage = (msg, newWorker) => {
if (msg._bcu.isPrime) {
for (let j = 0; j < workerList.length; j++) {
workerList[j].terminate();
}
while (workerList.length > 0) {
workerList.pop();
}
resolve(msg._bcu.value);
}
else {
const buf = randBitsSync(bitLength, true);
const rnd = fromBuffer(buf);
try {
const msgToWorker = {
_bcu: {
rnd,
iterations,
id: msg._bcu.id
}
};
newWorker.postMessage(msgToWorker);
}
catch (error) {
}
}
};
{
const workerURL = _isProbablyPrimeWorkerUrl();
for (let i = 0; i < self.navigator.hardwareConcurrency - 1; i++) {
const newWorker = new Worker(workerURL);
newWorker.onmessage = (event) => _onmessage(event.data, newWorker);
workerList.push(newWorker);
}
}
for (let i = 0; i < workerList.length; i++) {
randBits(bitLength, true).then(function (buf) {
const rnd = fromBuffer(buf);
workerList[i].postMessage({
_bcu: {
rnd,
iterations,
id: i
}
});
}).catch(reject);
}
});
}
function primeSync(bitLength, iterations = 16) {
if (bitLength < 1)
throw new RangeError('bitLength MUST be > 0');
let rnd = 0n;
do {
rnd = fromBuffer(randBitsSync(bitLength, true));
} while (!_isProbablyPrime(rnd, iterations));
return rnd;
}
export { n as abs, t as bitLength, i as crt, r as eGcd, u as gcd, isProbablyPrime, f as lcm, g as max, c as min, m as modAdd, o as modInv, p as modMultiply, s as modPow, a as phi, prime, primeSync, randBetween, randBits, randBitsSync, randBytes, randBytesSync, e as toZn };

File diff suppressed because one or more lines are too long

1
dist/bundle.iife.js vendored

File diff suppressed because one or more lines are too long

1
dist/bundle.umd.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

257
dist/index.d.ts vendored
View File

@ -1,257 +0,0 @@
/// <reference types="node" />
/**
* Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0
*
* @param a
*
* @returns The absolute value of a
*/
declare function abs(a: number | bigint): number | bigint;
/**
* Returns the (minimum) length of a number expressed in bits.
*
* @param a
* @returns The bit length
*/
declare function bitLength(a: number | bigint): number;
/**
* Chinese remainder theorem states that if one knows the remainders of the Euclidean division of an integer n by several integers, then one can determine uniquely the remainder of the division of n by the product of these integers, under the condition that the divisors are pairwise coprime (no two divisors share a common factor other than 1). Provided that n_i are pairwise coprime, and a_i any integers, this function returns a solution for the following system of equations:
x a_1 mod n_1
x a_2 mod n_2
x a_k mod n_k
*
* @param remainders the array of remainders a_i. For example [17n, 243n, 344n]
* @param modulos the array of modulos n_i. For example [769n, 2017n, 47701n]
* @param modulo the product of all modulos. Provided here just to save some operations if it is already known
* @returns x
*/
declare function crt(remainders: bigint[], modulos: bigint[], modulo?: bigint): bigint;
interface Egcd {
g: bigint;
x: bigint;
y: bigint;
}
/**
* 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).
*
* @param a
* @param b
*
* @throws {@link RangeError} if a or b are <= 0
*
* @returns A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/
declare function eGcd(a: number | bigint, b: number | bigint): Egcd;
/**
* Greatest common divisor of two integers based on the iterative binary algorithm.
*
* @param a
* @param b
*
* @returns The greatest common divisor of a and b
*/
declare function gcd(a: number | bigint, b: number | bigint): bigint;
/**
* The least common multiple computed as abs(a*b)/gcd(a,b)
* @param a
* @param b
*
* @returns The least common multiple of a and b
*/
declare function lcm(a: number | bigint, b: number | bigint): bigint;
/**
* Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<b
*
* @param a
* @param b
*
* @returns Maximum of numbers a and b
*/
declare function max(a: number | bigint, b: number | bigint): number | bigint;
/**
* Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<b
*
* @param a
* @param b
*
* @returns Minimum of numbers a and b
*/
declare function min(a: number | bigint, b: number | bigint): number | bigint;
/**
* Modular addition of (a_1 + ... + a_r) mod n
* @param addends an array of the numbers a_i to add. For example [3, 12353251235n, 1243, -12341232545990n]
* @param n the modulo
* @returns The smallest positive integer that is congruent with (a_1 + ... + a_r) mod n
*/
declare function modAdd(addends: Array<number | bigint>, n: number | bigint): bigint;
/**
* Modular inverse.
*
* @param a The number to find an inverse for
* @param n The modulo
*
* @throws {@link RangeError} if a does not have inverse modulo n
*
* @returns The inverse modulo n
*/
declare function modInv(a: number | bigint, n: number | bigint): bigint;
/**
* Modular addition of (a_1 * ... * a_r) mod n
* @param factors an array of the numbers a_i to multiply. For example [3, 12353251235n, 1243, -12341232545990n]
* @param n the modulo
* @returns The smallest positive integer that is congruent with (a_1 * ... * a_r) mod n
*/
declare function modMultiply(factors: Array<number | bigint>, n: number | bigint): bigint;
type PrimePower = [number | bigint, number | bigint];
type PrimeFactor = number | bigint | PrimePower;
/**
* Modular exponentiation b**e mod n. Currently using the right-to-left binary method if the prime factorization is not provided, or the chinese remainder theorem otherwise.
*
* @param b base
* @param e exponent
* @param n modulo
* @param primeFactorization an array of the prime factors, for example [5n, 5n, 13n, 27n], or prime powers as [p, k], for instance [[5, 2], [13, 1], [27, 1]]. If the prime factorization is provided the chinese remainder theorem is used to greatly speed up the exponentiation.
*
* @throws {@link RangeError} if n <= 0
*
* @returns b**e mod n
*/
declare function modPow(b: number | bigint, e: number | bigint, n: number | bigint, primeFactorization?: PrimeFactor[]): bigint;
type PrimeFactorization = Array<[bigint, bigint]>;
/**
* A function that computes the Euler's totien function of a number n, whose prime power factorization is known
*
* @param primeFactorization an array of arrays containing the prime power factorization of a number n. For example, for n = (p1**k1)*(p2**k2)*...*(pr**kr), one should provide [[p1, k1], [p2, k2], ... , [pr, kr]]
* @returns phi((p1**k1)*(p2**k2)*...*(pr**kr))
*/
declare function phi(primeFactorization: PrimeFactorization): bigint;
/**
* Finds the smallest positive element that is congruent to a in modulo n
*
* @remarks
* a and b must be the same type, either number or bigint
*
* @param a - An integer
* @param n - The modulo
*
* @throws {@link RangeError} if n <= 0
*
* @returns A bigint with the smallest positive representation of a modulo n
*/
declare function toZn(a: number | bigint, n: number | bigint): bigint;
/**
* The test first tries if any of the first 250 small primes are a factor of the input number and then passes several
* iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1)
*
* @param w - A positive integer to be tested for primality
* @param iterations - The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 of FIPS 186-4
* @param disableWorkers - Disable the use of workers for the primality test
*
* @throws {@link RangeError} if w<0
*
* @returns A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
*/
declare function isProbablyPrime(w: number | bigint, iterations?: number, disableWorkers?: boolean): Promise<boolean>;
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
* The browser version uses web workers to parallelise prime look up. Therefore, it does not lock the UI
* main process, and it can be much faster (if several cores or cpu are available).
* The node version can also use worker_threads if they are available (enabled by default with Node 11 and
* and can be enabled at runtime executing node --experimental-worker with node >=10.5.0).
*
* @param bitLength - The required bit length for the generated prime
* @param iterations - The number of iterations for the Miller-Rabin Probabilistic Primality Test
*
* @throws {@link RangeError} if bitLength < 1
*
* @returns A promise that resolves to a bigint probable prime of bitLength bits.
*/
declare function prime(bitLength: number, iterations?: number): Promise<bigint>;
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
* The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be slower and may freeze thw window in browser's javascript. Please consider using prime() instead.
*
* @param bitLength - The required bit length for the generated prime
* @param iterations - The number of iterations for the Miller-Rabin Probabilistic Primality Test
*
* @throws {@link RangeError} if bitLength < 1
*
* @returns A bigint probable prime of bitLength bits.
*/
declare function primeSync(bitLength: number, iterations?: number): bigint;
/**
* Returns a cryptographically secure random integer between [min,max].
* @param max Returned value will be <= max
* @param min Returned value will be >= min
*
* @throws {@link RangeError} if max <= min
*
* @returns A cryptographically secure random bigint between [min,max]
*/
declare function randBetween(max: bigint, min?: bigint): bigint;
/**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
*
* @param bitLength - The desired number of random bits
* @param forceLength - Set to true if you want to force the output to have a specific bit length. It basically forces the msb to be 1
*
* @throws {@link RangeError} if bitLength < 1
*
* @returns A Promise that resolves to a UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bits
*/
declare function randBits(bitLength: number, forceLength?: boolean): Promise<Uint8Array | Buffer>;
/**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* @param bitLength - The desired number of random bits
* @param forceLength - Set to true if you want to force the output to have a specific bit length. It basically forces the msb to be 1
*
* @throws {@link RangeError} if bitLength < 1
*
* @returns A Uint8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bits
*/
declare function randBitsSync(bitLength: number, forceLength?: boolean): Uint8Array | Buffer;
/**
* Secure random bytes for both node and browsers. Node version uses crypto.randomBytes() and browser one self.crypto.getRandomValues()
*
* @param byteLength - The desired number of random bytes
* @param forceLength - Set to true if you want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
*
* @throws {@link RangeError} if byteLength < 1
*
* @returns A promise that resolves to a UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bytes
*/
declare function randBytes(byteLength: number, forceLength?: boolean): Promise<Uint8Array | Buffer>;
/**
* Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* This is the synchronous version, consider using the asynchronous one for improved efficiency.
*
* @param byteLength - The desired number of random bytes
* @param forceLength - Set to true if you want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
*
* @throws {@link RangeError} if byteLength < 1
*
* @returns A UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bytes
*/
declare function randBytesSync(byteLength: number, forceLength?: boolean): Uint8Array | Buffer;
export { Egcd, PrimeFactor, PrimeFactorization, PrimePower, abs, bitLength, crt, eGcd, gcd, isProbablyPrime, lcm, max, min, modAdd, modInv, modMultiply, modPow, phi, prime, primeSync, randBetween, randBits, randBitsSync, randBytes, randBytesSync, toZn };

2
dist/index.node.cjs vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,584 +0,0 @@
# bigint-crypto-utils - v3.3.0
## Table of contents
### Interfaces
- [Egcd](interfaces/Egcd.md)
### Type Aliases
- [PrimeFactor](API.md#primefactor)
- [PrimeFactorization](API.md#primefactorization)
- [PrimePower](API.md#primepower)
### Functions
- [abs](API.md#abs)
- [bitLength](API.md#bitlength)
- [crt](API.md#crt)
- [eGcd](API.md#egcd)
- [gcd](API.md#gcd)
- [isProbablyPrime](API.md#isprobablyprime)
- [lcm](API.md#lcm)
- [max](API.md#max)
- [min](API.md#min)
- [modAdd](API.md#modadd)
- [modInv](API.md#modinv)
- [modMultiply](API.md#modmultiply)
- [modPow](API.md#modpow)
- [phi](API.md#phi)
- [prime](API.md#prime)
- [primeSync](API.md#primesync)
- [randBetween](API.md#randbetween)
- [randBits](API.md#randbits)
- [randBitsSync](API.md#randbitssync)
- [randBytes](API.md#randbytes)
- [randBytesSync](API.md#randbytessync)
- [toZn](API.md#tozn)
## Type Aliases
### PrimeFactor
Ƭ **PrimeFactor**: `number` \| `bigint` \| [`PrimePower`](API.md#primepower)
___
### PrimeFactorization
Ƭ **PrimeFactorization**: [`bigint`, `bigint`][]
___
### PrimePower
Ƭ **PrimePower**: [`number` \| `bigint`, `number` \| `bigint`]
## Functions
### abs
**abs**(`a`): `number` \| `bigint`
Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0
#### Parameters
| Name | Type |
| :------ | :------ |
| `a` | `number` \| `bigint` |
#### Returns
`number` \| `bigint`
The absolute value of a
___
### bitLength
**bitLength**(`a`): `number`
Returns the (minimum) length of a number expressed in bits.
#### Parameters
| Name | Type |
| :------ | :------ |
| `a` | `number` \| `bigint` |
#### Returns
`number`
The bit length
___
### crt
**crt**(`remainders`, `modulos`, `modulo?`): `bigint`
Chinese remainder theorem states that if one knows the remainders of the Euclidean division of an integer n by several integers, then one can determine uniquely the remainder of the division of n by the product of these integers, under the condition that the divisors are pairwise coprime (no two divisors share a common factor other than 1). Provided that n_i are pairwise coprime, and a_i any integers, this function returns a solution for the following system of equations:
x ≡ a_1 mod n_1
x ≡ a_2 mod n_2
x ≡ a_k mod n_k
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `remainders` | `bigint`[] | the array of remainders a_i. For example [17n, 243n, 344n] |
| `modulos` | `bigint`[] | the array of modulos n_i. For example [769n, 2017n, 47701n] |
| `modulo?` | `bigint` | the product of all modulos. Provided here just to save some operations if it is already known |
#### Returns
`bigint`
x
___
### eGcd
**eGcd**(`a`, `b`): [`Egcd`](interfaces/Egcd.md)
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 if a or b are <= 0
#### Parameters
| Name | Type |
| :------ | :------ |
| `a` | `number` \| `bigint` |
| `b` | `number` \| `bigint` |
#### Returns
[`Egcd`](interfaces/Egcd.md)
A triple (g, x, y), such that ax + by = g = gcd(a, b).
___
### gcd
**gcd**(`a`, `b`): `bigint`
Greatest common divisor of two integers based on the iterative binary algorithm.
#### Parameters
| Name | Type |
| :------ | :------ |
| `a` | `number` \| `bigint` |
| `b` | `number` \| `bigint` |
#### Returns
`bigint`
The greatest common divisor of a and b
___
### isProbablyPrime
**isProbablyPrime**(`w`, `iterations?`, `disableWorkers?`): `Promise`<`boolean`\>
The test first tries if any of the first 250 small primes are a factor of the input number and then passes several
iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1)
**`Throws`**
RangeError if w<0
#### Parameters
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `w` | `number` \| `bigint` | `undefined` | A positive integer to be tested for primality |
| `iterations` | `number` | `16` | The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 of FIPS 186-4 |
| `disableWorkers` | `boolean` | `false` | Disable the use of workers for the primality test |
#### Returns
`Promise`<`boolean`\>
A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
___
### lcm
**lcm**(`a`, `b`): `bigint`
The least common multiple computed as abs(a*b)/gcd(a,b)
#### Parameters
| Name | Type |
| :------ | :------ |
| `a` | `number` \| `bigint` |
| `b` | `number` \| `bigint` |
#### Returns
`bigint`
The least common multiple of a and b
___
### max
**max**(`a`, `b`): `number` \| `bigint`
Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<b
#### Parameters
| Name | Type |
| :------ | :------ |
| `a` | `number` \| `bigint` |
| `b` | `number` \| `bigint` |
#### Returns
`number` \| `bigint`
Maximum of numbers a and b
___
### min
**min**(`a`, `b`): `number` \| `bigint`
Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<b
#### Parameters
| Name | Type |
| :------ | :------ |
| `a` | `number` \| `bigint` |
| `b` | `number` \| `bigint` |
#### Returns
`number` \| `bigint`
Minimum of numbers a and b
___
### modAdd
**modAdd**(`addends`, `n`): `bigint`
Modular addition of (a_1 + ... + a_r) mod n
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `addends` | (`number` \| `bigint`)[] | an array of the numbers a_i to add. For example [3, 12353251235n, 1243, -12341232545990n] |
| `n` | `number` \| `bigint` | the modulo |
#### Returns
`bigint`
The smallest positive integer that is congruent with (a_1 + ... + a_r) mod n
___
### modInv
**modInv**(`a`, `n`): `bigint`
Modular inverse.
**`Throws`**
RangeError if a does not have inverse modulo n
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `a` | `number` \| `bigint` | The number to find an inverse for |
| `n` | `number` \| `bigint` | The modulo |
#### Returns
`bigint`
The inverse modulo n
___
### modMultiply
**modMultiply**(`factors`, `n`): `bigint`
Modular addition of (a_1 * ... * a_r) mod n
*
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `factors` | (`number` \| `bigint`)[] | an array of the numbers a_i to multiply. For example [3, 12353251235n, 1243, -12341232545990n] * |
| `n` | `number` \| `bigint` | the modulo * |
#### Returns
`bigint`
The smallest positive integer that is congruent with (a_1 * ... * a_r) mod n
___
### modPow
**modPow**(`b`, `e`, `n`, `primeFactorization?`): `bigint`
Modular exponentiation b**e mod n. Currently using the right-to-left binary method if the prime factorization is not provided, or the chinese remainder theorem otherwise.
**`Throws`**
RangeError if n <= 0
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `b` | `number` \| `bigint` | base |
| `e` | `number` \| `bigint` | exponent |
| `n` | `number` \| `bigint` | modulo |
| `primeFactorization?` | [`PrimeFactor`](API.md#primefactor)[] | an array of the prime factors, for example [5n, 5n, 13n, 27n], or prime powers as [p, k], for instance [[5, 2], [13, 1], [27, 1]]. If the prime factorization is provided the chinese remainder theorem is used to greatly speed up the exponentiation. |
#### Returns
`bigint`
b**e mod n
___
### phi
**phi**(`primeFactorization`): `bigint`
A function that computes the Euler's totien function of a number n, whose prime power factorization is known
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `primeFactorization` | [`PrimeFactorization`](API.md#primefactorization) | an array of arrays containing the prime power factorization of a number n. For example, for n = (p1**k1)*(p2**k2)*...*(pr**kr), one should provide [[p1, k1], [p2, k2], ... , [pr, kr]] |
#### Returns
`bigint`
phi((p1**k1)*(p2**k2)*...*(pr**kr))
___
### prime
**prime**(`bitLength`, `iterations?`): `Promise`<`bigint`\>
A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
The browser version uses web workers to parallelise prime look up. Therefore, it does not lock the UI
main process, and it can be much faster (if several cores or cpu are available).
The node version can also use worker_threads if they are available (enabled by default with Node 11 and
and can be enabled at runtime executing node --experimental-worker with node >=10.5.0).
**`Throws`**
RangeError if bitLength < 1
#### Parameters
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `bitLength` | `number` | `undefined` | The required bit length for the generated prime |
| `iterations` | `number` | `16` | The number of iterations for the Miller-Rabin Probabilistic Primality Test |
#### Returns
`Promise`<`bigint`\>
A promise that resolves to a bigint probable prime of bitLength bits.
___
### primeSync
**primeSync**(`bitLength`, `iterations?`): `bigint`
A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be slower and may freeze thw window in browser's javascript. Please consider using prime() instead.
**`Throws`**
RangeError if bitLength < 1
#### Parameters
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `bitLength` | `number` | `undefined` | The required bit length for the generated prime |
| `iterations` | `number` | `16` | The number of iterations for the Miller-Rabin Probabilistic Primality Test |
#### Returns
`bigint`
A bigint probable prime of bitLength bits.
___
### randBetween
**randBetween**(`max`, `min?`): `bigint`
Returns a cryptographically secure random integer between [min,max].
**`Throws`**
RangeError if max <= min
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `max` | `bigint` | Returned value will be <= max |
| `min` | `bigint` | Returned value will be >= min |
#### Returns
`bigint`
A cryptographically secure random bigint between [min,max]
___
### randBits
**randBits**(`bitLength`, `forceLength?`): `Promise`<`Uint8Array` \| `Buffer`\>
Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
**`Throws`**
RangeError if bitLength < 1
#### Parameters
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `bitLength` | `number` | `undefined` | The desired number of random bits |
| `forceLength` | `boolean` | `false` | Set to true if you want to force the output to have a specific bit length. It basically forces the msb to be 1 |
#### Returns
`Promise`<`Uint8Array` \| `Buffer`\>
A Promise that resolves to a UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bits
___
### randBitsSync
**randBitsSync**(`bitLength`, `forceLength?`): `Uint8Array` \| `Buffer`
Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
**`Throws`**
RangeError if bitLength < 1
#### Parameters
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `bitLength` | `number` | `undefined` | The desired number of random bits |
| `forceLength` | `boolean` | `false` | Set to true if you want to force the output to have a specific bit length. It basically forces the msb to be 1 |
#### Returns
`Uint8Array` \| `Buffer`
A Uint8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bits
___
### randBytes
**randBytes**(`byteLength`, `forceLength?`): `Promise`<`Uint8Array` \| `Buffer`\>
Secure random bytes for both node and browsers. Node version uses crypto.randomBytes() and browser one self.crypto.getRandomValues()
**`Throws`**
RangeError if byteLength < 1
#### Parameters
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `byteLength` | `number` | `undefined` | The desired number of random bytes |
| `forceLength` | `boolean` | `false` | Set to true if you want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 |
#### Returns
`Promise`<`Uint8Array` \| `Buffer`\>
A promise that resolves to a UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bytes
___
### randBytesSync
**randBytesSync**(`byteLength`, `forceLength?`): `Uint8Array` \| `Buffer`
Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
This is the synchronous version, consider using the asynchronous one for improved efficiency.
**`Throws`**
RangeError if byteLength < 1
#### Parameters
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `byteLength` | `number` | `undefined` | The desired number of random bytes |
| `forceLength` | `boolean` | `false` | Set to true if you want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 |
#### Returns
`Uint8Array` \| `Buffer`
A UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bytes
___
### toZn
**toZn**(`a`, `n`): `bigint`
Finds the smallest positive element that is congruent to a in modulo n
**`Remarks`**
a and b must be the same type, either number or bigint
**`Throws`**
RangeError if n <= 0
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `a` | `number` \| `bigint` | An integer |
| `n` | `number` \| `bigint` | The modulo |
#### Returns
`bigint`
A bigint with the smallest positive representation of a modulo n

View File

@ -1,27 +0,0 @@
# Interface: Egcd
## Table of contents
### Properties
- [g](Egcd.md#g)
- [x](Egcd.md#x)
- [y](Egcd.md#y)
## Properties
### g
**g**: `bigint`
___
### x
**x**: `bigint`
___
### y
**y**: `bigint`

View File

@ -1,54 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>bigint-crypto-utils</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="status">Still computing in background...</div>
<p>Look for the results in the JS console (Developer Tools)</p>
<script type="module">
import * as bigintCryptoUtils from '../dist/bundles/esm.js'
/* A BigInt with value 666 can be declared calling the bigint constructor as
BigInt('666') or with the shorter 666n.
Notice that you can also pass a number to the constructor, e.g. BigInt(666).
However, it is not recommended since values over 2**53 - 1 won't be safe but
no warning will be raised.
*/
const a = BigInt('5')
const b = BigInt('2')
const n = 19n
console.log(bigintCryptoUtils.modPow(a, b, n)) // prints 6
console.log(bigintCryptoUtils.modInv(2n, 5n)) // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))) // prints 2
console.log(bigintCryptoUtils.randBetween(2n ** 256n)) // Prints a cryptographically secure random number between 1 and 2**256 bits.
async function primeTesting() {
// Output of a probable prime of 2048 bits
console.log(await bigintCryptoUtils.prime(2048))
// Testing if a number is a probable prime (Miller-Rabin)
const number = 13139188972124309083000292697519085211422620620787723340749020496498012413131881656428777288953095338604061035790562501399090389032827482643578651715752317n
const isPrime = await bigintCryptoUtils.isProbablyPrime(number)
if (isPrime) {
console.log(`${number} is prime`)
} else {
console.log(`${number} is composite`)
}
}
primeTesting().then(() => {
document.getElementById(
"status"
).innerHTML = 'Done!'
})
</script>
</body>
</html>

View File

@ -1,54 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>bigint-crypto-utils</title>
<meta charset="UTF-8" />
<script src="../dist/bundles/iife.js"></script>
</head>
<body>
<div id="status">Still computing in background...</div>
<p>Look for the results in the JS console (Developer Tools)</p>
<script>
/* A BigInt with value 666 can be declared calling the bigint constructor as
BigInt('666') or with the shorter 666n.
Notice that you can also pass a number to the constructor, e.g. BigInt(666).
However, it is not recommended since values over 2**53 - 1 won't be safe but
no warning will be raised.
*/
const a = BigInt('5')
const b = BigInt('2')
const n = 19n
console.log(bigintCryptoUtils.modPow(a, b, n)) // prints 6
console.log(bigintCryptoUtils.modInv(2n, 5n)) // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))) // prints 2
console.log(bigintCryptoUtils.randBetween(2n ** 256n)) // Prints a cryptographically secure random number between 1 and 2**256 bits.
async function primeTesting() {
// Output of a probable prime of 2048 bits
console.log(await bigintCryptoUtils.prime(2048))
// Testing if a number is a probable prime (Miller-Rabin)
const number = 13139188972124309083000292697519085211422620620787723340749020496498012413131881656428777288953095338604061035790562501399090389032827482643578651715752317n
const isPrime = await bigintCryptoUtils.isProbablyPrime(number)
if (isPrime) {
console.log(`${number} is prime`)
} else {
console.log(`${number} is composite`)
}
}
primeTesting().then(() => {
document.getElementById(
"status"
).innerHTML = 'Done!'
})
</script>
</body>
</html>

View File

@ -1,36 +0,0 @@
const bigintCryptoUtils = require('..')
// const bigintCryptoUtils = require('bigint-crypto-utils')
/* A BigInt with value 666 can be declared calling the bigint constructor as
BigInt('666') or with the shorter 666n.
Notice that you can also pass a number to the constructor, e.g. BigInt(666).
However, it is not recommended since values over 2**53 - 1 won't be safe but
no warning will be raised.
*/
const a = BigInt('5')
const b = BigInt('2')
const n = 19n
console.log(bigintCryptoUtils.modPow(a, b, n)) // prints 6
console.log(bigintCryptoUtils.modInv(2n, 5n)) // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))) // prints 2
console.log(bigintCryptoUtils.randBetween(2n ** 256n)) // Prints a cryptographically secure random number between 1 and 2**256 bits.
async function primeTesting () {
// Output of a probable prime of 2048 bits
console.log(await bigintCryptoUtils.prime(2048))
// Testing if a number is a probable prime (Miller-Rabin)
const number = 13139188972124309083000292697519085211422620620787723340749020496498012413131881656428777288953095338604061035790562501399090389032827482643578651715752317n
const isPrime = await bigintCryptoUtils.isProbablyPrime(number)
if (isPrime) {
console.log(`${number} is prime`)
} else {
console.log(`${number} is composite`)
}
}
primeTesting()

View File

@ -1,38 +0,0 @@
import * as bigintCryptoUtils from '#pkg'
// const bigintCryptoUtils = require('bigint-crypto-utils')
/* A BigInt with value 666 can be declared calling the bigint constructor as
BigInt('666') or with the shorter 666n.
Notice that you can also pass a number to the constructor, e.g. BigInt(666).
However, it is not recommended since values over 2**53 - 1 won't be safe but
no warning will be raised.
*/
const a = BigInt('5')
const b = BigInt('2')
const n = 19n
console.log(bigintCryptoUtils.modPow(a, b, n)) // prints 6
console.log(bigintCryptoUtils.modInv(2n, 5n)) // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))) // prints 2
console.log(bigintCryptoUtils.randBetween(2n ** 256n)) // Prints a cryptographically secure random number between 1 and 2**256 bits.
async function primeTesting () {
// Output of a probable prime of 2048 bits
console.log(await bigintCryptoUtils.prime(2048))
// Testing if a number is a probable prime (Miller-Rabin)
const number = 13139188972124309083000292697519085211422620620787723340749020496498012413131881656428777288953095338604061035790562501399090389032827482643578651715752317n
const isPrime = await bigintCryptoUtils.isProbablyPrime(number)
if (isPrime) {
console.log(`${number} is prime`)
} else {
console.log(`${number} is composite`)
}
}
primeTesting()
export {}

19318
npm-shrinkwrap.json generated Normal file

File diff suppressed because it is too large Load Diff

6645
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{
"name": "bigint-crypto-utils",
"name": "@lumeweb/bigint-crypto-utils",
"version": "3.3.0",
"description": "Arbitrary precision modular arithmetic, cryptographically secure random numbers and strong probable prime generation/testing. It works in modern browsers, Angular, React, Node.js, etc. since it uses the native JS implementation of BigInt",
"keywords": [
@ -20,150 +20,36 @@
"email": "j.hernandez@upc.edu",
"url": "https://github.com/juanelas"
},
"repository": "github:juanelas/bigint-crypto-utils",
"repository": {
"type": "git",
"url": "gitea@git.lumeweb.com:LumeWeb/bigint-crypto-utils.git"
},
"engines": {
"node": ">=14.0.0"
},
"main": "lib/index.js",
"type": "module",
"main": "./dist/index.node.cjs",
"browser": "./dist/index.browser.esm.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"node": {
"module": {
"types": "./dist/index.d.ts",
"default": "./dist/index.node.esm.js"
},
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.node.esm.js"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.node.cjs"
}
},
"script": "./dist/bundle.iife.js",
"default": {
"types": "./dist/index.d.ts",
"default": "./dist/index.browser.esm.js"
}
},
"./esm-browser-bundle": "./dist/bundle.esm.min.js",
"./esm-browser-bundle-nomin": "./dist/bundle.esm.js",
"./iife-browser-bundle": "./dist/bundle.iife.js",
"./umd-browser-bundle": "./dist/bundle.umd.js",
"./types": "./dist/index.d.ts"
},
"imports": {
"#pkg": {
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.node.cjs"
},
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.node.esm.js"
},
"default": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/index.browser.esm.js"
}
}
},
"directories": {
"build": "./build",
"dist": "./dist",
"docs": "./docs",
"src": "./src",
"test": "./test",
"benchmark": "./benchmark",
"mocha-ts": "./.mocha-ts"
},
"scripts": {
"build": "run-s lint:src build:js lint:test docs",
"build:js": "rollup -c build/rollup.config.js",
"postbuild:js": "rimraf .types",
"clean": "rimraf .mocha-ts coverage dist .types docs",
"coverage": "c8 --clean --check-coverage --exclude \"{src/ts/**/*.spec.ts,test,test-vectors,build}\" --exclude-after-remap --reporter=text --reporter=lcov node ./build/bin/mocha-ts.cjs --commonjs ",
"docs": "node build/build.docs.cjs",
"git:add": "git add -A",
"lint": "ts-standard --fix",
"lint:src": "ts-standard --fix \"src/**/!(*.spec).ts\"",
"lint:test": "ts-standard --fix \"{test/**/*.ts,src/**/*.spec.ts}\"",
"mocha-ts": "node --experimental-modules --experimental-json-modules --es-module-specifier-resolution=node ./build/bin/mocha-ts.cjs ",
"mocha-ts:cjs": "node ./build/bin/mocha-ts.cjs --commonjs ",
"mocha-ts:watch": "npm run mocha-ts:cjs -- --watch ",
"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:src build:js lint:test coverage test:browser-headless",
"version": "run-s docs git:add",
"postversion": "git push --follow-tags",
"test": "run-s test:node test:browser-headless",
"test:browser": "npm run mocha-ts:browser",
"test:browser-headless": "npm run mocha-ts:browser-headless",
"test:node": "run-s test:node-cjs test:node-esm",
"test:node-cjs": "npm run mocha-ts:cjs ",
"test:node-esm": "npm run mocha-ts ",
"watch": "npm run mocha-ts:watch "
},
"ts-standard": {
"project": "tsconfig.json",
"env": [
"mocha"
],
"globals": [
"IS_BROWSER",
"browser",
"page",
"chai"
],
"ignore": [
"dist/**/*",
"examples/**/*",
"types/**/*",
"benchmark/**/*"
]
},
"nodeBrowserSkel": {
"badges": {
"workflow": true,
"coveralls": true
},
"git": {
"branch": "main"
}
},
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.2",
"@rollup/plugin-inject": "^5.0.3",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-multi-entry": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^5.0.1",
"@rollup/plugin-terser": "^0.4.0",
"@rollup/plugin-typescript": "^11.1.0",
"@types/chai": "^4.2.22",
"@types/mocha": "^10.0.0",
"bigint-mod-arith": "^3.3.1",
"c8": "^8.0.0",
"chai": "^4.3.3",
"dotenv": "^16.0.3",
"glob": "^10.0.0",
"json5": "^2.2.0",
"minimatch": "^9.0.0",
"mocha": "^10.0.0",
"npm-run-all": "^4.1.5",
"pirates": "^4.0.1",
"puppeteer": "^20.7.3",
"rimraf": "^5.0.0",
"rollup": "^3.20.2",
"rollup-plugin-dts": "^5.3.0",
"ts-standard": "^12.0.2",
"tslib": "^2.3.1",
"typedoc": "~0.23.0",
"typedoc-plugin-markdown": "~3.14.0",
"typescript": "^4.4.3"
"@lumeweb/node-library-preset": "^0.2.7",
"presetter": "*"
},
"bugs": {
"url": "https://github.com/juanelas/bigint-crypto-utils/issues"
},
"readme": "ERROR: No README data found!",
"homepage": "https://github.com/juanelas/bigint-crypto-utils#readme",
"_id": "bigint-crypto-utils@3.3.0",
"scripts": {
"prepare": "presetter bootstrap",
"build": "run build"
},
"dependencies": {
"@lumeweb/bigint-mod-arith": "^1.0.0"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
}
}

View File

@ -1,85 +0,0 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
{{GITHUB_ACTIONS_BADGES}}
# {{PKG_NAME}}
Arbitrary precision modular arithmetic, cryptographically secure random numbers and strong probable prime generation/testing.
It relies on the native JS implementation of ([BigInt](https://tc39.es/ecma262/#sec-bigint-objects)). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0). The bundles can be imported directly by the browser or in Angular projects, React apps, Node.js, etc.
Secure random numbers are generated using the native crypto implementation of the browsers ([Web Cryptography API](https://w3c.github.io/webcrypto/)) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html). Strong probable prime generation and testing use Miller-Rabin primality tests and are automatically sped up using parallel workers both in browsers and Node.js.
> The operations supported on BigInts are not constant time. BigInt can be therefore **[unsuitable for use in cryptography](https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html).** Many platforms provide native support for cryptography, such as [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html).
## Usage
`{{PKG_NAME}}` can be imported to your project with `npm`:
```console
npm install {{PKG_NAME}}
```
Then either require (Node.js CJS):
```javascript
const {{PKG_CAMELCASE}} = require('{{PKG_NAME}}')
```
or import (JavaScript ES module):
```javascript
import * as {{PKG_CAMELCASE}} from '{{PKG_NAME}}'
```
The appropriate version for browser or node is automatically exported.
> `{{PKG_NAME}}` uses [ES2020 BigInt](https://tc39.es/ecma262/#sec-bigint-objects), so take into account that:
>
> 1. If you experience issues using webpack/babel to create your production bundles, you may edit the supported browsers list and leave only [supported browsers and versions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility). The browsers list is usually located in your project's `package.json` or the `.browserslistrc` file.
> 2. In order to use `{{PKG_NAME}}` with TypeScript you need to set `target`, and `lib` and `module` if in use, to `ES2020` in your project's `tsconfig.json`.
You can also download the {{IIFE_BUNDLE}}, the {{ESM_BUNDLE}} or the {{UMD_BUNDLE}} and manually add it to your project, or, if you have already installed `{{PKG_NAME}}` in your project, just get the bundles from `node_modules/{{PKG_NAME}}/dist/bundles/`.
An example of usage could be (complete examples can be found in the [examples](https://github.com/juanelas/bigint-crypto-utils/tree/master/examples) directory):
```typescript
/* A BigInt with value 666 can be declared calling the bigint constructor as
BigInt('666') or with the shorter 666n.
Notice that you can also pass a number to the constructor, e.g. BigInt(666).
However, it is not recommended since values over 2**53 - 1 won't be safe but
no warning will be raised.
*/
const a = BigInt('5')
const b = BigInt('2')
const n = 19n
console.log(bigintCryptoUtils.modPow(a, b, n)) // prints 6
console.log(bigintCryptoUtils.modInv(2n, 5n)) // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))) // prints 2
console.log(bigintCryptoUtils.randBetween(2n ** 256n)) // prints a cryptographically secure random number between 1 and 2**256 (both included).
async function primeTesting (): void {
// Let us print out a probable prime of 2048 bits
console.log(await bigintCryptoUtils.prime(2048))
// Testing if number is a probable prime (Miller-Rabin)
const number = 27n
const isPrime = await bigintCryptoUtils.isProbablyPrime(number)
if (isPrime === true) {
console.log(`${number} is prime`)
} else {
console.log(`${number} is composite`)
}
}
primeTesting()
```
## API reference documentation
[Check the API](./docs/API.md)

8
src/fromBuffer.ts Normal file
View File

@ -0,0 +1,8 @@
export function fromBuffer(buf: Uint8Array | Buffer): bigint {
let ret = 0n;
for (const i of buf.values()) {
const bi = BigInt(i);
ret = (ret << 8n) + bi;
}
return ret;
}

7
src/index.ts Normal file
View File

@ -0,0 +1,7 @@
export * from "@lumeweb/bigint-mod-arith";
export { isProbablyPrime } from "./isProbablyPrime.js";
export { prime, primeSync } from "./prime.js";
export { randBetween } from "./randBetween.js";
export { randBits, randBitsSync } from "./randBits.js";
export { randBytes, randBytesSync } from "./randBytes.js";

View File

@ -1,9 +1,20 @@
import { eGcd, modInv, modPow, toZn, bitLength } from 'bigint-mod-arith'
import { fromBuffer } from './fromBuffer.js'
import { randBetween } from './randBetween.js'
import { randBitsSync } from './randBits.js'
import { randBytesSync } from './randBytes.js'
import { _useWorkers, _workerUrl, WorkerToMainMsg, MainToWorkerMsg } from './workerUtils.js'
import {
eGcd,
modInv,
modPow,
toZn,
bitLength,
} from "@lumeweb/bigint-mod-arith";
import { fromBuffer } from "./fromBuffer.js";
import { randBetween } from "./randBetween.js";
import { randBitsSync } from "./randBits.js";
import { randBytesSync } from "./randBytes.js";
import {
_useWorkers,
_workerUrl,
WorkerToMainMsg,
MainToWorkerMsg,
} from "./workerUtils.js";
/**
* The test first tries if any of the first 250 small primes are a factor of the input number and then passes several
@ -17,64 +28,71 @@ import { _useWorkers, _workerUrl, WorkerToMainMsg, MainToWorkerMsg } from './wor
*
* @returns A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
*/
export function isProbablyPrime (w: number|bigint, iterations: number = 16, disableWorkers: boolean = false): Promise<boolean> { // eslint-disable-line
if (typeof w === 'number') {
w = BigInt(w)
export function isProbablyPrime(
w: number | bigint,
iterations: number = 16,
disableWorkers: boolean = false,
): Promise<boolean> {
// eslint-disable-line
if (typeof w === "number") {
w = BigInt(w);
}
if (w < 0n) throw RangeError('w MUST be >= 0')
if (w < 0n) throw RangeError("w MUST be >= 0");
if (!IS_BROWSER) { // Node.js
if (!true) {
// Node.js
if (!disableWorkers && _useWorkers) {
return new Promise((resolve, reject) => {
const worker = new workerThreads.Worker(__filename)
const worker = new workerThreads.Worker(__filename);
worker.on('message', (data?: WorkerToMainMsg) => {
worker.on("message", (data?: WorkerToMainMsg) => {
if (data?._bcu?.isPrime !== undefined) {
worker.terminate().catch(reject)
resolve(data._bcu.isPrime)
worker.terminate().catch(reject);
resolve(data._bcu.isPrime);
}
})
});
worker.on('error', reject)
worker.on("error", reject);
const msg: MainToWorkerMsg = {
_bcu: {
rnd: w as bigint,
iterations,
id: 0
}
}
worker.postMessage(msg)
})
id: 0,
},
};
worker.postMessage(msg);
});
} else {
return new Promise((resolve) => {
resolve(_isProbablyPrime(w as bigint, iterations))
})
resolve(_isProbablyPrime(w as bigint, iterations));
});
}
} else { // browser
} else {
// browser
return new Promise((resolve, reject) => {
const worker = new Worker(_isProbablyPrimeWorkerUrl())
const worker = new Worker(_isProbablyPrimeWorkerUrl());
worker.onmessage = (event) => {
if (event?.data?._bcu?.isPrime !== undefined) {
worker.terminate()
resolve(event.data._bcu.isPrime)
}
worker.terminate();
resolve(event.data._bcu.isPrime);
}
};
worker.onmessageerror = (event) => {
reject(event)
}
reject(event);
};
const msg: MainToWorkerMsg = {
_bcu: {
rnd: w as bigint,
iterations,
id: 0
}
}
worker.postMessage(msg)
})
id: 0,
},
};
worker.postMessage(msg);
});
}
}
@ -83,8 +101,8 @@ export function _isProbablyPrime (w: bigint, iterations: number): boolean {
PREFILTERING. Even values but 2 are not primes, so don't test.
1 is not a prime and the M-R algorithm needs w>1.
*/
if (w === 2n) return true
else if ((w & 1n) === 0n || w === 1n) return false
if (w === 2n) return true;
else if ((w & 1n) === 0n || w === 1n) return false;
/*
Test if any of the first 250 small primes are a factor of w. 2 is not tested because it was already tested above.
@ -339,13 +357,13 @@ export function _isProbablyPrime (w: bigint, iterations: number): boolean {
1571n,
1579n,
1583n,
1597n
]
1597n,
];
for (let i = 0; i < firstPrimes.length && (firstPrimes[i] <= w); i++) {
const p = firstPrimes[i]
if (w === p) return true
else if (w % p === 0n) return false
for (let i = 0; i < firstPrimes.length && firstPrimes[i] <= w; i++) {
const p = firstPrimes[i];
if (w === p) return true;
else if (w % p === 0n) return false;
}
/*
@ -367,31 +385,31 @@ export function _isProbablyPrime (w: bigint, iterations: number): boolean {
Comment: Increment i for the do-loop in step 4.
5. Return PROBABLY PRIME.
*/
let a = 0n
const d = w - 1n
let aux = d
let a = 0n;
const d = w - 1n;
let aux = d;
while (aux % 2n === 0n) {
aux /= 2n
++a
aux /= 2n;
++a;
}
const m = d / (2n ** a)
const m = d / 2n ** a;
do {
const b = randBetween(d, 2n)
let z = modPow(b, m, w)
if (z === 1n || z === d) continue
let j = 1
const b = randBetween(d, 2n);
let z = modPow(b, m, w);
if (z === 1n || z === d) continue;
let j = 1;
while (j < a) {
z = modPow(z, 2n, w)
if (z === d) break
if (z === 1n) return false
j++
z = modPow(z, 2n, w);
if (z === d) break;
if (z === 1n) return false;
j++;
}
if (z !== d) return false
} while (--iterations !== 0)
if (z !== d) return false;
} while (--iterations !== 0);
return true
return true;
}
export function _isProbablyPrimeWorkerUrl(): string {
@ -407,7 +425,7 @@ export function _isProbablyPrimeWorkerUrl (): string {
const ${randBetween.name} = ${randBetween.toString()};
const ${isProbablyPrime.name} = ${_isProbablyPrime.toString()};
${bitLength.toString()};
${fromBuffer.toString()};`
${fromBuffer.toString()};`;
workerCode += `
onmessage = async function(msg) {
@ -421,30 +439,36 @@ export function _isProbablyPrimeWorkerUrl (): string {
};
postMessage(msgToParent);
}
}`
}`;
return _workerUrl(workerCode)
return _workerUrl(workerCode);
}
if (!IS_BROWSER && _useWorkers) { // node.js with support for workers
if (!true && _useWorkers) {
// node.js with support for workers
try {
var workerThreads = await import('worker_threads') // eslint-disable-line no-var
const isWorker = !(workerThreads.isMainThread)
if (isWorker && workerThreads.parentPort !== null) { // worker
const parentPort = workerThreads.parentPort
parentPort.on('message', function (data: MainToWorkerMsg | any) { // Let's start once we are called
if (data?._bcu?.iterations !== undefined && data?._bcu?.rnd !== undefined) {
const isPrime = _isProbablyPrime(data._bcu.rnd, data._bcu.iterations)
var workerThreads = await import("worker_threads"); // eslint-disable-line no-var
const isWorker = !workerThreads.isMainThread;
if (isWorker && workerThreads.parentPort !== null) {
// worker
const parentPort = workerThreads.parentPort;
parentPort.on("message", function (data: MainToWorkerMsg | any) {
// Let's start once we are called
if (
data?._bcu?.iterations !== undefined &&
data?._bcu?.rnd !== undefined
) {
const isPrime = _isProbablyPrime(data._bcu.rnd, data._bcu.iterations);
const msg: WorkerToMainMsg = {
_bcu: {
isPrime,
value: data._bcu.rnd,
id: data._bcu.id
id: data._bcu.id,
},
};
parentPort.postMessage(msg);
}
}
parentPort.postMessage(msg)
}
})
});
}
} catch (error) {}
}

133
src/prime.ts Normal file
View File

@ -0,0 +1,133 @@
import { fromBuffer } from "./fromBuffer.js";
import {
_isProbablyPrime,
_isProbablyPrimeWorkerUrl,
} from "./isProbablyPrime.js";
import { randBits, randBitsSync } from "./randBits.js";
import {
_useWorkers,
WorkerToMainMsg,
MainToWorkerMsg,
} from "./workerUtils.js";
import type { Worker as NodeWorker } from "worker_threads";
if (!true) var os = await import("os"); // eslint-disable-line no-var
if (!true) {
try {
var workerThreads = await import("worker_threads"); // eslint-disable-line no-var
} catch {}
}
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
* The browser version uses web workers to parallelise prime look up. Therefore, it does not lock the UI
* main process, and it can be much faster (if several cores or cpu are available).
* The node version can also use worker_threads if they are available (enabled by default with Node 11 and
* and can be enabled at runtime executing node --experimental-worker with node >=10.5.0).
*
* @param bitLength - The required bit length for the generated prime
* @param iterations - The number of iterations for the Miller-Rabin Probabilistic Primality Test
*
* @throws {@link RangeError} if bitLength < 1
*
* @returns A promise that resolves to a bigint probable prime of bitLength bits.
*/
export function prime(
bitLength: number,
iterations: number = 16,
): Promise<bigint> {
// eslint-disable-line
if (bitLength < 1) throw new RangeError("bitLength MUST be > 0");
/* c8 ignore start */
if (!_useWorkers) {
// If there is no support for workers
let rnd = 0n;
do {
rnd = fromBuffer(randBitsSync(bitLength, true));
} while (!_isProbablyPrime(rnd, iterations));
return new Promise((resolve) => {
resolve(rnd);
});
}
/* c8 ignore stop */
return new Promise((resolve, reject) => {
const workerList: Array<NodeWorker | Worker> = [];
const _onmessage = (
msg: WorkerToMainMsg,
newWorker: Worker | NodeWorker,
): void => {
if (msg._bcu.isPrime) {
// if a prime number has been found, stop all the workers, and return it
for (let j = 0; j < workerList.length; j++) {
workerList[j].terminate(); // eslint-disable-line @typescript-eslint/no-floating-promises
}
while (workerList.length > 0) {
workerList.pop();
}
resolve(msg._bcu.value);
} else {
// if a composite is found, make the worker test another random number
const buf = randBitsSync(bitLength, true);
const rnd = fromBuffer(buf);
try {
const msgToWorker: MainToWorkerMsg = {
_bcu: {
rnd,
iterations,
id: msg._bcu.id,
},
};
newWorker.postMessage(msgToWorker);
} catch (error) {
// The worker has already terminated. There is nothing to handle here
}
}
};
if (true) {
// browser
const workerURL = _isProbablyPrimeWorkerUrl();
for (let i = 0; i < self.navigator.hardwareConcurrency - 1; i++) {
const newWorker = new Worker(workerURL);
newWorker.onmessage = (event) => _onmessage(event.data, newWorker);
workerList.push(newWorker);
}
} else {
// Node.js
}
for (let i = 0; i < workerList.length; i++) {
randBits(bitLength, true)
.then(function (buf: Uint8Array | Buffer) {
const rnd = fromBuffer(buf);
workerList[i].postMessage({
_bcu: {
rnd,
iterations,
id: i,
},
});
})
.catch(reject);
}
});
}
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
* The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be slower and may freeze thw window in browser's javascript. Please consider using prime() instead.
*
* @param bitLength - The required bit length for the generated prime
* @param iterations - The number of iterations for the Miller-Rabin Probabilistic Primality Test
*
* @throws {@link RangeError} if bitLength < 1
*
* @returns A bigint probable prime of bitLength bits.
*/
export function primeSync(bitLength: number, iterations: number = 16): bigint {
if (bitLength < 1) throw new RangeError("bitLength MUST be > 0");
let rnd = 0n;
do {
rnd = fromBuffer(randBitsSync(bitLength, true));
} while (!_isProbablyPrime(rnd, iterations));
return rnd;
}

24
src/randBetween.ts Normal file
View File

@ -0,0 +1,24 @@
import { bitLength } from "@lumeweb/bigint-mod-arith";
import { fromBuffer } from "./fromBuffer.js";
import { randBitsSync } from "./randBits.js";
/**
* Returns a cryptographically secure random integer between [min,max].
* @param max Returned value will be <= max
* @param min Returned value will be >= min
*
* @throws {@link RangeError} if max <= min
*
* @returns A cryptographically secure random bigint between [min,max]
*/
export function randBetween(max: bigint, min: bigint = 1n): bigint {
if (max <= min) throw new RangeError("Arguments MUST be: max > min");
const interval = max - min;
const bitLen = bitLength(interval);
let rnd;
do {
const buf = randBitsSync(bitLen);
rnd = fromBuffer(buf);
} while (rnd > interval);
return rnd + min;
}

View File

@ -1,4 +1,4 @@
import { randBytes, randBytesSync } from './randBytes.js'
import { randBytes, randBytesSync } from "./randBytes.js";
/**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
@ -10,25 +10,30 @@ import { randBytes, randBytesSync } from './randBytes.js'
*
* @returns A Promise that resolves to a UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bits
*/
export function randBits (bitLength: number, forceLength: boolean = false): Promise<Uint8Array|Buffer> { // eslint-disable-line
if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
export function randBits(
bitLength: number,
forceLength: boolean = false,
): Promise<Uint8Array | Buffer> {
// eslint-disable-line
if (bitLength < 1) throw new RangeError("bitLength MUST be > 0");
const byteLength = Math.ceil(bitLength / 8)
const bitLengthMod8 = bitLength % 8
const byteLength = Math.ceil(bitLength / 8);
const bitLengthMod8 = bitLength % 8;
return new Promise((resolve, reject) => {
randBytes(byteLength, false).then(function (rndBytes) { // eslint-disable-line
randBytes(byteLength, false).then(function (rndBytes) {
// eslint-disable-line
if (bitLengthMod8 !== 0) {
// Fill with 0's the extra bits
rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1)
rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1);
}
if (forceLength) {
const mask = (bitLengthMod8 !== 0) ? 2 ** (bitLengthMod8 - 1) : 128
rndBytes[0] = rndBytes[0] | mask
const mask = bitLengthMod8 !== 0 ? 2 ** (bitLengthMod8 - 1) : 128;
rndBytes[0] = rndBytes[0] | mask;
}
resolve(rndBytes)
})
})
resolve(rndBytes);
});
});
}
/**
@ -40,19 +45,22 @@ export function randBits (bitLength: number, forceLength: boolean = false): Prom
*
* @returns A Uint8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bits
*/
export function randBitsSync (bitLength: number, forceLength: boolean = false): Uint8Array | Buffer {
if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
export function randBitsSync(
bitLength: number,
forceLength: boolean = false,
): Uint8Array | Buffer {
if (bitLength < 1) throw new RangeError("bitLength MUST be > 0");
const byteLength = Math.ceil(bitLength / 8)
const rndBytes = randBytesSync(byteLength, false)
const bitLengthMod8 = bitLength % 8
const byteLength = Math.ceil(bitLength / 8);
const rndBytes = randBytesSync(byteLength, false);
const bitLengthMod8 = bitLength % 8;
if (bitLengthMod8 !== 0) {
// Fill with 0's the extra bits
rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1)
rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1);
}
if (forceLength) {
const mask = (bitLengthMod8 !== 0) ? 2 ** (bitLengthMod8 - 1) : 128
rndBytes[0] = rndBytes[0] | mask
const mask = bitLengthMod8 !== 0 ? 2 ** (bitLengthMod8 - 1) : 128;
rndBytes[0] = rndBytes[0] | mask;
}
return rndBytes
return rndBytes;
}

View File

@ -1,4 +1,4 @@
if (!IS_BROWSER) var crypto = await import('crypto') // eslint-disable-line no-var
if (!true) var crypto = await import("crypto"); // eslint-disable-line no-var
/**
* Secure random bytes for both node and browsers. Node version uses crypto.randomBytes() and browser one self.crypto.getRandomValues()
@ -10,34 +10,40 @@ if (!IS_BROWSER) var crypto = await import('crypto') // eslint-disable-line no-v
*
* @returns A promise that resolves to a UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bytes
*/
export function randBytes (byteLength: number, forceLength = false): Promise<Uint8Array|Buffer> { // eslint-disable-line
if (byteLength < 1) throw new RangeError('byteLength MUST be > 0')
export function randBytes(
byteLength: number,
forceLength = false,
): Promise<Uint8Array | Buffer> {
// eslint-disable-line
if (byteLength < 1) throw new RangeError("byteLength MUST be > 0");
return new Promise(function (resolve, reject) {
if (!IS_BROWSER) {
if (!true) {
crypto.randomBytes(byteLength, function (err, buf: Buffer) {
if (err !== null) reject(err)
if (err !== null) reject(err);
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128
resolve(buf)
})
} else { // browser
const buf = new Uint8Array(byteLength)
if (forceLength) buf[0] = buf[0] | 128;
resolve(buf);
});
} else {
// browser
const buf = new Uint8Array(byteLength);
// the maximum number of bytes of entropy available via self.crypto.getRandomValues is 65536
if (byteLength <= 65536) {
self.crypto.getRandomValues(buf)
self.crypto.getRandomValues(buf);
} else {
for (let i = 0; i < Math.ceil(byteLength / 65536); i++) {
const begin = i * 65536
const end = ((begin + 65535) < byteLength) ? begin + 65535 : byteLength - 1
self.crypto.getRandomValues(buf.subarray(begin, end))
const begin = i * 65536;
const end =
begin + 65535 < byteLength ? begin + 65535 : byteLength - 1;
self.crypto.getRandomValues(buf.subarray(begin, end));
}
}
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128
resolve(buf)
if (forceLength) buf[0] = buf[0] | 128;
resolve(buf);
}
})
});
}
/**
@ -51,30 +57,35 @@ export function randBytes (byteLength: number, forceLength = false): Promise<Uin
*
* @returns A UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure random bytes
*/
export function randBytesSync (byteLength: number, forceLength: boolean = false): Uint8Array | Buffer {
if (byteLength < 1) throw new RangeError('byteLength MUST be > 0')
export function randBytesSync(
byteLength: number,
forceLength: boolean = false,
): Uint8Array | Buffer {
if (byteLength < 1) throw new RangeError("byteLength MUST be > 0");
/* eslint-disable no-lone-blocks */
if (!IS_BROWSER) { // node
const buf = crypto.randomBytes(byteLength)
if (!true) {
// node
const buf = crypto.randomBytes(byteLength);
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128
return buf
} else { // browser
const buf = new Uint8Array(byteLength)
if (forceLength) buf[0] = buf[0] | 128;
return buf;
} else {
// browser
const buf = new Uint8Array(byteLength);
// the maximum number of bytes of entropy available via self.crypto.getRandomValues is 65536
if (byteLength <= 65536) {
self.crypto.getRandomValues(buf)
self.crypto.getRandomValues(buf);
} else {
for (let i = 0; i < Math.ceil(byteLength / 65536); i++) {
const begin = i * 65536
const end = ((begin + 65535) < byteLength) ? begin + 65535 : byteLength - 1
self.crypto.getRandomValues(buf.subarray(begin, end))
const begin = i * 65536;
const end = begin + 65535 < byteLength ? begin + 65535 : byteLength - 1;
self.crypto.getRandomValues(buf.subarray(begin, end));
}
}
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128
return buf
if (forceLength) buf[0] = buf[0] | 128;
return buf;
}
/* eslint-enable no-lone-blocks */
}

View File

@ -1,8 +0,0 @@
export function fromBuffer (buf: Uint8Array | Buffer): bigint {
let ret = 0n
for (const i of buf.values()) {
const bi = BigInt(i)
ret = (ret << 8n) + bi
}
return ret
}

View File

@ -1,7 +0,0 @@
export * from 'bigint-mod-arith'
export { isProbablyPrime } from './isProbablyPrime.js'
export { prime, primeSync } from './prime.js'
export { randBetween } from './randBetween.js'
export { randBits, randBitsSync } from './randBits.js'
export { randBytes, randBytesSync } from './randBytes.js'

View File

@ -1,116 +0,0 @@
import { fromBuffer } from './fromBuffer.js'
import { _isProbablyPrime, _isProbablyPrimeWorkerUrl } from './isProbablyPrime.js'
import { randBits, randBitsSync } from './randBits.js'
import { _useWorkers, WorkerToMainMsg, MainToWorkerMsg } from './workerUtils.js'
import type { Worker as NodeWorker } from 'worker_threads'
if (!IS_BROWSER) var os = await import('os') // eslint-disable-line no-var
if (!IS_BROWSER) {
try {
var workerThreads = await import('worker_threads') // eslint-disable-line no-var
} catch {}
}
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
* The browser version uses web workers to parallelise prime look up. Therefore, it does not lock the UI
* main process, and it can be much faster (if several cores or cpu are available).
* The node version can also use worker_threads if they are available (enabled by default with Node 11 and
* and can be enabled at runtime executing node --experimental-worker with node >=10.5.0).
*
* @param bitLength - The required bit length for the generated prime
* @param iterations - The number of iterations for the Miller-Rabin Probabilistic Primality Test
*
* @throws {@link RangeError} if bitLength < 1
*
* @returns A promise that resolves to a bigint probable prime of bitLength bits.
*/
export function prime (bitLength: number, iterations: number = 16): Promise<bigint> { // eslint-disable-line
if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
/* c8 ignore start */
if (!_useWorkers) { // If there is no support for workers
let rnd = 0n
do {
rnd = fromBuffer(randBitsSync(bitLength, true))
} while (!_isProbablyPrime(rnd, iterations))
return new Promise((resolve) => { resolve(rnd) })
}
/* c8 ignore stop */
return new Promise((resolve, reject) => {
const workerList: Array<NodeWorker | Worker> = []
const _onmessage = (msg: WorkerToMainMsg, newWorker: Worker | NodeWorker): void => {
if (msg._bcu.isPrime) {
// if a prime number has been found, stop all the workers, and return it
for (let j = 0; j < workerList.length; j++) {
workerList[j].terminate() // eslint-disable-line @typescript-eslint/no-floating-promises
}
while (workerList.length > 0) {
workerList.pop()
}
resolve(msg._bcu.value)
} else { // if a composite is found, make the worker test another random number
const buf = randBitsSync(bitLength, true)
const rnd = fromBuffer(buf)
try {
const msgToWorker: MainToWorkerMsg = {
_bcu: {
rnd,
iterations,
id: msg._bcu.id
}
}
newWorker.postMessage(msgToWorker)
} catch (error) {
// The worker has already terminated. There is nothing to handle here
}
}
}
if (IS_BROWSER) { // browser
const workerURL = _isProbablyPrimeWorkerUrl()
for (let i = 0; i < self.navigator.hardwareConcurrency - 1; i++) {
const newWorker = new Worker(workerURL)
newWorker.onmessage = (event) => _onmessage(event.data, newWorker)
workerList.push(newWorker)
}
} else { // Node.js
for (let i = 0; i < os.cpus().length - 1; i++) {
const newWorker = new workerThreads.Worker(__filename)
newWorker.on('message', (msg: WorkerToMainMsg) => _onmessage(msg, newWorker))
workerList.push(newWorker)
}
}
for (let i = 0; i < workerList.length; i++) {
randBits(bitLength, true).then(function (buf: Uint8Array | Buffer) {
const rnd = fromBuffer(buf)
workerList[i].postMessage({
_bcu: {
rnd,
iterations,
id: i
}
})
}).catch(reject)
}
})
}
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
* The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be slower and may freeze thw window in browser's javascript. Please consider using prime() instead.
*
* @param bitLength - The required bit length for the generated prime
* @param iterations - The number of iterations for the Miller-Rabin Probabilistic Primality Test
*
* @throws {@link RangeError} if bitLength < 1
*
* @returns A bigint probable prime of bitLength bits.
*/
export function primeSync (bitLength: number, iterations: number = 16): bigint {
if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
let rnd = 0n
do {
rnd = fromBuffer(randBitsSync(bitLength, true))
} while (!_isProbablyPrime(rnd, iterations))
return rnd
}

View File

@ -1,24 +0,0 @@
import { bitLength } from 'bigint-mod-arith'
import { fromBuffer } from './fromBuffer.js'
import { randBitsSync } from './randBits.js'
/**
* Returns a cryptographically secure random integer between [min,max].
* @param max Returned value will be <= max
* @param min Returned value will be >= min
*
* @throws {@link RangeError} if max <= min
*
* @returns A cryptographically secure random bigint between [min,max]
*/
export function randBetween (max: bigint, min: bigint = 1n): bigint {
if (max <= min) throw new RangeError('Arguments MUST be: max > min')
const interval = max - min
const bitLen = bitLength(interval)
let rnd
do {
const buf = randBitsSync(bitLen)
rnd = fromBuffer(buf)
} while (rnd > interval)
return rnd + min
}

View File

@ -1,40 +0,0 @@
export function _workerUrl (workerCode: string): string {
workerCode = `(() => {${workerCode}})()` // encapsulate IIFE
const _blob = new Blob([workerCode], { type: 'text/javascript' })
return window.URL.createObjectURL(_blob)
}
let _useWorkers = false // The following is just to check whether we can use workers
/* eslint-disable no-lone-blocks */
if (!IS_BROWSER) { // Node.js
try {
await import('worker_threads')
_useWorkers = true
} /* c8 ignore start */ catch (e) {
console.log(`[bigint-crypto-utils] WARNING:
This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers.
· With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `)
}
/* c8 ignore stop */
} else { // Native JS
if (self.Worker !== undefined) _useWorkers = true
}
export { _useWorkers }
export interface WorkerToMainMsg {
_bcu: {
isPrime: boolean
value: bigint
id: number
}
}
export interface MainToWorkerMsg {
_bcu: {
rnd: bigint
iterations: number
id: number
}
}

42
src/workerUtils.ts Normal file
View File

@ -0,0 +1,42 @@
export function _workerUrl(workerCode: string): string {
workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE
const _blob = new Blob([workerCode], { type: "text/javascript" });
return window.URL.createObjectURL(_blob);
}
let _useWorkers = false; // The following is just to check whether we can use workers
/* eslint-disable no-lone-blocks */
if (!true) {
// Node.js
try {
await import("worker_threads");
_useWorkers = true;
} /* c8 ignore start */ catch (e) {
console.log(`[bigint-crypto-utils] WARNING:
This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers.
· With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `);
}
/* c8 ignore stop */
} else {
// Native JS
if (self.Worker !== undefined) _useWorkers = true;
}
export { _useWorkers };
export interface WorkerToMainMsg {
_bcu: {
isPrime: boolean;
value: bigint;
id: number;
};
}
export interface MainToWorkerMsg {
_bcu: {
rnd: bigint;
iterations: number;
id: number;
};
}

View File

@ -1,95 +0,0 @@
import * as bcu from '#pkg'
describe('isProbablyPrime', function () {
this.timeout(90000)
const numbers = [
{
value: BigInt(1),
prime: false,
iterations: 16,
workers: false
},
{
value: BigInt(2),
prime: true,
iterations: 16,
workers: false
},
{
value: 3,
prime: true,
iterations: 16,
workers: false
},
{
value: BigInt(15),
prime: false,
iterations: 32,
workers: false
},
{
value: 29,
prime: true,
iterations: 16,
workers: false
},
{
value: BigInt('669483106578092405936560831017556154622901950048903016651289'),
prime: true,
iterations: 24,
workers: false
},
{
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550077'),
prime: true,
iterations: 16,
workers: false
},
{
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550079'),
prime: false,
iterations: 16,
workers: false
},
{
value: BigInt('115922179551495973383410176342643722334557255682879605864838806293659619625004303206250384392546855063844106965156287951749387634112551089284595541103692716528774876311641700929986988023197242224581099872580798960693521778607396791006450968430359009613295725905514216842343121690916290236558767890728449777'),
prime: true,
iterations: 24,
workers: true
},
{
value: BigInt('168694196579467171180863939518634764192343817610869919231900537093664715354591592262546800497540343203057121816378265655992490621138321114570420047522219942818258345349322155251835677199539229050711145144861404607171419723967136221126986330819362088262358855325306938646602003059377699727688477555163239222109') * BigInt('144678545212641449725111562354371812236197961234111744040227045242578772124779004756249085154188369039159690638725821245974978963371615699005072473649705367893567309027634121825164880046600125480885803891136149601797439273507802533807541605261215613891134865916295914192271736572001975016089773532547481638243'),
prime: false,
iterations: 16,
workers: true
},
{
value: BigInt('918145944120889203205646923554374144932845997937845799234617798611046542304088105084854788397071323714642587188481158334265864050544813693415594035822877094557870151480865568334936301231664228940480803192289508235412296324312748621874408067955753620604885023289655277704554716080844406284392300643321715285709865081125252390440327650852470312931679380011885102491340191287595160450544053114365852338670819405357496612993587404998677760882578064637552397840566752638770525765833183986360029736508910848408875329873614164495552615086579144675027852136994842529623698055210822311666048300438808691619782893307972452223713060928388502843564836966586109748062827799521852219158489504529458627699284110902303538160168376473182639384638674469114371472053977558648090155686345760457454061117853710619580819749222459422610617170567016772342291486643520567969321969827786373531753524990712622940069883277763528926899970596407140603912036918433859986491820017690762751824769335720368488097262208835708414085501930989486498185503469986946236128468697606998536541209764920494156326791142098506801288127033229779646920082892258428128572765585196779698362187479280520327053508580551167899837393726371144977951402741307021389967382422805567365901203'),
prime: true,
iterations: 16,
workers: true
},
{
value: BigInt('940719693126280825126763871881743336375040232953039527942717290104060740215493004508206768342926022549956464101136893240409560470269654765366248516968645294076406953865805712688760371102637642013723011744011617678651884521901163090779813242269935310225049805992299292275574585773507915278612311449919050091057023179541184986547995894821648553256021675133997240195429424258757033557367142630663053464438840832073753440939208165158795269598771598124509831433327480118038278887538430675994497384283550890544882369140852048496460551123626856255619494025370171790720106325655890348475483349150258338517508459674722099347335608814922179633411167540545786247819334838979610017735984374883325689517847175539632896026875016305529321705457954181425405794479825617747354596485074451489940385640535898876551301296003465792117006135339109817937663957519031436646579178503423889430062127572272773511424424297800355292430651838502733756881154935252456036638082486459287411002911323257940893413982671660332662880068976408321968046549017547143836993553556640198884769590214676797037397502067035957959952990027503148987727895561468097917730167320715053689862847457761993196945361244822787209076446259359976421264285658106819879849052247546957718175231'),
prime: false,
iterations: 16,
workers: true
}
]
for (const num of numbers) {
describe(`isProbablyPrime(${num.value}, ${num.iterations}, ${String(!num.workers)})`, function () {
it(`should return ${String(num.prime)}`, async function () {
let ret
if (num.iterations === 16 && num.workers) ret = await bcu.isProbablyPrime(num.value)
else ret = await bcu.isProbablyPrime(num.value, num.iterations, !num.workers)
chai.expect(ret).to.equal(num.prime)
})
})
}
describe('isProbablyPrime(-1)', function () {
it('should throw RangeError', function () {
chai.expect(() => bcu.isProbablyPrime(-1)).to.throw(RangeError) // eslint-disable-line
})
})
})

View File

@ -1,37 +0,0 @@
import * as bcu from '#pkg'
describe('Testing prime generation', function () {
const bitLengths = [
0,
8,
255,
256,
258,
512,
1024,
2048,
3072
]
this.timeout(120000)
for (const bitLength of bitLengths) {
describe(`prime(${bitLength})`, function () {
if (bitLength > 0) {
it(`should return a random ${bitLength}-bits probable prime`, async function () {
const prime = await bcu.prime(bitLength)
chai.expect(bcu.bitLength(prime)).to.equal(bitLength)
})
} else {
it('should throw error', function () {
chai.expect(() => bcu.prime(bitLength)).to.throw(RangeError) // eslint-disable-line
})
}
})
}
describe('Testing sync (NOT-RECOMMENDED) version: primeSync()', function () {
it('should return a random 1024-bits probable prime', function () {
const prime = bcu.primeSync(1024, 16)
chai.expect(bcu.bitLength(prime)).to.equal(1024)
chai.expect(() => bcu.primeSync(0)).to.throw(RangeError)
})
})
})

View File

@ -1,90 +0,0 @@
import * as bcu from '#pkg'
describe('randBetween', function () {
const numbers = [
{
min: BigInt(1),
max: BigInt(2) ** BigInt(234),
iterations: 100,
error: false,
errorMax: false
},
{
min: BigInt('122461641436345153'),
max: BigInt(2) ** BigInt(234),
iterations: 100,
error: false,
errorMax: false
},
{
min: BigInt(-41536),
max: BigInt(213),
iterations: 100,
error: false,
errorMax: false
},
{
min: BigInt('-41446134643671357134674615124613467356734646146125'),
max: BigInt('-125246'),
iterations: 100,
error: false,
errorMax: true
},
{
min: BigInt(146347),
max: BigInt(232),
iterations: 1,
error: true,
errorMax: false
},
{
min: BigInt(2),
max: BigInt(2),
iterations: 1,
error: true,
errorMax: false
},
{
min: BigInt(1),
max: BigInt(-1),
iterations: 1,
error: true,
errorMax: true
}
]
this.timeout(90000)
for (const num of numbers) {
describe(`randBetween(${num.max}, ${num.min})`, function () {
if (!num.error) {
it(`[${num.iterations} iterations] should return x such that min <= x <= max`, function () {
let ret = true
for (let i = 0; i < num.iterations; i++) {
const x = bcu.randBetween(num.max, num.min)
ret = ret && x >= num.min && x <= num.max
}
chai.expect(ret).to.equal(true)
})
} else {
it('should throw RangeError (max <=0 || min <0 || min>=max)', function () {
chai.expect(() => bcu.randBetween(num.max, num.min)).to.throw(RangeError)
})
}
})
describe(`randBetween(${num.max})`, function () {
if (!num.errorMax) {
it(`[${num.iterations} iterations] should return x such that 1 <= x <= max`, function () {
let ret = true
for (let i = 0; i < num.iterations; i++) {
const x = bcu.randBetween(num.max)
ret = ret && x >= BigInt(1) && x <= num.max
}
chai.expect(ret).to.equal(true)
})
} else {
it('should throw RangeError (max <= min)', function () {
chai.expect(() => bcu.randBetween(num.max)).to.throw(RangeError)
})
}
})
}
})

View File

@ -1,107 +0,0 @@
import * as bcu from '#pkg'
const iterations = 10
const bitLengths = [-1, 0, 3, 8, 16, 511, 2048]
const byteLengths = [-7, 0, 1, 8, 33, 40, 65536, 67108864]
describe('testing randBits', function () {
for (const bitLength of bitLengths) {
describe(`${iterations} iterations of randBitsSync(${bitLength})`, function () {
if (bitLength > 0) {
it('should return buffers', function () {
let ret = true
for (let i = 0; i < iterations; i++) {
const randbits = bcu.randBitsSync(bitLength)
// console.log(JSON.stringify(randbits))
const randbits2 = bcu.randBitsSync(bitLength, true)
// console.log(JSON.stringify(randbits2))
if (!(((randbits instanceof Uint8Array) && (randbits2 instanceof Uint8Array)) ||
((randbits instanceof Buffer) && (randbits2 instanceof Buffer)))) {
ret = false
break
}
}
chai.expect(ret).to.equal(true)
})
} else {
it('should throw RangeError', function () {
chai.expect(() => bcu.randBitsSync(bitLength)).to.throw(RangeError)
})
}
})
describe(`${iterations} iterations of randBits(${bitLength})`, function () {
if (bitLength > 0) {
it('should return buffers', async function () {
let ret = true
for (let i = 0; i < iterations; i++) {
const randbits = await bcu.randBits(bitLength)
// console.log(JSON.stringify(randbits))
const randbits2 = await bcu.randBits(bitLength, true)
// console.log(JSON.stringify(randbits2))
if (!(((randbits instanceof Uint8Array) && (randbits2 instanceof Uint8Array)) ||
((randbits instanceof Buffer) && (randbits2 instanceof Buffer)))) {
ret = false
break
}
}
chai.expect(ret).to.equal(true)
})
} else {
it('should throw RangeError', function () {
chai.expect(() => bcu.randBits(bitLength)).to.throw(RangeError) // eslint-disable-line
})
}
})
}
})
describe('testing randBytes', function () {
for (const byteLength of byteLengths) {
describe(`${iterations} iterations of randBytesSync(${byteLength})`, function () {
if (byteLength > 0) {
it('should return buffers', function () {
let ret = true
for (let i = 0; i < iterations; i++) {
const randbytes = bcu.randBytesSync(byteLength)
// console.log(JSON.stringify(randbits))
const randbytes2 = bcu.randBytesSync(byteLength, true)
// console.log(JSON.stringify(randbits2))
if (!(((randbytes instanceof Uint8Array) && (randbytes2 instanceof Uint8Array)) ||
((randbytes instanceof Buffer) && (randbytes2 instanceof Buffer)))) {
ret = false
}
}
chai.expect(ret).to.equal(true)
})
} else {
it('should throw RangeError', function () {
chai.expect(() => bcu.randBytesSync(byteLength)).to.throw(RangeError)
})
}
})
describe(`${iterations} iterations of randBytes(${byteLength})`, function () {
if (byteLength > 0) {
it('should return buffers of the expected length', async function () {
let ret = true
for (let i = 0; i < iterations; i++) {
const randbytes = await bcu.randBytes(byteLength)
chai.expect(randbytes.length).to.equal(byteLength)
// console.log(JSON.stringify(randbits))
const randbytes2 = await bcu.randBytes(byteLength, true)
chai.expect(randbytes2.length).to.equal(byteLength)
// console.log(JSON.stringify(randbits2))
if (!(((randbytes instanceof Uint8Array) && (randbytes2 instanceof Uint8Array)) ||
((randbytes instanceof Buffer) && (randbytes2 instanceof Buffer)))) {
ret = false
}
}
chai.expect(ret).to.equal(true)
})
} else {
it('should throw RangeError', function () {
chai.expect(() => bcu.randBytes(byteLength)).to.throw(RangeError) // eslint-disable-line
})
}
})
}
})

View File

@ -1,47 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', 'ES2023', 'ES2023' or 'ESNEXT'. */
"module": "ESNext",
// "lib": [ "es2020" ], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
"outDir": ".dst", /* If not set we cannot import .js files without a warning that is going to be overwritten. outDir is not going to be used in any case */
"checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'react', 'react-jsx', 'react-jsxdev', 'preserve' or 'react-native'. */
"strict": true, /* Enable all strict type-checking options. */
"removeComments": true,
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDir": ".",
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": [ "node_modules/@types", "build/typings" ], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
"resolveJsonModule": true,
"paths": {
"#pkg": ["./src/ts/index.ts"]
}
},
"include": ["src/ts/**/*", "build/typings/**/*.d.ts", "test/**/*", "test-vectors/**/*.ts", "benchmark/**/*.ts"]
}

View File

@ -1,3 +0,0 @@
/// <reference types="node" />
export declare function fromBuffer(buf: Uint8Array | Buffer): bigint;
//# sourceMappingURL=fromBuffer.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"fromBuffer.d.ts","sourceRoot":"","sources":["../src/ts/fromBuffer.ts"],"names":[],"mappings":";AAAA,wBAAgB,UAAU,CAAE,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAO5D"}

View File

@ -1,3 +0,0 @@
/// <reference types="node" />
export declare function fromBuffer(buf: Uint8Array | Buffer): bigint;
//# sourceMappingURL=fromBuffer.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"fromBuffer.d.ts","sourceRoot":"","sources":["../src/ts/fromBuffer.ts"],"names":[],"mappings":";AAAA,wBAAgB,UAAU,CAAE,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAO5D"}

View File

@ -1,7 +0,0 @@
export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from 'bigint-mod-arith';
export { isProbablyPrime } from './isProbablyPrime.js';
export { prime, primeSync } from './prime.js';
export { randBetween } from './randBetween.js';
export { randBits, randBitsSync } from './randBits.js';
export { randBytes, randBytesSync } from './randBytes.js';
//# sourceMappingURL=index.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/ts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEjG,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA"}

7
types/index.d.ts vendored
View File

@ -1,7 +0,0 @@
export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from 'bigint-mod-arith';
export { isProbablyPrime } from './isProbablyPrime.js';
export { prime, primeSync } from './prime.js';
export { randBetween } from './randBetween.js';
export { randBits, randBitsSync } from './randBits.js';
export { randBytes, randBytesSync } from './randBytes.js';
//# sourceMappingURL=index.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/ts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEjG,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA"}

View File

@ -1,4 +0,0 @@
export declare function isProbablyPrime(w: number | bigint, iterations?: number, disableWorkers?: boolean): Promise<boolean>;
export declare function _isProbablyPrime(w: bigint, iterations: number): boolean;
export declare function _isProbablyPrimeWorkerUrl(): string;
//# sourceMappingURL=isProbablyPrime.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"isProbablyPrime.d.ts","sourceRoot":"","sources":["../src/ts/isProbablyPrime.ts"],"names":[],"mappings":"AAmBA,wBAAgB,eAAe,CAAE,CAAC,EAAE,MAAM,GAAC,MAAM,EAAE,UAAU,GAAE,MAAW,EAAE,cAAc,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CA2D7H;AAED,wBAAgB,gBAAgB,CAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CA0TxE;AAED,wBAAgB,yBAAyB,IAAK,MAAM,CA8BnD"}

View File

@ -1,4 +0,0 @@
export declare function isProbablyPrime(w: number | bigint, iterations?: number, disableWorkers?: boolean): Promise<boolean>;
export declare function _isProbablyPrime(w: bigint, iterations: number): boolean;
export declare function _isProbablyPrimeWorkerUrl(): string;
//# sourceMappingURL=isProbablyPrime.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"isProbablyPrime.d.ts","sourceRoot":"","sources":["../src/ts/isProbablyPrime.ts"],"names":[],"mappings":"AAmBA,wBAAgB,eAAe,CAAE,CAAC,EAAE,MAAM,GAAC,MAAM,EAAE,UAAU,GAAE,MAAW,EAAE,cAAc,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CA2D7H;AAED,wBAAgB,gBAAgB,CAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CA0TxE;AAED,wBAAgB,yBAAyB,IAAK,MAAM,CA8BnD"}

View File

@ -1,3 +0,0 @@
export declare function prime(bitLength: number, iterations?: number): Promise<bigint>;
export declare function primeSync(bitLength: number, iterations?: number): bigint;
//# sourceMappingURL=prime.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"prime.d.ts","sourceRoot":"","sources":["../src/ts/prime.ts"],"names":[],"mappings":"AA2BA,wBAAgB,KAAK,CAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAoElF;AAaD,wBAAgB,SAAS,CAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,MAAM,CAO7E"}

3
types/prime.d.ts vendored
View File

@ -1,3 +0,0 @@
export declare function prime(bitLength: number, iterations?: number): Promise<bigint>;
export declare function primeSync(bitLength: number, iterations?: number): bigint;
//# sourceMappingURL=prime.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"prime.d.ts","sourceRoot":"","sources":["../src/ts/prime.ts"],"names":[],"mappings":"AA2BA,wBAAgB,KAAK,CAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAoElF;AAaD,wBAAgB,SAAS,CAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,MAAM,CAO7E"}

View File

@ -1,2 +0,0 @@
export declare function randBetween(max: bigint, min?: bigint): bigint;
//# sourceMappingURL=randBetween.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"randBetween.d.ts","sourceRoot":"","sources":["../src/ts/randBetween.ts"],"names":[],"mappings":"AAaA,wBAAgB,WAAW,CAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAW,GAAG,MAAM,CAUlE"}

View File

@ -1,2 +0,0 @@
export declare function randBetween(max: bigint, min?: bigint): bigint;
//# sourceMappingURL=randBetween.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"randBetween.d.ts","sourceRoot":"","sources":["../src/ts/randBetween.ts"],"names":[],"mappings":"AAaA,wBAAgB,WAAW,CAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAW,GAAG,MAAM,CAUlE"}

View File

@ -1,4 +0,0 @@
/// <reference types="node" />
export declare function randBits(bitLength: number, forceLength?: boolean): Promise<Uint8Array | Buffer>;
export declare function randBitsSync(bitLength: number, forceLength?: boolean): Uint8Array | Buffer;
//# sourceMappingURL=randBits.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"randBits.d.ts","sourceRoot":"","sources":["../src/ts/randBits.ts"],"names":[],"mappings":";AAYA,wBAAgB,QAAQ,CAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,OAAO,CAAC,UAAU,GAAC,MAAM,CAAC,CAmBrG;AAWD,wBAAgB,YAAY,CAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,UAAU,GAAG,MAAM,CAelG"}

4
types/randBits.d.ts vendored
View File

@ -1,4 +0,0 @@
/// <reference types="node" />
export declare function randBits(bitLength: number, forceLength?: boolean): Promise<Uint8Array | Buffer>;
export declare function randBitsSync(bitLength: number, forceLength?: boolean): Uint8Array | Buffer;
//# sourceMappingURL=randBits.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"randBits.d.ts","sourceRoot":"","sources":["../src/ts/randBits.ts"],"names":[],"mappings":";AAYA,wBAAgB,QAAQ,CAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,OAAO,CAAC,UAAU,GAAC,MAAM,CAAC,CAmBrG;AAWD,wBAAgB,YAAY,CAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,UAAU,GAAG,MAAM,CAelG"}

View File

@ -1,4 +0,0 @@
/// <reference types="node" />
export declare function randBytes(byteLength: number, forceLength?: boolean): Promise<Uint8Array | Buffer>;
export declare function randBytesSync(byteLength: number, forceLength?: boolean): Uint8Array | Buffer;
//# sourceMappingURL=randBytes.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"randBytes.d.ts","sourceRoot":"","sources":["../src/ts/randBytes.ts"],"names":[],"mappings":";AAYA,wBAAgB,SAAS,CAAE,UAAU,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,UAAU,GAAC,MAAM,CAAC,CA4B9F;AAaD,wBAAgB,aAAa,CAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,UAAU,GAAG,MAAM,CA0BpG"}

View File

@ -1,4 +0,0 @@
/// <reference types="node" />
export declare function randBytes(byteLength: number, forceLength?: boolean): Promise<Uint8Array | Buffer>;
export declare function randBytesSync(byteLength: number, forceLength?: boolean): Uint8Array | Buffer;
//# sourceMappingURL=randBytes.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"randBytes.d.ts","sourceRoot":"","sources":["../src/ts/randBytes.ts"],"names":[],"mappings":";AAYA,wBAAgB,SAAS,CAAE,UAAU,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,UAAU,GAAC,MAAM,CAAC,CA4B9F;AAaD,wBAAgB,aAAa,CAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,UAAU,GAAG,MAAM,CA0BpG"}

View File

@ -1,18 +0,0 @@
export declare function _workerUrl(workerCode: string): string;
declare let _useWorkers: boolean;
export { _useWorkers };
export interface WorkerToMainMsg {
_bcu: {
isPrime: boolean;
value: bigint;
id: number;
};
}
export interface MainToWorkerMsg {
_bcu: {
rnd: bigint;
iterations: number;
id: number;
};
}
//# sourceMappingURL=workerUtils.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"workerUtils.d.ts","sourceRoot":"","sources":["../src/ts/workerUtils.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAItD;AAED,QAAA,IAAI,WAAW,SAAQ,CAAA;AAiBvB,OAAO,EAAE,WAAW,EAAE,CAAA;AAEtB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QACJ,OAAO,EAAE,OAAO,CAAA;QAChB,KAAK,EAAE,MAAM,CAAA;QACb,EAAE,EAAE,MAAM,CAAA;KACX,CAAA;CACF;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAA;QACX,UAAU,EAAE,MAAM,CAAA;QAClB,EAAE,EAAE,MAAM,CAAA;KACX,CAAA;CACF"}

View File

@ -1,18 +0,0 @@
export declare function _workerUrl(workerCode: string): string;
declare let _useWorkers: boolean;
export { _useWorkers };
export interface WorkerToMainMsg {
_bcu: {
isPrime: boolean;
value: bigint;
id: number;
};
}
export interface MainToWorkerMsg {
_bcu: {
rnd: bigint;
iterations: number;
id: number;
};
}
//# sourceMappingURL=workerUtils.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"workerUtils.d.ts","sourceRoot":"","sources":["../src/ts/workerUtils.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAItD;AAED,QAAA,IAAI,WAAW,SAAQ,CAAA;AAiBvB,OAAO,EAAE,WAAW,EAAE,CAAA;AAEtB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QACJ,OAAO,EAAE,OAAO,CAAA;QAChB,KAAK,EAAE,MAAM,CAAA;QACb,EAAE,EAAE,MAAM,CAAA;KACX,CAAA;CACF;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAA;QACX,UAAU,EAAE,MAAM,CAAA;QAClB,EAAE,EAAE,MAAM,CAAA;KACX,CAAA;CACF"}